mdrefine 0.2.0-rc.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +577 -0
- package/CODE_OF_CONDUCT.md +30 -0
- package/CONTRIBUTING.md +189 -16
- package/README.md +100 -154
- package/SECURITY.md +15 -13
- package/config.schema.json +91 -0
- package/dist/cli/diff.d.ts +10 -0
- package/dist/cli/diff.js +247 -0
- package/dist/cli/diff.js.map +1 -0
- package/dist/cli/main.js +270 -150
- package/dist/cli/main.js.map +1 -1
- package/dist/config/load.js +67 -6
- package/dist/config/load.js.map +1 -1
- package/dist/config/problems.d.ts +7 -0
- package/dist/config/problems.js +208 -0
- package/dist/config/problems.js.map +1 -0
- package/dist/config/resolve.d.ts +42 -109
- package/dist/config/resolve.js +193 -52
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/directives.d.ts +7 -0
- package/dist/core/directives.js +25 -13
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.d.ts +42 -4
- package/dist/core/engine.js +518 -79
- package/dist/core/engine.js.map +1 -1
- package/dist/core/frozen.d.ts +6 -0
- package/dist/core/frozen.js +15 -0
- package/dist/core/frozen.js.map +1 -0
- package/dist/core/scope.d.ts +30 -0
- package/dist/core/scope.js +122 -0
- package/dist/core/scope.js.map +1 -0
- package/dist/core/types.d.ts +202 -23
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +3 -0
- package/dist/node.js +4 -0
- package/dist/node.js.map +1 -0
- package/dist/rules/blocks.d.ts +34 -0
- package/dist/rules/blocks.js +550 -0
- package/dist/rules/blocks.js.map +1 -0
- package/dist/rules/content.d.ts +2 -0
- package/dist/rules/content.js +85 -0
- package/dist/rules/content.js.map +1 -0
- package/dist/rules/dialects.js +10 -6
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +302 -6
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/lists.d.ts +2 -0
- package/dist/rules/lists.js +924 -0
- package/dist/rules/lists.js.map +1 -0
- package/dist/rules/structure.js +127 -1
- package/dist/rules/structure.js.map +1 -1
- package/dist/rules/style.d.ts +8 -2
- package/dist/rules/style.js +1137 -165
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/block-identifier.d.ts +18 -0
- package/dist/syntax/block-identifier.js +24 -0
- package/dist/syntax/block-identifier.js.map +1 -0
- package/dist/syntax/obsidian.d.ts +29 -11
- package/dist/syntax/obsidian.js +9 -10
- package/dist/syntax/obsidian.js.map +1 -1
- package/dist/syntax/parse.d.ts +87 -3
- package/dist/syntax/parse.js +247 -19
- package/dist/syntax/parse.js.map +1 -1
- package/dist/workspace/access.d.ts +12 -3
- package/dist/workspace/access.js +33 -5
- package/dist/workspace/access.js.map +1 -1
- package/dist/workspace/discovery.d.ts +85 -0
- package/dist/workspace/discovery.js +155 -0
- package/dist/workspace/discovery.js.map +1 -0
- package/dist/workspace/encoding.d.ts +24 -0
- package/dist/workspace/encoding.js +113 -0
- package/dist/workspace/encoding.js.map +1 -0
- package/dist/workspace/files.d.ts +92 -6
- package/dist/workspace/files.js +715 -59
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/git.d.ts +54 -0
- package/dist/workspace/git.js +198 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/index.d.ts +58 -3
- package/dist/workspace/index.js +136 -37
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.d.ts +2 -2
- package/dist/workspace/selection.js +3 -3
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/slug.js +8 -1
- package/dist/workspace/slug.js.map +1 -1
- package/dist/workspace/vault.js +3 -1
- package/dist/workspace/vault.js.map +1 -1
- package/docs/api.md +239 -0
- package/docs/benchmarks.md +140 -200
- package/docs/changelog.md +1 -0
- package/docs/cli.md +281 -89
- package/docs/configuration.md +193 -480
- package/docs/design.md +163 -284
- package/docs/dialects.md +169 -0
- package/docs/examples.md +151 -109
- package/docs/index.md +21 -37
- package/docs/integrations.md +144 -0
- package/docs/obsidian.md +91 -115
- package/docs/plugins.md +147 -212
- package/docs/quick-start.md +68 -74
- package/docs/rules.md +1237 -0
- package/docs/versions.md +56 -0
- package/examples/README.md +17 -14
- package/examples/automation/summarize-report.mjs +5 -2
- package/examples/library/lint-folder.mjs +5 -4
- package/examples/library/lint-workspace.mjs +32 -0
- package/examples/plugin/notes/release-checklist.md +2 -2
- package/examples/plugin/rules.mjs +17 -23
- package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +2 -2
- package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +2 -2
- package/examples/workspaces/repository-docs/after/README.md +2 -2
- package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +0 -2
- package/examples/workspaces/repository-docs/before/README.md +2 -2
- package/examples/workspaces/repository-docs/before/docs/guide.md +7 -7
- package/examples/workspaces/repository-docs/before/docs/reference.md +3 -1
- package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +0 -2
- package/package.json +38 -12
- package/docs/contributing.md +0 -218
- package/docs/releases/0.1.0-beta.1.md +0 -39
- package/docs/releases/0.1.0-beta.2.md +0 -83
- package/docs/releases/0.1.0-beta.3.md +0 -61
- package/docs/releases/0.1.0-beta.4.md +0 -50
- package/docs/releases/0.1.0-rc.1.md +0 -99
- package/docs/releases/0.2.0-rc.1.md +0 -141
- package/docs/releases/0.2.0-rc.2.md +0 -225
- package/docs/releases.md +0 -144
- package/docs/testing.md +0 -93
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../../src/syntax/obsidian.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Literal } from \"mdast\";\nimport type { Extension, State, Tokenizer } from \"micromark-util-types\";\nimport type { Extension as TreeExtension } from \"mdast-util-from-markdown\";\n\nexport interface ObsidianLiteral extends Literal {\n type: \"wikiLink\" | \"obsidianComment\" | \"obsidianHighlight\";\n embed?: boolean;\n target?: string;\n label?: string;\n}\ndeclare module \"mdast\" {\n interface RootContentMap {\n wikiLink: ObsidianLiteral;\n obsidianComment: ObsidianLiteral;\n obsidianHighlight: ObsidianLiteral;\n }\n interface PhrasingContentMap {\n wikiLink: ObsidianLiteral;\n obsidianComment: ObsidianLiteral;\n obsidianHighlight: ObsidianLiteral;\n }\n interface BlockContentMap {\n obsidianComment: ObsidianLiteral;\n }\n}\ndeclare module \"micromark-util-types\" {\n interface TokenTypeMap {\n obsidianLiteral: \"obsidianLiteral\";\n obsidianBlockComment: \"obsidianBlockComment\";\n obsidianCommentData: \"obsidianCommentData\";\n }\n}\n\n/** A tokenizer, not a pre-parse replacement: code, escapes, and fenced blocks stay opaque. */\nconst tokenize: Tokenizer = function (effects, ok, nok) {\n let opening = \"\";\n let closing = \"\";\n let index = 0;\n let previous: number | null = null;\n let count = 0;\n const start: State = (code) => {\n opening = code === 33 ? \"![[\" : code === 91 ? \"[[\" : code === 37 ? \"%%\" : \"==\";\n closing = opening.endsWith(\"[[\") ? \"]]\" : opening;\n effects.enter(\"obsidianLiteral\");\n effects.enter(\"obsidianCommentData\");\n return open(code);\n };\n const open: State = (code) => {\n if (code !== opening.charCodeAt(index)) return nok(code);\n effects.consume(code);\n index++;\n return index === opening.length ? body : open;\n };\n const body: State = (code) => {\n if (code === null || (opening !== \"%%\" && code < 0 && code !== -1 && code !== -2))\n return nok(code);\n if (code < -2) {\n // Micromark subtokenizes each physical line separately. Expose every\n // line ending so a multiline inline comment can span more than two chunks.\n effects.exit(\"obsidianCommentData\");\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n previous = null;\n count++;\n return resume;\n }\n effects.consume(code);\n count++;\n if (code === closing.charCodeAt(1) && previous === closing.charCodeAt(0)) {\n if (count <= 2) return nok(code);\n effects.exit(\"obsidianCommentData\");\n effects.exit(\"obsidianLiteral\");\n return ok;\n }\n previous = code;\n return body;\n };\n const resume: State = (code) => {\n effects.enter(\"obsidianCommentData\");\n return body(code);\n };\n return start;\n};\n\n// Standalone comment blocks may cross blank lines. Keep their complete source opaque.\nconst tokenizeBlockComment: Tokenizer = function (effects, ok, nok) {\n let opened = 0;\n let previous: number | null = null;\n let closed = false;\n const start: State = (code) => {\n if (code !== 37) return nok(code);\n if (opened === 0) {\n effects.enter(\"obsidianBlockComment\");\n effects.enter(\"obsidianCommentData\");\n }\n effects.consume(code);\n opened++;\n return opened === 2 ? body : start;\n };\n const body: State = (code) => {\n if (code === null || (closed && code < -2)) {\n effects.exit(\"obsidianCommentData\");\n effects.exit(\"obsidianBlockComment\");\n return ok(code);\n }\n if (code < -2) {\n effects.exit(\"obsidianCommentData\");\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n previous = null;\n return before;\n }\n effects.consume(code);\n if (code === 37 && previous === 37) closed = true;\n previous = code;\n return body;\n };\n const before: State = (code) => {\n if (code === null) {\n effects.exit(\"obsidianBlockComment\");\n return ok(code);\n }\n if (code < -2) {\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n return before;\n }\n effects.enter(\"obsidianCommentData\");\n return body(code);\n };\n return start;\n};\n\nexport const obsidianSyntax: Extension = {\n flow: { 37: { name: \"obsidianBlockComment\", tokenize: tokenizeBlockComment, concrete: true } },\n text: Object.fromEntries(\n [33, 91, 37, 61].map((code) => [code, { name: \"obsidianLiteral\", tokenize }]),\n ),\n};\nexport const obsidianTree: TreeExtension = {\n enter: {\n obsidianBlockComment(token) {\n this.enter({ type: \"obsidianComment\", value: this.sliceSerialize(token) }, token);\n },\n obsidianLiteral(token) {\n const raw = this.sliceSerialize(token);\n const wiki = raw.startsWith(\"[[\") || raw.startsWith(\"![[\");\n const embed = raw.startsWith(\"!\");\n const value = raw.slice(wiki && embed ? 3 : 2, -2).replaceAll(\"\\\\|\", \"|\");\n const divider = value.indexOf(\"|\");\n const node: ObsidianLiteral = {\n type: wiki ? \"wikiLink\" : raw.startsWith(\"%%\") ? \"obsidianComment\" : \"obsidianHighlight\",\n value,\n };\n if (wiki) {\n node.embed = embed;\n node.target = divider < 0 ? value : value.slice(0, divider);\n if (divider >= 0) node.label = value.slice(divider + 1);\n }\n this.enter(node, token);\n },\n },\n exit: {\n obsidianBlockComment(token) {\n this.exit(token);\n },\n obsidianLiteral(token) {\n this.exit(token);\n },\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"obsidian.js","sourceRoot":"","sources":["../../src/syntax/obsidian.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Literal } from \"mdast\";\nimport type { Extension, State, Tokenizer } from \"micromark-util-types\";\nimport type { Extension as TreeExtension } from \"mdast-util-from-markdown\";\n\n/** An Obsidian wikilink, `[[target|label]]`, or embed, `![[target]]`, in the Obsidian dialect. */\nexport interface WikiLink extends Literal {\n type: \"wikiLink\";\n /** The text between the brackets, with `\\|` read as `|`. */\n value: string;\n /** Whether it is an embed, written with `!` before the brackets. */\n embed: boolean;\n /** The part before the first `|`: a note name or path, and an optional `#heading` or `#^block`. */\n target: string;\n /** The part after the first `|`, when there is one. */\n label?: string;\n}\n/**\n * An Obsidian comment in the Obsidian dialect. `value` is the text between the\n * `%%` markers of a comment in running text, and the whole source of a comment\n * that stands as a block of its own, markers included.\n */\nexport interface ObsidianComment extends Literal {\n type: \"obsidianComment\";\n}\n/** An Obsidian highlight, `==text==`, in the Obsidian dialect. `value` is the text between the markers. */\nexport interface ObsidianHighlight extends Literal {\n type: \"obsidianHighlight\";\n}\ndeclare module \"mdast\" {\n interface RootContentMap {\n wikiLink: WikiLink;\n obsidianComment: ObsidianComment;\n obsidianHighlight: ObsidianHighlight;\n }\n interface PhrasingContentMap {\n wikiLink: WikiLink;\n obsidianComment: ObsidianComment;\n obsidianHighlight: ObsidianHighlight;\n }\n interface BlockContentMap {\n obsidianComment: ObsidianComment;\n }\n}\ndeclare module \"micromark-util-types\" {\n interface TokenTypeMap {\n obsidianLiteral: \"obsidianLiteral\";\n obsidianBlockComment: \"obsidianBlockComment\";\n obsidianCommentData: \"obsidianCommentData\";\n }\n}\n\n/** A tokenizer, not a pre-parse replacement: code, escapes, and fenced blocks stay opaque. */\nconst tokenize: Tokenizer = function (effects, ok, nok) {\n let opening = \"\";\n let closing = \"\";\n let index = 0;\n let previous: number | null = null;\n let count = 0;\n const start: State = (code) => {\n opening = code === 33 ? \"![[\" : code === 91 ? \"[[\" : code === 37 ? \"%%\" : \"==\";\n closing = opening.endsWith(\"[[\") ? \"]]\" : opening;\n effects.enter(\"obsidianLiteral\");\n effects.enter(\"obsidianCommentData\");\n return open(code);\n };\n const open: State = (code) => {\n if (code !== opening.charCodeAt(index)) return nok(code);\n effects.consume(code);\n index++;\n return index === opening.length ? body : open;\n };\n const body: State = (code) => {\n if (code === null || (opening !== \"%%\" && code < 0 && code !== -1 && code !== -2))\n return nok(code);\n if (code < -2) {\n // Micromark subtokenizes each physical line separately. Expose every\n // line ending so a multiline inline comment can span more than two chunks.\n effects.exit(\"obsidianCommentData\");\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n previous = null;\n count++;\n return resume;\n }\n effects.consume(code);\n count++;\n if (code === closing.charCodeAt(1) && previous === closing.charCodeAt(0)) {\n if (count <= 2) return nok(code);\n effects.exit(\"obsidianCommentData\");\n effects.exit(\"obsidianLiteral\");\n return ok;\n }\n previous = code;\n return body;\n };\n const resume: State = (code) => {\n effects.enter(\"obsidianCommentData\");\n return body(code);\n };\n return start;\n};\n\n// Standalone comment blocks may cross blank lines. Keep their complete source opaque.\nconst tokenizeBlockComment: Tokenizer = function (effects, ok, nok) {\n let opened = 0;\n let previous: number | null = null;\n let closed = false;\n const start: State = (code) => {\n if (code !== 37) return nok(code);\n if (opened === 0) {\n effects.enter(\"obsidianBlockComment\");\n effects.enter(\"obsidianCommentData\");\n }\n effects.consume(code);\n opened++;\n return opened === 2 ? body : start;\n };\n const body: State = (code) => {\n if (code === null || (closed && code < -2)) {\n effects.exit(\"obsidianCommentData\");\n effects.exit(\"obsidianBlockComment\");\n return ok(code);\n }\n if (code < -2) {\n effects.exit(\"obsidianCommentData\");\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n previous = null;\n return before;\n }\n effects.consume(code);\n if (code === 37 && previous === 37) closed = true;\n previous = code;\n return body;\n };\n const before: State = (code) => {\n if (code === null) {\n effects.exit(\"obsidianBlockComment\");\n return ok(code);\n }\n if (code < -2) {\n effects.enter(\"lineEnding\");\n effects.consume(code);\n effects.exit(\"lineEnding\");\n return before;\n }\n effects.enter(\"obsidianCommentData\");\n return body(code);\n };\n return start;\n};\n\nexport const obsidianSyntax: Extension = {\n flow: { 37: { name: \"obsidianBlockComment\", tokenize: tokenizeBlockComment, concrete: true } },\n text: Object.fromEntries(\n [33, 91, 37, 61].map((code) => [code, { name: \"obsidianLiteral\", tokenize }]),\n ),\n};\nexport const obsidianTree: TreeExtension = {\n enter: {\n obsidianBlockComment(token) {\n this.enter({ type: \"obsidianComment\", value: this.sliceSerialize(token) }, token);\n },\n obsidianLiteral(token) {\n const raw = this.sliceSerialize(token);\n const wiki = raw.startsWith(\"[[\") || raw.startsWith(\"![[\");\n const embed = raw.startsWith(\"!\");\n const value = raw.slice(wiki && embed ? 3 : 2, -2).replaceAll(\"\\\\|\", \"|\");\n const divider = value.indexOf(\"|\");\n const node: WikiLink | ObsidianComment | ObsidianHighlight = wiki\n ? {\n type: \"wikiLink\",\n value,\n embed,\n target: divider < 0 ? value : value.slice(0, divider),\n ...(divider >= 0 ? { label: value.slice(divider + 1) } : {}),\n }\n : { type: raw.startsWith(\"%%\") ? \"obsidianComment\" : \"obsidianHighlight\", value };\n this.enter(node, token);\n },\n },\n exit: {\n obsidianBlockComment(token) {\n this.exit(token);\n },\n obsidianLiteral(token) {\n this.exit(token);\n },\n },\n};\n"]}
|
package/dist/syntax/parse.d.ts
CHANGED
|
@@ -1,5 +1,89 @@
|
|
|
1
|
-
import type { Nodes } from "mdast";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import type { InlineCode, Nodes, Paragraph, Root } from "mdast";
|
|
2
|
+
import type { DialectName, Document, Plugin } from "../core/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where the text of a link or the description of an image ends in the source:
|
|
5
|
+
* the offset of its closing `]`. Undefined for other nodes and for nodes that
|
|
6
|
+
* `parse` did not make.
|
|
7
|
+
*/
|
|
8
|
+
export declare function labelEnd(node: Nodes): number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The text of a code span as GitHub shows it, read in CommonMark's order: the
|
|
11
|
+
* spaces and tabs that start each line after the first are dropped, as from
|
|
12
|
+
* every line of a paragraph, but kept on a lazy continuation line, one that
|
|
13
|
+
* lacks its block quote's `>` or its list item's indentation; then each line
|
|
14
|
+
* ending becomes a space; then one space comes off each end when both ends
|
|
15
|
+
* have one and the span is not only spaces. The parser keeps the spaces and
|
|
16
|
+
* tabs in the span's value, and takes a space or line ending off each end
|
|
17
|
+
* before line endings become spaces (issues #276 and #290). A node that
|
|
18
|
+
* `parse` did not make is read as if its value were all of its content.
|
|
19
|
+
*/
|
|
20
|
+
export declare function codeSpanText(node: InlineCode): string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a code span goes on in a lazy continuation line that starts with
|
|
23
|
+
* spaces or tabs. GitHub keeps them in the span, as `codeSpanText` does, and
|
|
24
|
+
* commonmark.js drops them, so renderers show such a span differently.
|
|
25
|
+
*/
|
|
26
|
+
export declare function lazyIndentation(node: InlineCode): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The byte order marks that `source` starts with, and the text after them.
|
|
29
|
+
* Some editors write a mark at the start of a UTF-8 file, and a program that
|
|
30
|
+
* adds one without looking can leave two. The parser skips one mark at the
|
|
31
|
+
* start of what it is given.
|
|
32
|
+
*/
|
|
33
|
+
export declare function splitByteOrderMark(source: string): [string, string];
|
|
34
|
+
/**
|
|
35
|
+
* The document read with front matter, where `parse` read it without front
|
|
36
|
+
* matter because its front matter would end inside a code block, an HTML
|
|
37
|
+
* block, or display math. Site generators read front matter there, and so does
|
|
38
|
+
* GitHub's file view when it is a YAML mapping; GitHub's comments and
|
|
39
|
+
* renderers without front matter read the block. Undefined for other trees.
|
|
40
|
+
*/
|
|
41
|
+
export declare function frontMatterReading(tree: Root): Root | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* The fence, `---` or `+++`, of front matter that the first line of `source`
|
|
44
|
+
* opens and no line closes. The front matter extension opens front matter at a
|
|
45
|
+
* first line of `---` or `+++` followed only by spaces or tabs, and closes it
|
|
46
|
+
* at the next line of the same fence, also followed only by spaces or tabs.
|
|
47
|
+
* Without a closing line the document has no front matter, but the extension
|
|
48
|
+
* then reads the lists and block quotes below as paragraphs (issue #278,
|
|
49
|
+
* micromark/micromark-extension-frontmatter#5). An edit that writes such a line
|
|
50
|
+
* below makes front matter of everything above it.
|
|
51
|
+
*/
|
|
52
|
+
export declare function unclosedFrontMatter(source: string): "---" | "+++" | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Parse a document in `dialect`, or the dialect that an alias such as
|
|
55
|
+
* `codeberg` stands for, which the result holds; an unknown dialect is an
|
|
56
|
+
* error. Byte order marks at the start of `source` are not part of the
|
|
57
|
+
* document: the document's source starts after them, as the positions in the
|
|
58
|
+
* tree count. `path` is the document's path relative to the workspace root,
|
|
59
|
+
* and `plugins` add their syntax extensions.
|
|
60
|
+
*
|
|
61
|
+
* Front matter whose `---` or `+++` closes it inside a code block, an HTML
|
|
62
|
+
* block, or display math, opened by a line of the front matter, is read as
|
|
63
|
+
* Markdown: the document is read without it (issue #295), and
|
|
64
|
+
* `frontMatterReading` gives the other reading. A document whose first line
|
|
65
|
+
* would open front matter that no line closes is read without the front matter
|
|
66
|
+
* extension, as renderers read it (issue #278).
|
|
67
|
+
*/
|
|
68
|
+
export declare function parse(source: string, dialect: DialectName, path?: string, plugins?: readonly Plugin[]): Document;
|
|
69
|
+
/**
|
|
70
|
+
* The `[start, end]` offsets of a node of a parsed document in its `source`:
|
|
71
|
+
* half-open, in UTF-16 code units. A node without a source position, such as
|
|
72
|
+
* one that a plugin's tree extension built without one, is an error.
|
|
73
|
+
*/
|
|
4
74
|
export declare function range(node: Nodes): [number, number];
|
|
75
|
+
/**
|
|
76
|
+
* Whether a paragraph starts with text that `pattern` matches, by default an
|
|
77
|
+
* alert's or callout's marker: in `written`, its source from its start, or in
|
|
78
|
+
* its text as renderers read it, where a backslash escape or a character
|
|
79
|
+
* reference stands for the character it writes. GitHub reads `\[!NOTE]`,
|
|
80
|
+
* `[!NOTE]`, and `[!NOTE]` as alerts too (issue #294).
|
|
81
|
+
*/
|
|
82
|
+
export declare function startsWithMarker(written: string, paragraph: Paragraph, pattern?: RegExp): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* A node's text as a reader sees it: the values of its text, code, and other
|
|
85
|
+
* literal nodes, in order. A code span gives its text as `codeSpanText` reads
|
|
86
|
+
* it, on one line, a wikilink its label, or else its target, and an Obsidian
|
|
87
|
+
* comment nothing.
|
|
88
|
+
*/
|
|
5
89
|
export declare function textContent(node: Nodes): string;
|
package/dist/syntax/parse.js
CHANGED
|
@@ -4,6 +4,7 @@ import { gfmFromMarkdown } from "mdast-util-gfm";
|
|
|
4
4
|
import { frontmatter } from "micromark-extension-frontmatter";
|
|
5
5
|
import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
|
|
6
6
|
import { mathFromMarkdown } from "mdast-util-math";
|
|
7
|
+
import { knownDialect } from "../config/resolve.js";
|
|
7
8
|
import { math } from "./math.js";
|
|
8
9
|
import { obsidianSyntax, obsidianTree } from "./obsidian.js";
|
|
9
10
|
// GFM's tree transform performs GitHub's second, transform-time autolink pass
|
|
@@ -16,6 +17,112 @@ const gfmTree = gfmFromMarkdown().map((extension) => {
|
|
|
16
17
|
delete copy.transforms;
|
|
17
18
|
return copy;
|
|
18
19
|
});
|
|
20
|
+
/** The offset of the `]` that closes the text of each link and image, by node. */
|
|
21
|
+
const labelEnds = new WeakMap();
|
|
22
|
+
/**
|
|
23
|
+
* Record where the text of a link or image ends. The tree keeps an image's
|
|
24
|
+
* description only as plain text, so its source positions are otherwise lost.
|
|
25
|
+
*/
|
|
26
|
+
const labelEndTree = {
|
|
27
|
+
exit: {
|
|
28
|
+
// A label has an opening and a closing marker; the closing one comes last.
|
|
29
|
+
labelMarker(token) {
|
|
30
|
+
// The label's fragment is on top of the stack, and the link or image below it.
|
|
31
|
+
const node = this.stack[this.stack.length - 2];
|
|
32
|
+
if (node?.type === "link" || node?.type === "image")
|
|
33
|
+
labelEnds.set(node, token.start.offset);
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Where the text of a link or the description of an image ends in the source:
|
|
39
|
+
* the offset of its closing `]`. Undefined for other nodes and for nodes that
|
|
40
|
+
* `parse` did not make.
|
|
41
|
+
*/
|
|
42
|
+
export function labelEnd(node) {
|
|
43
|
+
return labelEnds.get(node);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* What the value of a code span in the tree leaves out, by node: the space or
|
|
47
|
+
* line ending that the parser took off each end, and which lines of the
|
|
48
|
+
* document are lazy continuation lines.
|
|
49
|
+
*/
|
|
50
|
+
const codeSpanSources = new WeakMap();
|
|
51
|
+
/**
|
|
52
|
+
* The extensions of one parse that record what code spans leave out. The
|
|
53
|
+
* parser knows which lines are lazy once it has read the document's blocks, and
|
|
54
|
+
* a construct at a backtick, which never matches, reads that from it before a
|
|
55
|
+
* code span is read there.
|
|
56
|
+
*/
|
|
57
|
+
function codeSpanRecording() {
|
|
58
|
+
let lazy = {};
|
|
59
|
+
return {
|
|
60
|
+
syntax: {
|
|
61
|
+
text: {
|
|
62
|
+
[0x60]: {
|
|
63
|
+
// The text tokenizer asks each construct at a backtick whether the
|
|
64
|
+
// character before allows it, and tries them all if one does. This
|
|
65
|
+
// one never does, so the parse is the same as without it.
|
|
66
|
+
previous() {
|
|
67
|
+
lazy = this.parser.lazy;
|
|
68
|
+
return false;
|
|
69
|
+
},
|
|
70
|
+
tokenize(_effects, _ok, nok) {
|
|
71
|
+
lazy = this.parser.lazy;
|
|
72
|
+
return nok;
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
tree: {
|
|
78
|
+
exit: {
|
|
79
|
+
// The span's value is collected in a fragment on top of the stack, with
|
|
80
|
+
// the span's node below it.
|
|
81
|
+
codeTextSequence() {
|
|
82
|
+
const node = this.stack[this.stack.length - 2];
|
|
83
|
+
if (node?.type === "inlineCode" && !codeSpanSources.has(node))
|
|
84
|
+
codeSpanSources.set(node, { padding: [], lazy });
|
|
85
|
+
},
|
|
86
|
+
codeTextPadding(token) {
|
|
87
|
+
const node = this.stack[this.stack.length - 2];
|
|
88
|
+
if (node?.type === "inlineCode")
|
|
89
|
+
codeSpanSources.get(node)?.padding.push(this.sliceSerialize(token));
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The text of a code span as GitHub shows it, read in CommonMark's order: the
|
|
97
|
+
* spaces and tabs that start each line after the first are dropped, as from
|
|
98
|
+
* every line of a paragraph, but kept on a lazy continuation line, one that
|
|
99
|
+
* lacks its block quote's `>` or its list item's indentation; then each line
|
|
100
|
+
* ending becomes a space; then one space comes off each end when both ends
|
|
101
|
+
* have one and the span is not only spaces. The parser keeps the spaces and
|
|
102
|
+
* tabs in the span's value, and takes a space or line ending off each end
|
|
103
|
+
* before line endings become spaces (issues #276 and #290). A node that
|
|
104
|
+
* `parse` did not make is read as if its value were all of its content.
|
|
105
|
+
*/
|
|
106
|
+
export function codeSpanText(node) {
|
|
107
|
+
const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};
|
|
108
|
+
let line = node.position?.start.line ?? 0;
|
|
109
|
+
const content = `${padding[0] ?? ""}${node.value}${padding[1] ?? ""}`.replace(/(?:\r\n|\r|\n)([ \t]*)/g, (_, indentation) => (lazy[++line] ? ` ${indentation}` : " "));
|
|
110
|
+
return /^ [^]* $/.test(content) && /[^ ]/.test(content) ? content.slice(1, -1) : content;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Whether a code span goes on in a lazy continuation line that starts with
|
|
114
|
+
* spaces or tabs. GitHub keeps them in the span, as `codeSpanText` does, and
|
|
115
|
+
* commonmark.js drops them, so renderers show such a span differently.
|
|
116
|
+
*/
|
|
117
|
+
export function lazyIndentation(node) {
|
|
118
|
+
const { padding = [], lazy = {} } = codeSpanSources.get(node) ?? {};
|
|
119
|
+
let line = node.position?.start.line ?? 0;
|
|
120
|
+
const content = `${padding[0] ?? ""}${node.value}${padding[1] ?? ""}`;
|
|
121
|
+
for (const [, indentation] of content.matchAll(/(?:\r\n|\r|\n)([ \t]*)/g))
|
|
122
|
+
if (lazy[++line] && indentation)
|
|
123
|
+
return true;
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
19
126
|
/**
|
|
20
127
|
* Whose conditions for `$…$` in running text each dialect follows, as observed
|
|
21
128
|
* on GitHub's file view, on Forgejo 16 and Gitea 1.25 and 28, and in Obsidian's
|
|
@@ -27,39 +134,160 @@ const mathConditions = {
|
|
|
27
134
|
gitea: "forge",
|
|
28
135
|
obsidian: "obsidian",
|
|
29
136
|
};
|
|
137
|
+
/**
|
|
138
|
+
* The byte order marks that `source` starts with, and the text after them.
|
|
139
|
+
* Some editors write a mark at the start of a UTF-8 file, and a program that
|
|
140
|
+
* adds one without looking can leave two. The parser skips one mark at the
|
|
141
|
+
* start of what it is given.
|
|
142
|
+
*/
|
|
143
|
+
export function splitByteOrderMark(source) {
|
|
144
|
+
let end = 0;
|
|
145
|
+
while (source.charCodeAt(end) === 0xfeff)
|
|
146
|
+
end++;
|
|
147
|
+
return [source.slice(0, end), source.slice(end)];
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The reading with front matter of a document that `parse` read without it,
|
|
151
|
+
* by the tree of the reading without it.
|
|
152
|
+
*/
|
|
153
|
+
const frontMatterReadings = new WeakMap();
|
|
154
|
+
/**
|
|
155
|
+
* The document read with front matter, where `parse` read it without front
|
|
156
|
+
* matter because its front matter would end inside a code block, an HTML
|
|
157
|
+
* block, or display math. Site generators read front matter there, and so does
|
|
158
|
+
* GitHub's file view when it is a YAML mapping; GitHub's comments and
|
|
159
|
+
* renderers without front matter read the block. Undefined for other trees.
|
|
160
|
+
*/
|
|
161
|
+
export function frontMatterReading(tree) {
|
|
162
|
+
return frontMatterReadings.get(tree);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* A line of front matter that could open a block whose content is literal: a
|
|
166
|
+
* code fence, display math, or an HTML block.
|
|
167
|
+
*/
|
|
168
|
+
const literalOpener = /^ {0,3}(?:`{3,}|~{3,}|\$\$|<)/m;
|
|
169
|
+
/** Blocks in which a `---` or `+++` line is content, not a closing line. */
|
|
170
|
+
const literalBlocks = new Set(["code", "html", "math"]);
|
|
171
|
+
/**
|
|
172
|
+
* The fence, `---` or `+++`, of front matter that the first line of `source`
|
|
173
|
+
* opens and no line closes. The front matter extension opens front matter at a
|
|
174
|
+
* first line of `---` or `+++` followed only by spaces or tabs, and closes it
|
|
175
|
+
* at the next line of the same fence, also followed only by spaces or tabs.
|
|
176
|
+
* Without a closing line the document has no front matter, but the extension
|
|
177
|
+
* then reads the lists and block quotes below as paragraphs (issue #278,
|
|
178
|
+
* micromark/micromark-extension-frontmatter#5). An edit that writes such a line
|
|
179
|
+
* below makes front matter of everything above it.
|
|
180
|
+
*/
|
|
181
|
+
export function unclosedFrontMatter(source) {
|
|
182
|
+
const opening = /^(---|\+\+\+)[ \t]*(?:\r\n|\r|\n)/.exec(source);
|
|
183
|
+
if (!opening)
|
|
184
|
+
return undefined;
|
|
185
|
+
const fence = opening[1];
|
|
186
|
+
const closing = new RegExp(`(?:^|\\r|\\n)${fence.replaceAll("+", "\\+")}[ \\t]*(?:\\r|\\n|$)`);
|
|
187
|
+
return closing.test(source.slice(opening[0].length)) ? undefined : fence;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parse a document in `dialect`, or the dialect that an alias such as
|
|
191
|
+
* `codeberg` stands for, which the result holds; an unknown dialect is an
|
|
192
|
+
* error. Byte order marks at the start of `source` are not part of the
|
|
193
|
+
* document: the document's source starts after them, as the positions in the
|
|
194
|
+
* tree count. `path` is the document's path relative to the workspace root,
|
|
195
|
+
* and `plugins` add their syntax extensions.
|
|
196
|
+
*
|
|
197
|
+
* Front matter whose `---` or `+++` closes it inside a code block, an HTML
|
|
198
|
+
* block, or display math, opened by a line of the front matter, is read as
|
|
199
|
+
* Markdown: the document is read without it (issue #295), and
|
|
200
|
+
* `frontMatterReading` gives the other reading. A document whose first line
|
|
201
|
+
* would open front matter that no line closes is read without the front matter
|
|
202
|
+
* extension, as renderers read it (issue #278).
|
|
203
|
+
*/
|
|
30
204
|
export function parse(source, dialect, path = "document.md", plugins = []) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
205
|
+
const read = knownDialect(dialect);
|
|
206
|
+
source = splitByteOrderMark(source)[1];
|
|
207
|
+
const tree = (withFrontMatter) => {
|
|
208
|
+
const codeSpans = codeSpanRecording();
|
|
209
|
+
const extensions = [codeSpans.syntax];
|
|
210
|
+
const mdastExtensions = [labelEndTree, codeSpans.tree];
|
|
211
|
+
if (withFrontMatter) {
|
|
212
|
+
extensions.unshift(frontmatter(["yaml", "toml"]));
|
|
213
|
+
mdastExtensions.unshift(frontmatterFromMarkdown(["yaml", "toml"]));
|
|
214
|
+
}
|
|
215
|
+
// GitHub, Forgejo, and Gitea share GFM tables, task lists, strikethrough,
|
|
216
|
+
// footnotes, autolinks, and dollar math. Forgejo and Gitea syntax beyond
|
|
217
|
+
// that is protected by rules rather than parsed.
|
|
218
|
+
if (read !== "commonmark") {
|
|
219
|
+
extensions.push(gfm(), math(mathConditions[read]));
|
|
220
|
+
mdastExtensions.push(...gfmTree, mathFromMarkdown());
|
|
221
|
+
}
|
|
222
|
+
if (read === "obsidian") {
|
|
223
|
+
extensions.push(obsidianSyntax);
|
|
224
|
+
mdastExtensions.push(obsidianTree);
|
|
225
|
+
}
|
|
226
|
+
for (const plugin of plugins) {
|
|
227
|
+
if (plugin.syntax) {
|
|
228
|
+
extensions.push(plugin.syntax.micromark);
|
|
229
|
+
mdastExtensions.push(plugin.syntax.mdast);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return fromMarkdown(source, { extensions, mdastExtensions });
|
|
233
|
+
};
|
|
234
|
+
let result = tree(!unclosedFrontMatter(source));
|
|
235
|
+
// The tree's types know YAML front matter only; the parser also reads TOML.
|
|
236
|
+
const matter = result.children[0];
|
|
237
|
+
// Only front matter with a line that could open such a block is read again.
|
|
238
|
+
// It is given up only when the line that closes it is content of that block
|
|
239
|
+
// in the other reading, which a multiline TOML string or a YAML block
|
|
240
|
+
// scalar holding a fence does not decide by itself.
|
|
241
|
+
if ((matter?.type === "yaml" || matter?.type === "toml") &&
|
|
242
|
+
literalOpener.test(matter.value ?? "")) {
|
|
243
|
+
const closing = result.children[0].position.end.line;
|
|
244
|
+
const plain = tree(false);
|
|
245
|
+
if (plain.children.some((node) => literalBlocks.has(node.type) &&
|
|
246
|
+
node.position.start.line < closing &&
|
|
247
|
+
node.position.end.line >= closing)) {
|
|
248
|
+
frontMatterReadings.set(plain, result);
|
|
249
|
+
result = plain;
|
|
48
250
|
}
|
|
49
251
|
}
|
|
50
|
-
return { source, dialect, path, tree:
|
|
252
|
+
return { source, dialect: read, path, tree: result };
|
|
51
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* The `[start, end]` offsets of a node of a parsed document in its `source`:
|
|
256
|
+
* half-open, in UTF-16 code units. A node without a source position, such as
|
|
257
|
+
* one that a plugin's tree extension built without one, is an error.
|
|
258
|
+
*/
|
|
52
259
|
export function range(node) {
|
|
53
260
|
if (node.position?.start.offset === undefined || node.position.end.offset === undefined) {
|
|
54
261
|
throw new Error(`Missing source position for ${node.type}`);
|
|
55
262
|
}
|
|
56
263
|
return [node.position.start.offset, node.position.end.offset];
|
|
57
264
|
}
|
|
265
|
+
/** An alert's or callout's marker, such as `[!NOTE]` or `[!tip]`. */
|
|
266
|
+
const calloutMarker = /^\[![\w-]+\]/;
|
|
267
|
+
/**
|
|
268
|
+
* Whether a paragraph starts with text that `pattern` matches, by default an
|
|
269
|
+
* alert's or callout's marker: in `written`, its source from its start, or in
|
|
270
|
+
* its text as renderers read it, where a backslash escape or a character
|
|
271
|
+
* reference stands for the character it writes. GitHub reads `\[!NOTE]`,
|
|
272
|
+
* `[!NOTE]`, and `[!NOTE]` as alerts too (issue #294).
|
|
273
|
+
*/
|
|
274
|
+
export function startsWithMarker(written, paragraph, pattern = calloutMarker) {
|
|
275
|
+
const first = paragraph.children[0];
|
|
276
|
+
return pattern.test(written) || (first?.type === "text" && pattern.test(first.value));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* A node's text as a reader sees it: the values of its text, code, and other
|
|
280
|
+
* literal nodes, in order. A code span gives its text as `codeSpanText` reads
|
|
281
|
+
* it, on one line, a wikilink its label, or else its target, and an Obsidian
|
|
282
|
+
* comment nothing.
|
|
283
|
+
*/
|
|
58
284
|
export function textContent(node) {
|
|
59
285
|
if (node.type === "wikiLink")
|
|
60
|
-
return node.label ?? node.target
|
|
286
|
+
return node.label ?? node.target;
|
|
61
287
|
if (node.type === "obsidianComment")
|
|
62
288
|
return "";
|
|
289
|
+
if (node.type === "inlineCode")
|
|
290
|
+
return codeSpanText(node);
|
|
63
291
|
if ("value" in node)
|
|
64
292
|
return node.value;
|
|
65
293
|
if ("children" in node)
|
package/dist/syntax/parse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/syntax/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7D,8EAA8E;AAC9E,+EAA+E;AAC/E,iFAAiF;AACjF,iFAAiF;AACjF,4DAA4D;AAC5D,MAAM,OAAO,GAAG,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;IAClD,MAAM,IAAI,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;IAC9B,OAAO,IAAI,CAAC,UAAU,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,cAAc,GAA2D;IAC7E,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,MAAM,UAAU,KAAK,CACnB,MAAc,EACd,OAAgB,EAChB,IAAI,GAAG,aAAa,EACpB,UAAoB,EAAE;IAEtB,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,eAAe,GAAG,CAAC,uBAAuB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACpE,0EAA0E;IAC1E,yEAAyE;IACzE,iDAAiD;IACjD,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,eAAe,CAAC,IAAI,CAAC,GAAG,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3B,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAChC,eAAe,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACzC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;AAChG,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAW;IAC/B,IAAI,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACxF,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAChE,CAAC;AACD,MAAM,UAAU,WAAW,CAAC,IAAW;IACrC,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;IAC7E,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAO,EAAE,CAAC;IAC/C,IAAI,OAAO,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC;IACvC,IAAI,UAAU,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC;AACZ,CAAC","sourcesContent":["import { fromMarkdown } from \"mdast-util-from-markdown\";\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 { Nodes } from \"mdast\";\nimport type { Dialect, Document, Plugin } from \"../core/types.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/**\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\nexport function parse(\n source: string,\n dialect: Dialect,\n path = \"document.md\",\n plugins: Plugin[] = [],\n): Document {\n const extensions = [frontmatter([\"yaml\", \"toml\"])];\n const mdastExtensions = [frontmatterFromMarkdown([\"yaml\", \"toml\"])];\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 (dialect !== \"commonmark\") {\n extensions.push(gfm(), math(mathConditions[dialect]));\n mdastExtensions.push(...gfmTree, mathFromMarkdown());\n }\n if (dialect === \"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 { source, dialect, path, tree: fromMarkdown(source, { extensions, mdastExtensions }) };\n}\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}\nexport function textContent(node: Nodes): string {\n if (node.type === \"wikiLink\") return node.label ?? node.target ?? node.value;\n if (node.type === \"obsidianComment\") return \"\";\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 * 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,19 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The error codes with which a system refuses to let the process read a path:
|
|
3
3
|
* `EACCES` for permission bits and access control lists, `EPERM` for
|
|
4
|
-
* operating-system policy such as a privacy-protected or system folder
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* operating-system policy such as a privacy-protected or system folder and for
|
|
5
|
+
* access control lists on Windows, and `EBUSY` for a file that another program
|
|
6
|
+
* has locked, which Windows allows. Any other read error is a fault, such as a
|
|
7
|
+
* path that vanished during the run, a failing device, or exhausted resources,
|
|
8
|
+
* and is never treated as a refusal.
|
|
7
9
|
*/
|
|
8
10
|
declare const refusals: {
|
|
9
11
|
readonly EACCES: "permission denied";
|
|
10
12
|
readonly EPERM: "operation not permitted";
|
|
13
|
+
readonly EBUSY: "resource busy or locked";
|
|
11
14
|
};
|
|
12
15
|
export type RefusalCode = keyof typeof refusals;
|
|
13
16
|
/** The code of an error that refuses a read, or undefined for any other error. */
|
|
14
17
|
export declare function refusal(error: unknown): RefusalCode | undefined;
|
|
15
18
|
/** A refusal in words, such as `EACCES: permission denied`. */
|
|
16
19
|
export declare function refusalText(code: RefusalCode): string;
|
|
20
|
+
/**
|
|
21
|
+
* A system error in words, without the paths that the system's message names,
|
|
22
|
+
* such as `EROFS: read-only file system`, or undefined for an error that does
|
|
23
|
+
* not come from the system.
|
|
24
|
+
*/
|
|
25
|
+
export declare function systemErrorText(error: unknown): string | undefined;
|
|
17
26
|
/**
|
|
18
27
|
* The error to report for a path that the user named and that cannot be used:
|
|
19
28
|
* what the path was named as, the path as it was given, and whether nothing is
|
package/dist/workspace/access.js
CHANGED
|
@@ -2,20 +2,48 @@ import { stat } from "node:fs/promises";
|
|
|
2
2
|
/**
|
|
3
3
|
* The error codes with which a system refuses to let the process read a path:
|
|
4
4
|
* `EACCES` for permission bits and access control lists, `EPERM` for
|
|
5
|
-
* operating-system policy such as a privacy-protected or system folder
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* operating-system policy such as a privacy-protected or system folder and for
|
|
6
|
+
* access control lists on Windows, and `EBUSY` for a file that another program
|
|
7
|
+
* has locked, which Windows allows. Any other read error is a fault, such as a
|
|
8
|
+
* path that vanished during the run, a failing device, or exhausted resources,
|
|
9
|
+
* and is never treated as a refusal.
|
|
8
10
|
*/
|
|
9
|
-
const refusals = {
|
|
11
|
+
const refusals = {
|
|
12
|
+
EACCES: "permission denied",
|
|
13
|
+
EPERM: "operation not permitted",
|
|
14
|
+
EBUSY: "resource busy or locked",
|
|
15
|
+
};
|
|
10
16
|
/** The code of an error that refuses a read, or undefined for any other error. */
|
|
11
17
|
export function refusal(error) {
|
|
12
18
|
const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
|
|
13
|
-
return code === "
|
|
19
|
+
return typeof code === "string" && Object.hasOwn(refusals, code)
|
|
20
|
+
? code
|
|
21
|
+
: undefined;
|
|
14
22
|
}
|
|
15
23
|
/** A refusal in words, such as `EACCES: permission denied`. */
|
|
16
24
|
export function refusalText(code) {
|
|
17
25
|
return `${code}: ${refusals[code]}`;
|
|
18
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* A system error in words, without the paths that the system's message names,
|
|
29
|
+
* such as `EROFS: read-only file system`, or undefined for an error that does
|
|
30
|
+
* not come from the system.
|
|
31
|
+
*/
|
|
32
|
+
export function systemErrorText(error) {
|
|
33
|
+
if (typeof error !== "object" || error === null || !("code" in error))
|
|
34
|
+
return undefined;
|
|
35
|
+
const { code } = error;
|
|
36
|
+
if (typeof code !== "string")
|
|
37
|
+
return undefined;
|
|
38
|
+
const refused = refusal(error);
|
|
39
|
+
if (refused)
|
|
40
|
+
return refusalText(refused);
|
|
41
|
+
// Node.js writes `CODE: description, syscall 'path'`.
|
|
42
|
+
const message = error instanceof Error ? error.message : "";
|
|
43
|
+
const syscall = "syscall" in error && typeof error.syscall === "string" ? error.syscall : "";
|
|
44
|
+
const end = syscall ? message.indexOf(`, ${syscall}`) : -1;
|
|
45
|
+
return message.startsWith(`${code}: `) && end > 0 ? message.slice(0, end) : code;
|
|
46
|
+
}
|
|
19
47
|
/**
|
|
20
48
|
* The error to report for a path that the user named and that cannot be used:
|
|
21
49
|
* what the path was named as, the path as it was given, and whether nothing is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/workspace/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/workspace/access.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from \"node:fs/promises\";\n\n/**\n * The error codes with which a system refuses to let the process read a path:\n * `EACCES` for permission bits and access control lists, `EPERM` for\n * operating-system policy such as a privacy-protected or system folder and for\n * access control lists on Windows, and `EBUSY` for a file that another program\n * has locked, which Windows allows. Any other read error is a fault, such as a\n * path that vanished during the run, a failing device, or exhausted resources,\n * and is never treated as a refusal.\n */\nconst refusals = {\n EACCES: \"permission denied\",\n EPERM: \"operation not permitted\",\n EBUSY: \"resource busy or locked\",\n} as const;\nexport type RefusalCode = keyof typeof refusals;\n\n/** The code of an error that refuses a read, or undefined for any other error. */\nexport function refusal(error: unknown): RefusalCode | undefined {\n const code =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n return typeof code === \"string\" && Object.hasOwn(refusals, code)\n ? (code as RefusalCode)\n : undefined;\n}\n/** A refusal in words, such as `EACCES: permission denied`. */\nexport function refusalText(code: RefusalCode): string {\n return `${code}: ${refusals[code]}`;\n}\n/**\n * A system error in words, without the paths that the system's message names,\n * such as `EROFS: read-only file system`, or undefined for an error that does\n * not come from the system.\n */\nexport function systemErrorText(error: unknown): string | undefined {\n if (typeof error !== \"object\" || error === null || !(\"code\" in error)) return undefined;\n const { code } = error;\n if (typeof code !== \"string\") return undefined;\n const refused = refusal(error);\n if (refused) return refusalText(refused);\n // Node.js writes `CODE: description, syscall 'path'`.\n const message = error instanceof Error ? error.message : \"\";\n const syscall = \"syscall\" in error && typeof error.syscall === \"string\" ? error.syscall : \"\";\n const end = syscall ? message.indexOf(`, ${syscall}`) : -1;\n return message.startsWith(`${code}: `) && end > 0 ? message.slice(0, end) : code;\n}\n/**\n * The error to report for a path that the user named and that cannot be used:\n * what the path was named as, the path as it was given, and whether nothing is\n * there or the system refuses to let it be read. The system's own message names\n * the internal step that failed, which tells the user nothing. Any other error\n * is a fault and is returned as it is.\n */\nexport function pathError(role: string, given: string, error: unknown): unknown {\n const code =\n typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n // A path through something that is not a directory leads nowhere either.\n if (code === \"ENOENT\" || code === \"ENOTDIR\")\n return new Error(`${role} does not exist: ${given}`, { cause: error });\n const refused = refusal(error);\n return refused\n ? new Error(`${role} cannot be read: ${given} (${refusalText(refused)})`, { cause: error })\n : error;\n}\n/**\n * Whether something exists at `file`. A path through something that is not a\n * directory leads nowhere; a refusal, or any other error, is not an answer.\n */\nexport async function exists(file: string): Promise<boolean> {\n try {\n await stat(file);\n return true;\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") return false;\n throw error;\n }\n}\n"]}
|