comark 0.3.1 → 0.3.2
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/dist/devtools/index.d.ts +1 -0
- package/dist/devtools/index.js +1 -0
- package/dist/devtools/register.d.ts +1 -0
- package/dist/devtools/register.js +1 -0
- package/dist/devtools/registry.d.ts +1 -0
- package/dist/devtools/registry.js +1 -0
- package/dist/devtools/vite.d.ts +1 -0
- package/dist/devtools/vite.js +1 -0
- package/dist/internal/frontmatter.d.ts +1 -0
- package/dist/internal/frontmatter.js +4 -2
- package/dist/internal/parse/auto-close/index.js +25 -13
- package/dist/internal/parse/auto-close/table.js +12 -9
- package/dist/internal/parse/auto-unwrap.js +2 -10
- package/dist/internal/parse/html/html_block_rule.js +1 -1
- package/dist/internal/parse/html/html_inline_rule.js +3 -7
- package/dist/internal/parse/html/html_re.js +1 -1
- package/dist/internal/parse/html/index.js +14 -2
- package/dist/internal/parse/syntax/block-params.d.ts +9 -0
- package/dist/internal/parse/syntax/block-params.js +48 -0
- package/dist/internal/parse/syntax/brackets.d.ts +8 -0
- package/dist/internal/parse/syntax/brackets.js +20 -0
- package/dist/internal/parse/syntax/props.d.ts +5 -0
- package/dist/internal/parse/syntax/props.js +119 -0
- package/dist/internal/parse/token-processor.js +25 -24
- package/dist/internal/props-validation.js +4 -9
- package/dist/internal/stringify/attributes.js +4 -1
- package/dist/internal/stringify/handlers/a.js +1 -3
- package/dist/internal/stringify/handlers/blockquote.js +2 -4
- package/dist/internal/stringify/handlers/code.js +1 -3
- package/dist/internal/stringify/handlers/emphesis.js +1 -3
- package/dist/internal/stringify/handlers/html.js +26 -16
- package/dist/internal/stringify/handlers/img.js +1 -3
- package/dist/internal/stringify/handlers/li.js +14 -8
- package/dist/internal/stringify/handlers/mdc.js +2 -3
- package/dist/internal/stringify/handlers/ol.js +1 -1
- package/dist/internal/stringify/handlers/p.d.ts +1 -1
- package/dist/internal/stringify/handlers/p.js +4 -1
- package/dist/internal/stringify/handlers/pre.js +10 -13
- package/dist/internal/stringify/handlers/strong.js +1 -3
- package/dist/internal/stringify/handlers/table.js +7 -5
- package/dist/internal/stringify/handlers/template.js +1 -1
- package/dist/internal/stringify/handlers/ul.js +1 -1
- package/dist/internal/stringify/indent.d.ts +1 -5
- package/dist/internal/stringify/indent.js +1 -9
- package/dist/internal/stringify/state.js +1 -1
- package/dist/internal/yaml.js +1 -1
- package/dist/parse.js +14 -8
- package/dist/plugins/alert.js +1 -1
- package/dist/plugins/binding.js +1 -3
- package/dist/plugins/breaks.js +1 -1
- package/dist/plugins/emoji.js +8 -8
- package/dist/plugins/footnotes.js +19 -13
- package/dist/plugins/headings.js +2 -4
- package/dist/plugins/highlight.d.ts +1 -11
- package/dist/plugins/highlight.js +198 -103
- package/dist/plugins/json-render.js +5 -9
- package/dist/plugins/math.js +4 -6
- package/dist/plugins/mermaid.js +6 -20
- package/dist/plugins/punctuation.js +5 -6
- package/dist/plugins/security.js +2 -2
- package/dist/plugins/syntax.d.ts +49 -0
- package/dist/plugins/syntax.js +522 -0
- package/dist/plugins/task-list.d.ts +1 -1
- package/dist/plugins/task-list.js +11 -8
- package/dist/plugins/toc.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/utils/comark.tmLanguage.d.ts +335 -0
- package/dist/utils/comark.tmLanguage.js +597 -0
- package/dist/utils/helpers.js +1 -3
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +25 -3
- package/package.json +39 -40
- package/skills/skills/comark/AGENTS.md +0 -261
- package/skills/skills/comark/SKILL.md +0 -489
- package/skills/skills/comark/references/markdown-syntax.md +0 -599
- package/skills/skills/comark/references/parsing-ast.md +0 -378
- package/skills/skills/comark/references/rendering-react.md +0 -445
- package/skills/skills/comark/references/rendering-svelte.md +0 -453
- package/skills/skills/comark/references/rendering-vue.md +0 -462
- /package/skills/{skills/migrate-mdc-to-comark → migrate-mdc-to-comark}/SKILL.md +0 -0
|
@@ -83,7 +83,10 @@ export function comarkAttributes(attributes) {
|
|
|
83
83
|
return `#${value}`;
|
|
84
84
|
}
|
|
85
85
|
if (key === 'class') {
|
|
86
|
-
return value
|
|
86
|
+
return value
|
|
87
|
+
.split(' ')
|
|
88
|
+
.map((c) => `.${c}`)
|
|
89
|
+
.join('');
|
|
87
90
|
}
|
|
88
91
|
if (typeof value === 'object') {
|
|
89
92
|
return `${key}="${JSON.stringify(value).replace(/"/g, '\\"')}"`;
|
|
@@ -3,9 +3,7 @@ import { comarkAttributes } from "../attributes.js";
|
|
|
3
3
|
export async function a(node, state) {
|
|
4
4
|
const [_, attrs] = node;
|
|
5
5
|
const { href, ...rest } = attrs;
|
|
6
|
-
const attrsString = Object.keys(rest).length > 0
|
|
7
|
-
? comarkAttributes(rest)
|
|
8
|
-
: '';
|
|
6
|
+
const attrsString = Object.keys(rest).length > 0 ? comarkAttributes(rest) : '';
|
|
9
7
|
const content = await state.flow(node, state);
|
|
10
8
|
if (content === href && !attrsString) {
|
|
11
9
|
return `<${href}>`;
|
|
@@ -7,12 +7,10 @@ export async function blockquote(node, state) {
|
|
|
7
7
|
const content = childResult
|
|
8
8
|
.trim()
|
|
9
9
|
.split('\n')
|
|
10
|
-
.map(line => line ? `> ${line}` : '>')
|
|
10
|
+
.map((line) => (line ? `> ${line}` : '>'))
|
|
11
11
|
.join('\n');
|
|
12
12
|
if (node[1].as) {
|
|
13
|
-
return `> [!${String(node[1].as).toUpperCase()}]\n`
|
|
14
|
-
+ content
|
|
15
|
-
+ state.context.blockSeparator;
|
|
13
|
+
return `> [!${String(node[1].as).toUpperCase()}]\n` + content + state.context.blockSeparator;
|
|
16
14
|
}
|
|
17
15
|
return content + state.context.blockSeparator;
|
|
18
16
|
}
|
|
@@ -2,9 +2,7 @@ import { comarkAttributes } from "../attributes.js";
|
|
|
2
2
|
import { textContent } from "../../../utils/index.js";
|
|
3
3
|
export function code(node, _state) {
|
|
4
4
|
const [_, attrs] = node;
|
|
5
|
-
const attrsString = Object.keys(attrs).length > 0
|
|
6
|
-
? comarkAttributes(attrs)
|
|
7
|
-
: '';
|
|
5
|
+
const attrsString = Object.keys(attrs).length > 0 ? comarkAttributes(attrs) : '';
|
|
8
6
|
const content = textContent(node);
|
|
9
7
|
const fence = content.includes('`') ? '``' : '`';
|
|
10
8
|
return `${fence}${content}${fence}${attrsString}`;
|
|
@@ -6,8 +6,6 @@ export async function emphesis(node, state) {
|
|
|
6
6
|
content += await state.one(child, state, node);
|
|
7
7
|
}
|
|
8
8
|
content = content.trim();
|
|
9
|
-
const attrsString = Object.keys(attrs).length > 0
|
|
10
|
-
? comarkAttributes(attrs)
|
|
11
|
-
: '';
|
|
9
|
+
const attrsString = Object.keys(attrs).length > 0 ? comarkAttributes(attrs) : '';
|
|
12
10
|
return `*${content}*${attrsString}`;
|
|
13
11
|
}
|
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import { htmlAttributes } from "../attributes.js";
|
|
2
|
-
import { indent } from "
|
|
2
|
+
import { indent } from "../../../utils/index.js";
|
|
3
3
|
const textBlocks = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'td', 'th']);
|
|
4
4
|
const selfCloseTags = new Set(['br', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr']);
|
|
5
5
|
const inlineTags = new Set(['strong', 'em', 'code', 'a', 'br', 'span', 'img']);
|
|
6
|
-
const blockTags = new Set([
|
|
6
|
+
const blockTags = new Set([
|
|
7
|
+
'p',
|
|
8
|
+
'h1',
|
|
9
|
+
'h2',
|
|
10
|
+
'h3',
|
|
11
|
+
'h4',
|
|
12
|
+
'h5',
|
|
13
|
+
'h6',
|
|
14
|
+
'li',
|
|
15
|
+
'ul',
|
|
16
|
+
'ol',
|
|
17
|
+
'blockquote',
|
|
18
|
+
'hr',
|
|
19
|
+
'table',
|
|
20
|
+
'td',
|
|
21
|
+
'th',
|
|
22
|
+
]);
|
|
7
23
|
export async function html(node, state, parent) {
|
|
8
24
|
const [tag, attr, ...children] = node;
|
|
9
25
|
const { $ = {}, ...rawAttributes } = attr;
|
|
@@ -15,11 +31,9 @@ export async function html(node, state, parent) {
|
|
|
15
31
|
// back to the raw (empty) attrs to avoid leaking parent props onto native
|
|
16
32
|
// wrappers like `<p>` or `<ul>`.
|
|
17
33
|
const rawHasAttrs = Object.keys(rawAttributes).length > 0;
|
|
18
|
-
const attributes = state.context.html
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const hasOnlyTextChildren = children.every(child => typeof child === 'string' || inlineTags.has(String(child?.[0])));
|
|
22
|
-
const hasTextSibling = children.some(child => typeof child === 'string');
|
|
34
|
+
const attributes = state.context.html ? (rawHasAttrs ? state.renderData.props : rawAttributes) : rawAttributes;
|
|
35
|
+
const hasOnlyTextChildren = children.every((child) => typeof child === 'string' || inlineTags.has(String(child?.[0])));
|
|
36
|
+
const hasTextSibling = children.some((child) => typeof child === 'string');
|
|
23
37
|
const isBlock = textBlocks.has(String(tag));
|
|
24
38
|
const isInline = inlineTags.has(String(tag)) && $.block === 0;
|
|
25
39
|
let oneLiner = isBlock && hasOnlyTextChildren;
|
|
@@ -48,7 +62,8 @@ export async function html(node, state, parent) {
|
|
|
48
62
|
for (let i = 0; i < children.length; i++) {
|
|
49
63
|
const childContent = childrenContent[i];
|
|
50
64
|
const child = children[i];
|
|
51
|
-
const isBlock = typeof child !== 'string' &&
|
|
65
|
+
const isBlock = typeof child !== 'string' &&
|
|
66
|
+
(blockTags.has(String(child?.[0])) || (!inlineTags.has(String(child?.[0])) && !hasTextSibling));
|
|
52
67
|
if (i > 0 && !isPrevBlock && isBlock) {
|
|
53
68
|
content += state.context.blockSeparator;
|
|
54
69
|
}
|
|
@@ -62,23 +77,18 @@ export async function html(node, state, parent) {
|
|
|
62
77
|
if (revert) {
|
|
63
78
|
state.applyContext(revert);
|
|
64
79
|
}
|
|
65
|
-
const attrs = Object.keys(attributes).length > 0
|
|
66
|
-
? ` ${htmlAttributes(attributes)}`
|
|
67
|
-
: '';
|
|
80
|
+
const attrs = Object.keys(attributes).length > 0 ? ` ${htmlAttributes(attributes)}` : '';
|
|
68
81
|
if (isSelfClose) {
|
|
69
82
|
return `<${tag}${attrs} />` + (!parent && !isInline ? state.context.blockSeparator : '');
|
|
70
83
|
}
|
|
71
84
|
if (!oneLiner && content) {
|
|
72
85
|
content = '\n' + paddNoneHtmlContent(content, state).trimEnd() + '\n';
|
|
73
86
|
}
|
|
74
|
-
return `<${tag}${attrs}>${content}</${tag}>`
|
|
75
|
-
+ (!parent && !isInline ? state.context.blockSeparator : '');
|
|
87
|
+
return `<${tag}${attrs}>${content}</${tag}>` + (!parent && !isInline ? state.context.blockSeparator : '');
|
|
76
88
|
}
|
|
77
89
|
function paddNoneHtmlContent(content, state) {
|
|
78
90
|
if (state.context.html) {
|
|
79
91
|
return indent(content);
|
|
80
92
|
}
|
|
81
|
-
return (
|
|
82
|
-
+ content
|
|
83
|
-
+ (content.trim().endsWith('>') ? '' : ''));
|
|
93
|
+
return (content.trim().startsWith('<') ? '' : '') + content + (content.trim().endsWith('>') ? '' : '');
|
|
84
94
|
}
|
|
@@ -2,8 +2,6 @@ import { comarkAttributes } from "../attributes.js";
|
|
|
2
2
|
export function img(node, _state) {
|
|
3
3
|
const [_, attrs] = node;
|
|
4
4
|
const { title, src, alt = '', ...rest } = attrs;
|
|
5
|
-
const attrsString = Object.keys(rest).length > 0
|
|
6
|
-
? comarkAttributes(rest)
|
|
7
|
-
: '';
|
|
5
|
+
const attrsString = Object.keys(rest).length > 0 ? comarkAttributes(rest) : '';
|
|
8
6
|
return title ? `` : `${attrsString}`;
|
|
9
7
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "
|
|
1
|
+
import { indent } from "../../../utils/index.js";
|
|
2
2
|
// Block elements that need explicit indentation in list items.
|
|
3
3
|
// Note: ol/ul are handled by their own handlers which manage indentation via listIndent context.
|
|
4
4
|
const blockElements = new Set(['pre', 'blockquote', 'table']);
|
|
@@ -18,14 +18,20 @@ export async function li(node, state) {
|
|
|
18
18
|
let result = '';
|
|
19
19
|
for (const child of children) {
|
|
20
20
|
const rendered = await state.one(child, state, node);
|
|
21
|
-
if (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
if (result && Array.isArray(child)) {
|
|
22
|
+
if (blockElements.has(child[0])) {
|
|
23
|
+
// Block-level child: put on its own line and indent to align with list prefix
|
|
24
|
+
const indented = indent(rendered, { width: prefixWidth });
|
|
25
|
+
result = result.trimEnd() + '\n' + indented.trimEnd() + '\n';
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (child[0] === 'p') {
|
|
29
|
+
const indented = indent(rendered, { width: prefixWidth });
|
|
30
|
+
result = result.trimEnd() + '\n\n' + indented.trimEnd() + '\n';
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
28
33
|
}
|
|
34
|
+
result += rendered;
|
|
29
35
|
}
|
|
30
36
|
result = result.trim();
|
|
31
37
|
if (!order) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indent } from "
|
|
1
|
+
import { indent } from "../../../utils/index.js";
|
|
2
2
|
import { comarkAttributes, comarkYamlAttributes } from "../attributes.js";
|
|
3
3
|
import { html } from "./html.js";
|
|
4
4
|
// HTML elements that always create an inline context for their children
|
|
@@ -28,8 +28,7 @@ export async function mdc(node, state, parent) {
|
|
|
28
28
|
content = content.trimEnd();
|
|
29
29
|
const attrs = attributeEntries.length > 0 ? comarkAttributes(attributes) : '';
|
|
30
30
|
if (tag === 'span') {
|
|
31
|
-
return `[${content}]${attrs}`
|
|
32
|
-
+ (inline ? '' : state.context.blockSeparator);
|
|
31
|
+
return `[${content}]${attrs}` + (inline ? '' : state.context.blockSeparator);
|
|
33
32
|
}
|
|
34
33
|
const fence = ':'.repeat((state.nodeDepthInTree || 0) + 2);
|
|
35
34
|
let result = `:${tag}${content && `[${content}]`}${attrs}` + (!parent ? state.context.blockSeparator : '');
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { State } from 'comark/render';
|
|
2
2
|
import type { ComarkElement } from 'comark';
|
|
3
|
-
export declare function p(node: ComarkElement, state: State): Promise<string>;
|
|
3
|
+
export declare function p(node: ComarkElement, state: State, parent?: ComarkElement): Promise<string>;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export async function p(node, state) {
|
|
1
|
+
export async function p(node, state, parent) {
|
|
2
2
|
const children = node.slice(2);
|
|
3
3
|
let result = '';
|
|
4
4
|
for (const child of children) {
|
|
5
5
|
result += await state.one(child, state, node);
|
|
6
6
|
}
|
|
7
|
+
if (parent?.[0] === 'li') {
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
7
10
|
return result + state.context.blockSeparator;
|
|
8
11
|
}
|
|
@@ -2,21 +2,18 @@ import { textContent } from "../../../utils/index.js";
|
|
|
2
2
|
export function pre(node, state) {
|
|
3
3
|
const [_, attributes, ...children] = node;
|
|
4
4
|
const codeClasses = children[0]?.[1]?.class;
|
|
5
|
-
const language =
|
|
5
|
+
const language = attributes.language ||
|
|
6
|
+
codeClasses
|
|
7
|
+
?.split(' ')
|
|
8
|
+
.find((cls) => cls.startsWith('language-'))
|
|
9
|
+
?.slice(9) ||
|
|
10
|
+
'';
|
|
6
11
|
// Escape ] in filename
|
|
7
|
-
const filename = attributes.filename
|
|
8
|
-
|
|
9
|
-
: '';
|
|
10
|
-
const highlights = attributes.highlights
|
|
11
|
-
? ' {' + formatHighlights(attributes.highlights) + '}'
|
|
12
|
-
: '';
|
|
12
|
+
const filename = attributes.filename ? ' [' + String(attributes.filename).split(']').join('\\\\]') + ']' : '';
|
|
13
|
+
const highlights = attributes.highlights ? ' {' + formatHighlights(attributes.highlights) + '}' : '';
|
|
13
14
|
// Meta always has a leading space
|
|
14
|
-
const meta = attributes.meta
|
|
15
|
-
|
|
16
|
-
: '';
|
|
17
|
-
const result = '```' + language + filename + highlights + meta + '\n'
|
|
18
|
-
+ String(node[1]?.code || textContent(node)).trim()
|
|
19
|
-
+ '\n```';
|
|
15
|
+
const meta = attributes.meta ? ' ' + attributes.meta : '';
|
|
16
|
+
const result = '```' + language + filename + highlights + meta + '\n' + String(node[1]?.code || textContent(node)).trim() + '\n```';
|
|
20
17
|
return result + state.context.blockSeparator;
|
|
21
18
|
}
|
|
22
19
|
function formatHighlights(highlights) {
|
|
@@ -6,8 +6,6 @@ export async function strong(node, state) {
|
|
|
6
6
|
content += await state.one(child, state, node);
|
|
7
7
|
}
|
|
8
8
|
content = content.trim();
|
|
9
|
-
const attrsString = Object.keys(attrs).length > 0
|
|
10
|
-
? comarkAttributes(attrs)
|
|
11
|
-
: '';
|
|
9
|
+
const attrsString = Object.keys(attrs).length > 0 ? comarkAttributes(attrs) : '';
|
|
12
10
|
return `**${content}**${attrsString}`;
|
|
13
11
|
}
|
|
@@ -52,14 +52,14 @@ function getRows(element) {
|
|
|
52
52
|
}
|
|
53
53
|
// If it's thead/tbody, extract tr elements
|
|
54
54
|
if (tag === 'thead' || tag === 'tbody') {
|
|
55
|
-
return children.filter(child => typeof child !== 'string' && child[0] === 'tr');
|
|
55
|
+
return children.filter((child) => typeof child !== 'string' && child[0] === 'tr');
|
|
56
56
|
}
|
|
57
57
|
return [];
|
|
58
58
|
}
|
|
59
59
|
// Helper function to get cells from a row
|
|
60
60
|
function getCells(row) {
|
|
61
61
|
const [, , ...children] = row;
|
|
62
|
-
return children.filter(child => typeof child !== 'string' && (child[0] === 'th' || child[0] === 'td'));
|
|
62
|
+
return children.filter((child) => typeof child !== 'string' && (child[0] === 'th' || child[0] === 'td'));
|
|
63
63
|
}
|
|
64
64
|
export async function table(node, state) {
|
|
65
65
|
const [, , ...children] = node;
|
|
@@ -110,7 +110,7 @@ export async function table(node, state) {
|
|
|
110
110
|
return getAlignment(attributes);
|
|
111
111
|
});
|
|
112
112
|
// Calculate column widths (minimum 3 characters per column)
|
|
113
|
-
const columnWidths = headerContent.map(content => Math.max(3, content.length));
|
|
113
|
+
const columnWidths = headerContent.map((content) => Math.max(3, content.length));
|
|
114
114
|
// Update column widths based on body content
|
|
115
115
|
for (const row of bodyRows) {
|
|
116
116
|
const cells = getCells(row);
|
|
@@ -127,7 +127,8 @@ export async function table(node, state) {
|
|
|
127
127
|
result += ' |\n';
|
|
128
128
|
// Add separator row with alignment
|
|
129
129
|
result += '| ';
|
|
130
|
-
result += columnWidths
|
|
130
|
+
result += columnWidths
|
|
131
|
+
.map((width, i) => {
|
|
131
132
|
const alignment = alignments[i];
|
|
132
133
|
if (alignment === 'left') {
|
|
133
134
|
return ':' + '-'.repeat(width - 1);
|
|
@@ -139,7 +140,8 @@ export async function table(node, state) {
|
|
|
139
140
|
return '-'.repeat(width - 1) + ':';
|
|
140
141
|
}
|
|
141
142
|
return '-'.repeat(width);
|
|
142
|
-
})
|
|
143
|
+
})
|
|
144
|
+
.join(' | ');
|
|
143
145
|
result += ' |\n';
|
|
144
146
|
// Add body rows
|
|
145
147
|
for (const row of bodyRows) {
|
|
@@ -5,7 +5,7 @@ export async function template(node, state, parent) {
|
|
|
5
5
|
// Omit #default marker if this is the only slot
|
|
6
6
|
if (attrs.name === 'default') {
|
|
7
7
|
const siblings = parent ? parent.slice(2) : [];
|
|
8
|
-
const templateCount = siblings.filter(child => Array.isArray(child) && child[0] === 'template').length;
|
|
8
|
+
const templateCount = siblings.filter((child) => Array.isArray(child) && child[0] === 'template').length;
|
|
9
9
|
if (templateCount === 1) {
|
|
10
10
|
return content + state.context.blockSeparator;
|
|
11
11
|
}
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
const pad = width ? ' '.repeat(width) : ' '.repeat(level);
|
|
3
|
-
return text.split('\n').map((line, index) => {
|
|
4
|
-
if (ignoreFirstLine && index === 0) {
|
|
5
|
-
return line;
|
|
6
|
-
}
|
|
7
|
-
return line ? pad + line : line;
|
|
8
|
-
}).join('\n');
|
|
9
|
-
}
|
|
1
|
+
export * from '../../../src/internal/stringify/indent.ts'
|
package/dist/internal/yaml.js
CHANGED
|
@@ -36,7 +36,7 @@ export function stringifyYaml(data, options) {
|
|
|
36
36
|
const line = lines[i];
|
|
37
37
|
const trimmed = line.trimStart();
|
|
38
38
|
// Check if line starts with a quote followed by colon
|
|
39
|
-
if (trimmed[0] === '
|
|
39
|
+
if (trimmed[0] === "'" || trimmed[0] === '"') {
|
|
40
40
|
const quote = trimmed[0];
|
|
41
41
|
if (trimmed[1] === ':') {
|
|
42
42
|
// Find the closing quote
|
package/dist/parse.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import MarkdownExit from 'markdown-exit';
|
|
2
|
-
import
|
|
2
|
+
import syntax from "./plugins/syntax.js";
|
|
3
3
|
import taskList from "./plugins/task-list.js";
|
|
4
4
|
import alert from "./plugins/alert.js";
|
|
5
5
|
import { applyAutoUnwrap } from "./internal/parse/auto-unwrap.js";
|
|
@@ -45,14 +45,13 @@ export { defineComarkPlugin } from "./utils/helpers.js";
|
|
|
45
45
|
*/
|
|
46
46
|
export function createParse(options = {}) {
|
|
47
47
|
const { autoUnwrap = true, autoClose = true, plugins = [] } = options;
|
|
48
|
+
plugins.unshift(syntax());
|
|
48
49
|
plugins.unshift(taskList());
|
|
49
50
|
plugins.unshift(alert());
|
|
50
51
|
const parser = new MarkdownExit({
|
|
51
52
|
html: false,
|
|
52
53
|
linkify: true,
|
|
53
|
-
})
|
|
54
|
-
.enable(['table', 'strikethrough'])
|
|
55
|
-
.use(pluginMdc);
|
|
54
|
+
}).enable(['table', 'strikethrough']);
|
|
56
55
|
if (options.html !== false) {
|
|
57
56
|
parser.inline.ruler.before('text', 'comark_html_inline', html_inline);
|
|
58
57
|
parser.block.ruler.before('html_block', 'comark_html_block', html_block, {
|
|
@@ -60,7 +59,7 @@ export function createParse(options = {}) {
|
|
|
60
59
|
});
|
|
61
60
|
}
|
|
62
61
|
for (const plugin of plugins) {
|
|
63
|
-
for (const markdownItPlugin of
|
|
62
|
+
for (const markdownItPlugin of plugin.markdownItPlugins || []) {
|
|
64
63
|
parser.use(markdownItPlugin);
|
|
65
64
|
}
|
|
66
65
|
}
|
|
@@ -76,7 +75,8 @@ export function createParse(options = {}) {
|
|
|
76
75
|
reusableNodes: [],
|
|
77
76
|
};
|
|
78
77
|
const prevOutput = lastOutput;
|
|
79
|
-
|
|
78
|
+
const isStartsWithLastInput = markdown.startsWith(lastInput ?? '');
|
|
79
|
+
if (opts.streaming && prevOutput && isStartsWithLastInput) {
|
|
80
80
|
const { remainingMarkdownStartLine, reusedNodes, remainingMarkdown } = extractReusableNodes(markdown, prevOutput);
|
|
81
81
|
// If there is no remaining markdown, return the previous output
|
|
82
82
|
if (!remainingMarkdown)
|
|
@@ -91,7 +91,13 @@ export function createParse(options = {}) {
|
|
|
91
91
|
for (const plugin of options.plugins || []) {
|
|
92
92
|
await plugin.pre?.(state);
|
|
93
93
|
}
|
|
94
|
-
const { content, data } = parseFrontmatter(state.markdown);
|
|
94
|
+
const { content, data, frontmatterText } = parseFrontmatter(state.markdown);
|
|
95
|
+
// Count frontmatter lines for line number tracking
|
|
96
|
+
if (content && frontmatterText) {
|
|
97
|
+
state.parsedLines +=
|
|
98
|
+
frontmatterText.split('\n').length + // Number of lines in frontmatter
|
|
99
|
+
1; // Separator line
|
|
100
|
+
}
|
|
95
101
|
try {
|
|
96
102
|
state.tokens = parser.parse(content, {});
|
|
97
103
|
}
|
|
@@ -114,7 +120,7 @@ export function createParse(options = {}) {
|
|
|
114
120
|
}
|
|
115
121
|
if (opts.streaming) {
|
|
116
122
|
state.tree = {
|
|
117
|
-
frontmatter:
|
|
123
|
+
frontmatter: frontmatterText ? data : (prevOutput?.frontmatter ?? data),
|
|
118
124
|
meta: {},
|
|
119
125
|
nodes: [...state.reusableNodes, ...nodes],
|
|
120
126
|
};
|
package/dist/plugins/alert.js
CHANGED
|
@@ -30,7 +30,7 @@ const markers = {
|
|
|
30
30
|
export default defineComarkPlugin(() => ({
|
|
31
31
|
name: 'alert',
|
|
32
32
|
post(state) {
|
|
33
|
-
visit(state.tree, node => Array.isArray(node) && node[0] === 'blockquote', (node) => {
|
|
33
|
+
visit(state.tree, (node) => Array.isArray(node) && node[0] === 'blockquote', (node) => {
|
|
34
34
|
const element = node;
|
|
35
35
|
if (node[2]?.[0] === 'span') {
|
|
36
36
|
const content = String(node[2][2]).toUpperCase();
|
package/dist/plugins/binding.js
CHANGED
|
@@ -36,9 +36,7 @@ const markdownItInlineBinding = (md, options = {}) => {
|
|
|
36
36
|
export default defineComarkPlugin((opts = {}) => {
|
|
37
37
|
return {
|
|
38
38
|
name: 'binding',
|
|
39
|
-
markdownItPlugins: [
|
|
40
|
-
((md) => markdownItInlineBinding(md, opts)),
|
|
41
|
-
],
|
|
39
|
+
markdownItPlugins: [((md) => markdownItInlineBinding(md, opts))],
|
|
42
40
|
};
|
|
43
41
|
});
|
|
44
42
|
/**
|
package/dist/plugins/breaks.js
CHANGED
|
@@ -3,7 +3,7 @@ import { visit } from "../utils/index.js";
|
|
|
3
3
|
export default defineComarkPlugin(() => ({
|
|
4
4
|
name: 'breaks',
|
|
5
5
|
post(state) {
|
|
6
|
-
visit(state.tree, node => Array.isArray(node) && node.length > 2, (node) => {
|
|
6
|
+
visit(state.tree, (node) => Array.isArray(node) && node.length > 2, (node) => {
|
|
7
7
|
const parent = node;
|
|
8
8
|
const newParent = [parent[0], parent[1]];
|
|
9
9
|
let hasModified = false;
|
package/dist/plugins/emoji.js
CHANGED
|
@@ -386,7 +386,7 @@ const emojiRule = (state, silent) => {
|
|
|
386
386
|
const max = state.posMax;
|
|
387
387
|
const start = state.pos;
|
|
388
388
|
// Quick check: must start with ':'
|
|
389
|
-
if (state.src.charCodeAt(start) !==
|
|
389
|
+
if (state.src.charCodeAt(start) !== 0x3a /* : */) {
|
|
390
390
|
return false;
|
|
391
391
|
}
|
|
392
392
|
// Find the closing ':'
|
|
@@ -394,7 +394,7 @@ const emojiRule = (state, silent) => {
|
|
|
394
394
|
while (pos < max) {
|
|
395
395
|
const code = state.src.charCodeAt(pos);
|
|
396
396
|
// Found closing ':'
|
|
397
|
-
if (code ===
|
|
397
|
+
if (code === 0x3a /* : */) {
|
|
398
398
|
const emojiName = state.src.slice(start + 1, pos);
|
|
399
399
|
// Check if this is a valid emoji
|
|
400
400
|
const emojiChar = EMOJI_MAP.get(emojiName);
|
|
@@ -412,12 +412,12 @@ const emojiRule = (state, silent) => {
|
|
|
412
412
|
}
|
|
413
413
|
// Only allow word characters, digits, underscores, hyphens, and plus
|
|
414
414
|
// This matches the pattern of valid emoji names
|
|
415
|
-
if ((code >= 0x61 && code <=
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
415
|
+
if ((code >= 0x61 && code <= 0x7a) || // a-z
|
|
416
|
+
(code >= 0x41 && code <= 0x5a) || // A-Z
|
|
417
|
+
(code >= 0x30 && code <= 0x39) || // 0-9
|
|
418
|
+
code === 0x5f || // _
|
|
419
|
+
code === 0x2d || // -
|
|
420
|
+
code === 0x2b // +
|
|
421
421
|
) {
|
|
422
422
|
pos++;
|
|
423
423
|
continue;
|
|
@@ -9,10 +9,7 @@ const FOOTNOTE_DEF_RE = /^\[\^([^\s\]]+)\]:[ \t]?(.*)$/gm;
|
|
|
9
9
|
* on every node in the tree.
|
|
10
10
|
*/
|
|
11
11
|
function maybeFootnoteRef(node) {
|
|
12
|
-
return Array.isArray(node)
|
|
13
|
-
&& node[0] === 'span'
|
|
14
|
-
&& node.length === 3
|
|
15
|
-
&& typeof node[2] === 'string';
|
|
12
|
+
return Array.isArray(node) && node[0] === 'span' && node.length === 3 && typeof node[2] === 'string';
|
|
16
13
|
}
|
|
17
14
|
/**
|
|
18
15
|
* Check if a node is a footnote reference: ['span', {}, '^label']
|
|
@@ -29,12 +26,12 @@ function isFootnoteRef(node) {
|
|
|
29
26
|
}
|
|
30
27
|
const child = node[2];
|
|
31
28
|
// Must start with '^' and have at least one label char
|
|
32
|
-
if (child.charCodeAt(0) !==
|
|
29
|
+
if (child.charCodeAt(0) !== 0x5e /* ^ */ || child.length < 2)
|
|
33
30
|
return null;
|
|
34
31
|
// Check for whitespace using charCode scanning (avoid regex)
|
|
35
32
|
for (let i = 1; i < child.length; i++) {
|
|
36
33
|
const c = child.charCodeAt(i);
|
|
37
|
-
if (c === 0x20 || c === 0x09 || c ===
|
|
34
|
+
if (c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d)
|
|
38
35
|
return null;
|
|
39
36
|
}
|
|
40
37
|
return child.slice(1);
|
|
@@ -59,7 +56,7 @@ function isFootnoteRef(node) {
|
|
|
59
56
|
* ```
|
|
60
57
|
*/
|
|
61
58
|
export default defineComarkPlugin((config = {}) => {
|
|
62
|
-
const { label = 'Footnotes', hr = true, backRef = '↩'
|
|
59
|
+
const { label = 'Footnotes', hr = true, backRef = '↩' } = config;
|
|
63
60
|
return {
|
|
64
61
|
name: 'footnotes',
|
|
65
62
|
// extract footnote definitions from markdown before MDC parsing
|
|
@@ -88,11 +85,17 @@ export default defineComarkPlugin((config = {}) => {
|
|
|
88
85
|
refIndexMap.set(refLabel, refIndexMap.size + 1);
|
|
89
86
|
}
|
|
90
87
|
const refIndex = refIndexMap.get(refLabel);
|
|
91
|
-
return [
|
|
92
|
-
|
|
88
|
+
return [
|
|
89
|
+
'sup',
|
|
90
|
+
{ class: 'footnote-ref' },
|
|
91
|
+
[
|
|
92
|
+
'a',
|
|
93
|
+
{
|
|
93
94
|
href: `#fn-${refLabel}`,
|
|
94
95
|
id: `fnref-${refLabel}`,
|
|
95
|
-
},
|
|
96
|
+
},
|
|
97
|
+
`[${refIndex}]`,
|
|
98
|
+
],
|
|
96
99
|
];
|
|
97
100
|
});
|
|
98
101
|
let nodes = state.tree.nodes;
|
|
@@ -121,8 +124,11 @@ export default defineComarkPlugin((config = {}) => {
|
|
|
121
124
|
const footnoteItems = [];
|
|
122
125
|
for (const [refLabel] of refIndexMap) {
|
|
123
126
|
const content = definitions.get(refLabel);
|
|
124
|
-
footnoteItems.push([
|
|
125
|
-
|
|
127
|
+
footnoteItems.push([
|
|
128
|
+
'li',
|
|
129
|
+
{ id: `fn-${refLabel}` },
|
|
130
|
+
content,
|
|
131
|
+
' ',
|
|
126
132
|
['a', { href: `#fnref-${refLabel}`, class: 'footnote-backref' }, backRef],
|
|
127
133
|
]);
|
|
128
134
|
}
|
|
@@ -166,7 +172,7 @@ export const Footnote = {
|
|
|
166
172
|
},
|
|
167
173
|
handler: (node) => {
|
|
168
174
|
if (node[1].class === 'footnotes') {
|
|
169
|
-
const ol = node.find(n => Array.isArray(n) && n[0] === 'ol');
|
|
175
|
+
const ol = node.find((n) => Array.isArray(n) && n[0] === 'ol');
|
|
170
176
|
if (!ol)
|
|
171
177
|
return '';
|
|
172
178
|
let result = '';
|
package/dist/plugins/headings.js
CHANGED
|
@@ -57,7 +57,7 @@ export default defineComarkPlugin((options = {}) => {
|
|
|
57
57
|
post(state) {
|
|
58
58
|
const nodes = state.tree.nodes;
|
|
59
59
|
// Top-level content nodes — skip raw text nodes and <hr>
|
|
60
|
-
const contentNodes = nodes.filter(node => Array.isArray(node) && getTag(node) !== 'hr');
|
|
60
|
+
const contentNodes = nodes.filter((node) => Array.isArray(node) && getTag(node) !== 'hr');
|
|
61
61
|
let titleNodeIndex = -1;
|
|
62
62
|
let descriptionNodeIndex = -1;
|
|
63
63
|
const first = contentNodes[0];
|
|
@@ -74,9 +74,7 @@ export default defineComarkPlugin((options = {}) => {
|
|
|
74
74
|
}
|
|
75
75
|
if (remove) {
|
|
76
76
|
// Remove in reverse order to preserve indices
|
|
77
|
-
const toRemove = [titleNodeIndex, descriptionNodeIndex]
|
|
78
|
-
.filter(i => i !== -1)
|
|
79
|
-
.sort((a, b) => b - a);
|
|
77
|
+
const toRemove = [titleNodeIndex, descriptionNodeIndex].filter((i) => i !== -1).sort((a, b) => b - a);
|
|
80
78
|
for (const i of toRemove) {
|
|
81
79
|
nodes.splice(i, 1);
|
|
82
80
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LanguageRegistration, ShikiTransformer, ShikiPrimitive, ThemeRegistration } from 'shiki';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ComarkTree } from 'comark';
|
|
3
3
|
export interface HighlightOptions {
|
|
4
4
|
/**
|
|
5
5
|
* Whether to use the default language definitions
|
|
@@ -46,16 +46,6 @@ export interface CodeBlockAttributes {
|
|
|
46
46
|
* Uses a singleton pattern to avoid creating multiple highlighters
|
|
47
47
|
*/
|
|
48
48
|
export declare function getHighlighter(options?: HighlightOptions): Promise<ShikiPrimitive>;
|
|
49
|
-
/**
|
|
50
|
-
* Highlight code using Shiki with codeToTokens
|
|
51
|
-
* Returns comark nodes built from hast
|
|
52
|
-
*/
|
|
53
|
-
export declare function highlightCode(code: string, attrs: CodeBlockAttributes, options?: HighlightOptions): Promise<{
|
|
54
|
-
nodes: ComarkNode[];
|
|
55
|
-
language: string;
|
|
56
|
-
bgColor?: string;
|
|
57
|
-
fgColor?: string;
|
|
58
|
-
}>;
|
|
59
49
|
/**
|
|
60
50
|
* Apply syntax highlighting to all code blocks in a Comark tree
|
|
61
51
|
* Uses codeToTokens API with batched async operations
|