comark 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +63 -0
  91. package/dist/plugins/highlight.js +235 -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 +73 -9
@@ -0,0 +1,18 @@
1
+ import { indent } from '../indent';
2
+ export async function ol(node, state) {
3
+ const children = node.slice(2);
4
+ const revert = state.applyContext({ list: true, order: 1 });
5
+ let result = '';
6
+ for (const child of children) {
7
+ result += await state.one(child, state);
8
+ }
9
+ result = result.trim();
10
+ if (revert.list) {
11
+ result = '\n' + indent(result);
12
+ }
13
+ else {
14
+ result = result + state.context.blockSeparator;
15
+ }
16
+ state.applyContext(revert);
17
+ return result;
18
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function p(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,8 @@
1
+ export async function p(node, state) {
2
+ const children = node.slice(2);
3
+ let result = '';
4
+ for (const child of children) {
5
+ result += await state.one(child, state, node);
6
+ }
7
+ return result + 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 pre(node: ComarkElement, state: State): string;
@@ -0,0 +1,60 @@
1
+ import { textContent } from 'comark/utils';
2
+ export function pre(node, state) {
3
+ const [_, attributes, ...children] = node;
4
+ const codeClasses = children[0]?.[1]?.class;
5
+ const language = (attributes.language || (codeClasses?.split(' ').find(cls => cls.startsWith('language-')))?.slice(9)) || '';
6
+ // Escape ] in filename
7
+ const filename = attributes.filename
8
+ ? ' [' + String(attributes.filename).split(']').join('\\\\]') + ']'
9
+ : '';
10
+ const highlights = attributes.highlights
11
+ ? ' {' + formatHighlights(attributes.highlights) + '}'
12
+ : '';
13
+ // Meta always has a leading space
14
+ const meta = attributes.meta
15
+ ? ' ' + attributes.meta
16
+ : '';
17
+ const result = '```' + language + filename + highlights + meta + '\n'
18
+ + String(node[1]?.code || extractCode(node)).trim()
19
+ + '\n```';
20
+ return result + state.context.blockSeparator;
21
+ }
22
+ function extractCode(node) {
23
+ const codeNode = node[2];
24
+ if (Array.isArray(codeNode) && codeNode[0] === 'code') {
25
+ const spans = codeNode.slice(2);
26
+ const lineSpans = spans.filter(s => Array.isArray(s) && String(s[1]?.class ?? '').includes('line'));
27
+ if (lineSpans.length > 0) {
28
+ return lineSpans.map(span => textContent(span)).join('\n');
29
+ }
30
+ }
31
+ return textContent(node);
32
+ }
33
+ function formatHighlights(highlights) {
34
+ if (highlights.length === 0)
35
+ return '';
36
+ const sorted = [...highlights].sort((a, b) => a - b);
37
+ const ranges = [];
38
+ let start = sorted[0];
39
+ let end = sorted[0];
40
+ for (let i = 1; i <= sorted.length; i++) {
41
+ if (i < sorted.length && sorted[i] === end + 1) {
42
+ end = sorted[i];
43
+ }
44
+ else {
45
+ // Add the current range
46
+ if (start === end) {
47
+ ranges.push(String(start));
48
+ }
49
+ else {
50
+ ranges.push(start + '-' + end);
51
+ }
52
+ // Start a new range
53
+ if (i < sorted.length) {
54
+ start = sorted[i];
55
+ end = sorted[i];
56
+ }
57
+ }
58
+ }
59
+ return ranges.join(',');
60
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function strong(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,13 @@
1
+ import { comarkAttributes } from '../attributes';
2
+ export async function strong(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,8 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function table(node: ComarkElement, state: State): Promise<string>;
4
+ export declare function thead(_node: ComarkElement, _state: State): string;
5
+ export declare function tbody(_node: ComarkElement, _state: State): string;
6
+ export declare function tr(_node: ComarkElement, _state: State): string;
7
+ export declare function th(_node: ComarkElement, _state: State): string;
8
+ export declare function td(_node: ComarkElement, _state: State): string;
@@ -0,0 +1,180 @@
1
+ // Helper function to extract alignment from style attribute
2
+ function getAlignment(attributes) {
3
+ const style = attributes.style;
4
+ if (typeof style !== 'string') {
5
+ return null;
6
+ }
7
+ // Parse text-align from style attribute - normalize and check
8
+ const normalized = style.toLowerCase().split(' ').join('').split('\t').join('');
9
+ if (normalized.includes('text-align:left')) {
10
+ return 'left';
11
+ }
12
+ if (normalized.includes('text-align:center')) {
13
+ return 'center';
14
+ }
15
+ if (normalized.includes('text-align:right')) {
16
+ return 'right';
17
+ }
18
+ return null;
19
+ }
20
+ // Helper function to extract text content from a cell
21
+ async function getCellContent(cell, state) {
22
+ if (typeof cell === 'string') {
23
+ return escapePipes(cell);
24
+ }
25
+ const [, , ...children] = cell;
26
+ let content = '';
27
+ for (const child of children) {
28
+ if (typeof child === 'string') {
29
+ content += child;
30
+ }
31
+ else {
32
+ content += await state.one(child, state, cell);
33
+ }
34
+ }
35
+ return escapePipes(content.trim());
36
+ }
37
+ // Helper function to escape pipe characters and handle newlines
38
+ function escapePipes(text) {
39
+ // Replace newlines with spaces (tables don't support multi-line cells)
40
+ // Then escape pipe characters
41
+ return text.split('\n').join(' ').split('|').join('\\|');
42
+ }
43
+ // Helper function to get all rows from thead/tbody
44
+ function getRows(element) {
45
+ if (typeof element === 'string') {
46
+ return [];
47
+ }
48
+ const [tag, , ...children] = element;
49
+ // If it's a tr, return it directly
50
+ if (tag === 'tr') {
51
+ return [element];
52
+ }
53
+ // If it's thead/tbody, extract tr elements
54
+ if (tag === 'thead' || tag === 'tbody') {
55
+ return children.filter(child => typeof child !== 'string' && child[0] === 'tr');
56
+ }
57
+ return [];
58
+ }
59
+ // Helper function to get cells from a row
60
+ function getCells(row) {
61
+ const [, , ...children] = row;
62
+ return children.filter(child => typeof child !== 'string' && (child[0] === 'th' || child[0] === 'td'));
63
+ }
64
+ export async function table(node, state) {
65
+ const [, , ...children] = node;
66
+ // Extract thead and tbody
67
+ let headerRows = [];
68
+ let bodyRows = [];
69
+ for (const child of children) {
70
+ if (typeof child === 'string')
71
+ continue;
72
+ const [tag] = child;
73
+ if (tag === 'thead') {
74
+ headerRows = getRows(child);
75
+ }
76
+ else if (tag === 'tbody') {
77
+ bodyRows = getRows(child);
78
+ }
79
+ else if (tag === 'tr') {
80
+ // Direct tr children (no thead/tbody wrapper)
81
+ const cells = getCells(child);
82
+ if (cells.length > 0 && cells[0][0] === 'th') {
83
+ headerRows.push(child);
84
+ }
85
+ else {
86
+ bodyRows.push(child);
87
+ }
88
+ }
89
+ }
90
+ // If no header rows, create a default header based on the first body row
91
+ if (headerRows.length === 0 && bodyRows.length > 0) {
92
+ const firstRow = bodyRows[0];
93
+ const cells = getCells(firstRow);
94
+ const headerCells = cells.map((_, i) => ['th', {}, `Column ${i + 1}`]);
95
+ headerRows = [['tr', {}, ...headerCells]];
96
+ }
97
+ if (headerRows.length === 0) {
98
+ return '';
99
+ }
100
+ // Process header row (use the first header row)
101
+ const headerRow = headerRows[0];
102
+ const headerCells = getCells(headerRow);
103
+ const headerContent = [];
104
+ for (const cell of headerCells) {
105
+ headerContent.push(await getCellContent(cell, state));
106
+ }
107
+ // Extract alignment from header cells
108
+ const alignments = headerCells.map((cell) => {
109
+ const [, attributes] = cell;
110
+ return getAlignment(attributes);
111
+ });
112
+ // Calculate column widths (minimum 3 characters per column)
113
+ const columnWidths = headerContent.map(content => Math.max(3, content.length));
114
+ // Update column widths based on body content
115
+ for (const row of bodyRows) {
116
+ const cells = getCells(row);
117
+ for (let i = 0; i < cells.length; i++) {
118
+ if (i < columnWidths.length) {
119
+ const content = await getCellContent(cells[i], state);
120
+ columnWidths[i] = Math.max(columnWidths[i], content.length);
121
+ }
122
+ }
123
+ }
124
+ // Build the markdown table
125
+ let result = '| ';
126
+ result += headerContent.map((content, i) => content.padEnd(columnWidths[i])).join(' | ');
127
+ result += ' |\n';
128
+ // Add separator row with alignment
129
+ result += '| ';
130
+ result += columnWidths.map((width, i) => {
131
+ const alignment = alignments[i];
132
+ if (alignment === 'left') {
133
+ return ':' + '-'.repeat(width - 1);
134
+ }
135
+ else if (alignment === 'center') {
136
+ return ':' + '-'.repeat(width - 2) + ':';
137
+ }
138
+ else if (alignment === 'right') {
139
+ return '-'.repeat(width - 1) + ':';
140
+ }
141
+ return '-'.repeat(width);
142
+ }).join(' | ');
143
+ result += ' |\n';
144
+ // Add body rows
145
+ for (const row of bodyRows) {
146
+ const cells = getCells(row);
147
+ const cellContents = [];
148
+ for (let i = 0; i < cells.length; i++) {
149
+ const content = await getCellContent(cells[i], state);
150
+ cellContents.push(content.padEnd(columnWidths[i] || 0));
151
+ }
152
+ // Fill missing columns with empty cells
153
+ while (cellContents.length < columnWidths.length) {
154
+ cellContents.push(''.padEnd(columnWidths[cellContents.length]));
155
+ }
156
+ result += '| ' + cellContents.join(' | ') + ' |\n';
157
+ }
158
+ // result already ends with \n, so we only need to add one more \n
159
+ return result + '\n';
160
+ }
161
+ export function thead(_node, _state) {
162
+ // thead is handled by the table handler
163
+ return '';
164
+ }
165
+ export function tbody(_node, _state) {
166
+ // tbody is handled by the table handler
167
+ return '';
168
+ }
169
+ export function tr(_node, _state) {
170
+ // tr is handled by the table handler
171
+ return '';
172
+ }
173
+ export function th(_node, _state) {
174
+ // th is handled by the table handler
175
+ return '';
176
+ }
177
+ export function td(_node, _state) {
178
+ // td is handled by the table handler
179
+ return '';
180
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function template(node: ComarkElement, state: State, parent?: ComarkElement): Promise<string>;
@@ -0,0 +1,14 @@
1
+ // slot template
2
+ export async function template(node, state, parent) {
3
+ const [_, attrs] = node;
4
+ const content = (await state.flow(node, state)).trim();
5
+ // Omit #default marker if this is the only slot
6
+ if (attrs.name === 'default') {
7
+ const siblings = parent ? parent.slice(2) : [];
8
+ const templateCount = siblings.filter(child => Array.isArray(child) && child[0] === 'template').length;
9
+ if (templateCount === 1) {
10
+ return content + state.context.blockSeparator;
11
+ }
12
+ }
13
+ return `#${attrs.name}\n${content}` + state.context.blockSeparator;
14
+ }
@@ -0,0 +1,3 @@
1
+ import type { State } from 'comark/render';
2
+ import type { ComarkElement } from 'comark';
3
+ export declare function ul(node: ComarkElement, state: State): Promise<string>;
@@ -0,0 +1,18 @@
1
+ import { indent } from '../indent';
2
+ export async function ul(node, state) {
3
+ const children = node.slice(2);
4
+ const revert = state.applyContext({ list: true, order: false });
5
+ let result = '';
6
+ for (const child of children) {
7
+ result += await state.one(child, state);
8
+ }
9
+ result = result.trim();
10
+ if (revert.list) {
11
+ result = '\n' + indent(result);
12
+ }
13
+ else {
14
+ result = result + state.context.blockSeparator;
15
+ }
16
+ state.applyContext(revert);
17
+ return result;
18
+ }
@@ -0,0 +1,4 @@
1
+ export declare function indent(text: string, { ignoreFirstLine, level }?: {
2
+ ignoreFirstLine?: boolean;
3
+ level?: number;
4
+ }): string;
@@ -0,0 +1,8 @@
1
+ export function indent(text, { ignoreFirstLine = false, level = 1 } = {}) {
2
+ return text.split('\n').map((line, index) => {
3
+ if (ignoreFirstLine && index === 0) {
4
+ return line;
5
+ }
6
+ return line ? ' '.repeat(level) + line : line;
7
+ }).join('\n');
8
+ }
@@ -0,0 +1,13 @@
1
+ import type { State, Context } from 'comark/render';
2
+ import type { ComarkElement, ComarkNode } from 'comark';
3
+ /**
4
+ * Render a single node
5
+ * @param node - The node to render
6
+ * @param state - The state of the renderer
7
+ * @param parent - The parent node
8
+ * @returns The rendered node
9
+ */
10
+ export declare function one(node: ComarkNode, state: State, parent?: ComarkElement): Promise<string>;
11
+ export declare function flow(node: ComarkElement, state: State, parent?: ComarkElement): Promise<string>;
12
+ export declare function createState(ctx?: Partial<Context>): State;
13
+ export declare const state: State;
@@ -0,0 +1,121 @@
1
+ import { handlers } from "./handlers/index.js";
2
+ import { pascalCase } from "../../utils/index.js";
3
+ /**
4
+ * Render a single node
5
+ * @param node - The node to render
6
+ * @param state - The state of the renderer
7
+ * @param parent - The parent node
8
+ * @returns The rendered node
9
+ */
10
+ export async function one(node, state, parent) {
11
+ if (typeof node === 'string') {
12
+ if (state.context.html) {
13
+ return escapeHtml(node);
14
+ }
15
+ return node;
16
+ }
17
+ if (node[0] === null) {
18
+ return await state.handlers.comment(node, state);
19
+ }
20
+ const userHandler = state.context.handlers[node[0]] || state.context.handlers[pascalCase(node[0])];
21
+ if (userHandler) {
22
+ return await userHandler(node, state, parent);
23
+ }
24
+ if (state.context.html || node[1].$?.html === 1) {
25
+ return await state.handlers.html(node, state, parent);
26
+ }
27
+ const nodeHandler = state.handlers[node[0]];
28
+ if (nodeHandler) {
29
+ return await nodeHandler(node, state, parent);
30
+ }
31
+ return state.context.format === 'markdown/comark'
32
+ ? await state.handlers.mdc(node, state, parent)
33
+ : await state.handlers.html(node, state, parent);
34
+ }
35
+ export async function flow(node, state, parent) {
36
+ const children = node.slice(2);
37
+ let result = '';
38
+ for (const child of children) {
39
+ result += await one(child, state, parent || node);
40
+ }
41
+ return result;
42
+ }
43
+ export function createState(ctx = {}) {
44
+ const context = {
45
+ ...ctx,
46
+ blockSeparator: ctx.blockSeparator || '\n\n',
47
+ format: ctx.format || 'markdown/comark',
48
+ handlers: ctx.handlers || {}, // user defined node handlers
49
+ // Enable html mode for text/html format
50
+ html: ctx.format === 'text/html',
51
+ };
52
+ const state = {
53
+ handlers,
54
+ context,
55
+ one,
56
+ flow,
57
+ data: ctx.data || {},
58
+ render: async (input) => {
59
+ if (Array.isArray(input) && typeof input[0] === 'string' && input.length > 1) {
60
+ return state.one(input, state);
61
+ }
62
+ let result = '';
63
+ for (const child of input) {
64
+ result += await state.one(child, state);
65
+ }
66
+ return result;
67
+ },
68
+ applyContext: (edit) => {
69
+ const revert = {};
70
+ for (const [key, value] of Object.entries(edit)) {
71
+ revert[key] = context[key];
72
+ context[key] = value;
73
+ }
74
+ return revert;
75
+ },
76
+ };
77
+ return state;
78
+ }
79
+ export const state = {
80
+ handlers,
81
+ data: {},
82
+ context: {
83
+ blockSeparator: '\n\n',
84
+ format: 'markdown/comark',
85
+ handlers: {}, // user defined node handlers
86
+ },
87
+ flow,
88
+ one,
89
+ render: async (input) => {
90
+ if (typeof input === 'string') {
91
+ return input;
92
+ }
93
+ if (Array.isArray(input) && typeof input[0] === 'string') {
94
+ return one(input, state);
95
+ }
96
+ let result = '';
97
+ for (const child of input) {
98
+ result += await one(child, state);
99
+ }
100
+ return result;
101
+ },
102
+ applyContext: (edit) => {
103
+ const revert = {};
104
+ for (const [key, value] of Object.entries(edit)) {
105
+ revert[key] = state.context[key];
106
+ state.context[key] = value;
107
+ }
108
+ return revert;
109
+ },
110
+ };
111
+ /**
112
+ * Escape HTML special characters
113
+ */
114
+ function escapeHtml(text) {
115
+ const map = {
116
+ '<': '&lt;',
117
+ '>': '&gt;',
118
+ '&amp;': '&',
119
+ };
120
+ return text.replace(/[<>]/g, char => map[char]);
121
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parse YAML content
3
+ * @param content - The content to parse
4
+ * @returns The parsed data
5
+ */
6
+ export declare function parseYaml(content: string): Record<string, unknown>;
7
+ /**
8
+ * Stringify YAML data
9
+ * @param data - The data to stringify
10
+ * @returns The stringified data
11
+ */
12
+ export declare function stringifyYaml(data: Record<string, unknown>): string;
@@ -0,0 +1,51 @@
1
+ import { dump, JSON_SCHEMA, load } from 'js-yaml';
2
+ /**
3
+ * Parse YAML content
4
+ * @param content - The content to parse
5
+ * @returns The parsed data
6
+ */
7
+ export function parseYaml(content) {
8
+ return load(content, { schema: JSON_SCHEMA });
9
+ }
10
+ /**
11
+ * Stringify YAML data
12
+ * @param data - The data to stringify
13
+ * @returns The stringified data
14
+ */
15
+ export function stringifyYaml(data) {
16
+ const yamlOutput = dump(data, {
17
+ indent: 2,
18
+ replacer: (_key, value) => {
19
+ if (value === 'true')
20
+ return true;
21
+ if (value === 'false')
22
+ return false;
23
+ return value;
24
+ },
25
+ });
26
+ /**
27
+ * js-yaml wraps keys with quotes if they start with a colon. This function removes the quotes.
28
+ * `':test': true` becomes `:test: true`
29
+ *
30
+ * Using js-yaml and this function is faster than using other libraries like yaml.
31
+ */
32
+ const lines = yamlOutput.split('\n');
33
+ for (let i = 0; i < lines.length; i++) {
34
+ const line = lines[i];
35
+ const trimmed = line.trimStart();
36
+ // Check if line starts with a quote followed by colon
37
+ if (trimmed[0] === '\'' || trimmed[0] === '"') {
38
+ const quote = trimmed[0];
39
+ if (trimmed[1] === ':') {
40
+ // Find the closing quote
41
+ const quoteEnd = trimmed.indexOf(quote, 1);
42
+ if (quoteEnd > 1 && trimmed[quoteEnd + 1] === ':') {
43
+ // Remove quotes: keep indentation + unquoted key + rest
44
+ const indent = line.length - trimmed.length;
45
+ lines[i] = ' '.repeat(indent) + trimmed.slice(1, quoteEnd) + trimmed.slice(quoteEnd + 1);
46
+ }
47
+ }
48
+ }
49
+ }
50
+ return lines.join('\n');
51
+ }
@@ -0,0 +1,66 @@
1
+ import type { ComarkParseFn, ParseOptions, ComarkTree } from './types.ts';
2
+ export { parseFrontmatter } from './internal/frontmatter.ts';
3
+ /**
4
+ * Creates a parser function for Comark content.
5
+ *
6
+ * Returns an async function that takes a markdown string and returns a Promise resolving to a ComarkTree AST.
7
+ * The returned parser applies frontmatter extraction, Comark syntax parsing, token-to-AST conversion,
8
+ * auto-closing of incomplete markdown, optional AST transformations and plugin hooks.
9
+ *
10
+ * @param options - Parser options controlling parsing behavior.
11
+ * @returns An async parser function: (markdown) => Promise<ComarkTree>
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { createParse } from 'comark'
16
+ *
17
+ * const parse = createParse({ autoUnwrap: false })
18
+ * const tree = await parse('# Hello **World**\n::alert\nhi\n::')
19
+ * console.log(tree.nodes)
20
+ * // → [ ['h1', { id: 'hello-world' }, 'Hello ', ['strong', {}, 'World'] ], ['alert', {}, 'hi'] ]
21
+ *
22
+ * // Enable HTML parsing (on by default) — HTML tags are included in the AST
23
+ * const parseWithHtml = createParse({ html: true })
24
+ * const tree2 = await parseWithHtml('<strong class="bold">Hello</strong> _world_')
25
+ * console.log(tree2.nodes)
26
+ * // → [ ['strong', { class: 'bold' }, 'Hello'], ' ', ['em', {}, 'world'] ]
27
+ *
28
+ * // Disable HTML parsing — HTML tags are treated as plain text
29
+ * const parseNoHtml = createParse({ html: false })
30
+ * ```
31
+ */
32
+ export declare function createParse(options?: ParseOptions): ComarkParseFn;
33
+ /**
34
+ * Parse Comark content from a string
35
+ *
36
+ * @param markdown - The markdown/Comark content as a string
37
+ * @param options - Parser options
38
+ * @returns ComarkTree - The parsed AST tree
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import { parse } from 'comark'
43
+ *
44
+ * const content = `---
45
+ * title: Hello World
46
+ * ---
47
+ *
48
+ * # Hello World
49
+ *
50
+ * This is a **markdown** document with *Comark* components.
51
+ *
52
+ * ::alert{type="info"}
53
+ * This is an alert component
54
+ * ::
55
+ * `
56
+ *
57
+ * const tree = await parse(content)
58
+ * console.log(tree.nodes) // Array of AST nodes
59
+ * console.log(tree.frontmatter) // { title: 'Hello World' }
60
+ * console.log(tree.meta) // Additional metadata
61
+ *
62
+ * // Disable auto-unwrap
63
+ * const tree2 = await parse(content, { autoUnwrap: false })
64
+ * ```
65
+ */
66
+ export declare function parse(markdown: string, options?: ParseOptions): Promise<ComarkTree>;