@datalackey/update-markdown-toc 1.1.13 → 1.3.0

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
@@ -32,7 +32,7 @@
32
32
  - [Design Goals and Philosophy](#design-goals-and-philosophy)
33
33
  - [Packaging, Publishing, and Inter-relationship with Other Plugins](#packaging-publishing-and-inter-relationship-with-other-plugins)
34
34
  - [Known Limitations](#known-limitations)
35
- - [Guidelines For Project Contributors](#guidelines-for-project-contributors)
35
+ - [Contributing and Releasing](#contributing-and-releasing)
36
36
  <!-- TOC:END -->
37
37
 
38
38
 
@@ -40,14 +40,14 @@
40
40
 
41
41
  A Node.js command-line **documentation helper** which automatically:
42
42
 
43
- - generates Table of Contents (TOC) blocks for Markdown files
43
+ - generates Table of Contents (TOC) blocks for Markdown files (using GitHub's Markdown renderer)
44
44
  - operates on either a single file, or recursively finds all `*.md` files from a root path
45
45
  - regenerates TOCs from headings, targeting only regions explicitly marked with [TOC markers](#toc-markers)
46
46
  - avoids gratuitous reformatting or changes of any kind outside of regions marked by the aforementioned [TOC markers](#toc-markers)
47
47
  - avoids updating files when the generated TOC is already correct
48
48
  - provides a `--check` mode which flags Markdown files with stale TOCs (intended for CI)
49
- - generates TOC links with GitHub’s Markdown renderer.
50
-
49
+ - validates intra-document links (i.e., those between Markdown docs in the repo (including #fragments, image paths)
50
+ - validates external HTTP/HTTPS links, with configurable timeout
51
51
 
52
52
 
53
53
  ## Why not Some Other Markdown TOC Generator ?
@@ -316,11 +316,6 @@ When such errors occur, the tool prints an error message and exits non-zero with
316
316
 
317
317
  When combined with `--verbose`, skipped files (Markdown files without start/end region markers) are reported explicitly. For example:
318
318
 
319
- ```bash
320
- update-markdown-toc --recursive docs/ --verbose
321
-
322
-
323
-
324
319
  ```bash
325
320
  update-markdown-toc --recursive docs/ --verbose
326
321
  ```
@@ -355,7 +350,7 @@ The intended workflow is:
355
350
  This package is one component of a small ecosystem of JavaScript tooling plugins maintained as individual npm packages in this repository.
356
351
  The versioning and release of these packages is governed by a coordinated release policy, and
357
352
  the packages adhere to common design and architectural principles policies
358
- that are more completely described [here](../README.md).
353
+ that are more completely described [here](../../README.md).
359
354
 
360
355
  ## Known Limitations
361
356
 
@@ -369,7 +364,9 @@ In practice this affects only headings with inline code, bold, or italic syntax.
369
364
  Plain-text headings are unaffected. A fix to unify both paths is planned for a
370
365
  future release.
371
366
 
372
- ## Guidelines For Project Contributors
367
+ ## Contributing and Releasing
373
368
 
374
- Contributors to the project should consult [this document](docs/CONTRIBUTING.md)
369
+ For development setup, build workflow, and release procedures (including how to
370
+ trigger a publish via Changesets), see
371
+ [CONTRIBUTING.md](../docs/CONTRIBUTING.md).
375
372
 
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { runCli } from "@datalackey/tooling-core"
4
- import { TocFileProcessor } from "../dist/engine/TocFileProcessor.js"
5
- import { descriptor } from "../dist/cli/descriptor.js"
3
+ import { runCli } from "@datalackey/tooling-core";
4
+ import { TocFileProcessor } from "../dist/engine/TocFileProcessor.js";
5
+ import { descriptor } from "../dist/cli/descriptor.js";
6
6
 
7
7
  await runCli({
8
8
  descriptor: descriptor,
9
- processor: new TocFileProcessor()
10
- })
9
+ processor: new TocFileProcessor(),
10
+ });
@@ -0,0 +1,2 @@
1
+ import type { RunConfig } from "@datalackey/tooling-core";
2
+ export type TocRunConfig = RunConfig;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,2 @@
1
- import type { RunConfig } from "@datalackey/tooling-core";
2
1
  import type { PluginDescriptor } from "@datalackey/tooling-core";
3
- export declare const descriptor: PluginDescriptor<RunConfig>;
2
+ export declare const descriptor: PluginDescriptor;
@@ -1,40 +1,12 @@
1
- import { parseBooleanOption, parseNumberOption } from "@datalackey/tooling-core";
2
- const DEFAULT_LINK_TIMEOUT_MS = 3000;
1
+ import { runLinkValidation } from "@datalackey/tooling-core";
3
2
  export const descriptor = {
4
3
  name: "update-markdown-toc",
5
4
  description: "Auto-generate Table of Contents for Markdown files",
6
- options: [
7
- {
8
- flag: "--no-external-link-check",
9
- description: "Skip external link validation in check mode"
10
- },
11
- {
12
- flag: "-n",
13
- description: "Skip external link validation in check mode (short form)"
14
- },
15
- {
16
- flag: "--link-timeout-ms",
17
- description: "Timeout in milliseconds for external link requests (default: 3000)",
18
- requiresValue: true,
19
- valueName: "ms"
20
- },
21
- {
22
- flag: "-l",
23
- description: "Timeout in milliseconds for external link requests (short form)",
24
- requiresValue: true,
25
- valueName: "ms"
5
+ options: [],
6
+ async afterRun(files, config) {
7
+ if (config.runMode !== "check") {
8
+ return;
26
9
  }
27
- ],
28
- parseOptions(standard, passthrough) {
29
- const noExternalCheck = parseBooleanOption("--no-external-link-check", passthrough) ||
30
- parseBooleanOption("-n", passthrough);
31
- const timeoutMs = parseNumberOption("--link-timeout-ms", passthrough) ??
32
- parseNumberOption("-l", passthrough) ??
33
- DEFAULT_LINK_TIMEOUT_MS;
34
- return {
35
- ...standard,
36
- validateExternalLinks: noExternalCheck ? false : standard.validateExternalLinks,
37
- linkTimeoutMs: timeoutMs
38
- };
39
- }
10
+ await runLinkValidation(files, config);
11
+ },
40
12
  };
@@ -1,5 +1,5 @@
1
- import type { FileProcessor } from "@datalackey/tooling-core";
1
+ import type { FileProcessor, ProcessingStatus } from "@datalackey/tooling-core";
2
2
  import type { RunConfig } from "@datalackey/tooling-core";
3
3
  export declare class TocFileProcessor implements FileProcessor<RunConfig> {
4
- process(filePath: string, config: RunConfig): import("@datalackey/tooling-core").ProcessingStatus;
4
+ process(filePath: string, config: RunConfig): ProcessingStatus;
5
5
  }
@@ -1,6 +1,21 @@
1
1
  import GithubSlugger from "github-slugger";
2
2
  const START = "<!-- TOC:START -->";
3
3
  const END = "<!-- TOC:END -->";
4
+ function stripFencedLines(lines) {
5
+ let inFence = false;
6
+ const result = [];
7
+ for (const line of lines) {
8
+ if (line.startsWith("```")) {
9
+ inFence = !inFence;
10
+ continue;
11
+ }
12
+ if (!inFence)
13
+ result.push(line);
14
+ }
15
+ if (inFence)
16
+ throw new Error("Unclosed code fence (```) in document");
17
+ return result;
18
+ }
4
19
  function detectLineEnding(text) {
5
20
  return text.includes("\r\n") ? "\r\n" : "\n";
6
21
  }
@@ -18,13 +33,11 @@ export function generateTOC(content) {
18
33
  const endIndex = content.indexOf(END);
19
34
  const before = content.slice(0, startIndex);
20
35
  const after = content.slice(endIndex + END.length);
21
- const contentWithoutTOC = before.replace(/\s*$/, "") +
22
- lineEnding +
23
- after.replace(/^\s*/, "");
36
+ const contentWithoutTOC = before.replace(/\s*$/, "") + lineEnding + after.replace(/^\s*/, "");
24
37
  const lines = contentWithoutTOC.split(lineEnding);
25
38
  const headings = [];
26
39
  const slugger = new GithubSlugger();
27
- for (const line of lines) {
40
+ for (const line of stripFencedLines(lines)) {
28
41
  const m = /^(#{1,6})\s+(.*)$/.exec(line);
29
42
  if (!m)
30
43
  continue;
@@ -41,8 +54,6 @@ export function generateTOC(content) {
41
54
  const indent = " ".repeat(h.level - minLevel);
42
55
  return `${indent}- [${h.title}](#${h.anchor})`;
43
56
  });
44
- const tocBlock = lineEnding +
45
- tocLines.join(lineEnding) +
46
- lineEnding;
57
+ const tocBlock = lineEnding + tocLines.join(lineEnding) + lineEnding;
47
58
  return before + START + tocBlock + END + after;
48
59
  }
@@ -29,8 +29,8 @@ export function processFile(filePath, config) {
29
29
  return "unchanged";
30
30
  }
31
31
  if (config.runMode === "check") {
32
- debugLog(config, `processFile: stale filePath=${absolutePath}`);
33
- return "stale";
32
+ debugLog(config, `processFile: needsUpdate filePath=${absolutePath}`);
33
+ return "needsUpdate";
34
34
  }
35
35
  fs.writeFileSync(filePath, updated, "utf8");
36
36
  debugLog(config, `processFile: updated filePath=${absolutePath}`);
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@datalackey/update-markdown-toc",
3
- "version": "1.1.13",
3
+ "version": "1.3.0",
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",
7
7
  "private": false,
8
8
  "scripts": {
9
9
  "build": "tsc -p tsconfig.json",
10
- "test": "bash scripts/run-all-tests.sh"
10
+ "test": "npx vitest run --config vitest.config.ts && bash scripts/run-all-tests.sh"
11
11
  },
12
12
  "bin": {
13
13
  "update-markdown-toc": "./bin/update-markdown-toc.js"
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^18.19.130",
53
- "jest": "^30.2.0",
54
- "typescript": "^5.9.3"
53
+ "typescript": "^5.9.3",
54
+ "vitest": "^1.0.0"
55
55
  }
56
56
  }