@substrate-system/markdown-toc 1.3.7 → 1.4.1

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
@@ -11,6 +11,10 @@ This is a fork of [jonschlinkert/markdown-toc](https://github.com/jonschlinkert/
11
11
  <details>
12
12
  <summary><h2>Contents</h2></summary>
13
13
 
14
+ - [Install](#install)
15
+ - [Quickstart](#quick-start)
16
+ - [CLI](#cli)
17
+ - [SPA](#spa)
14
18
  - [Highlights](#highlights)
15
19
  - [Usage](#usage)
16
20
  - [API](#api)
@@ -32,24 +36,12 @@ This is a fork of [jonschlinkert/markdown-toc](https://github.com/jonschlinkert/
32
36
 
33
37
  ## Install
34
38
 
35
- Install with [npm](https://www.npmjs.com/):
39
+ Install with [npm](https://www.npmjs.com/).
36
40
 
37
41
  ```sh
38
- npm i @substrate-system/markdown-toc
42
+ npm i -D @substrate-system/markdown-toc
39
43
  ```
40
44
 
41
- # Sponsors
42
-
43
- Thanks to the following companies, organizations, and individuals for supporting the ongoing maintenance and development of markdown-toc! [Become a Sponsor](https://github.com/sponsors/jonschlinkert) to add your logo to this README, or any of [my other projects](https://github.com/jonschlinkert?tab=repositories&q=&type=&language=&sort=stargazers)
44
-
45
- ## Gold Sponsors
46
-
47
- | [<img src="https://github.com/jonschlinkert/clone-deep/assets/383994/98036489-2cae-48a2-8d29-7dec58ea05c4" alt="https://jaake.tech/" width="100"/>](https://jaake.tech/) |
48
- |:---:|
49
- | [https://jaake.tech/](https://jaake.tech/) |
50
-
51
- <br />
52
-
53
45
  ## Quick Start
54
46
 
55
47
  Assuming you want to add a TOC to README.md:
@@ -106,6 +98,74 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](
106
98
  (to specify a tab, use the bash-escaped $'\t')
107
99
  ```
108
100
 
101
+ ## SPA
102
+
103
+ Create a table of contents in a client-side-rendered app.
104
+
105
+ Pass `toc` an element, or a ref to an element, instead of a markdown
106
+ string. It reads the headings inside that element, gives each heading
107
+ without an `id` a slug `id`, and returns the same result object as the
108
+ markdown version (see [API](#api)). Options like `maxdepth` work the same
109
+ way, and `json` only includes headings within `maxdepth`.
110
+
111
+ ```ts
112
+ import { toc, type TocHeading } from '@substrate-system/markdown-toc'
113
+ import { html } from 'htm/preact'
114
+ import { type FunctionComponent } from 'preact'
115
+ import { useLayoutEffect, useRef, useState } from 'preact/hooks'
116
+
117
+ export const Main:FunctionComponent = function () {
118
+ const div = useRef<HTMLDivElement>(null)
119
+ const [tocJson, setTocJson] = useState<TocHeading[]>([])
120
+
121
+ // after the first render, so the headings are in the DOM
122
+ useLayoutEffect(() => {
123
+ if (!div.current) return
124
+ setTocJson(toc(div, { maxdepth: 3 }).json)
125
+ }, [])
126
+
127
+ return html`<div ref=${div} class="example">
128
+ <h2>Contents</h2>
129
+ <ul>
130
+ ${tocJson.map((t) => {
131
+ return html`<li><a href="#${t.slug}">${t.content}</a></li>`
132
+ })}
133
+ </ul>
134
+ <h2>Example</h2>
135
+ <h3>Level 3</h3>
136
+ hello
137
+ <h4>Level 4</h4>
138
+ </div>`
139
+ }
140
+ ```
141
+
142
+ ### Preact
143
+
144
+ Because I like `preact`, this includes a ready-to-use preact component.
145
+ `preact` is an optional peer dependency.
146
+
147
+ `<TOC />` renders a `<nav>` with a nested list of links to the headings
148
+ that come *after* it in the document, so a heading above it (like the
149
+ `Contents` title here) is left out. It takes the same options as `toc`,
150
+ as props. The list updates when headings are added, removed, or changed.
151
+
152
+ ```tsx
153
+ import { TOC } from '@substrate-system/markdown-toc/preact'
154
+ import { type FunctionComponent } from 'preact'
155
+
156
+ export const Main:FunctionComponent = function () {
157
+ return <main>
158
+ <details>
159
+ <summary><h2>Contents</h2></summary>
160
+ <TOC maxdepth={3} />
161
+ </details>
162
+
163
+ <h2>Hello Table of Contents</h2>
164
+ <h3>more testing</h3>
165
+ </main>
166
+ }
167
+ ```
168
+
109
169
  ## Highlights
110
170
 
111
171
  **Features**
@@ -126,7 +186,7 @@ _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](
126
186
  ## Usage
127
187
 
128
188
  ```js
129
- import {toc} from '@substrate-system/markdown-toc'
189
+ import { toc } from '@substrate-system/markdown-toc'
130
190
 
131
191
  toc('# One\n\n# Two').content;
132
192
  // Results in:
@@ -147,7 +207,7 @@ To allow customization of the output, an object is returned with the following p
147
207
  Use as a [remarkable](https://github.com/jonschlinkert/remarkable) plugin.
148
208
 
149
209
  ```js
150
- import {Remarkable, toc} from '@substrate-system/markdown-toc'
210
+ import { Remarkable, toc } from '@substrate-system/markdown-toc'
151
211
 
152
212
  function render(str, options) {
153
213
  return new Remarkable()
@@ -175,7 +235,7 @@ Results in:
175
235
  Object for creating a custom TOC.
176
236
 
177
237
  ```js
178
- import {toc} from '@substrate-system/markdown-toc'
238
+ import { toc } from '@substrate-system/markdown-toc'
179
239
 
180
240
  toc('# AAA\n## BBB\n### CCC\nfoo').json;
181
241
 
@@ -187,9 +247,12 @@ toc('# AAA\n## BBB\n### CCC\nfoo').json;
187
247
 
188
248
  ### toc.insert
189
249
 
190
- Insert a table of contents immediately after an _opening_ `<!-- toc -->` code comment, or replace an existing TOC if both an _opening_ comment and a _closing_ comment (`<!-- tocstop -->`) are found.
250
+ Insert a table of contents immediately after an _opening_ `<!-- toc -->` code
251
+ comment, or replace an existing TOC if both an _opening_ comment and
252
+ a _closing_ comment (`<!-- tocstop -->`) are found.
191
253
 
192
- _(This strategy works well since code comments in markdown are hidden when viewed as HTML, like when viewing a README on GitHub README for example)._
254
+ _(This strategy works well since code comments in markdown are hidden when_
255
+ _viewed as HTML, like when viewing a README on GitHub README for example)._
193
256
 
194
257
  **Example**
195
258
 
package/dist/dom.js ADDED
@@ -0,0 +1,80 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { renderBullets, slugify, titleize } from "./markdown-toc.js";
4
+ const HEADINGS = "h1, h2, h3, h4, h5, h6";
5
+ function domToc(target, options = {}) {
6
+ const root = resolveTarget(target);
7
+ return headingsToc(Array.from(root.querySelectorAll(HEADINGS)), options);
8
+ }
9
+ __name(domToc, "domToc");
10
+ function headingsToc(elements, options = {}) {
11
+ const maxdepth = options.maxdepth ?? 6;
12
+ const headings = elements.filter((el) => headingLevel(el) <= maxdepth);
13
+ const used = new Set(headings.map((el) => el.id).filter(Boolean));
14
+ const seen = {};
15
+ const json = [];
16
+ const tokens = [];
17
+ headings.forEach((el, i) => {
18
+ const content2 = (el.textContent ?? "").trim();
19
+ const seenCount = seen[content2] ?? 0;
20
+ seen[content2] = seenCount + 1;
21
+ if (!el.id) {
22
+ el.id = uniqueSlug(content2, seenCount, used, options);
23
+ used.add(el.id);
24
+ }
25
+ const slug = el.id;
26
+ const lvl = headingLevel(el);
27
+ const text = titleize(content2, options);
28
+ json.push({ content: content2, slug, lvl, i, seen: seenCount });
29
+ tokens.push({
30
+ type: "inline",
31
+ content: `[${text}](#${encodeURIComponent(slug)})`,
32
+ lvl,
33
+ i,
34
+ seen: seenCount,
35
+ slug
36
+ });
37
+ });
38
+ const highest = tokens.reduce((min, token) => {
39
+ return Math.min(min, token.lvl ?? 0);
40
+ }, tokens.length ? Number.POSITIVE_INFINITY : 0);
41
+ const content = renderBullets(
42
+ options.firsth1 === false ? tokens.slice(1) : tokens,
43
+ { ...options, highest }
44
+ ) + (options.append ?? "");
45
+ return { content, highest, json, tokens };
46
+ }
47
+ __name(headingsToc, "headingsToc");
48
+ function headingLevel(el) {
49
+ return Number(el.tagName.slice(1));
50
+ }
51
+ __name(headingLevel, "headingLevel");
52
+ function resolveTarget(target) {
53
+ if ("current" in target) {
54
+ if (!target.current) {
55
+ throw new TypeError("toc: the ref has no element yet");
56
+ }
57
+ return target.current;
58
+ }
59
+ return target;
60
+ }
61
+ __name(resolveTarget, "resolveTarget");
62
+ function uniqueSlug(content, seenCount, used, options) {
63
+ let num = seenCount;
64
+ let slug = slugify(content, { ...options, num });
65
+ while (used.has(slug)) {
66
+ num++;
67
+ const next = slugify(content, { ...options, num });
68
+ if (next === slug) break;
69
+ slug = next;
70
+ }
71
+ return slug;
72
+ }
73
+ __name(uniqueSlug, "uniqueSlug");
74
+ export {
75
+ HEADINGS,
76
+ domToc,
77
+ headingLevel,
78
+ headingsToc
79
+ };
80
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/dom.ts"],
4
+ "sourcesContent": ["import { renderBullets, slugify, titleize } from './markdown-toc.js'\nimport type {\n TocHeading,\n TocOptions,\n TocResult,\n TocToken,\n} from './types.js'\n\n/**\n * Something we can read headings from: an element, a document, or a\n * ref object like the one returned by preact's `useRef`.\n */\nexport type TocTarget = ParentNode|{ readonly current:ParentNode|null }\n\nexport const HEADINGS = 'h1, h2, h3, h4, h5, h6'\n\n/**\n * Create a table of contents from the headings inside `target`.\n * Headings without an `id` get one, so the generated links work.\n */\nexport function domToc (\n target:TocTarget,\n options:Readonly<TocOptions> = {},\n):TocResult {\n const root = resolveTarget(target)\n return headingsToc(Array.from(root.querySelectorAll(HEADINGS)), options)\n}\n\n/**\n * Create a table of contents from a list of heading elements.\n */\nexport function headingsToc (\n elements:ReadonlyArray<Element>,\n options:Readonly<TocOptions> = {},\n):TocResult {\n const maxdepth = options.maxdepth ?? 6\n const headings = elements.filter(el => headingLevel(el) <= maxdepth)\n const used = new Set(headings.map(el => el.id).filter(Boolean))\n const seen:Record<string, number> = {}\n const json:Array<TocHeading> = []\n const tokens:Array<TocToken> = []\n\n headings.forEach((el, i) => {\n const content = (el.textContent ?? '').trim()\n const seenCount = seen[content] ?? 0\n seen[content] = seenCount + 1\n\n if (!el.id) {\n el.id = uniqueSlug(content, seenCount, used, options)\n used.add(el.id)\n }\n\n const slug = el.id\n const lvl = headingLevel(el)\n const text = titleize(content, options)\n json.push({ content, slug, lvl, i, seen:seenCount })\n tokens.push({\n type:'inline',\n content:`[${text}](#${encodeURIComponent(slug)})`,\n lvl,\n i,\n seen:seenCount,\n slug,\n })\n })\n\n const highest = tokens.reduce((min, token) => {\n return Math.min(min, token.lvl ?? 0)\n }, tokens.length ? Number.POSITIVE_INFINITY : 0)\n const content = renderBullets(\n options.firsth1 === false ? tokens.slice(1) : tokens,\n { ...options, highest }\n ) + (options.append ?? '')\n\n return { content, highest, json, tokens }\n}\n\n/**\n * Get the heading level from the tag name, `h3` -> 3.\n */\nexport function headingLevel (el:Element):number {\n return Number(el.tagName.slice(1))\n}\n\nfunction resolveTarget (target:TocTarget):ParentNode {\n if ('current' in target) {\n if (!target.current) {\n throw new TypeError('toc: the ref has no element yet')\n }\n return target.current\n }\n return target\n}\n\n/**\n * Slugify the heading text, then bump the number suffix until the slug\n * does not collide with an id that is already in use.\n */\nfunction uniqueSlug (\n content:string,\n seenCount:number,\n used:ReadonlySet<string>,\n options:Readonly<TocOptions>,\n):string {\n let num = seenCount\n let slug = slugify(content, { ...options, num })\n while (used.has(slug)) {\n num++\n const next = slugify(content, { ...options, num })\n // a custom slugify, or `slugify:false`, may ignore `num`\n if (next === slug) break\n slug = next\n }\n return slug\n}\n"],
5
+ "mappings": ";;AAAA,SAAS,eAAe,SAAS,gBAAgB;AAc1C,MAAM,WAAW;AAMjB,SAAS,OACZ,QACA,UAA+B,CAAC,GACxB;AACR,QAAM,OAAO,cAAc,MAAM;AACjC,SAAO,YAAY,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC,GAAG,OAAO;AAC3E;AANgB;AAWT,SAAS,YACZ,UACA,UAA+B,CAAC,GACxB;AACR,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,WAAW,SAAS,OAAO,QAAM,aAAa,EAAE,KAAK,QAAQ;AACnE,QAAM,OAAO,IAAI,IAAI,SAAS,IAAI,QAAM,GAAG,EAAE,EAAE,OAAO,OAAO,CAAC;AAC9D,QAAM,OAA8B,CAAC;AACrC,QAAM,OAAyB,CAAC;AAChC,QAAM,SAAyB,CAAC;AAEhC,WAAS,QAAQ,CAAC,IAAI,MAAM;AACxB,UAAMA,YAAW,GAAG,eAAe,IAAI,KAAK;AAC5C,UAAM,YAAY,KAAKA,QAAO,KAAK;AACnC,SAAKA,QAAO,IAAI,YAAY;AAE5B,QAAI,CAAC,GAAG,IAAI;AACR,SAAG,KAAK,WAAWA,UAAS,WAAW,MAAM,OAAO;AACpD,WAAK,IAAI,GAAG,EAAE;AAAA,IAClB;AAEA,UAAM,OAAO,GAAG;AAChB,UAAM,MAAM,aAAa,EAAE;AAC3B,UAAM,OAAO,SAASA,UAAS,OAAO;AACtC,SAAK,KAAK,EAAE,SAAAA,UAAS,MAAM,KAAK,GAAG,MAAK,UAAU,CAAC;AACnD,WAAO,KAAK;AAAA,MACR,MAAK;AAAA,MACL,SAAQ,IAAI,IAAI,MAAM,mBAAmB,IAAI,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,MACA,MAAK;AAAA,MACL;AAAA,IACJ,CAAC;AAAA,EACL,CAAC;AAED,QAAM,UAAU,OAAO,OAAO,CAAC,KAAK,UAAU;AAC1C,WAAO,KAAK,IAAI,KAAK,MAAM,OAAO,CAAC;AAAA,EACvC,GAAG,OAAO,SAAS,OAAO,oBAAoB,CAAC;AAC/C,QAAM,UAAU;AAAA,IACZ,QAAQ,YAAY,QAAQ,OAAO,MAAM,CAAC,IAAI;AAAA,IAC9C,EAAE,GAAG,SAAS,QAAQ;AAAA,EAC1B,KAAK,QAAQ,UAAU;AAEvB,SAAO,EAAE,SAAS,SAAS,MAAM,OAAO;AAC5C;AA5CgB;AAiDT,SAAS,aAAc,IAAmB;AAC7C,SAAO,OAAO,GAAG,QAAQ,MAAM,CAAC,CAAC;AACrC;AAFgB;AAIhB,SAAS,cAAe,QAA6B;AACjD,MAAI,aAAa,QAAQ;AACrB,QAAI,CAAC,OAAO,SAAS;AACjB,YAAM,IAAI,UAAU,iCAAiC;AAAA,IACzD;AACA,WAAO,OAAO;AAAA,EAClB;AACA,SAAO;AACX;AARS;AAcT,SAAS,WACL,SACA,WACA,MACA,SACK;AACL,MAAI,MAAM;AACV,MAAI,OAAO,QAAQ,SAAS,EAAE,GAAG,SAAS,IAAI,CAAC;AAC/C,SAAO,KAAK,IAAI,IAAI,GAAG;AACnB;AACA,UAAM,OAAO,QAAQ,SAAS,EAAE,GAAG,SAAS,IAAI,CAAC;AAEjD,QAAI,SAAS,KAAM;AACnB,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAhBS;",
6
+ "names": ["content"]
7
+ }
package/dist/index.js CHANGED
@@ -13,8 +13,9 @@ import {
13
13
  titleize as titleizeHeading
14
14
  } from "./markdown-toc.js";
15
15
  import { insertMarkdown } from "./insert-markdown.js";
16
- function toc(markdown, options = {}) {
17
- return generateToc(markdown, options);
16
+ import { domToc } from "./dom.js";
17
+ function toc(source, options = {}) {
18
+ return typeof source === "string" ? generateToc(source, options) : domToc(source, options);
18
19
  }
19
20
  __name(toc, "toc");
20
21
  const utils = {
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import Remarkable from 'remarkable'\nimport {\n generatePlugin,\n generateToc,\n getHighest,\n getTitle,\n linkify as linkifyHeading,\n renderBullets,\n slugify as slugifyHeading,\n strip as stripHeading,\n titleize as titleizeHeading,\n} from './markdown-toc.js'\nimport { insertMarkdown } from './insert-markdown.js'\nimport type { TocOptions, TocResult, TocUtils } from './types.js'\n\nexport type * from './types.js'\nexport { Remarkable }\nexport {\n generatePlugin as plugin,\n generateToc,\n getHighest,\n getTitle,\n insertMarkdown,\n linkifyHeading as linkify,\n renderBullets,\n slugifyHeading as slugify,\n stripHeading as strip,\n titleizeHeading as titleize,\n}\nexport { renderBullets as bullets }\n\nexport function toc (\n markdown:string,\n options:Readonly<TocOptions> = {},\n):TocResult {\n return generateToc(markdown, options)\n}\n\nexport const utils:TocUtils = {\n Remarkable,\n getTitle,\n slugify:slugifyHeading,\n strip:stripHeading,\n titleize:titleizeHeading,\n}\n"],
5
- "mappings": ";;AAAA,OAAO,gBAAgB;AACvB;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,OACT;AACP,SAAS,sBAAsB;AAmBxB,SAAS,IACZ,UACA,UAA+B,CAAC,GACxB;AACR,SAAO,YAAY,UAAU,OAAO;AACxC;AALgB;AAOT,MAAM,QAAiB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,SAAQ;AAAA,EACR,OAAM;AAAA,EACN,UAAS;AACb;",
4
+ "sourcesContent": ["import Remarkable from 'remarkable'\nimport {\n generatePlugin,\n generateToc,\n getHighest,\n getTitle,\n linkify as linkifyHeading,\n renderBullets,\n slugify as slugifyHeading,\n strip as stripHeading,\n titleize as titleizeHeading,\n} from './markdown-toc.js'\nimport { insertMarkdown } from './insert-markdown.js'\nimport { domToc, type TocTarget } from './dom.js'\nimport type { TocOptions, TocResult, TocUtils } from './types.js'\n\nexport type * from './types.js'\nexport type { TocTarget }\nexport { Remarkable }\nexport {\n generatePlugin as plugin,\n generateToc,\n getHighest,\n getTitle,\n insertMarkdown,\n linkifyHeading as linkify,\n renderBullets,\n slugifyHeading as slugify,\n stripHeading as strip,\n titleizeHeading as titleize,\n}\nexport { renderBullets as bullets }\n\n/**\n * Create a table of contents from a markdown string, or, in a browser,\n * from the headings inside an element (or a ref to an element).\n */\nexport function toc (\n markdown:string,\n options?:Readonly<TocOptions>,\n):TocResult\nexport function toc (\n target:TocTarget,\n options?:Readonly<TocOptions>,\n):TocResult\nexport function toc (\n source:string|TocTarget,\n options:Readonly<TocOptions> = {},\n):TocResult {\n return typeof source === 'string' ?\n generateToc(source, options) :\n domToc(source, options)\n}\n\nexport const utils:TocUtils = {\n Remarkable,\n getTitle,\n slugify:slugifyHeading,\n strip:stripHeading,\n titleize:titleizeHeading,\n}\n"],
5
+ "mappings": ";;AAAA,OAAO,gBAAgB;AACvB;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,OACT;AACP,SAAS,sBAAsB;AAC/B,SAAS,cAA8B;AAgChC,SAAS,IACZ,QACA,UAA+B,CAAC,GACxB;AACR,SAAO,OAAO,WAAW,WACrB,YAAY,QAAQ,OAAO,IAC3B,OAAO,QAAQ,OAAO;AAC9B;AAPgB;AAST,MAAM,QAAiB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,SAAQ;AAAA,EACR,OAAM;AAAA,EACN,UAAS;AACb;",
6
6
  "names": []
7
7
  }
package/dist/meta.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "inputs": {
3
3
  "src/index.ts": {
4
- "bytes": 1028,
4
+ "bytes": 1507,
5
5
  "imports": [],
6
6
  "format": "esm"
7
7
  },
@@ -19,6 +19,16 @@
19
19
  "bytes": 1454,
20
20
  "imports": [],
21
21
  "format": "esm"
22
+ },
23
+ "src/dom.ts": {
24
+ "bytes": 3320,
25
+ "imports": [],
26
+ "format": "esm"
27
+ },
28
+ "src/preact.ts": {
29
+ "bytes": 3742,
30
+ "imports": [],
31
+ "format": "esm"
22
32
  }
23
33
  },
24
34
  "outputs": {
@@ -26,7 +36,7 @@
26
36
  "imports": [],
27
37
  "exports": [],
28
38
  "inputs": {},
29
- "bytes": 1510
39
+ "bytes": 2059
30
40
  },
31
41
  "dist/index.js": {
32
42
  "imports": [
@@ -44,6 +54,11 @@
44
54
  "path": "./insert-markdown.js",
45
55
  "kind": "import-statement",
46
56
  "external": true
57
+ },
58
+ {
59
+ "path": "./dom.js",
60
+ "kind": "import-statement",
61
+ "external": true
47
62
  }
48
63
  ],
49
64
  "exports": [
@@ -65,10 +80,10 @@
65
80
  "entryPoint": "src/index.ts",
66
81
  "inputs": {
67
82
  "src/index.ts": {
68
- "bytesInOutput": 543
83
+ "bytesInOutput": 629
69
84
  }
70
85
  },
71
- "bytes": 993
86
+ "bytes": 1079
72
87
  },
73
88
  "dist/cli.js.map": {
74
89
  "imports": [],
@@ -172,6 +187,74 @@
172
187
  }
173
188
  },
174
189
  "bytes": 1518
190
+ },
191
+ "dist/dom.js.map": {
192
+ "imports": [],
193
+ "exports": [],
194
+ "inputs": {},
195
+ "bytes": 5277
196
+ },
197
+ "dist/dom.js": {
198
+ "imports": [
199
+ {
200
+ "path": "./markdown-toc.js",
201
+ "kind": "import-statement",
202
+ "external": true
203
+ }
204
+ ],
205
+ "exports": [
206
+ "HEADINGS",
207
+ "domToc",
208
+ "headingLevel",
209
+ "headingsToc"
210
+ ],
211
+ "entryPoint": "src/dom.ts",
212
+ "inputs": {
213
+ "src/dom.ts": {
214
+ "bytesInOutput": 2254
215
+ }
216
+ },
217
+ "bytes": 2479
218
+ },
219
+ "dist/preact.js.map": {
220
+ "imports": [],
221
+ "exports": [],
222
+ "inputs": {},
223
+ "bytes": 6030
224
+ },
225
+ "dist/preact.js": {
226
+ "imports": [
227
+ {
228
+ "path": "preact",
229
+ "kind": "import-statement",
230
+ "external": true
231
+ },
232
+ {
233
+ "path": "preact/hooks",
234
+ "kind": "import-statement",
235
+ "external": true
236
+ },
237
+ {
238
+ "path": "./dom.js",
239
+ "kind": "import-statement",
240
+ "external": true
241
+ },
242
+ {
243
+ "path": "./markdown-toc.js",
244
+ "kind": "import-statement",
245
+ "external": true
246
+ }
247
+ ],
248
+ "exports": [
249
+ "TOC"
250
+ ],
251
+ "entryPoint": "src/preact.ts",
252
+ "inputs": {
253
+ "src/preact.ts": {
254
+ "bytesInOutput": 2680
255
+ }
256
+ },
257
+ "bytes": 2862
175
258
  }
176
259
  }
177
260
  }
package/dist/preact.js ADDED
@@ -0,0 +1,93 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { h } from "preact";
4
+ import { useLayoutEffect, useRef, useState } from "preact/hooks";
5
+ import { HEADINGS, headingsToc } from "./dom.js";
6
+ import { titleize } from "./markdown-toc.js";
7
+ const TOC = /* @__PURE__ */ __name(function TOC2(props) {
8
+ const { class: className, children: _children, ...options } = props;
9
+ const nav = useRef(null);
10
+ const latestOptions = useRef(options);
11
+ latestOptions.current = options;
12
+ const update = useRef(() => {
13
+ });
14
+ const [headings, setHeadings] = useState([]);
15
+ useLayoutEffect(() => {
16
+ const el = nav.current;
17
+ if (!el) return;
18
+ update.current = () => {
19
+ const next = headingsToc(
20
+ headingsAfter(el),
21
+ latestOptions.current
22
+ ).json;
23
+ setHeadings((prev) => isSame(prev, next) ? prev : next);
24
+ };
25
+ const observer = new MutationObserver(() => update.current());
26
+ observer.observe(el.ownerDocument.body, {
27
+ childList: true,
28
+ subtree: true,
29
+ characterData: true
30
+ });
31
+ return () => {
32
+ observer.disconnect();
33
+ update.current = () => {
34
+ };
35
+ };
36
+ }, []);
37
+ useLayoutEffect(() => update.current());
38
+ return h("nav", {
39
+ ref: nav,
40
+ class: className,
41
+ "aria-label": "Table of contents"
42
+ }, headings.length ? renderList(toTree(headings)) : null);
43
+ }, "TOC");
44
+ function headingsAfter(el) {
45
+ const all = Array.from(el.ownerDocument.querySelectorAll(HEADINGS));
46
+ return all.filter((heading) => {
47
+ const position = el.compareDocumentPosition(heading);
48
+ return Boolean(position & Node.DOCUMENT_POSITION_FOLLOWING) && !el.contains(heading);
49
+ });
50
+ }
51
+ __name(headingsAfter, "headingsAfter");
52
+ function toTree(headings) {
53
+ const roots = [];
54
+ const stack = [];
55
+ for (const heading of headings) {
56
+ const node = { heading, children: [] };
57
+ while (stack.length && stack[stack.length - 1].heading.lvl >= heading.lvl) {
58
+ stack.pop();
59
+ }
60
+ const parent = stack[stack.length - 1];
61
+ if (parent) parent.children.push(node);
62
+ else roots.push(node);
63
+ stack.push(node);
64
+ }
65
+ return roots;
66
+ }
67
+ __name(toTree, "toTree");
68
+ function renderList(nodes) {
69
+ return h("ul", null, nodes.map(({ heading, children }) => {
70
+ return h(
71
+ "li",
72
+ { key: heading.slug },
73
+ h(
74
+ "a",
75
+ { href: "#" + encodeURIComponent(heading.slug) },
76
+ titleize(heading.content)
77
+ ),
78
+ children.length ? renderList(children) : null
79
+ );
80
+ }));
81
+ }
82
+ __name(renderList, "renderList");
83
+ function isSame(a, b) {
84
+ return a.length === b.length && a.every((heading, i) => {
85
+ const other = b[i];
86
+ return heading.slug === other.slug && heading.content === other.content && heading.lvl === other.lvl;
87
+ });
88
+ }
89
+ __name(isSame, "isSame");
90
+ export {
91
+ TOC
92
+ };
93
+ //# sourceMappingURL=preact.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/preact.ts"],
4
+ "sourcesContent": ["import { h, type FunctionComponent, type VNode } from 'preact'\nimport { useLayoutEffect, useRef, useState } from 'preact/hooks'\nimport { HEADINGS, headingsToc } from './dom.js'\nimport { titleize } from './markdown-toc.js'\nimport type { TocHeading, TocOptions } from './types.js'\n\nexport type TocProps = TocOptions&{\n class?:string;\n}\n\ntype TocNode = {\n heading:TocHeading;\n children:TocNode[];\n}\n\n/**\n * Render a nested list of links to the headings that come after this\n * component in the document. Headings without an `id` get one. The list\n * updates when the document changes.\n */\nexport const TOC:FunctionComponent<TocProps> = function TOC (props) {\n const { class:className, children:_children, ...options } = props\n const nav = useRef<HTMLElement>(null)\n const latestOptions = useRef<TocOptions>(options)\n latestOptions.current = options\n const update = useRef<() => void>(() => {})\n const [headings, setHeadings] = useState<TocHeading[]>([])\n\n useLayoutEffect(() => {\n const el = nav.current\n if (!el) return\n\n update.current = () => {\n const next = headingsToc(\n headingsAfter(el),\n latestOptions.current\n ).json\n setHeadings(prev => isSame(prev, next) ? prev : next)\n }\n\n // Watch structure and text only. Adding an `id` is an attribute\n // change, so it does not trigger another update.\n const observer = new MutationObserver(() => update.current())\n observer.observe(el.ownerDocument.body, {\n childList:true,\n subtree:true,\n characterData:true,\n })\n\n return () => {\n observer.disconnect()\n update.current = () => {}\n }\n }, [])\n\n // re-check after every render, in case the options changed\n useLayoutEffect(() => update.current())\n\n return h('nav', {\n ref:nav,\n class:className,\n 'aria-label':'Table of contents',\n }, headings.length ? renderList(toTree(headings)) : null)\n}\n\n/**\n * Headings that come after `el` in document order.\n */\nfunction headingsAfter (el:Element):Element[] {\n const all = Array.from(el.ownerDocument.querySelectorAll(HEADINGS))\n return all.filter(heading => {\n const position = el.compareDocumentPosition(heading)\n return Boolean(position & Node.DOCUMENT_POSITION_FOLLOWING) &&\n !el.contains(heading)\n })\n}\n\n/**\n * Nest each heading under the closest previous heading with a lower\n * level.\n */\nfunction toTree (headings:ReadonlyArray<TocHeading>):TocNode[] {\n const roots:TocNode[] = []\n const stack:TocNode[] = []\n\n for (const heading of headings) {\n const node:TocNode = { heading, children:[] }\n while (stack.length && stack[stack.length - 1]!.heading.lvl >=\n heading.lvl) {\n stack.pop()\n }\n const parent = stack[stack.length - 1]\n if (parent) parent.children.push(node)\n else roots.push(node)\n stack.push(node)\n }\n\n return roots\n}\n\nfunction renderList (nodes:ReadonlyArray<TocNode>):VNode {\n return h('ul', null, nodes.map(({ heading, children }) => {\n return h('li', { key:heading.slug },\n h('a', { href:'#' + encodeURIComponent(heading.slug) },\n titleize(heading.content)),\n children.length ? renderList(children) : null\n )\n }))\n}\n\nfunction isSame (\n a:ReadonlyArray<TocHeading>,\n b:ReadonlyArray<TocHeading>,\n):boolean {\n return a.length === b.length && a.every((heading, i) => {\n const other = b[i]!\n return heading.slug === other.slug &&\n heading.content === other.content &&\n heading.lvl === other.lvl\n })\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["TOC"]
7
+ }
@@ -0,0 +1,23 @@
1
+ import type { TocOptions, TocResult } from './types.js';
2
+ /**
3
+ * Something we can read headings from: an element, a document, or a
4
+ * ref object like the one returned by preact's `useRef`.
5
+ */
6
+ export type TocTarget = ParentNode | {
7
+ readonly current: ParentNode | null;
8
+ };
9
+ export declare const HEADINGS = "h1, h2, h3, h4, h5, h6";
10
+ /**
11
+ * Create a table of contents from the headings inside `target`.
12
+ * Headings without an `id` get one, so the generated links work.
13
+ */
14
+ export declare function domToc(target: TocTarget, options?: Readonly<TocOptions>): TocResult;
15
+ /**
16
+ * Create a table of contents from a list of heading elements.
17
+ */
18
+ export declare function headingsToc(elements: ReadonlyArray<Element>, options?: Readonly<TocOptions>): TocResult;
19
+ /**
20
+ * Get the heading level from the tag name, `h3` -> 3.
21
+ */
22
+ export declare function headingLevel(el: Element): number;
23
+ //# sourceMappingURL=dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../src/dom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAER,UAAU,EACV,SAAS,EAEZ,MAAM,YAAY,CAAA;AAEnB;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAC;IAAE,QAAQ,CAAC,OAAO,EAAC,UAAU,GAAC,IAAI,CAAA;CAAE,CAAA;AAEvE,eAAO,MAAM,QAAQ,2BAA2B,CAAA;AAEhD;;;GAGG;AACH,wBAAgB,MAAM,CAClB,MAAM,EAAC,SAAS,EAChB,OAAO,GAAC,QAAQ,CAAC,UAAU,CAAM,GACnC,SAAS,CAGV;AAED;;GAEG;AACH,wBAAgB,WAAW,CACvB,QAAQ,EAAC,aAAa,CAAC,OAAO,CAAC,EAC/B,OAAO,GAAC,QAAQ,CAAC,UAAU,CAAM,GACnC,SAAS,CAyCV;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAE,EAAE,EAAC,OAAO,GAAE,MAAM,CAE/C"}
@@ -1,11 +1,18 @@
1
1
  import Remarkable from 'remarkable';
2
2
  import { generatePlugin, generateToc, getHighest, getTitle, linkify as linkifyHeading, renderBullets, slugify as slugifyHeading, strip as stripHeading, titleize as titleizeHeading } from './markdown-toc.js';
3
3
  import { insertMarkdown } from './insert-markdown.js';
4
+ import { type TocTarget } from './dom.js';
4
5
  import type { TocOptions, TocResult, TocUtils } from './types.js';
5
6
  export type * from './types.js';
7
+ export type { TocTarget };
6
8
  export { Remarkable };
7
9
  export { generatePlugin as plugin, generateToc, getHighest, getTitle, insertMarkdown, linkifyHeading as linkify, renderBullets, slugifyHeading as slugify, stripHeading as strip, titleizeHeading as titleize, };
8
10
  export { renderBullets as bullets };
11
+ /**
12
+ * Create a table of contents from a markdown string, or, in a browser,
13
+ * from the headings inside an element (or a ref to an element).
14
+ */
9
15
  export declare function toc(markdown: string, options?: Readonly<TocOptions>): TocResult;
16
+ export declare function toc(target: TocTarget, options?: Readonly<TocOptions>): TocResult;
10
17
  export declare const utils: TocUtils;
11
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EACH,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,OAAO,IAAI,cAAc,EACzB,aAAa,EACb,OAAO,IAAI,cAAc,EACzB,KAAK,IAAI,YAAY,EACrB,QAAQ,IAAI,eAAe,EAC9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEjE,mBAAmB,YAAY,CAAA;AAC/B,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EACH,cAAc,IAAI,MAAM,EACxB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,cAAc,IAAI,OAAO,EACzB,aAAa,EACb,cAAc,IAAI,OAAO,EACzB,YAAY,IAAI,KAAK,EACrB,eAAe,IAAI,QAAQ,GAC9B,CAAA;AACD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,CAAA;AAEnC,wBAAgB,GAAG,CACf,QAAQ,EAAC,MAAM,EACf,OAAO,GAAC,QAAQ,CAAC,UAAU,CAAM,GACnC,SAAS,CAEV;AAED,eAAO,MAAM,KAAK,EAAC,QAMlB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAA;AACnC,OAAO,EACH,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,OAAO,IAAI,cAAc,EACzB,aAAa,EACb,OAAO,IAAI,cAAc,EACzB,KAAK,IAAI,YAAY,EACrB,QAAQ,IAAI,eAAe,EAC9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAEjE,mBAAmB,YAAY,CAAA;AAC/B,YAAY,EAAE,SAAS,EAAE,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,CAAA;AACrB,OAAO,EACH,cAAc,IAAI,MAAM,EACxB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,cAAc,IAAI,OAAO,EACzB,aAAa,EACb,cAAc,IAAI,OAAO,EACzB,YAAY,IAAI,KAAK,EACrB,eAAe,IAAI,QAAQ,GAC9B,CAAA;AACD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,CAAA;AAEnC;;;GAGG;AACH,wBAAgB,GAAG,CACf,QAAQ,EAAC,MAAM,EACf,OAAO,CAAC,EAAC,QAAQ,CAAC,UAAU,CAAC,GAC/B,SAAS,CAAA;AACX,wBAAgB,GAAG,CACf,MAAM,EAAC,SAAS,EAChB,OAAO,CAAC,EAAC,QAAQ,CAAC,UAAU,CAAC,GAC/B,SAAS,CAAA;AAUX,eAAO,MAAM,KAAK,EAAC,QAMlB,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { type FunctionComponent } from 'preact';
2
+ import type { TocOptions } from './types.js';
3
+ export type TocProps = TocOptions & {
4
+ class?: string;
5
+ };
6
+ /**
7
+ * Render a nested list of links to the headings that come after this
8
+ * component in the document. Headings without an `id` get one. The list
9
+ * updates when the document changes.
10
+ */
11
+ export declare const TOC: FunctionComponent<TocProps>;
12
+ //# sourceMappingURL=preact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preact.d.ts","sourceRoot":"","sources":["../../src/preact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,KAAK,iBAAiB,EAAc,MAAM,QAAQ,CAAA;AAI9D,OAAO,KAAK,EAAc,UAAU,EAAE,MAAM,YAAY,CAAA;AAExD,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAC;IAC9B,KAAK,CAAC,EAAC,MAAM,CAAC;CACjB,CAAA;AAOD;;;;GAIG;AACH,eAAO,MAAM,GAAG,EAAC,iBAAiB,CAAC,QAAQ,CA2C1C,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@substrate-system/markdown-toc",
3
3
  "description": "Generate a markdown TOC (table of contents) with Remarkable.",
4
- "version": "1.3.7",
4
+ "version": "1.4.1",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -20,8 +20,10 @@
20
20
  },
21
21
  "scripts": {
22
22
  "lint": "eslint \"./**/*.{ts,js}\"",
23
- "test": "esbuild test/index.ts --platform=node --format=esm --keep-names --bundle --external:gray-matter --external:remarkable --external:diacritics-map | node --input-type=module | tap-spec",
24
- "build-esm": "esbuild src/index.ts src/cli.ts src/markdown-toc.ts src/insert-markdown.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist",
23
+ "test": "npm run test:node && npm run test:browser",
24
+ "test:node": "esbuild test/index.ts --platform=node --format=esm --keep-names --bundle --external:gray-matter --external:remarkable --external:diacritics-map | node --input-type=module | tap-spec",
25
+ "test:browser": "esbuild test/browser.ts --bundle --keep-names | tapout | tap-spec",
26
+ "build-esm": "esbuild src/index.ts src/cli.ts src/markdown-toc.ts src/insert-markdown.ts src/dom.ts src/preact.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist",
25
27
  "build": "mkdir -p ./dist && rm -rf ./dist/* && npm run build-esm",
26
28
  "preversion": "npm run lint",
27
29
  "version": "auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md README.md",
@@ -34,13 +36,25 @@
34
36
  "gray-matter": "^4.0.3",
35
37
  "remarkable": "^1.7.1"
36
38
  },
39
+ "peerDependencies": {
40
+ "preact": "^10.0.0 || ^11.0.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "preact": {
44
+ "optional": true
45
+ }
46
+ },
37
47
  "devDependencies": {
48
+ "@substrate-system/redeye": "^0.1.7",
49
+ "@substrate-system/tapout": "^0.1.1",
38
50
  "@substrate-system/tapzero": "^0.10.16",
39
51
  "@types/node": "^24.0.3",
40
52
  "auto-changelog": "^2.6.1",
41
53
  "esbuild": "^0.28.2",
42
54
  "eslint": "^10.10.0",
55
+ "htm": "^3.1.1",
43
56
  "newneostandard": "^0.14.3",
57
+ "preact": "^11.2.0",
44
58
  "tap-spec": "^5.0.0",
45
59
  "typescript": "^6.0.3",
46
60
  "typescript-eslint": "^8.70.0"
@@ -50,5 +64,8 @@
50
64
  "repository": "substrate-system/markdown-toc",
51
65
  "engines": {
52
66
  "node": ">=18"
67
+ },
68
+ "allowScripts": {
69
+ "esbuild": true
53
70
  }
54
71
  }