@hawkeyexl/manni 4.3.1 → 4.4.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/README.md +9 -1
- package/dist/batch-GFNLTGOH.js +36 -0
- package/dist/build-COYIQ25L.js +31 -0
- package/dist/calibrate-MDBLJKEE.js +31 -0
- package/dist/capture-LRML2PKV.js +32 -0
- package/dist/check-ASPIHA6B.js +40 -0
- package/dist/check-ASPIHA6B.js.map +1 -0
- package/dist/check-EDLBYL5N.js +27 -0
- package/dist/check-EDLBYL5N.js.map +1 -0
- package/dist/check-HYNGXYJV.js +18 -0
- package/dist/check-HYNGXYJV.js.map +1 -0
- package/dist/chunk-2B7ZNTPV.js +230 -0
- package/dist/chunk-2B7ZNTPV.js.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-37LP5HMZ.js +1042 -0
- package/dist/chunk-37LP5HMZ.js.map +1 -0
- package/dist/chunk-3VK37OR2.js +2957 -0
- package/dist/chunk-3VK37OR2.js.map +1 -0
- package/dist/chunk-4IUC2MNL.js +2165 -0
- package/dist/chunk-4IUC2MNL.js.map +1 -0
- package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
- package/dist/chunk-4TSWU2SU.js.map +1 -0
- package/dist/chunk-57NEQRVO.js +10921 -0
- package/dist/chunk-57NEQRVO.js.map +1 -0
- package/dist/chunk-6JSSEWBG.js +6737 -0
- package/dist/chunk-6JSSEWBG.js.map +1 -0
- package/dist/chunk-6OYEI2VV.js +88 -0
- package/dist/chunk-6OYEI2VV.js.map +1 -0
- package/dist/chunk-77AINBYM.js +19 -0
- package/dist/chunk-77AINBYM.js.map +1 -0
- package/dist/chunk-7UKO3ASZ.js +54 -0
- package/dist/chunk-7UKO3ASZ.js.map +1 -0
- package/dist/chunk-A5ULMVHQ.js +164 -0
- package/dist/chunk-A5ULMVHQ.js.map +1 -0
- package/dist/chunk-C4SQM22E.js +299 -0
- package/dist/chunk-C4SQM22E.js.map +1 -0
- package/dist/chunk-CK7SKVHR.js +1042 -0
- package/dist/chunk-CK7SKVHR.js.map +1 -0
- package/dist/{chunk-G5CM2VGQ.js → chunk-DXNZS72W.js} +8 -94
- package/dist/chunk-DXNZS72W.js.map +1 -0
- package/dist/chunk-EPH54CAY.js +374 -0
- package/dist/chunk-EPH54CAY.js.map +1 -0
- package/dist/{chunk-KJR7OVDX.js → chunk-FBEQCK47.js} +56 -137
- package/dist/chunk-FBEQCK47.js.map +1 -0
- package/dist/{chunk-IOYXPBCL.js → chunk-G3T47CFV.js} +1 -7
- package/dist/{chunk-IOYXPBCL.js.map → chunk-G3T47CFV.js.map} +1 -1
- package/dist/chunk-GCQRPUSB.js +1287 -0
- package/dist/chunk-GCQRPUSB.js.map +1 -0
- package/dist/chunk-GLP5PSXB.js +72 -0
- package/dist/chunk-GLP5PSXB.js.map +1 -0
- package/dist/chunk-I2AH3CJJ.js +1192 -0
- package/dist/chunk-I2AH3CJJ.js.map +1 -0
- package/dist/chunk-IDV6HJCB.js +388 -0
- package/dist/chunk-IDV6HJCB.js.map +1 -0
- package/dist/chunk-IKEWBMCN.js +98 -0
- package/dist/chunk-IKEWBMCN.js.map +1 -0
- package/dist/chunk-JCDXE76J.js +52 -0
- package/dist/chunk-JCDXE76J.js.map +1 -0
- package/dist/chunk-JXLTXMJ7.js +455 -0
- package/dist/chunk-JXLTXMJ7.js.map +1 -0
- package/dist/chunk-LJ2RDH3F.js +49 -0
- package/dist/chunk-LJ2RDH3F.js.map +1 -0
- package/dist/chunk-M2JCK5F6.js +805 -0
- package/dist/chunk-M2JCK5F6.js.map +1 -0
- package/dist/chunk-M3W7J6MU.js +4398 -0
- package/dist/chunk-M3W7J6MU.js.map +1 -0
- package/dist/chunk-MMTSSTG7.js +23 -0
- package/dist/chunk-MMTSSTG7.js.map +1 -0
- package/dist/chunk-MNH7QO4Z.js +15 -0
- package/dist/chunk-MNH7QO4Z.js.map +1 -0
- package/dist/chunk-MWJLZMBL.js +934 -0
- package/dist/chunk-MWJLZMBL.js.map +1 -0
- package/dist/chunk-MYMBCTDE.js +387 -0
- package/dist/chunk-MYMBCTDE.js.map +1 -0
- package/dist/chunk-NCPWXHDO.js +683 -0
- package/dist/chunk-NCPWXHDO.js.map +1 -0
- package/dist/chunk-NLXTXE5Y.js +1661 -0
- package/dist/chunk-NLXTXE5Y.js.map +1 -0
- package/dist/{chunk-CFDUZRM2.js → chunk-NXV7WE2D.js} +32 -240
- package/dist/chunk-NXV7WE2D.js.map +1 -0
- package/dist/chunk-OWBRDNRZ.js +16 -0
- package/dist/chunk-OWBRDNRZ.js.map +1 -0
- package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
- package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
- package/dist/chunk-PCE7QHXJ.js +68 -0
- package/dist/chunk-PCE7QHXJ.js.map +1 -0
- package/dist/chunk-PIKOVN6W.js +1651 -0
- package/dist/chunk-PIKOVN6W.js.map +1 -0
- package/dist/chunk-PNPQTHBT.js +17 -0
- package/dist/chunk-PNPQTHBT.js.map +1 -0
- package/dist/chunk-PU6DFJMY.js +1975 -0
- package/dist/chunk-PU6DFJMY.js.map +1 -0
- package/dist/chunk-Q35YIDBV.js +1202 -0
- package/dist/chunk-Q35YIDBV.js.map +1 -0
- package/dist/{chunk-5HNHEUD5.js → chunk-QQUKGCAJ.js} +3 -3
- package/dist/chunk-QRWGPQKO.js +198 -0
- package/dist/chunk-QRWGPQKO.js.map +1 -0
- package/dist/{chunk-UBMMXS2Y.js → chunk-QZ6LRSNV.js} +69 -48
- package/dist/{chunk-UBMMXS2Y.js.map → chunk-QZ6LRSNV.js.map} +1 -1
- package/dist/chunk-RL5XWQOL.js +3872 -0
- package/dist/chunk-RL5XWQOL.js.map +1 -0
- package/dist/chunk-RNVYGWIC.js +30 -0
- package/dist/chunk-RNVYGWIC.js.map +1 -0
- package/dist/chunk-SE4JN5XN.js +19 -0
- package/dist/chunk-SE4JN5XN.js.map +1 -0
- package/dist/chunk-SKSDRUUK.js +622 -0
- package/dist/chunk-SKSDRUUK.js.map +1 -0
- package/dist/chunk-TIUTIOFB.js +25 -0
- package/dist/chunk-TIUTIOFB.js.map +1 -0
- package/dist/chunk-TK3FC4RF.js +3090 -0
- package/dist/chunk-TK3FC4RF.js.map +1 -0
- package/dist/chunk-TU2WUBVZ.js +2 -0
- package/dist/chunk-TU2WUBVZ.js.map +1 -0
- package/dist/chunk-TZOYZK2E.js +98 -0
- package/dist/chunk-TZOYZK2E.js.map +1 -0
- package/dist/chunk-UJCVAWP7.js +178 -0
- package/dist/chunk-UJCVAWP7.js.map +1 -0
- package/dist/chunk-UKZNLVJW.js +17 -0
- package/dist/chunk-UKZNLVJW.js.map +1 -0
- package/dist/{chunk-HAPTXWU2.js → chunk-UQISLDAV.js} +4 -4
- package/dist/chunk-WB3QM6PP.js +5440 -0
- package/dist/chunk-WB3QM6PP.js.map +1 -0
- package/dist/chunk-YB4IBW3M.js +96 -0
- package/dist/chunk-YB4IBW3M.js.map +1 -0
- package/dist/chunk-YEUYKR62.js +72 -0
- package/dist/chunk-YEUYKR62.js.map +1 -0
- package/dist/chunk-YJOGUXGP.js +634 -0
- package/dist/chunk-YJOGUXGP.js.map +1 -0
- package/dist/chunk-YNHZHSSM.js +41 -0
- package/dist/chunk-YNHZHSSM.js.map +1 -0
- package/dist/chunk-YTURO7SI.js +979 -0
- package/dist/chunk-YTURO7SI.js.map +1 -0
- package/dist/chunk-ZXRAE55H.js +31 -0
- package/dist/chunk-ZXRAE55H.js.map +1 -0
- package/dist/cli-3BIBYHFK.js +473 -0
- package/dist/cli-3BIBYHFK.js.map +1 -0
- package/dist/cli-673OTRGE.js +28 -0
- package/dist/cli-673OTRGE.js.map +1 -0
- package/dist/cli-6HIDIFVT.js +1203 -0
- package/dist/cli-6HIDIFVT.js.map +1 -0
- package/dist/cli-6UB5N6CU.js +232 -0
- package/dist/cli-6UB5N6CU.js.map +1 -0
- package/dist/cli-LFP5ZGYS.js +346 -0
- package/dist/cli-LFP5ZGYS.js.map +1 -0
- package/dist/cli-MN7PYMBI.js +552 -0
- package/dist/cli-MN7PYMBI.js.map +1 -0
- package/dist/cli-PYPX6O6E.js +739 -0
- package/dist/cli-PYPX6O6E.js.map +1 -0
- package/dist/cli-RPM5PELX.js +626 -0
- package/dist/cli-RPM5PELX.js.map +1 -0
- package/dist/cli-VI7VHWNZ.js +423 -0
- package/dist/cli-VI7VHWNZ.js.map +1 -0
- package/dist/cli.d.ts +12 -2
- package/dist/cli.js +109 -7661
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +15 -3
- package/dist/docmeta.js.map +1 -1
- package/dist/github-6QMYOHC3.js +27 -0
- package/dist/github-6QMYOHC3.js.map +1 -0
- package/dist/github-SXPGP5DB.js +32 -0
- package/dist/github-SXPGP5DB.js.map +1 -0
- package/dist/index.d.ts +65 -3
- package/dist/index.js +407 -337
- package/dist/index.js.map +1 -1
- package/dist/json-7WV7KL6H.js +17 -0
- package/dist/json-7WV7KL6H.js.map +1 -0
- package/dist/json-SY7KLNC7.js +35 -0
- package/dist/json-SY7KLNC7.js.map +1 -0
- package/dist/lint-ASMJLLPA.js +28 -0
- package/dist/lint-ASMJLLPA.js.map +1 -0
- package/dist/list-KZNVTNVC.js +31 -0
- package/dist/list-KZNVTNVC.js.map +1 -0
- package/dist/list-Q57YTFUQ.js +29 -0
- package/dist/list-Q57YTFUQ.js.map +1 -0
- package/dist/page-KSGV5RD4.js +41 -0
- package/dist/page-KSGV5RD4.js.map +1 -0
- package/dist/picker-3XF36MJF.js +14 -0
- package/dist/picker-3XF36MJF.js.map +1 -0
- package/dist/pretty-A5EGTN35.js +37 -0
- package/dist/pretty-A5EGTN35.js.map +1 -0
- package/dist/pretty-CXSDXNAS.js +43 -0
- package/dist/pretty-CXSDXNAS.js.map +1 -0
- package/dist/reporters-HUY7UXFS.js +42 -0
- package/dist/reporters-HUY7UXFS.js.map +1 -0
- package/dist/reporters-K2VUKSAK.js +71 -0
- package/dist/reporters-K2VUKSAK.js.map +1 -0
- package/dist/reporters-M6LJ2IYU.js +21 -0
- package/dist/reporters-M6LJ2IYU.js.map +1 -0
- package/dist/run-3DYJNRIG.js +33 -0
- package/dist/run-3DYJNRIG.js.map +1 -0
- package/dist/term-BIQUPMOR.js +1592 -0
- package/dist/term-BIQUPMOR.js.map +1 -0
- package/dist/types-Y6TXHM7M.js +10 -0
- package/dist/types-Y6TXHM7M.js.map +1 -0
- package/dist/validate-53RKM2XG.js +16 -0
- package/dist/validate-53RKM2XG.js.map +1 -0
- package/package.json +1 -1
- package/plugin/manni/.claude-plugin/plugin.json +9 -0
- package/plugin/manni/hooks/hooks.json +69 -0
- package/plugin/manni/hooks/manni.mjs +157 -0
- package/plugin/manni/skills/check/SKILL.md +20 -0
- package/plugin/manni/skills/evals/SKILL.md +27 -0
- package/plugin/manni/skills/fix/SKILL.md +56 -0
- package/plugin/manni/skills/setup/SKILL.md +41 -0
- package/dist/batch-A5HZKYIW.js +0 -18
- package/dist/calibrate-S5K6QHUY.js +0 -13
- package/dist/capture-OZEZDABQ.js +0 -15
- package/dist/chunk-3R23C5DT.js.map +0 -1
- package/dist/chunk-CFDUZRM2.js.map +0 -1
- package/dist/chunk-G5CM2VGQ.js.map +0 -1
- package/dist/chunk-KJR7OVDX.js.map +0 -1
- package/dist/chunk-V4KUVE47.js +0 -27170
- package/dist/chunk-V4KUVE47.js.map +0 -1
- package/dist/chunk-ZQBTK3DO.js +0 -26518
- package/dist/chunk-ZQBTK3DO.js.map +0 -1
- package/dist/picker-6HRMETE7.js +0 -12
- package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
- package/plugin/tracevals/hooks/hooks.json +0 -17
- /package/dist/{batch-A5HZKYIW.js.map → batch-GFNLTGOH.js.map} +0 -0
- /package/dist/{calibrate-S5K6QHUY.js.map → build-COYIQ25L.js.map} +0 -0
- /package/dist/{capture-OZEZDABQ.js.map → calibrate-MDBLJKEE.js.map} +0 -0
- /package/dist/{picker-6HRMETE7.js.map → capture-LRML2PKV.js.map} +0 -0
- /package/dist/{chunk-5HNHEUD5.js.map → chunk-QQUKGCAJ.js.map} +0 -0
- /package/dist/{chunk-HAPTXWU2.js.map → chunk-UQISLDAV.js.map} +0 -0
|
@@ -0,0 +1,1042 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ToolError
|
|
4
|
+
} from "./chunk-G3T47CFV.js";
|
|
5
|
+
|
|
6
|
+
// src/lint/types.ts
|
|
7
|
+
var LintError = class extends ToolError {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "LintError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/lint/rules/paragraphs.ts
|
|
15
|
+
function checkParagraphsIn(content, rule, ctx) {
|
|
16
|
+
if (!rule) return [];
|
|
17
|
+
const paragraphs = paragraphsOf(content);
|
|
18
|
+
const findings = checkCount(
|
|
19
|
+
paragraphs.length,
|
|
20
|
+
rule,
|
|
21
|
+
"paragraph",
|
|
22
|
+
"paragraphs_count_error",
|
|
23
|
+
ctx
|
|
24
|
+
);
|
|
25
|
+
const pattern = rule.pattern;
|
|
26
|
+
if (pattern) {
|
|
27
|
+
const regex = compilePattern(pattern);
|
|
28
|
+
paragraphs.forEach((paragraph, index) => {
|
|
29
|
+
if (!regex.test(paragraph.text)) {
|
|
30
|
+
findings.push({
|
|
31
|
+
type: "paragraphs_pattern_error",
|
|
32
|
+
heading: ctx.heading,
|
|
33
|
+
message: `Paragraph ${index + 1} does not match /${pattern}/`,
|
|
34
|
+
position: paragraph.position,
|
|
35
|
+
severity: "error"
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return findings;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/lint/rules/code-blocks.ts
|
|
44
|
+
function checkCodeBlocksIn(content, rule, ctx) {
|
|
45
|
+
if (!rule) return [];
|
|
46
|
+
const codeBlocks = codeBlocksOf(content);
|
|
47
|
+
const counted = rule.fenceInfo ? codeBlocks.filter((block) => matchesFenceInfo(block, rule.fenceInfo)) : codeBlocks;
|
|
48
|
+
const findings = checkCount(
|
|
49
|
+
counted.length,
|
|
50
|
+
rule,
|
|
51
|
+
"code block",
|
|
52
|
+
"code_blocks_count_error",
|
|
53
|
+
ctx
|
|
54
|
+
);
|
|
55
|
+
if (rule.language) {
|
|
56
|
+
findings.push(...checkLanguages(codeBlocks, rule.language, ctx));
|
|
57
|
+
}
|
|
58
|
+
return findings;
|
|
59
|
+
}
|
|
60
|
+
function matchesFenceInfo(block, pattern) {
|
|
61
|
+
return compilePattern(pattern).test(block.fenceInfo ?? "");
|
|
62
|
+
}
|
|
63
|
+
function checkLanguages(codeBlocks, language, ctx) {
|
|
64
|
+
const languages = Array.isArray(language) ? language : [language];
|
|
65
|
+
const only = languages[0];
|
|
66
|
+
if (languages.length === 0 || only === void 0) return [];
|
|
67
|
+
const label = languages.length === 1 ? `"${only}"` : `one of ${languages.map((lang) => `"${lang}"`).join(", ")}`;
|
|
68
|
+
const findings = [];
|
|
69
|
+
codeBlocks.forEach((block, index) => {
|
|
70
|
+
if (block.language !== void 0 && languages.includes(block.language)) return;
|
|
71
|
+
const found = block.language ? `but found "${block.language}"` : "but found no language";
|
|
72
|
+
findings.push({
|
|
73
|
+
type: "code_blocks_language_error",
|
|
74
|
+
heading: ctx.heading,
|
|
75
|
+
message: `Expected code block ${index + 1} to declare ${label}, ${found}`,
|
|
76
|
+
position: block.position,
|
|
77
|
+
severity: "error"
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
return findings;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/lint/rules/tables.ts
|
|
84
|
+
function checkTablesIn(content, rule, ctx) {
|
|
85
|
+
if (!rule) return [];
|
|
86
|
+
const tables = tablesOf(content);
|
|
87
|
+
const findings = checkCount(tables.length, rule, "table", "tables_count_error", ctx);
|
|
88
|
+
if (rule.columns) {
|
|
89
|
+
for (const table of tables) {
|
|
90
|
+
const actual = headerCells(table);
|
|
91
|
+
if (!sameColumns(actual, rule.columns)) {
|
|
92
|
+
findings.push({
|
|
93
|
+
type: "tables_columns_error",
|
|
94
|
+
heading: ctx.heading,
|
|
95
|
+
message: `Expected table columns ${quoteList(rule.columns)}, but found ${quoteList(actual)}`,
|
|
96
|
+
position: table.position,
|
|
97
|
+
severity: "error"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return findings;
|
|
103
|
+
}
|
|
104
|
+
function headerCells(table) {
|
|
105
|
+
const header = table.children.find((row) => row.header);
|
|
106
|
+
return header ? header.children.map((cell) => cell.text) : [];
|
|
107
|
+
}
|
|
108
|
+
function sameColumns(actual, expected) {
|
|
109
|
+
return actual.length === expected.length && actual.every((cell, index) => cell === expected[index]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/lint/rules/admonitions.ts
|
|
113
|
+
function checkAdmonitionsIn(content, rule, ctx) {
|
|
114
|
+
if (!rule) return [];
|
|
115
|
+
const admonitions = admonitionsOf(content);
|
|
116
|
+
const findings = checkCount(
|
|
117
|
+
admonitions.length,
|
|
118
|
+
rule,
|
|
119
|
+
"admonition",
|
|
120
|
+
"admonitions_count_error",
|
|
121
|
+
ctx
|
|
122
|
+
);
|
|
123
|
+
if (rule.variant) {
|
|
124
|
+
findings.push(...checkVariant(admonitions, rule.variant, ctx));
|
|
125
|
+
}
|
|
126
|
+
return findings;
|
|
127
|
+
}
|
|
128
|
+
function checkVariant(admonitions, variant, ctx) {
|
|
129
|
+
const findings = [];
|
|
130
|
+
for (const admonition of admonitions) {
|
|
131
|
+
if (admonition.variant !== variant) {
|
|
132
|
+
const found = admonition.variant ? `but found a ${admonition.variant}` : "but found one with no type";
|
|
133
|
+
findings.push({
|
|
134
|
+
type: "admonitions_variant_error",
|
|
135
|
+
heading: ctx.heading,
|
|
136
|
+
message: `Expected a ${variant} admonition, ${found}`,
|
|
137
|
+
position: admonition.position,
|
|
138
|
+
severity: "error"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return findings;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/lint/rules/images.ts
|
|
146
|
+
function checkImagesIn(content, rule, ctx) {
|
|
147
|
+
if (!rule) return [];
|
|
148
|
+
const matching = imagesOf(content).filter((image) => matches(image, rule));
|
|
149
|
+
return checkCount(matching.length, rule, "image", "images_count_error", ctx);
|
|
150
|
+
}
|
|
151
|
+
function matches(image, rule) {
|
|
152
|
+
if (rule.url !== void 0 && image.url !== rule.url) return false;
|
|
153
|
+
if (rule.alt !== void 0 && image.alt !== rule.alt) return false;
|
|
154
|
+
if (rule.attributes && Object.keys(rule.attributes).length > 0) return false;
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/lint/rules/blockquotes.ts
|
|
159
|
+
function checkBlockquotesIn(content, rule, ctx) {
|
|
160
|
+
if (!rule) return [];
|
|
161
|
+
return checkCount(blockquotesOf(content).length, rule, "blockquote", "blockquotes_count_error", ctx);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/lint/rules/definition-lists.ts
|
|
165
|
+
function checkDefinitionListsIn(content, rule, ctx) {
|
|
166
|
+
if (!rule) return [];
|
|
167
|
+
return checkCount(
|
|
168
|
+
definitionListsOf(content).length,
|
|
169
|
+
rule,
|
|
170
|
+
"definition list",
|
|
171
|
+
"definition_lists_count_error",
|
|
172
|
+
ctx
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/lint/rules/sequence.ts
|
|
177
|
+
function groupRuns(content) {
|
|
178
|
+
const runs = [];
|
|
179
|
+
for (const node of content) {
|
|
180
|
+
const current = runs[runs.length - 1];
|
|
181
|
+
if (current && current.kind === node.kind) {
|
|
182
|
+
current.nodes.push(node);
|
|
183
|
+
current.position = {
|
|
184
|
+
start: current.position.start,
|
|
185
|
+
end: node.position.end
|
|
186
|
+
};
|
|
187
|
+
} else {
|
|
188
|
+
runs.push({
|
|
189
|
+
kind: node.kind,
|
|
190
|
+
nodes: [node],
|
|
191
|
+
position: { start: node.position.start, end: node.position.end }
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return runs;
|
|
196
|
+
}
|
|
197
|
+
var KIND_OF_BLOCK = {
|
|
198
|
+
paragraphs: "paragraph",
|
|
199
|
+
codeBlocks: "codeBlock",
|
|
200
|
+
lists: "list",
|
|
201
|
+
tables: "table",
|
|
202
|
+
admonitions: "admonition",
|
|
203
|
+
images: "image",
|
|
204
|
+
blockquotes: "blockquote",
|
|
205
|
+
definitionLists: "definitionList",
|
|
206
|
+
elements: "element"
|
|
207
|
+
};
|
|
208
|
+
function sequenceLabel(kind) {
|
|
209
|
+
switch (kind) {
|
|
210
|
+
case "paragraph":
|
|
211
|
+
return "paragraph";
|
|
212
|
+
case "codeBlock":
|
|
213
|
+
return "code";
|
|
214
|
+
case "list":
|
|
215
|
+
return "list";
|
|
216
|
+
case "table":
|
|
217
|
+
return "table";
|
|
218
|
+
case "admonition":
|
|
219
|
+
return "admonition";
|
|
220
|
+
case "image":
|
|
221
|
+
return "image";
|
|
222
|
+
case "blockquote":
|
|
223
|
+
return "blockquote";
|
|
224
|
+
case "definitionList":
|
|
225
|
+
return "definition list";
|
|
226
|
+
case "element":
|
|
227
|
+
return "element";
|
|
228
|
+
default:
|
|
229
|
+
return kind;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function keyOf(entry) {
|
|
233
|
+
for (const kind of BLOCK_KINDS) {
|
|
234
|
+
if (entry[kind] !== void 0) return kind;
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
function checkSequence(section, rule) {
|
|
239
|
+
return checkSequenceIn(section.children, rule, sectionContext(section));
|
|
240
|
+
}
|
|
241
|
+
function checkSequenceIn(content, rule, ctx) {
|
|
242
|
+
if (!rule) return [];
|
|
243
|
+
const runs = groupRuns(content);
|
|
244
|
+
const expectedKinds = rule.map((entry) => {
|
|
245
|
+
const key = keyOf(entry);
|
|
246
|
+
return key ? KIND_OF_BLOCK[key] : null;
|
|
247
|
+
});
|
|
248
|
+
const actualKinds = runs.map((run) => run.kind);
|
|
249
|
+
const matches2 = expectedKinds.length === actualKinds.length && expectedKinds.every((kind, index) => kind === actualKinds[index]);
|
|
250
|
+
if (!matches2) {
|
|
251
|
+
const expected = expectedKinds.map((kind) => kind ? sequenceLabel(kind) : "?");
|
|
252
|
+
const found = actualKinds.length > 0 ? actualKinds.map(sequenceLabel) : ["nothing"];
|
|
253
|
+
return [
|
|
254
|
+
{
|
|
255
|
+
type: "content_order_error",
|
|
256
|
+
heading: ctx.heading,
|
|
257
|
+
message: `Expected ${expected.join(" then ")}, but found ${found.join(" then ")}`,
|
|
258
|
+
position: ctx.position,
|
|
259
|
+
severity: "error"
|
|
260
|
+
}
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
const findings = [];
|
|
264
|
+
runs.forEach((run, index) => {
|
|
265
|
+
const entry = rule[index];
|
|
266
|
+
if (!entry) return;
|
|
267
|
+
const runCtx = { heading: ctx.heading, position: run.position };
|
|
268
|
+
findings.push(...checkContainsIn(run.nodes, entry, runCtx));
|
|
269
|
+
});
|
|
270
|
+
return findings;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/lint/rules/elements.ts
|
|
274
|
+
function checkElementsIn(content, rule, ctx) {
|
|
275
|
+
if (!rule) return [];
|
|
276
|
+
const tags = rule.tag === void 0 ? void 0 : Array.isArray(rule.tag) ? rule.tag : [rule.tag];
|
|
277
|
+
const matching = tags ? elementsOf(content).filter((element) => tags.includes(element.name)) : elementsOf(content);
|
|
278
|
+
const noun = tags ? `${tags.map((tag) => `"${tag}"`).join(" or ")} element` : "element";
|
|
279
|
+
const findings = checkCount(matching.length, rule, noun, "elements_count_error", ctx);
|
|
280
|
+
if (rule.attributes) {
|
|
281
|
+
for (const element of matching) {
|
|
282
|
+
findings.push(...checkAttributes(element, rule.attributes, ctx));
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (rule.sequence || rule.contains) {
|
|
286
|
+
for (const element of matching) {
|
|
287
|
+
const elementCtx = { heading: ctx.heading, position: element.position };
|
|
288
|
+
findings.push(...checkSequenceIn(element.children, rule.sequence, elementCtx));
|
|
289
|
+
findings.push(...checkContainsIn(element.children, rule.contains, elementCtx));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return findings;
|
|
293
|
+
}
|
|
294
|
+
function checkAttributes(element, rule, ctx) {
|
|
295
|
+
const findings = [];
|
|
296
|
+
for (const [key, expected] of Object.entries(rule)) {
|
|
297
|
+
const finding = checkOne(element, key, expected, ctx);
|
|
298
|
+
if (finding) findings.push(finding);
|
|
299
|
+
}
|
|
300
|
+
return findings;
|
|
301
|
+
}
|
|
302
|
+
function checkOne(element, key, expected, ctx) {
|
|
303
|
+
const attributes = element.attributes ?? {};
|
|
304
|
+
const present = key in attributes;
|
|
305
|
+
const actual = attributes[key];
|
|
306
|
+
const fail = (want, detail) => ({
|
|
307
|
+
type: "elements_attribute_error",
|
|
308
|
+
heading: ctx.heading,
|
|
309
|
+
message: `Expected the "${element.name}" element ${want}, but ${detail}`,
|
|
310
|
+
position: element.position,
|
|
311
|
+
severity: "error"
|
|
312
|
+
});
|
|
313
|
+
if (expected === false) {
|
|
314
|
+
if (!present) return null;
|
|
315
|
+
const found = actual === true ? `an expression` : `${key}="${String(actual)}"`;
|
|
316
|
+
return fail(`not to carry ${key}`, `found ${found}`);
|
|
317
|
+
}
|
|
318
|
+
if (!present) return fail(attributeWant(key, expected), `it carries no ${key}`);
|
|
319
|
+
if (expected === true) return null;
|
|
320
|
+
if (actual === true) return fail(attributeWant(key, expected), `its ${key} is an expression`);
|
|
321
|
+
if (actual !== expected) return fail(attributeWant(key, expected), `found ${key}="${String(actual)}"`);
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
function attributeWant(key, expected) {
|
|
325
|
+
return expected === true ? `to carry ${key}` : `to carry ${key}="${expected}"`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/lint/rules/contains.ts
|
|
329
|
+
function checkContains(section, rule) {
|
|
330
|
+
return checkContainsIn(section.children, rule, sectionContext(section));
|
|
331
|
+
}
|
|
332
|
+
function checkContainsIn(content, rule, ctx) {
|
|
333
|
+
if (!rule) return [];
|
|
334
|
+
const findings = [];
|
|
335
|
+
for (const kind of BLOCK_KINDS) {
|
|
336
|
+
switch (kind) {
|
|
337
|
+
case "paragraphs":
|
|
338
|
+
findings.push(...checkParagraphsIn(content, rule.paragraphs, ctx));
|
|
339
|
+
break;
|
|
340
|
+
case "codeBlocks":
|
|
341
|
+
findings.push(...checkCodeBlocksIn(content, rule.codeBlocks, ctx));
|
|
342
|
+
break;
|
|
343
|
+
case "lists":
|
|
344
|
+
findings.push(...checkListsIn(content, rule.lists, ctx));
|
|
345
|
+
break;
|
|
346
|
+
case "tables":
|
|
347
|
+
findings.push(...checkTablesIn(content, rule.tables, ctx));
|
|
348
|
+
break;
|
|
349
|
+
case "admonitions":
|
|
350
|
+
findings.push(...checkAdmonitionsIn(content, rule.admonitions, ctx));
|
|
351
|
+
break;
|
|
352
|
+
case "images":
|
|
353
|
+
findings.push(...checkImagesIn(content, rule.images, ctx));
|
|
354
|
+
break;
|
|
355
|
+
case "blockquotes":
|
|
356
|
+
findings.push(...checkBlockquotesIn(content, rule.blockquotes, ctx));
|
|
357
|
+
break;
|
|
358
|
+
case "definitionLists":
|
|
359
|
+
findings.push(...checkDefinitionListsIn(content, rule.definitionLists, ctx));
|
|
360
|
+
break;
|
|
361
|
+
case "elements":
|
|
362
|
+
findings.push(...checkElementsIn(content, rule.elements, ctx));
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return findings;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/lint/rules/lists.ts
|
|
370
|
+
function checkListsIn(content, rule, ctx) {
|
|
371
|
+
if (!rule) return [];
|
|
372
|
+
const lists = listsOf(content);
|
|
373
|
+
const findings = checkCount(lists.length, rule, "list", "lists_count_error", ctx);
|
|
374
|
+
if (rule.ordered !== void 0) {
|
|
375
|
+
findings.push(...checkOrdered(lists, rule.ordered, ctx));
|
|
376
|
+
}
|
|
377
|
+
const items = rule.items;
|
|
378
|
+
if (!items) return findings;
|
|
379
|
+
findings.push(...checkItemCounts(lists, items, ctx));
|
|
380
|
+
if (!items.sequence && !items.contains) return findings;
|
|
381
|
+
for (const list of lists) {
|
|
382
|
+
for (const item of list.items) {
|
|
383
|
+
const itemCtx = { heading: ctx.heading, position: item.position };
|
|
384
|
+
if (items.sequence) {
|
|
385
|
+
findings.push(...checkSequenceIn(item.children, items.sequence, itemCtx));
|
|
386
|
+
} else if (items.contains) {
|
|
387
|
+
findings.push(...checkContainsIn(item.children, items.contains, itemCtx));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return findings;
|
|
392
|
+
}
|
|
393
|
+
function checkOrdered(lists, ordered, ctx) {
|
|
394
|
+
const findings = [];
|
|
395
|
+
for (const list of lists) {
|
|
396
|
+
if (list.ordered !== ordered) {
|
|
397
|
+
findings.push({
|
|
398
|
+
type: "lists_ordered_error",
|
|
399
|
+
heading: ctx.heading,
|
|
400
|
+
message: ordered ? "Expected a numbered list, but found a bulleted list" : "Expected a bulleted list, but found a numbered list",
|
|
401
|
+
position: list.position,
|
|
402
|
+
severity: "error"
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return findings;
|
|
407
|
+
}
|
|
408
|
+
function checkItemCounts(lists, items, ctx) {
|
|
409
|
+
const findings = [];
|
|
410
|
+
const min = items.min ?? (items.max === 0 ? 0 : 1);
|
|
411
|
+
const max = items.max ?? null;
|
|
412
|
+
if (max !== null) {
|
|
413
|
+
const offender = lists.find((list) => list.items.length > max);
|
|
414
|
+
if (offender) {
|
|
415
|
+
findings.push({
|
|
416
|
+
type: "lists_items_count_error",
|
|
417
|
+
heading: ctx.heading,
|
|
418
|
+
message: `Expected at most ${max} items in a list, but found ${offender.items.length}`,
|
|
419
|
+
position: offender.position,
|
|
420
|
+
severity: "error"
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (min > 0) {
|
|
425
|
+
const offender = lists.find((list) => list.items.length < min);
|
|
426
|
+
if (offender) {
|
|
427
|
+
findings.push({
|
|
428
|
+
type: "lists_items_count_error",
|
|
429
|
+
heading: ctx.heading,
|
|
430
|
+
message: `Expected at least ${min} items in a list, but found ${offender.items.length}`,
|
|
431
|
+
position: offender.position,
|
|
432
|
+
severity: "error"
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return findings;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/lint/rules/index.ts
|
|
440
|
+
var BLOCK_KINDS = [
|
|
441
|
+
"paragraphs",
|
|
442
|
+
"codeBlocks",
|
|
443
|
+
"lists",
|
|
444
|
+
"tables",
|
|
445
|
+
"admonitions",
|
|
446
|
+
"images",
|
|
447
|
+
"blockquotes",
|
|
448
|
+
"definitionLists",
|
|
449
|
+
"elements"
|
|
450
|
+
];
|
|
451
|
+
function sectionContext(section) {
|
|
452
|
+
return { heading: section.title || null, position: section.position };
|
|
453
|
+
}
|
|
454
|
+
function paragraphsOf(content) {
|
|
455
|
+
return content.filter((node) => node.kind === "paragraph");
|
|
456
|
+
}
|
|
457
|
+
function codeBlocksOf(content) {
|
|
458
|
+
return content.filter((node) => node.kind === "codeBlock");
|
|
459
|
+
}
|
|
460
|
+
function listsOf(content) {
|
|
461
|
+
return content.filter((node) => node.kind === "list");
|
|
462
|
+
}
|
|
463
|
+
function tablesOf(content) {
|
|
464
|
+
return content.filter((node) => node.kind === "table");
|
|
465
|
+
}
|
|
466
|
+
function admonitionsOf(content) {
|
|
467
|
+
return content.filter((node) => node.kind === "admonition");
|
|
468
|
+
}
|
|
469
|
+
function imagesOf(content) {
|
|
470
|
+
return content.filter((node) => node.kind === "image");
|
|
471
|
+
}
|
|
472
|
+
function blockquotesOf(content) {
|
|
473
|
+
return content.filter((node) => node.kind === "blockquote");
|
|
474
|
+
}
|
|
475
|
+
function definitionListsOf(content) {
|
|
476
|
+
return content.filter((node) => node.kind === "definitionList");
|
|
477
|
+
}
|
|
478
|
+
function elementsOf(content) {
|
|
479
|
+
return content.filter((node) => node.kind === "element");
|
|
480
|
+
}
|
|
481
|
+
function plural(noun, count) {
|
|
482
|
+
return count === 1 ? noun : `${noun}s`;
|
|
483
|
+
}
|
|
484
|
+
function quoteList(items) {
|
|
485
|
+
return items.map((item) => `"${item}"`).join(", ");
|
|
486
|
+
}
|
|
487
|
+
function checkCount(count, rule, noun, type, ctx) {
|
|
488
|
+
const findings = [];
|
|
489
|
+
const min = rule.min ?? (rule.max === 0 ? 0 : 1);
|
|
490
|
+
const max = rule.max ?? null;
|
|
491
|
+
if (count < min) {
|
|
492
|
+
findings.push({
|
|
493
|
+
type,
|
|
494
|
+
heading: ctx.heading,
|
|
495
|
+
message: `Expected at least ${min} ${plural(noun, min)}, but found ${count}`,
|
|
496
|
+
position: ctx.position,
|
|
497
|
+
severity: "error"
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
if (max !== null && count > max) {
|
|
501
|
+
findings.push({
|
|
502
|
+
type,
|
|
503
|
+
heading: ctx.heading,
|
|
504
|
+
message: `Expected at most ${max} ${plural(noun, max)}, but found ${count}`,
|
|
505
|
+
position: ctx.position,
|
|
506
|
+
severity: "error"
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
return findings;
|
|
510
|
+
}
|
|
511
|
+
var compiled = /* @__PURE__ */ new Map();
|
|
512
|
+
function compilePattern(pattern) {
|
|
513
|
+
const hit = compiled.get(pattern);
|
|
514
|
+
if (hit) return hit;
|
|
515
|
+
try {
|
|
516
|
+
const regex = new RegExp(pattern);
|
|
517
|
+
compiled.set(pattern, regex);
|
|
518
|
+
return regex;
|
|
519
|
+
} catch (error) {
|
|
520
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
521
|
+
throw new LintError(`Invalid pattern "${pattern}": ${reason}`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function clearPatternCache() {
|
|
525
|
+
compiled.clear();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/lint/rules/heading.ts
|
|
529
|
+
function ownTitle(section) {
|
|
530
|
+
return section.titlePosition === null ? null : section.title;
|
|
531
|
+
}
|
|
532
|
+
function checkHeading(section, rule) {
|
|
533
|
+
if (rule === void 0) return [];
|
|
534
|
+
const position = section.titlePosition ?? section.position;
|
|
535
|
+
const title = ownTitle(section);
|
|
536
|
+
const found = title === null ? "no heading" : `"${title}"`;
|
|
537
|
+
const fail = (message) => [
|
|
538
|
+
{
|
|
539
|
+
type: "heading_error",
|
|
540
|
+
// `|| null`, as `sectionContext` and `match.ts`'s `anchor` both do. A
|
|
541
|
+
// headless section's title is `""`, and the JSON reporter's `heading` is
|
|
542
|
+
// a wire key `manni docevals` parses rather than validates, so two
|
|
543
|
+
// spellings of "no heading" would reach it as two different facts.
|
|
544
|
+
heading: section.title || null,
|
|
545
|
+
message,
|
|
546
|
+
position,
|
|
547
|
+
severity: "error"
|
|
548
|
+
}
|
|
549
|
+
];
|
|
550
|
+
if (rule === false) {
|
|
551
|
+
return title === null ? [] : fail(`Expected no heading of its own, but found ${found}`);
|
|
552
|
+
}
|
|
553
|
+
if (typeof rule === "string") {
|
|
554
|
+
return title === rule ? [] : fail(`Expected title "${rule}", but found ${found}`);
|
|
555
|
+
}
|
|
556
|
+
if (Array.isArray(rule)) {
|
|
557
|
+
if (title !== null && rule.includes(title)) return [];
|
|
558
|
+
return fail(`Expected one of ${rule.map((option) => `"${option}"`).join(", ")}, but found ${found}`);
|
|
559
|
+
}
|
|
560
|
+
if (title !== null && compilePattern(rule.pattern).test(title)) return [];
|
|
561
|
+
return fail(`Expected title matching /${rule.pattern}/, but found ${found}`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/lint/core/template.ts
|
|
565
|
+
var BLOCK_KINDS2 = [
|
|
566
|
+
"paragraphs",
|
|
567
|
+
"codeBlocks",
|
|
568
|
+
"lists",
|
|
569
|
+
"tables",
|
|
570
|
+
"admonitions",
|
|
571
|
+
"images",
|
|
572
|
+
"blockquotes",
|
|
573
|
+
"definitionLists",
|
|
574
|
+
"elements"
|
|
575
|
+
];
|
|
576
|
+
var BLOCK_KIND_NODE = {
|
|
577
|
+
paragraphs: "paragraph",
|
|
578
|
+
codeBlocks: "codeBlock",
|
|
579
|
+
lists: "list",
|
|
580
|
+
tables: "table",
|
|
581
|
+
admonitions: "admonition",
|
|
582
|
+
images: "image",
|
|
583
|
+
blockquotes: "blockquote",
|
|
584
|
+
definitionLists: "definitionList",
|
|
585
|
+
elements: "element"
|
|
586
|
+
};
|
|
587
|
+
function isWildcard(rule) {
|
|
588
|
+
return rule.heading === void 0;
|
|
589
|
+
}
|
|
590
|
+
function occurrenceRange(rule) {
|
|
591
|
+
return { min: rule.min ?? (rule.max === 0 ? 0 : 1), max: rule.max ?? null };
|
|
592
|
+
}
|
|
593
|
+
function headingMatches(heading, title) {
|
|
594
|
+
if (heading === void 0) return true;
|
|
595
|
+
if (heading === false) return title === null;
|
|
596
|
+
if (title === null) return false;
|
|
597
|
+
if (typeof heading === "string") return title === heading;
|
|
598
|
+
if (Array.isArray(heading)) return heading.includes(title);
|
|
599
|
+
return compilePattern(heading.pattern).test(title);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// src/lint/core/match.ts
|
|
603
|
+
var ALIGNMENT_COSTS = {
|
|
604
|
+
match: 0,
|
|
605
|
+
coerce: 1,
|
|
606
|
+
skipRule: 2,
|
|
607
|
+
skipSection: 2
|
|
608
|
+
};
|
|
609
|
+
var STATE_LIMIT = 2e3;
|
|
610
|
+
var RANK = {
|
|
611
|
+
match: 0,
|
|
612
|
+
structure: 0,
|
|
613
|
+
coerce: 1,
|
|
614
|
+
skipRule: 2,
|
|
615
|
+
skipSection: 3
|
|
616
|
+
};
|
|
617
|
+
var NO_EPS = [];
|
|
618
|
+
function expandedStates(rules) {
|
|
619
|
+
let total = 1;
|
|
620
|
+
for (const rule of rules) total += ruleStates(rule);
|
|
621
|
+
return total;
|
|
622
|
+
}
|
|
623
|
+
function ruleStates(rule) {
|
|
624
|
+
let unit = 1;
|
|
625
|
+
if (rule.repeat) {
|
|
626
|
+
unit = 0;
|
|
627
|
+
for (const member of rule.repeat) unit += ruleStates(member);
|
|
628
|
+
}
|
|
629
|
+
const { min, max } = occurrenceRange(rule);
|
|
630
|
+
if (max === null) return unit * (Math.max(min, 0) + 1) + 2;
|
|
631
|
+
return unit * Math.max(0, max);
|
|
632
|
+
}
|
|
633
|
+
function listAt(lists, index) {
|
|
634
|
+
const list = lists[index];
|
|
635
|
+
if (list) return list;
|
|
636
|
+
const created = [];
|
|
637
|
+
lists[index] = created;
|
|
638
|
+
return created;
|
|
639
|
+
}
|
|
640
|
+
function compile(rules) {
|
|
641
|
+
const consume = [];
|
|
642
|
+
const consumeTo = [];
|
|
643
|
+
const epsOut = [];
|
|
644
|
+
const epsIn = [];
|
|
645
|
+
const inRepeat = [];
|
|
646
|
+
const newState = () => {
|
|
647
|
+
consume.push(null);
|
|
648
|
+
consumeTo.push(-1);
|
|
649
|
+
epsOut.push([]);
|
|
650
|
+
epsIn.push([]);
|
|
651
|
+
inRepeat.push(false);
|
|
652
|
+
return consume.length - 1;
|
|
653
|
+
};
|
|
654
|
+
const eps = (from, to, cost, rank, occ) => {
|
|
655
|
+
const edge = { from, to, cost, rank, occ };
|
|
656
|
+
listAt(epsOut, from).push(edge);
|
|
657
|
+
listAt(epsIn, to).push(edge);
|
|
658
|
+
};
|
|
659
|
+
const leaf = (from, occ) => {
|
|
660
|
+
const to = newState();
|
|
661
|
+
consume[from] = occ;
|
|
662
|
+
consumeTo[from] = to;
|
|
663
|
+
eps(from, to, occ.required ? ALIGNMENT_COSTS.skipRule : 0, RANK.skipRule, occ);
|
|
664
|
+
return to;
|
|
665
|
+
};
|
|
666
|
+
const buildCopy = (rule, from, groups, required) => {
|
|
667
|
+
if (rule.repeat) {
|
|
668
|
+
const copy = { rule, members: [] };
|
|
669
|
+
const nested = [...groups, copy];
|
|
670
|
+
let cursor2 = from;
|
|
671
|
+
for (const member of rule.repeat) {
|
|
672
|
+
cursor2 = buildRule(member, cursor2, nested, required);
|
|
673
|
+
}
|
|
674
|
+
return cursor2;
|
|
675
|
+
}
|
|
676
|
+
const { max } = occurrenceRange(rule);
|
|
677
|
+
const occ = {
|
|
678
|
+
rule,
|
|
679
|
+
required,
|
|
680
|
+
coercible: required && max === 1,
|
|
681
|
+
wildcard: isWildcard(rule),
|
|
682
|
+
groups
|
|
683
|
+
};
|
|
684
|
+
for (const group of groups) group.members.push(occ);
|
|
685
|
+
return leaf(from, occ);
|
|
686
|
+
};
|
|
687
|
+
function buildRule(rule, from, groups, context) {
|
|
688
|
+
const { min, max } = occurrenceRange(rule);
|
|
689
|
+
const floor = max === null ? Math.max(min, 0) : Math.min(Math.max(min, 0), max);
|
|
690
|
+
const copies = max === null ? floor : Math.max(0, max);
|
|
691
|
+
const first = consume.length;
|
|
692
|
+
let cursor2 = from;
|
|
693
|
+
for (let copy = 0; copy < copies; copy++) {
|
|
694
|
+
cursor2 = buildCopy(rule, cursor2, groups, context && copy < floor);
|
|
695
|
+
}
|
|
696
|
+
let end = cursor2;
|
|
697
|
+
if (max === null) {
|
|
698
|
+
const loop = newState();
|
|
699
|
+
eps(cursor2, loop, 0, RANK.structure, null);
|
|
700
|
+
const back = buildCopy(rule, loop, groups, false);
|
|
701
|
+
eps(back, loop, 0, RANK.structure, null);
|
|
702
|
+
end = newState();
|
|
703
|
+
eps(loop, end, 0, RANK.structure, null);
|
|
704
|
+
}
|
|
705
|
+
if (copies > 1 || max === null) {
|
|
706
|
+
for (let state = first; state < consume.length; state++) {
|
|
707
|
+
if (state !== end) inRepeat[state] = true;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return end;
|
|
711
|
+
}
|
|
712
|
+
const start = newState();
|
|
713
|
+
let cursor = start;
|
|
714
|
+
for (const rule of rules) cursor = buildRule(rule, cursor, [], true);
|
|
715
|
+
return {
|
|
716
|
+
states: consume.length,
|
|
717
|
+
start,
|
|
718
|
+
accept: cursor,
|
|
719
|
+
consume,
|
|
720
|
+
consumeTo,
|
|
721
|
+
epsOut,
|
|
722
|
+
epsIn,
|
|
723
|
+
inRepeat
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
var compiled2 = /* @__PURE__ */ new WeakMap();
|
|
727
|
+
function automatonFor(rules, options) {
|
|
728
|
+
const hit = compiled2.get(rules);
|
|
729
|
+
if (hit) return hit;
|
|
730
|
+
const states = expandedStates(rules);
|
|
731
|
+
if (states > STATE_LIMIT) {
|
|
732
|
+
const source = options.source ?? "template";
|
|
733
|
+
const label = options.template != null ? `${source}#${options.template}` : `${source}: the rule list`;
|
|
734
|
+
throw new LintError(
|
|
735
|
+
`${label} expands to ${states} states; the limit is ${STATE_LIMIT}. Cap a max, or split the template.`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
const built = compile(rules);
|
|
739
|
+
compiled2.set(rules, built);
|
|
740
|
+
return built;
|
|
741
|
+
}
|
|
742
|
+
function titleOf(section) {
|
|
743
|
+
return section.titlePosition === null ? null : section.title;
|
|
744
|
+
}
|
|
745
|
+
var Heap = class {
|
|
746
|
+
costs = [];
|
|
747
|
+
items = [];
|
|
748
|
+
get size() {
|
|
749
|
+
return this.items.length;
|
|
750
|
+
}
|
|
751
|
+
/*
|
|
752
|
+
* Every `?? 0` in this class is unreachable, and is here only because
|
|
753
|
+
* `noUncheckedIndexedAccess` cannot see the bounds the arithmetic already
|
|
754
|
+
* guarantees. `costs` and `items` are pushed and popped together and so
|
|
755
|
+
* always have the same length; `child` is the index just pushed, and
|
|
756
|
+
* `parent = (child - 1) >> 1` is in bounds whenever `child > 0`; `left` and
|
|
757
|
+
* `right` are compared against `length` before they are read.
|
|
758
|
+
*
|
|
759
|
+
* Worth stating rather than leaving to inference: were an index ever out of
|
|
760
|
+
* bounds, the fallback would read as cost 0, quietly inverting the heap
|
|
761
|
+
* order rather than throwing. It is a type-checker concession, not a
|
|
762
|
+
* defensive default.
|
|
763
|
+
*/
|
|
764
|
+
push(cost, item) {
|
|
765
|
+
this.costs.push(cost);
|
|
766
|
+
this.items.push(item);
|
|
767
|
+
let child = this.items.length - 1;
|
|
768
|
+
while (child > 0) {
|
|
769
|
+
const parent = child - 1 >> 1;
|
|
770
|
+
if ((this.costs[parent] ?? 0) <= (this.costs[child] ?? 0)) break;
|
|
771
|
+
this.swap(parent, child);
|
|
772
|
+
child = parent;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
pop() {
|
|
776
|
+
const cost = this.costs[0] ?? 0;
|
|
777
|
+
const item = this.items[0] ?? 0;
|
|
778
|
+
const lastCost = this.costs.pop();
|
|
779
|
+
const lastItem = this.items.pop();
|
|
780
|
+
if (this.items.length > 0 && lastCost !== void 0 && lastItem !== void 0) {
|
|
781
|
+
this.costs[0] = lastCost;
|
|
782
|
+
this.items[0] = lastItem;
|
|
783
|
+
let parent = 0;
|
|
784
|
+
for (; ; ) {
|
|
785
|
+
const left = parent * 2 + 1;
|
|
786
|
+
const right = left + 1;
|
|
787
|
+
let smallest = parent;
|
|
788
|
+
if (left < this.items.length && (this.costs[left] ?? 0) < (this.costs[smallest] ?? 0)) {
|
|
789
|
+
smallest = left;
|
|
790
|
+
}
|
|
791
|
+
if (right < this.items.length && (this.costs[right] ?? 0) < (this.costs[smallest] ?? 0)) {
|
|
792
|
+
smallest = right;
|
|
793
|
+
}
|
|
794
|
+
if (smallest === parent) break;
|
|
795
|
+
this.swap(parent, smallest);
|
|
796
|
+
parent = smallest;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return { cost, item };
|
|
800
|
+
}
|
|
801
|
+
swap(a, b) {
|
|
802
|
+
const cost = this.costs[a] ?? 0;
|
|
803
|
+
const item = this.items[a] ?? 0;
|
|
804
|
+
this.costs[a] = this.costs[b] ?? 0;
|
|
805
|
+
this.items[a] = this.items[b] ?? 0;
|
|
806
|
+
this.costs[b] = cost;
|
|
807
|
+
this.items[b] = item;
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
function align(sections, nfa) {
|
|
811
|
+
const n = sections.length;
|
|
812
|
+
const states = nfa.states;
|
|
813
|
+
const radix = 2 * (n + states) + 2;
|
|
814
|
+
const weigh = (primary, wildcards, brokenRuns) => (primary * radix + wildcards) * radix + brokenRuns;
|
|
815
|
+
const SKIP_SECTION = weigh(ALIGNMENT_COSTS.skipSection, 0, 0);
|
|
816
|
+
const SKIP_SECTION_IN_RUN = weigh(ALIGNMENT_COSTS.skipSection, 0, 1);
|
|
817
|
+
const MATCH = weigh(ALIGNMENT_COSTS.match, 0, 0);
|
|
818
|
+
const MATCH_WILDCARD = weigh(ALIGNMENT_COSTS.match, 1, 0);
|
|
819
|
+
const COERCE = weigh(ALIGNMENT_COSTS.coerce, 0, 0);
|
|
820
|
+
const consumeCost = (state2, index2) => {
|
|
821
|
+
const occ = nfa.consume[state2];
|
|
822
|
+
const section = sections[index2];
|
|
823
|
+
const to = nfa.consumeTo[state2];
|
|
824
|
+
if (!occ || !section || to === void 0 || to < 0) return null;
|
|
825
|
+
if (headingMatches(occ.rule.heading, titleOf(section))) {
|
|
826
|
+
const cost = occ.wildcard ? MATCH_WILDCARD : MATCH;
|
|
827
|
+
return { cost, rank: RANK.match, to, occ };
|
|
828
|
+
}
|
|
829
|
+
if (occ.coercible) return { cost: COERCE, rank: RANK.coerce, to, occ };
|
|
830
|
+
return null;
|
|
831
|
+
};
|
|
832
|
+
const skipSectionCost = (state2) => nfa.inRepeat[state2] === true ? SKIP_SECTION_IN_RUN : SKIP_SECTION;
|
|
833
|
+
const dist = new Float64Array((n + 1) * states).fill(Infinity);
|
|
834
|
+
const distAt = (slot) => dist[slot] ?? Infinity;
|
|
835
|
+
for (let index2 = n; index2 >= 0; index2--) {
|
|
836
|
+
const base = index2 * states;
|
|
837
|
+
const next = base + states;
|
|
838
|
+
const heap = new Heap();
|
|
839
|
+
for (let state2 = 0; state2 < states; state2++) {
|
|
840
|
+
let best = Infinity;
|
|
841
|
+
if (index2 === n) {
|
|
842
|
+
if (state2 === nfa.accept) best = 0;
|
|
843
|
+
} else {
|
|
844
|
+
const step = consumeCost(state2, index2);
|
|
845
|
+
if (step) best = step.cost + distAt(next + step.to);
|
|
846
|
+
const skipped = skipSectionCost(state2) + distAt(next + state2);
|
|
847
|
+
if (skipped < best) best = skipped;
|
|
848
|
+
}
|
|
849
|
+
dist[base + state2] = best;
|
|
850
|
+
if (best < Infinity) heap.push(best, state2);
|
|
851
|
+
}
|
|
852
|
+
const done = new Uint8Array(states);
|
|
853
|
+
while (heap.size > 0) {
|
|
854
|
+
const { cost, item } = heap.pop();
|
|
855
|
+
if (done[item] === 1 || cost > distAt(base + item)) continue;
|
|
856
|
+
done[item] = 1;
|
|
857
|
+
for (const edge of nfa.epsIn[item] ?? NO_EPS) {
|
|
858
|
+
const candidate = cost + edge.cost;
|
|
859
|
+
if (candidate < distAt(base + edge.from)) {
|
|
860
|
+
dist[base + edge.from] = candidate;
|
|
861
|
+
heap.push(candidate, edge.from);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
if (distAt(nfa.start) === Infinity) {
|
|
867
|
+
throw new LintError("internal: no alignment exists for this rule list.");
|
|
868
|
+
}
|
|
869
|
+
const steps = [];
|
|
870
|
+
let index = 0;
|
|
871
|
+
let state = nfa.start;
|
|
872
|
+
let seen = /* @__PURE__ */ new Set([state]);
|
|
873
|
+
while (!(index === n && state === nfa.accept)) {
|
|
874
|
+
const moves = [];
|
|
875
|
+
const offer = (cost, rank, to, column, kind, occ) => {
|
|
876
|
+
const total = cost + distAt(column * states + to);
|
|
877
|
+
if (total === Infinity) return;
|
|
878
|
+
moves.push({ kind, to, column, occ, total, rank });
|
|
879
|
+
};
|
|
880
|
+
if (index < n) {
|
|
881
|
+
const step = consumeCost(state, index);
|
|
882
|
+
if (step) {
|
|
883
|
+
offer(
|
|
884
|
+
step.cost,
|
|
885
|
+
step.rank,
|
|
886
|
+
step.to,
|
|
887
|
+
index + 1,
|
|
888
|
+
step.rank === RANK.coerce ? "coerce" : "match",
|
|
889
|
+
step.occ
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
offer(skipSectionCost(state), RANK.skipSection, state, index + 1, "skip-section", null);
|
|
893
|
+
}
|
|
894
|
+
for (const edge of nfa.epsOut[state] ?? NO_EPS) {
|
|
895
|
+
if (seen.has(edge.to)) continue;
|
|
896
|
+
offer(edge.cost, edge.rank, edge.to, index, "skip-rule", edge.occ);
|
|
897
|
+
}
|
|
898
|
+
let move;
|
|
899
|
+
for (const candidate of moves) {
|
|
900
|
+
if (move === void 0 || candidate.total < move.total || candidate.total === move.total && candidate.rank < move.rank || candidate.total === move.total && candidate.rank === move.rank && candidate.to < move.to) {
|
|
901
|
+
move = candidate;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (move === void 0) {
|
|
905
|
+
throw new LintError("internal: the alignment could not be reconstructed.");
|
|
906
|
+
}
|
|
907
|
+
if (move.kind !== "skip-rule" || move.occ !== null) {
|
|
908
|
+
steps.push({ kind: move.kind, index, occ: move.occ });
|
|
909
|
+
}
|
|
910
|
+
if (move.column !== index) {
|
|
911
|
+
index = move.column;
|
|
912
|
+
seen = /* @__PURE__ */ new Set();
|
|
913
|
+
}
|
|
914
|
+
state = move.to;
|
|
915
|
+
seen.add(state);
|
|
916
|
+
}
|
|
917
|
+
return steps;
|
|
918
|
+
}
|
|
919
|
+
function collapse(point) {
|
|
920
|
+
return { start: { ...point }, end: { ...point } };
|
|
921
|
+
}
|
|
922
|
+
function anchor(index, sections, parent) {
|
|
923
|
+
const section = sections[index];
|
|
924
|
+
if (section) {
|
|
925
|
+
return { heading: section.title || null, position: section.position };
|
|
926
|
+
}
|
|
927
|
+
if (parent) {
|
|
928
|
+
return { heading: parent.title || null, position: collapse(parent.position.end) };
|
|
929
|
+
}
|
|
930
|
+
const last = sections[sections.length - 1];
|
|
931
|
+
if (last) return { heading: null, position: collapse(last.position.end) };
|
|
932
|
+
return { heading: null, position: collapse({ line: 1, column: 1, offset: 0 }) };
|
|
933
|
+
}
|
|
934
|
+
function describeHeading(rule) {
|
|
935
|
+
const heading = rule.heading;
|
|
936
|
+
if (typeof heading === "string") return `"${heading}"`;
|
|
937
|
+
if (Array.isArray(heading)) return heading.map((text) => `"${text}"`).join(", ");
|
|
938
|
+
if (heading === false) return "a section with no heading";
|
|
939
|
+
if (heading !== void 0) return `/${heading.pattern}/`;
|
|
940
|
+
return rule.id === void 0 ? "any section" : `"${rule.id}"`;
|
|
941
|
+
}
|
|
942
|
+
function missingMessage(rule) {
|
|
943
|
+
const heading = rule.heading;
|
|
944
|
+
if (typeof heading === "string") return `Missing section "${heading}"`;
|
|
945
|
+
if (Array.isArray(heading)) {
|
|
946
|
+
return `Missing one of ${heading.map((text) => `"${text}"`).join(", ")}`;
|
|
947
|
+
}
|
|
948
|
+
if (heading === false) return "Missing section without a heading";
|
|
949
|
+
if (heading !== void 0) return `Missing section matching /${heading.pattern}/`;
|
|
950
|
+
return rule.id === void 0 ? "Missing section" : `Missing section "${rule.id}"`;
|
|
951
|
+
}
|
|
952
|
+
function missingGroupMessage(copy) {
|
|
953
|
+
const members = (copy.rule.repeat ?? []).map(describeHeading).join(" followed by ");
|
|
954
|
+
const name = copy.rule.id === void 0 ? "" : ` "${copy.rule.id}"`;
|
|
955
|
+
return `Missing group${name}: expected ${members}`;
|
|
956
|
+
}
|
|
957
|
+
function unexpectedMessage(section) {
|
|
958
|
+
const what = section.titlePosition === null ? "Unexpected section without a heading" : `Unexpected section "${section.title}"`;
|
|
959
|
+
return `${what}. Add a trailing rule with min: 0 to allow sections the template does not describe.`;
|
|
960
|
+
}
|
|
961
|
+
function matchSections(sections, rules, options = {}) {
|
|
962
|
+
const matches2 = [];
|
|
963
|
+
const findings = [];
|
|
964
|
+
if (!rules) return { matches: matches2, findings };
|
|
965
|
+
const parent = options.parent ?? null;
|
|
966
|
+
const nfa = automatonFor(rules, options);
|
|
967
|
+
const steps = align(sections, nfa);
|
|
968
|
+
const taken = /* @__PURE__ */ new Set();
|
|
969
|
+
for (const step of steps) {
|
|
970
|
+
if ((step.kind === "match" || step.kind === "coerce") && step.occ) {
|
|
971
|
+
taken.add(step.occ);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
const absentGroup = (occ) => {
|
|
975
|
+
for (const group of occ.groups) {
|
|
976
|
+
if (group.members.every((member) => !taken.has(member))) return group;
|
|
977
|
+
}
|
|
978
|
+
return null;
|
|
979
|
+
};
|
|
980
|
+
const reported = /* @__PURE__ */ new Set();
|
|
981
|
+
for (const step of steps) {
|
|
982
|
+
const section = sections[step.index];
|
|
983
|
+
if ((step.kind === "match" || step.kind === "coerce") && step.occ && section) {
|
|
984
|
+
matches2.push({
|
|
985
|
+
rule: step.occ.rule,
|
|
986
|
+
section,
|
|
987
|
+
coerced: step.kind === "coerce"
|
|
988
|
+
});
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
if (step.kind === "skip-section" && section) {
|
|
992
|
+
findings.push({
|
|
993
|
+
type: "unexpected_section",
|
|
994
|
+
heading: section.title || null,
|
|
995
|
+
message: unexpectedMessage(section),
|
|
996
|
+
position: section.position,
|
|
997
|
+
severity: "error"
|
|
998
|
+
});
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
if (step.kind !== "skip-rule" || !step.occ || !step.occ.required) continue;
|
|
1002
|
+
const where = anchor(step.index, sections, parent);
|
|
1003
|
+
const group = absentGroup(step.occ);
|
|
1004
|
+
if (group) {
|
|
1005
|
+
if (reported.has(group)) continue;
|
|
1006
|
+
reported.add(group);
|
|
1007
|
+
findings.push({
|
|
1008
|
+
type: "missing_group",
|
|
1009
|
+
heading: where.heading,
|
|
1010
|
+
message: missingGroupMessage(group),
|
|
1011
|
+
position: where.position,
|
|
1012
|
+
severity: "error"
|
|
1013
|
+
});
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
findings.push({
|
|
1017
|
+
type: "missing_section",
|
|
1018
|
+
heading: where.heading,
|
|
1019
|
+
message: missingMessage(step.occ.rule),
|
|
1020
|
+
position: where.position,
|
|
1021
|
+
severity: "error"
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
return { matches: matches2, findings };
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
export {
|
|
1028
|
+
LintError,
|
|
1029
|
+
checkHeading,
|
|
1030
|
+
checkSequence,
|
|
1031
|
+
checkSequenceIn,
|
|
1032
|
+
checkContains,
|
|
1033
|
+
checkContainsIn,
|
|
1034
|
+
clearPatternCache,
|
|
1035
|
+
BLOCK_KINDS2 as BLOCK_KINDS,
|
|
1036
|
+
BLOCK_KIND_NODE,
|
|
1037
|
+
isWildcard,
|
|
1038
|
+
occurrenceRange,
|
|
1039
|
+
headingMatches,
|
|
1040
|
+
matchSections
|
|
1041
|
+
};
|
|
1042
|
+
//# sourceMappingURL=chunk-CK7SKVHR.js.map
|