mdrefine 0.1.0-beta.4 → 0.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +3 -1
- package/README.md +20 -12
- package/SECURITY.md +4 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/config/resolve.d.ts +5 -1
- package/dist/config/resolve.js +30 -4
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/directives.js +5 -2
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.js +66 -32
- package/dist/core/engine.js.map +1 -1
- package/dist/core/types.d.ts +5 -3
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rules/dialects.js +51 -5
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +24 -15
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/style.d.ts +8 -0
- package/dist/rules/style.js +269 -51
- package/dist/rules/style.js.map +1 -1
- package/dist/syntax/parse.js +14 -1
- package/dist/syntax/parse.js.map +1 -1
- package/dist/syntax/walk.d.ts +8 -0
- package/dist/syntax/walk.js +15 -0
- package/dist/syntax/walk.js.map +1 -0
- package/dist/workspace/heading-attributes.d.ts +87 -0
- package/dist/workspace/heading-attributes.js +392 -0
- package/dist/workspace/heading-attributes.js.map +1 -0
- package/dist/workspace/index.js +424 -37
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/slug.d.ts +34 -0
- package/dist/workspace/slug.js +85 -0
- package/dist/workspace/slug.js.map +1 -0
- package/docs/configuration.md +136 -25
- package/docs/contributing.md +15 -8
- package/docs/design.md +131 -19
- package/docs/index.md +6 -4
- package/docs/obsidian.md +5 -3
- package/docs/plugins.md +7 -4
- package/docs/quick-start.md +6 -4
- package/docs/releases/0.1.0-rc.1.md +99 -0
- package/docs/releases/0.2.0-rc.1.md +141 -0
- package/docs/releases.md +52 -23
- package/examples/forgejo.jsonc +8 -0
- package/examples/gitea.jsonc +8 -0
- package/package.json +13 -13
package/CONTRIBUTING.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Human and agent-authored issues and pull requests are welcome. Read the
|
|
4
4
|
[contribution guide](https://viell-dev.github.io/slop-markdown-tools/contributing.html)
|
|
5
5
|
for defect reports, development, verification, and PR policy. This project is
|
|
6
|
-
agent-maintained, as described in
|
|
6
|
+
entirely agent-authored and agent-maintained, as described in
|
|
7
|
+
[README.md](README.md); no person reads or reviews its code. Write issue and PR
|
|
8
|
+
descriptions for a reader who has not seen the code.
|
|
7
9
|
|
|
8
10
|
Changes to document rendering need a minimal synthetic regression example.
|
|
9
11
|
Remove private vault content and credentials from all reports. Report security
|
package/README.md
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
# mdrefine — Markdown Tools
|
|
2
2
|
|
|
3
|
-
Configurable linting and formatting for CommonMark, GitHub Markdown,
|
|
4
|
-
Obsidian. Separate `lint` and `format` commands share
|
|
5
|
-
and an extensible rule engine.
|
|
3
|
+
Configurable linting and formatting for CommonMark, GitHub Markdown, Forgejo
|
|
4
|
+
(Codeberg), Gitea, and Obsidian. Separate `lint` and `format` commands share
|
|
5
|
+
syntax, style settings, and an extensible rule engine.
|
|
6
6
|
|
|
7
7
|
[npm package](https://www.npmjs.com/package/mdrefine) ·
|
|
8
8
|
[GitHub releases](https://github.com/viell-dev/slop-markdown-tools/releases)
|
|
9
9
|
|
|
10
10
|
## Maintenance
|
|
11
11
|
|
|
12
|
-
This project is
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
This project is entirely agent-authored and agent-maintained. AI agents wrote
|
|
13
|
+
the code, tests, documentation, CI, and release tooling, and agents implement,
|
|
14
|
+
verify, and merge every change. It is **not human-maintained**; do not assume
|
|
15
|
+
that a person has reviewed generated changes. The repository owner does not
|
|
16
|
+
write, read, or review code, does not know the codebase or API, and does not use
|
|
17
|
+
the tool directly. They read issue and PR descriptions to check the project's
|
|
18
|
+
direction, read Markdown documents that agents have processed with the tool, and
|
|
19
|
+
assign agents to tasks. Human and agent-authored issues and pull requests are
|
|
20
|
+
welcome. Reproducible examples, automated checks, and regression tests guide
|
|
21
|
+
maintenance.
|
|
16
22
|
|
|
17
23
|
The project is currently an early prerelease. Report document corruption or
|
|
18
24
|
changed rendering with a minimal input, the configuration, actual output, and
|
|
@@ -32,10 +38,11 @@ current limitations. The Markdown source lives in `docs/`.
|
|
|
32
38
|
## Getting started
|
|
33
39
|
|
|
34
40
|
The CLI requires Node.js 22.12 or newer. The npm package is `mdrefine`; the
|
|
35
|
-
executable is `mdtools`. To install the
|
|
41
|
+
executable is `mdtools`. To install the release candidate in your document
|
|
42
|
+
workspace:
|
|
36
43
|
|
|
37
44
|
```sh
|
|
38
|
-
npm install --save-dev --save-exact mdrefine@0.
|
|
45
|
+
npm install --save-dev --save-exact mdrefine@0.2.0-rc.1
|
|
39
46
|
npx --no-install mdtools --help
|
|
40
47
|
```
|
|
41
48
|
|
|
@@ -115,9 +122,10 @@ remaining limitations in the PR.
|
|
|
115
122
|
|
|
116
123
|
Agent contributors should read
|
|
117
124
|
[AGENTS.md](https://github.com/viell-dev/slop-markdown-tools/blob/main/AGENTS.md)
|
|
118
|
-
for maintenance-specific instructions
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
for maintenance-specific instructions, including the limits of an assigned
|
|
126
|
+
agent's authority. `test:package` packs and installs a temporary consumer
|
|
127
|
+
project, then exercises the installed CLI and library; it can require access to
|
|
128
|
+
the npm registry.
|
|
121
129
|
|
|
122
130
|
## Documentation website
|
|
123
131
|
|
package/SECURITY.md
CHANGED
|
@@ -5,9 +5,10 @@ Report suspected vulnerabilities through
|
|
|
5
5
|
Include the affected version, configuration, and a minimal synthetic example. Do
|
|
6
6
|
not disclose exploitable details or private documents in a public issue.
|
|
7
7
|
|
|
8
|
-
This project is maintained by agents, without a guaranteed response
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
This project is authored and maintained by agents, without a guaranteed response
|
|
9
|
+
time. Reports are handled by an agent assigned by the repository owner; no
|
|
10
|
+
person reads the code. Security fixes target the latest prerelease until a
|
|
11
|
+
stable release exists; older prereleases do not receive separate backports.
|
|
11
12
|
|
|
12
13
|
JavaScript configuration and plugins execute with the caller's permissions. Only
|
|
13
14
|
load trusted code. JSON configuration is data, but its plugin entries can still
|
package/dist/cli/main.js
CHANGED
|
@@ -19,7 +19,7 @@ function common(command) {
|
|
|
19
19
|
return command
|
|
20
20
|
.option("--config <file>", "Explicit JSON, JSONC, or .mjs configuration")
|
|
21
21
|
.option("--root <directory>", "Workspace root (default: configuration directory or cwd)")
|
|
22
|
-
.option("--dialect <dialect>", "commonmark, github, or obsidian")
|
|
22
|
+
.option("--dialect <dialect>", "commonmark, github, forgejo (alias: codeberg), gitea, or obsidian")
|
|
23
23
|
.option("--exclude <path>", "Exclude an exact file or directory (repeatable)", (value, previous) => [...previous, value], [])
|
|
24
24
|
.option("--json", "Machine-readable output")
|
|
25
25
|
.option("--stdin-filepath <file>", "Filename context for stdin")
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { createTwoFilesPatch } from \"diff\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { format, lint, ruleRegistry } from \"../core/engine.js\";\nimport { loadConfig } from \"../config/load.js\";\nimport { resolveConfig } from \"../config/resolve.js\";\nimport { discover, writeAtomic } from \"../workspace/files.js\";\nimport { excludeSelection } from \"../workspace/selection.js\";\nimport { createWorkspace } from \"../workspace/index.js\";\nimport type { Diagnostic, Dialect, ProcessOptions } from \"../core/types.js\";\n\ninterface Flags {\n config?: string;\n root?: string;\n dialect?: Dialect;\n json?: boolean;\n check?: boolean;\n diff?: boolean;\n write?: boolean;\n stdinFilepath?: string;\n maxWarnings?: number;\n exclude?: string[];\n}\nconst manifest = JSON.parse(\n await readFile(fileURLToPath(new URL(\"../../package.json\", import.meta.url)), \"utf8\"),\n) as { version: string };\nconst program = new Command()\n .name(\"mdtools\")\n .description(\"Lint and format Markdown with explicit dialects and configurable rules.\")\n .version(manifest.version);\nfunction common(command: Command): Command {\n return command\n .option(\"--config <file>\", \"Explicit JSON, JSONC, or .mjs configuration\")\n .option(\"--root <directory>\", \"Workspace root (default: configuration directory or cwd)\")\n .option(\"--dialect <dialect>\", \"commonmark, github, or obsidian\")\n .option(\n \"--exclude <path>\",\n \"Exclude an exact file or directory (repeatable)\",\n (value: string, previous: string[]) => [...previous, value],\n [],\n )\n .option(\"--json\", \"Machine-readable output\")\n .option(\"--stdin-filepath <file>\", \"Filename context for stdin\")\n .option(\n \"--max-warnings <count>\",\n \"Fail when warning count exceeds this nonnegative integer\",\n (value: string) => {\n const number = Number(value);\n if (!Number.isInteger(number) || number < 0)\n throw new Error(\"--max-warnings must be a nonnegative integer.\");\n return number;\n },\n );\n}\ninterface Report {\n path: string;\n changed?: boolean;\n diagnostics: Diagnostic[];\n output?: string;\n}\nasync function run(mode: \"lint\" | \"format\", inputs: string[], flags: Flags) {\n if ([flags.check, flags.diff, flags.write].filter(Boolean).length > 1)\n throw new Error(\"Choose only one of --check, --diff, and --write.\");\n if (inputs.includes(\"-\") && flags.exclude?.length)\n throw new Error(\"--exclude cannot be combined with stdin.\");\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n const root = path.resolve(flags.root ?? loaded.root);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const resolved = resolveConfig(loaded.config, \"document.md\", loaded.plugins);\n const stdin = inputs.includes(\"-\");\n if (stdin && (inputs.length !== 1 || flags.write))\n throw new Error(\"stdin must be the only input and cannot be combined with --write.\");\n const set = await discover(root, stdin ? [] : inputs, resolved.ignore, resolved.resolve);\n set.selected = await excludeSelection(set.root, set.selected, flags.exclude ?? []);\n if (stdin) {\n const name = path\n .relative(root, path.resolve(flags.stdinFilepath ?? path.join(root, \"stdin.md\")))\n .split(path.sep)\n .join(\"/\");\n if (name.startsWith(\"../\") || path.isAbsolute(name))\n throw new Error(\"--stdin-filepath must be within the workspace.\");\n let source = \"\";\n process.stdin.setEncoding(\"utf8\");\n for await (const chunk of process.stdin) source += String(chunk);\n set.files[name] = source;\n set.selected = [name];\n }\n const reports: Report[] = [];\n const changes: { file: string; before: string; after: string }[] = [];\n // One index per active dialect; build lazily for mixed documentation workspaces.\n const indexes = new Map<Dialect, ReturnType<typeof createWorkspace>>();\n for (const name of set.selected) {\n const config = resolveConfig(loaded.config, name, loaded.plugins);\n if (!indexes.has(config.dialect))\n indexes.set(\n config.dialect,\n createWorkspace(set.files, {\n dialect: config.dialect,\n directories: set.directories,\n ...(set.strictLineBreaks !== undefined ? { strictLineBreaks: set.strictLineBreaks } : {}),\n }),\n );\n const options: ProcessOptions = {\n path: name,\n config: loaded.config,\n plugins: loaded.plugins,\n workspace: indexes.get(config.dialect)!,\n };\n const value = set.files[name]!;\n const source = typeof value === \"function\" ? value() : value;\n if (source === null) continue;\n if (mode === \"lint\") reports.push({ path: name, diagnostics: lint(source, options) });\n else {\n const result = format(source, options);\n reports.push({\n path: name,\n changed: result.changed,\n diagnostics: result.diagnostics,\n ...(stdin && !flags.check ? { output: result.output } : {}),\n });\n if (result.changed) changes.push({ file: name, before: source, after: result.output });\n }\n }\n const unsafe = reports.some((report) =>\n report.diagnostics.some((item) => item.rule === \"engine/unsafe-format\"),\n );\n if (flags.write && !unsafe)\n for (const change of changes)\n await writeAtomic(path.join(set.root, change.file), change.before, change.after);\n if (flags.json)\n process.stdout.write(\n JSON.stringify(\n {\n version: manifest.version,\n mode,\n files: reports,\n written: flags.write === true && !unsafe,\n },\n null,\n 2,\n ) + \"\\n\",\n );\n else {\n if (stdin && mode === \"format\" && !flags.check && !flags.diff)\n process.stdout.write(reports[0]?.output ?? \"\");\n else if (mode === \"format\" && !flags.check && !flags.write)\n for (const change of changes)\n process.stdout.write(\n createTwoFilesPatch(`a/${change.file}`, `b/${change.file}`, change.before, change.after),\n );\n for (const report of reports)\n for (const item of report.diagnostics)\n process.stderr.write(\n `${report.path}:${item.line}:${item.column}: ${item.severity} ${item.rule}: ${item.message}\\n`,\n );\n if (!stdin)\n process.stderr.write(\n `${set.selected.length} file(s) ${mode === \"lint\" ? \"linted\" : \"processed\"}; ${changes.length} ${flags.write && !unsafe ? \"written\" : \"would change\"}.\\n`,\n );\n }\n const diagnostics = reports.flatMap((report) => report.diagnostics);\n const errors = diagnostics.some((item) => item.severity === \"error\");\n const tooManyWarnings =\n flags.maxWarnings !== undefined &&\n diagnostics.filter((item) => item.severity === \"warn\").length > flags.maxWarnings;\n process.exitCode = unsafe\n ? 2\n : errors || tooManyWarnings || (flags.check && changes.length > 0)\n ? 1\n : 0;\n}\ncommon(\n program.command(\"lint\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n).action((inputs: string[], flags: Flags) => run(\"lint\", inputs, flags));\ncommon(\n program.command(\"format\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n)\n .option(\"--check\", \"Fail if formatting changes would be needed\")\n .option(\"--diff\", \"Preview the complete diff (default for file inputs)\")\n .option(\"--write\", \"Write validated formatting changes\")\n .action((inputs: string[], flags: Flags) => run(\"format\", inputs, flags));\ncommon(program.command(\"config\").description(\"Inspect effective configuration\"))\n .command(\"explain <file>\")\n .action(async (file: string, _flags: unknown, command: Command) => {\n const flags = command.parent!.opts<Flags>();\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const root = path.resolve(flags.root ?? loaded.root);\n process.stdout.write(\n JSON.stringify(\n {\n root,\n file: loaded.file ?? null,\n effective: resolveConfig(\n loaded.config,\n path.relative(root, path.resolve(file)).split(path.sep).join(\"/\"),\n loaded.plugins,\n ),\n },\n null,\n 2,\n ) + \"\\n\",\n );\n });\nprogram\n .command(\"rules\")\n .description(\"List built-in rules\")\n .action(() => {\n for (const [name, rule] of Object.entries(ruleRegistry()))\n process.stdout.write(`${name}\\t${rule.kind}\\t${rule.description}\\n`);\n });\ntry {\n await program.parseAsync();\n} catch (error) {\n process.stderr.write(`mdtools: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 2;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { createTwoFilesPatch } from \"diff\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { format, lint, ruleRegistry } from \"../core/engine.js\";\nimport { loadConfig } from \"../config/load.js\";\nimport { resolveConfig } from \"../config/resolve.js\";\nimport { discover, writeAtomic } from \"../workspace/files.js\";\nimport { excludeSelection } from \"../workspace/selection.js\";\nimport { createWorkspace } from \"../workspace/index.js\";\nimport type { Diagnostic, Dialect, DialectName, ProcessOptions } from \"../core/types.js\";\n\ninterface Flags {\n config?: string;\n root?: string;\n dialect?: DialectName;\n json?: boolean;\n check?: boolean;\n diff?: boolean;\n write?: boolean;\n stdinFilepath?: string;\n maxWarnings?: number;\n exclude?: string[];\n}\nconst manifest = JSON.parse(\n await readFile(fileURLToPath(new URL(\"../../package.json\", import.meta.url)), \"utf8\"),\n) as { version: string };\nconst program = new Command()\n .name(\"mdtools\")\n .description(\"Lint and format Markdown with explicit dialects and configurable rules.\")\n .version(manifest.version);\nfunction common(command: Command): Command {\n return command\n .option(\"--config <file>\", \"Explicit JSON, JSONC, or .mjs configuration\")\n .option(\"--root <directory>\", \"Workspace root (default: configuration directory or cwd)\")\n .option(\n \"--dialect <dialect>\",\n \"commonmark, github, forgejo (alias: codeberg), gitea, or obsidian\",\n )\n .option(\n \"--exclude <path>\",\n \"Exclude an exact file or directory (repeatable)\",\n (value: string, previous: string[]) => [...previous, value],\n [],\n )\n .option(\"--json\", \"Machine-readable output\")\n .option(\"--stdin-filepath <file>\", \"Filename context for stdin\")\n .option(\n \"--max-warnings <count>\",\n \"Fail when warning count exceeds this nonnegative integer\",\n (value: string) => {\n const number = Number(value);\n if (!Number.isInteger(number) || number < 0)\n throw new Error(\"--max-warnings must be a nonnegative integer.\");\n return number;\n },\n );\n}\ninterface Report {\n path: string;\n changed?: boolean;\n diagnostics: Diagnostic[];\n output?: string;\n}\nasync function run(mode: \"lint\" | \"format\", inputs: string[], flags: Flags) {\n if ([flags.check, flags.diff, flags.write].filter(Boolean).length > 1)\n throw new Error(\"Choose only one of --check, --diff, and --write.\");\n if (inputs.includes(\"-\") && flags.exclude?.length)\n throw new Error(\"--exclude cannot be combined with stdin.\");\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n const root = path.resolve(flags.root ?? loaded.root);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const resolved = resolveConfig(loaded.config, \"document.md\", loaded.plugins);\n const stdin = inputs.includes(\"-\");\n if (stdin && (inputs.length !== 1 || flags.write))\n throw new Error(\"stdin must be the only input and cannot be combined with --write.\");\n const set = await discover(root, stdin ? [] : inputs, resolved.ignore, resolved.resolve);\n set.selected = await excludeSelection(set.root, set.selected, flags.exclude ?? []);\n if (stdin) {\n const name = path\n .relative(root, path.resolve(flags.stdinFilepath ?? path.join(root, \"stdin.md\")))\n .split(path.sep)\n .join(\"/\");\n if (name.startsWith(\"../\") || path.isAbsolute(name))\n throw new Error(\"--stdin-filepath must be within the workspace.\");\n let source = \"\";\n process.stdin.setEncoding(\"utf8\");\n for await (const chunk of process.stdin) source += String(chunk);\n set.files[name] = source;\n set.selected = [name];\n }\n const reports: Report[] = [];\n const changes: { file: string; before: string; after: string }[] = [];\n // One index per active dialect; build lazily for mixed documentation workspaces.\n const indexes = new Map<Dialect, ReturnType<typeof createWorkspace>>();\n for (const name of set.selected) {\n const config = resolveConfig(loaded.config, name, loaded.plugins);\n if (!indexes.has(config.dialect))\n indexes.set(\n config.dialect,\n createWorkspace(set.files, {\n dialect: config.dialect,\n directories: set.directories,\n ...(set.strictLineBreaks !== undefined ? { strictLineBreaks: set.strictLineBreaks } : {}),\n }),\n );\n const options: ProcessOptions = {\n path: name,\n config: loaded.config,\n plugins: loaded.plugins,\n workspace: indexes.get(config.dialect)!,\n };\n const value = set.files[name]!;\n const source = typeof value === \"function\" ? value() : value;\n if (source === null) continue;\n if (mode === \"lint\") reports.push({ path: name, diagnostics: lint(source, options) });\n else {\n const result = format(source, options);\n reports.push({\n path: name,\n changed: result.changed,\n diagnostics: result.diagnostics,\n ...(stdin && !flags.check ? { output: result.output } : {}),\n });\n if (result.changed) changes.push({ file: name, before: source, after: result.output });\n }\n }\n const unsafe = reports.some((report) =>\n report.diagnostics.some((item) => item.rule === \"engine/unsafe-format\"),\n );\n if (flags.write && !unsafe)\n for (const change of changes)\n await writeAtomic(path.join(set.root, change.file), change.before, change.after);\n if (flags.json)\n process.stdout.write(\n JSON.stringify(\n {\n version: manifest.version,\n mode,\n files: reports,\n written: flags.write === true && !unsafe,\n },\n null,\n 2,\n ) + \"\\n\",\n );\n else {\n if (stdin && mode === \"format\" && !flags.check && !flags.diff)\n process.stdout.write(reports[0]?.output ?? \"\");\n else if (mode === \"format\" && !flags.check && !flags.write)\n for (const change of changes)\n process.stdout.write(\n createTwoFilesPatch(`a/${change.file}`, `b/${change.file}`, change.before, change.after),\n );\n for (const report of reports)\n for (const item of report.diagnostics)\n process.stderr.write(\n `${report.path}:${item.line}:${item.column}: ${item.severity} ${item.rule}: ${item.message}\\n`,\n );\n if (!stdin)\n process.stderr.write(\n `${set.selected.length} file(s) ${mode === \"lint\" ? \"linted\" : \"processed\"}; ${changes.length} ${flags.write && !unsafe ? \"written\" : \"would change\"}.\\n`,\n );\n }\n const diagnostics = reports.flatMap((report) => report.diagnostics);\n const errors = diagnostics.some((item) => item.severity === \"error\");\n const tooManyWarnings =\n flags.maxWarnings !== undefined &&\n diagnostics.filter((item) => item.severity === \"warn\").length > flags.maxWarnings;\n process.exitCode = unsafe\n ? 2\n : errors || tooManyWarnings || (flags.check && changes.length > 0)\n ? 1\n : 0;\n}\ncommon(\n program.command(\"lint\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n).action((inputs: string[], flags: Flags) => run(\"lint\", inputs, flags));\ncommon(\n program.command(\"format\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n)\n .option(\"--check\", \"Fail if formatting changes would be needed\")\n .option(\"--diff\", \"Preview the complete diff (default for file inputs)\")\n .option(\"--write\", \"Write validated formatting changes\")\n .action((inputs: string[], flags: Flags) => run(\"format\", inputs, flags));\ncommon(program.command(\"config\").description(\"Inspect effective configuration\"))\n .command(\"explain <file>\")\n .action(async (file: string, _flags: unknown, command: Command) => {\n const flags = command.parent!.opts<Flags>();\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const root = path.resolve(flags.root ?? loaded.root);\n process.stdout.write(\n JSON.stringify(\n {\n root,\n file: loaded.file ?? null,\n effective: resolveConfig(\n loaded.config,\n path.relative(root, path.resolve(file)).split(path.sep).join(\"/\"),\n loaded.plugins,\n ),\n },\n null,\n 2,\n ) + \"\\n\",\n );\n });\nprogram\n .command(\"rules\")\n .description(\"List built-in rules\")\n .action(() => {\n for (const [name, rule] of Object.entries(ruleRegistry()))\n process.stdout.write(`${name}\\t${rule.kind}\\t${rule.description}\\n`);\n });\ntry {\n await program.parseAsync();\n} catch (error) {\n process.stderr.write(`mdtools: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 2;\n}\n"]}
|
package/dist/config/resolve.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type { Config, Plugin, ResolvedConfig, RuleSetting } from "../core/types.js";
|
|
1
|
+
import type { Config, Dialect, DialectName, Plugin, ResolvedConfig, RuleSetting } from "../core/types.js";
|
|
2
|
+
/** Alias names that select an existing dialect; Codeberg runs Forgejo's renderer. */
|
|
3
|
+
export declare const dialectAliases: Record<string, Dialect>;
|
|
4
|
+
export declare const dialects: Dialect[];
|
|
5
|
+
export declare function canonicalDialect(name: DialectName): Dialect;
|
|
2
6
|
export declare const presets: Record<string, Config>;
|
|
3
7
|
export declare const configSchema: {
|
|
4
8
|
$schema: string;
|
package/dist/config/resolve.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Ajv } from "ajv";
|
|
2
2
|
import { minimatch } from "minimatch";
|
|
3
|
+
/** Alias names that select an existing dialect; Codeberg runs Forgejo's renderer. */
|
|
4
|
+
export const dialectAliases = { codeberg: "forgejo" };
|
|
5
|
+
export const dialects = ["commonmark", "github", "forgejo", "gitea", "obsidian"];
|
|
6
|
+
export function canonicalDialect(name) {
|
|
7
|
+
return dialectAliases[name] ?? name;
|
|
8
|
+
}
|
|
3
9
|
export const presets = {
|
|
4
10
|
recommended: {
|
|
5
11
|
rules: {
|
|
@@ -14,6 +20,25 @@ export const presets = {
|
|
|
14
20
|
dialect: "github",
|
|
15
21
|
rules: { "github/task-marker": "warn", "github/alert-marker": "warn", "style/table": "warn" },
|
|
16
22
|
},
|
|
23
|
+
forgejo: {
|
|
24
|
+
dialect: "forgejo",
|
|
25
|
+
rules: {
|
|
26
|
+
"github/task-marker": "warn",
|
|
27
|
+
"github/alert-marker": "warn",
|
|
28
|
+
"style/table": "warn",
|
|
29
|
+
"forgejo/heading-id": "error",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
codeberg: { extends: ["forgejo"] },
|
|
33
|
+
gitea: {
|
|
34
|
+
dialect: "gitea",
|
|
35
|
+
rules: {
|
|
36
|
+
"github/task-marker": "warn",
|
|
37
|
+
"github/alert-marker": "warn",
|
|
38
|
+
"style/table": "warn",
|
|
39
|
+
"forgejo/heading-id": "error",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
17
42
|
obsidian: {
|
|
18
43
|
dialect: "obsidian",
|
|
19
44
|
rules: {
|
|
@@ -26,6 +51,7 @@ export const presets = {
|
|
|
26
51
|
},
|
|
27
52
|
};
|
|
28
53
|
const severity = { enum: ["off", "warn", "error"] };
|
|
54
|
+
const dialect = { enum: [...dialects, ...Object.keys(dialectAliases)] };
|
|
29
55
|
const rules = {
|
|
30
56
|
type: "object",
|
|
31
57
|
additionalProperties: {
|
|
@@ -47,7 +73,7 @@ export const configSchema = {
|
|
|
47
73
|
properties: { gitIgnored: { type: "boolean" }, nestedRepositories: { type: "boolean" } },
|
|
48
74
|
},
|
|
49
75
|
extends: { type: "array", items: { type: "string" } },
|
|
50
|
-
dialect
|
|
76
|
+
dialect,
|
|
51
77
|
rules,
|
|
52
78
|
ignore: { type: "array", items: { type: "string" } },
|
|
53
79
|
plugins: { type: "array", items: { type: "string" } },
|
|
@@ -59,7 +85,7 @@ export const configSchema = {
|
|
|
59
85
|
additionalProperties: false,
|
|
60
86
|
properties: {
|
|
61
87
|
files: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
62
|
-
dialect
|
|
88
|
+
dialect,
|
|
63
89
|
rules,
|
|
64
90
|
},
|
|
65
91
|
},
|
|
@@ -95,7 +121,7 @@ export function resolveConfig(config = {}, path = "document.md", plugins = []) {
|
|
|
95
121
|
merge(preset, [...chain, name]);
|
|
96
122
|
}
|
|
97
123
|
if (part.dialect)
|
|
98
|
-
result.dialect = part.dialect;
|
|
124
|
+
result.dialect = canonicalDialect(part.dialect);
|
|
99
125
|
Object.assign(result.rules, part.rules);
|
|
100
126
|
Object.assign(result.resolve, part.resolve);
|
|
101
127
|
result.ignore.push(...(part.ignore ?? []));
|
|
@@ -107,7 +133,7 @@ export function resolveConfig(config = {}, path = "document.md", plugins = []) {
|
|
|
107
133
|
for (const override of overrides) {
|
|
108
134
|
if (override.files.some((pattern) => minimatch(path.replaceAll("\\", "/"), pattern, { dot: true }))) {
|
|
109
135
|
if (override.dialect)
|
|
110
|
-
result.dialect = override.dialect;
|
|
136
|
+
result.dialect = canonicalDialect(override.dialect);
|
|
111
137
|
Object.assign(result.rules, override.rules);
|
|
112
138
|
}
|
|
113
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/config/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ajv } from \"ajv\";\nimport { minimatch } from \"minimatch\";\nimport type { Config, Plugin, ResolvedConfig, RuleSetting } from \"../core/types.js\";\n\nexport const presets: Record<string, Config> = {\n recommended: {\n rules: {\n \"style/wrap\": [\"warn\", { width: 80 }],\n \"style/emphasis\": [\"warn\", { marker: \"_\" }],\n \"style/strong\": [\"warn\", { marker: \"*\" }],\n \"style/final-newline\": \"warn\",\n \"links/valid\": \"error\",\n },\n },\n github: {\n dialect: \"github\",\n rules: { \"github/task-marker\": \"warn\", \"github/alert-marker\": \"warn\", \"style/table\": \"warn\" },\n },\n obsidian: {\n dialect: \"obsidian\",\n rules: {\n \"obsidian/callout-marker\": \"warn\",\n \"obsidian/block-reference\": \"error\",\n \"obsidian/strict-line-breaks\": \"warn\",\n \"github/task-marker\": \"warn\",\n \"style/table\": \"warn\",\n },\n },\n};\nconst severity = { enum: [\"off\", \"warn\", \"error\"] };\nconst rules = {\n type: \"object\",\n additionalProperties: {\n anyOf: [\n severity,\n { type: \"array\", items: [severity, { type: \"object\" }], minItems: 2, maxItems: 2 },\n ],\n },\n};\nexport const configSchema = {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n type: \"object\",\n additionalProperties: false,\n properties: {\n $schema: { type: \"string\" },\n resolve: {\n type: \"object\",\n additionalProperties: false,\n properties: { gitIgnored: { type: \"boolean\" }, nestedRepositories: { type: \"boolean\" } },\n },\n extends: { type: \"array\", items: { type: \"string\" } },\n dialect: { enum: [\"commonmark\", \"github\", \"obsidian\"] },\n rules,\n ignore: { type: \"array\", items: { type: \"string\" } },\n plugins: { type: \"array\", items: { type: \"string\" } },\n overrides: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"files\"],\n additionalProperties: false,\n properties: {\n files: { type: \"array\", items: { type: \"string\" }, minItems: 1 },\n dialect: { enum: [\"commonmark\", \"github\", \"obsidian\"] },\n rules,\n },\n },\n },\n },\n};\nconst validate = new Ajv({ allErrors: true }).compile(configSchema);\nexport function validateConfig(value: unknown): asserts value is Config {\n if (!validate(value))\n throw new Error(`Invalid configuration: ${new Ajv().errorsText(validate.errors)}`);\n}\nexport function setting(value: RuleSetting): {\n severity: \"off\" | \"warn\" | \"error\";\n options: Record<string, unknown>;\n} {\n return Array.isArray(value)\n ? { severity: value[0], options: value[1] }\n : { severity: value, options: {} };\n}\nexport function resolveConfig(\n config: Config = {},\n path = \"document.md\",\n plugins: Plugin[] = [],\n): ResolvedConfig {\n validateConfig(config);\n const available = { ...presets };\n for (const plugin of plugins)\n for (const [name, preset] of Object.entries(plugin.presets ?? {}))\n available[`${plugin.name}/${name}`] = preset;\n const result: ResolvedConfig = { dialect: \"commonmark\", rules: {}, ignore: [], resolve: {} };\n const overrides: NonNullable<Config[\"overrides\"]> = [];\n function merge(part: Config, chain: string[]) {\n validateConfig(part);\n for (const name of part.extends ?? []) {\n if (chain.includes(name))\n throw new Error(`Circular preset: ${[...chain, name].join(\" -> \")}`);\n const preset = available[name];\n if (!preset) throw new Error(`Unknown preset: ${name}`);\n merge(preset, [...chain, name]);\n }\n if (part.dialect) result.dialect = part.dialect;\n Object.assign(result.rules, part.rules);\n Object.assign(result.resolve, part.resolve);\n result.ignore.push(...(part.ignore ?? []));\n overrides.push(...(part.overrides ?? []));\n }\n if (config.extends === undefined) merge(presets.recommended!, []);\n merge(config, []);\n for (const override of overrides) {\n if (\n override.files.some((pattern) =>\n minimatch(path.replaceAll(\"\\\\\", \"/\"), pattern, { dot: true }),\n )\n ) {\n if (override.dialect) result.dialect = override.dialect;\n Object.assign(result.rules, override.rules);\n }\n }\n return result;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/config/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ajv } from \"ajv\";\nimport { minimatch } from \"minimatch\";\nimport type {\n Config,\n Dialect,\n DialectName,\n Plugin,\n ResolvedConfig,\n RuleSetting,\n} from \"../core/types.js\";\n\n/** Alias names that select an existing dialect; Codeberg runs Forgejo's renderer. */\nexport const dialectAliases: Record<string, Dialect> = { codeberg: \"forgejo\" };\nexport const dialects: Dialect[] = [\"commonmark\", \"github\", \"forgejo\", \"gitea\", \"obsidian\"];\nexport function canonicalDialect(name: DialectName): Dialect {\n return dialectAliases[name] ?? (name as Dialect);\n}\n\nexport const presets: Record<string, Config> = {\n recommended: {\n rules: {\n \"style/wrap\": [\"warn\", { width: 80 }],\n \"style/emphasis\": [\"warn\", { marker: \"_\" }],\n \"style/strong\": [\"warn\", { marker: \"*\" }],\n \"style/final-newline\": \"warn\",\n \"links/valid\": \"error\",\n },\n },\n github: {\n dialect: \"github\",\n rules: { \"github/task-marker\": \"warn\", \"github/alert-marker\": \"warn\", \"style/table\": \"warn\" },\n },\n forgejo: {\n dialect: \"forgejo\",\n rules: {\n \"github/task-marker\": \"warn\",\n \"github/alert-marker\": \"warn\",\n \"style/table\": \"warn\",\n \"forgejo/heading-id\": \"error\",\n },\n },\n codeberg: { extends: [\"forgejo\"] },\n gitea: {\n dialect: \"gitea\",\n rules: {\n \"github/task-marker\": \"warn\",\n \"github/alert-marker\": \"warn\",\n \"style/table\": \"warn\",\n \"forgejo/heading-id\": \"error\",\n },\n },\n obsidian: {\n dialect: \"obsidian\",\n rules: {\n \"obsidian/callout-marker\": \"warn\",\n \"obsidian/block-reference\": \"error\",\n \"obsidian/strict-line-breaks\": \"warn\",\n \"github/task-marker\": \"warn\",\n \"style/table\": \"warn\",\n },\n },\n};\nconst severity = { enum: [\"off\", \"warn\", \"error\"] };\nconst dialect = { enum: [...dialects, ...Object.keys(dialectAliases)] };\nconst rules = {\n type: \"object\",\n additionalProperties: {\n anyOf: [\n severity,\n { type: \"array\", items: [severity, { type: \"object\" }], minItems: 2, maxItems: 2 },\n ],\n },\n};\nexport const configSchema = {\n $schema: \"http://json-schema.org/draft-07/schema#\",\n type: \"object\",\n additionalProperties: false,\n properties: {\n $schema: { type: \"string\" },\n resolve: {\n type: \"object\",\n additionalProperties: false,\n properties: { gitIgnored: { type: \"boolean\" }, nestedRepositories: { type: \"boolean\" } },\n },\n extends: { type: \"array\", items: { type: \"string\" } },\n dialect,\n rules,\n ignore: { type: \"array\", items: { type: \"string\" } },\n plugins: { type: \"array\", items: { type: \"string\" } },\n overrides: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"files\"],\n additionalProperties: false,\n properties: {\n files: { type: \"array\", items: { type: \"string\" }, minItems: 1 },\n dialect,\n rules,\n },\n },\n },\n },\n};\nconst validate = new Ajv({ allErrors: true }).compile(configSchema);\nexport function validateConfig(value: unknown): asserts value is Config {\n if (!validate(value))\n throw new Error(`Invalid configuration: ${new Ajv().errorsText(validate.errors)}`);\n}\nexport function setting(value: RuleSetting): {\n severity: \"off\" | \"warn\" | \"error\";\n options: Record<string, unknown>;\n} {\n return Array.isArray(value)\n ? { severity: value[0], options: value[1] }\n : { severity: value, options: {} };\n}\nexport function resolveConfig(\n config: Config = {},\n path = \"document.md\",\n plugins: Plugin[] = [],\n): ResolvedConfig {\n validateConfig(config);\n const available = { ...presets };\n for (const plugin of plugins)\n for (const [name, preset] of Object.entries(plugin.presets ?? {}))\n available[`${plugin.name}/${name}`] = preset;\n const result: ResolvedConfig = { dialect: \"commonmark\", rules: {}, ignore: [], resolve: {} };\n const overrides: NonNullable<Config[\"overrides\"]> = [];\n function merge(part: Config, chain: string[]) {\n validateConfig(part);\n for (const name of part.extends ?? []) {\n if (chain.includes(name))\n throw new Error(`Circular preset: ${[...chain, name].join(\" -> \")}`);\n const preset = available[name];\n if (!preset) throw new Error(`Unknown preset: ${name}`);\n merge(preset, [...chain, name]);\n }\n if (part.dialect) result.dialect = canonicalDialect(part.dialect);\n Object.assign(result.rules, part.rules);\n Object.assign(result.resolve, part.resolve);\n result.ignore.push(...(part.ignore ?? []));\n overrides.push(...(part.overrides ?? []));\n }\n if (config.extends === undefined) merge(presets.recommended!, []);\n merge(config, []);\n for (const override of overrides) {\n if (\n override.files.some((pattern) =>\n minimatch(path.replaceAll(\"\\\\\", \"/\"), pattern, { dot: true }),\n )\n ) {\n if (override.dialect) result.dialect = canonicalDialect(override.dialect);\n Object.assign(result.rules, override.rules);\n }\n }\n return result;\n}\n"]}
|
package/dist/core/directives.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { walk } from "../syntax/walk.js";
|
|
2
2
|
import { range } from "../syntax/parse.js";
|
|
3
3
|
/** Directives are recognized only in parsed HTML comments, never in code examples. */
|
|
4
4
|
export function suppressions(document) {
|
|
5
|
+
// Directives can only appear in HTML comments containing this prefix.
|
|
6
|
+
if (!document.source.includes("mdtools-"))
|
|
7
|
+
return () => false;
|
|
5
8
|
const intervals = [];
|
|
6
9
|
const active = new Map();
|
|
7
|
-
|
|
10
|
+
walk(document.tree, "html", (node) => {
|
|
8
11
|
const match = /^<!--\s*mdtools-(disable-next-line|disable|enable)(?:\s+([^]*?))?\s*-->$/.exec(node.value.trim());
|
|
9
12
|
if (!match)
|
|
10
13
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directives.js","sourceRoot":"","sources":["../../src/core/directives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"directives.js","sourceRoot":"","sources":["../../src/core/directives.ts"],"names":[],"mappings":"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","sourcesContent":["import { walk } from \"../syntax/walk.js\";\nimport type { Document, Finding } from \"./types.js\";\nimport { range } from \"../syntax/parse.js\";\n\n/** Directives are recognized only in parsed HTML comments, never in code examples. */\nexport function suppressions(document: Document): (rule: string, finding: Finding) => boolean {\n // Directives can only appear in HTML comments containing this prefix.\n if (!document.source.includes(\"mdtools-\")) return () => false;\n const intervals: { start: number; end: number; rules: Set<string> }[] = [];\n const active = new Map<string, number>();\n walk(document.tree, \"html\", (node) => {\n const match = /^<!--\\s*mdtools-(disable-next-line|disable|enable)(?:\\s+([^]*?))?\\s*-->$/.exec(\n node.value.trim(),\n );\n if (!match) return;\n const names = match[2]\n ?.trim()\n .split(/[\\s,]+/)\n .filter(Boolean) ?? [\"*\"];\n const rules = new Set(names.length ? names : [\"*\"]);\n const [start, end] = range(node);\n if (match[1] === \"disable-next-line\") {\n const next = document.source.indexOf(\"\\n\", end);\n if (next < 0) return;\n const last = document.source.indexOf(\"\\n\", next + 1);\n intervals.push({ start: next + 1, end: last < 0 ? document.source.length : last, rules });\n } else if (match[1] === \"disable\") {\n for (const rule of rules) if (!active.has(rule)) active.set(rule, end);\n } else {\n for (const rule of rules.has(\"*\") ? active.keys() : rules) {\n const from = active.get(rule);\n if (from !== undefined) intervals.push({ start: from, end: start, rules: new Set([rule]) });\n active.delete(rule);\n }\n }\n });\n for (const [rule, start] of active)\n intervals.push({ start, end: document.source.length, rules: new Set([rule]) });\n return (rule, finding) =>\n intervals.some((interval) => {\n const start = finding.edit?.start ?? finding.start;\n const end = finding.edit?.end ?? finding.end ?? finding.start;\n return (\n (interval.rules.has(\"*\") || interval.rules.has(rule)) &&\n start <= interval.end &&\n end >= interval.start\n );\n });\n}\n"]}
|
package/dist/core/engine.js
CHANGED
|
@@ -5,6 +5,7 @@ import { styleRules } from "../rules/style.js";
|
|
|
5
5
|
import { dialectRules } from "../rules/dialects.js";
|
|
6
6
|
import { structureRules } from "../rules/structure.js";
|
|
7
7
|
import { linkRules } from "../rules/links.js";
|
|
8
|
+
import { headingAnchorSource, headingAttributeSource } from "../workspace/heading-attributes.js";
|
|
8
9
|
import { suppressions } from "./directives.js";
|
|
9
10
|
export const builtInRules = {
|
|
10
11
|
...styleRules,
|
|
@@ -52,24 +53,34 @@ function prepare(options) {
|
|
|
52
53
|
}
|
|
53
54
|
return { config, rules };
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
/** Offsets where each line starts, computed once per source so findings locate in O(log lines). */
|
|
57
|
+
function lineStarts(source) {
|
|
58
|
+
const starts = [0];
|
|
59
|
+
for (let at = source.indexOf("\n"); at >= 0; at = source.indexOf("\n", at + 1))
|
|
60
|
+
starts.push(at + 1);
|
|
61
|
+
return starts;
|
|
62
|
+
}
|
|
63
|
+
function diagnostic(starts, rule, severity, finding) {
|
|
64
|
+
let low = 0;
|
|
65
|
+
let high = starts.length - 1;
|
|
66
|
+
while (low < high) {
|
|
67
|
+
const middle = (low + high + 1) >> 1;
|
|
68
|
+
if (starts[middle] <= finding.start)
|
|
69
|
+
low = middle;
|
|
70
|
+
else
|
|
71
|
+
high = middle - 1;
|
|
72
|
+
}
|
|
73
|
+
return { ...finding, rule, severity, line: low + 1, column: finding.start - starts[low] + 1 };
|
|
64
74
|
}
|
|
65
|
-
|
|
75
|
+
const sortDiagnostics = (a, b) => a.start - b.start || a.rule.localeCompare(b.rule);
|
|
76
|
+
function inspect(document, config, rules, workspace, include = () => true) {
|
|
66
77
|
const result = [];
|
|
67
78
|
const suppressed = suppressions(document);
|
|
79
|
+
const starts = lineStarts(document.source);
|
|
68
80
|
for (const [name, value] of Object.entries(config.rules)) {
|
|
69
81
|
const enabled = setting(value);
|
|
70
82
|
const rule = rules[name];
|
|
71
|
-
if (enabled.severity === "off" ||
|
|
72
|
-
(phase && (rule.kind !== "style" || (rule.phase ?? "inline") !== phase)))
|
|
83
|
+
if (enabled.severity === "off" || !include(rule))
|
|
73
84
|
continue;
|
|
74
85
|
const findings = rule.check({
|
|
75
86
|
document,
|
|
@@ -78,9 +89,9 @@ function inspect(document, config, rules, workspace, phase) {
|
|
|
78
89
|
});
|
|
79
90
|
for (const finding of findings)
|
|
80
91
|
if (!suppressed(name, finding))
|
|
81
|
-
result.push(diagnostic(
|
|
92
|
+
result.push(diagnostic(starts, name, enabled.severity, finding));
|
|
82
93
|
}
|
|
83
|
-
return result.sort(
|
|
94
|
+
return result.sort(sortDiagnostics);
|
|
84
95
|
}
|
|
85
96
|
export function lint(source, options = {}) {
|
|
86
97
|
const { config, rules } = prepare(options);
|
|
@@ -108,10 +119,15 @@ export function applyEdits(source, edits) {
|
|
|
108
119
|
accepted.push(edit);
|
|
109
120
|
previous = edit;
|
|
110
121
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
122
|
+
// Assemble the result in one pass; rebuilding the string per edit is quadratic.
|
|
123
|
+
const parts = [];
|
|
124
|
+
let cursor = 0;
|
|
125
|
+
for (const edit of accepted) {
|
|
126
|
+
parts.push(source.slice(cursor, edit.start), edit.text);
|
|
127
|
+
cursor = edit.end;
|
|
128
|
+
}
|
|
129
|
+
parts.push(source.slice(cursor));
|
|
130
|
+
return parts.join("");
|
|
115
131
|
}
|
|
116
132
|
/** Compare semantic trees, with link identity supplied by the selected renderer's resolver. */
|
|
117
133
|
export function semanticFingerprint(document, workspace) {
|
|
@@ -121,18 +137,30 @@ export function semanticFingerprint(document, workspace) {
|
|
|
121
137
|
? `${resolution.target}#${resolution.fragment ?? ""}`
|
|
122
138
|
: url;
|
|
123
139
|
};
|
|
140
|
+
// Keys are emitted in sorted order so the JSON text is canonical without a replacer.
|
|
124
141
|
function normalize(node, calloutHeader = false) {
|
|
125
142
|
if (node.type === "wikiLink") {
|
|
126
143
|
if (node.embed)
|
|
127
|
-
return { type: "embed", url: canonicalUrl(node.target ?? "", true)
|
|
144
|
+
return { label: node.label, type: "embed", url: canonicalUrl(node.target ?? "", true) };
|
|
128
145
|
return {
|
|
146
|
+
children: [{ type: "text", value: node.label ?? node.target }],
|
|
147
|
+
title: null,
|
|
129
148
|
type: "link",
|
|
130
149
|
url: canonicalUrl(node.target ?? "", true),
|
|
131
|
-
title: null,
|
|
132
|
-
children: [{ type: "text", value: node.label ?? node.target }],
|
|
133
150
|
};
|
|
134
151
|
}
|
|
135
152
|
const result = {};
|
|
153
|
+
// Forgejo and Gitea read a heading's trailing `{...}` block as attributes,
|
|
154
|
+
// character for character, where the tree holds ordinary inline content,
|
|
155
|
+
// and generate the anchor from the heading's last line as written.
|
|
156
|
+
if (node.type === "heading") {
|
|
157
|
+
const attributes = headingAttributeSource(document, node);
|
|
158
|
+
if (attributes !== undefined)
|
|
159
|
+
result.attributes = attributes;
|
|
160
|
+
const anchors = headingAnchorSource(document, node);
|
|
161
|
+
if (anchors !== undefined)
|
|
162
|
+
result.anchors = anchors;
|
|
163
|
+
}
|
|
136
164
|
if (calloutHeader && document.dialect === "obsidian" && node.type === "paragraph") {
|
|
137
165
|
const [start, end] = range(node);
|
|
138
166
|
const header = document.source.slice(start, end).split(/\r?\n/, 1)[0];
|
|
@@ -142,7 +170,8 @@ export function semanticFingerprint(document, workspace) {
|
|
|
142
170
|
result.calloutTitle = normalize(parse(title, document.dialect, document.path).tree);
|
|
143
171
|
}
|
|
144
172
|
}
|
|
145
|
-
for (const
|
|
173
|
+
for (const key of Object.keys(node).sort()) {
|
|
174
|
+
const value = node[key];
|
|
146
175
|
if (key === "position" || key === "data")
|
|
147
176
|
continue;
|
|
148
177
|
if (key === "children" && "children" in node) {
|
|
@@ -163,29 +192,30 @@ export function semanticFingerprint(document, workspace) {
|
|
|
163
192
|
}
|
|
164
193
|
return result;
|
|
165
194
|
}
|
|
166
|
-
return JSON.stringify(normalize(document.tree)
|
|
167
|
-
if (value && typeof value === "object" && !Array.isArray(value))
|
|
168
|
-
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
|
|
169
|
-
return value;
|
|
170
|
-
});
|
|
195
|
+
return JSON.stringify(normalize(document.tree));
|
|
171
196
|
}
|
|
172
197
|
export function format(source, options = {}) {
|
|
173
198
|
const { config, rules } = prepare(options);
|
|
174
199
|
let output = source;
|
|
175
200
|
let document = parse(source, config.dialect, options.path, options.plugins);
|
|
176
|
-
|
|
201
|
+
// The original fingerprint is only needed once an edit produces a candidate.
|
|
202
|
+
let fingerprint;
|
|
177
203
|
const seen = new Set([source]);
|
|
178
204
|
try {
|
|
179
205
|
for (let pass = 0; pass < 8; pass++) {
|
|
180
206
|
const before = output;
|
|
207
|
+
const findings = [];
|
|
181
208
|
for (const phase of ["inline", "block", "document"]) {
|
|
182
|
-
const
|
|
209
|
+
const diagnostics = inspect(document, config, rules, options.workspace, (rule) => rule.kind === "style" && (rule.phase ?? "inline") === phase);
|
|
210
|
+
findings.push(...diagnostics);
|
|
211
|
+
const edits = diagnostics.flatMap((item) => (item.edit ? [item.edit] : []));
|
|
183
212
|
if (!edits.length)
|
|
184
213
|
continue;
|
|
185
214
|
const candidate = applyEdits(output, edits);
|
|
186
215
|
if (candidate === output)
|
|
187
216
|
continue;
|
|
188
217
|
const candidateDocument = parse(candidate, config.dialect, options.path, options.plugins);
|
|
218
|
+
fingerprint ??= semanticFingerprint(document, options.workspace);
|
|
189
219
|
if (semanticFingerprint(candidateDocument, options.workspace) !== fingerprint)
|
|
190
220
|
throw new Error(`Formatting in the ${phase} phase would change parsed meaning; the document was left unchanged.`);
|
|
191
221
|
output = candidate;
|
|
@@ -193,12 +223,16 @@ export function format(source, options = {}) {
|
|
|
193
223
|
// tree until another phase changes its source, including final diagnostics.
|
|
194
224
|
document = candidateDocument;
|
|
195
225
|
}
|
|
196
|
-
if (output === before)
|
|
226
|
+
if (output === before) {
|
|
227
|
+
// No phase changed the document, so every style finding of this pass
|
|
228
|
+
// describes the final document; only the non-style rules still need to run.
|
|
229
|
+
const remaining = inspect(document, config, rules, options.workspace, (rule) => rule.kind !== "style");
|
|
197
230
|
return {
|
|
198
231
|
output,
|
|
199
232
|
changed: output !== source,
|
|
200
|
-
diagnostics:
|
|
233
|
+
diagnostics: [...findings, ...remaining].sort(sortDiagnostics),
|
|
201
234
|
};
|
|
235
|
+
}
|
|
202
236
|
if (seen.has(output))
|
|
203
237
|
throw new Error("Formatting rules oscillate; the document was left unchanged.");
|
|
204
238
|
seen.add(output);
|
|
@@ -210,7 +244,7 @@ export function format(source, options = {}) {
|
|
|
210
244
|
output: source,
|
|
211
245
|
changed: false,
|
|
212
246
|
diagnostics: [
|
|
213
|
-
diagnostic(
|
|
247
|
+
diagnostic([0], "engine/unsafe-format", "error", {
|
|
214
248
|
start: 0,
|
|
215
249
|
message: error instanceof Error ? error.message : String(error),
|
|
216
250
|
}),
|