comark 0.0.1 → 0.1.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.
Files changed (112) hide show
  1. package/README.md +104 -0
  2. package/dist/index.d.ts +4 -0
  3. package/dist/index.js +6 -0
  4. package/dist/internal/frontmatter.d.ts +16 -0
  5. package/dist/internal/frontmatter.js +43 -0
  6. package/dist/internal/parse/auto-close/index.d.ts +12 -0
  7. package/dist/internal/parse/auto-close/index.js +457 -0
  8. package/dist/internal/parse/auto-close/table.d.ts +4 -0
  9. package/dist/internal/parse/auto-close/table.js +161 -0
  10. package/dist/internal/parse/auto-unwrap.d.ts +20 -0
  11. package/dist/internal/parse/auto-unwrap.js +42 -0
  12. package/dist/internal/parse/html/html_block_rule.d.ts +2 -0
  13. package/dist/internal/parse/html/html_block_rule.js +60 -0
  14. package/dist/internal/parse/html/html_blocks.d.ts +2 -0
  15. package/dist/internal/parse/html/html_blocks.js +66 -0
  16. package/dist/internal/parse/html/html_inline_rule.d.ts +2 -0
  17. package/dist/internal/parse/html/html_inline_rule.js +43 -0
  18. package/dist/internal/parse/html/html_re.d.ts +3 -0
  19. package/dist/internal/parse/html/html_re.js +18 -0
  20. package/dist/internal/parse/html/index.d.ts +18 -0
  21. package/dist/internal/parse/html/index.js +122 -0
  22. package/dist/internal/parse/incremental.d.ts +12 -0
  23. package/dist/internal/parse/incremental.js +39 -0
  24. package/dist/internal/parse/token-processor.d.ts +9 -0
  25. package/dist/internal/parse/token-processor.js +803 -0
  26. package/dist/internal/props-validation.d.ts +12 -0
  27. package/dist/internal/props-validation.js +112 -0
  28. package/dist/internal/stringify/attributes.d.ts +21 -0
  29. package/dist/internal/stringify/attributes.js +67 -0
  30. package/dist/internal/stringify/handlers/a.d.ts +3 -0
  31. package/dist/internal/stringify/handlers/a.js +11 -0
  32. package/dist/internal/stringify/handlers/blockquote.d.ts +3 -0
  33. package/dist/internal/stringify/handlers/blockquote.js +18 -0
  34. package/dist/internal/stringify/handlers/br.d.ts +3 -0
  35. package/dist/internal/stringify/handlers/br.js +3 -0
  36. package/dist/internal/stringify/handlers/code.d.ts +3 -0
  37. package/dist/internal/stringify/handlers/code.js +11 -0
  38. package/dist/internal/stringify/handlers/comment.d.ts +3 -0
  39. package/dist/internal/stringify/handlers/comment.js +6 -0
  40. package/dist/internal/stringify/handlers/del.d.ts +3 -0
  41. package/dist/internal/stringify/handlers/del.js +4 -0
  42. package/dist/internal/stringify/handlers/emphesis.d.ts +3 -0
  43. package/dist/internal/stringify/handlers/emphesis.js +13 -0
  44. package/dist/internal/stringify/handlers/heading.d.ts +3 -0
  45. package/dist/internal/stringify/handlers/heading.js +7 -0
  46. package/dist/internal/stringify/handlers/hr.d.ts +3 -0
  47. package/dist/internal/stringify/handlers/hr.js +3 -0
  48. package/dist/internal/stringify/handlers/html.d.ts +3 -0
  49. package/dist/internal/stringify/handlers/html.js +73 -0
  50. package/dist/internal/stringify/handlers/img.d.ts +3 -0
  51. package/dist/internal/stringify/handlers/img.js +9 -0
  52. package/dist/internal/stringify/handlers/index.d.ts +2 -0
  53. package/dist/internal/stringify/handlers/index.js +56 -0
  54. package/dist/internal/stringify/handlers/li.d.ts +3 -0
  55. package/dist/internal/stringify/handlers/li.js +43 -0
  56. package/dist/internal/stringify/handlers/math.d.ts +3 -0
  57. package/dist/internal/stringify/handlers/math.js +8 -0
  58. package/dist/internal/stringify/handlers/mdc.d.ts +3 -0
  59. package/dist/internal/stringify/handlers/mdc.js +47 -0
  60. package/dist/internal/stringify/handlers/mermaid.d.ts +3 -0
  61. package/dist/internal/stringify/handlers/mermaid.js +8 -0
  62. package/dist/internal/stringify/handlers/ol.d.ts +3 -0
  63. package/dist/internal/stringify/handlers/ol.js +18 -0
  64. package/dist/internal/stringify/handlers/p.d.ts +3 -0
  65. package/dist/internal/stringify/handlers/p.js +8 -0
  66. package/dist/internal/stringify/handlers/pre.d.ts +3 -0
  67. package/dist/internal/stringify/handlers/pre.js +60 -0
  68. package/dist/internal/stringify/handlers/strong.d.ts +3 -0
  69. package/dist/internal/stringify/handlers/strong.js +13 -0
  70. package/dist/internal/stringify/handlers/table.d.ts +8 -0
  71. package/dist/internal/stringify/handlers/table.js +180 -0
  72. package/dist/internal/stringify/handlers/template.d.ts +3 -0
  73. package/dist/internal/stringify/handlers/template.js +14 -0
  74. package/dist/internal/stringify/handlers/ul.d.ts +3 -0
  75. package/dist/internal/stringify/handlers/ul.js +18 -0
  76. package/dist/internal/stringify/indent.d.ts +4 -0
  77. package/dist/internal/stringify/indent.js +8 -0
  78. package/dist/internal/stringify/state.d.ts +13 -0
  79. package/dist/internal/stringify/state.js +121 -0
  80. package/dist/internal/yaml.d.ts +12 -0
  81. package/dist/internal/yaml.js +51 -0
  82. package/dist/parse.d.ts +66 -0
  83. package/dist/parse.js +163 -0
  84. package/dist/plugins/alert.d.ts +2 -0
  85. package/dist/plugins/alert.js +66 -0
  86. package/dist/plugins/emoji.d.ts +3 -0
  87. package/dist/plugins/emoji.js +438 -0
  88. package/dist/plugins/headings.d.ts +48 -0
  89. package/dist/plugins/headings.js +85 -0
  90. package/dist/plugins/highlight.d.ts +71 -0
  91. package/dist/plugins/highlight.js +234 -0
  92. package/dist/plugins/math.d.ts +59 -0
  93. package/dist/plugins/math.js +263 -0
  94. package/dist/plugins/mermaid.d.ts +38 -0
  95. package/dist/plugins/mermaid.js +185 -0
  96. package/dist/plugins/security.d.ts +11 -0
  97. package/dist/plugins/security.js +32 -0
  98. package/dist/plugins/summary.d.ts +2 -0
  99. package/dist/plugins/summary.js +22 -0
  100. package/dist/plugins/task-list.d.ts +8 -0
  101. package/dist/plugins/task-list.js +117 -0
  102. package/dist/plugins/toc.d.ts +15 -0
  103. package/dist/plugins/toc.js +118 -0
  104. package/dist/render.d.ts +18 -0
  105. package/dist/render.js +29 -0
  106. package/dist/types.d.ts +258 -0
  107. package/dist/types.js +1 -0
  108. package/dist/utils/caret.d.ts +7 -0
  109. package/dist/utils/caret.js +36 -0
  110. package/dist/utils/index.d.ts +38 -0
  111. package/dist/utils/index.js +149 -0
  112. package/package.json +75 -9
@@ -0,0 +1,12 @@
1
+ export declare const unsafeTags: string[];
2
+ export declare const unsafeAttributes: string[];
3
+ export declare const unsafeLinkPrefix: string[];
4
+ export interface PropsValidationOptions {
5
+ allowedLinkPrefixes?: string[];
6
+ allowedImagePrefixes?: string[];
7
+ allowedProtocols?: string[];
8
+ defaultOrigin?: string;
9
+ allowDataImages?: boolean;
10
+ }
11
+ export declare function validateProp(attribute: string, value: string, options?: PropsValidationOptions): string | false;
12
+ export declare function validateProps(type: string, props?: Record<string, any>, options?: PropsValidationOptions): Record<string, any>;
@@ -0,0 +1,112 @@
1
+ export const unsafeTags = [
2
+ 'object',
3
+ ];
4
+ export const unsafeAttributes = [
5
+ 'srcdoc',
6
+ 'formaction',
7
+ ];
8
+ export const unsafeLinkPrefix = [
9
+ 'javascript:',
10
+ 'data:text/html',
11
+ 'vbscript:',
12
+ 'data:text/javascript',
13
+ 'data:text/vbscript',
14
+ 'data:text/css',
15
+ 'data:text/plain',
16
+ 'data:text/xml',
17
+ ];
18
+ function rewriteToDefaultOrigin(urlStr, defaultOrigin) {
19
+ try {
20
+ const parsed = new URL(urlStr);
21
+ const origin = new URL(defaultOrigin);
22
+ parsed.protocol = origin.protocol;
23
+ parsed.host = origin.host;
24
+ return parsed.href;
25
+ }
26
+ catch {
27
+ return defaultOrigin;
28
+ }
29
+ }
30
+ function validateUrl(value, mode, options) {
31
+ const { allowedLinkPrefixes = ['*'], allowedImagePrefixes = ['*'], allowedProtocols = ['*'], defaultOrigin, allowDataImages = true, } = options;
32
+ const decodedUrl = decodeURIComponent(value);
33
+ const urlSanitized = decodedUrl
34
+ .replace(/&#x([0-9a-f]+);?/gi, '')
35
+ .replace(/&#(\d+);?/g, '')
36
+ .replace(/&[a-z]+;?/gi, '');
37
+ let url;
38
+ try {
39
+ // Parse without a base — throws for relative URLs, succeeds for absolute
40
+ url = new URL(urlSanitized);
41
+ }
42
+ catch {
43
+ // Relative URLs are always allowed
44
+ return value;
45
+ }
46
+ // Block known-unsafe protocols — hard floor, not overrideable by options
47
+ if (unsafeLinkPrefix.some(prefix => url.href.toLowerCase().startsWith(prefix))) {
48
+ return false;
49
+ }
50
+ // Block data: images when allowDataImages is false
51
+ if (mode === 'image' && !allowDataImages && url.protocol === 'data:') {
52
+ return false;
53
+ }
54
+ // Check allowed protocols
55
+ if (!allowedProtocols.includes('*')) {
56
+ const protocol = url.protocol.replace(':', '');
57
+ if (!allowedProtocols.includes(protocol)) {
58
+ return false;
59
+ }
60
+ }
61
+ // Check allowed URL prefixes
62
+ const allowedPrefixes = mode === 'link' ? allowedLinkPrefixes : allowedImagePrefixes;
63
+ if (!allowedPrefixes.includes('*')) {
64
+ const href = url.href.toLowerCase();
65
+ const matchesPrefix = allowedPrefixes.some(prefix => href.startsWith(prefix.toLowerCase()));
66
+ if (!matchesPrefix) {
67
+ if (defaultOrigin) {
68
+ return rewriteToDefaultOrigin(urlSanitized, defaultOrigin);
69
+ }
70
+ return false;
71
+ }
72
+ }
73
+ return value;
74
+ }
75
+ export function validateProp(attribute, value, options = {}) {
76
+ attribute = attribute.toLowerCase();
77
+ if (attribute.startsWith('on') || unsafeAttributes.includes(attribute)) {
78
+ return false;
79
+ }
80
+ if (attribute === 'href') {
81
+ return validateUrl(value, 'link', options);
82
+ }
83
+ if (attribute === 'src') {
84
+ return validateUrl(value, 'image', options);
85
+ }
86
+ return value;
87
+ }
88
+ export function validateProps(type, props, options = {}) {
89
+ /**
90
+ * If the tag is marked as unsafe, drop all props
91
+ */
92
+ if (unsafeTags.includes(type.toLowerCase())) {
93
+ return {};
94
+ }
95
+ if (!props)
96
+ return {};
97
+ const entries = Object.entries(props);
98
+ if (entries.length === 0)
99
+ return {};
100
+ props = Object.fromEntries(entries.flatMap(([name, value]) => {
101
+ if (name === 'id' && !value) {
102
+ return [];
103
+ }
104
+ const result = validateProp(name, value, options);
105
+ if (result === false) {
106
+ console.warn(`[comark/plugins/security] removing unsafe attribute: ${name}="${value}"`);
107
+ return [];
108
+ }
109
+ return [[name, result]];
110
+ }));
111
+ return props;
112
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Convert attributes to a string of Comark attributes
3
+ *
4
+ * @param attributes - The attributes to stringify
5
+ * @returns The stringified attributes
6
+ */
7
+ export declare function comarkAttributes(attributes: Record<string, unknown>): string;
8
+ /**
9
+ * Convert attributes to a string of HTML attributes
10
+ *
11
+ * @param attributes - The attributes to stringify
12
+ * @returns The stringified attributes
13
+ */
14
+ export declare function htmlAttributes(attributes: Record<string, unknown>): string;
15
+ /**
16
+ * Convert attributes to a string of YAML attributes
17
+ *
18
+ * @param attributes - The attributes to stringify
19
+ * @returns The stringified attributes
20
+ */
21
+ export declare function comarkYamlAttributes(attributes: Record<string, unknown>): string;
@@ -0,0 +1,67 @@
1
+ import { stringifyYaml } from "../yaml.js";
2
+ /**
3
+ * Convert attributes to a string of Comark attributes
4
+ *
5
+ * @param attributes - The attributes to stringify
6
+ * @returns The stringified attributes
7
+ */
8
+ export function comarkAttributes(attributes) {
9
+ const attrs = Object.entries(attributes)
10
+ .map(([key, value]) => {
11
+ if (key.startsWith(':') && value === 'true') {
12
+ return key.slice(1);
13
+ }
14
+ if (key === 'id') {
15
+ return `#${value}`;
16
+ }
17
+ if (key === 'class') {
18
+ return value.split(' ').map(c => `.${c}`).join('');
19
+ }
20
+ if (typeof value === 'object') {
21
+ return `${key}="${JSON.stringify(value).replace(/"/g, '\\"')}"`;
22
+ }
23
+ return `${key}="${value}"`;
24
+ })
25
+ .join(' ');
26
+ return attrs.length > 0 ? `{${attrs}}` : '';
27
+ }
28
+ /**
29
+ * Convert attributes to a string of HTML attributes
30
+ *
31
+ * @param attributes - The attributes to stringify
32
+ * @returns The stringified attributes
33
+ */
34
+ export function htmlAttributes(attributes) {
35
+ return Object.entries(attributes)
36
+ .map(([key, value]) => {
37
+ if (key.startsWith(':')) {
38
+ if (value === 'true') {
39
+ return key.slice(1);
40
+ }
41
+ return `${key.slice(1)}="${value}"`;
42
+ }
43
+ if (value === 'true')
44
+ return key;
45
+ if (typeof value === 'object') {
46
+ return `${key}="${JSON.stringify(value).replace(/"/g, '\\"')}"`;
47
+ }
48
+ return `${key}="${value}"`;
49
+ })
50
+ .join(' ');
51
+ }
52
+ /**
53
+ * Convert attributes to a string of YAML attributes
54
+ *
55
+ * @param attributes - The attributes to stringify
56
+ * @returns The stringified attributes
57
+ */
58
+ export function comarkYamlAttributes(attributes) {
59
+ // Normalize boolean attributes to remove the colon prefix
60
+ const normalized = Object.fromEntries(Object.entries(attributes).map(([key, value]) => {
61
+ if (key.startsWith(':') && (value === 'true' || value === 'false')) {
62
+ return [key.slice(1), value];
63
+ }
64
+ return [key, value];
65
+ }));
66
+ return `---\n${stringifyYaml(normalized).trim()}\n---`;
67
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function a(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,11 @@
1
+ import { comarkAttributes } from "../attributes.js";
2
+ // TODO: support title & attributes
3
+ export async function a(node, state) {
4
+ const [_, attrs] = node;
5
+ const { href, ...rest } = attrs;
6
+ const attrsString = Object.keys(rest).length > 0
7
+ ? comarkAttributes(rest)
8
+ : '';
9
+ const content = await state.flow(node, state);
10
+ return `[${content}](${href})${attrsString}`;
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function blockquote(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,18 @@
1
+ export async function blockquote(node, state) {
2
+ const children = node.slice(2);
3
+ let childResult = '';
4
+ for (const child of children) {
5
+ childResult += await state.one(child, state, node);
6
+ }
7
+ const content = childResult
8
+ .trim()
9
+ .split('\n')
10
+ .map(line => line ? `> ${line}` : '>')
11
+ .join('\n');
12
+ if (node[1].as) {
13
+ return `> [!${String(node[1].as).toUpperCase()}]\n`
14
+ + content
15
+ + state.context.blockSeparator;
16
+ }
17
+ return content + state.context.blockSeparator;
18
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function br(_: ComarkElement, _state: State): string;
@@ -0,0 +1,3 @@
1
+ export function br(_, _state) {
2
+ return ' \n';
3
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function code(node: ComarkElement, _state: State): string;
@@ -0,0 +1,11 @@
1
+ import { comarkAttributes } from "../attributes.js";
2
+ import { textContent } from 'comark/utils';
3
+ export function code(node, _state) {
4
+ const [_, attrs] = node;
5
+ const attrsString = Object.keys(attrs).length > 0
6
+ ? comarkAttributes(attrs)
7
+ : '';
8
+ const content = textContent(node);
9
+ const fence = content.includes('`') ? '``' : '`';
10
+ return `${fence}${content}${fence}${attrsString}`;
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function comment(node: ComarkElement, _state: State): string;
@@ -0,0 +1,6 @@
1
+ export function comment(node, _state) {
2
+ if (node[0] === null) {
3
+ return `<!--${node[2]}-->` + _state.context.blockSeparator;
4
+ }
5
+ return '';
6
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function del(node: ComarkElement, _: State): string;
@@ -0,0 +1,4 @@
1
+ import { textContent } from 'comark/utils';
2
+ export function del(node, _) {
3
+ return `~~${textContent(node)}~~`;
4
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function emphesis(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,13 @@
1
+ import { comarkAttributes } from "../attributes.js";
2
+ export async function emphesis(node, state) {
3
+ const [_, attrs, ...children] = node;
4
+ let content = '';
5
+ for (const child of children) {
6
+ content += await state.one(child, state, node);
7
+ }
8
+ content = content.trim();
9
+ const attrsString = Object.keys(attrs).length > 0
10
+ ? comarkAttributes(attrs)
11
+ : '';
12
+ return `*${content}*${attrsString}`;
13
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function heading(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,7 @@
1
+ // h1, h2, h3, h4, h5, h6
2
+ export async function heading(node, state) {
3
+ const [tag] = node;
4
+ const level = Number(tag.slice(1));
5
+ const content = await state.flow(node, state);
6
+ return '#'.repeat(level) + ' ' + content + state.context.blockSeparator;
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function hr(_: ComarkElement, state: State): string;
@@ -0,0 +1,3 @@
1
+ export function hr(_, state) {
2
+ return '---' + state.context.blockSeparator;
3
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function html(node: ComarkElement, state: State, parent?: ComarkElement): Promise<string>;
@@ -0,0 +1,73 @@
1
+ import { htmlAttributes } from "../attributes.js";
2
+ import { indent } from "../indent.js";
3
+ const textBlocks = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'td', 'th']);
4
+ const selfCloseTags = new Set(['br', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr']);
5
+ const inlineTags = new Set(['strong', 'em', 'code', 'a', 'br', 'span', 'img']);
6
+ const blockTags = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'ul', 'ol', 'blockquote', 'hr', 'table', 'td', 'th']);
7
+ export async function html(node, state, parent) {
8
+ const [tag, attr, ...children] = node;
9
+ const { $ = {}, ...attributes } = attr;
10
+ const hasOnlyTextChildren = children.every(child => typeof child === 'string' || inlineTags.has(String(child?.[0])));
11
+ const hasTextSibling = children.some(child => typeof child === 'string');
12
+ const isBlock = textBlocks.has(String(tag));
13
+ const isInline = inlineTags.has(String(tag)) && $.block === 0;
14
+ let oneLiner = isBlock && hasOnlyTextChildren;
15
+ if (!oneLiner && inlineTags.has(String(tag)) && hasOnlyTextChildren) {
16
+ oneLiner = true;
17
+ }
18
+ if (tag === 'pre') {
19
+ oneLiner = true;
20
+ }
21
+ // If parent is a paragraph, it is inline
22
+ if (parent?.[0] === 'p' || state.context.inline) {
23
+ oneLiner = true;
24
+ }
25
+ if ($.block === 0) {
26
+ oneLiner = true;
27
+ }
28
+ const isSelfClose = selfCloseTags.has(String(tag));
29
+ // Do not modify context if we are already in html mode
30
+ const revert = state.applyContext({ inline: oneLiner });
31
+ const childrenContent = [];
32
+ for (const child of children) {
33
+ childrenContent.push(await state.one(child, state, node));
34
+ }
35
+ let content = '';
36
+ let isPrevBlock = true;
37
+ for (let i = 0; i < children.length; i++) {
38
+ const childContent = childrenContent[i];
39
+ const child = children[i];
40
+ const isBlock = blockTags.has(String(child?.[0])) || (!inlineTags.has(String(child?.[0])) && !hasTextSibling);
41
+ if (i > 0 && !isPrevBlock && isBlock) {
42
+ content += state.context.blockSeparator;
43
+ }
44
+ content += childContent;
45
+ isPrevBlock = isBlock;
46
+ if (isBlock && i < children.length - 1) {
47
+ content += state.context.blockSeparator;
48
+ }
49
+ }
50
+ // Revert, only if we modified the context
51
+ if (revert) {
52
+ state.applyContext(revert);
53
+ }
54
+ const attrs = Object.keys(attributes).length > 0
55
+ ? ` ${htmlAttributes(attributes)}`
56
+ : '';
57
+ if (isSelfClose) {
58
+ return `<${tag}${attrs} />` + (!parent && !isInline ? state.context.blockSeparator : '');
59
+ }
60
+ if (!oneLiner && content) {
61
+ content = '\n' + paddNoneHtmlContent(content, state).trimEnd() + '\n';
62
+ }
63
+ return `<${tag}${attrs}>${content}</${tag}>`
64
+ + (!parent && !isInline ? state.context.blockSeparator : '');
65
+ }
66
+ function paddNoneHtmlContent(content, state) {
67
+ if (state.context.html) {
68
+ return indent(content);
69
+ }
70
+ return ((content.trim().startsWith('<') ? '' : '')
71
+ + content
72
+ + (content.trim().endsWith('>') ? '' : ''));
73
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function img(node: ComarkElement, _state: State): string;
@@ -0,0 +1,9 @@
1
+ import { comarkAttributes } from "../attributes.js";
2
+ export function img(node, _state) {
3
+ const [_, attrs] = node;
4
+ const { title, src, alt, ...rest } = attrs;
5
+ const attrsString = Object.keys(rest).length > 0
6
+ ? comarkAttributes(rest)
7
+ : '';
8
+ return title ? `![${alt}](${src} "${title}")` : `![${alt}](${src})${attrsString}`;
9
+ }
@@ -0,0 +1,2 @@
1
+ import type { NodeHandler } from 'comark/render';
2
+ export declare const handlers: Record<string, NodeHandler>;
@@ -0,0 +1,56 @@
1
+ import { code } from "./code.js";
2
+ import { pre } from "./pre.js";
3
+ import { hr } from "./hr.js";
4
+ import { heading } from "./heading.js";
5
+ import { p } from "./p.js";
6
+ import { a } from "./a.js";
7
+ import { ul } from "./ul.js";
8
+ import { ol } from "./ol.js";
9
+ import { li } from "./li.js";
10
+ import { html } from "./html.js";
11
+ import { strong } from "./strong.js";
12
+ import { emphesis } from "./emphesis.js";
13
+ import { blockquote } from "./blockquote.js";
14
+ import { img } from "./img.js";
15
+ import { del } from "./del.js";
16
+ import { mdc } from "./mdc.js";
17
+ import { br } from "./br.js";
18
+ import { template } from "./template.js";
19
+ import { table, thead, tbody, tr, th, td } from "./table.js";
20
+ import { comment } from "./comment.js";
21
+ import { math } from "./math.js";
22
+ import { mermaid } from "./mermaid.js";
23
+ export const handlers = {
24
+ code,
25
+ pre,
26
+ hr,
27
+ br,
28
+ h1: heading,
29
+ h2: heading,
30
+ h3: heading,
31
+ h4: heading,
32
+ h5: heading,
33
+ h6: heading,
34
+ p,
35
+ a,
36
+ ul,
37
+ ol,
38
+ li,
39
+ html,
40
+ strong,
41
+ em: emphesis,
42
+ blockquote,
43
+ img,
44
+ del,
45
+ mdc,
46
+ template,
47
+ table,
48
+ thead,
49
+ tbody,
50
+ tr,
51
+ th,
52
+ td,
53
+ comment,
54
+ math,
55
+ mermaid,
56
+ };
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function li(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,43 @@
1
+ export async function li(node, state) {
2
+ const children = node.slice(2);
3
+ const order = state.context.order;
4
+ let prefix = order ? `${order}. ` : '- ';
5
+ const className = node[1].className && Array.isArray(node[1].className)
6
+ ? node[1].className.join(' ')
7
+ : String(node[1].className || node[1].class);
8
+ const taskList = className.includes('task-list-item');
9
+ if (taskList) {
10
+ const input = children.shift();
11
+ prefix += input[1].checked || input[1][':checked'] ? '[x] ' : '[ ] ';
12
+ }
13
+ let result = '';
14
+ for (const child of children) {
15
+ result += await state.one(child, state, node);
16
+ }
17
+ result = result.trim();
18
+ if (!order) {
19
+ result = escapeLeadingNumberDot(result);
20
+ }
21
+ if (order) {
22
+ state.applyContext({ order: order + 1 });
23
+ }
24
+ return `${prefix}${result}\n`;
25
+ }
26
+ function escapeLeadingNumberDot(str) {
27
+ if (str.length === 0)
28
+ return str;
29
+ const len = str.length;
30
+ const firstChar = str.charCodeAt(0);
31
+ if (firstChar < 48 || firstChar > 57)
32
+ return str; // Not a digit
33
+ let i = 1;
34
+ for (; i < len; i++) {
35
+ const code = str.charCodeAt(i);
36
+ if (code < 48 || code > 57)
37
+ break;
38
+ }
39
+ if (i < len && str[i] === '.') {
40
+ return str.slice(0, i) + '\\.' + str.slice(i + 1);
41
+ }
42
+ return str;
43
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function math(node: ComarkElement, state: State, parent?: ComarkElement): string;
@@ -0,0 +1,8 @@
1
+ import { textContent } from 'comark/utils';
2
+ export function math(node, state, parent) {
3
+ const content = textContent(node);
4
+ if (parent?.some((child, index) => index > 1 && typeof child === 'string')) {
5
+ return `$$${content}$$`;
6
+ }
7
+ return `$$\n${content}\n$$${state.context.blockSeparator}`;
8
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function mdc(node: ComarkElement, state: State, parent?: ComarkElement): Promise<string>;
@@ -0,0 +1,47 @@
1
+ import { indent } from "../indent.js";
2
+ import { comarkAttributes, comarkYamlAttributes } from "../attributes.js";
3
+ import { html } from "./html.js";
4
+ // HTML elements that always create an inline context for their children
5
+ const INLINE_HTML_ELEMENTS = new Set(['a', 'strong', 'em', 'span']);
6
+ export async function mdc(node, state, parent) {
7
+ const [tag, attr, ...children] = node;
8
+ const { $, ...attributes } = attr;
9
+ if (tag === 'table') {
10
+ return html(node, state);
11
+ }
12
+ const attributeEntries = Object.entries(attributes);
13
+ const hasObjectAttributes = attributeEntries.some(([, value]) => typeof value === 'object');
14
+ // Component is inline if it has text siblings in parent
15
+ // or is inside an inline HTML element
16
+ const hasTextSiblings = parent?.some((child, index) => index > 1 && typeof child === 'string') ?? false;
17
+ const insideInlineElement = parent !== undefined && INLINE_HTML_ELEMENTS.has(String(parent[0]));
18
+ let inline = hasTextSiblings || insideInlineElement;
19
+ // if component has object attributes, it cannot be inline
20
+ if (hasObjectAttributes) {
21
+ inline = false;
22
+ }
23
+ let content = '';
24
+ const childState = { ...state, nodeDepthInTree: (state.nodeDepthInTree || 0) + 1 };
25
+ for (const child of children) {
26
+ content += await state.one(child, childState, node);
27
+ }
28
+ content = content.trimEnd();
29
+ const attrs = attributeEntries.length > 0 ? comarkAttributes(attributes) : '';
30
+ if (tag === 'span') {
31
+ return `[${content}]${attrs}`;
32
+ }
33
+ const fence = ':'.repeat((state.nodeDepthInTree || 0) + 2);
34
+ let result = `:${tag}${content && `[${content}]`}${attrs}` + (!parent ? state.context.blockSeparator : '');
35
+ if (!inline) {
36
+ const maxInlineAttributes = state.context.maxInlineAttributes ?? 3;
37
+ const useYaml = hasObjectAttributes || maxInlineAttributes === 0 || attributeEntries.length > maxInlineAttributes;
38
+ if (useYaml) {
39
+ const yamlAttrs = comarkYamlAttributes(attributes);
40
+ result = `${fence}${tag}\n${yamlAttrs}${content ? `\n${content}` : ''}\n${fence}` + state.context.blockSeparator;
41
+ }
42
+ else {
43
+ result = `${fence}${tag}${attrs}${content ? `\n${content}` : ''}\n${fence}` + state.context.blockSeparator;
44
+ }
45
+ }
46
+ return inline ? result : indent(result, { level: parent ? 1 : 0 });
47
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function mermaid(node: ComarkElement, state: State): string;
@@ -0,0 +1,8 @@
1
+ import { comarkAttributes } from "../attributes.js";
2
+ const fence = '```';
3
+ export function mermaid(node, state) {
4
+ const [_, attributes] = node;
5
+ const { content, ...rest } = attributes;
6
+ const attrs = comarkAttributes(rest);
7
+ return `${fence}mermaid${attrs ? ` ${attrs}` : ''}\n${content}\n${fence}${state.context.blockSeparator}`;
8
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function ol(node: ComarkElement, state: State): Promise<string>;