@hawkeyexl/manni 4.3.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/batch-DFRCLY77.js +36 -0
- package/dist/build-Q5C6SX5J.js +31 -0
- package/dist/calibrate-XQILKXS3.js +31 -0
- package/dist/capture-JQMOC3BW.js +32 -0
- package/dist/check-HYNGXYJV.js +18 -0
- package/dist/check-HYNGXYJV.js.map +1 -0
- package/dist/check-NFZQLJKN.js +27 -0
- package/dist/check-NFZQLJKN.js.map +1 -0
- package/dist/check-U54XV6BP.js +40 -0
- package/dist/check-U54XV6BP.js.map +1 -0
- package/dist/chunk-252LNOEG.js +622 -0
- package/dist/chunk-252LNOEG.js.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-2U3ORT6D.js +230 -0
- package/dist/chunk-2U3ORT6D.js.map +1 -0
- package/dist/chunk-3AFUD75K.js +23 -0
- package/dist/chunk-3AFUD75K.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-5KHIFVD2.js +1192 -0
- package/dist/chunk-5KHIFVD2.js.map +1 -0
- package/dist/chunk-5YXBLLAC.js +2165 -0
- package/dist/chunk-5YXBLLAC.js.map +1 -0
- package/dist/chunk-644JVKJ4.js +3872 -0
- package/dist/chunk-644JVKJ4.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-77HX34KD.js +1287 -0
- package/dist/chunk-77HX34KD.js.map +1 -0
- package/dist/{chunk-CFDUZRM2.js → chunk-7QMCHA4N.js} +32 -240
- package/dist/chunk-7QMCHA4N.js.map +1 -0
- package/dist/chunk-BUXDLRDT.js +455 -0
- package/dist/chunk-BUXDLRDT.js.map +1 -0
- package/dist/chunk-C6OH6CNF.js +388 -0
- package/dist/chunk-C6OH6CNF.js.map +1 -0
- package/dist/chunk-CK7SKVHR.js +1042 -0
- package/dist/chunk-CK7SKVHR.js.map +1 -0
- package/dist/chunk-CYWHFSRT.js +6737 -0
- package/dist/chunk-CYWHFSRT.js.map +1 -0
- package/dist/chunk-ERMSO34F.js +17 -0
- package/dist/chunk-ERMSO34F.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-G7NJSREW.js +979 -0
- package/dist/chunk-G7NJSREW.js.map +1 -0
- package/dist/chunk-GLP5PSXB.js +72 -0
- package/dist/chunk-GLP5PSXB.js.map +1 -0
- package/dist/chunk-H2DLH2JI.js +198 -0
- package/dist/chunk-H2DLH2JI.js.map +1 -0
- package/dist/chunk-H6FZ66XR.js +634 -0
- package/dist/chunk-H6FZ66XR.js.map +1 -0
- package/dist/{chunk-KJR7OVDX.js → chunk-IEQHSCZG.js} +56 -137
- package/dist/chunk-IEQHSCZG.js.map +1 -0
- package/dist/chunk-IKEWBMCN.js +98 -0
- package/dist/chunk-IKEWBMCN.js.map +1 -0
- package/dist/chunk-JOHKIAVH.js +5440 -0
- package/dist/chunk-JOHKIAVH.js.map +1 -0
- package/dist/chunk-JVTNMVYU.js +10921 -0
- package/dist/chunk-JVTNMVYU.js.map +1 -0
- package/dist/chunk-K3D6FPV6.js +164 -0
- package/dist/chunk-K3D6FPV6.js.map +1 -0
- package/dist/{chunk-UBMMXS2Y.js → chunk-KCGV52AM.js} +69 -48
- package/dist/{chunk-UBMMXS2Y.js.map → chunk-KCGV52AM.js.map} +1 -1
- package/dist/chunk-LAKAWRV5.js +1651 -0
- package/dist/chunk-LAKAWRV5.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-MNH7QO4Z.js +15 -0
- package/dist/chunk-MNH7QO4Z.js.map +1 -0
- package/dist/{chunk-HAPTXWU2.js → chunk-MYC3O5EJ.js} +4 -4
- 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-NWZ2SLYL.js +2957 -0
- package/dist/chunk-NWZ2SLYL.js.map +1 -0
- package/dist/chunk-OBFENF3U.js +934 -0
- package/dist/chunk-OBFENF3U.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-PCHZY2I7.js +299 -0
- package/dist/chunk-PCHZY2I7.js.map +1 -0
- package/dist/chunk-PHF3D7ER.js +1042 -0
- package/dist/chunk-PHF3D7ER.js.map +1 -0
- package/dist/chunk-Q35YIDBV.js +1202 -0
- package/dist/chunk-Q35YIDBV.js.map +1 -0
- package/dist/chunk-RLR4TKUZ.js +4398 -0
- package/dist/chunk-RLR4TKUZ.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-TIUTIOFB.js +25 -0
- package/dist/chunk-TIUTIOFB.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-UKZNLVJW.js +17 -0
- package/dist/chunk-UKZNLVJW.js.map +1 -0
- package/dist/chunk-UZC7LDAQ.js +178 -0
- package/dist/chunk-UZC7LDAQ.js.map +1 -0
- package/dist/chunk-V46OILVC.js +3090 -0
- package/dist/chunk-V46OILVC.js.map +1 -0
- package/dist/chunk-WQE54PNH.js +1975 -0
- package/dist/chunk-WQE54PNH.js.map +1 -0
- package/dist/chunk-X3BPBDGQ.js +52 -0
- package/dist/chunk-X3BPBDGQ.js.map +1 -0
- package/dist/chunk-XSLV76YK.js +54 -0
- package/dist/chunk-XSLV76YK.js.map +1 -0
- package/dist/{chunk-5HNHEUD5.js → chunk-XYLUCIKQ.js} +3 -3
- package/dist/chunk-YEUYKR62.js +72 -0
- package/dist/chunk-YEUYKR62.js.map +1 -0
- package/dist/{chunk-G5CM2VGQ.js → chunk-YFQBFNQS.js} +8 -94
- package/dist/chunk-YFQBFNQS.js.map +1 -0
- package/dist/chunk-YIL4XLLJ.js +1661 -0
- package/dist/chunk-YIL4XLLJ.js.map +1 -0
- package/dist/chunk-YNHZHSSM.js +41 -0
- package/dist/chunk-YNHZHSSM.js.map +1 -0
- package/dist/chunk-YTCZRLTU.js +96 -0
- package/dist/chunk-YTCZRLTU.js.map +1 -0
- package/dist/chunk-YYBFZLAJ.js +374 -0
- package/dist/chunk-YYBFZLAJ.js.map +1 -0
- package/dist/chunk-ZXRAE55H.js +31 -0
- package/dist/chunk-ZXRAE55H.js.map +1 -0
- package/dist/cli-2QPR2OZG.js +232 -0
- package/dist/cli-2QPR2OZG.js.map +1 -0
- package/dist/cli-4IOPGZHR.js +1203 -0
- package/dist/cli-4IOPGZHR.js.map +1 -0
- package/dist/cli-5FL73WHA.js +346 -0
- package/dist/cli-5FL73WHA.js.map +1 -0
- package/dist/cli-C262QOWM.js +739 -0
- package/dist/cli-C262QOWM.js.map +1 -0
- package/dist/cli-LJLLIQB2.js +423 -0
- package/dist/cli-LJLLIQB2.js.map +1 -0
- package/dist/cli-OFPZ5MAS.js +626 -0
- package/dist/cli-OFPZ5MAS.js.map +1 -0
- package/dist/cli-SHSMSRQY.js +28 -0
- package/dist/cli-SHSMSRQY.js.map +1 -0
- package/dist/cli-SRHFUZHA.js +473 -0
- package/dist/cli-SRHFUZHA.js.map +1 -0
- package/dist/cli-URZQXL4K.js +552 -0
- package/dist/cli-URZQXL4K.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-FE6KCNYJ.js +27 -0
- package/dist/github-FE6KCNYJ.js.map +1 -0
- package/dist/github-QXL3VMK2.js +32 -0
- package/dist/github-QXL3VMK2.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-SGZ6ZRFD.js +35 -0
- package/dist/json-SGZ6ZRFD.js.map +1 -0
- package/dist/lint-DL4LJRAY.js +28 -0
- package/dist/lint-DL4LJRAY.js.map +1 -0
- package/dist/list-6BAXTZUM.js +29 -0
- package/dist/list-6BAXTZUM.js.map +1 -0
- package/dist/list-7BKNYPSB.js +31 -0
- package/dist/list-7BKNYPSB.js.map +1 -0
- package/dist/page-3U2GCFNE.js +41 -0
- package/dist/page-3U2GCFNE.js.map +1 -0
- package/dist/picker-3XF36MJF.js +14 -0
- package/dist/picker-3XF36MJF.js.map +1 -0
- package/dist/pretty-2H57QVTY.js +43 -0
- package/dist/pretty-2H57QVTY.js.map +1 -0
- package/dist/pretty-N6DO4OOL.js +37 -0
- package/dist/pretty-N6DO4OOL.js.map +1 -0
- package/dist/reporters-M6LJ2IYU.js +21 -0
- package/dist/reporters-M6LJ2IYU.js.map +1 -0
- package/dist/reporters-N6GHXSS7.js +42 -0
- package/dist/reporters-N6GHXSS7.js.map +1 -0
- package/dist/reporters-UCNEV7PH.js +71 -0
- package/dist/reporters-UCNEV7PH.js.map +1 -0
- package/dist/run-XZDAREJE.js +33 -0
- package/dist/run-XZDAREJE.js.map +1 -0
- package/dist/term-5ICGKPC3.js +1592 -0
- package/dist/term-5ICGKPC3.js.map +1 -0
- package/dist/types-Y6TXHM7M.js +10 -0
- package/dist/types-Y6TXHM7M.js.map +1 -0
- package/dist/validate-GQRM77YN.js +16 -0
- package/dist/validate-GQRM77YN.js.map +1 -0
- package/package.json +1 -1
- package/plugin/manni/.claude-plugin/plugin.json +9 -0
- package/plugin/manni/hooks/hooks.json +52 -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-DFRCLY77.js.map} +0 -0
- /package/dist/{calibrate-S5K6QHUY.js.map → build-Q5C6SX5J.js.map} +0 -0
- /package/dist/{capture-OZEZDABQ.js.map → calibrate-XQILKXS3.js.map} +0 -0
- /package/dist/{picker-6HRMETE7.js.map → capture-JQMOC3BW.js.map} +0 -0
- /package/dist/{chunk-HAPTXWU2.js.map → chunk-MYC3O5EJ.js.map} +0 -0
- /package/dist/{chunk-5HNHEUD5.js.map → chunk-XYLUCIKQ.js.map} +0 -0
|
@@ -0,0 +1,1592 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
renderFindingsJson,
|
|
4
|
+
renderFormatsJson,
|
|
5
|
+
renderGetJson,
|
|
6
|
+
renderListJson
|
|
7
|
+
} from "./chunk-X3BPBDGQ.js";
|
|
8
|
+
import {
|
|
9
|
+
renderFindingsPretty,
|
|
10
|
+
renderFormatsPretty,
|
|
11
|
+
renderGetPretty,
|
|
12
|
+
renderListPretty,
|
|
13
|
+
renderValeWiring,
|
|
14
|
+
renderWritePretty
|
|
15
|
+
} from "./chunk-2U3ORT6D.js";
|
|
16
|
+
import {
|
|
17
|
+
FINDING_FORMATS,
|
|
18
|
+
JUNIT_CLASSNAME,
|
|
19
|
+
RULE_ID_PREFIX,
|
|
20
|
+
foldValeSeverity,
|
|
21
|
+
isTermRule,
|
|
22
|
+
proseRuleId,
|
|
23
|
+
reportedFindings,
|
|
24
|
+
runCheck,
|
|
25
|
+
summarize,
|
|
26
|
+
toResults
|
|
27
|
+
} from "./chunk-YYBFZLAJ.js";
|
|
28
|
+
import {
|
|
29
|
+
LIST_FORMATS,
|
|
30
|
+
runList
|
|
31
|
+
} from "./chunk-ERMSO34F.js";
|
|
32
|
+
import {
|
|
33
|
+
renderFindingsGithub
|
|
34
|
+
} from "./chunk-3AFUD75K.js";
|
|
35
|
+
import {
|
|
36
|
+
DITA_HOLDS,
|
|
37
|
+
DOCBOOK_HOLDS,
|
|
38
|
+
LIST_HOLDS,
|
|
39
|
+
MANIFEST_FORMAT,
|
|
40
|
+
TERM_FIELDS,
|
|
41
|
+
TERM_READERS,
|
|
42
|
+
TERM_RULES,
|
|
43
|
+
TERM_WRITE_FORMATS,
|
|
44
|
+
TermError,
|
|
45
|
+
asciidocEntry,
|
|
46
|
+
displayPath,
|
|
47
|
+
ditaEntry,
|
|
48
|
+
docbookEntry,
|
|
49
|
+
extractInput,
|
|
50
|
+
fileOf,
|
|
51
|
+
formatChoice,
|
|
52
|
+
htmlDlEntry,
|
|
53
|
+
loadSet,
|
|
54
|
+
loadTerms,
|
|
55
|
+
markdownEntry,
|
|
56
|
+
plural,
|
|
57
|
+
readerForConstruct,
|
|
58
|
+
reindent,
|
|
59
|
+
resolveTermRun,
|
|
60
|
+
rstEntry,
|
|
61
|
+
sharedLanguage,
|
|
62
|
+
valeConfigFor,
|
|
63
|
+
xmlAttribute,
|
|
64
|
+
xmlText
|
|
65
|
+
} from "./chunk-WQE54PNH.js";
|
|
66
|
+
import "./chunk-77AINBYM.js";
|
|
67
|
+
import "./chunk-IKEWBMCN.js";
|
|
68
|
+
import "./chunk-TU2WUBVZ.js";
|
|
69
|
+
import "./chunk-H2DLH2JI.js";
|
|
70
|
+
import {
|
|
71
|
+
registerTermLabelSource
|
|
72
|
+
} from "./chunk-CYWHFSRT.js";
|
|
73
|
+
import "./chunk-K3D6FPV6.js";
|
|
74
|
+
import {
|
|
75
|
+
collect,
|
|
76
|
+
readStdin,
|
|
77
|
+
splitList
|
|
78
|
+
} from "./chunk-V46OILVC.js";
|
|
79
|
+
import "./chunk-RLR4TKUZ.js";
|
|
80
|
+
import {
|
|
81
|
+
OMITTED_WHEN_CLEAN,
|
|
82
|
+
renderJunit,
|
|
83
|
+
renderSarif
|
|
84
|
+
} from "./chunk-C6OH6CNF.js";
|
|
85
|
+
import {
|
|
86
|
+
STDIN_TOKEN,
|
|
87
|
+
extractorByName,
|
|
88
|
+
extractorForExtension
|
|
89
|
+
} from "./chunk-JVTNMVYU.js";
|
|
90
|
+
import {
|
|
91
|
+
fail
|
|
92
|
+
} from "./chunk-YEUYKR62.js";
|
|
93
|
+
import {
|
|
94
|
+
shouldColor
|
|
95
|
+
} from "./chunk-RNVYGWIC.js";
|
|
96
|
+
import "./chunk-MNH7QO4Z.js";
|
|
97
|
+
import {
|
|
98
|
+
notice
|
|
99
|
+
} from "./chunk-Q35YIDBV.js";
|
|
100
|
+
import {
|
|
101
|
+
package_default
|
|
102
|
+
} from "./chunk-UZC7LDAQ.js";
|
|
103
|
+
import "./chunk-OWBRDNRZ.js";
|
|
104
|
+
import {
|
|
105
|
+
errorMessage
|
|
106
|
+
} from "./chunk-G3T47CFV.js";
|
|
107
|
+
import "./chunk-2H7UOFLK.js";
|
|
108
|
+
|
|
109
|
+
// src/term/cli.ts
|
|
110
|
+
import { Command } from "commander";
|
|
111
|
+
|
|
112
|
+
// src/term/core/writers/documents.ts
|
|
113
|
+
import { join } from "node:path";
|
|
114
|
+
|
|
115
|
+
// src/term/core/writers/page.ts
|
|
116
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
117
|
+
function pageMetadata(term) {
|
|
118
|
+
const { record } = term;
|
|
119
|
+
const metadata = { title: record.label };
|
|
120
|
+
if (record.abstract !== void 0) metadata["description"] = record.abstract;
|
|
121
|
+
metadata["type"] = "term";
|
|
122
|
+
metadata["id"] = term.id;
|
|
123
|
+
if (term.language !== void 0) metadata["language"] = term.language;
|
|
124
|
+
for (const field of TERM_FIELDS) {
|
|
125
|
+
const value = record[field];
|
|
126
|
+
if (value !== void 0) metadata[field] = value;
|
|
127
|
+
}
|
|
128
|
+
return metadata;
|
|
129
|
+
}
|
|
130
|
+
function metadataPage(format, term) {
|
|
131
|
+
const apply = extractorByName(format)?.apply;
|
|
132
|
+
if (apply === void 0) throw new TermError(`manni cannot write ${format} metadata.`);
|
|
133
|
+
return apply("---\n---\n", pageMetadata(term));
|
|
134
|
+
}
|
|
135
|
+
function yamlFrontmatter(metadata) {
|
|
136
|
+
return `---
|
|
137
|
+
${stringifyYaml(metadata, { lineWidth: 0 })}---
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
function metaContent(value) {
|
|
141
|
+
if (typeof value === "string" && !value.includes("\n")) {
|
|
142
|
+
try {
|
|
143
|
+
if (parseYaml(value) === value) return value;
|
|
144
|
+
} catch {
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return JSON.stringify(value);
|
|
148
|
+
}
|
|
149
|
+
function htmlPage(term) {
|
|
150
|
+
const metadata = pageMetadata(term);
|
|
151
|
+
const metas = Object.entries(metadata).filter(([key]) => key !== "title").map(([key, value]) => ` <meta name="${xmlAttribute(key)}" content="${xmlAttribute(metaContent(value))}">`);
|
|
152
|
+
return fileOf([
|
|
153
|
+
"<!DOCTYPE html>",
|
|
154
|
+
"<html>",
|
|
155
|
+
" <head>",
|
|
156
|
+
` <title>${xmlText(term.record.label)}</title>`,
|
|
157
|
+
...metas,
|
|
158
|
+
" </head>",
|
|
159
|
+
" <body></body>",
|
|
160
|
+
"</html>"
|
|
161
|
+
]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/term/core/writers/render-util.ts
|
|
165
|
+
var DEFAULT_LANGUAGE = "en";
|
|
166
|
+
function droppedFields(terms, held) {
|
|
167
|
+
const dropped = [];
|
|
168
|
+
for (const term of terms) {
|
|
169
|
+
for (const field of TERM_FIELDS) {
|
|
170
|
+
if (term.record[field] !== void 0 && !held.includes(field)) dropped.push({ id: term.id, field });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return dropped;
|
|
174
|
+
}
|
|
175
|
+
function requireShape(format, target, shape) {
|
|
176
|
+
if (target.shape === shape) return;
|
|
177
|
+
throw new TermError(
|
|
178
|
+
shape === "file" ? `-f ${format} writes one file, not a directory. Pass -o <file>.` : `-f ${format} writes a styles directory, not a file. Pass -o <styles directory>.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/term/core/writers/documents.ts
|
|
183
|
+
var UNSAFE_NAME = /[/\\:*?"<>|]/;
|
|
184
|
+
function pagePaths(terms, target, extension) {
|
|
185
|
+
const seen = /* @__PURE__ */ new Set();
|
|
186
|
+
return terms.map((term) => {
|
|
187
|
+
if (UNSAFE_NAME.test(term.id) || term.id === "." || term.id === "..") {
|
|
188
|
+
throw new TermError(`the id "${term.id}" cannot name a file. Give the term an id without path characters.`);
|
|
189
|
+
}
|
|
190
|
+
if (seen.has(term.id)) {
|
|
191
|
+
throw new TermError(`two terms have the id "${term.id}", and a directory holds one file per id.`);
|
|
192
|
+
}
|
|
193
|
+
seen.add(term.id);
|
|
194
|
+
return join(target.path, `${term.id}${extension}`);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function documentWriter(spec) {
|
|
198
|
+
const { directory } = spec;
|
|
199
|
+
return {
|
|
200
|
+
format: spec.format,
|
|
201
|
+
shapes: directory === void 0 ? ["file"] : ["file", "directory"],
|
|
202
|
+
holds: (shape) => shape === "directory" && directory !== void 0 ? directory.holds : spec.file.holds,
|
|
203
|
+
render(terms, target) {
|
|
204
|
+
if (target.shape === "directory" && directory !== void 0) {
|
|
205
|
+
const paths = pagePaths(terms, target, spec.extension);
|
|
206
|
+
return {
|
|
207
|
+
files: terms.map((term, i) => ({ path: paths[i] ?? "", content: directory.render(term) })),
|
|
208
|
+
removals: [],
|
|
209
|
+
dropped: droppedFields(terms, directory.holds),
|
|
210
|
+
skipped: []
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
requireShape(spec.format, target, "file");
|
|
214
|
+
const written = spec.file.needsDefinition ? terms.filter((t) => t.record.definition !== void 0) : terms;
|
|
215
|
+
return {
|
|
216
|
+
files: [{ path: target.path, content: spec.file.render(written) }],
|
|
217
|
+
removals: [],
|
|
218
|
+
dropped: droppedFields(written, spec.file.holds),
|
|
219
|
+
skipped: terms.filter((t) => !written.includes(t)).map((t) => t.id)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function entriesBlock(entries, indent = "") {
|
|
225
|
+
return entries.flatMap((entry, i) => [
|
|
226
|
+
...i === 0 ? [] : [""],
|
|
227
|
+
`${indent}${reindent(entry, indent, "\n")}`
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
function markupBlock(entries, indent) {
|
|
231
|
+
return entries.map((entry) => `${indent}${reindent(entry, indent, "\n")}`);
|
|
232
|
+
}
|
|
233
|
+
function textPage(format) {
|
|
234
|
+
return (term) => metadataPage(format, term);
|
|
235
|
+
}
|
|
236
|
+
function listFrontmatter(terms, type) {
|
|
237
|
+
const metadata = {};
|
|
238
|
+
if (type) metadata["type"] = "term-set";
|
|
239
|
+
const language = sharedLanguage(terms);
|
|
240
|
+
if (language !== void 0) metadata["language"] = language;
|
|
241
|
+
if (Object.keys(metadata).length === 0) return [];
|
|
242
|
+
return [yamlFrontmatter(metadata).replace(/\n$/, ""), ""];
|
|
243
|
+
}
|
|
244
|
+
function markdownList(terms) {
|
|
245
|
+
return fileOf([...listFrontmatter(terms, true), ...entriesBlock(terms.map((t) => markdownEntry(t.record)))]);
|
|
246
|
+
}
|
|
247
|
+
var XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>';
|
|
248
|
+
function langAttribute(language) {
|
|
249
|
+
return language === void 0 ? "" : ` xml:lang="${xmlAttribute(language)}"`;
|
|
250
|
+
}
|
|
251
|
+
var markdownWriter = documentWriter({
|
|
252
|
+
format: "markdown",
|
|
253
|
+
extension: ".md",
|
|
254
|
+
file: { holds: LIST_HOLDS, render: markdownList, needsDefinition: true },
|
|
255
|
+
directory: { holds: TERM_FIELDS, render: textPage("markdown") }
|
|
256
|
+
});
|
|
257
|
+
var mdxWriter = documentWriter({
|
|
258
|
+
format: "mdx",
|
|
259
|
+
extension: ".mdx",
|
|
260
|
+
file: { holds: LIST_HOLDS, render: markdownList, needsDefinition: true },
|
|
261
|
+
directory: { holds: TERM_FIELDS, render: textPage("mdx") }
|
|
262
|
+
});
|
|
263
|
+
var asciidocWriter = documentWriter({
|
|
264
|
+
format: "asciidoc",
|
|
265
|
+
extension: ".adoc",
|
|
266
|
+
file: {
|
|
267
|
+
holds: LIST_HOLDS,
|
|
268
|
+
needsDefinition: true,
|
|
269
|
+
render: (terms) => fileOf([...listFrontmatter(terms, false), "[glossary]", ...entriesBlock(terms.map((t) => asciidocEntry(t.record)))])
|
|
270
|
+
},
|
|
271
|
+
directory: { holds: TERM_FIELDS, render: textPage("asciidoc") }
|
|
272
|
+
});
|
|
273
|
+
var rstWriter = documentWriter({
|
|
274
|
+
format: "rst",
|
|
275
|
+
extension: ".rst",
|
|
276
|
+
file: {
|
|
277
|
+
holds: LIST_HOLDS,
|
|
278
|
+
needsDefinition: true,
|
|
279
|
+
render: (terms) => fileOf([
|
|
280
|
+
...listFrontmatter(terms, false),
|
|
281
|
+
".. glossary::",
|
|
282
|
+
"",
|
|
283
|
+
...entriesBlock(
|
|
284
|
+
terms.map((t) => rstEntry(t.record)),
|
|
285
|
+
" "
|
|
286
|
+
)
|
|
287
|
+
])
|
|
288
|
+
},
|
|
289
|
+
directory: { holds: TERM_FIELDS, render: textPage("rst") }
|
|
290
|
+
});
|
|
291
|
+
var htmlWriter = documentWriter({
|
|
292
|
+
format: "html",
|
|
293
|
+
extension: ".html",
|
|
294
|
+
file: {
|
|
295
|
+
holds: LIST_HOLDS,
|
|
296
|
+
needsDefinition: true,
|
|
297
|
+
render: (terms) => {
|
|
298
|
+
const language = sharedLanguage(terms);
|
|
299
|
+
return fileOf([
|
|
300
|
+
"<!DOCTYPE html>",
|
|
301
|
+
"<html>",
|
|
302
|
+
" <head>",
|
|
303
|
+
" <title>Glossary</title>",
|
|
304
|
+
' <meta name="type" content="term-set">',
|
|
305
|
+
...language === void 0 ? [] : [` <meta name="language" content="${xmlAttribute(language)}">`],
|
|
306
|
+
" </head>",
|
|
307
|
+
" <body>",
|
|
308
|
+
" <dl>",
|
|
309
|
+
...markupBlock(terms.map(htmlDlEntry), " "),
|
|
310
|
+
" </dl>",
|
|
311
|
+
" </body>",
|
|
312
|
+
"</html>"
|
|
313
|
+
]);
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
directory: { holds: TERM_FIELDS, render: htmlPage }
|
|
317
|
+
});
|
|
318
|
+
var ditaWriter = documentWriter({
|
|
319
|
+
format: "dita",
|
|
320
|
+
extension: ".dita",
|
|
321
|
+
file: {
|
|
322
|
+
holds: DITA_HOLDS,
|
|
323
|
+
needsDefinition: false,
|
|
324
|
+
render: (terms) => fileOf([
|
|
325
|
+
XML_DECLARATION,
|
|
326
|
+
'<!DOCTYPE glossgroup PUBLIC "-//OASIS//DTD DITA 2.0 Glossary Group//EN" "glossgroup.dtd">',
|
|
327
|
+
`<glossgroup id="glossary"${langAttribute(sharedLanguage(terms))}>`,
|
|
328
|
+
" <title>Glossary</title>",
|
|
329
|
+
...markupBlock(terms.map((t) => ditaEntry(t)), " "),
|
|
330
|
+
"</glossgroup>"
|
|
331
|
+
])
|
|
332
|
+
},
|
|
333
|
+
directory: {
|
|
334
|
+
holds: DITA_HOLDS,
|
|
335
|
+
render: (term) => fileOf([
|
|
336
|
+
XML_DECLARATION,
|
|
337
|
+
'<!DOCTYPE glossentry PUBLIC "-//OASIS//DTD DITA 2.0 Glossary Entry//EN" "glossentry.dtd">',
|
|
338
|
+
ditaEntry(term, term.language)
|
|
339
|
+
])
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
var docbookWriter = documentWriter({
|
|
343
|
+
format: "docbook",
|
|
344
|
+
extension: ".xml",
|
|
345
|
+
file: {
|
|
346
|
+
holds: DOCBOOK_HOLDS,
|
|
347
|
+
needsDefinition: false,
|
|
348
|
+
render: (terms) => fileOf([
|
|
349
|
+
XML_DECLARATION,
|
|
350
|
+
`<glossary xmlns="http://docbook.org/ns/docbook" version="5.0"${langAttribute(sharedLanguage(terms))}>`,
|
|
351
|
+
" <title>Glossary</title>",
|
|
352
|
+
...markupBlock(terms.map(docbookEntry), " "),
|
|
353
|
+
"</glossary>"
|
|
354
|
+
])
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
var DOCUMENT_WRITERS = [
|
|
358
|
+
markdownWriter,
|
|
359
|
+
mdxWriter,
|
|
360
|
+
asciidocWriter,
|
|
361
|
+
rstWriter,
|
|
362
|
+
htmlWriter,
|
|
363
|
+
ditaWriter,
|
|
364
|
+
docbookWriter
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
// src/term/core/writers/csv.ts
|
|
368
|
+
var LANGUAGE_COLUMNS = [
|
|
369
|
+
["label", "Term"],
|
|
370
|
+
["definition", "Definition"],
|
|
371
|
+
["abstract", "Abstract"],
|
|
372
|
+
["alt-labels", "Alt labels"],
|
|
373
|
+
["hidden-labels", "Hidden labels"],
|
|
374
|
+
["scope-note", "Scope note"]
|
|
375
|
+
];
|
|
376
|
+
var NEUTRAL_COLUMNS = [
|
|
377
|
+
["broader", "Broader"],
|
|
378
|
+
["narrower", "Narrower"],
|
|
379
|
+
["related-terms", "Related terms"],
|
|
380
|
+
["see", "See"]
|
|
381
|
+
];
|
|
382
|
+
var LIST_SEPARATOR = " | ";
|
|
383
|
+
function cell(value) {
|
|
384
|
+
return /[",\r\n]/.test(value) ? `"${value.replaceAll('"', '""')}"` : value;
|
|
385
|
+
}
|
|
386
|
+
function fieldText(term, field) {
|
|
387
|
+
const value = term.record[field];
|
|
388
|
+
if (value === void 0) return "";
|
|
389
|
+
return typeof value === "string" ? value : value.join(LIST_SEPARATOR);
|
|
390
|
+
}
|
|
391
|
+
var csvWriter = {
|
|
392
|
+
format: "csv",
|
|
393
|
+
shapes: ["file"],
|
|
394
|
+
holds: (_shape) => TERM_FIELDS,
|
|
395
|
+
render(terms, target) {
|
|
396
|
+
requireShape("csv", target, "file");
|
|
397
|
+
const languages = [];
|
|
398
|
+
for (const term of terms) {
|
|
399
|
+
const language = term.language ?? DEFAULT_LANGUAGE;
|
|
400
|
+
if (!languages.includes(language)) languages.push(language);
|
|
401
|
+
}
|
|
402
|
+
const header = [
|
|
403
|
+
"ID",
|
|
404
|
+
...languages.flatMap((language) => LANGUAGE_COLUMNS.map(([, name]) => `${name} [${language}]`)),
|
|
405
|
+
...NEUTRAL_COLUMNS.map(([, name]) => name)
|
|
406
|
+
];
|
|
407
|
+
const rows = terms.map((term) => {
|
|
408
|
+
const own = term.language ?? DEFAULT_LANGUAGE;
|
|
409
|
+
return [
|
|
410
|
+
term.id,
|
|
411
|
+
...languages.flatMap(
|
|
412
|
+
(language) => LANGUAGE_COLUMNS.map(([field]) => language === own ? fieldText(term, field) : "")
|
|
413
|
+
),
|
|
414
|
+
...NEUTRAL_COLUMNS.map(([field]) => fieldText(term, field))
|
|
415
|
+
];
|
|
416
|
+
});
|
|
417
|
+
const content = [header, ...rows].map((row) => `${row.map(cell).join(",")}
|
|
418
|
+
`).join("");
|
|
419
|
+
return {
|
|
420
|
+
files: [{ path: target.path, content }],
|
|
421
|
+
removals: [],
|
|
422
|
+
dropped: droppedFields(terms, TERM_FIELDS),
|
|
423
|
+
skipped: []
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// src/term/core/writers/json.ts
|
|
429
|
+
var jsonWriter = {
|
|
430
|
+
format: "json",
|
|
431
|
+
shapes: ["file"],
|
|
432
|
+
holds: (_shape) => TERM_FIELDS,
|
|
433
|
+
render(terms, target) {
|
|
434
|
+
requireShape("json", target, "file");
|
|
435
|
+
const entries = terms.map((term) => {
|
|
436
|
+
const entry = { id: term.id };
|
|
437
|
+
if (term.language !== void 0) entry["language"] = term.language;
|
|
438
|
+
for (const field of TERM_FIELDS) {
|
|
439
|
+
const value = term.record[field];
|
|
440
|
+
if (value !== void 0) entry[field] = value;
|
|
441
|
+
}
|
|
442
|
+
return entry;
|
|
443
|
+
});
|
|
444
|
+
const content = `${JSON.stringify({ terms: entries }, null, 2)}
|
|
445
|
+
`;
|
|
446
|
+
return {
|
|
447
|
+
files: [{ path: target.path, content }],
|
|
448
|
+
removals: [],
|
|
449
|
+
dropped: droppedFields(terms, TERM_FIELDS),
|
|
450
|
+
skipped: []
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/term/core/writers/skos.ts
|
|
456
|
+
var HOLDS = [
|
|
457
|
+
"label",
|
|
458
|
+
"definition",
|
|
459
|
+
"alt-labels",
|
|
460
|
+
"hidden-labels",
|
|
461
|
+
"broader",
|
|
462
|
+
"narrower",
|
|
463
|
+
"related-terms",
|
|
464
|
+
"scope-note"
|
|
465
|
+
];
|
|
466
|
+
var CONTEXT = {
|
|
467
|
+
skos: "http://www.w3.org/2004/02/skos/core#",
|
|
468
|
+
dcterms: "http://purl.org/dc/terms/"
|
|
469
|
+
};
|
|
470
|
+
var SCHEME_ID = "";
|
|
471
|
+
function literal(term, value) {
|
|
472
|
+
return term.language === void 0 ? value : { "@value": value, "@language": term.language };
|
|
473
|
+
}
|
|
474
|
+
function resolve(terms, self, value) {
|
|
475
|
+
const wanted = value.toLowerCase();
|
|
476
|
+
const others = terms.filter((t) => t !== self);
|
|
477
|
+
return others.find((t) => t.record.label.toLowerCase() === wanted) ?? others.find((t) => t.id.toLowerCase() === wanted);
|
|
478
|
+
}
|
|
479
|
+
function references(terms, self, values) {
|
|
480
|
+
const ids = [];
|
|
481
|
+
for (const value of values ?? []) {
|
|
482
|
+
const target = resolve(terms, self, value);
|
|
483
|
+
if (target !== void 0 && !ids.includes(target.id)) ids.push(target.id);
|
|
484
|
+
}
|
|
485
|
+
return ids.map((id) => ({ "@id": `#${id}` }));
|
|
486
|
+
}
|
|
487
|
+
function concept(terms, term) {
|
|
488
|
+
const { record } = term;
|
|
489
|
+
const node = {
|
|
490
|
+
"@id": `#${term.id}`,
|
|
491
|
+
"@type": "skos:Concept",
|
|
492
|
+
"skos:inScheme": { "@id": SCHEME_ID },
|
|
493
|
+
"skos:prefLabel": literal(term, record.label)
|
|
494
|
+
};
|
|
495
|
+
if (record["alt-labels"] !== void 0) node["skos:altLabel"] = record["alt-labels"].map((v) => literal(term, v));
|
|
496
|
+
if (record["hidden-labels"] !== void 0) {
|
|
497
|
+
node["skos:hiddenLabel"] = record["hidden-labels"].map((v) => literal(term, v));
|
|
498
|
+
}
|
|
499
|
+
if (record.definition !== void 0) node["skos:definition"] = literal(term, record.definition);
|
|
500
|
+
if (record["scope-note"] !== void 0) node["skos:scopeNote"] = literal(term, record["scope-note"]);
|
|
501
|
+
const relations = [
|
|
502
|
+
["skos:broader", record.broader],
|
|
503
|
+
["skos:narrower", record.narrower],
|
|
504
|
+
["skos:related", record["related-terms"]]
|
|
505
|
+
];
|
|
506
|
+
for (const [key, values] of relations) {
|
|
507
|
+
const refs = references(terms, term, values);
|
|
508
|
+
if (refs.length > 0) node[key] = refs;
|
|
509
|
+
}
|
|
510
|
+
return node;
|
|
511
|
+
}
|
|
512
|
+
var skosWriter = {
|
|
513
|
+
format: "skos",
|
|
514
|
+
shapes: ["file"],
|
|
515
|
+
holds: (_shape) => HOLDS,
|
|
516
|
+
render(terms, target) {
|
|
517
|
+
requireShape("skos", target, "file");
|
|
518
|
+
const document = {
|
|
519
|
+
"@context": CONTEXT,
|
|
520
|
+
"@graph": [
|
|
521
|
+
{
|
|
522
|
+
"@id": SCHEME_ID,
|
|
523
|
+
"@type": "skos:ConceptScheme",
|
|
524
|
+
"dcterms:description": "Generated by manni term write."
|
|
525
|
+
},
|
|
526
|
+
...terms.map((term) => concept(terms, term))
|
|
527
|
+
]
|
|
528
|
+
};
|
|
529
|
+
const content = `${JSON.stringify(document, null, 2)}
|
|
530
|
+
`;
|
|
531
|
+
return {
|
|
532
|
+
files: [{ path: target.path, content }],
|
|
533
|
+
removals: [],
|
|
534
|
+
dropped: droppedFields(terms, HOLDS),
|
|
535
|
+
skipped: []
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
// src/term/core/writers/tbx.ts
|
|
541
|
+
var HOLDS2 = ["label", "definition", "alt-labels", "hidden-labels"];
|
|
542
|
+
function text(value) {
|
|
543
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
544
|
+
}
|
|
545
|
+
function attribute(value) {
|
|
546
|
+
return text(value).replaceAll('"', """);
|
|
547
|
+
}
|
|
548
|
+
function tig(designation, status) {
|
|
549
|
+
return [
|
|
550
|
+
" <tig>",
|
|
551
|
+
` <term>${text(designation)}</term>`,
|
|
552
|
+
` <termNote type="administrativeStatus">${status}</termNote>`,
|
|
553
|
+
" </tig>"
|
|
554
|
+
];
|
|
555
|
+
}
|
|
556
|
+
function termEntry(term) {
|
|
557
|
+
const { record } = term;
|
|
558
|
+
return [
|
|
559
|
+
` <termEntry id="${attribute(term.id)}">`,
|
|
560
|
+
...record.definition === void 0 ? [] : [` <descrip type="definition">${text(record.definition)}</descrip>`],
|
|
561
|
+
` <langSet xml:lang="${attribute(term.language ?? DEFAULT_LANGUAGE)}">`,
|
|
562
|
+
...tig(record.label, "preferredTerm-admn-sts"),
|
|
563
|
+
...(record["alt-labels"] ?? []).flatMap((label) => tig(label, "admittedTerm-admn-sts")),
|
|
564
|
+
...(record["hidden-labels"] ?? []).flatMap((label) => tig(label, "deprecatedTerm-admn-sts")),
|
|
565
|
+
" </langSet>",
|
|
566
|
+
" </termEntry>"
|
|
567
|
+
];
|
|
568
|
+
}
|
|
569
|
+
var tbxWriter = {
|
|
570
|
+
format: "tbx",
|
|
571
|
+
shapes: ["file"],
|
|
572
|
+
holds: (_shape) => HOLDS2,
|
|
573
|
+
render(terms, target) {
|
|
574
|
+
requireShape("tbx", target, "file");
|
|
575
|
+
const language = terms[0]?.language ?? DEFAULT_LANGUAGE;
|
|
576
|
+
const lines = [
|
|
577
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
578
|
+
`<martif type="TBX" xml:lang="${attribute(language)}">`,
|
|
579
|
+
" <martifHeader>",
|
|
580
|
+
" <fileDesc>",
|
|
581
|
+
" <sourceDesc>",
|
|
582
|
+
" <p>manni term write</p>",
|
|
583
|
+
" </sourceDesc>",
|
|
584
|
+
" </fileDesc>",
|
|
585
|
+
" </martifHeader>",
|
|
586
|
+
" <text>",
|
|
587
|
+
" <body>",
|
|
588
|
+
...terms.flatMap(termEntry),
|
|
589
|
+
" </body>",
|
|
590
|
+
" </text>",
|
|
591
|
+
"</martif>"
|
|
592
|
+
];
|
|
593
|
+
const content = lines.map((line) => `${line}
|
|
594
|
+
`).join("");
|
|
595
|
+
return {
|
|
596
|
+
files: [{ path: target.path, content }],
|
|
597
|
+
removals: [],
|
|
598
|
+
dropped: droppedFields(terms, HOLDS2),
|
|
599
|
+
skipped: []
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/term/core/writers/vale.ts
|
|
605
|
+
import { isAbsolute, join as join2, relative, sep } from "node:path";
|
|
606
|
+
import { stringify } from "yaml";
|
|
607
|
+
var VALE_MARKER = "# Generated by manni term write. Edit the terms, not this file.";
|
|
608
|
+
var STYLE = "Terms";
|
|
609
|
+
var HOLDS3 = ["label", "alt-labels", "hidden-labels"];
|
|
610
|
+
var OWN_FILES = ["Casing", "Lowercase", "SentenceStart", "Deprecated"];
|
|
611
|
+
var REGEX_METACHARACTERS = /[\\^$.|?*+()[\]{}]/g;
|
|
612
|
+
function escapeRegex(value) {
|
|
613
|
+
return value.replace(REGEX_METACHARACTERS, "\\$&");
|
|
614
|
+
}
|
|
615
|
+
function swapValue(value) {
|
|
616
|
+
return value.search(REGEX_METACHARACTERS) === -1 ? value : escapeRegex(value);
|
|
617
|
+
}
|
|
618
|
+
function scalar(value) {
|
|
619
|
+
if (/[\r\n]/.test(value)) return JSON.stringify(value);
|
|
620
|
+
const out = stringify(value, { version: "1.1", lineWidth: 0 });
|
|
621
|
+
return out.endsWith("\n") ? out.slice(0, -1) : out;
|
|
622
|
+
}
|
|
623
|
+
function doubleQuoted(value) {
|
|
624
|
+
const escaped = value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r");
|
|
625
|
+
return `"${escaped}"`;
|
|
626
|
+
}
|
|
627
|
+
function singleQuoted(value) {
|
|
628
|
+
return `'${value.replaceAll("'", "''").replaceAll(/[\r\n]+/g, " ")}'`;
|
|
629
|
+
}
|
|
630
|
+
function messageText(value) {
|
|
631
|
+
return value.replaceAll("%", "%%");
|
|
632
|
+
}
|
|
633
|
+
function casingOf(value) {
|
|
634
|
+
if (!/[\p{Lu}\p{Ll}\p{Lt}]/u.test(value)) return "none";
|
|
635
|
+
const letters = value.match(new RegExp("\\p{L}", "gu"))?.length ?? 0;
|
|
636
|
+
if (letters >= 2 && !/[\p{Ll}\p{Lt}]/u.test(value)) return "all-caps";
|
|
637
|
+
if (!/[\p{Lu}\p{Lt}]/u.test(value)) return "lowercase";
|
|
638
|
+
if (new RegExp("^\\p{Ll}", "u").test(value)) return "lowercase-initial";
|
|
639
|
+
return "mixed";
|
|
640
|
+
}
|
|
641
|
+
function sentenceStartPattern(value) {
|
|
642
|
+
const [first = "", ...rest] = Array.from(value);
|
|
643
|
+
const upper = first.toUpperCase();
|
|
644
|
+
const head = upper !== first && Array.from(upper).length === 1 ? `[${upper}${first}]` : escapeRegex(first);
|
|
645
|
+
return `${head}${escapeRegex(rest.join(""))}`;
|
|
646
|
+
}
|
|
647
|
+
var Swaps = class {
|
|
648
|
+
entries = /* @__PURE__ */ new Map();
|
|
649
|
+
add(key, value) {
|
|
650
|
+
if (!this.entries.has(key)) this.entries.set(key, value);
|
|
651
|
+
}
|
|
652
|
+
get size() {
|
|
653
|
+
return this.entries.size;
|
|
654
|
+
}
|
|
655
|
+
lines() {
|
|
656
|
+
return [...this.entries].map(([key, value]) => ` ${scalar(key)}: ${scalar(value)}`);
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
var ASCII_WORD = /^[A-Za-z0-9_]$/;
|
|
660
|
+
function bounded(pattern, text2) {
|
|
661
|
+
const chars = Array.from(text2);
|
|
662
|
+
const edge = (char) => char !== void 0 && ASCII_WORD.test(char) ? "\\b" : "\\B";
|
|
663
|
+
return `${edge(chars[0])}${pattern}${edge(chars.at(-1))}`;
|
|
664
|
+
}
|
|
665
|
+
function substitution(message, level, options, swaps) {
|
|
666
|
+
return [
|
|
667
|
+
VALE_MARKER,
|
|
668
|
+
"extends: substitution",
|
|
669
|
+
`message: ${doubleQuoted(message)}`,
|
|
670
|
+
`level: ${level}`,
|
|
671
|
+
"ignorecase: true",
|
|
672
|
+
"nonword: true",
|
|
673
|
+
...options,
|
|
674
|
+
"swap:",
|
|
675
|
+
...swaps.lines()
|
|
676
|
+
].map((line) => `${line}
|
|
677
|
+
`).join("");
|
|
678
|
+
}
|
|
679
|
+
function acronymRule(acronym, label) {
|
|
680
|
+
const expansion = escapeRegex(label).replaceAll(/[\r\n]+/g, " ");
|
|
681
|
+
return [
|
|
682
|
+
VALE_MARKER,
|
|
683
|
+
"extends: conditional",
|
|
684
|
+
`message: ${doubleQuoted(messageText(`Spell out '${acronym}' on first use, as '${label} (${acronym})'.`))}`,
|
|
685
|
+
"level: warning",
|
|
686
|
+
"ignorecase: false",
|
|
687
|
+
`first: ${singleQuoted(`\\b(${escapeRegex(acronym)})\\b`)}`,
|
|
688
|
+
`second: ${singleQuoted(`(?i)${expansion} \\((${escapeRegex(acronym)})\\)`)}`
|
|
689
|
+
].map((line) => `${line}
|
|
690
|
+
`).join("");
|
|
691
|
+
}
|
|
692
|
+
function acronymFileName(acronym) {
|
|
693
|
+
return acronym.replaceAll(/[^\p{L}\p{N}_-]/gu, "-");
|
|
694
|
+
}
|
|
695
|
+
function firstLine(content) {
|
|
696
|
+
const end = content.search(/\r?\n/);
|
|
697
|
+
return end === -1 ? content : content.slice(0, end);
|
|
698
|
+
}
|
|
699
|
+
var valeWriter = {
|
|
700
|
+
format: "vale",
|
|
701
|
+
shapes: ["directory"],
|
|
702
|
+
holds: (_shape) => HOLDS3,
|
|
703
|
+
render(terms, target, context) {
|
|
704
|
+
requireShape("vale", target, "directory");
|
|
705
|
+
const termsDir = join2(target.path, STYLE);
|
|
706
|
+
const marked = [];
|
|
707
|
+
for (const [path, content] of context.existing) {
|
|
708
|
+
const rel = relative(termsDir, path);
|
|
709
|
+
if (rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) continue;
|
|
710
|
+
if (firstLine(content) !== VALE_MARKER) {
|
|
711
|
+
throw new TermError(`${path} was not written by manni. Move it, or pass -o <styles directory>.`);
|
|
712
|
+
}
|
|
713
|
+
if (!rel.includes(sep) && !rel.includes("/")) marked.push(path);
|
|
714
|
+
}
|
|
715
|
+
const casing = new Swaps();
|
|
716
|
+
const lowercase = new Swaps();
|
|
717
|
+
const sentenceStart = new Swaps();
|
|
718
|
+
const deprecated = new Swaps();
|
|
719
|
+
const acronyms = [];
|
|
720
|
+
const labels = new Set(terms.map((t) => t.record.label));
|
|
721
|
+
for (const term of terms) {
|
|
722
|
+
const { label } = term.record;
|
|
723
|
+
const labelIsAllCaps = casingOf(label) === "all-caps";
|
|
724
|
+
for (const designation of [label, ...term.record["alt-labels"] ?? []]) {
|
|
725
|
+
const kind = casingOf(designation);
|
|
726
|
+
if (kind === "lowercase") lowercase.add(bounded(escapeRegex(designation), designation), sentenceStartPattern(designation));
|
|
727
|
+
if (kind === "lowercase-initial") {
|
|
728
|
+
sentenceStart.add(bounded(escapeRegex(designation.toLowerCase()), designation), sentenceStartPattern(designation));
|
|
729
|
+
}
|
|
730
|
+
if (kind === "mixed") casing.add(bounded(escapeRegex(designation.toLowerCase()), designation), swapValue(designation));
|
|
731
|
+
if (kind !== "all-caps" || designation === label || labelIsAllCaps || labels.has(designation)) continue;
|
|
732
|
+
const name = acronymFileName(designation);
|
|
733
|
+
const own = OWN_FILES.find((file) => file.toLowerCase() === name.toLowerCase());
|
|
734
|
+
if (own !== void 0) {
|
|
735
|
+
throw new TermError(`the acronym "${designation}" would replace Terms/${own}.yml. Rename the alt-label.`);
|
|
736
|
+
}
|
|
737
|
+
const taken = acronyms.find((a) => a.name.toLowerCase() === name.toLowerCase());
|
|
738
|
+
if (taken?.acronym === designation) continue;
|
|
739
|
+
if (taken !== void 0) {
|
|
740
|
+
throw new TermError(
|
|
741
|
+
`the acronyms "${taken.acronym}" and "${designation}" would both write Terms/${taken.name}.yml. Rename one.`
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
acronyms.push({ acronym: designation, name, content: acronymRule(designation, label) });
|
|
745
|
+
}
|
|
746
|
+
for (const hidden of term.record["hidden-labels"] ?? []) {
|
|
747
|
+
deprecated.add(bounded(escapeRegex(hidden), hidden), swapValue(label));
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const files = [];
|
|
751
|
+
const add = (name, content) => {
|
|
752
|
+
files.push({ path: join2(termsDir, `${name}.yml`), content });
|
|
753
|
+
};
|
|
754
|
+
if (casing.size > 0) {
|
|
755
|
+
add("Casing", substitution("Use '%s' instead of '%s'.", "error", ["vocab: false"], casing));
|
|
756
|
+
}
|
|
757
|
+
if (lowercase.size > 0) {
|
|
758
|
+
add(
|
|
759
|
+
"Lowercase",
|
|
760
|
+
substitution("Write '%[2]s' in lowercase, except to start a sentence.", "error", ["vocab: false"], lowercase)
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
if (sentenceStart.size > 0) {
|
|
764
|
+
add(
|
|
765
|
+
"SentenceStart",
|
|
766
|
+
substitution(
|
|
767
|
+
"Write '%[2]s' as the term list spells it, with a capital only to start a sentence.",
|
|
768
|
+
"error",
|
|
769
|
+
["vocab: false"],
|
|
770
|
+
sentenceStart
|
|
771
|
+
)
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
if (deprecated.size > 0) {
|
|
775
|
+
add("Deprecated", substitution("Use '%s' instead of '%s'.", "warning", ["matchcase: true"], deprecated));
|
|
776
|
+
}
|
|
777
|
+
for (const acronym of acronyms) add(acronym.name, acronym.content);
|
|
778
|
+
const produced = new Set(files.map((f) => relative(termsDir, f.path).toLowerCase()));
|
|
779
|
+
const removals = marked.filter((path) => !produced.has(relative(termsDir, path).toLowerCase())).sort();
|
|
780
|
+
return { files, removals, dropped: droppedFields(terms, HOLDS3), skipped: [] };
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// src/term/core/writers/interchange.ts
|
|
785
|
+
var INTERCHANGE_WRITERS = [tbxWriter, skosWriter, csvWriter, jsonWriter, valeWriter];
|
|
786
|
+
|
|
787
|
+
// src/term/core/writers/index.ts
|
|
788
|
+
var TERM_WRITERS = [...DOCUMENT_WRITERS, ...INTERCHANGE_WRITERS];
|
|
789
|
+
|
|
790
|
+
// src/term/commands/formats.ts
|
|
791
|
+
var FORMATS_FORMATS = ["pretty", "json"];
|
|
792
|
+
var DOCUMENT_RENDERS = {
|
|
793
|
+
markdown: { input: "markdown", shapes: { directory: "page", file: "markdown-deflist" } },
|
|
794
|
+
mdx: { input: "mdx", shapes: { directory: "page", file: "markdown-deflist" } },
|
|
795
|
+
asciidoc: { input: "asciidoc", shapes: { directory: "page", file: "asciidoc-glossary" } },
|
|
796
|
+
rst: { input: "rst", shapes: { directory: "page", file: "rst-glossary" } },
|
|
797
|
+
html: { input: "html", shapes: { directory: "page", file: "html-dl" } },
|
|
798
|
+
dita: { input: "xml", shapes: { directory: "dita-glossentry", file: "dita-glossgroup" } },
|
|
799
|
+
docbook: { input: "xml", shapes: { file: "docbook-glossary" } }
|
|
800
|
+
};
|
|
801
|
+
var INTERCHANGE_LABELS = {
|
|
802
|
+
tbx: "TBX v2 Core",
|
|
803
|
+
skos: "JSON-LD",
|
|
804
|
+
vale: "style"
|
|
805
|
+
};
|
|
806
|
+
function rendered(writers, format, construct) {
|
|
807
|
+
return writers.some((writer) => {
|
|
808
|
+
const render = DOCUMENT_RENDERS[writer.format];
|
|
809
|
+
if (render?.input !== format) return false;
|
|
810
|
+
return writer.shapes.some((shape) => render.shapes[shape] === construct);
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
function listFormats(opts = {}) {
|
|
814
|
+
const readers = opts.readers ?? TERM_READERS;
|
|
815
|
+
const writers = opts.writers ?? TERM_WRITERS;
|
|
816
|
+
const formats = [];
|
|
817
|
+
for (const reader of readers) {
|
|
818
|
+
for (const format of reader.formats) if (!formats.includes(format)) formats.push(format);
|
|
819
|
+
}
|
|
820
|
+
const rows = [];
|
|
821
|
+
for (const format of formats) {
|
|
822
|
+
for (const reader of readers.filter((r) => r.formats.includes(format))) {
|
|
823
|
+
rows.push({
|
|
824
|
+
format,
|
|
825
|
+
construct: reader.construct,
|
|
826
|
+
label: reader.label,
|
|
827
|
+
read: true,
|
|
828
|
+
write: reader.apply !== void 0 || rendered(writers, format, reader.construct)
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
for (const writer of writers) {
|
|
833
|
+
if (DOCUMENT_RENDERS[writer.format] !== void 0) continue;
|
|
834
|
+
rows.push({ format: writer.format, label: INTERCHANGE_LABELS[writer.format] ?? "", read: false, write: true });
|
|
835
|
+
}
|
|
836
|
+
return rows;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/term/commands/get.ts
|
|
840
|
+
var GET_FORMATS = ["pretty", "json"];
|
|
841
|
+
function findTerm(terms, query) {
|
|
842
|
+
const folded = query.toLowerCase();
|
|
843
|
+
return terms.find((t) => t.id === query) ?? terms.find((t) => t.record.label.toLowerCase() === folded) ?? terms.find((t) => (t.record["alt-labels"] ?? []).some((alt) => alt.toLowerCase() === folded));
|
|
844
|
+
}
|
|
845
|
+
function editDistance(a, b) {
|
|
846
|
+
let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
847
|
+
for (let i = 1; i <= a.length; i++) {
|
|
848
|
+
const row = [i];
|
|
849
|
+
for (let j = 1; j <= b.length; j++) {
|
|
850
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
851
|
+
row.push(Math.min((row[j - 1] ?? 0) + 1, (prev[j] ?? 0) + 1, (prev[j - 1] ?? 0) + cost));
|
|
852
|
+
}
|
|
853
|
+
prev = row;
|
|
854
|
+
}
|
|
855
|
+
return prev[b.length] ?? 0;
|
|
856
|
+
}
|
|
857
|
+
function closestName(query, names) {
|
|
858
|
+
const limit = Math.max(2, Math.floor(query.length / 3));
|
|
859
|
+
let best;
|
|
860
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
861
|
+
for (const name of names) {
|
|
862
|
+
const distance = editDistance(query, name);
|
|
863
|
+
if (distance < bestDistance) {
|
|
864
|
+
best = name;
|
|
865
|
+
bestDistance = distance;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return bestDistance <= limit ? best : void 0;
|
|
869
|
+
}
|
|
870
|
+
async function runGet(opts) {
|
|
871
|
+
const { set } = await loadTerms(opts);
|
|
872
|
+
const found = findTerm(set.terms, opts.term);
|
|
873
|
+
if (found !== void 0) return found;
|
|
874
|
+
const nearest = closestName(
|
|
875
|
+
opts.term,
|
|
876
|
+
set.terms.flatMap((t) => [t.id, t.record.label])
|
|
877
|
+
);
|
|
878
|
+
const hint = nearest === void 0 ? "." : `; did you mean "${nearest}"?`;
|
|
879
|
+
throw new TermError(`no term "${opts.term}". ${plural(set.terms.length, "term")}${hint}`);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/term/core/lint.ts
|
|
883
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
884
|
+
import { tmpdir } from "node:os";
|
|
885
|
+
import { join as join3 } from "node:path";
|
|
886
|
+
|
|
887
|
+
// src/term/core/vale.ts
|
|
888
|
+
import { execFile } from "node:child_process";
|
|
889
|
+
var MAX_BUFFER = 64 * 1024 * 1024;
|
|
890
|
+
var SEVERITIES = ["error", "warning", "suggestion"];
|
|
891
|
+
var spawnVale = (args, opts) => new Promise((settle, reject) => {
|
|
892
|
+
execFile(
|
|
893
|
+
"vale",
|
|
894
|
+
args,
|
|
895
|
+
{ cwd: opts.cwd, windowsHide: true, maxBuffer: MAX_BUFFER, encoding: "utf8" },
|
|
896
|
+
(error, stdout, stderr) => {
|
|
897
|
+
if (!error) {
|
|
898
|
+
settle({ code: 0, stdout, stderr });
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
if (typeof error.code === "number") {
|
|
902
|
+
settle({ code: error.code, stdout, stderr });
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (error.code == null && typeof error.signal === "string") {
|
|
906
|
+
settle({ code: null, signal: error.signal, stdout, stderr });
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const failure = error;
|
|
910
|
+
reject(failure);
|
|
911
|
+
}
|
|
912
|
+
);
|
|
913
|
+
});
|
|
914
|
+
function isEnoent(err) {
|
|
915
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
916
|
+
}
|
|
917
|
+
async function start(spawn, args, cwd, missing) {
|
|
918
|
+
try {
|
|
919
|
+
return await spawn(args, { cwd });
|
|
920
|
+
} catch (err) {
|
|
921
|
+
if (isEnoent(err)) throw new TermError(missing);
|
|
922
|
+
throw err;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function isMapping(value) {
|
|
926
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
927
|
+
}
|
|
928
|
+
function failureText(result) {
|
|
929
|
+
const stderr = result.stderr.trim();
|
|
930
|
+
try {
|
|
931
|
+
const parsed = JSON.parse(stderr);
|
|
932
|
+
if (isMapping(parsed) && typeof parsed["Text"] === "string") return parsed["Text"].trim();
|
|
933
|
+
} catch {
|
|
934
|
+
}
|
|
935
|
+
if (stderr !== "") return stderr;
|
|
936
|
+
if (result.signal != null) return `killed by ${result.signal}`;
|
|
937
|
+
return `exit code ${String(result.code)}`;
|
|
938
|
+
}
|
|
939
|
+
function parseJson(stdout, what) {
|
|
940
|
+
try {
|
|
941
|
+
return JSON.parse(stdout);
|
|
942
|
+
} catch {
|
|
943
|
+
throw new TermError(`vale printed ${what} that is not JSON: ${stdout.trim().slice(0, 200)}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
function parseAlert(raw, path) {
|
|
947
|
+
const unreadable = () => new TermError(`vale printed an alert for ${path} that manni cannot read: ${JSON.stringify(raw)}`);
|
|
948
|
+
if (!isMapping(raw)) throw unreadable();
|
|
949
|
+
const { Check, Message, Line, Span, Severity } = raw;
|
|
950
|
+
if (typeof Check !== "string" || typeof Message !== "string" || typeof Line !== "number") throw unreadable();
|
|
951
|
+
if (!Array.isArray(Span) || Span.length !== 2) throw unreadable();
|
|
952
|
+
const span = Span;
|
|
953
|
+
const [from, to] = span;
|
|
954
|
+
if (typeof from !== "number" || typeof to !== "number") throw unreadable();
|
|
955
|
+
const severity = SEVERITIES.find((s) => s === Severity);
|
|
956
|
+
if (severity === void 0) throw unreadable();
|
|
957
|
+
return { Check, Message, Line, Span: [from, to], Severity: severity };
|
|
958
|
+
}
|
|
959
|
+
function parseAlerts(stdout) {
|
|
960
|
+
const parsed = parseJson(stdout, "alerts");
|
|
961
|
+
if (!isMapping(parsed)) throw new TermError("vale printed alerts that are not a JSON object.");
|
|
962
|
+
const alerts = {};
|
|
963
|
+
for (const [path, list] of Object.entries(parsed)) {
|
|
964
|
+
if (!Array.isArray(list)) throw new TermError(`vale printed alerts for ${path} that are not a list.`);
|
|
965
|
+
alerts[path] = list.map((raw) => parseAlert(raw, path));
|
|
966
|
+
}
|
|
967
|
+
return alerts;
|
|
968
|
+
}
|
|
969
|
+
async function runValeJson(files, opts, spawn = spawnVale) {
|
|
970
|
+
const args = ["--output=JSON", ...opts.config === void 0 ? [] : ["--config", opts.config], ...files];
|
|
971
|
+
const result = await start(spawn, args, opts.cwd, "vale is not on PATH. Install Vale to lint definitions.");
|
|
972
|
+
if (result.code !== 0 && result.code !== 1) {
|
|
973
|
+
throw new TermError(`vale could not lint definitions: ${failureText(result)}`);
|
|
974
|
+
}
|
|
975
|
+
return parseAlerts(result.stdout);
|
|
976
|
+
}
|
|
977
|
+
var NO_CONFIG = /no config file found|\.vale\.ini not found/i;
|
|
978
|
+
function stringList(value) {
|
|
979
|
+
if (!Array.isArray(value)) return void 0;
|
|
980
|
+
const list = [];
|
|
981
|
+
for (const item of value) {
|
|
982
|
+
if (typeof item !== "string") return void 0;
|
|
983
|
+
list.push(item);
|
|
984
|
+
}
|
|
985
|
+
return list;
|
|
986
|
+
}
|
|
987
|
+
async function valeLsConfig(opts, spawn = spawnVale) {
|
|
988
|
+
const args = [...opts.config === void 0 ? [] : ["--config", opts.config], "ls-config"];
|
|
989
|
+
const result = await start(
|
|
990
|
+
spawn,
|
|
991
|
+
args,
|
|
992
|
+
opts.cwd,
|
|
993
|
+
"vale is not on PATH. Install Vale, or pass -o <styles directory>."
|
|
994
|
+
);
|
|
995
|
+
if (result.code !== 0) {
|
|
996
|
+
const text2 = failureText(result);
|
|
997
|
+
if (NO_CONFIG.test(text2)) return null;
|
|
998
|
+
throw new TermError(`vale could not resolve its configuration: ${text2}`);
|
|
999
|
+
}
|
|
1000
|
+
const parsed = parseJson(result.stdout, "a configuration");
|
|
1001
|
+
if (!isMapping(parsed)) throw new TermError("vale printed a configuration that is not a JSON object.");
|
|
1002
|
+
const paths = stringList(parsed["Paths"]);
|
|
1003
|
+
const stylesPath = paths?.[paths.length - 1];
|
|
1004
|
+
if (stylesPath === void 0 || stylesPath === "") {
|
|
1005
|
+
throw new TermError(
|
|
1006
|
+
"vale's configuration names no styles directory. Set StylesPath in it, or pass -o <styles directory>."
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
const root = parsed["RootINI"];
|
|
1010
|
+
const configFiles = stringList(parsed["ConfigFiles"]);
|
|
1011
|
+
const rootIni = typeof root === "string" && root !== "" ? root : configFiles?.[configFiles.length - 1] ?? opts.config ?? "";
|
|
1012
|
+
const baseStyles = {};
|
|
1013
|
+
const sections = parsed["SBaseStyles"];
|
|
1014
|
+
if (sections !== null && sections !== void 0) {
|
|
1015
|
+
if (!isMapping(sections)) throw new TermError("vale printed SBaseStyles that is not a JSON object.");
|
|
1016
|
+
for (const [glob, styles] of Object.entries(sections)) {
|
|
1017
|
+
const list = stringList(styles);
|
|
1018
|
+
if (list === void 0) throw new TermError(`vale printed styles for ${glob} that are not a list of names.`);
|
|
1019
|
+
baseStyles[glob] = list;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return { stylesPath, rootIni, baseStyles };
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// src/term/core/lint.ts
|
|
1026
|
+
var PROSE_FIELDS = ["definition", "abstract", "scope-note"];
|
|
1027
|
+
function fileStem(id) {
|
|
1028
|
+
const safe = id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1029
|
+
return safe === "" ? "_" : safe;
|
|
1030
|
+
}
|
|
1031
|
+
function baseName(path) {
|
|
1032
|
+
const parts = path.split(/[\\/]/);
|
|
1033
|
+
return parts[parts.length - 1] ?? path;
|
|
1034
|
+
}
|
|
1035
|
+
function sourceLine(written, proseLine) {
|
|
1036
|
+
const { term, field, value } = written;
|
|
1037
|
+
const fieldLine = term.location.fieldLines[field] ?? term.location.line;
|
|
1038
|
+
if (!value.includes("\n")) return fieldLine;
|
|
1039
|
+
const { construct } = term.location;
|
|
1040
|
+
return construct === "page" || construct === "manifest" ? fieldLine + proseLine : fieldLine + proseLine - 1;
|
|
1041
|
+
}
|
|
1042
|
+
function compareFindings(a, b) {
|
|
1043
|
+
if (a.file !== b.file) return a.file < b.file ? -1 : 1;
|
|
1044
|
+
const lineA = a.line ?? 0;
|
|
1045
|
+
const lineB = b.line ?? 0;
|
|
1046
|
+
if (lineA !== lineB) return lineA - lineB;
|
|
1047
|
+
if (a.ruleId !== b.ruleId) return a.ruleId < b.ruleId ? -1 : 1;
|
|
1048
|
+
if (a.message !== b.message) return a.message < b.message ? -1 : 1;
|
|
1049
|
+
return 0;
|
|
1050
|
+
}
|
|
1051
|
+
async function lintTermSet(set, opts = {}) {
|
|
1052
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1053
|
+
const files = [];
|
|
1054
|
+
const stemCount = /* @__PURE__ */ new Map();
|
|
1055
|
+
for (const term of set.terms) {
|
|
1056
|
+
const fields = PROSE_FIELDS.filter((field) => term.record[field] !== void 0);
|
|
1057
|
+
if (fields.length === 0) continue;
|
|
1058
|
+
const stem = fileStem(term.id);
|
|
1059
|
+
const seen = (stemCount.get(stem.toLowerCase()) ?? 0) + 1;
|
|
1060
|
+
stemCount.set(stem.toLowerCase(), seen);
|
|
1061
|
+
const prefix = seen === 1 ? stem : `${stem}.${String(seen)}`;
|
|
1062
|
+
for (const field of fields) {
|
|
1063
|
+
const value = term.record[field];
|
|
1064
|
+
if (value === void 0) continue;
|
|
1065
|
+
const name = `${prefix}.${field}.md`;
|
|
1066
|
+
byName.set(name.toLowerCase(), { term, field, value });
|
|
1067
|
+
files.push({ name, content: `${value}
|
|
1068
|
+
` });
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (files.length === 0) return [];
|
|
1072
|
+
const runVale = opts.runVale ?? runValeJson;
|
|
1073
|
+
const dir = await mkdtemp(join3(tmpdir(), "manni-term-lint-"));
|
|
1074
|
+
try {
|
|
1075
|
+
for (const file of files) await writeFile(join3(dir, file.name), file.content, "utf8");
|
|
1076
|
+
const alerts = await runVale([dir], { config: opts.valeConfig, cwd: opts.cwd });
|
|
1077
|
+
const findings = [];
|
|
1078
|
+
for (const [path, list] of Object.entries(alerts)) {
|
|
1079
|
+
const written = byName.get(baseName(path).toLowerCase());
|
|
1080
|
+
if (written === void 0) continue;
|
|
1081
|
+
for (const alert of list) {
|
|
1082
|
+
findings.push({
|
|
1083
|
+
ruleId: proseRuleId(alert.Check),
|
|
1084
|
+
severity: foldValeSeverity(alert.Severity),
|
|
1085
|
+
message: alert.Message,
|
|
1086
|
+
file: written.term.location.file,
|
|
1087
|
+
line: sourceLine(written, alert.Line),
|
|
1088
|
+
id: written.term.id,
|
|
1089
|
+
tool: "vale",
|
|
1090
|
+
check: alert.Check,
|
|
1091
|
+
toolSeverity: alert.Severity,
|
|
1092
|
+
field: written.field
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return findings.sort(compareFindings);
|
|
1097
|
+
} finally {
|
|
1098
|
+
await rm(dir, { recursive: true, force: true });
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/term/commands/lint.ts
|
|
1103
|
+
async function runLint(opts) {
|
|
1104
|
+
const { cwd, run, set } = await loadTerms(opts);
|
|
1105
|
+
const valeConfig = valeConfigFor(run);
|
|
1106
|
+
const findings = await lintTermSet(set, {
|
|
1107
|
+
cwd,
|
|
1108
|
+
...valeConfig === void 0 ? {} : { valeConfig },
|
|
1109
|
+
...opts.runVale === void 0 ? {} : { runVale: opts.runVale }
|
|
1110
|
+
});
|
|
1111
|
+
const { results, origin } = toResults(set, findings);
|
|
1112
|
+
return {
|
|
1113
|
+
findings: reportedFindings(results, origin),
|
|
1114
|
+
results,
|
|
1115
|
+
summary: summarize(results),
|
|
1116
|
+
frame: { cwd, base: run.configDir ?? cwd, runBase: run.base },
|
|
1117
|
+
terms: set.terms.length,
|
|
1118
|
+
references: set.references.length,
|
|
1119
|
+
found: findings.length,
|
|
1120
|
+
cwd
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/term/commands/write.ts
|
|
1125
|
+
import { existsSync, statSync } from "node:fs";
|
|
1126
|
+
import { lstat, mkdir, readdir, readFile, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
1127
|
+
import { dirname, extname, join as join4, resolve as resolve2 } from "node:path";
|
|
1128
|
+
var SUGGESTED_SECTION = "*.md";
|
|
1129
|
+
var STYLE2 = "Terms";
|
|
1130
|
+
function writeFormats(writers = TERM_WRITERS) {
|
|
1131
|
+
return TERM_WRITE_FORMATS.filter((format) => writers.some((writer) => writer.format === format));
|
|
1132
|
+
}
|
|
1133
|
+
function isDirectory(path) {
|
|
1134
|
+
try {
|
|
1135
|
+
return statSync(path).isDirectory();
|
|
1136
|
+
} catch {
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
var MAX_EXISTING_FILE_BYTES = 1024 * 1024;
|
|
1141
|
+
async function filesUnder(dir, into) {
|
|
1142
|
+
let entries;
|
|
1143
|
+
try {
|
|
1144
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
1145
|
+
} catch {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
for (const entry of entries) {
|
|
1149
|
+
const path = join4(dir, entry.name);
|
|
1150
|
+
if (entry.isDirectory()) await filesUnder(path, into);
|
|
1151
|
+
else if (entry.isFile()) {
|
|
1152
|
+
const { size } = await lstat(path);
|
|
1153
|
+
into.set(path, size > MAX_EXISTING_FILE_BYTES ? "" : await readFile(path, "utf8"));
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
async function existingFor(target) {
|
|
1158
|
+
const existing = /* @__PURE__ */ new Map();
|
|
1159
|
+
if (target.shape === "directory") {
|
|
1160
|
+
await filesUnder(target.path, existing);
|
|
1161
|
+
} else if (existsSync(target.path) && !isDirectory(target.path)) {
|
|
1162
|
+
existing.set(target.path, await readFile(target.path, "utf8"));
|
|
1163
|
+
}
|
|
1164
|
+
return existing;
|
|
1165
|
+
}
|
|
1166
|
+
function relativized(error, paths, cwd) {
|
|
1167
|
+
let message = error.message;
|
|
1168
|
+
for (const path of [...paths].sort((a, b) => b.length - a.length)) {
|
|
1169
|
+
if (message.includes(path)) message = message.replaceAll(path, displayPath(path, cwd));
|
|
1170
|
+
}
|
|
1171
|
+
return message === error.message ? error : new TermError(message);
|
|
1172
|
+
}
|
|
1173
|
+
function droppedFrom(format, shape, readers) {
|
|
1174
|
+
const construct = DOCUMENT_RENDERS[format]?.shapes[shape];
|
|
1175
|
+
const reader = construct === void 0 ? void 0 : readerForConstruct(construct, readers);
|
|
1176
|
+
return reader === void 0 ? format : `a ${reader.label}`;
|
|
1177
|
+
}
|
|
1178
|
+
async function applyChanges(files, removals, existing, cwd, write) {
|
|
1179
|
+
const changes = [];
|
|
1180
|
+
for (const file of files) {
|
|
1181
|
+
const before = existing.get(file.path) ?? (existsSync(file.path) ? await readFile(file.path, "utf8") : void 0);
|
|
1182
|
+
if (before === file.content) continue;
|
|
1183
|
+
changes.push({ path: displayPath(file.path, cwd), action: before === void 0 ? "create" : "change" });
|
|
1184
|
+
if (write) {
|
|
1185
|
+
await mkdir(dirname(file.path), { recursive: true });
|
|
1186
|
+
await writeFile2(file.path, file.content, "utf8");
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
for (const path of removals) {
|
|
1190
|
+
if (!existsSync(path)) continue;
|
|
1191
|
+
changes.push({ path: displayPath(path, cwd), action: "remove" });
|
|
1192
|
+
if (write) await rm2(path, { force: true });
|
|
1193
|
+
}
|
|
1194
|
+
return changes;
|
|
1195
|
+
}
|
|
1196
|
+
async function runWrite(opts) {
|
|
1197
|
+
const cwd = resolve2(opts.cwd ?? process.cwd());
|
|
1198
|
+
const writers = opts.writers ?? TERM_WRITERS;
|
|
1199
|
+
const readers = opts.readers ?? TERM_READERS;
|
|
1200
|
+
const check = opts.check === true;
|
|
1201
|
+
const dryRun = opts.dryRun === true;
|
|
1202
|
+
if (check && dryRun) throw new TermError("--check and --dry-run cannot be combined.");
|
|
1203
|
+
if (opts.format === void 0) {
|
|
1204
|
+
if (opts.out !== void 0) throw new TermError("-o needs -f <format>.");
|
|
1205
|
+
return writeInPlace({ ...opts, cwd, readers }, check, dryRun);
|
|
1206
|
+
}
|
|
1207
|
+
const format = formatChoice(opts.format, writeFormats(writers));
|
|
1208
|
+
const writer = writers.find((w) => w.format === format);
|
|
1209
|
+
if (writer === void 0) throw new TermError(`unknown format "${format}".`);
|
|
1210
|
+
if (opts.out === void 0 && format !== "vale") {
|
|
1211
|
+
throw new TermError(`-f ${format} needs -o <path>.`);
|
|
1212
|
+
}
|
|
1213
|
+
const { run, set } = await loadTerms({ ...opts, cwd, readers });
|
|
1214
|
+
let targetPath;
|
|
1215
|
+
let wiring;
|
|
1216
|
+
if (opts.out === void 0) {
|
|
1217
|
+
const lsConfig = opts.lsConfig ?? ((o) => valeLsConfig(o));
|
|
1218
|
+
const config = valeConfigFor(run);
|
|
1219
|
+
const resolved = await lsConfig({ cwd, ...config === void 0 ? {} : { config } });
|
|
1220
|
+
if (resolved === null) {
|
|
1221
|
+
throw new TermError(
|
|
1222
|
+
"Vale found no config file. Set tools.vale.config in manni.config.yaml, or pass -o <styles directory>."
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
targetPath = resolve2(cwd, resolved.stylesPath);
|
|
1226
|
+
if (!Object.values(resolved.baseStyles).some((styles) => styles.includes(STYLE2))) {
|
|
1227
|
+
const first = Object.entries(resolved.baseStyles)[0];
|
|
1228
|
+
const [section, styles] = first ?? [SUGGESTED_SECTION, []];
|
|
1229
|
+
wiring = {
|
|
1230
|
+
rootIni: displayPath(resolve2(cwd, resolved.rootIni), cwd),
|
|
1231
|
+
section,
|
|
1232
|
+
styles: [...styles, STYLE2],
|
|
1233
|
+
addSection: first === void 0
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
} else {
|
|
1237
|
+
targetPath = resolve2(cwd, opts.out);
|
|
1238
|
+
}
|
|
1239
|
+
const shape = format === "vale" || /[\\/]$/.test(opts.out ?? "") || isDirectory(targetPath) ? "directory" : "file";
|
|
1240
|
+
const target = { path: targetPath, shape };
|
|
1241
|
+
const existing = await existingFor(target);
|
|
1242
|
+
let render;
|
|
1243
|
+
try {
|
|
1244
|
+
render = writer.render(set.terms, target, { existing });
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
if (error instanceof TermError) throw relativized(error, [...existing.keys(), targetPath], cwd);
|
|
1247
|
+
throw error;
|
|
1248
|
+
}
|
|
1249
|
+
const changes = await applyChanges(render.files, render.removals, existing, cwd, !check && !dryRun);
|
|
1250
|
+
const shown = format === "vale" ? displayPath(join4(targetPath, STYLE2), cwd) : opts.out ?? displayPath(targetPath, cwd);
|
|
1251
|
+
return {
|
|
1252
|
+
mode: "render",
|
|
1253
|
+
format,
|
|
1254
|
+
shape,
|
|
1255
|
+
target: shown,
|
|
1256
|
+
terms: set.terms.length,
|
|
1257
|
+
changes,
|
|
1258
|
+
files: render.files.map((file) => ({ path: displayPath(file.path, cwd), content: file.content })),
|
|
1259
|
+
// A Vale style holds designations only, by design; its dropped definitions are not news.
|
|
1260
|
+
dropped: format === "vale" ? [] : render.dropped,
|
|
1261
|
+
droppedFrom: droppedFrom(format, shape, readers),
|
|
1262
|
+
skipped: render.skipped,
|
|
1263
|
+
check,
|
|
1264
|
+
dryRun,
|
|
1265
|
+
...wiring === void 0 ? {} : { wiring }
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
function inputFor(content, path, construct, file) {
|
|
1269
|
+
if (construct === "manifest") {
|
|
1270
|
+
return { content, file, path, format: MANIFEST_FORMAT, metadata: {}, lineFor: () => void 0 };
|
|
1271
|
+
}
|
|
1272
|
+
const extractor = extractorForExtension(extname(path));
|
|
1273
|
+
if (extractor === void 0) throw new TermError(`${file}: no reader for its extension.`);
|
|
1274
|
+
return extractInput(content, file, path, extractor.name);
|
|
1275
|
+
}
|
|
1276
|
+
function carryRecords(current, edited) {
|
|
1277
|
+
const count = (terms, id) => terms.filter((t) => t.id === id).length;
|
|
1278
|
+
return current.map((entry, index) => {
|
|
1279
|
+
const unique = count(current, entry.id) === 1 && count(edited, entry.id) === 1;
|
|
1280
|
+
const match = unique ? edited.find((t) => t.id === entry.id) : edited[index];
|
|
1281
|
+
return match?.id === entry.id ? { ...entry, record: match.record } : entry;
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
function applyInPlace(original, path, file, constructs, readers) {
|
|
1285
|
+
let content = original;
|
|
1286
|
+
let first = true;
|
|
1287
|
+
for (const [construct, terms] of constructs) {
|
|
1288
|
+
const reader = readerForConstruct(construct, readers);
|
|
1289
|
+
if (reader?.apply === void 0) continue;
|
|
1290
|
+
const input = inputFor(content, path, construct, file);
|
|
1291
|
+
content = reader.apply(input, first ? terms : carryRecords(reader.read(input).terms, terms));
|
|
1292
|
+
first = false;
|
|
1293
|
+
}
|
|
1294
|
+
return content;
|
|
1295
|
+
}
|
|
1296
|
+
async function writeInPlace(opts, check, dryRun) {
|
|
1297
|
+
const { cwd, readers } = opts;
|
|
1298
|
+
const { set } = await loadTerms(opts);
|
|
1299
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
1300
|
+
for (const term of set.terms) {
|
|
1301
|
+
const { path, construct } = term.location;
|
|
1302
|
+
if (path === void 0) {
|
|
1303
|
+
throw new TermError(`${term.location.file} has nowhere to write back to. Pass -f <format> -o <path>.`);
|
|
1304
|
+
}
|
|
1305
|
+
const reader = readerForConstruct(construct, readers);
|
|
1306
|
+
if (reader?.apply === void 0) {
|
|
1307
|
+
const name = reader?.label ?? construct;
|
|
1308
|
+
throw new TermError(
|
|
1309
|
+
`${displayPath(term.location.file, cwd)}: a ${name} cannot be written in place. Pass -f <format> -o <path>.`
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
const entry = byPath.get(path) ?? { file: term.location.file, constructs: /* @__PURE__ */ new Map() };
|
|
1313
|
+
entry.constructs.set(construct, [...entry.constructs.get(construct) ?? [], term]);
|
|
1314
|
+
byPath.set(path, entry);
|
|
1315
|
+
}
|
|
1316
|
+
const files = [];
|
|
1317
|
+
const existing = /* @__PURE__ */ new Map();
|
|
1318
|
+
for (const [path, { file, constructs }] of byPath) {
|
|
1319
|
+
let original;
|
|
1320
|
+
try {
|
|
1321
|
+
original = await readFile(path, "utf8");
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
throw new TermError(`${file}: could not be read: ${errorMessage(error)}`);
|
|
1324
|
+
}
|
|
1325
|
+
existing.set(path, original);
|
|
1326
|
+
files.push({ path, content: applyInPlace(original, path, file, constructs, readers) });
|
|
1327
|
+
}
|
|
1328
|
+
const changes = await applyChanges(files, [], existing, cwd, !check && !dryRun);
|
|
1329
|
+
return {
|
|
1330
|
+
mode: "in-place",
|
|
1331
|
+
terms: set.terms.length,
|
|
1332
|
+
changes,
|
|
1333
|
+
files: files.map((f) => ({ path: displayPath(f.path, cwd), content: f.content })),
|
|
1334
|
+
dropped: [],
|
|
1335
|
+
skipped: [],
|
|
1336
|
+
check,
|
|
1337
|
+
dryRun
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// src/term/reporters/csv.ts
|
|
1342
|
+
var LIST_SEPARATOR2 = "|";
|
|
1343
|
+
function cell2(value) {
|
|
1344
|
+
return /[",\r\n]/.test(value) ? `"${value.replaceAll('"', '""')}"` : value;
|
|
1345
|
+
}
|
|
1346
|
+
function renderListCsv(terms) {
|
|
1347
|
+
const rows = [
|
|
1348
|
+
["id", "label", "alt-labels", "abstract"],
|
|
1349
|
+
...terms.map((t) => [
|
|
1350
|
+
t.id,
|
|
1351
|
+
t.record.label,
|
|
1352
|
+
(t.record["alt-labels"] ?? []).join(LIST_SEPARATOR2),
|
|
1353
|
+
t.record.abstract ?? ""
|
|
1354
|
+
])
|
|
1355
|
+
];
|
|
1356
|
+
return rows.map((row) => row.map(cell2).join(",")).join("\n");
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/term/reporters/sarif.ts
|
|
1360
|
+
var INFORMATION_URI = "https://hawkeyexl.github.io/manni/term/";
|
|
1361
|
+
var RULES_URI = "https://hawkeyexl.github.io/manni/term/reference/rules/";
|
|
1362
|
+
var RULE_DESCRIPTIONS = {
|
|
1363
|
+
"undefined-term": "A page's concepts: names a label no entry claims as its preferred label.",
|
|
1364
|
+
"duplicate-id": "Two entries share an id.",
|
|
1365
|
+
"label-collision": "Two entries claim the same preferred label, ignoring case.",
|
|
1366
|
+
"alt-label-collision": "An entry's alt-label is another entry's preferred label, ignoring case.",
|
|
1367
|
+
"dangling-reference": "A value of broader, narrower, related-terms or see names no entry, by label or by id.",
|
|
1368
|
+
"broader-cycle": "A chain of broader returns to where it started.",
|
|
1369
|
+
"see-not-empty": "An entry with see also carries a definition.",
|
|
1370
|
+
"asymmetric-hierarchy": "A names B as broader, and B omits A from narrower, or the reverse.",
|
|
1371
|
+
"abstract-too-long": "An abstract is longer than term.abstractMaxLength.",
|
|
1372
|
+
"unused-term": "No page's concepts: names the entry's preferred label."
|
|
1373
|
+
};
|
|
1374
|
+
var PROSE_PREFIX = `${RULE_ID_PREFIX}/prose/`;
|
|
1375
|
+
var FIELD_LABEL = new RegExp(`^(?:\\(root\\)|/(?:${TERM_FIELDS.join("|")})) `);
|
|
1376
|
+
function describe(rule) {
|
|
1377
|
+
if (rule.id.startsWith(PROSE_PREFIX)) {
|
|
1378
|
+
const check = rule.id.slice(PROSE_PREFIX.length);
|
|
1379
|
+
return { id: rule.id, shortDescription: { text: `Vale's ${check} rule flagged the prose of an entry's field.` } };
|
|
1380
|
+
}
|
|
1381
|
+
const name = rule.id.slice(RULE_ID_PREFIX.length + 1);
|
|
1382
|
+
if (!isTermRule(name)) return rule;
|
|
1383
|
+
return { id: rule.id, shortDescription: { text: RULE_DESCRIPTIONS[name] }, helpUri: `${RULES_URI}#${name}` };
|
|
1384
|
+
}
|
|
1385
|
+
function renderFindingsSarif(report, opts = {}) {
|
|
1386
|
+
const log = JSON.parse(
|
|
1387
|
+
renderSarif(report.results, {
|
|
1388
|
+
frame: report.frame,
|
|
1389
|
+
...opts.onNotice === void 0 ? {} : { onNotice: opts.onNotice }
|
|
1390
|
+
})
|
|
1391
|
+
);
|
|
1392
|
+
for (const run of log.runs) {
|
|
1393
|
+
run.tool.driver.informationUri = INFORMATION_URI;
|
|
1394
|
+
run.tool.driver.rules = run.tool.driver.rules.map(describe);
|
|
1395
|
+
for (const result of run.results) result.message.text = result.message.text.replace(FIELD_LABEL, "");
|
|
1396
|
+
}
|
|
1397
|
+
return JSON.stringify(log, null, 2);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/term/reporters/junit.ts
|
|
1401
|
+
var MESSAGE_LABEL = new RegExp(`message="${FIELD_LABEL.source.slice(1)}`, "g");
|
|
1402
|
+
function renderFindingsJunit(report) {
|
|
1403
|
+
return renderJunit(report.results, { frame: report.frame, classname: JUNIT_CLASSNAME }).replace(
|
|
1404
|
+
MESSAGE_LABEL,
|
|
1405
|
+
'message="'
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/term/commands/labels.ts
|
|
1410
|
+
async function termLabels(request) {
|
|
1411
|
+
const run = await resolveTermRun({
|
|
1412
|
+
cwd: request.cwd,
|
|
1413
|
+
inputs: [],
|
|
1414
|
+
...request.configPath === void 0 ? {} : { configPath: request.configPath },
|
|
1415
|
+
...request.noConfig === true ? { noConfig: true } : {}
|
|
1416
|
+
});
|
|
1417
|
+
if (run.inputs.length === 0 && run.manifests.length === 0) return [];
|
|
1418
|
+
const set = await loadSet(run, { inputs: [], allowEmpty: true });
|
|
1419
|
+
return set.terms.map((term) => ({ label: term.record.label, id: term.id }));
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// src/term/cli.ts
|
|
1423
|
+
registerTermLabelSource(termLabels);
|
|
1424
|
+
function colorFor(options) {
|
|
1425
|
+
return shouldColor({ noColor: !options.color, isTTY: process.stdout.isTTY });
|
|
1426
|
+
}
|
|
1427
|
+
async function coreOptions(paths, options) {
|
|
1428
|
+
return {
|
|
1429
|
+
inputs: paths,
|
|
1430
|
+
...paths.includes(STDIN_TOKEN) ? { stdin: await readStdin() } : {},
|
|
1431
|
+
...options.as === void 0 ? {} : { as: options.as },
|
|
1432
|
+
...options.ext === void 0 ? {} : { exts: splitList(options.ext) },
|
|
1433
|
+
exclude: options.exclude,
|
|
1434
|
+
collection: options.collection,
|
|
1435
|
+
...options.allowEmpty === true ? { allowEmpty: true } : {},
|
|
1436
|
+
...options.gitignore ? {} : { noGitignore: true },
|
|
1437
|
+
...options.config === void 0 ? {} : { configPath: options.config },
|
|
1438
|
+
onNotice: notice
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function withInputs(command) {
|
|
1442
|
+
return command.option("--as <format>", "input format for stdin (-)").option("--ext <list>", "comma-separated extensions for directory walks").option("--exclude <glob>", "glob to exclude; repeatable", collect, []).option("--collection <name>", "configured collection to read; repeatable", collect, []).option("--allow-empty", "treat a missing path, no matched files or no terms as success").option("--no-gitignore", "read files .gitignore covers").option("-c, --config <path>", "path to a manni config file").option("--no-color", "disable colored output");
|
|
1443
|
+
}
|
|
1444
|
+
function print(text2) {
|
|
1445
|
+
process.stdout.write(`${text2}
|
|
1446
|
+
`);
|
|
1447
|
+
}
|
|
1448
|
+
function renderFindings(report, format, options, references2) {
|
|
1449
|
+
let text2;
|
|
1450
|
+
switch (format) {
|
|
1451
|
+
case "pretty":
|
|
1452
|
+
text2 = renderFindingsPretty(report, { color: colorFor(options), references: references2 });
|
|
1453
|
+
break;
|
|
1454
|
+
case "json":
|
|
1455
|
+
text2 = renderFindingsJson(report);
|
|
1456
|
+
break;
|
|
1457
|
+
case "github":
|
|
1458
|
+
text2 = renderFindingsGithub(report);
|
|
1459
|
+
break;
|
|
1460
|
+
case "sarif":
|
|
1461
|
+
text2 = renderFindingsSarif(report, { onNotice: notice });
|
|
1462
|
+
break;
|
|
1463
|
+
case "junit":
|
|
1464
|
+
text2 = renderFindingsJunit(report);
|
|
1465
|
+
break;
|
|
1466
|
+
}
|
|
1467
|
+
if (text2.length > 0 || !OMITTED_WHEN_CLEAN.has(format)) print(text2);
|
|
1468
|
+
process.exitCode = report.summary.failed > 0 ? 1 : 0;
|
|
1469
|
+
}
|
|
1470
|
+
function buildProgram() {
|
|
1471
|
+
const program = new Command();
|
|
1472
|
+
program.name("term").description("Check, lint and render a docset's terms and the references into them.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
1473
|
+
withInputs(
|
|
1474
|
+
program.command("list").description("List the resolved entries").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
1475
|
+
).option("-f, --format <format>", `output: ${LIST_FORMATS.join(" | ")}`, "pretty").addHelpText(
|
|
1476
|
+
"after",
|
|
1477
|
+
[
|
|
1478
|
+
"",
|
|
1479
|
+
"Examples:",
|
|
1480
|
+
" manni term list # every collection",
|
|
1481
|
+
" manni term list docs/terms/ # one directory",
|
|
1482
|
+
" manni term list -f csv | head -2 # for a script"
|
|
1483
|
+
].join("\n")
|
|
1484
|
+
).action(async (paths, options) => {
|
|
1485
|
+
try {
|
|
1486
|
+
const format = formatChoice(options.format, LIST_FORMATS);
|
|
1487
|
+
const { terms } = await runList(await coreOptions(paths, options));
|
|
1488
|
+
print(format === "json" ? renderListJson(terms) : format === "csv" ? renderListCsv(terms) : renderListPretty(terms));
|
|
1489
|
+
process.exitCode = 0;
|
|
1490
|
+
} catch (err) {
|
|
1491
|
+
fail(err);
|
|
1492
|
+
}
|
|
1493
|
+
});
|
|
1494
|
+
withInputs(
|
|
1495
|
+
program.command("get").description("Show one entry").argument("<term>", "the entry's id, or its label or an alt-label in any case").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
1496
|
+
).option("-f, --format <format>", `output: ${GET_FORMATS.join(" | ")}`, "pretty").addHelpText(
|
|
1497
|
+
"after",
|
|
1498
|
+
["", "Examples:", " manni term get progressive-lens", ' manni term get "progressive lens" -f json'].join("\n")
|
|
1499
|
+
).action(async (term, paths, options) => {
|
|
1500
|
+
try {
|
|
1501
|
+
const format = formatChoice(options.format, GET_FORMATS);
|
|
1502
|
+
const found = await runGet({ ...await coreOptions(paths, options), term });
|
|
1503
|
+
print(format === "json" ? renderGetJson(found) : renderGetPretty(found, { color: colorFor(options) }));
|
|
1504
|
+
process.exitCode = 0;
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
fail(err);
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
withInputs(
|
|
1510
|
+
program.command("check").description("Check the set and every reference into it").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
1511
|
+
).option("-f, --format <format>", `output: ${FINDING_FORMATS.join(" | ")}`, "pretty").option("--baseline", "record the findings on the first run; after that, fail only on new ones").addHelpText(
|
|
1512
|
+
"after",
|
|
1513
|
+
[
|
|
1514
|
+
"",
|
|
1515
|
+
"Examples:",
|
|
1516
|
+
" manni term check # every collection",
|
|
1517
|
+
" manni term check -f github # CI annotations",
|
|
1518
|
+
" manni term check --baseline # ramp in on an existing docset"
|
|
1519
|
+
].join("\n")
|
|
1520
|
+
).action(async (paths, options) => {
|
|
1521
|
+
try {
|
|
1522
|
+
const format = formatChoice(options.format, FINDING_FORMATS);
|
|
1523
|
+
const report = await runCheck({
|
|
1524
|
+
...await coreOptions(paths, options),
|
|
1525
|
+
...options.baseline === true ? { baseline: true } : {}
|
|
1526
|
+
});
|
|
1527
|
+
renderFindings(report, format, options, true);
|
|
1528
|
+
} catch (err) {
|
|
1529
|
+
fail(err);
|
|
1530
|
+
}
|
|
1531
|
+
});
|
|
1532
|
+
withInputs(
|
|
1533
|
+
program.command("lint").description("Run Vale over the definitions").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
1534
|
+
).option("-f, --format <format>", `output: ${FINDING_FORMATS.join(" | ")}`, "pretty").addHelpText(
|
|
1535
|
+
"after",
|
|
1536
|
+
["", "Examples:", " manni term lint", " manni term lint -f github"].join("\n")
|
|
1537
|
+
).action(async (paths, options) => {
|
|
1538
|
+
try {
|
|
1539
|
+
const format = formatChoice(options.format, FINDING_FORMATS);
|
|
1540
|
+
const report = await runLint(await coreOptions(paths, options));
|
|
1541
|
+
renderFindings(report, format, options, false);
|
|
1542
|
+
} catch (err) {
|
|
1543
|
+
fail(err);
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
withInputs(
|
|
1547
|
+
program.command("write").description("Write the set back, or render it elsewhere").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
1548
|
+
).option("-f, --format <format>", `render as: ${writeFormats().join(" | ")} (default: each entry's own, in place)`).option("-o, --out <path>", "file or directory to render into; a trailing / means a directory").option("--check", "write nothing; exit 1 when the target differs from the render").option("--dry-run", "write nothing; print what would change").addHelpText(
|
|
1549
|
+
"after",
|
|
1550
|
+
[
|
|
1551
|
+
"",
|
|
1552
|
+
"Examples:",
|
|
1553
|
+
" manni term write # each entry back to its source",
|
|
1554
|
+
" manni term write -f json -o build/terms.json",
|
|
1555
|
+
" manni term write -f vale # into Vale's styles directory",
|
|
1556
|
+
" manni term write -f vale --check # keep the style in step"
|
|
1557
|
+
].join("\n")
|
|
1558
|
+
).action(async (paths, options) => {
|
|
1559
|
+
try {
|
|
1560
|
+
const report = await runWrite({
|
|
1561
|
+
...await coreOptions(paths, options),
|
|
1562
|
+
...options.format === void 0 ? {} : { format: options.format },
|
|
1563
|
+
...options.out === void 0 ? {} : { out: options.out },
|
|
1564
|
+
...options.check === true ? { check: true } : {},
|
|
1565
|
+
...options.dryRun === true ? { dryRun: true } : {}
|
|
1566
|
+
});
|
|
1567
|
+
print(renderWritePretty(report));
|
|
1568
|
+
if (report.wiring !== void 0) process.stderr.write(`${renderValeWiring(report.wiring)}
|
|
1569
|
+
`);
|
|
1570
|
+
process.exitCode = report.check && report.changes.length > 0 ? 1 : 0;
|
|
1571
|
+
} catch (err) {
|
|
1572
|
+
fail(err);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
program.command("formats").description("List the constructs read and written, per format").option("-f, --format <format>", `output: ${FORMATS_FORMATS.join(" | ")}`, "pretty").action((options) => {
|
|
1576
|
+
try {
|
|
1577
|
+
const format = formatChoice(options.format, FORMATS_FORMATS);
|
|
1578
|
+
const rows = listFormats();
|
|
1579
|
+
print(format === "json" ? renderFormatsJson(rows) : renderFormatsPretty(rows));
|
|
1580
|
+
process.exitCode = 0;
|
|
1581
|
+
} catch (err) {
|
|
1582
|
+
fail(err);
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1585
|
+
return program;
|
|
1586
|
+
}
|
|
1587
|
+
export {
|
|
1588
|
+
TERM_RULES,
|
|
1589
|
+
TermError,
|
|
1590
|
+
buildProgram
|
|
1591
|
+
};
|
|
1592
|
+
//# sourceMappingURL=term-5ICGKPC3.js.map
|