@tanstack/markdown 0.0.13 → 0.0.15

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
@@ -1,9 +1,27 @@
1
+ <div align="center">
2
+ <picture>
3
+ <source
4
+ media="(prefers-color-scheme: dark)"
5
+ srcset="https://tanstack.com/api/readme/markdown.png?theme=dark"
6
+ />
7
+ <source
8
+ media="(prefers-color-scheme: light)"
9
+ srcset="https://tanstack.com/api/readme/markdown.png"
10
+ />
11
+ <img
12
+ src="https://tanstack.com/api/readme/markdown.png"
13
+ alt="TanStack Markdown"
14
+ width="900"
15
+ />
16
+ </picture>
17
+ </div>
18
+
1
19
  # TanStack Markdown
2
20
 
3
21
  A tiny, fast, deterministic Markdown parser and renderer for blogs and documentation.
4
22
 
5
- - 4.9 KB gzip parser
6
- - 6.7 KB gzip HTML renderer
23
+ - 5.0 KB gzip parser
24
+ - 6.8 KB gzip HTML renderer
7
25
  - 6.7 KB gzip React adapter
8
26
  - 6.7 KB gzip Octane adapter
9
27
  - zero runtime dependencies
@@ -12,6 +30,8 @@ A tiny, fast, deterministic Markdown parser and renderer for blogs and documenta
12
30
  - optional docs extensions and external syntax highlighting
13
31
  - optional AI streaming profile
14
32
 
33
+ Bundle sizes include the parser and exclude framework runtimes and syntax highlighters.
34
+
15
35
  ```bash
16
36
  pnpm add @tanstack/markdown
17
37
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"callouts.d.ts","sourceRoot":"","sources":["../../src/extensions/callouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEjE,wBAAgB,iBAAiB,IAAI,iBAAiB,CAKrD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,GAAG,SAAS,CA+B/H"}
1
+ {"version":3,"file":"callouts.d.ts","sourceRoot":"","sources":["../../src/extensions/callouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEjE,wBAAgB,iBAAiB,IAAI,iBAAiB,CAKrD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,GAAG,SAAS,CAyB/H"}
@@ -15,11 +15,6 @@ export function parseCalloutBlock(context) {
15
15
  let cursor = context.index + 1;
16
16
  while (cursor < context.lines.length) {
17
17
  const line = context.lines[cursor] ?? '';
18
- if (/^\s*$/.test(line)) {
19
- body.push('');
20
- cursor++;
21
- continue;
22
- }
23
18
  const quoted = line.match(/^ {0,3}>\s?(.*)$/);
24
19
  if (!quoted)
25
20
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"comment-components.d.ts","sourceRoot":"","sources":["../../src/extensions/comment-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEnE,MAAM,WAAW,uBAAuB;IACtC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,aAAa,CAAA;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,uBAA4B,GAAG,iBAAiB,CAKnG;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACpE,OAAO,GAAE,uBAA4B,GACpC,aAAa,GAAG,SAAS,CA2C3B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAQhF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrE"}
1
+ {"version":3,"file":"comment-components.d.ts","sourceRoot":"","sources":["../../src/extensions/comment-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEnE,MAAM,WAAW,uBAAuB;IACtC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,aAAa,CAAA;CAC5D;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,uBAA4B,GAAG,iBAAiB,CAKnG;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACpE,OAAO,GAAE,uBAA4B,GACpC,aAAa,GAAG,SAAS,CA0B3B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAQhF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrE"}
@@ -9,35 +9,26 @@ export function parseCommentComponentBlock(context, options = {}) {
9
9
  const start = parseComponentComment(line);
10
10
  if (!start)
11
11
  return undefined;
12
- if (!start.block) {
13
- context.consume(1);
14
- return transform({
15
- type: 'component',
16
- name: start.name,
17
- attributes: start.attributes,
18
- children: [],
19
- }, options);
20
- }
21
- const body = [];
22
- let cursor = context.index + 1;
23
- let foundEnd = false;
24
- while (cursor < context.lines.length) {
25
- const candidate = context.lines[cursor] ?? '';
26
- if (isEndComment(candidate, start.name)) {
27
- foundEnd = true;
28
- cursor++;
29
- break;
12
+ let body;
13
+ let consumed = 1;
14
+ if (start.block) {
15
+ const end = new RegExp(`^ {0,3}<!--\\s*::end:${start.name}\\s*-->\\s*$`, 'i');
16
+ for (let cursor = context.index + 1; cursor < context.lines.length; cursor++) {
17
+ if (end.test(context.lines[cursor])) {
18
+ body = context.lines.slice(context.index + 1, cursor).join('\n');
19
+ consumed = cursor - context.index + 1;
20
+ break;
21
+ }
30
22
  }
31
- body.push(candidate);
32
- cursor++;
33
23
  }
34
- context.consume(foundEnd ? cursor - context.index : 1);
35
- return transform({
24
+ context.consume(consumed);
25
+ const node = {
36
26
  type: 'component',
37
27
  name: start.name,
38
28
  attributes: start.attributes,
39
- children: foundEnd ? context.parseBlocks(body.join('\n')) : [],
40
- }, options);
29
+ children: body === undefined ? [] : context.parseBlocks(body),
30
+ };
31
+ return options.transformComponent?.(node) ?? node;
41
32
  }
42
33
  export function parseComponentComment(line) {
43
34
  const match = line.match(/^ {0,3}<!--\s*::(start:)?([A-Za-z][\w-]*)(.*?)\s*-->\s*$/);
@@ -50,19 +41,10 @@ export function parseComponentComment(line) {
50
41
  };
51
42
  }
52
43
  export function parseAttributes(value) {
53
- const attrs = {};
44
+ const attrs = Object.create(null);
54
45
  const regex = /([A-Za-z_][\w:-]*)(?:=(?:"([^"]*)"|'([^']*)'|([^\s"']+)))?/g;
55
46
  for (const match of value.matchAll(regex)) {
56
47
  attrs[match[1]] = match[2] ?? match[3] ?? match[4] ?? 'true';
57
48
  }
58
49
  return attrs;
59
50
  }
60
- function transform(node, options) {
61
- return options.transformComponent?.(node) ?? node;
62
- }
63
- function isEndComment(line, name) {
64
- return new RegExp(`^ {0,3}<!--\\s*::end:${escapeRegex(name)}\\s*-->\\s*$`, 'i').test(line);
65
- }
66
- function escapeRegex(value) {
67
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
- }
@@ -13,13 +13,13 @@ export function headingCollectionExtension(options = {}) {
13
13
  export function collectMarkdownHeadings(document, options = {}) {
14
14
  const headings = [];
15
15
  const skip = new Set(options.skipComponentNames ?? ['tabs']);
16
- collectFromBlocks(document.children, headings, undefined, false, skip);
16
+ collectFromBlocks(document.children, headings, undefined, skip);
17
17
  return headings;
18
18
  }
19
- function collectFromBlocks(blocks, headings, framework, insideSkippedComponent, skipComponentNames) {
19
+ function collectFromBlocks(blocks, headings, framework, skipComponentNames) {
20
20
  for (const block of blocks) {
21
21
  if (block.type === 'heading') {
22
- if (!insideSkippedComponent && block.id) {
22
+ if (block.id) {
23
23
  const heading = {
24
24
  id: block.id,
25
25
  text: plainText(block.children),
@@ -34,18 +34,19 @@ function collectFromBlocks(blocks, headings, framework, insideSkippedComponent,
34
34
  }
35
35
  if (block.type === 'list') {
36
36
  for (const item of block.items)
37
- collectFromBlocks(item.children, headings, framework, insideSkippedComponent, skipComponentNames);
37
+ collectFromBlocks(item.children, headings, framework, skipComponentNames);
38
38
  continue;
39
39
  }
40
40
  if (block.type === 'blockquote' || block.type === 'callout') {
41
- collectFromBlocks(block.children, headings, framework, insideSkippedComponent, skipComponentNames);
41
+ collectFromBlocks(block.children, headings, framework, skipComponentNames);
42
42
  continue;
43
43
  }
44
44
  if (block.type === 'component') {
45
45
  const name = block.name.toLowerCase();
46
- const nextInsideSkippedComponent = insideSkippedComponent || skipComponentNames.has(name);
46
+ if (skipComponentNames.has(name))
47
+ continue;
47
48
  const nextFramework = block.tagName === 'md-framework-panel' ? block.properties?.['data-framework'] ?? framework : framework;
48
- collectFromBlocks(block.children, headings, nextFramework, nextInsideSkippedComponent, skipComponentNames);
49
+ collectFromBlocks(block.children, headings, nextFramework, skipComponentNames);
49
50
  }
50
51
  }
51
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/extensions/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAe,MAAM,aAAa,CAAA;AAGxE,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAsBxD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAsB5F;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAWtD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAmCzF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,UAEnD"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/extensions/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3D,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,SAAS,EAAE,CAAA;CACtB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAsBxD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAqB5F;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,UAWtD;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CA4BzF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,UAEnD"}
@@ -23,8 +23,7 @@ export function blocksToText(blocks) {
23
23
  .join('\n');
24
24
  }
25
25
  export function splitByHeading(children, forcedDepth) {
26
- const headings = children.filter((child) => child.type === 'heading');
27
- const depth = forcedDepth ?? Math.min(...headings.map(heading => heading.depth));
26
+ const depth = forcedDepth ?? children.reduce((depth, child) => child.type === 'heading' ? Math.min(depth, child.depth) : depth, Infinity);
28
27
  if (!Number.isFinite(depth))
29
28
  return [];
30
29
  const sections = [];
@@ -63,7 +62,7 @@ export function markFrameworkHeadings(blocks, framework) {
63
62
  framework,
64
63
  };
65
64
  }
66
- if (block.type === 'blockquote' || block.type === 'callout') {
65
+ if (block.type === 'blockquote' || block.type === 'callout' || block.type === 'component') {
67
66
  return {
68
67
  ...block,
69
68
  children: markFrameworkHeadings(block.children, framework),
@@ -78,12 +77,6 @@ export function markFrameworkHeadings(blocks, framework) {
78
77
  })),
79
78
  };
80
79
  }
81
- if (block.type === 'component') {
82
- return {
83
- ...block,
84
- children: markFrameworkHeadings(block.children, framework),
85
- };
86
- }
87
80
  return block;
88
81
  });
89
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/extensions/tabs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,aAAa,CAAA;AAK3D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAQzE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAkCpE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CA4B9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAiCvE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CA2BvE"}
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/extensions/tabs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,aAAa,EAAE,MAAM,aAAa,CAAA;AAK3D,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAQzE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAkCpE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CA0B9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CAgCvE;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,GAAG,aAAa,CA2BvE"}
@@ -45,16 +45,13 @@ export function transformFileTabs(node) {
45
45
  };
46
46
  }
47
47
  export function transformPackageManagerTabs(node) {
48
- const packagesByFramework = {};
48
+ const packagesByFramework = Object.create(null);
49
49
  for (const line of blocksToText(node.children).split('\n')) {
50
- const trimmed = line.trim();
51
- if (!trimmed)
52
- continue;
53
- const colon = trimmed.indexOf(':');
50
+ const colon = line.indexOf(':');
54
51
  if (colon === -1)
55
52
  continue;
56
- const framework = trimmed.slice(0, colon).trim().toLowerCase();
57
- const packages = trimmed.slice(colon + 1).trim().split(/\s+/).filter(Boolean);
53
+ const framework = line.slice(0, colon).trim().toLowerCase();
54
+ const packages = line.slice(colon + 1).trim().split(/\s+/).filter(Boolean);
58
55
  if (!framework || packages.length === 0)
59
56
  continue;
60
57
  packagesByFramework[framework] ??= [];
@@ -76,13 +73,13 @@ export function transformPackageManagerTabs(node) {
76
73
  }
77
74
  export function transformBundlerTabs(node) {
78
75
  const sections = splitByHeading(node.children);
79
- const selected = sections.filter(section => isBundler(section.name.toLowerCase()));
76
+ const selected = bundlers.flatMap(bundler => {
77
+ const section = sections.find(section => section.name.toLowerCase() === bundler);
78
+ return section ? [section] : [];
79
+ });
80
80
  if (!selected.length)
81
81
  return node;
82
- const tabs = bundlers
83
- .map(bundler => selected.find(section => section.name.toLowerCase() === bundler))
84
- .filter((section) => Boolean(section))
85
- .map(section => ({ slug: section.name.toLowerCase(), name: section.name.toLowerCase() }));
82
+ const tabs = selected.map(section => ({ slug: section.name.toLowerCase(), name: section.name.toLowerCase() }));
86
83
  return {
87
84
  ...node,
88
85
  properties: {
@@ -90,15 +87,14 @@ export function transformBundlerTabs(node) {
90
87
  'data-attributes': JSON.stringify({ tabs }),
91
88
  'data-bundler-meta': JSON.stringify({ bundlers: tabs.map(tab => tab.slug) }),
92
89
  },
93
- children: tabs.map((tab, index) => {
94
- const section = selected.find(section => section.name.toLowerCase() === tab.slug);
90
+ children: selected.map((section, index) => {
95
91
  return {
96
92
  type: 'component',
97
93
  name: 'tab-panel',
98
94
  tagName: 'md-tab-panel',
99
95
  attributes: {},
100
96
  properties: {
101
- 'data-tab-slug': tab.slug,
97
+ 'data-tab-slug': section.name.toLowerCase(),
102
98
  'data-tab-index': String(index),
103
99
  'data-content': section.children.length === 1 && section.children[0]?.type === 'code' ? 'code-only' : 'mixed',
104
100
  },
@@ -140,6 +136,3 @@ function resolveInstallMode(value) {
140
136
  return mode;
141
137
  return 'install';
142
138
  }
143
- function isBundler(value) {
144
- return bundlers.includes(value);
145
- }
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAyD,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAG7K,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAGpF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA0ChF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA0BlF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAE9F"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAA8E,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAA;AAGlM,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAGpF;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA0ChF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA4BlF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAE9F"}
package/dist/html.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parseMarkdown } from './parser.js';
2
- import { escapeAttr, escapeHtml } from './utils.js';
2
+ import { escapeAttr, escapeHtml, footnoteReferenceId } from './utils.js';
3
3
  export function renderHtml(input, options = {}) {
4
4
  const document = typeof input === 'string' ? parseMarkdown(input, options) : input;
5
5
  return document.children.map(node => renderBlock(node, options)).join('\n');
@@ -20,7 +20,7 @@ export function renderBlock(node, options = {}) {
20
20
  return renderCodeBlock(node, options);
21
21
  case 'list': {
22
22
  const tag = node.ordered ? 'ol' : 'ul';
23
- const start = node.ordered && node.start && node.start !== 1 ? ` start="${node.start}"` : '';
23
+ const start = node.ordered && node.start !== undefined && node.start !== 1 ? ` start="${node.start}"` : '';
24
24
  const items = node.items.map(item => `<li>${renderListItemChildren(item.children, item.checked, node.loose, options)}</li>`).join('\n');
25
25
  return `<${tag}${start}>\n${items}\n</${tag}>`;
26
26
  }
@@ -63,7 +63,7 @@ export function renderInline(node, options = {}) {
63
63
  case 'strike':
64
64
  return `<del>${renderInlines(node.children, options)}</del>`;
65
65
  case 'footnoteReference':
66
- return `<sup><a id="user-content-fnref-${escapeAttr(footnoteReferenceId(node))}" data-footnote-ref="" aria-describedby="footnote-label" href="#user-content-fn-${escapeAttr(node.id)}">${node.number}</a></sup>`;
66
+ return `<sup><a id="user-content-fnref-${escapeAttr(footnoteReferenceId(node.id, node.referenceIndex))}" data-footnote-ref="" aria-describedby="footnote-label" href="#user-content-fn-${escapeAttr(node.id)}">${node.number}</a></sup>`;
67
67
  case 'link':
68
68
  return `<a href="${escapeAttr(node.href)}"${node.title ? ` title="${escapeAttr(node.title)}"` : ''}>${renderInlines(node.children, options)}</a>`;
69
69
  case 'image':
@@ -72,6 +72,8 @@ export function renderInline(node, options = {}) {
72
72
  return '<br>';
73
73
  case 'inlineHtml':
74
74
  return options.allowHtml ? node.value : escapeHtml(node.value);
75
+ case 'inlineComponent':
76
+ return renderComponent(node, options);
75
77
  }
76
78
  }
77
79
  export function renderDocument(document, options = {}) {
@@ -82,16 +84,13 @@ function renderInlines(nodes, options) {
82
84
  }
83
85
  function renderCodeBlock(node, options) {
84
86
  const lang = node.lang ?? 'plaintext';
85
- const preAttrs = [
86
- `class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}"`,
87
- `data-lang="${escapeAttr(lang)}"`,
88
- node.title ? `data-code-title="${escapeAttr(node.title)}"` : '',
89
- node.file ? `data-filename="${escapeAttr(node.file)}"` : '',
90
- node.framework ? `data-framework="${escapeAttr(node.framework)}"` : '',
91
- ]
92
- .filter(Boolean)
93
- .join(' ');
87
+ const preAttrs = `class="tm-code${options.codeLineNumbers ? ' tm-code--line-numbers' : ''}" data-lang="${escapeAttr(lang)}"`
88
+ + (node.meta ? ` data-meta="${escapeAttr(node.meta)}"` : '')
89
+ + (node.title ? ` data-code-title="${escapeAttr(node.title)}"` : '')
90
+ + (node.file ? ` data-filename="${escapeAttr(node.file)}"` : '')
91
+ + (node.framework ? ` data-framework="${escapeAttr(node.framework)}"` : '');
94
92
  const html = options.highlighter?.(node.value, lang, {
93
+ ...(node.meta && { meta: node.meta }),
95
94
  ...(node.highlightLines && { highlightLines: node.highlightLines }),
96
95
  ...(options.codeLineNumbers !== undefined && { lineNumbers: options.codeLineNumbers }),
97
96
  }) ?? escapeHtml(node.value);
@@ -101,14 +100,18 @@ function renderCodeBlock(node, options) {
101
100
  return `<figure class="tm-code-frame" data-lang="${escapeAttr(lang)}"><figcaption>${escapeHtml(node.title)}</figcaption>${pre}</figure>`;
102
101
  }
103
102
  function renderListItemChildren(children, checked, loose, options) {
104
- const first = children[0];
105
- const task = checked === undefined ? '' : `<input type="checkbox" disabled${checked ? ' checked' : ''}> `;
106
- if (first?.type === 'paragraph') {
107
- const content = `${task}${renderInlines(first.children, options)}`;
108
- const rest = children.length > 1 ? `\n${children.slice(1).map(child => renderBlock(child, options)).join('\n')}` : '';
109
- return `${loose ? `<p>${content}</p>` : content}${rest}`;
103
+ let result = checked === undefined ? '' : `<input type="checkbox" disabled${checked ? ' checked' : ''}> `;
104
+ for (let index = 0; index < children.length; index++) {
105
+ const child = children[index];
106
+ if (child.type === 'paragraph' && (!loose || index === 0)) {
107
+ result += renderInlines(child.children, options);
108
+ if (loose)
109
+ result = `<p>${result}</p>`;
110
+ }
111
+ else
112
+ result += (index ? '\n' : '') + renderBlock(child, options);
110
113
  }
111
- return `${task}${children.map(child => renderBlock(child, options)).join('\n')}`;
114
+ return result;
112
115
  }
113
116
  function renderCallout(node, options) {
114
117
  const kind = node.kind.toLowerCase();
@@ -122,33 +125,32 @@ function renderFootnotes(items, options) {
122
125
  function renderFootnoteItem(item, options) {
123
126
  const backref = renderFootnoteBackrefs(item);
124
127
  const lastIndex = item.children.length - 1;
125
- if (lastIndex < 0)
126
- return `<p>${backref.trimStart()}</p>`;
127
- return item.children
128
+ const result = item.children
128
129
  .map((child, index) => {
129
130
  if (index === lastIndex && child.type === 'paragraph') {
130
131
  return `<p>${renderInlines(child.children, options)}${backref}</p>`;
131
132
  }
132
133
  return renderBlock(child, options);
133
- })
134
- .join('\n');
134
+ });
135
+ if (item.children[lastIndex]?.type !== 'paragraph')
136
+ result.push(`<p>${backref.trimStart()}</p>`);
137
+ return result.join('\n');
135
138
  }
136
139
  function renderFootnoteBackrefs(item) {
137
140
  let result = '';
138
141
  for (let index = 1; index <= (item.referenceCount ?? 1); index++) {
139
- const referenceId = index === 1 ? item.id : `${item.id}-${index}`;
142
+ const referenceId = footnoteReferenceId(item.id, index);
140
143
  const label = index === 1 ? `${item.number}` : `${item.number}-${index}`;
141
144
  result += ` <a data-footnote-backref="" aria-label="Back to reference ${label}" class="data-footnote-backref" href="#user-content-fnref-${escapeAttr(referenceId)}">&#8617;</a>`;
142
145
  }
143
146
  return result;
144
147
  }
145
- function footnoteReferenceId(node) {
146
- return node.referenceIndex && node.referenceIndex > 1 ? `${node.id}-${node.referenceIndex}` : node.id;
147
- }
148
148
  function renderComponent(node, options) {
149
- const tag = node.tagName ?? 'md-comment-component';
149
+ const tag = node.tagName ?? (node.type === 'inlineComponent' ? 'span' : 'md-comment-component');
150
150
  const attrs = renderComponentAttrs(node);
151
- const children = node.children.map(child => renderBlock(child, options)).join('\n');
151
+ const children = node.type === 'inlineComponent'
152
+ ? renderInlines(node.children, options)
153
+ : node.children.map(child => renderBlock(child, options)).join('\n');
152
154
  return `<${tag}${attrs}>${children}</${tag}>`;
153
155
  }
154
156
  function renderTableCell(tag, cell, align, options) {
@@ -183,8 +185,5 @@ function renderComponentAttrs(node) {
183
185
  if (!props['data-attributes'])
184
186
  props['data-attributes'] = JSON.stringify(node.attributes);
185
187
  }
186
- const entries = Object.entries(props);
187
- if (!entries.length)
188
- return '';
189
- return ` ${entries.map(([key, value]) => `${key}="${escapeAttr(value)}"`).join(' ')}`;
188
+ return Object.entries(props).map(([key, value]) => ` ${key}="${escapeAttr(value)}"`).join('');
190
189
  }
@@ -1 +1 @@
1
- {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,UAAU,EAAE,CASnF"}
1
+ {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG1D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,UAAU,EAAE,CAQnF"}