@datalackey/update-markdown-toc 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,21 +2,22 @@
2
2
 
3
3
  <!-- TOC:START -->
4
4
  - [update-markdown-toc](#update-markdown-toc)
5
- - [Introduction](#introduction)
6
- - [Installation](#installation)
7
- - [Usage](#usage)
8
- - [Using npx (recommended)](#using-npx-recommended)
9
- - [Using npm scripts](#using-npm-scripts)
10
- - [Using a direct path (advanced)](#using-a-direct-path-advanced)
11
- - [Options](#options)
12
- - [TOC Markers](#toc-markers)
13
- - [Usage Scenarios](#usage-scenarios)
14
- - [As Part of code/test/debug Work Flow](#as-part-of-codetestdebug-work-flow)
15
- - [Continuous Integration (CI)](#continuous-integration--ci)
16
- - [Recursively Traversing a Folder Hierarchy to Process all files vs. Single File Processing](#recursively-traversing-a-folder-hierarchy-to-process-all-files-vs-single-file-processing)
17
- - [Single-File Processing (Strict Mode)](#single-file-processing-strict-mode)
18
- - [Recursive Folder Traversal (Lenient Mode)](#recursive-folder-traversal-lenient-mode)
19
- - [Guidelines For Project Contributors](#guidelines-for-project-contributors)
5
+ - [Introduction](#introduction)
6
+ - [Why not Some Other Markdown TOC Generator ?](#why-not-some-other-markdown-toc-generator-)
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [Using npx (recommended)](#using-npx-recommended)
10
+ - [Using npm scripts](#using-npm-scripts)
11
+ - [Using a direct path (advanced)](#using-a-direct-path-advanced)
12
+ - [Options](#options)
13
+ - [TOC Markers](#toc-markers)
14
+ - [Usage Scenarios](#usage-scenarios)
15
+ - [As Part of code/test/debug Work Flow](#as-part-of-codetestdebug-work-flow)
16
+ - [Continuous Integration](#continuous-integration)
17
+ - [Recursively Traversing a Folder Hierarchy to Process all files vs. Single File Processing](#recursively-traversing-a-folder-hierarchy-to-process-all-files-vs-single-file-processing)
18
+ - [Single-File Processing (Strict Mode)](#single-file-processing-strict-mode)
19
+ - [Recursive Folder Traversal (Lenient Mode)](#recursive-folder-traversal-lenient-mode)
20
+ - [Guidelines For Project Contributors](#guidelines-for-project-contributors)
20
21
  <!-- TOC:END -->
21
22
 
22
23
  ## Introduction
@@ -29,6 +30,18 @@ A Node.js command-line **documentation helper** which automatically:
29
30
  - avoids gratuitous reformatting or changes of any kind outside of regions marked by the aforementioned [TOC markers](#toc-markers)
30
31
  - avoids updating files when the generated TOC is already correct
31
32
  - provides a `--check` mode which flags Markdown files with stale TOCs (intended for CI)
33
+ - generates TOC links with GitHub’s Markdown renderer.
34
+
35
+
36
+
37
+ ## Why not Some Other Markdown TOC Generator ?
38
+
39
+ Most Markdown TOC tools (e.g., [markdown-toc](https://github.com/jonschlinkert/markdown-toc),
40
+ [md-toc-cli](https://github.com/eugene-khyst/md-toc-cli))
41
+ operate on a **single file at a time**, a mode which our tool also supports.
42
+ But we are aware of no other alternative which supports our tool's main distinguishing feature:
43
+ the ability to search for, check and update all Markdown documents within an entire folder hierarchy.
44
+
32
45
 
33
46
 
34
47
 
@@ -103,6 +116,10 @@ Options:
103
116
  -h, --help Show this help message and exit
104
117
  ```
105
118
 
119
+ When using --check, a target file or a recursive folder must be specified
120
+ explicitly. Unlike normal operation, --check does not default to README.md.
121
+
122
+
106
123
  ## TOC Markers
107
124
 
108
125
  The tool operates only on files containing **both** start and end markers,
@@ -184,23 +201,17 @@ In the case of the latter mode, we assume some files may not yet have TOC marker
184
201
  #### Single-File Processing (Strict Mode)
185
202
 
186
203
 
187
- When a single Markdown file is explicitly specified (or when the default README.md is used),
204
+ When a single Markdown file is explicitly specified (or when the default README.md is used and --check not specified),
188
205
  the tool operates in strict mode.
189
206
  In this mode, any of the following conditions cause an immediate error and a non-zero exit code:
190
207
 
191
208
  - file does not exist, or cannot be read (e.g. due to permissions).
192
- - file does not contain both TOC delimiters (<!-- TOC:START --> and <!-- TOC:END -->).
209
+ - file does not contain both TOC delimiters ({!-- TOC:START --} and {!-- TOC:END --}).
210
+ (See [TOC markers](#toc-markers) section about how we describe markers in this guide.)
193
211
  - file is stale (i.e. the existing TOC differs from the generated TOC).
194
- - file contains TOC delimiters but no Markdown headings are found from which a TOC can be generated.
212
+ - file contains TOC delimiters but no Markdown headings are found from which a TOC can be generated.
195
213
 
196
214
 
197
-
198
- ```md
199
- {!-- TOC:START --}
200
- {!-- TOC:END --}
201
-
202
- ```
203
-
204
215
  If either marker is missing, the tool prints an error message and exits with a non-zero status code.
205
216
 
206
217
 
package/bin/parseCli.js CHANGED
@@ -89,5 +89,9 @@ function printHelp() {
89
89
  -q, --quiet Suppress all non-error output
90
90
  -d, --debug Print debug diagnostics to stderr
91
91
  -h, --help Show this help message and exit
92
+
93
+ When using --check, a target file or a recursive folder must be specified
94
+ explicitly. Unlike normal operation, --check does not default to README.md.
95
+
92
96
  `);
93
97
  }
@@ -3,6 +3,7 @@
3
3
  import fs from "fs";
4
4
  import path from "path";
5
5
  import { parseCli } from "./parseCli.js";
6
+ import GithubSlugger from "github-slugger";
6
7
 
7
8
  /* ============================================================
8
9
  * Constants
@@ -39,6 +40,10 @@ function debugLog(msg) {
39
40
  * Helpers
40
41
  * ============================================================ */
41
42
 
43
+ function detectLineEnding(text) {
44
+ return text.includes("\r\n") ? "\r\n" : "\n";
45
+ }
46
+
42
47
  function collectMarkdownFiles(dir) {
43
48
  const results = [];
44
49
 
@@ -56,6 +61,8 @@ function collectMarkdownFiles(dir) {
56
61
  }
57
62
 
58
63
  function generateTOC(content) {
64
+ const lineEnding = detectLineEnding(content);
65
+
59
66
  const hasStart = content.includes(START);
60
67
  const hasEnd = content.includes(END);
61
68
 
@@ -72,28 +79,27 @@ function generateTOC(content) {
72
79
  const startIndex = content.indexOf(START);
73
80
  const endIndex = content.indexOf(END);
74
81
 
75
- const before = content.slice(0, startIndex + START.length);
76
- const after = content.slice(endIndex);
82
+ const before = content.slice(0, startIndex);
83
+ const after = content.slice(endIndex + END.length);
77
84
 
85
+ // Preserve exactly one line boundary for parsing
78
86
  const contentWithoutTOC =
79
- content.slice(0, startIndex) +
80
- content.slice(endIndex + END.length);
87
+ before.replace(/\s*$/, "") +
88
+ lineEnding +
89
+ after.replace(/^\s*/, "");
81
90
 
82
- const lines = contentWithoutTOC.split("\n");
91
+ const lines = contentWithoutTOC.split(lineEnding);
83
92
  const headings = [];
84
93
 
94
+ const slugger = new GithubSlugger();
95
+
85
96
  for (const line of lines) {
86
97
  const m = /^(#{1,6})\s+(.*)$/.exec(line);
87
98
  if (!m) continue;
88
99
 
89
100
  const level = m[1].length;
90
101
  const title = m[2].trim();
91
-
92
- const anchor = title
93
- .toLowerCase()
94
- .replace(/[^\w\s-]/g, "")
95
- .replace(/\s/g, "-")
96
- .replace(/^-|-$/g, "");
102
+ const anchor = slugger.slug(title);
97
103
 
98
104
  headings.push({ level, title, anchor });
99
105
  }
@@ -108,10 +114,22 @@ function generateTOC(content) {
108
114
  return `${indent}- [${h.title}](#${h.anchor})`;
109
115
  });
110
116
 
111
- const tocBlock = "\n" + tocLines.join("\n") + "\n";
112
- return before + tocBlock + after;
117
+ const tocBlock =
118
+ lineEnding +
119
+ tocLines.join(lineEnding) +
120
+ lineEnding;
121
+
122
+ return (
123
+ before +
124
+ START +
125
+ tocBlock +
126
+ END +
127
+ after
128
+ );
113
129
  }
114
130
 
131
+
132
+
115
133
  /* ============================================================
116
134
  * File processing
117
135
  * ============================================================ */
@@ -135,7 +153,7 @@ function processFile(filePath) {
135
153
  debugLog("result: skipped (no markers)");
136
154
  return { status: "skipped" };
137
155
  }
138
- throw err; // single-file mode → hard error
156
+ throw err;
139
157
  }
140
158
  throw err;
141
159
  }
@@ -165,15 +183,11 @@ function maybePrintStatus(status, filePath) {
165
183
  if (quiet) return;
166
184
 
167
185
  if (checkMode) {
168
- // In --check mode we ALWAYS report stale files (unless --quiet).
169
- // This makes CI output actionable without requiring --verbose.
170
186
  if (status === "stale") {
171
187
  console.log(`Stale: ${filePath}`);
172
188
  return;
173
189
  }
174
190
 
175
- // In recursive mode, files without markers are intentionally ignored.
176
- // They should not cause failures and do not need reporting by default.
177
191
  if (status === "skipped") {
178
192
  if (verbose) {
179
193
  console.log(`Skipped (no markers): ${filePath}`);
@@ -207,7 +221,6 @@ function maybePrintStatus(status, filePath) {
207
221
  }
208
222
  }
209
223
 
210
-
211
224
  /* ============================================================
212
225
  * Execution
213
226
  * ============================================================ */
@@ -227,7 +240,7 @@ if (recursivePath) {
227
240
  }
228
241
 
229
242
  files = collectMarkdownFiles(resolved);
230
- files.sort(); // deterministic order
243
+ files.sort();
231
244
  } else {
232
245
  const resolved = path.resolve(
233
246
  process.cwd(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalackey/update-markdown-toc",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Auto-generate Table of Contents for a Markdown file (or files, recursively from a top level folder)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "homepage": "https://github.com/datalackey/build-tools/tree/main/javascript/update-markdown-toc",
39
39
  "dependencies": {
40
+ "github-slugger": "^2.0.0",
40
41
  "ts-dedent": "^2.2.0"
41
42
  }
42
43
  }