mdrefine 0.1.0-beta.3 → 0.1.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 +17 -9
- package/SECURITY.md +4 -3
- package/dist/core/directives.js +5 -2
- package/dist/core/directives.js.map +1 -1
- package/dist/core/engine.js +64 -33
- package/dist/core/engine.js.map +1 -1
- package/dist/rules/dialects.js +4 -4
- package/dist/rules/dialects.js.map +1 -1
- package/dist/rules/links.js +6 -14
- package/dist/rules/links.js.map +1 -1
- package/dist/rules/style.js +112 -36
- package/dist/rules/style.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/index.js +45 -14
- package/dist/workspace/index.js.map +1 -1
- package/docs/configuration.md +3 -0
- package/docs/contributing.md +15 -8
- package/docs/design.md +21 -10
- package/docs/index.md +5 -3
- package/docs/obsidian.md +17 -3
- package/docs/quick-start.md +2 -2
- package/docs/releases/0.1.0-beta.4.md +50 -0
- package/docs/releases/0.1.0-rc.1.md +99 -0
- package/docs/releases.md +52 -23
- package/package.json +3 -4
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
|
@@ -9,10 +9,16 @@ and an extensible rule engine.
|
|
|
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.1.0-
|
|
45
|
+
npm install --save-dev --save-exact mdrefine@0.1.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/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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Ajv } from "ajv";
|
|
2
2
|
import { resolveConfig, setting } from "../config/resolve.js";
|
|
3
|
-
import { parse } from "../syntax/parse.js";
|
|
3
|
+
import { parse, range } from "../syntax/parse.js";
|
|
4
4
|
import { styleRules } from "../rules/style.js";
|
|
5
5
|
import { dialectRules } from "../rules/dialects.js";
|
|
6
6
|
import { structureRules } from "../rules/structure.js";
|
|
@@ -52,24 +52,34 @@ function prepare(options) {
|
|
|
52
52
|
}
|
|
53
53
|
return { config, rules };
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
/** Offsets where each line starts, computed once per source so findings locate in O(log lines). */
|
|
56
|
+
function lineStarts(source) {
|
|
57
|
+
const starts = [0];
|
|
58
|
+
for (let at = source.indexOf("\n"); at >= 0; at = source.indexOf("\n", at + 1))
|
|
59
|
+
starts.push(at + 1);
|
|
60
|
+
return starts;
|
|
61
|
+
}
|
|
62
|
+
function diagnostic(starts, rule, severity, finding) {
|
|
63
|
+
let low = 0;
|
|
64
|
+
let high = starts.length - 1;
|
|
65
|
+
while (low < high) {
|
|
66
|
+
const middle = (low + high + 1) >> 1;
|
|
67
|
+
if (starts[middle] <= finding.start)
|
|
68
|
+
low = middle;
|
|
69
|
+
else
|
|
70
|
+
high = middle - 1;
|
|
71
|
+
}
|
|
72
|
+
return { ...finding, rule, severity, line: low + 1, column: finding.start - starts[low] + 1 };
|
|
64
73
|
}
|
|
65
|
-
|
|
74
|
+
const sortDiagnostics = (a, b) => a.start - b.start || a.rule.localeCompare(b.rule);
|
|
75
|
+
function inspect(document, config, rules, workspace, include = () => true) {
|
|
66
76
|
const result = [];
|
|
67
77
|
const suppressed = suppressions(document);
|
|
78
|
+
const starts = lineStarts(document.source);
|
|
68
79
|
for (const [name, value] of Object.entries(config.rules)) {
|
|
69
80
|
const enabled = setting(value);
|
|
70
81
|
const rule = rules[name];
|
|
71
|
-
if (enabled.severity === "off" ||
|
|
72
|
-
(phase && (rule.kind !== "style" || (rule.phase ?? "inline") !== phase)))
|
|
82
|
+
if (enabled.severity === "off" || !include(rule))
|
|
73
83
|
continue;
|
|
74
84
|
const findings = rule.check({
|
|
75
85
|
document,
|
|
@@ -78,9 +88,9 @@ function inspect(document, config, rules, workspace, phase) {
|
|
|
78
88
|
});
|
|
79
89
|
for (const finding of findings)
|
|
80
90
|
if (!suppressed(name, finding))
|
|
81
|
-
result.push(diagnostic(
|
|
91
|
+
result.push(diagnostic(starts, name, enabled.severity, finding));
|
|
82
92
|
}
|
|
83
|
-
return result.sort(
|
|
93
|
+
return result.sort(sortDiagnostics);
|
|
84
94
|
}
|
|
85
95
|
export function lint(source, options = {}) {
|
|
86
96
|
const { config, rules } = prepare(options);
|
|
@@ -108,10 +118,15 @@ export function applyEdits(source, edits) {
|
|
|
108
118
|
accepted.push(edit);
|
|
109
119
|
previous = edit;
|
|
110
120
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
// Assemble the result in one pass; rebuilding the string per edit is quadratic.
|
|
122
|
+
const parts = [];
|
|
123
|
+
let cursor = 0;
|
|
124
|
+
for (const edit of accepted) {
|
|
125
|
+
parts.push(source.slice(cursor, edit.start), edit.text);
|
|
126
|
+
cursor = edit.end;
|
|
127
|
+
}
|
|
128
|
+
parts.push(source.slice(cursor));
|
|
129
|
+
return parts.join("");
|
|
115
130
|
}
|
|
116
131
|
/** Compare semantic trees, with link identity supplied by the selected renderer's resolver. */
|
|
117
132
|
export function semanticFingerprint(document, workspace) {
|
|
@@ -121,19 +136,30 @@ export function semanticFingerprint(document, workspace) {
|
|
|
121
136
|
? `${resolution.target}#${resolution.fragment ?? ""}`
|
|
122
137
|
: url;
|
|
123
138
|
};
|
|
139
|
+
// Keys are emitted in sorted order so the JSON text is canonical without a replacer.
|
|
124
140
|
function normalize(node, calloutHeader = false) {
|
|
125
141
|
if (node.type === "wikiLink") {
|
|
126
142
|
if (node.embed)
|
|
127
|
-
return { type: "embed", url: canonicalUrl(node.target ?? "", true)
|
|
143
|
+
return { label: node.label, type: "embed", url: canonicalUrl(node.target ?? "", true) };
|
|
128
144
|
return {
|
|
145
|
+
children: [{ type: "text", value: node.label ?? node.target }],
|
|
146
|
+
title: null,
|
|
129
147
|
type: "link",
|
|
130
148
|
url: canonicalUrl(node.target ?? "", true),
|
|
131
|
-
title: null,
|
|
132
|
-
children: [{ type: "text", value: node.label ?? node.target }],
|
|
133
149
|
};
|
|
134
150
|
}
|
|
135
151
|
const result = {};
|
|
136
|
-
|
|
152
|
+
if (calloutHeader && document.dialect === "obsidian" && node.type === "paragraph") {
|
|
153
|
+
const [start, end] = range(node);
|
|
154
|
+
const header = document.source.slice(start, end).split(/\r?\n/, 1)[0];
|
|
155
|
+
if (/^\[![\w-]+\]/.test(header)) {
|
|
156
|
+
// CommonMark merges title and body into one paragraph. Preserve their boundary too.
|
|
157
|
+
const title = header.replace(/^\[!([\w-]+)\]/, (_, marker) => `[!${marker.toLowerCase()}]`);
|
|
158
|
+
result.calloutTitle = normalize(parse(title, document.dialect, document.path).tree);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const key of Object.keys(node).sort()) {
|
|
162
|
+
const value = node[key];
|
|
137
163
|
if (key === "position" || key === "data")
|
|
138
164
|
continue;
|
|
139
165
|
if (key === "children" && "children" in node) {
|
|
@@ -154,29 +180,30 @@ export function semanticFingerprint(document, workspace) {
|
|
|
154
180
|
}
|
|
155
181
|
return result;
|
|
156
182
|
}
|
|
157
|
-
return JSON.stringify(normalize(document.tree)
|
|
158
|
-
if (value && typeof value === "object" && !Array.isArray(value))
|
|
159
|
-
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
|
|
160
|
-
return value;
|
|
161
|
-
});
|
|
183
|
+
return JSON.stringify(normalize(document.tree));
|
|
162
184
|
}
|
|
163
185
|
export function format(source, options = {}) {
|
|
164
186
|
const { config, rules } = prepare(options);
|
|
165
187
|
let output = source;
|
|
166
188
|
let document = parse(source, config.dialect, options.path, options.plugins);
|
|
167
|
-
|
|
189
|
+
// The original fingerprint is only needed once an edit produces a candidate.
|
|
190
|
+
let fingerprint;
|
|
168
191
|
const seen = new Set([source]);
|
|
169
192
|
try {
|
|
170
193
|
for (let pass = 0; pass < 8; pass++) {
|
|
171
194
|
const before = output;
|
|
195
|
+
const findings = [];
|
|
172
196
|
for (const phase of ["inline", "block", "document"]) {
|
|
173
|
-
const
|
|
197
|
+
const diagnostics = inspect(document, config, rules, options.workspace, (rule) => rule.kind === "style" && (rule.phase ?? "inline") === phase);
|
|
198
|
+
findings.push(...diagnostics);
|
|
199
|
+
const edits = diagnostics.flatMap((item) => (item.edit ? [item.edit] : []));
|
|
174
200
|
if (!edits.length)
|
|
175
201
|
continue;
|
|
176
202
|
const candidate = applyEdits(output, edits);
|
|
177
203
|
if (candidate === output)
|
|
178
204
|
continue;
|
|
179
205
|
const candidateDocument = parse(candidate, config.dialect, options.path, options.plugins);
|
|
206
|
+
fingerprint ??= semanticFingerprint(document, options.workspace);
|
|
180
207
|
if (semanticFingerprint(candidateDocument, options.workspace) !== fingerprint)
|
|
181
208
|
throw new Error(`Formatting in the ${phase} phase would change parsed meaning; the document was left unchanged.`);
|
|
182
209
|
output = candidate;
|
|
@@ -184,12 +211,16 @@ export function format(source, options = {}) {
|
|
|
184
211
|
// tree until another phase changes its source, including final diagnostics.
|
|
185
212
|
document = candidateDocument;
|
|
186
213
|
}
|
|
187
|
-
if (output === before)
|
|
214
|
+
if (output === before) {
|
|
215
|
+
// No phase changed the document, so every style finding of this pass
|
|
216
|
+
// describes the final document; only the non-style rules still need to run.
|
|
217
|
+
const remaining = inspect(document, config, rules, options.workspace, (rule) => rule.kind !== "style");
|
|
188
218
|
return {
|
|
189
219
|
output,
|
|
190
220
|
changed: output !== source,
|
|
191
|
-
diagnostics:
|
|
221
|
+
diagnostics: [...findings, ...remaining].sort(sortDiagnostics),
|
|
192
222
|
};
|
|
223
|
+
}
|
|
193
224
|
if (seen.has(output))
|
|
194
225
|
throw new Error("Formatting rules oscillate; the document was left unchanged.");
|
|
195
226
|
seen.add(output);
|
|
@@ -201,7 +232,7 @@ export function format(source, options = {}) {
|
|
|
201
232
|
output: source,
|
|
202
233
|
changed: false,
|
|
203
234
|
diagnostics: [
|
|
204
|
-
diagnostic(
|
|
235
|
+
diagnostic([0], "engine/unsafe-format", "error", {
|
|
205
236
|
start: 0,
|
|
206
237
|
message: error instanceof Error ? error.message : String(error),
|
|
207
238
|
}),
|
package/dist/core/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ajv } from \"ajv\";\nimport type { Nodes } from \"mdast\";\nimport { resolveConfig, setting } from \"../config/resolve.js\";\nimport { parse } from \"../syntax/parse.js\";\nimport { styleRules } from \"../rules/style.js\";\nimport { dialectRules } from \"../rules/dialects.js\";\nimport { structureRules } from \"../rules/structure.js\";\nimport { linkRules } from \"../rules/links.js\";\nimport { suppressions } from \"./directives.js\";\nimport type {\n Diagnostic,\n Document,\n Edit,\n Finding,\n FormatResult,\n Plugin,\n ProcessOptions,\n ResolvedConfig,\n Rule,\n Workspace,\n} from \"./types.js\";\n\nexport const builtInRules: Record<string, Rule> = {\n ...styleRules,\n ...dialectRules,\n ...linkRules,\n ...structureRules,\n};\nexport function ruleRegistry(plugins: Plugin[] = []): Record<string, Rule> {\n const rules = { ...builtInRules };\n const names = new Set<string>();\n for (const plugin of plugins) {\n if (!/^[a-z][a-z\\d-]*$/.test(plugin.name) || names.has(plugin.name))\n throw new Error(`Invalid or duplicate plugin name: ${plugin.name}`);\n names.add(plugin.name);\n for (const [name, rule] of Object.entries(plugin.rules ?? {})) {\n const id = `${plugin.name}/${name}`;\n if (rules[id]) throw new Error(`Duplicate rule: ${id}`);\n rules[id] = rule;\n }\n }\n return rules;\n}\nconst ruleValidator = new Ajv({ allErrors: true });\nconst builtInSchemas = new Set(Object.values(builtInRules).map((rule) => rule.schema));\nfunction prepare(options: ProcessOptions) {\n const config = resolveConfig(options.config, options.path, options.plugins);\n const rules = ruleRegistry(options.plugins);\n let pluginValidator: Ajv | undefined;\n for (const [name, value] of Object.entries(config.rules)) {\n const rule = rules[name];\n if (!rule) throw new Error(`Unknown rule: ${name}`);\n const enabled = setting(value);\n if (rule.schema && enabled.severity !== \"off\") {\n // Plugin schema IDs are scoped to this call; distinct plugin instances\n // may legitimately reuse the same ID with different schemas.\n const validator = builtInSchemas.has(rule.schema)\n ? ruleValidator\n : (pluginValidator ??= new Ajv({ allErrors: true }));\n const validate = validator.compile(rule.schema);\n if (!validate(enabled.options))\n throw new Error(`Invalid options for ${name}: ${validator.errorsText(validate.errors)}`);\n }\n }\n return { config, rules };\n}\nfunction diagnostic(\n source: string,\n rule: string,\n severity: \"warn\" | \"error\",\n finding: Finding,\n): Diagnostic {\n const prefix = source.slice(0, finding.start);\n return {\n ...finding,\n rule,\n severity,\n line: prefix.split(\"\\n\").length,\n column: finding.start - prefix.lastIndexOf(\"\\n\"),\n };\n}\nfunction inspect(\n document: Document,\n config: ResolvedConfig,\n rules: Record<string, Rule>,\n workspace?: Workspace,\n phase?: string,\n): Diagnostic[] {\n const result: Diagnostic[] = [];\n const suppressed = suppressions(document);\n for (const [name, value] of Object.entries(config.rules)) {\n const enabled = setting(value);\n const rule = rules[name]!;\n if (\n enabled.severity === \"off\" ||\n (phase && (rule.kind !== \"style\" || (rule.phase ?? \"inline\") !== phase))\n )\n continue;\n const findings = rule.check({\n document,\n options: enabled.options,\n ...(workspace ? { workspace } : {}),\n });\n for (const finding of findings)\n if (!suppressed(name, finding))\n result.push(diagnostic(document.source, name, enabled.severity, finding));\n }\n return result.sort((a, b) => a.start - b.start || a.rule.localeCompare(b.rule));\n}\nexport function lint(source: string, options: ProcessOptions = {}): Diagnostic[] {\n const { config, rules } = prepare(options);\n const document = parse(source, config.dialect, options.path, options.plugins);\n return inspect(document, config, rules, options.workspace);\n}\nexport function applyEdits(source: string, edits: Edit[]): string {\n const sorted = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);\n let previous: Edit | undefined;\n const accepted: Edit[] = [];\n for (const edit of sorted) {\n if (\n !Number.isInteger(edit.start) ||\n !Number.isInteger(edit.end) ||\n edit.start < 0 ||\n edit.end < edit.start ||\n edit.end > source.length\n )\n throw new Error(\"Rule returned an invalid edit range.\");\n if (\n previous &&\n edit.start === previous.start &&\n edit.end === previous.end &&\n edit.text === previous.text\n )\n continue;\n if (previous && (edit.start < previous.end || edit.start === previous.start))\n throw new Error(\n \"Enabled rules proposed overlapping edits; disable one of the conflicting rules.\",\n );\n accepted.push(edit);\n previous = edit;\n }\n let output = source;\n for (const edit of accepted.reverse())\n output = output.slice(0, edit.start) + edit.text + output.slice(edit.end);\n return output;\n}\n/** Compare semantic trees, with link identity supplied by the selected renderer's resolver. */\nexport function semanticFingerprint(document: Document, workspace?: Workspace): string {\n const canonicalUrl = (url: string, wiki = false) => {\n const resolution = workspace?.resolve(document.path, url, document.dialect, wiki);\n return resolution?.status === \"resolved\"\n ? `${resolution.target}#${resolution.fragment ?? \"\"}`\n : url;\n };\n function normalize(node: Nodes, calloutHeader = false): unknown {\n if (node.type === \"wikiLink\") {\n if (node.embed)\n return { type: \"embed\", url: canonicalUrl(node.target ?? \"\", true), label: node.label };\n return {\n type: \"link\",\n url: canonicalUrl(node.target ?? \"\", true),\n title: null,\n children: [{ type: \"text\", value: node.label ?? node.target }],\n };\n }\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(node)) {\n if (key === \"position\" || key === \"data\") continue;\n if (key === \"children\" && \"children\" in node) {\n result.children = node.children.map((child, index) =>\n normalize(child, index === 0 && (node.type === \"blockquote\" || calloutHeader)),\n );\n } else if (key === \"url\" && typeof value === \"string\") result[key] = canonicalUrl(value);\n else if (key === \"value\" && node.type === \"text\") {\n let text = node.value.replace(/[ \\t\\r\\n]+/g, \" \");\n if (calloutHeader && document.dialect !== \"commonmark\")\n text = text.replace(\n /^\\[!([\\w-]+)\\]/,\n (_, marker: string) => `[!${marker.toLowerCase()}]`,\n );\n result[key] = text;\n } else if (key === \"value\" && node.type === \"inlineCode\")\n result[key] = node.value.replace(/\\r\\n|\\r|\\n/g, \" \");\n else result[key] = value;\n }\n return result;\n }\n return JSON.stringify(normalize(document.tree), (_key, value: unknown) => {\n if (value && typeof value === \"object\" && !Array.isArray(value))\n return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));\n return value;\n });\n}\nexport function format(source: string, options: ProcessOptions = {}): FormatResult {\n const { config, rules } = prepare(options);\n let output = source;\n let document = parse(source, config.dialect, options.path, options.plugins);\n const fingerprint = semanticFingerprint(document, options.workspace);\n const seen = new Set([source]);\n try {\n for (let pass = 0; pass < 8; pass++) {\n const before = output;\n for (const phase of [\"inline\", \"block\", \"document\"]) {\n const edits = inspect(document, config, rules, options.workspace, phase).flatMap((item) =>\n item.edit ? [item.edit] : [],\n );\n if (!edits.length) continue;\n const candidate = applyEdits(output, edits);\n if (candidate === output) continue;\n const candidateDocument = parse(candidate, config.dialect, options.path, options.plugins);\n if (semanticFingerprint(candidateDocument, options.workspace) !== fingerprint)\n throw new Error(\n `Formatting in the ${phase} phase would change parsed meaning; the document was left unchanged.`,\n );\n output = candidate;\n // Rules consume the document without mutating it. Reuse the validated\n // tree until another phase changes its source, including final diagnostics.\n document = candidateDocument;\n }\n if (output === before)\n return {\n output,\n changed: output !== source,\n diagnostics: inspect(document, config, rules, options.workspace),\n };\n if (seen.has(output))\n throw new Error(\"Formatting rules oscillate; the document was left unchanged.\");\n seen.add(output);\n }\n throw new Error(\n \"Formatting did not converge in eight passes; the document was left unchanged.\",\n );\n } catch (error) {\n return {\n output: source,\n changed: false,\n diagnostics: [\n diagnostic(source, \"engine/unsafe-format\", \"error\", {\n start: 0,\n message: error instanceof Error ? error.message : String(error),\n }),\n ],\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ajv } from \"ajv\";\nimport type { Nodes } from \"mdast\";\nimport { resolveConfig, setting } from \"../config/resolve.js\";\nimport { parse, range } from \"../syntax/parse.js\";\nimport { styleRules } from \"../rules/style.js\";\nimport { dialectRules } from \"../rules/dialects.js\";\nimport { structureRules } from \"../rules/structure.js\";\nimport { linkRules } from \"../rules/links.js\";\nimport { suppressions } from \"./directives.js\";\nimport type {\n Diagnostic,\n Document,\n Edit,\n Finding,\n FormatResult,\n Plugin,\n ProcessOptions,\n ResolvedConfig,\n Rule,\n Workspace,\n} from \"./types.js\";\n\nexport const builtInRules: Record<string, Rule> = {\n ...styleRules,\n ...dialectRules,\n ...linkRules,\n ...structureRules,\n};\nexport function ruleRegistry(plugins: Plugin[] = []): Record<string, Rule> {\n const rules = { ...builtInRules };\n const names = new Set<string>();\n for (const plugin of plugins) {\n if (!/^[a-z][a-z\\d-]*$/.test(plugin.name) || names.has(plugin.name))\n throw new Error(`Invalid or duplicate plugin name: ${plugin.name}`);\n names.add(plugin.name);\n for (const [name, rule] of Object.entries(plugin.rules ?? {})) {\n const id = `${plugin.name}/${name}`;\n if (rules[id]) throw new Error(`Duplicate rule: ${id}`);\n rules[id] = rule;\n }\n }\n return rules;\n}\nconst ruleValidator = new Ajv({ allErrors: true });\nconst builtInSchemas = new Set(Object.values(builtInRules).map((rule) => rule.schema));\nfunction prepare(options: ProcessOptions) {\n const config = resolveConfig(options.config, options.path, options.plugins);\n const rules = ruleRegistry(options.plugins);\n let pluginValidator: Ajv | undefined;\n for (const [name, value] of Object.entries(config.rules)) {\n const rule = rules[name];\n if (!rule) throw new Error(`Unknown rule: ${name}`);\n const enabled = setting(value);\n if (rule.schema && enabled.severity !== \"off\") {\n // Plugin schema IDs are scoped to this call; distinct plugin instances\n // may legitimately reuse the same ID with different schemas.\n const validator = builtInSchemas.has(rule.schema)\n ? ruleValidator\n : (pluginValidator ??= new Ajv({ allErrors: true }));\n const validate = validator.compile(rule.schema);\n if (!validate(enabled.options))\n throw new Error(`Invalid options for ${name}: ${validator.errorsText(validate.errors)}`);\n }\n }\n return { config, rules };\n}\n/** Offsets where each line starts, computed once per source so findings locate in O(log lines). */\nfunction lineStarts(source: string): number[] {\n const starts = [0];\n for (let at = source.indexOf(\"\\n\"); at >= 0; at = source.indexOf(\"\\n\", at + 1))\n starts.push(at + 1);\n return starts;\n}\nfunction diagnostic(\n starts: number[],\n rule: string,\n severity: \"warn\" | \"error\",\n finding: Finding,\n): Diagnostic {\n let low = 0;\n let high = starts.length - 1;\n while (low < high) {\n const middle = (low + high + 1) >> 1;\n if (starts[middle]! <= finding.start) low = middle;\n else high = middle - 1;\n }\n return { ...finding, rule, severity, line: low + 1, column: finding.start - starts[low]! + 1 };\n}\nconst sortDiagnostics = (a: Diagnostic, b: Diagnostic) =>\n a.start - b.start || a.rule.localeCompare(b.rule);\nfunction inspect(\n document: Document,\n config: ResolvedConfig,\n rules: Record<string, Rule>,\n workspace?: Workspace,\n include: (rule: Rule) => boolean = () => true,\n): Diagnostic[] {\n const result: Diagnostic[] = [];\n const suppressed = suppressions(document);\n const starts = lineStarts(document.source);\n for (const [name, value] of Object.entries(config.rules)) {\n const enabled = setting(value);\n const rule = rules[name]!;\n if (enabled.severity === \"off\" || !include(rule)) continue;\n const findings = rule.check({\n document,\n options: enabled.options,\n ...(workspace ? { workspace } : {}),\n });\n for (const finding of findings)\n if (!suppressed(name, finding))\n result.push(diagnostic(starts, name, enabled.severity, finding));\n }\n return result.sort(sortDiagnostics);\n}\nexport function lint(source: string, options: ProcessOptions = {}): Diagnostic[] {\n const { config, rules } = prepare(options);\n const document = parse(source, config.dialect, options.path, options.plugins);\n return inspect(document, config, rules, options.workspace);\n}\nexport function applyEdits(source: string, edits: Edit[]): string {\n const sorted = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);\n let previous: Edit | undefined;\n const accepted: Edit[] = [];\n for (const edit of sorted) {\n if (\n !Number.isInteger(edit.start) ||\n !Number.isInteger(edit.end) ||\n edit.start < 0 ||\n edit.end < edit.start ||\n edit.end > source.length\n )\n throw new Error(\"Rule returned an invalid edit range.\");\n if (\n previous &&\n edit.start === previous.start &&\n edit.end === previous.end &&\n edit.text === previous.text\n )\n continue;\n if (previous && (edit.start < previous.end || edit.start === previous.start))\n throw new Error(\n \"Enabled rules proposed overlapping edits; disable one of the conflicting rules.\",\n );\n accepted.push(edit);\n previous = edit;\n }\n // Assemble the result in one pass; rebuilding the string per edit is quadratic.\n const parts: string[] = [];\n let cursor = 0;\n for (const edit of accepted) {\n parts.push(source.slice(cursor, edit.start), edit.text);\n cursor = edit.end;\n }\n parts.push(source.slice(cursor));\n return parts.join(\"\");\n}\n/** Compare semantic trees, with link identity supplied by the selected renderer's resolver. */\nexport function semanticFingerprint(document: Document, workspace?: Workspace): string {\n const canonicalUrl = (url: string, wiki = false) => {\n const resolution = workspace?.resolve(document.path, url, document.dialect, wiki);\n return resolution?.status === \"resolved\"\n ? `${resolution.target}#${resolution.fragment ?? \"\"}`\n : url;\n };\n // Keys are emitted in sorted order so the JSON text is canonical without a replacer.\n function normalize(node: Nodes, calloutHeader = false): unknown {\n if (node.type === \"wikiLink\") {\n if (node.embed)\n return { label: node.label, type: \"embed\", url: canonicalUrl(node.target ?? \"\", true) };\n return {\n children: [{ type: \"text\", value: node.label ?? node.target }],\n title: null,\n type: \"link\",\n url: canonicalUrl(node.target ?? \"\", true),\n };\n }\n const result: Record<string, unknown> = {};\n if (calloutHeader && document.dialect === \"obsidian\" && node.type === \"paragraph\") {\n const [start, end] = range(node);\n const header = document.source.slice(start, end).split(/\\r?\\n/, 1)[0]!;\n if (/^\\[![\\w-]+\\]/.test(header)) {\n // CommonMark merges title and body into one paragraph. Preserve their boundary too.\n const title = header.replace(\n /^\\[!([\\w-]+)\\]/,\n (_, marker: string) => `[!${marker.toLowerCase()}]`,\n );\n result.calloutTitle = normalize(parse(title, document.dialect, document.path).tree);\n }\n }\n for (const key of Object.keys(node).sort()) {\n const value = (node as unknown as Record<string, unknown>)[key];\n if (key === \"position\" || key === \"data\") continue;\n if (key === \"children\" && \"children\" in node) {\n result.children = node.children.map((child, index) =>\n normalize(child, index === 0 && (node.type === \"blockquote\" || calloutHeader)),\n );\n } else if (key === \"url\" && typeof value === \"string\") result[key] = canonicalUrl(value);\n else if (key === \"value\" && node.type === \"text\") {\n let text = node.value.replace(/[ \\t\\r\\n]+/g, \" \");\n if (calloutHeader && document.dialect !== \"commonmark\")\n text = text.replace(\n /^\\[!([\\w-]+)\\]/,\n (_, marker: string) => `[!${marker.toLowerCase()}]`,\n );\n result[key] = text;\n } else if (key === \"value\" && node.type === \"inlineCode\")\n result[key] = node.value.replace(/\\r\\n|\\r|\\n/g, \" \");\n else result[key] = value;\n }\n return result;\n }\n return JSON.stringify(normalize(document.tree));\n}\nexport function format(source: string, options: ProcessOptions = {}): FormatResult {\n const { config, rules } = prepare(options);\n let output = source;\n let document = parse(source, config.dialect, options.path, options.plugins);\n // The original fingerprint is only needed once an edit produces a candidate.\n let fingerprint: string | undefined;\n const seen = new Set([source]);\n try {\n for (let pass = 0; pass < 8; pass++) {\n const before = output;\n const findings: Diagnostic[] = [];\n for (const phase of [\"inline\", \"block\", \"document\"]) {\n const diagnostics = inspect(\n document,\n config,\n rules,\n options.workspace,\n (rule) => rule.kind === \"style\" && (rule.phase ?? \"inline\") === phase,\n );\n findings.push(...diagnostics);\n const edits = diagnostics.flatMap((item) => (item.edit ? [item.edit] : []));\n if (!edits.length) continue;\n const candidate = applyEdits(output, edits);\n if (candidate === output) continue;\n const candidateDocument = parse(candidate, config.dialect, options.path, options.plugins);\n fingerprint ??= semanticFingerprint(document, options.workspace);\n if (semanticFingerprint(candidateDocument, options.workspace) !== fingerprint)\n throw new Error(\n `Formatting in the ${phase} phase would change parsed meaning; the document was left unchanged.`,\n );\n output = candidate;\n // Rules consume the document without mutating it. Reuse the validated\n // tree until another phase changes its source, including final diagnostics.\n document = candidateDocument;\n }\n if (output === before) {\n // No phase changed the document, so every style finding of this pass\n // describes the final document; only the non-style rules still need to run.\n const remaining = inspect(\n document,\n config,\n rules,\n options.workspace,\n (rule) => rule.kind !== \"style\",\n );\n return {\n output,\n changed: output !== source,\n diagnostics: [...findings, ...remaining].sort(sortDiagnostics),\n };\n }\n if (seen.has(output))\n throw new Error(\"Formatting rules oscillate; the document was left unchanged.\");\n seen.add(output);\n }\n throw new Error(\n \"Formatting did not converge in eight passes; the document was left unchanged.\",\n );\n } catch (error) {\n return {\n output: source,\n changed: false,\n diagnostics: [\n diagnostic([0], \"engine/unsafe-format\", \"error\", {\n start: 0,\n message: error instanceof Error ? error.message : String(error),\n }),\n ],\n };\n }\n}\n"]}
|
package/dist/rules/dialects.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { walk } from "../syntax/walk.js";
|
|
2
2
|
import { range } from "../syntax/parse.js";
|
|
3
3
|
import { optionsSchema } from "./style.js";
|
|
4
4
|
function calloutRule(obsidian) {
|
|
@@ -13,7 +13,7 @@ function calloutRule(obsidian) {
|
|
|
13
13
|
if (document.dialect !== (obsidian ? "obsidian" : "github"))
|
|
14
14
|
return [];
|
|
15
15
|
const findings = [];
|
|
16
|
-
|
|
16
|
+
walk(document.tree, "blockquote", (node) => {
|
|
17
17
|
const child = node.children[0];
|
|
18
18
|
if (child?.type !== "paragraph")
|
|
19
19
|
return;
|
|
@@ -48,7 +48,7 @@ export const dialectRules = {
|
|
|
48
48
|
schema: optionsSchema({}),
|
|
49
49
|
check({ document }) {
|
|
50
50
|
const findings = [];
|
|
51
|
-
|
|
51
|
+
walk(document.tree, "listItem", (node) => {
|
|
52
52
|
if (node.checked !== true)
|
|
53
53
|
return;
|
|
54
54
|
const start = range(node)[0];
|
|
@@ -74,7 +74,7 @@ export const dialectRules = {
|
|
|
74
74
|
return [];
|
|
75
75
|
const seen = new Set();
|
|
76
76
|
const findings = [];
|
|
77
|
-
|
|
77
|
+
walk(document.tree, "text", (node) => {
|
|
78
78
|
const match = /(?:^|\s)\^([A-Za-z0-9-]+)\s*$/.exec(node.value);
|
|
79
79
|
if (!match)
|
|
80
80
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialects.js","sourceRoot":"","sources":["../../src/rules/dialects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dialects.js","sourceRoot":"","sources":["../../src/rules/dialects.ts"],"names":[],"mappings":"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","sourcesContent":["import { walk } from \"../syntax/walk.js\";\nimport type { Finding, Rule } from \"../core/types.js\";\nimport { range } from \"../syntax/parse.js\";\nimport { optionsSchema } from \"./style.js\";\n\nfunction calloutRule(obsidian: boolean): Rule {\n return {\n description: obsidian\n ? \"Use lowercase Obsidian callout types.\"\n : \"Use uppercase GitHub alert types.\",\n kind: \"style\",\n phase: \"inline\",\n schema: optionsSchema({}),\n check({ document }) {\n if (document.dialect !== (obsidian ? \"obsidian\" : \"github\")) return [];\n const findings: Finding[] = [];\n walk(document.tree, \"blockquote\", (node) => {\n const child = node.children[0];\n if (child?.type !== \"paragraph\") return;\n const start = range(child)[0];\n const match = /^\\[!([\\w-]+)\\]/.exec(document.source.slice(start));\n if (!match) return;\n const original = match[1]!;\n if (\n !obsidian &&\n ![\"NOTE\", \"TIP\", \"IMPORTANT\", \"WARNING\", \"CAUTION\"].includes(original.toUpperCase())\n )\n return;\n const replacement = obsidian ? original.toLowerCase() : original.toUpperCase();\n if (original !== replacement)\n findings.push({\n start: start + 2,\n end: start + 2 + original.length,\n message: `Use ${replacement} for the callout type.`,\n edit: { start: start + 2, end: start + 2 + original.length, text: replacement },\n });\n });\n return findings;\n },\n };\n}\nexport const dialectRules: Record<string, Rule> = {\n \"github/alert-marker\": calloutRule(false),\n \"obsidian/callout-marker\": calloutRule(true),\n \"github/task-marker\": {\n description: \"Use lowercase x in completed task markers.\",\n kind: \"style\",\n phase: \"inline\",\n schema: optionsSchema({}),\n check({ document }) {\n const findings: Finding[] = [];\n walk(document.tree, \"listItem\", (node) => {\n if (node.checked !== true) return;\n const start = range(node)[0];\n const match = /^(?:[-+*]|\\d+[.)])\\s+\\[X\\]/.exec(document.source.slice(start));\n if (!match) return;\n const offset = start + match[0].length - 2;\n findings.push({\n start: offset,\n message: \"Use lowercase x in task markers.\",\n edit: { start: offset, end: offset + 1, text: \"x\" },\n });\n });\n return findings;\n },\n },\n \"obsidian/block-reference\": {\n description: \"Report duplicate block identifiers.\",\n kind: \"problem\",\n schema: optionsSchema({}),\n check({ document }) {\n if (document.dialect !== \"obsidian\") return [];\n const seen = new Set<string>();\n const findings: Finding[] = [];\n walk(document.tree, \"text\", (node) => {\n const match = /(?:^|\\s)\\^([A-Za-z0-9-]+)\\s*$/.exec(node.value);\n if (!match) return;\n const id = match[1]!;\n if (seen.has(id))\n findings.push({ start: range(node)[0], message: `Duplicate block identifier ^${id}.` });\n seen.add(id);\n });\n return findings;\n },\n },\n \"obsidian/strict-line-breaks\": {\n description: \"Require verified soft-line-break behavior before reflowing an Obsidian document.\",\n kind: \"problem\",\n schema: optionsSchema({}),\n check({ document, workspace }) {\n return document.dialect === \"obsidian\" && workspace?.strictLineBreaks !== true\n ? [\n {\n start: 0,\n message:\n \"Obsidian strictLineBreaks is not verified true; paragraph reflow is disabled. Set it in Obsidian or supply workspace.strictLineBreaks to the library.\",\n },\n ]\n : [];\n },\n },\n};\n"]}
|
package/dist/rules/links.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { walk } from "../syntax/walk.js";
|
|
3
3
|
import { range } from "../syntax/parse.js";
|
|
4
4
|
import { splitDestination } from "../workspace/index.js";
|
|
5
5
|
import { optionsSchema } from "./style.js";
|
|
@@ -62,17 +62,9 @@ export const linkRules = {
|
|
|
62
62
|
schema: optionsSchema({}),
|
|
63
63
|
check({ document, workspace }) {
|
|
64
64
|
const findings = [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
visit(document.tree, (node) => {
|
|
70
|
-
if ((node.type === "linkReference" || node.type === "imageReference") &&
|
|
71
|
-
!definitions.has(node.identifier))
|
|
72
|
-
findings.push({
|
|
73
|
-
start: range(node)[0],
|
|
74
|
-
message: `Missing link definition: ${node.identifier}.`,
|
|
75
|
-
});
|
|
65
|
+
// The parser only produces reference nodes for labels that have a
|
|
66
|
+
// definition; undefined references stay plain text.
|
|
67
|
+
walk(document.tree, (node) => {
|
|
76
68
|
const url = destination(node);
|
|
77
69
|
if (url === undefined || !workspace)
|
|
78
70
|
return;
|
|
@@ -102,7 +94,7 @@ export const linkRules = {
|
|
|
102
94
|
}),
|
|
103
95
|
check({ document, options, workspace }) {
|
|
104
96
|
const findings = [];
|
|
105
|
-
|
|
97
|
+
walk(document.tree, (node) => {
|
|
106
98
|
const url = destination(node);
|
|
107
99
|
if (url === undefined || !workspace)
|
|
108
100
|
return;
|
|
@@ -220,7 +212,7 @@ export const linkRules = {
|
|
|
220
212
|
if (document.dialect !== "obsidian" || !workspace)
|
|
221
213
|
return [];
|
|
222
214
|
const findings = [];
|
|
223
|
-
|
|
215
|
+
walk(document.tree, (node) => {
|
|
224
216
|
const [start, end] = range(node);
|
|
225
217
|
let replacement;
|
|
226
218
|
if (options.style === "markdown" &&
|