Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

markdown-notes-tree

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

markdown-notes-tree - npm Package Compare versions

Comparing version 1.8.3 to 1.9.0

2

package.json
{
"name": "markdown-notes-tree",
"version": "1.8.3",
"version": "1.9.0",
"description": "Generate Markdown trees that act as a table of contents for a folder structure with Markdown notes",

@@ -5,0 +5,0 @@ "main": "src/index.js",

@@ -45,2 +45,6 @@ # markdown-notes-tree

## Subdirectory titles
If you specify a custom title for a subdirectory, it will be preserved and it will be used in the tree instead of the name of the subdirectory.
## Command line arguments

@@ -47,0 +51,0 @@

@@ -24,12 +24,17 @@ "use strict";

function getTitleFromMarkdownContents(contents) {
const firstLine = contents.split(/\r\n|\r|\n/, 1)[0];
contents = getContentsWithUpdatedMarkers(contents);
const lines = contents.split(/\r\n|\r|\n/);
if (firstLine.startsWith("# ")) {
return firstLine.substring(2);
} else {
return undefined;
for (const line of lines) {
if (line.startsWith("# ")) {
return line.substring(2);
} else if (line !== "" && line !== markers.directoryReadmeStart) {
return undefined;
}
}
return undefined;
}
function getNewMainReadmeContents(currentContents, markdownForTree, endOfLine) {
function getNewMainReadmeContents(currentContents, markdownForTree, environment) {
currentContents = getContentsWithUpdatedMarkers(currentContents);

@@ -44,3 +49,3 @@

} else {
contentsBeforeTree = currentContents + endOfLine.repeat(2);
contentsBeforeTree = currentContents + environment.endOfLine.repeat(2);
}

@@ -62,3 +67,3 @@

} else {
contentsAfterTree = endOfLine;
contentsAfterTree = environment.endOfLine;
}

@@ -69,5 +74,5 @@

markers.mainReadmeTreeStart +
endOfLine.repeat(2) +
environment.endOfLine.repeat(2) +
markdownForTree +
endOfLine.repeat(2) +
environment.endOfLine.repeat(2) +
markers.mainReadmeTreeEnd +

@@ -85,12 +90,16 @@ contentsAfterTree

function getNewDirectoryReadmeContents(name, currentContents, markdownForTree, endOfLine) {
function getNewDirectoryReadmeContents(name, currentContents, markdownForTree, environment) {
currentContents = getContentsWithUpdatedMarkers(currentContents);
const title = `# ${name}`;
const currentTitle = getTitleFromMarkdownContents(currentContents);
const title = currentTitle || name;
const titleLine = `# ${title}`;
const description = getDirectoryDescriptionFromCurrentContents(currentContents);
let partBetweenDescriptionMarkers = endOfLine.repeat(2);
let partBetweenDescriptionMarkers = environment.endOfLine.repeat(2);
if (description) {
partBetweenDescriptionMarkers = endOfLine.repeat(2) + description + endOfLine.repeat(2);
partBetweenDescriptionMarkers =
environment.endOfLine.repeat(2) + description + environment.endOfLine.repeat(2);
}

@@ -100,11 +109,11 @@

markers.directoryReadmeStart +
endOfLine.repeat(2) +
title +
endOfLine.repeat(2) +
environment.endOfLine.repeat(2) +
titleLine +
environment.endOfLine.repeat(2) +
markers.directoryReadmeDescriptionStart +
partBetweenDescriptionMarkers +
markers.directoryReadmeDescriptionEnd +
endOfLine.repeat(2) +
environment.endOfLine.repeat(2) +
markdownForTree +
endOfLine
environment.endOfLine
);

@@ -111,0 +120,0 @@ }

@@ -26,3 +26,18 @@ "use strict";

test("it should return undefined if the first line doesn't have a title", () => {
test("it should ignore starting empty lines", () => {
const contents = "\n\n# test";
expect(fileContents.getTitleFromMarkdownContents(contents)).toBe("test");
});
test("it should ignore the directory README start marker", () => {
const contents = "<!-- generated by markdown-notes-tree -->\n\n# test";
expect(fileContents.getTitleFromMarkdownContents(contents)).toBe("test");
});
test("it should ignore the old directory README start marker", () => {
const contents = "<!-- this entire file is auto-generated -->\n\n# test";
expect(fileContents.getTitleFromMarkdownContents(contents)).toBe("test");
});
test("it should return undefined if the first real doesn't have a title", () => {
const contents = "some non-title content";

@@ -40,3 +55,3 @@ expect(fileContents.getTitleFromMarkdownContents(contents)).toBeUndefined();

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -71,3 +86,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -104,3 +119,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -133,3 +148,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -160,3 +175,5 @@

expect(() =>
fileContents.getNewMainReadmeContents(currentContents, "markdownForTree", endOfLine)
fileContents.getNewMainReadmeContents(currentContents, "markdownForTree", {
endOfLine
})
).toThrow("Invalid file structure: tree end marker found before tree start marker");

@@ -174,3 +191,5 @@ });

expect(() =>
fileContents.getNewMainReadmeContents(currentContents, "markdownForTree", endOfLine)
fileContents.getNewMainReadmeContents(currentContents, "markdownForTree", {
endOfLine
})
).toThrow("Invalid file structure: tree end marker found before tree start marker");

@@ -188,3 +207,3 @@ });

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -218,3 +237,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -252,3 +271,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -288,3 +307,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -326,3 +345,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
);

@@ -363,3 +382,3 @@

"markdownForTree",
endOfLine
{ endOfLine }
)

@@ -370,3 +389,36 @@ ).toThrow(

});
test("it should preserve the title from the current contents if provided", () => {
const currentContents =
dedent(`<!-- generated by markdown-notes-tree -->
# Custom title goes here
<!-- optional markdown-notes-tree directory description starts here -->
<!-- optional markdown-notes-tree directory description ends here -->
markdownForTree`) + endOfLine;
const result = fileContents.getNewDirectoryReadmeContents(
"name",
currentContents,
"markdownForTree",
{ endOfLine }
);
const expected =
dedent(`<!-- generated by markdown-notes-tree -->
# Custom title goes here
<!-- optional markdown-notes-tree directory description starts here -->
<!-- optional markdown-notes-tree directory description ends here -->
markdownForTree`) + endOfLine;
expect(result).toBe(expected);
});
});
});

@@ -8,12 +8,12 @@ "use strict";

function shouldIgnoreDirectory(name, relativeParentPath, options) {
if (shouldIgnoreDirectoryBasedOnName(name, options)) {
function shouldIgnoreDirectory(name, relativeParentPath, environment) {
if (shouldIgnoreDirectoryBasedOnName(name, environment)) {
return true;
}
return shouldIgnoreBasedOnGlobs(name, relativeParentPath, options);
return shouldIgnoreBasedOnGlobs(name, relativeParentPath, environment);
}
function shouldIgnoreDirectoryBasedOnName(name, options) {
if (options.includeAllDirectoriesByDefault) {
function shouldIgnoreDirectoryBasedOnName(name, environment) {
if (environment.options.includeAllDirectoriesByDefault) {
return false;

@@ -25,3 +25,3 @@ } else {

function shouldIgnoreFile(name, relativeParentPath, options) {
function shouldIgnoreFile(name, relativeParentPath, environment) {
if (!name.endsWith(".md") || name === "README.md") {

@@ -31,9 +31,9 @@ return true;

return shouldIgnoreBasedOnGlobs(name, relativeParentPath, options);
return shouldIgnoreBasedOnGlobs(name, relativeParentPath, environment);
}
function shouldIgnoreBasedOnGlobs(name, relativeParentPath, options) {
function shouldIgnoreBasedOnGlobs(name, relativeParentPath, environment) {
const relativePath = path.join(relativeParentPath, name);
for (const ignoredGlob of options.ignore) {
for (const ignoredGlob of environment.options.ignore) {
if (minimatch(relativePath, ignoredGlob)) {

@@ -40,0 +40,0 @@ return true;

@@ -10,4 +10,6 @@ "use strict";

ignores.shouldIgnoreDirectory("sub1", "", {
ignore: [],
includeAllDirectoriesByDefault: false
options: {
ignore: [],
includeAllDirectoriesByDefault: false
}
})

@@ -20,4 +22,6 @@ ).toBe(false);

ignores.shouldIgnoreDirectory(".test", "", {
ignore: [],
includeAllDirectoriesByDefault: false
options: {
ignore: [],
includeAllDirectoriesByDefault: false
}
})

@@ -30,4 +34,6 @@ ).toBe(true);

ignores.shouldIgnoreDirectory("_test", "", {
ignore: [],
includeAllDirectoriesByDefault: false
options: {
ignore: [],
includeAllDirectoriesByDefault: false
}
})

@@ -40,4 +46,6 @@ ).toBe(true);

ignores.shouldIgnoreDirectory("node_modules", "", {
ignore: [],
includeAllDirectoriesByDefault: false
options: {
ignore: [],
includeAllDirectoriesByDefault: false
}
})

@@ -50,4 +58,6 @@ ).toBe(true);

ignores.shouldIgnoreDirectory(".test", "", {
ignore: [],
includeAllDirectoriesByDefault: true
options: {
ignore: [],
includeAllDirectoriesByDefault: true
}
})

@@ -60,4 +70,6 @@ ).toBe(false);

ignores.shouldIgnoreDirectory("exclude-this-folder", "parent", {
ignore: ["parent/exclude-this-folder"],
includeAllDirectoriesByDefault: false
options: {
ignore: ["parent/exclude-this-folder"],
includeAllDirectoriesByDefault: false
}
})

@@ -70,4 +82,6 @@ ).toBe(true);

ignores.shouldIgnoreDirectory("node_modules", "", {
ignore: ["node_modules"],
includeAllDirectoriesByDefault: true
options: {
ignore: ["node_modules"],
includeAllDirectoriesByDefault: true
}
})

@@ -80,11 +94,15 @@ ).toBe(true);

test("it should return false for normal files", () => {
expect(ignores.shouldIgnoreFile("test.md", "", { ignore: [] })).toBe(false);
expect(ignores.shouldIgnoreFile("test.md", "", { options: { ignore: [] } })).toBe(
false
);
});
test("it should return true for non-Markdown files", () => {
expect(ignores.shouldIgnoreFile("test.js", "", { ignore: [] })).toBe(true);
expect(ignores.shouldIgnoreFile("test.js", "", { options: { ignore: [] } })).toBe(true);
});
test("it should return true for README.md files", () => {
expect(ignores.shouldIgnoreFile("README.md", "", { ignore: [] })).toBe(true);
expect(ignores.shouldIgnoreFile("README.md", "", { options: { ignore: [] } })).toBe(
true
);
});

@@ -95,3 +113,5 @@

ignores.shouldIgnoreFile("test.md", "exclude-this-folder", {
ignore: ["exclude-this-folder/*.md"]
options: {
ignore: ["exclude-this-folder/*.md"]
}
})

@@ -98,0 +118,0 @@ ).toBe(true);

@@ -15,14 +15,13 @@ "use strict";

const logger = getLogger(defaultLogger, options);
const environment = { options, logger, endOfLine: os.EOL };
logger("Processing files in order to build notes tree");
const tree = treeBuilder.buildTree(options);
const tree = treeBuilder.buildTree(environment);
const endOfLine = os.EOL;
logger("Writing notes tree to main README file");
treeWriter.writeTreeToMainReadme(tree, endOfLine, options);
treeWriter.writeTreeToMainReadme(tree, environment);
if (!options.noSubdirectoryTrees) {
logger("Writing trees for directories");
treeWriter.writeTreesForDirectories(tree, endOfLine, options, logger);
treeWriter.writeTreesForDirectories(tree, environment);
}

@@ -29,0 +28,0 @@

@@ -13,7 +13,7 @@ "use strict";

function buildTree(options) {
return buildTreeStartingAt("", options);
function buildTree(environment) {
return buildTreeStartingAt("", environment);
}
function buildTreeStartingAt(relativePath, options) {
function buildTreeStartingAt(relativePath, environment) {
const absolutePath = pathUtils.getAbsolutePath(relativePath);

@@ -26,6 +26,11 @@ const entries = fs.readdirSync(absolutePath, { withFileTypes: true });

const treeNodesForDirectories = getTreeNodesForDirectories(directories, relativePath, options);
const treeNodesForFiles = getTreeNodesForFiles(files, relativePath, options);
const treeNodesForDirectories = getTreeNodesForDirectories(
directories,
relativePath,
environment
);
if (options.notesBeforeDirectories) {
const treeNodesForFiles = getTreeNodesForFiles(files, relativePath, environment);
if (environment.options.notesBeforeDirectories) {
return [...treeNodesForFiles, ...treeNodesForDirectories];

@@ -37,15 +42,20 @@ } else {

function getTreeNodesForDirectories(directories, relativeParentPath, options) {
function getTreeNodesForDirectories(directories, relativeParentPath, environment) {
const treeNodes = [];
for (const directory of directories) {
if (!ignores.shouldIgnoreDirectory(directory.name, relativeParentPath, options)) {
if (!ignores.shouldIgnoreDirectory(directory.name, relativeParentPath, environment)) {
const relativePath = path.join(relativeParentPath, directory.name);
const relativeReadmePath = path.join(relativePath, "README.md");
const readmeContents = getCurrentContents(relativeReadmePath);
treeNodes.push({
isDirectory: true,
title: directory.name,
description: getDescriptionFromDirectoryReadme(relativePath),
title: fileContents.getTitleFromMarkdownContents(readmeContents) || directory.name,
description: getDescriptionFromDirectoryReadmeContents(
readmeContents,
relativeReadmePath
),
filename: directory.name,
children: buildTreeStartingAt(relativePath, options)
children: buildTreeStartingAt(relativePath, environment)
});

@@ -58,7 +68,7 @@ }

function getTreeNodesForFiles(files, relativeParentPath, options) {
function getTreeNodesForFiles(files, relativeParentPath, environment) {
const treeNodes = [];
for (const file of files) {
if (!ignores.shouldIgnoreFile(file.name, relativeParentPath, options)) {
if (!ignores.shouldIgnoreFile(file.name, relativeParentPath, environment)) {
const relativePath = path.join(relativeParentPath, file.name);

@@ -74,3 +84,3 @@

if (options.orderNotesByTitle) {
if (environment.options.orderNotesByTitle) {
treeNodes.sort((a, b) => stringUtils.compareIgnoringCaseAndDiacritics(a.title, b.title));

@@ -82,17 +92,5 @@ }

function getTitleFromMarkdownFileOrThrow(relativePath) {
function getCurrentContents(relativePath) {
const absolutePath = pathUtils.getAbsolutePath(relativePath);
const contents = fs.readFileSync(absolutePath, { encoding: "utf-8" });
const title = fileContents.getTitleFromMarkdownContents(contents);
if (!title) {
throw new Error(`No title found for Markdown file ${absolutePath}`);
}
return title;
}
function getDescriptionFromDirectoryReadme(relativeDirectoryPath) {
const absolutePath = pathUtils.getAbsolutePath(path.join(relativeDirectoryPath, "README.md"));
if (!fs.existsSync(absolutePath)) {

@@ -102,12 +100,24 @@ return "";

let description;
return fs.readFileSync(absolutePath, { encoding: "utf-8" });
}
function getDescriptionFromDirectoryReadmeContents(contents, relativePath) {
try {
const contents = fs.readFileSync(absolutePath, { encoding: "utf-8" });
description = fileContents.getDirectoryDescriptionFromCurrentContents(contents);
return fileContents.getDirectoryDescriptionFromCurrentContents(contents);
} catch (error) {
const absolutePath = pathUtils.getAbsolutePath(relativePath);
throw new Error(`Cannot get description from file ${absolutePath}: ${error.message}`);
}
}
return description;
function getTitleFromMarkdownFileOrThrow(relativePath) {
const contents = getCurrentContents(relativePath);
const title = fileContents.getTitleFromMarkdownContents(contents);
if (!title) {
const absolutePath = pathUtils.getAbsolutePath(relativePath);
throw new Error(`No title found for Markdown file ${absolutePath}`);
}
return title;
}

@@ -7,20 +7,15 @@ "use strict";

function getMarkdownForTree(tree, endOfLine, options) {
const lines = getMarkdownLinesForTree(tree, [], endOfLine, options);
return lines.join(endOfLine);
function getMarkdownForTree(tree, environment) {
const lines = getMarkdownLinesForTree(tree, [], environment);
return lines.join(environment.endOfLine);
}
function getMarkdownLinesForTree(tree, parentPathParts, endOfLine, options) {
function getMarkdownLinesForTree(tree, parentPathParts, environment) {
const markdownLines = [];
const indentationUnit = getIndentationUnit(options);
const indentationUnit = getIndentationUnit(environment);
for (const treeNode of tree) {
const markdownForTreeNode = getMarkdownForTreeNode(
treeNode,
parentPathParts,
endOfLine,
options
);
const markdownForTreeNode = getMarkdownForTreeNode(treeNode, parentPathParts, environment);
markdownLines.push(...markdownForTreeNode.split(endOfLine));
markdownLines.push(...markdownForTreeNode.split(environment.endOfLine));

@@ -33,4 +28,3 @@ if (treeNode.isDirectory) {

fullPathParts,
endOfLine,
options
environment
);

@@ -46,5 +40,5 @@

function getIndentationUnit(options) {
function getIndentationUnit(environment) {
// Markdown standard: either four spaces or tabs
if (options.useTabs) {
if (environment.options.useTabs) {
return "\t";

@@ -56,5 +50,5 @@ } else {

function getMarkdownForTreeNode(treeNode, parentPath, endOfLine, options) {
function getMarkdownForTreeNode(treeNode, parentPath, environment) {
const linkText = getLinkTextForTreeNode(treeNode);
const linkTarget = getLinkTargetForTreeNode(treeNode, parentPath, options);
const linkTarget = getLinkTargetForTreeNode(treeNode, parentPath, environment);

@@ -64,3 +58,3 @@ const basicLine = `- [${linkText}](${linkTarget})`;

if (treeNode.description) {
const descriptionSeparator = getDescriptionSeparator(endOfLine, options);
const descriptionSeparator = getDescriptionSeparator(environment);
return basicLine + descriptionSeparator + treeNode.description;

@@ -80,7 +74,7 @@ } else {

function getLinkTargetForTreeNode(treeNode, parentPathParts, options) {
function getLinkTargetForTreeNode(treeNode, parentPathParts, environment) {
const fullPathParts = [...parentPathParts, treeNode.filename];
let linkTarget = fullPathParts.join("/");
if (treeNode.isDirectory && options.linkToSubdirectoryReadme) {
if (treeNode.isDirectory && environment.options.linkToSubdirectoryReadme) {
linkTarget = linkTarget + "/README.md";

@@ -92,5 +86,5 @@ }

function getDescriptionSeparator(endOfLine, options) {
if (options.subdirectoryDescriptionOnNewLine) {
return " " + endOfLine + getIndentationUnit(options);
function getDescriptionSeparator(environment) {
if (environment.options.subdirectoryDescriptionOnNewLine) {
return " " + environment.endOfLine + getIndentationUnit(environment);
} else {

@@ -97,0 +91,0 @@ return " - ";

@@ -25,5 +25,8 @@ "use strict";

test("it should generate a tree with proper formatting and indentation", () => {
const result = treeMarkdownGenerator.getMarkdownForTree(tree, endOfLine, {
linkToSubdirectoryReadme: false,
useTabs: false
const result = treeMarkdownGenerator.getMarkdownForTree(tree, {
endOfLine,
options: {
linkToSubdirectoryReadme: false,
useTabs: false
}
});

@@ -38,5 +41,8 @@

test("it should allow linking directly to subdirectory README files", () => {
const result = treeMarkdownGenerator.getMarkdownForTree(tree, endOfLine, {
linkToSubdirectoryReadme: true,
useTabs: false
const result = treeMarkdownGenerator.getMarkdownForTree(tree, {
endOfLine,
options: {
linkToSubdirectoryReadme: true,
useTabs: false
}
});

@@ -53,5 +59,8 @@

test("it should allow using tabs instead of spaces", () => {
const result = treeMarkdownGenerator.getMarkdownForTree(tree, endOfLine, {
linkToSubdirectoryReadme: false,
useTabs: true
const result = treeMarkdownGenerator.getMarkdownForTree(tree, {
endOfLine,
options: {
linkToSubdirectoryReadme: false,
useTabs: true
}
});

@@ -92,7 +101,9 @@

treeIncludingFolderDescription,
endOfLine,
{
linkToSubdirectoryReadme: false,
subdirectoryDescriptionOnNewLine: false,
useTabs: false
endOfLine,
options: {
linkToSubdirectoryReadme: false,
subdirectoryDescriptionOnNewLine: false,
useTabs: false
}
}

@@ -114,7 +125,9 @@ );

treeIncludingFolderDescription,
endOfLine,
{
linkToSubdirectoryReadme: false,
subdirectoryDescriptionOnNewLine: true,
useTabs: false
endOfLine,
options: {
linkToSubdirectoryReadme: false,
subdirectoryDescriptionOnNewLine: true,
useTabs: false
}
}

@@ -121,0 +134,0 @@ );

@@ -12,6 +12,6 @@ "use strict";

function writeTreeToMainReadme(tree, endOfLine, options) {
function writeTreeToMainReadme(tree, environment) {
const mainReadmePath = pathUtils.getAbsolutePath("README.md");
const currentContents = fs.readFileSync(mainReadmePath, { encoding: "utf-8" });
const markdownForTree = treeMarkdownGenerator.getMarkdownForTree(tree, endOfLine, options);
const markdownForTree = treeMarkdownGenerator.getMarkdownForTree(tree, environment);

@@ -24,3 +24,3 @@ let newContents;

markdownForTree,
endOfLine
environment
);

@@ -34,3 +34,3 @@ } catch (error) {

function writeTreesForDirectories(mainTree, endOfLine, options, logger) {
function writeTreesForDirectories(mainTree, environment) {
for (const treeNode of mainTree) {

@@ -42,5 +42,3 @@ if (treeNode.isDirectory) {

treeNode.children,
endOfLine,
options,
logger
environment
);

@@ -51,4 +49,4 @@ }

function writeTreesForDirectory(pathParts, name, treeForDirectory, endOfLine, options, logger) {
writeTreeToDirectoryReadme(pathParts, name, treeForDirectory, endOfLine, options, logger);
function writeTreesForDirectory(pathParts, name, treeForDirectory, environment) {
writeTreeToDirectoryReadme(pathParts, name, treeForDirectory, environment);

@@ -61,5 +59,3 @@ for (const treeNode of treeForDirectory) {

treeNode.children,
endOfLine,
options,
logger
environment
);

@@ -70,3 +66,3 @@ }

function writeTreeToDirectoryReadme(pathParts, name, treeForDirectory, endOfLine, options, logger) {
function writeTreeToDirectoryReadme(pathParts, name, treeForDirectory, environment) {
const filePathParts = [...pathParts, "README.md"];

@@ -82,7 +78,3 @@ const relativeFilePath = path.join(...filePathParts);

const markdownForTree = treeMarkdownGenerator.getMarkdownForTree(
treeForDirectory,
endOfLine,
options
);
const markdownForTree = treeMarkdownGenerator.getMarkdownForTree(treeForDirectory, environment);

@@ -93,7 +85,7 @@ const newContents = fileContents.getNewDirectoryReadmeContents(

markdownForTree,
endOfLine
environment
);
logger(`Writing to ${absoluteFilePath}`);
environment.logger(`Writing to ${absoluteFilePath}`);
fs.writeFileSync(absoluteFilePath, newContents, { encoding: "utf-8" });
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc