remark-flexible-toc 1.2.4 → 1.2.6

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 ipikuka
3
+ Copyright (c) 2026 ipikuka
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,12 +1,11 @@
1
- ### [Become a sponsor](https://github.com/sponsors/ipikuka) 🚀
1
+ # remark-flexible-toc
2
2
 
3
- If you find **`remark-flexible-toc`** useful in your projects, consider supporting my work.
4
- Your sponsorship means a lot 💖
3
+ **A robust Next.js newsletter `Next.js Weekly` is sponsoring me** 💖
4
+ [![NextjsWeekly banner](./assets/next-js-weekly.png)](https://nextjsweekly.com/)
5
5
 
6
- Be the **first sponsor** and get featured here and on [my sponsor wall](https://github.com/sponsors/ipikuka).
7
- Thank you for supporting open source! 🙌
6
+ A warm thanks 🙌 to [@ErfanEbrahimnia](https://github.com/ErfanEbrahimnia), [@recepkyk](https://github.com/recepkyk), and [@LSeaburg](https://github.com/LSeaburg) for the [support](https://github.com/sponsors/ipikuka) 💖
8
7
 
9
- # remark-flexible-toc
8
+ ---
10
9
 
11
10
  [![npm version][badge-npm-version]][url-npm-package]
12
11
  [![npm downloads][badge-npm-download]][url-npm-package]
@@ -292,7 +291,7 @@ type TocItem = {
292
291
  ```
293
292
 
294
293
  > [!NOTE]
295
- > If there is a remark plugin before **`remark-flexible-toc`** in the plugin chain, which provides custom id for headings like **`remark-heading-id`**, that custom id takes precedence for `href`.
294
+ > If there is a remark plugin before **`remark-flexible-toc`** in the plugin chain like **`remark-heading-id`** or **`remark-custom-header-id`**, which provides custom id for headings, that custom id takes precedence for `href`.
296
295
 
297
296
  **`remark-flexible-toc`** uses **`github-slugger`** internally for producing unique links. Then, it is possible you to use [**rehype-slug**][rehype-slug] (forIDs on headings) and [**rehype-autolink-headings**][rehype-autolink-headings] (for anchors that link-to-self) because they use the same **`github-slugger`**.
298
297
 
@@ -396,6 +395,14 @@ Use of **`remark-flexible-toc`** does not involve rehype (hast) or user content
396
395
 
397
396
  I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to have a look my plugins.
398
397
 
398
+ ### Support My Work ([become a sponsor](https://github.com/sponsors/ipikuka) 🚀)
399
+
400
+ If you find **`remark-flexible-toc`** or any of my projects is useful and helpful, please consider supporting my work. Your sponsorship means a lot to me and keeps these projects alive and updated! 💖
401
+
402
+ My sponsors are going to be featured at the very top of the page and proudly displayed on my [Sponsor Wall](https://github.com/sponsors/ipikuka).
403
+
404
+ Thank you for supporting open source! 🙌
405
+
399
406
  ### My Remark Plugins
400
407
 
401
408
  - [`remark-flexible-code-titles`](https://www.npmjs.com/package/remark-flexible-code-titles)
@@ -412,6 +419,8 @@ I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to
412
419
  – Remark plugin to expose the table of contents via `vfile.data` or via an option reference
413
420
  - [`remark-mdx-remove-esm`](https://www.npmjs.com/package/remark-mdx-remove-esm)
414
421
  – Remark plugin to remove import and/or export statements (mdxjsEsm)
422
+ - [`remark-mdx-remove-expressions`](https://www.npmjs.com/package/remark-mdx-remove-expressions)
423
+ – Remark plugin to remove MDX expressions within curlybraces {} in MDX content
415
424
 
416
425
  ### My Rehype Plugins
417
426
 
@@ -441,6 +450,15 @@ I like to contribute the Unified / Remark / MDX ecosystem, so I recommend you to
441
450
  - [`recma-mdx-interpolate`](https://www.npmjs.com/package/recma-mdx-interpolate)
442
451
  – Recma plugin to enable interpolation of identifiers wrapped in curly braces within the `alt`, `src`, `href`, and `title` attributes of markdown link and image syntax in MDX.
443
452
 
453
+ ### My Unist Utils and Unified Plugins
454
+
455
+ I also build low-level utilities and plugins for the Unified ecosystem that can be used across Remark, Rehype, Recma, and other unist-based abstract syntax trees (ASTs).
456
+
457
+ - [`unist-util-find-between`](https://www.npmjs.com/package/unist-util-find-between)
458
+ – Unist utility to find the nodes between two nodes.
459
+ - [`unified-log-tree`](https://www.npmjs.com/package/unified-log-tree)
460
+ – Unified plugin to log abstract syntax trees (ASTs) for debugging without mutating.
461
+
444
462
  ## License
445
463
 
446
464
  [MIT License](./LICENSE) © ipikuka
@@ -1,5 +1,5 @@
1
- import { type Plugin } from "unified";
2
- import { type Root } from "mdast";
1
+ import type { Plugin } from "unified";
2
+ import type { Root } from "mdast";
3
3
  export type Prettify<T> = {
4
4
  [K in keyof T]: T[K];
5
5
  } & {};
@@ -12,7 +12,9 @@ export type TocItem = {
12
12
  depth: HeadingDepth;
13
13
  numbering: number[];
14
14
  parent: HeadingParent;
15
- data?: Record<string, unknown>;
15
+ data?: {
16
+ [PropertyName: string]: string | number | boolean | (string | number)[] | null | undefined;
17
+ };
16
18
  };
17
19
  /**
18
20
  * tocName (default: "toc") - the key name which is attached into vfile.data
@@ -36,3 +38,4 @@ export type FlexibleTocOptions = {
36
38
  };
37
39
  declare const RemarkFlexibleToc: Plugin<[FlexibleTocOptions?], Root>;
38
40
  export default RemarkFlexibleToc;
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAKlC,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AAExD,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAExF,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,YAAY,GACZ,oBAAoB,GACpB,UAAU,GACV,WAAW,GACX,mBAAmB,CAAC;AAExB,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEjD,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE;QACL,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;KAC5F,CAAC;CACH,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,UAAU,CAAC,EAAE,YAAY,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,EAAE,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,SAAS,CAAC;CAC1C,CAAC;AA4DF,QAAA,MAAM,iBAAiB,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAmF1D,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
@@ -21,7 +21,7 @@ function addNumbering(arr) {
21
21
  for (let i = 0; i < arr.length; i++) {
22
22
  const tocItem = arr[i];
23
23
  const depth = tocItem.depth;
24
- let numbering = [];
24
+ let numbering;
25
25
  const prevObj = i > 0 ? arr[i - 1] : undefined;
26
26
  const prevDepth = prevObj ? prevObj.depth : undefined;
27
27
  const prevNumbering = prevObj ? prevObj.numbering : undefined;
@@ -78,9 +78,10 @@ const RemarkFlexibleToc = (options) => {
78
78
  return CONTINUE;
79
79
  // Other remark plugins can store custom data in node.data.hProperties
80
80
  // I omitted node.data.hName and node.data.hChildren since not related with toc
81
- const data = _node.data?.hProperties
82
- ? { ..._node.data.hProperties }
83
- : undefined;
81
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
82
+ // @ts-ignore, vscode resolves the type but tsc build raises an error awkwardly, saying "hProperties" does not exist in "HeadingData".
83
+ const hProperties = _node.data?.hProperties;
84
+ const data = hProperties ? { ...hProperties } : undefined;
84
85
  if (data?.["id"])
85
86
  href = `#${data["id"]}`;
86
87
  tocItems.push({
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin } from \"unified\";\nimport type { Root } from \"mdast\";\nimport { visit, CONTINUE } from \"unist-util-visit\";\nimport GithubSlugger from \"github-slugger\";\nimport { toString } from \"mdast-util-to-string\";\n\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n\nexport type PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\nexport type HeadingParent =\n | \"root\"\n | \"blockquote\"\n | \"footnoteDefinition\"\n | \"listItem\"\n | \"container\"\n | \"mdxJsxFlowElement\";\n\nexport type HeadingDepth = 1 | 2 | 3 | 4 | 5 | 6;\n\nexport type TocItem = {\n value: string;\n href: string;\n depth: HeadingDepth;\n numbering: number[];\n parent: HeadingParent;\n data?: {\n [PropertyName: string]: string | number | boolean | (string | number)[] | null | undefined;\n };\n};\n\n/**\n * tocName (default: \"toc\") - the key name which is attached into vfile.data\n * tocRef (default: []) — another way of exposing the tocItems\n * maxDepth (default: 6) — max heading depth to include in the table of contents; this is inclusive: when set to 3, level three headings are included\n * skipLevels (default: [1]) — disallowed heading levels, by default the article h1 is not expected to be in the TOC\n * skipParents (default: []) — disallow headings to be children of certain node types, (the \"root\" can not be skipped)\n * exclude — headings to skip, wrapped in new RegExp('^(' + value + ')$', 'i'); any heading matching this expression will not be present in the table of contents\n * prefix - the text that will be attached to headings as prefix, like \"text-prefix-\"\n * callback - It is a callback function to take the array of toc items as an argument\n */\nexport type FlexibleTocOptions = {\n tocName?: string;\n tocRef?: TocItem[];\n maxDepth?: HeadingDepth;\n skipLevels?: HeadingDepth[];\n skipParents?: Exclude<HeadingParent, \"root\">[];\n exclude?: string | string[];\n prefix?: string;\n callback?: (toc: TocItem[]) => undefined;\n};\n\ntype PartiallyRequiredFlexibleTocOptions = Prettify<\n PartiallyRequired<\n FlexibleTocOptions,\n \"tocName\" | \"tocRef\" | \"maxDepth\" | \"skipLevels\" | \"skipParents\"\n >\n>;\n\nconst DEFAULT_SETTINGS: FlexibleTocOptions = {\n tocName: \"toc\",\n tocRef: [],\n maxDepth: 6,\n skipLevels: [1],\n skipParents: [],\n};\n\n/**\n * adds numberings to the TOC items.\n * why \"number[]\"? It is because up to you joining with dot or dash or slicing the first number (reserved for h1)\n *\n * [1]\n * [1,1]\n * [1,2]\n * [1,2,1]\n */\nfunction addNumbering(arr: TocItem[]) {\n for (let i = 0; i < arr.length; i++) {\n const tocItem = arr[i];\n const depth = tocItem.depth;\n\n let numbering: number[];\n\n const prevObj = i > 0 ? arr[i - 1] : undefined;\n const prevDepth = prevObj ? prevObj.depth : undefined;\n const prevNumbering = prevObj ? prevObj.numbering : undefined;\n\n if (!prevNumbering || !prevDepth) {\n numbering = Array.from({ length: depth }, () => 1);\n } else if (depth === prevDepth) {\n numbering = [...prevNumbering];\n numbering[depth - 1]++;\n } else if (depth > prevDepth) {\n numbering = [\n ...prevNumbering,\n ...(Array.from(\n { length: depth - prevDepth }, // if depth is more bigger than prevDepth, put more \"1\" inside the array\n () => 1,\n ) as HeadingDepth[]),\n ];\n } else {\n // if (depth < prevDepth)\n numbering = prevNumbering.slice(0, depth);\n numbering[depth - 1]++;\n }\n\n tocItem.numbering = numbering;\n }\n}\n\nconst RemarkFlexibleToc: Plugin<[FlexibleTocOptions?], Root> = (options) => {\n const settings = Object.assign(\n {},\n DEFAULT_SETTINGS,\n options,\n ) as PartiallyRequiredFlexibleTocOptions;\n\n const exludeRegexFilter =\n settings.exclude &&\n (Array.isArray(settings.exclude)\n ? new RegExp(\"^(\" + settings.exclude.join(\"|\") + \")$\", \"i\")\n : new RegExp(\"^(\" + settings.exclude + \")$\", \"i\"));\n\n return (tree, file) => {\n const slugger = new GithubSlugger();\n const tocItems: TocItem[] = [];\n\n visit(tree, \"heading\", (_node, _index, _parent) => {\n /* v8 ignore next -- @preserve */\n if (!_parent || typeof _index === \"undefined\") return;\n\n const depth = _node.depth;\n const value = toString(_node, { includeImageAlt: false });\n let href = `#${settings.prefix ?? \"\"}${slugger.slug(value)}`;\n const parent = _parent.type;\n\n // maxDepth check\n if (depth > settings.maxDepth) return CONTINUE;\n\n // skipLevels check\n if (settings.skipLevels.includes(depth)) return CONTINUE;\n\n // skipParents check\n if (parent !== \"root\" && settings.skipParents.includes(parent)) return CONTINUE;\n\n // exclude check\n if (exludeRegexFilter && exludeRegexFilter.test(value)) return CONTINUE;\n\n // Other remark plugins can store custom data in node.data.hProperties\n // I omitted node.data.hName and node.data.hChildren since not related with toc\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore, vscode resolves the type but tsc build raises an error awkwardly, saying \"hProperties\" does not exist in \"HeadingData\".\n const hProperties = _node.data?.hProperties;\n const data = hProperties ? { ...hProperties } : undefined;\n\n if (data?.[\"id\"]) href = `#${data[\"id\"]}`;\n\n tocItems.push({\n value,\n href,\n depth,\n numbering: [],\n parent,\n ...(data && { data }),\n });\n\n return CONTINUE;\n });\n\n addNumbering(tocItems);\n\n // it is allowed to modify the TOC in the callback\n settings.callback?.(tocItems);\n\n // method - 1 for exposing the data via vfile.data **************************\n\n // other plugins are not allowed to mutate the exposed TOC\n // The spreading is slower than push but need to fresh copy\n file.data[settings.tocName] = [...tocItems];\n\n // method - 2 for exposing the data via reference array *********************\n\n if (options?.tocRef) {\n // prevent dublication if the plugin is called more than once\n settings.tocRef.length = 0;\n\n tocItems.forEach((tocItem) => {\n // the tocRef is not allowed to mutate the vfile.data.toc\n settings.tocRef.push(tocItem);\n });\n }\n };\n};\n\nexport default RemarkFlexibleToc;\n"]}
package/package.json CHANGED
@@ -1,26 +1,31 @@
1
1
  {
2
2
  "name": "remark-flexible-toc",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "description": "Remark plugin to expose the table of contents via Vfile.data or via an option reference",
5
5
  "type": "module",
6
- "exports": "./dist/esm/index.js",
7
- "main": "./dist/esm/index.js",
8
- "types": "./dist/esm/index.d.ts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
9
15
  "scripts": {
10
- "build": "rimraf dist && tsc --build && type-coverage",
16
+ "build": "rimraf dist && tsc --build tsconfig.build.json && type-coverage",
11
17
  "format": "npm run prettier && npm run lint",
12
18
  "prettier": "prettier --write .",
13
19
  "lint": "eslint .",
14
- "test": "vitest --watch=false",
20
+ "test": "vitest run",
15
21
  "test:watch": "vitest",
16
- "test:file": "vitest without_options.spec.ts",
22
+ "test:file": "vitest test.plugin.spec.ts",
23
+ "test-coverage": "vitest run --coverage",
17
24
  "prepack": "npm run build",
18
- "prepublishOnly": "npm run test && npm run format && npm run test-coverage",
19
- "test-coverage": "vitest run --coverage"
25
+ "prepublishOnly": "npm run format && npm run test-coverage"
20
26
  },
21
27
  "files": [
22
28
  "dist/",
23
- "src/",
24
29
  "LICENSE",
25
30
  "README.md"
26
31
  ],
@@ -48,35 +53,34 @@
48
53
  "url": "https://github.com/ipikuka/remark-flexible-toc/issues"
49
54
  },
50
55
  "devDependencies": {
51
- "@eslint/js": "^9.39.1",
52
- "@types/node": "^24.10.0",
53
- "@types/remark-heading-id": "^1.0.0",
54
- "@vitest/coverage-v8": "^4.0.6",
55
- "@vitest/eslint-plugin": "^1.4.0",
56
- "dedent": "^1.7.0",
57
- "eslint": "^9.39.1",
56
+ "@eslint/js": "^10.0.1",
57
+ "@types/node": "^24.12.4",
58
+ "@vitest/coverage-v8": "^4.1.7",
59
+ "@vitest/eslint-plugin": "^1.6.18",
60
+ "dedent": "^1.7.2",
61
+ "eslint": "^10.4.0",
58
62
  "eslint-config-prettier": "^10.1.8",
59
- "eslint-plugin-prettier": "^5.5.4",
60
- "globals": "^16.5.0",
61
- "prettier": "^3.6.2",
63
+ "eslint-plugin-prettier": "^5.5.5",
64
+ "globals": "^17.6.0",
65
+ "prettier": "^3.8.3",
62
66
  "rehype-format": "^5.0.1",
63
67
  "rehype-stringify": "^10.0.1",
68
+ "remark-custom-header-id": "^1.0.0",
64
69
  "remark-gfm": "^4.0.1",
65
- "remark-heading-id": "^1.0.1",
66
70
  "remark-parse": "^11.0.0",
67
71
  "remark-rehype": "^11.1.2",
68
- "rimraf": "^6.1.0",
72
+ "rimraf": "^6.1.3",
69
73
  "type-coverage": "^2.29.7",
70
74
  "typescript": "^5.9.3",
71
- "typescript-eslint": "^8.46.3",
75
+ "typescript-eslint": "^8.60.0",
72
76
  "unified": "^11.0.5",
73
- "vitest": "^4.0.6"
77
+ "vitest": "^4.1.7"
74
78
  },
75
79
  "dependencies": {
76
80
  "@types/mdast": "^4.0.4",
77
81
  "github-slugger": "^2.0.0",
78
82
  "mdast-util-to-string": "^4.0.0",
79
- "unist-util-visit": "^5.0.0"
83
+ "unist-util-visit": "^5.1.0"
80
84
  },
81
85
  "peerDependencies": {
82
86
  "unified": "^11"
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"root":["../../src/index.ts"],"version":"5.9.3"}
package/src/index.ts DELETED
@@ -1,194 +0,0 @@
1
- import { type Plugin } from "unified";
2
- import { type Root, type HeadingData } from "mdast";
3
- import { visit, CONTINUE } from "unist-util-visit";
4
- import GithubSlugger from "github-slugger";
5
- import { toString } from "mdast-util-to-string";
6
-
7
- export type Prettify<T> = { [K in keyof T]: T[K] } & {};
8
-
9
- export type PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
10
-
11
- export type HeadingParent =
12
- | "root"
13
- | "blockquote"
14
- | "footnoteDefinition"
15
- | "listItem"
16
- | "container"
17
- | "mdxJsxFlowElement";
18
-
19
- export type HeadingDepth = 1 | 2 | 3 | 4 | 5 | 6;
20
-
21
- export type TocItem = {
22
- value: string;
23
- href: string;
24
- depth: HeadingDepth;
25
- numbering: number[];
26
- parent: HeadingParent;
27
- data?: Record<string, unknown>;
28
- };
29
-
30
- /**
31
- * tocName (default: "toc") - the key name which is attached into vfile.data
32
- * tocRef (default: []) — another way of exposing the tocItems
33
- * maxDepth (default: 6) — max heading depth to include in the table of contents; this is inclusive: when set to 3, level three headings are included
34
- * skipLevels (default: [1]) — disallowed heading levels, by default the article h1 is not expected to be in the TOC
35
- * skipParents (default: []) — disallow headings to be children of certain node types, (the "root" can not be skipped)
36
- * exclude — headings to skip, wrapped in new RegExp('^(' + value + ')$', 'i'); any heading matching this expression will not be present in the table of contents
37
- * prefix - the text that will be attached to headings as prefix, like "text-prefix-"
38
- * callback - It is a callback function to take the array of toc items as an argument
39
- */
40
- export type FlexibleTocOptions = {
41
- tocName?: string;
42
- tocRef?: TocItem[];
43
- maxDepth?: HeadingDepth;
44
- skipLevels?: HeadingDepth[];
45
- skipParents?: Exclude<HeadingParent, "root">[];
46
- exclude?: string | string[];
47
- prefix?: string;
48
- callback?: (toc: TocItem[]) => undefined;
49
- };
50
-
51
- type PartiallyRequiredFlexibleTocOptions = Prettify<
52
- PartiallyRequired<
53
- FlexibleTocOptions,
54
- "tocName" | "tocRef" | "maxDepth" | "skipLevels" | "skipParents"
55
- >
56
- >;
57
-
58
- const DEFAULT_SETTINGS: FlexibleTocOptions = {
59
- tocName: "toc",
60
- tocRef: [],
61
- maxDepth: 6,
62
- skipLevels: [1],
63
- skipParents: [],
64
- };
65
-
66
- type ExtendedHeadingData = HeadingData & { hProperties: Record<string, unknown> };
67
-
68
- /**
69
- * adds numberings to the TOC items.
70
- * why "number[]"? It is because up to you joining with dot or dash or slicing the first number (reserved for h1)
71
- *
72
- * [1]
73
- * [1,1]
74
- * [1,2]
75
- * [1,2,1]
76
- */
77
- function addNumbering(arr: TocItem[]) {
78
- for (let i = 0; i < arr.length; i++) {
79
- const tocItem = arr[i];
80
- const depth = tocItem.depth;
81
-
82
- let numbering: number[] = [];
83
-
84
- const prevObj = i > 0 ? arr[i - 1] : undefined;
85
- const prevDepth = prevObj ? prevObj.depth : undefined;
86
- const prevNumbering = prevObj ? prevObj.numbering : undefined;
87
-
88
- if (!prevNumbering || !prevDepth) {
89
- numbering = Array.from({ length: depth }, () => 1);
90
- } else if (depth === prevDepth) {
91
- numbering = [...prevNumbering];
92
- numbering[depth - 1]++;
93
- } else if (depth > prevDepth) {
94
- numbering = [
95
- ...prevNumbering,
96
- ...(Array.from(
97
- { length: depth - prevDepth }, // if depth is more bigger than prevDepth, put more "1" inside the array
98
- () => 1,
99
- ) as HeadingDepth[]),
100
- ];
101
- } else {
102
- // if (depth < prevDepth)
103
- numbering = prevNumbering.slice(0, depth);
104
- numbering[depth - 1]++;
105
- }
106
-
107
- tocItem.numbering = numbering;
108
- }
109
- }
110
-
111
- const RemarkFlexibleToc: Plugin<[FlexibleTocOptions?], Root> = (options) => {
112
- const settings = Object.assign(
113
- {},
114
- DEFAULT_SETTINGS,
115
- options,
116
- ) as PartiallyRequiredFlexibleTocOptions;
117
-
118
- const exludeRegexFilter =
119
- settings.exclude &&
120
- (Array.isArray(settings.exclude)
121
- ? new RegExp("^(" + settings.exclude.join("|") + ")$", "i")
122
- : new RegExp("^(" + settings.exclude + ")$", "i"));
123
-
124
- return (tree, file) => {
125
- const slugger = new GithubSlugger();
126
- const tocItems: TocItem[] = [];
127
-
128
- visit(tree, "heading", (_node, _index, _parent) => {
129
- /* v8 ignore next -- @preserve */
130
- if (!_parent || typeof _index === "undefined") return;
131
-
132
- const depth = _node.depth;
133
- const value = toString(_node, { includeImageAlt: false });
134
- let href = `#${settings.prefix ?? ""}${slugger.slug(value)}`;
135
- const parent = _parent.type;
136
-
137
- // maxDepth check
138
- if (depth > settings.maxDepth) return CONTINUE;
139
-
140
- // skipLevels check
141
- if (settings.skipLevels.includes(depth)) return CONTINUE;
142
-
143
- // skipParents check
144
- if (parent !== "root" && settings.skipParents.includes(parent)) return CONTINUE;
145
-
146
- // exclude check
147
- if (exludeRegexFilter && exludeRegexFilter.test(value)) return CONTINUE;
148
-
149
- // Other remark plugins can store custom data in node.data.hProperties
150
- // I omitted node.data.hName and node.data.hChildren since not related with toc
151
- const data = (_node.data as ExtendedHeadingData)?.hProperties
152
- ? { ...(_node.data as ExtendedHeadingData).hProperties }
153
- : undefined;
154
-
155
- if (data?.["id"]) href = `#${data["id"]}`;
156
-
157
- tocItems.push({
158
- value,
159
- href,
160
- depth,
161
- numbering: [],
162
- parent,
163
- ...(data && { data }),
164
- });
165
-
166
- return CONTINUE;
167
- });
168
-
169
- addNumbering(tocItems);
170
-
171
- // it is allowed to modify the TOC in the callback
172
- settings.callback?.(tocItems);
173
-
174
- // method - 1 for exposing the data via vfile.data **************************
175
-
176
- // other plugins are not allowed to mutate the exposed TOC
177
- // The spreading is slower than push but need to fresh copy
178
- file.data[settings.tocName] = [...tocItems];
179
-
180
- // method - 2 for exposing the data via reference array *********************
181
-
182
- if (options?.tocRef) {
183
- // prevent dublication if the plugin is called more than once
184
- settings.tocRef.length = 0;
185
-
186
- tocItems.forEach((tocItem) => {
187
- // the tocRef is not allowed to mutate the vfile.data.toc
188
- settings.tocRef.push(tocItem);
189
- });
190
- }
191
- };
192
- };
193
-
194
- export default RemarkFlexibleToc;