mdrefine 0.2.0 → 0.2.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/CHANGELOG.md +56 -0
- package/CONTRIBUTING.md +13 -0
- package/README.md +1 -1
- package/dist/core/engine.js +73 -10
- package/dist/core/engine.js.map +1 -1
- package/dist/rules/blocks.js +10 -3
- package/dist/rules/blocks.js.map +1 -1
- package/dist/rules/dialects.js +12 -2
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/lists.js +17 -0
- package/dist/rules/lists.js.map +1 -1
- package/dist/rules/style.js +118 -35
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/misread-lists.d.ts +46 -0
- package/dist/syntax/misread-lists.js +100 -0
- package/dist/syntax/misread-lists.js.map +1 -0
- package/dist/syntax/parse.d.ts +27 -3
- package/dist/syntax/parse.js +55 -7
- package/dist/syntax/parse.js.map +1 -1
- package/docs/design.md +43 -21
- package/docs/dialects.md +13 -9
- package/docs/plugins.md +4 -4
- package/docs/quick-start.md +1 -1
- package/docs/rules.md +57 -17
- package/package.json +4 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Nodes } from "mdast";
|
|
2
|
+
import type { Document } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* A list marker as CommonMark reads one at the start of a line's content: a
|
|
5
|
+
* bullet, `-`, `*`, or `+`, or a number of one to nine digits followed by `.`
|
|
6
|
+
* or `)`, and then a space, a tab, or the end of the line.
|
|
7
|
+
*/
|
|
8
|
+
export declare const listMarker: RegExp;
|
|
9
|
+
/** Text that the parser read where renderers may read a list, and the blocks after it. */
|
|
10
|
+
export interface MisreadLists {
|
|
11
|
+
/**
|
|
12
|
+
* Paragraphs, and headings underlined with `===` or `---`, whose text starts
|
|
13
|
+
* with a list marker, and the text that renderers read as continuing it.
|
|
14
|
+
*/
|
|
15
|
+
text: ReadonlySet<Nodes>;
|
|
16
|
+
/**
|
|
17
|
+
* The blocks after such text in its container whose first lines start
|
|
18
|
+
* further right than the text, up to one that does not. Renderers may read
|
|
19
|
+
* them inside the list's last item, where their indentation counts from the
|
|
20
|
+
* item's text: an indented code block can be a paragraph there.
|
|
21
|
+
*/
|
|
22
|
+
nested: ReadonlySet<Nodes>;
|
|
23
|
+
/**
|
|
24
|
+
* The block after those, which starts no further right than the text.
|
|
25
|
+
* Renderers may read it as more items of the list.
|
|
26
|
+
*/
|
|
27
|
+
next: ReadonlySet<Nodes>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Where renderers may read a list in text that the parser read as text.
|
|
31
|
+
*
|
|
32
|
+
* micromark refuses to start a list that does not start at 1, or an empty
|
|
33
|
+
* item, where it would interrupt a paragraph, as CommonMark says, but also
|
|
34
|
+
* where no paragraph is open: at the start of a list item, block quote, or
|
|
35
|
+
* footnote definition below a paragraph, and after an indented code block
|
|
36
|
+
* (issue #191). It reads the line as text there, where GitHub, commonmark.js,
|
|
37
|
+
* and markdown-it read a list. By CommonMark's rules, text can start with a
|
|
38
|
+
* list marker only below a link reference definition, as what remains of the
|
|
39
|
+
* definition's lines; GitHub and commonmark.js read text there too, and
|
|
40
|
+
* markdown-it a list. The paragraph of a task list item starts after its
|
|
41
|
+
* checkbox, which renderers read as the start of the item's text, so it does
|
|
42
|
+
* not count. Renderers read the `===` below such text as text, of the list
|
|
43
|
+
* item or after an empty one, so a paragraph or heading on the next line
|
|
44
|
+
* continues it too, and a block there is nested: a `---` would underline it.
|
|
45
|
+
*/
|
|
46
|
+
export declare function misreadLists(document: Document): MisreadLists;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A list marker as CommonMark reads one at the start of a line's content: a
|
|
3
|
+
* bullet, `-`, `*`, or `+`, or a number of one to nine digits followed by `.`
|
|
4
|
+
* or `)`, and then a space, a tab, or the end of the line.
|
|
5
|
+
*/
|
|
6
|
+
export const listMarker = /^(?:[-+*]|\d{1,9}[.)])(?:[ \t\r\n]|$)/;
|
|
7
|
+
/** Leaf blocks: their children, if they have any, are not blocks. */
|
|
8
|
+
const leaves = new Set(["paragraph", "heading", "table", "code", "html"]);
|
|
9
|
+
const found = new WeakMap();
|
|
10
|
+
/**
|
|
11
|
+
* Where renderers may read a list in text that the parser read as text.
|
|
12
|
+
*
|
|
13
|
+
* micromark refuses to start a list that does not start at 1, or an empty
|
|
14
|
+
* item, where it would interrupt a paragraph, as CommonMark says, but also
|
|
15
|
+
* where no paragraph is open: at the start of a list item, block quote, or
|
|
16
|
+
* footnote definition below a paragraph, and after an indented code block
|
|
17
|
+
* (issue #191). It reads the line as text there, where GitHub, commonmark.js,
|
|
18
|
+
* and markdown-it read a list. By CommonMark's rules, text can start with a
|
|
19
|
+
* list marker only below a link reference definition, as what remains of the
|
|
20
|
+
* definition's lines; GitHub and commonmark.js read text there too, and
|
|
21
|
+
* markdown-it a list. The paragraph of a task list item starts after its
|
|
22
|
+
* checkbox, which renderers read as the start of the item's text, so it does
|
|
23
|
+
* not count. Renderers read the `===` below such text as text, of the list
|
|
24
|
+
* item or after an empty one, so a paragraph or heading on the next line
|
|
25
|
+
* continues it too, and a block there is nested: a `---` would underline it.
|
|
26
|
+
*/
|
|
27
|
+
export function misreadLists(document) {
|
|
28
|
+
const known = found.get(document.tree);
|
|
29
|
+
if (known)
|
|
30
|
+
return known;
|
|
31
|
+
const { source } = document;
|
|
32
|
+
const text = new Set();
|
|
33
|
+
const nested = new Set();
|
|
34
|
+
const next = new Set();
|
|
35
|
+
/** The column of the first character from `offset` on, after spaces and tabs. */
|
|
36
|
+
const columnAt = (offset) => {
|
|
37
|
+
let start = offset;
|
|
38
|
+
while (start > 0 && source[start - 1] !== "\n" && source[start - 1] !== "\r")
|
|
39
|
+
start--;
|
|
40
|
+
let column = 0;
|
|
41
|
+
for (let at = start; at < source.length; at++) {
|
|
42
|
+
const character = source[at];
|
|
43
|
+
if (at >= offset && character !== " " && character !== "\t")
|
|
44
|
+
break;
|
|
45
|
+
column = character === "\t" ? column + 4 - (column % 4) : column + 1;
|
|
46
|
+
}
|
|
47
|
+
return column;
|
|
48
|
+
};
|
|
49
|
+
/** Where a paragraph's or an underlined heading's text starts. */
|
|
50
|
+
const textStart = (node) => {
|
|
51
|
+
const start = node.position?.start.offset;
|
|
52
|
+
if (start === undefined || node.position?.end.offset === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (node.type === "paragraph")
|
|
55
|
+
return start;
|
|
56
|
+
// An underlined heading below link reference definitions starts at them.
|
|
57
|
+
if (node.type === "heading" && source[start] !== "#")
|
|
58
|
+
return node.children[0]?.position?.start.offset ?? start;
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
const visit = (parent) => {
|
|
62
|
+
let reach;
|
|
63
|
+
/** The last line of misread text whose `===` renderers read as text, if the last block is that. */
|
|
64
|
+
let continued;
|
|
65
|
+
parent.children.forEach((child, index) => {
|
|
66
|
+
const offset = child.position?.start.offset;
|
|
67
|
+
if (offset === undefined)
|
|
68
|
+
return;
|
|
69
|
+
const start = textStart(child);
|
|
70
|
+
const adjacent = continued === child.position.start.line - 1;
|
|
71
|
+
const misread = start !== undefined &&
|
|
72
|
+
(listMarker.test(source.slice(start, start + 11)) || adjacent) &&
|
|
73
|
+
!(index === 0 && parent.type === "listItem" && typeof parent.checked === "boolean");
|
|
74
|
+
if (misread || adjacent || reach !== undefined) {
|
|
75
|
+
const column = columnAt(misread ? start : offset);
|
|
76
|
+
if ((adjacent && !misread) || (reach !== undefined && column > reach))
|
|
77
|
+
nested.add(child);
|
|
78
|
+
else if (reach !== undefined) {
|
|
79
|
+
next.add(child);
|
|
80
|
+
reach = undefined;
|
|
81
|
+
}
|
|
82
|
+
if (misread) {
|
|
83
|
+
text.add(child);
|
|
84
|
+
reach = Math.min(reach ?? column, column);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
continued =
|
|
88
|
+
misread && child.type === "heading" && child.depth === 1
|
|
89
|
+
? child.position.end.line
|
|
90
|
+
: undefined;
|
|
91
|
+
if ("children" in child && !leaves.has(child.type))
|
|
92
|
+
visit(child);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
visit(document.tree);
|
|
96
|
+
const result = { text, nested, next };
|
|
97
|
+
found.set(document.tree, result);
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=misread-lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misread-lists.js","sourceRoot":"","sources":["../../src/syntax/misread-lists.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Nodes, Parents, Root } from \"mdast\";\nimport type { Document } from \"../core/types.js\";\n\n/**\n * A list marker as CommonMark reads one at the start of a line's content: a\n * bullet, `-`, `*`, or `+`, or a number of one to nine digits followed by `.`\n * or `)`, and then a space, a tab, or the end of the line.\n */\nexport const listMarker = /^(?:[-+*]|\\d{1,9}[.)])(?:[ \\t\\r\\n]|$)/;\n\n/** Text that the parser read where renderers may read a list, and the blocks after it. */\nexport interface MisreadLists {\n /**\n * Paragraphs, and headings underlined with `===` or `---`, whose text starts\n * with a list marker, and the text that renderers read as continuing it.\n */\n text: ReadonlySet<Nodes>;\n /**\n * The blocks after such text in its container whose first lines start\n * further right than the text, up to one that does not. Renderers may read\n * them inside the list's last item, where their indentation counts from the\n * item's text: an indented code block can be a paragraph there.\n */\n nested: ReadonlySet<Nodes>;\n /**\n * The block after those, which starts no further right than the text.\n * Renderers may read it as more items of the list.\n */\n next: ReadonlySet<Nodes>;\n}\n\n/** Leaf blocks: their children, if they have any, are not blocks. */\nconst leaves = new Set<Nodes[\"type\"]>([\"paragraph\", \"heading\", \"table\", \"code\", \"html\"]);\nconst found = new WeakMap<Root, MisreadLists>();\n\n/**\n * Where renderers may read a list in text that the parser read as text.\n *\n * micromark refuses to start a list that does not start at 1, or an empty\n * item, where it would interrupt a paragraph, as CommonMark says, but also\n * where no paragraph is open: at the start of a list item, block quote, or\n * footnote definition below a paragraph, and after an indented code block\n * (issue #191). It reads the line as text there, where GitHub, commonmark.js,\n * and markdown-it read a list. By CommonMark's rules, text can start with a\n * list marker only below a link reference definition, as what remains of the\n * definition's lines; GitHub and commonmark.js read text there too, and\n * markdown-it a list. The paragraph of a task list item starts after its\n * checkbox, which renderers read as the start of the item's text, so it does\n * not count. Renderers read the `===` below such text as text, of the list\n * item or after an empty one, so a paragraph or heading on the next line\n * continues it too, and a block there is nested: a `---` would underline it.\n */\nexport function misreadLists(document: Document): MisreadLists {\n const known = found.get(document.tree);\n if (known) return known;\n const { source } = document;\n const text = new Set<Nodes>();\n const nested = new Set<Nodes>();\n const next = new Set<Nodes>();\n /** The column of the first character from `offset` on, after spaces and tabs. */\n const columnAt = (offset: number) => {\n let start = offset;\n while (start > 0 && source[start - 1] !== \"\\n\" && source[start - 1] !== \"\\r\") start--;\n let column = 0;\n for (let at = start; at < source.length; at++) {\n const character = source[at];\n if (at >= offset && character !== \" \" && character !== \"\\t\") break;\n column = character === \"\\t\" ? column + 4 - (column % 4) : column + 1;\n }\n return column;\n };\n /** Where a paragraph's or an underlined heading's text starts. */\n const textStart = (node: Nodes): number | undefined => {\n const start = node.position?.start.offset;\n if (start === undefined || node.position?.end.offset === undefined) return undefined;\n if (node.type === \"paragraph\") return start;\n // An underlined heading below link reference definitions starts at them.\n if (node.type === \"heading\" && source[start] !== \"#\")\n return node.children[0]?.position?.start.offset ?? start;\n return undefined;\n };\n const visit = (parent: Parents) => {\n let reach: number | undefined;\n /** The last line of misread text whose `===` renderers read as text, if the last block is that. */\n let continued: number | undefined;\n parent.children.forEach((child, index) => {\n const offset = child.position?.start.offset;\n if (offset === undefined) return;\n const start = textStart(child);\n const adjacent = continued === child.position!.start.line - 1;\n const misread =\n start !== undefined &&\n (listMarker.test(source.slice(start, start + 11)) || adjacent) &&\n !(index === 0 && parent.type === \"listItem\" && typeof parent.checked === \"boolean\");\n if (misread || adjacent || reach !== undefined) {\n const column = columnAt(misread ? start! : offset);\n if ((adjacent && !misread) || (reach !== undefined && column > reach)) nested.add(child);\n else if (reach !== undefined) {\n next.add(child);\n reach = undefined;\n }\n if (misread) {\n text.add(child);\n reach = Math.min(reach ?? column, column);\n }\n }\n continued =\n misread && child.type === \"heading\" && child.depth === 1\n ? child.position!.end.line\n : undefined;\n if (\"children\" in child && !leaves.has(child.type)) visit(child);\n });\n };\n visit(document.tree);\n const result = { text, nested, next };\n found.set(document.tree, result);\n return result;\n}\n"]}
|
package/dist/syntax/parse.d.ts
CHANGED
|
@@ -39,6 +39,29 @@ export declare function splitByteOrderMark(source: string): [string, string];
|
|
|
39
39
|
* renderers without front matter read the block. Undefined for other trees.
|
|
40
40
|
*/
|
|
41
41
|
export declare function frontMatterReading(tree: Root): Root | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* The other reading of a document, whose meaning an edit has to keep too:
|
|
44
|
+
* `frontMatterReading`, or, for a document that `parse` read with TOML front
|
|
45
|
+
* matter, the reading without it. Site generators read TOML front matter, and
|
|
46
|
+
* GitHub, Forgejo, Gitea, Obsidian, commonmark.js, and markdown-it read its
|
|
47
|
+
* lines as a paragraph, which the line below it can continue or underline as a
|
|
48
|
+
* heading (issue #333). Undefined for other trees.
|
|
49
|
+
*/
|
|
50
|
+
export declare function otherReading(tree: Root): Root | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Whether `node` is the first block below a document's TOML front matter and
|
|
53
|
+
* starts on the line after it, where renderers without TOML front matter can
|
|
54
|
+
* read it as part of the paragraph that the front matter's lines make.
|
|
55
|
+
*/
|
|
56
|
+
export declare function belowTomlFrontMatter(tree: Root, node: Nodes): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether renderers without TOML front matter read the first line of `node`,
|
|
59
|
+
* the block directly below a document's TOML front matter, as part of the
|
|
60
|
+
* paragraph that the front matter's lines make: as its text, as for a list
|
|
61
|
+
* that cannot interrupt a paragraph, or as the underline that makes it a
|
|
62
|
+
* heading.
|
|
63
|
+
*/
|
|
64
|
+
export declare function joinsTomlFrontMatter(tree: Root, node: Nodes): boolean;
|
|
42
65
|
/**
|
|
43
66
|
* The fence, `---` or `+++`, of front matter that the first line of `source`
|
|
44
67
|
* opens and no line closes. The front matter extension opens front matter at a
|
|
@@ -61,9 +84,10 @@ export declare function unclosedFrontMatter(source: string): "---" | "+++" | und
|
|
|
61
84
|
* Front matter whose `---` or `+++` closes it inside a code block, an HTML
|
|
62
85
|
* block, or display math, opened by a line of the front matter, is read as
|
|
63
86
|
* Markdown: the document is read without it (issue #295), and
|
|
64
|
-
* `frontMatterReading` gives the other reading.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
87
|
+
* `frontMatterReading` gives the other reading. For a document read with TOML
|
|
88
|
+
* front matter, `otherReading` gives the reading without it (issue #333). A
|
|
89
|
+
* document whose first line would open front matter that no line closes is
|
|
90
|
+
* read without the front matter extension, as renderers read it (issue #278).
|
|
67
91
|
*/
|
|
68
92
|
export declare function parse(source: string, dialect: DialectName, path?: string, plugins?: readonly Plugin[]): Document;
|
|
69
93
|
/**
|
package/dist/syntax/parse.js
CHANGED
|
@@ -161,6 +161,48 @@ const frontMatterReadings = new WeakMap();
|
|
|
161
161
|
export function frontMatterReading(tree) {
|
|
162
162
|
return frontMatterReadings.get(tree);
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* The reading without front matter of a document that `parse` read with TOML
|
|
166
|
+
* front matter, made when it is first asked for, by the tree of the reading
|
|
167
|
+
* with it.
|
|
168
|
+
*/
|
|
169
|
+
const tomlReadings = new WeakMap();
|
|
170
|
+
/**
|
|
171
|
+
* The other reading of a document, whose meaning an edit has to keep too:
|
|
172
|
+
* `frontMatterReading`, or, for a document that `parse` read with TOML front
|
|
173
|
+
* matter, the reading without it. Site generators read TOML front matter, and
|
|
174
|
+
* GitHub, Forgejo, Gitea, Obsidian, commonmark.js, and markdown-it read its
|
|
175
|
+
* lines as a paragraph, which the line below it can continue or underline as a
|
|
176
|
+
* heading (issue #333). Undefined for other trees.
|
|
177
|
+
*/
|
|
178
|
+
export function otherReading(tree) {
|
|
179
|
+
return frontMatterReadings.get(tree) ?? tomlReadings.get(tree)?.();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Whether `node` is the first block below a document's TOML front matter and
|
|
183
|
+
* starts on the line after it, where renderers without TOML front matter can
|
|
184
|
+
* read it as part of the paragraph that the front matter's lines make.
|
|
185
|
+
*/
|
|
186
|
+
export function belowTomlFrontMatter(tree, node) {
|
|
187
|
+
const [matter, block] = tree.children;
|
|
188
|
+
// The tree's types know YAML front matter only; the parser also reads TOML.
|
|
189
|
+
const end = matter?.type === "toml" ? matter.position?.end.line : undefined;
|
|
190
|
+
return end !== undefined && block === node && block.position?.start.line === end + 1;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Whether renderers without TOML front matter read the first line of `node`,
|
|
194
|
+
* the block directly below a document's TOML front matter, as part of the
|
|
195
|
+
* paragraph that the front matter's lines make: as its text, as for a list
|
|
196
|
+
* that cannot interrupt a paragraph, or as the underline that makes it a
|
|
197
|
+
* heading.
|
|
198
|
+
*/
|
|
199
|
+
export function joinsTomlFrontMatter(tree, node) {
|
|
200
|
+
const plain = belowTomlFrontMatter(tree, node) ? tomlReadings.get(tree)?.() : undefined;
|
|
201
|
+
if (!plain)
|
|
202
|
+
return false;
|
|
203
|
+
const line = node.position.start.line;
|
|
204
|
+
return plain.children.some(({ position }) => position !== undefined && position.start.line < line && position.end.line >= line);
|
|
205
|
+
}
|
|
164
206
|
/**
|
|
165
207
|
* A line of front matter that could open a block whose content is literal: a
|
|
166
208
|
* code fence, display math, or an HTML block.
|
|
@@ -197,9 +239,10 @@ export function unclosedFrontMatter(source) {
|
|
|
197
239
|
* Front matter whose `---` or `+++` closes it inside a code block, an HTML
|
|
198
240
|
* block, or display math, opened by a line of the front matter, is read as
|
|
199
241
|
* Markdown: the document is read without it (issue #295), and
|
|
200
|
-
* `frontMatterReading` gives the other reading.
|
|
201
|
-
*
|
|
202
|
-
*
|
|
242
|
+
* `frontMatterReading` gives the other reading. For a document read with TOML
|
|
243
|
+
* front matter, `otherReading` gives the reading without it (issue #333). A
|
|
244
|
+
* document whose first line would open front matter that no line closes is
|
|
245
|
+
* read without the front matter extension, as renderers read it (issue #278).
|
|
203
246
|
*/
|
|
204
247
|
export function parse(source, dialect, path = "document.md", plugins = []) {
|
|
205
248
|
const read = knownDialect(dialect);
|
|
@@ -216,7 +259,9 @@ export function parse(source, dialect, path = "document.md", plugins = []) {
|
|
|
216
259
|
// footnotes, autolinks, and dollar math. Forgejo and Gitea syntax beyond
|
|
217
260
|
// that is protected by rules rather than parsed.
|
|
218
261
|
if (read !== "commonmark") {
|
|
219
|
-
|
|
262
|
+
// Obsidian marks strikethrough with two tildes only; `~a~` is text there
|
|
263
|
+
// (issue #319).
|
|
264
|
+
extensions.push(gfm({ singleTilde: read !== "obsidian" }), math(mathConditions[read]));
|
|
220
265
|
mdastExtensions.push(...gfmTree, mathFromMarkdown());
|
|
221
266
|
}
|
|
222
267
|
if (read === "obsidian") {
|
|
@@ -231,9 +276,10 @@ export function parse(source, dialect, path = "document.md", plugins = []) {
|
|
|
231
276
|
}
|
|
232
277
|
return fromMarkdown(source, { extensions, mdastExtensions });
|
|
233
278
|
};
|
|
234
|
-
|
|
279
|
+
const result = tree(!unclosedFrontMatter(source));
|
|
235
280
|
// The tree's types know YAML front matter only; the parser also reads TOML.
|
|
236
281
|
const matter = result.children[0];
|
|
282
|
+
let plain;
|
|
237
283
|
// Only front matter with a line that could open such a block is read again.
|
|
238
284
|
// It is given up only when the line that closes it is content of that block
|
|
239
285
|
// in the other reading, which a multiline TOML string or a YAML block
|
|
@@ -241,14 +287,16 @@ export function parse(source, dialect, path = "document.md", plugins = []) {
|
|
|
241
287
|
if ((matter?.type === "yaml" || matter?.type === "toml") &&
|
|
242
288
|
literalOpener.test(matter.value ?? "")) {
|
|
243
289
|
const closing = result.children[0].position.end.line;
|
|
244
|
-
|
|
290
|
+
plain = tree(false);
|
|
245
291
|
if (plain.children.some((node) => literalBlocks.has(node.type) &&
|
|
246
292
|
node.position.start.line < closing &&
|
|
247
293
|
node.position.end.line >= closing)) {
|
|
248
294
|
frontMatterReadings.set(plain, result);
|
|
249
|
-
|
|
295
|
+
return { source, dialect: read, path, tree: plain };
|
|
250
296
|
}
|
|
251
297
|
}
|
|
298
|
+
if (matter?.type === "toml")
|
|
299
|
+
tomlReadings.set(result, () => (plain ??= tree(false)));
|
|
252
300
|
return { source, dialect: read, path, tree: result };
|
|
253
301
|
}
|
|
254
302
|
/**
|
package/dist/syntax/parse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/syntax/parse.ts"],"names":[],"mappings":"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","sourcesContent":["import { fromMarkdown } from \"mdast-util-from-markdown\";\nimport type { Extension as TreeExtension } from \"mdast-util-from-markdown\";\nimport type { Extension as SyntaxExtension } from \"micromark-util-types\";\nimport { gfm } from \"micromark-extension-gfm\";\nimport { gfmFromMarkdown } from \"mdast-util-gfm\";\nimport { frontmatter } from \"micromark-extension-frontmatter\";\nimport { frontmatterFromMarkdown } from \"mdast-util-frontmatter\";\nimport { mathFromMarkdown } from \"mdast-util-math\";\nimport type { InlineCode, Nodes, Paragraph, Root } from \"mdast\";\nimport type { Dialect, DialectName, Document, Plugin } from \"../core/types.js\";\nimport { knownDialect } from \"../config/resolve.js\";\nimport { math } from \"./math.js\";\nimport type { MathConditions } from \"./math.js\";\nimport { obsidianSyntax, obsidianTree } from \"./obsidian.js\";\n\n// GFM's tree transform performs GitHub's second, transform-time autolink pass\n// (`[www.example.com]` or a Forgejo shortlink's `|https://` in files), but its\n// nodes carry no source positions, which made the engine refuse whole documents.\n// No built-in rule acts on literal autolinks, so output is unchanged without it;\n// plugins inspecting link nodes do not see those autolinks.\nconst gfmTree = gfmFromMarkdown().map((extension) => {\n const copy = { ...extension };\n delete copy.transforms;\n return copy;\n});\n\n/** The offset of the `]` that closes the text of each link and image, by node. */\nconst labelEnds = new WeakMap<Nodes, number>();\n/**\n * Record where the text of a link or image ends. The tree keeps an image's\n * description only as plain text, so its source positions are otherwise lost.\n */\nconst labelEndTree: TreeExtension = {\n exit: {\n // A label has an opening and a closing marker; the closing one comes last.\n labelMarker(token) {\n // The label's fragment is on top of the stack, and the link or image below it.\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"link\" || node?.type === \"image\") labelEnds.set(node, token.start.offset);\n },\n },\n};\n/**\n * Where the text of a link or the description of an image ends in the source:\n * the offset of its closing `]`. Undefined for other nodes and for nodes that\n * `parse` did not make.\n */\nexport function labelEnd(node: Nodes): number | undefined {\n return labelEnds.get(node);\n}\n/**\n * What the value of a code span in the tree leaves out, by node: the space or\n * line ending that the parser took off each end, and which lines of the\n * document are lazy continuation lines.\n */\nconst codeSpanSources = new WeakMap<\n InlineCode,\n { padding: string[]; lazy: Record<number, boolean> }\n>();\n/**\n * The extensions of one parse that record what code spans leave out. The\n * parser knows which lines are lazy once it has read the document's blocks, and\n * a construct at a backtick, which never matches, reads that from it before a\n * code span is read there.\n */\nfunction codeSpanRecording(): { syntax: SyntaxExtension; tree: TreeExtension } {\n let lazy: Record<number, boolean> = {};\n return {\n syntax: {\n text: {\n [0x60]: {\n // The text tokenizer asks each construct at a backtick whether the\n // character before allows it, and tries them all if one does. This\n // one never does, so the parse is the same as without it.\n previous() {\n lazy = this.parser.lazy;\n return false;\n },\n tokenize(_effects, _ok, nok) {\n lazy = this.parser.lazy;\n return nok;\n },\n },\n },\n },\n tree: {\n exit: {\n // The span's value is collected in a fragment on top of the stack, with\n // the span's node below it.\n codeTextSequence() {\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"inlineCode\" && !codeSpanSources.has(node))\n codeSpanSources.set(node, { padding: [], lazy });\n },\n codeTextPadding(token) {\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"inlineCode\")\n codeSpanSources.get(node)?.padding.push(this.sliceSerialize(token));\n },\n },\n },\n };\n}\n/**\n * The text of a code span as GitHub shows it, read in CommonMark's order: the\n * spaces and tabs that start each line after the first are dropped, as from\n * every line of a paragraph, but kept on a lazy continuation line, one that\n * lacks its block quote's `>` or its list item's indentation; then each line\n * ending becomes a space; then one space comes off each end when both ends\n * have one and the span is not only spaces. The parser keeps the spaces and\n * tabs in the span's value, and takes a space or line ending off each end\n * before line endings become spaces (issues #276 and #290). A node that\n * `parse` did not make is read as if its value were all of its content.\n */\nexport function codeSpanText(node: InlineCode): string {\n const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};\n let line = node.position?.start.line ?? 0;\n const content = `${padding[0] ?? \"\"}${node.value}${padding[1] ?? \"\"}`.replace(\n /(?:\\r\\n|\\r|\\n)([ \\t]*)/g,\n (_, indentation: string) => (lazy[++line] ? ` ${indentation}` : \" \"),\n );\n return /^ [^]* $/.test(content) && /[^ ]/.test(content) ? content.slice(1, -1) : content;\n}\n/**\n * Whether a code span goes on in a lazy continuation line that starts with\n * spaces or tabs. GitHub keeps them in the span, as `codeSpanText` does, and\n * commonmark.js drops them, so renderers show such a span differently.\n */\nexport function lazyIndentation(node: InlineCode): boolean {\n const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};\n let line = node.position?.start.line ?? 0;\n const content = `${padding[0] ?? \"\"}${node.value}${padding[1] ?? \"\"}`;\n for (const [, indentation] of content.matchAll(/(?:\\r\\n|\\r|\\n)([ \\t]*)/g))\n if (lazy[++line] && indentation) return true;\n return false;\n}\n\n/**\n * Whose conditions for `$…$` in running text each dialect follows, as observed\n * on GitHub's file view, on Forgejo 16 and Gitea 1.25 and 28, and in Obsidian's\n * Reading view and Live Preview. CommonMark has no math.\n */\nconst mathConditions: Record<Exclude<Dialect, \"commonmark\">, MathConditions> = {\n github: \"github\",\n forgejo: \"forge\",\n gitea: \"forge\",\n obsidian: \"obsidian\",\n};\n\n/**\n * The byte order marks that `source` starts with, and the text after them.\n * Some editors write a mark at the start of a UTF-8 file, and a program that\n * adds one without looking can leave two. The parser skips one mark at the\n * start of what it is given.\n */\nexport function splitByteOrderMark(source: string): [string, string] {\n let end = 0;\n while (source.charCodeAt(end) === 0xfeff) end++;\n return [source.slice(0, end), source.slice(end)];\n}\n\n/**\n * The reading with front matter of a document that `parse` read without it,\n * by the tree of the reading without it.\n */\nconst frontMatterReadings = new WeakMap<Root, Root>();\n/**\n * The document read with front matter, where `parse` read it without front\n * matter because its front matter would end inside a code block, an HTML\n * block, or display math. Site generators read front matter there, and so does\n * GitHub's file view when it is a YAML mapping; GitHub's comments and\n * renderers without front matter read the block. Undefined for other trees.\n */\nexport function frontMatterReading(tree: Root): Root | undefined {\n return frontMatterReadings.get(tree);\n}\n/**\n * A line of front matter that could open a block whose content is literal: a\n * code fence, display math, or an HTML block.\n */\nconst literalOpener = /^ {0,3}(?:`{3,}|~{3,}|\\$\\$|<)/m;\n/** Blocks in which a `---` or `+++` line is content, not a closing line. */\nconst literalBlocks = new Set([\"code\", \"html\", \"math\"]);\n/**\n * The fence, `---` or `+++`, of front matter that the first line of `source`\n * opens and no line closes. The front matter extension opens front matter at a\n * first line of `---` or `+++` followed only by spaces or tabs, and closes it\n * at the next line of the same fence, also followed only by spaces or tabs.\n * Without a closing line the document has no front matter, but the extension\n * then reads the lists and block quotes below as paragraphs (issue #278,\n * micromark/micromark-extension-frontmatter#5). An edit that writes such a line\n * below makes front matter of everything above it.\n */\nexport function unclosedFrontMatter(source: string): \"---\" | \"+++\" | undefined {\n const opening = /^(---|\\+\\+\\+)[ \\t]*(?:\\r\\n|\\r|\\n)/.exec(source);\n if (!opening) return undefined;\n const fence = opening[1] as \"---\" | \"+++\";\n const closing = new RegExp(`(?:^|\\\\r|\\\\n)${fence.replaceAll(\"+\", \"\\\\+\")}[ \\\\t]*(?:\\\\r|\\\\n|$)`);\n return closing.test(source.slice(opening[0].length)) ? undefined : fence;\n}\n\n/**\n * Parse a document in `dialect`, or the dialect that an alias such as\n * `codeberg` stands for, which the result holds; an unknown dialect is an\n * error. Byte order marks at the start of `source` are not part of the\n * document: the document's source starts after them, as the positions in the\n * tree count. `path` is the document's path relative to the workspace root,\n * and `plugins` add their syntax extensions.\n *\n * Front matter whose `---` or `+++` closes it inside a code block, an HTML\n * block, or display math, opened by a line of the front matter, is read as\n * Markdown: the document is read without it (issue #295), and\n * `frontMatterReading` gives the other reading. A document whose first line\n * would open front matter that no line closes is read without the front matter\n * extension, as renderers read it (issue #278).\n */\nexport function parse(\n source: string,\n dialect: DialectName,\n path = \"document.md\",\n plugins: readonly Plugin[] = [],\n): Document {\n const read = knownDialect(dialect);\n source = splitByteOrderMark(source)[1];\n const tree = (withFrontMatter: boolean): Root => {\n const codeSpans = codeSpanRecording();\n const extensions = [codeSpans.syntax];\n const mdastExtensions = [labelEndTree, codeSpans.tree];\n if (withFrontMatter) {\n extensions.unshift(frontmatter([\"yaml\", \"toml\"]));\n mdastExtensions.unshift(frontmatterFromMarkdown([\"yaml\", \"toml\"]));\n }\n // GitHub, Forgejo, and Gitea share GFM tables, task lists, strikethrough,\n // footnotes, autolinks, and dollar math. Forgejo and Gitea syntax beyond\n // that is protected by rules rather than parsed.\n if (read !== \"commonmark\") {\n extensions.push(gfm(), math(mathConditions[read]));\n mdastExtensions.push(...gfmTree, mathFromMarkdown());\n }\n if (read === \"obsidian\") {\n extensions.push(obsidianSyntax);\n mdastExtensions.push(obsidianTree);\n }\n for (const plugin of plugins) {\n if (plugin.syntax) {\n extensions.push(plugin.syntax.micromark);\n mdastExtensions.push(plugin.syntax.mdast);\n }\n }\n return fromMarkdown(source, { extensions, mdastExtensions });\n };\n let result = tree(!unclosedFrontMatter(source));\n // The tree's types know YAML front matter only; the parser also reads TOML.\n const matter = result.children[0] as { type: string; value?: string } | undefined;\n // Only front matter with a line that could open such a block is read again.\n // It is given up only when the line that closes it is content of that block\n // in the other reading, which a multiline TOML string or a YAML block\n // scalar holding a fence does not decide by itself.\n if (\n (matter?.type === \"yaml\" || matter?.type === \"toml\") &&\n literalOpener.test(matter.value ?? \"\")\n ) {\n const closing = result.children[0]!.position!.end.line;\n const plain = tree(false);\n if (\n plain.children.some(\n (node) =>\n literalBlocks.has(node.type) &&\n node.position!.start.line < closing &&\n node.position!.end.line >= closing,\n )\n ) {\n frontMatterReadings.set(plain, result);\n result = plain;\n }\n }\n return { source, dialect: read, path, tree: result };\n}\n\n/**\n * The `[start, end]` offsets of a node of a parsed document in its `source`:\n * half-open, in UTF-16 code units. A node without a source position, such as\n * one that a plugin's tree extension built without one, is an error.\n */\nexport function range(node: Nodes): [number, number] {\n if (node.position?.start.offset === undefined || node.position.end.offset === undefined) {\n throw new Error(`Missing source position for ${node.type}`);\n }\n return [node.position.start.offset, node.position.end.offset];\n}\n/** An alert's or callout's marker, such as `[!NOTE]` or `[!tip]`. */\nconst calloutMarker = /^\\[![\\w-]+\\]/;\n/**\n * Whether a paragraph starts with text that `pattern` matches, by default an\n * alert's or callout's marker: in `written`, its source from its start, or in\n * its text as renderers read it, where a backslash escape or a character\n * reference stands for the character it writes. GitHub reads `\\[!NOTE]`,\n * `[!NOTE]`, and `[!NOTE]` as alerts too (issue #294).\n */\nexport function startsWithMarker(\n written: string,\n paragraph: Paragraph,\n pattern: RegExp = calloutMarker,\n): boolean {\n const first = paragraph.children[0];\n return pattern.test(written) || (first?.type === \"text\" && pattern.test(first.value));\n}\n/**\n * A node's text as a reader sees it: the values of its text, code, and other\n * literal nodes, in order. A code span gives its text as `codeSpanText` reads\n * it, on one line, a wikilink its label, or else its target, and an Obsidian\n * comment nothing.\n */\nexport function textContent(node: Nodes): string {\n if (node.type === \"wikiLink\") return node.label ?? node.target;\n if (node.type === \"obsidianComment\") return \"\";\n if (node.type === \"inlineCode\") return codeSpanText(node);\n if (\"value\" in node) return node.value;\n if (\"children\" in node) return node.children.map(textContent).join(\"\");\n return \"\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/syntax/parse.ts"],"names":[],"mappings":"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","sourcesContent":["import { fromMarkdown } from \"mdast-util-from-markdown\";\nimport type { Extension as TreeExtension } from \"mdast-util-from-markdown\";\nimport type { Extension as SyntaxExtension } from \"micromark-util-types\";\nimport { gfm } from \"micromark-extension-gfm\";\nimport { gfmFromMarkdown } from \"mdast-util-gfm\";\nimport { frontmatter } from \"micromark-extension-frontmatter\";\nimport { frontmatterFromMarkdown } from \"mdast-util-frontmatter\";\nimport { mathFromMarkdown } from \"mdast-util-math\";\nimport type { InlineCode, Nodes, Paragraph, Root } from \"mdast\";\nimport type { Dialect, DialectName, Document, Plugin } from \"../core/types.js\";\nimport { knownDialect } from \"../config/resolve.js\";\nimport { math } from \"./math.js\";\nimport type { MathConditions } from \"./math.js\";\nimport { obsidianSyntax, obsidianTree } from \"./obsidian.js\";\n\n// GFM's tree transform performs GitHub's second, transform-time autolink pass\n// (`[www.example.com]` or a Forgejo shortlink's `|https://` in files), but its\n// nodes carry no source positions, which made the engine refuse whole documents.\n// No built-in rule acts on literal autolinks, so output is unchanged without it;\n// plugins inspecting link nodes do not see those autolinks.\nconst gfmTree = gfmFromMarkdown().map((extension) => {\n const copy = { ...extension };\n delete copy.transforms;\n return copy;\n});\n\n/** The offset of the `]` that closes the text of each link and image, by node. */\nconst labelEnds = new WeakMap<Nodes, number>();\n/**\n * Record where the text of a link or image ends. The tree keeps an image's\n * description only as plain text, so its source positions are otherwise lost.\n */\nconst labelEndTree: TreeExtension = {\n exit: {\n // A label has an opening and a closing marker; the closing one comes last.\n labelMarker(token) {\n // The label's fragment is on top of the stack, and the link or image below it.\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"link\" || node?.type === \"image\") labelEnds.set(node, token.start.offset);\n },\n },\n};\n/**\n * Where the text of a link or the description of an image ends in the source:\n * the offset of its closing `]`. Undefined for other nodes and for nodes that\n * `parse` did not make.\n */\nexport function labelEnd(node: Nodes): number | undefined {\n return labelEnds.get(node);\n}\n/**\n * What the value of a code span in the tree leaves out, by node: the space or\n * line ending that the parser took off each end, and which lines of the\n * document are lazy continuation lines.\n */\nconst codeSpanSources = new WeakMap<\n InlineCode,\n { padding: string[]; lazy: Record<number, boolean> }\n>();\n/**\n * The extensions of one parse that record what code spans leave out. The\n * parser knows which lines are lazy once it has read the document's blocks, and\n * a construct at a backtick, which never matches, reads that from it before a\n * code span is read there.\n */\nfunction codeSpanRecording(): { syntax: SyntaxExtension; tree: TreeExtension } {\n let lazy: Record<number, boolean> = {};\n return {\n syntax: {\n text: {\n [0x60]: {\n // The text tokenizer asks each construct at a backtick whether the\n // character before allows it, and tries them all if one does. This\n // one never does, so the parse is the same as without it.\n previous() {\n lazy = this.parser.lazy;\n return false;\n },\n tokenize(_effects, _ok, nok) {\n lazy = this.parser.lazy;\n return nok;\n },\n },\n },\n },\n tree: {\n exit: {\n // The span's value is collected in a fragment on top of the stack, with\n // the span's node below it.\n codeTextSequence() {\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"inlineCode\" && !codeSpanSources.has(node))\n codeSpanSources.set(node, { padding: [], lazy });\n },\n codeTextPadding(token) {\n const node = this.stack[this.stack.length - 2];\n if (node?.type === \"inlineCode\")\n codeSpanSources.get(node)?.padding.push(this.sliceSerialize(token));\n },\n },\n },\n };\n}\n/**\n * The text of a code span as GitHub shows it, read in CommonMark's order: the\n * spaces and tabs that start each line after the first are dropped, as from\n * every line of a paragraph, but kept on a lazy continuation line, one that\n * lacks its block quote's `>` or its list item's indentation; then each line\n * ending becomes a space; then one space comes off each end when both ends\n * have one and the span is not only spaces. The parser keeps the spaces and\n * tabs in the span's value, and takes a space or line ending off each end\n * before line endings become spaces (issues #276 and #290). A node that\n * `parse` did not make is read as if its value were all of its content.\n */\nexport function codeSpanText(node: InlineCode): string {\n const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};\n let line = node.position?.start.line ?? 0;\n const content = `${padding[0] ?? \"\"}${node.value}${padding[1] ?? \"\"}`.replace(\n /(?:\\r\\n|\\r|\\n)([ \\t]*)/g,\n (_, indentation: string) => (lazy[++line] ? ` ${indentation}` : \" \"),\n );\n return /^ [^]* $/.test(content) && /[^ ]/.test(content) ? content.slice(1, -1) : content;\n}\n/**\n * Whether a code span goes on in a lazy continuation line that starts with\n * spaces or tabs. GitHub keeps them in the span, as `codeSpanText` does, and\n * commonmark.js drops them, so renderers show such a span differently.\n */\nexport function lazyIndentation(node: InlineCode): boolean {\n const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};\n let line = node.position?.start.line ?? 0;\n const content = `${padding[0] ?? \"\"}${node.value}${padding[1] ?? \"\"}`;\n for (const [, indentation] of content.matchAll(/(?:\\r\\n|\\r|\\n)([ \\t]*)/g))\n if (lazy[++line] && indentation) return true;\n return false;\n}\n\n/**\n * Whose conditions for `$…$` in running text each dialect follows, as observed\n * on GitHub's file view, on Forgejo 16 and Gitea 1.25 and 28, and in Obsidian's\n * Reading view and Live Preview. CommonMark has no math.\n */\nconst mathConditions: Record<Exclude<Dialect, \"commonmark\">, MathConditions> = {\n github: \"github\",\n forgejo: \"forge\",\n gitea: \"forge\",\n obsidian: \"obsidian\",\n};\n\n/**\n * The byte order marks that `source` starts with, and the text after them.\n * Some editors write a mark at the start of a UTF-8 file, and a program that\n * adds one without looking can leave two. The parser skips one mark at the\n * start of what it is given.\n */\nexport function splitByteOrderMark(source: string): [string, string] {\n let end = 0;\n while (source.charCodeAt(end) === 0xfeff) end++;\n return [source.slice(0, end), source.slice(end)];\n}\n\n/**\n * The reading with front matter of a document that `parse` read without it,\n * by the tree of the reading without it.\n */\nconst frontMatterReadings = new WeakMap<Root, Root>();\n/**\n * The document read with front matter, where `parse` read it without front\n * matter because its front matter would end inside a code block, an HTML\n * block, or display math. Site generators read front matter there, and so does\n * GitHub's file view when it is a YAML mapping; GitHub's comments and\n * renderers without front matter read the block. Undefined for other trees.\n */\nexport function frontMatterReading(tree: Root): Root | undefined {\n return frontMatterReadings.get(tree);\n}\n/**\n * The reading without front matter of a document that `parse` read with TOML\n * front matter, made when it is first asked for, by the tree of the reading\n * with it.\n */\nconst tomlReadings = new WeakMap<Root, () => Root>();\n/**\n * The other reading of a document, whose meaning an edit has to keep too:\n * `frontMatterReading`, or, for a document that `parse` read with TOML front\n * matter, the reading without it. Site generators read TOML front matter, and\n * GitHub, Forgejo, Gitea, Obsidian, commonmark.js, and markdown-it read its\n * lines as a paragraph, which the line below it can continue or underline as a\n * heading (issue #333). Undefined for other trees.\n */\nexport function otherReading(tree: Root): Root | undefined {\n return frontMatterReadings.get(tree) ?? tomlReadings.get(tree)?.();\n}\n/**\n * Whether `node` is the first block below a document's TOML front matter and\n * starts on the line after it, where renderers without TOML front matter can\n * read it as part of the paragraph that the front matter's lines make.\n */\nexport function belowTomlFrontMatter(tree: Root, node: Nodes): boolean {\n const [matter, block] = tree.children;\n // The tree's types know YAML front matter only; the parser also reads TOML.\n const end = matter?.type === (\"toml\" as string) ? matter.position?.end.line : undefined;\n return end !== undefined && block === node && block.position?.start.line === end + 1;\n}\n/**\n * Whether renderers without TOML front matter read the first line of `node`,\n * the block directly below a document's TOML front matter, as part of the\n * paragraph that the front matter's lines make: as its text, as for a list\n * that cannot interrupt a paragraph, or as the underline that makes it a\n * heading.\n */\nexport function joinsTomlFrontMatter(tree: Root, node: Nodes): boolean {\n const plain = belowTomlFrontMatter(tree, node) ? tomlReadings.get(tree)?.() : undefined;\n if (!plain) return false;\n const line = node.position!.start.line;\n return plain.children.some(\n ({ position }) =>\n position !== undefined && position.start.line < line && position.end.line >= line,\n );\n}\n/**\n * A line of front matter that could open a block whose content is literal: a\n * code fence, display math, or an HTML block.\n */\nconst literalOpener = /^ {0,3}(?:`{3,}|~{3,}|\\$\\$|<)/m;\n/** Blocks in which a `---` or `+++` line is content, not a closing line. */\nconst literalBlocks = new Set([\"code\", \"html\", \"math\"]);\n/**\n * The fence, `---` or `+++`, of front matter that the first line of `source`\n * opens and no line closes. The front matter extension opens front matter at a\n * first line of `---` or `+++` followed only by spaces or tabs, and closes it\n * at the next line of the same fence, also followed only by spaces or tabs.\n * Without a closing line the document has no front matter, but the extension\n * then reads the lists and block quotes below as paragraphs (issue #278,\n * micromark/micromark-extension-frontmatter#5). An edit that writes such a line\n * below makes front matter of everything above it.\n */\nexport function unclosedFrontMatter(source: string): \"---\" | \"+++\" | undefined {\n const opening = /^(---|\\+\\+\\+)[ \\t]*(?:\\r\\n|\\r|\\n)/.exec(source);\n if (!opening) return undefined;\n const fence = opening[1] as \"---\" | \"+++\";\n const closing = new RegExp(`(?:^|\\\\r|\\\\n)${fence.replaceAll(\"+\", \"\\\\+\")}[ \\\\t]*(?:\\\\r|\\\\n|$)`);\n return closing.test(source.slice(opening[0].length)) ? undefined : fence;\n}\n\n/**\n * Parse a document in `dialect`, or the dialect that an alias such as\n * `codeberg` stands for, which the result holds; an unknown dialect is an\n * error. Byte order marks at the start of `source` are not part of the\n * document: the document's source starts after them, as the positions in the\n * tree count. `path` is the document's path relative to the workspace root,\n * and `plugins` add their syntax extensions.\n *\n * Front matter whose `---` or `+++` closes it inside a code block, an HTML\n * block, or display math, opened by a line of the front matter, is read as\n * Markdown: the document is read without it (issue #295), and\n * `frontMatterReading` gives the other reading. For a document read with TOML\n * front matter, `otherReading` gives the reading without it (issue #333). A\n * document whose first line would open front matter that no line closes is\n * read without the front matter extension, as renderers read it (issue #278).\n */\nexport function parse(\n source: string,\n dialect: DialectName,\n path = \"document.md\",\n plugins: readonly Plugin[] = [],\n): Document {\n const read = knownDialect(dialect);\n source = splitByteOrderMark(source)[1];\n const tree = (withFrontMatter: boolean): Root => {\n const codeSpans = codeSpanRecording();\n const extensions = [codeSpans.syntax];\n const mdastExtensions = [labelEndTree, codeSpans.tree];\n if (withFrontMatter) {\n extensions.unshift(frontmatter([\"yaml\", \"toml\"]));\n mdastExtensions.unshift(frontmatterFromMarkdown([\"yaml\", \"toml\"]));\n }\n // GitHub, Forgejo, and Gitea share GFM tables, task lists, strikethrough,\n // footnotes, autolinks, and dollar math. Forgejo and Gitea syntax beyond\n // that is protected by rules rather than parsed.\n if (read !== \"commonmark\") {\n // Obsidian marks strikethrough with two tildes only; `~a~` is text there\n // (issue #319).\n extensions.push(gfm({ singleTilde: read !== \"obsidian\" }), math(mathConditions[read]));\n mdastExtensions.push(...gfmTree, mathFromMarkdown());\n }\n if (read === \"obsidian\") {\n extensions.push(obsidianSyntax);\n mdastExtensions.push(obsidianTree);\n }\n for (const plugin of plugins) {\n if (plugin.syntax) {\n extensions.push(plugin.syntax.micromark);\n mdastExtensions.push(plugin.syntax.mdast);\n }\n }\n return fromMarkdown(source, { extensions, mdastExtensions });\n };\n const result = tree(!unclosedFrontMatter(source));\n // The tree's types know YAML front matter only; the parser also reads TOML.\n const matter = result.children[0] as { type: string; value?: string } | undefined;\n let plain: Root | undefined;\n // Only front matter with a line that could open such a block is read again.\n // It is given up only when the line that closes it is content of that block\n // in the other reading, which a multiline TOML string or a YAML block\n // scalar holding a fence does not decide by itself.\n if (\n (matter?.type === \"yaml\" || matter?.type === \"toml\") &&\n literalOpener.test(matter.value ?? \"\")\n ) {\n const closing = result.children[0]!.position!.end.line;\n plain = tree(false);\n if (\n plain.children.some(\n (node) =>\n literalBlocks.has(node.type) &&\n node.position!.start.line < closing &&\n node.position!.end.line >= closing,\n )\n ) {\n frontMatterReadings.set(plain, result);\n return { source, dialect: read, path, tree: plain };\n }\n }\n if (matter?.type === \"toml\") tomlReadings.set(result, () => (plain ??= tree(false)));\n return { source, dialect: read, path, tree: result };\n}\n\n/**\n * The `[start, end]` offsets of a node of a parsed document in its `source`:\n * half-open, in UTF-16 code units. A node without a source position, such as\n * one that a plugin's tree extension built without one, is an error.\n */\nexport function range(node: Nodes): [number, number] {\n if (node.position?.start.offset === undefined || node.position.end.offset === undefined) {\n throw new Error(`Missing source position for ${node.type}`);\n }\n return [node.position.start.offset, node.position.end.offset];\n}\n/** An alert's or callout's marker, such as `[!NOTE]` or `[!tip]`. */\nconst calloutMarker = /^\\[![\\w-]+\\]/;\n/**\n * Whether a paragraph starts with text that `pattern` matches, by default an\n * alert's or callout's marker: in `written`, its source from its start, or in\n * its text as renderers read it, where a backslash escape or a character\n * reference stands for the character it writes. GitHub reads `\\[!NOTE]`,\n * `[!NOTE]`, and `[!NOTE]` as alerts too (issue #294).\n */\nexport function startsWithMarker(\n written: string,\n paragraph: Paragraph,\n pattern: RegExp = calloutMarker,\n): boolean {\n const first = paragraph.children[0];\n return pattern.test(written) || (first?.type === \"text\" && pattern.test(first.value));\n}\n/**\n * A node's text as a reader sees it: the values of its text, code, and other\n * literal nodes, in order. A code span gives its text as `codeSpanText` reads\n * it, on one line, a wikilink its label, or else its target, and an Obsidian\n * comment nothing.\n */\nexport function textContent(node: Nodes): string {\n if (node.type === \"wikiLink\") return node.label ?? node.target;\n if (node.type === \"obsidianComment\") return \"\";\n if (node.type === \"inlineCode\") return codeSpanText(node);\n if (\"value\" in node) return node.value;\n if (\"children\" in node) return node.children.map(textContent).join(\"\");\n return \"\";\n}\n"]}
|
package/docs/design.md
CHANGED
|
@@ -29,32 +29,43 @@ that were kept changed it.
|
|
|
29
29
|
The comparison is only as good as the parser's reading:
|
|
30
30
|
|
|
31
31
|
- It compares syntax trees, and does not run GitHub's, Forgejo's, Gitea's, or
|
|
32
|
-
Obsidian's renderer.
|
|
32
|
+
Obsidian's renderer. To find documents that renderers read otherwise than the
|
|
33
|
+
parser, the tests format generated documents and CommonMark's examples and
|
|
34
|
+
compare the HTML of commonmark.js and markdown-it before and after, and a
|
|
35
|
+
weekly workflow does the same with GitHub's renderer. Each difference found is
|
|
36
|
+
tracked as an issue.
|
|
33
37
|
- It reads the document in its dialect, so it cannot notice that the dialect is
|
|
34
38
|
wrong. In CommonMark a table is an ordinary paragraph, and joining its lines
|
|
35
39
|
changes nothing that CommonMark sees. This is why the tool [assumes a
|
|
36
40
|
dialect](dialects.md#the-assumed-dialect) with more syntax, not less, when
|
|
37
41
|
none is named.
|
|
38
|
-
- The parser, micromark, follows CommonMark closely but not everywhere.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
and
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
uses
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
- The parser, micromark, follows CommonMark closely but not everywhere. A list
|
|
43
|
+
that does not start at 1, or an empty item, is read as text at the start of a
|
|
44
|
+
list item, block quote, or footnote definition directly below a paragraph, as
|
|
45
|
+
in `- 2. Step` or `> -`, and after an indented code block, where CommonMark
|
|
46
|
+
and GitHub read a list ([issue
|
|
47
|
+
#191](https://github.com/viell-dev/slop-markdown-tools/issues/191)). The
|
|
48
|
+
comparison uses that reading before and after, so it cannot see what an edit
|
|
49
|
+
does to the list that renderers show. A paragraph or underlined heading that
|
|
50
|
+
starts with a list marker is therefore left as written, as are the blocks
|
|
51
|
+
indented below it, which renderers read inside that list: every rule's edits
|
|
52
|
+
there are left out, plugins' too, and so are their findings. The list rules
|
|
53
|
+
also leave a list there, or directly after it, as written, because another
|
|
54
|
+
marker or indentation could join it to that list or move it out of the list,
|
|
55
|
+
and `style/block-spacing` adds no blank line that would make the list loose.
|
|
56
|
+
The same holds below a link reference definition, where the parser and GitHub
|
|
57
|
+
read such a line as text and markdown-it reads a list. Below an empty list
|
|
58
|
+
item (`-`), the parser takes the `[x]` that begins the next line for a
|
|
59
|
+
checkbox that belongs to no item, where GitHub shows the text `[x]`, so a
|
|
60
|
+
blank line between them, as `style/block-spacing` would add, is refused as a
|
|
61
|
+
change of meaning. The parser also keeps the spaces and tabs that start a code
|
|
62
|
+
span's next line, where CommonMark and GitHub leave them out, so that a span
|
|
63
|
+
`npm run` continued by an indented `build` reads `npm run build`, and it
|
|
64
|
+
takes a space or line ending off each end of a span before its line endings
|
|
65
|
+
become spaces, where CommonMark takes spaces off after. The comparison reads a
|
|
66
|
+
code span as GitHub shows it, also where renderers differ: GitHub keeps the
|
|
67
|
+
indentation of a lazy continuation line in the span, and commonmark.js does
|
|
68
|
+
not. `style/inline-code` and `style/wrap` join a span to that text ([issue
|
|
58
69
|
#265](https://github.com/viell-dev/slop-markdown-tools/issues/265), [issue
|
|
59
70
|
#285](https://github.com/viell-dev/slop-markdown-tools/issues/285), [issue
|
|
60
71
|
#290](https://github.com/viell-dev/slop-markdown-tools/issues/290)). Directly
|
|
@@ -78,6 +89,17 @@ The comparison is only as good as the parser's reading:
|
|
|
78
89
|
document, a rule whose edits would change either, such as joining the lines of
|
|
79
90
|
the front matter, is refused ([issue
|
|
80
91
|
#295](https://github.com/viell-dev/slop-markdown-tools/issues/295)).
|
|
92
|
+
- TOML front matter, between lines of `+++`, is read in every dialect, for site
|
|
93
|
+
generators. GitHub, Forgejo, Gitea, Obsidian, commonmark.js, and markdown-it
|
|
94
|
+
have no TOML front matter and read its lines as a paragraph, which the block
|
|
95
|
+
directly below, with no blank line between, can continue as text or underline
|
|
96
|
+
as a heading. An edit has to keep the meaning of both readings, and the rules
|
|
97
|
+
that would change that block's reading leave it as written: a thematic break
|
|
98
|
+
that is or would become `---`, an underlined heading, and a list that cannot
|
|
99
|
+
interrupt a paragraph, such as a numbered list that does not start at 1
|
|
100
|
+
([issue #333](https://github.com/viell-dev/slop-markdown-tools/issues/333)).
|
|
101
|
+
YAML front matter, outside the case above, is read as front matter only, as
|
|
102
|
+
GitHub's file view, Forgejo, Gitea, and Obsidian read it.
|
|
81
103
|
|
|
82
104
|
Which `$…$` is math was observed on the renderers themselves: about 125 cases
|
|
83
105
|
rendered by GitHub, Forgejo, and Gitea, which the tests compare with the tool on
|
package/docs/dialects.md
CHANGED
|
@@ -20,15 +20,19 @@ what is text, and so what formatting has to leave alone. Choose it in the
|
|
|
20
20
|
| Wikilinks, embeds, highlights (`==…==`), comments (`%%…%%`), `^block-ids` | No | No | No | No | Yes |
|
|
21
21
|
|
|
22
22
|
GitHub's syntax is the common base of the Forgejo, Gitea, and Obsidian dialects,
|
|
23
|
-
and the rules for GitHub Markdown apply to all three.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
and the rules for GitHub Markdown apply to all three. TOML front matter is read
|
|
24
|
+
in every dialect for site generators, though the renderers read its lines as a
|
|
25
|
+
paragraph, and formatting keeps the meaning of [both
|
|
26
|
+
readings](design.md#safety-model). Obsidian marks strikethrough with two tildes
|
|
27
|
+
only, so `~a~` is text there. The syntax that only Forgejo and Gitea have is
|
|
28
|
+
protected from formatting rather than parsed; see [Forgejo and
|
|
29
|
+
Gitea](#forgejo-and-gitea). Syntax that no dialect knows, such as an Obsidian
|
|
30
|
+
plugin's, is read as text: use a suppression comment or an `ignore` pattern for
|
|
31
|
+
it. Site-generator syntax is read as text too, but its lines are kept:
|
|
32
|
+
[`style/wrap`](rules.md#stylewrap) leaves a paragraph as written when one of its
|
|
33
|
+
lines starts with a container such as Docusaurus's `:::note` or a template tag
|
|
34
|
+
such as Liquid's `{% tab %}` or `{{ title }}`, because site generators read them
|
|
35
|
+
by line.
|
|
32
36
|
|
|
33
37
|
An alert's or callout's marker is read from the text, as GitHub reads it, so
|
|
34
38
|
`> \[!NOTE]` and `> [!NOTE]` are alerts too. Formatting keeps the line of
|
package/docs/plugins.md
CHANGED
|
@@ -114,10 +114,10 @@ A style rule's `phase` is `"inline"` (the default), `"block"`, or `"document"`.
|
|
|
114
114
|
`format` runs the phases in that order and repeats them until nothing changes,
|
|
115
115
|
for at most eight passes. After each phase that changes the document, it parses
|
|
116
116
|
the result again and compares its meaning with the original's; the [safety
|
|
117
|
-
model](design.md#safety-model) describes what happens to edits that overlap
|
|
118
|
-
would change the meaning
|
|
119
|
-
|
|
120
|
-
code does.
|
|
117
|
+
model](design.md#safety-model) describes what happens to edits that overlap,
|
|
118
|
+
would change the meaning, or fall in text that the parser reads where renderers
|
|
119
|
+
read a list. The comparison covers the syntax tree, not every application's
|
|
120
|
+
rendering, and it constrains the text a rule returns, not what its code does.
|
|
121
121
|
|
|
122
122
|
## Resolve links in a rule
|
|
123
123
|
|
package/docs/quick-start.md
CHANGED