@jpeakai/mermaidjs-lint 0.4.0 → 0.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/dist/bin.js +192 -185
- package/dist/index.js +192 -185
- package/dist/testing.js +87 -68
- package/dist/types/check.d.ts +1 -0
- package/package.json +1 -1
- package/src/check.ts +20 -0
- package/src/cli.ts +4 -8
- package/src/fix/engine.ts +2 -1
package/dist/bin.js
CHANGED
|
@@ -9,7 +9,7 @@ import { parseArgs } from "node:util";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "@jpeakai/mermaidjs-lint",
|
|
12
|
-
version: "0.4.
|
|
12
|
+
version: "0.4.1",
|
|
13
13
|
type: "module",
|
|
14
14
|
description: "Deterministic lint gates and verified fixers for Mermaid diagrams in markdown",
|
|
15
15
|
license: "MIT",
|
|
@@ -79,6 +79,184 @@ var package_default = {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
// src/errors.ts
|
|
83
|
+
class MermaidLintError extends Error {
|
|
84
|
+
code;
|
|
85
|
+
constructor(code, message) {
|
|
86
|
+
super(message);
|
|
87
|
+
this.code = code;
|
|
88
|
+
this.name = new.target.name;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class UsageError extends MermaidLintError {
|
|
93
|
+
constructor(message, code = "usage") {
|
|
94
|
+
super(code, message);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class FlagError extends UsageError {
|
|
99
|
+
flag;
|
|
100
|
+
constructor(flag, message) {
|
|
101
|
+
super(message, "flag");
|
|
102
|
+
this.flag = flag;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
class ConfigFileError extends UsageError {
|
|
107
|
+
file;
|
|
108
|
+
constructor(file, message) {
|
|
109
|
+
super(`${file}: ${message}`, "config-file");
|
|
110
|
+
this.file = file;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class ConfigNotFoundError extends UsageError {
|
|
115
|
+
path;
|
|
116
|
+
constructor(path) {
|
|
117
|
+
super(`--config ${path}: no such file`, "config-not-found");
|
|
118
|
+
this.path = path;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class ConfigCodeError extends UsageError {
|
|
123
|
+
file;
|
|
124
|
+
constructor(file) {
|
|
125
|
+
super(`${file}: a .mjs config is code, and --no-plugins runs no project code; pass --config with a .json config, or drop --no-plugins`, "config-code");
|
|
126
|
+
this.file = file;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
class AmbiguousConfigError extends UsageError {
|
|
131
|
+
files;
|
|
132
|
+
constructor(root, files) {
|
|
133
|
+
super(`more than one config file at ${root}: ${files.join(", ")}`, "config-ambiguous");
|
|
134
|
+
this.files = files;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
class ConfigShapeError extends UsageError {
|
|
139
|
+
file;
|
|
140
|
+
key;
|
|
141
|
+
constructor(file, message, key = null) {
|
|
142
|
+
super(`${file}: ${message}`, "config-shape");
|
|
143
|
+
this.file = file;
|
|
144
|
+
this.key = key;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class RuleSettingError extends UsageError {
|
|
149
|
+
file;
|
|
150
|
+
rule;
|
|
151
|
+
constructor(file, rule, message) {
|
|
152
|
+
super(`${file}: rule "${rule}" ${message}`, "rule-setting");
|
|
153
|
+
this.file = file;
|
|
154
|
+
this.rule = rule;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
class UnknownRuleError extends UsageError {
|
|
159
|
+
file;
|
|
160
|
+
rule;
|
|
161
|
+
known;
|
|
162
|
+
constructor(file, rule, known) {
|
|
163
|
+
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
164
|
+
this.file = file;
|
|
165
|
+
this.rule = rule;
|
|
166
|
+
this.known = known;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
class RuleOptionError extends UsageError {
|
|
171
|
+
file;
|
|
172
|
+
rule;
|
|
173
|
+
option;
|
|
174
|
+
constructor(file, rule, option, message) {
|
|
175
|
+
super(`${file}: ${message}`, "rule-option");
|
|
176
|
+
this.file = file;
|
|
177
|
+
this.rule = rule;
|
|
178
|
+
this.option = option;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class FixOutsideRootError extends UsageError {
|
|
183
|
+
file;
|
|
184
|
+
target;
|
|
185
|
+
constructor(file, target, root) {
|
|
186
|
+
super(`${file} is a symbolic link to ${target}, outside the project root ${root}; --fix does not write through it. Fix the target in its own project, or run on the target path itself`, "fix-outside-root");
|
|
187
|
+
this.file = file;
|
|
188
|
+
this.target = target;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
class PackageJsonError extends MermaidLintError {
|
|
193
|
+
file;
|
|
194
|
+
constructor(file) {
|
|
195
|
+
super("package-json", `${file} is not a JSON object; fix or remove it`);
|
|
196
|
+
this.file = file;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class FixNotConvergingError extends MermaidLintError {
|
|
201
|
+
file;
|
|
202
|
+
rules;
|
|
203
|
+
constructor(file, passes, rules) {
|
|
204
|
+
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
205
|
+
this.file = file;
|
|
206
|
+
this.rules = rules;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
class PluginLoadError extends MermaidLintError {
|
|
211
|
+
source;
|
|
212
|
+
constructor(source, reason, code = "plugin") {
|
|
213
|
+
super(code, `plugin ${source}: ${reason}`);
|
|
214
|
+
this.source = source;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class PluginImportError extends PluginLoadError {
|
|
219
|
+
constructor(source, reason) {
|
|
220
|
+
super(source, `could not be imported: ${reason}`, "plugin-import");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
class PluginContractError extends PluginLoadError {
|
|
225
|
+
constructor(source, reason) {
|
|
226
|
+
super(source, reason, "plugin-contract");
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
class PluginConflictError extends PluginLoadError {
|
|
231
|
+
other;
|
|
232
|
+
constructor(source, other, reason) {
|
|
233
|
+
super(source, reason, "plugin-conflict");
|
|
234
|
+
this.other = other;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
class PluginNotInstalledError extends PluginLoadError {
|
|
239
|
+
root;
|
|
240
|
+
constructor(source, root) {
|
|
241
|
+
super(source, `is declared or listed but not installed; looked from ${root} upward. Install it, or remove it`, "plugin-not-installed");
|
|
242
|
+
this.root = root;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
class PluginSourceError extends PluginLoadError {
|
|
247
|
+
constructor(source, reason) {
|
|
248
|
+
super(source, reason, "plugin-source");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
class PluginFixerError extends MermaidLintError {
|
|
253
|
+
rule;
|
|
254
|
+
constructor(rule, reason) {
|
|
255
|
+
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
256
|
+
this.rule = rule;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
82
260
|
// src/model.ts
|
|
83
261
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
84
262
|
import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
|
|
@@ -854,184 +1032,6 @@ var buildDocModel = (src, kind = "markdown") => {
|
|
|
854
1032
|
return { src, kind, tree, diagrams };
|
|
855
1033
|
};
|
|
856
1034
|
|
|
857
|
-
// src/errors.ts
|
|
858
|
-
class MermaidLintError extends Error {
|
|
859
|
-
code;
|
|
860
|
-
constructor(code, message) {
|
|
861
|
-
super(message);
|
|
862
|
-
this.code = code;
|
|
863
|
-
this.name = new.target.name;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
class UsageError extends MermaidLintError {
|
|
868
|
-
constructor(message, code = "usage") {
|
|
869
|
-
super(code, message);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
class FlagError extends UsageError {
|
|
874
|
-
flag;
|
|
875
|
-
constructor(flag, message) {
|
|
876
|
-
super(message, "flag");
|
|
877
|
-
this.flag = flag;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
class ConfigFileError extends UsageError {
|
|
882
|
-
file;
|
|
883
|
-
constructor(file, message) {
|
|
884
|
-
super(`${file}: ${message}`, "config-file");
|
|
885
|
-
this.file = file;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
class ConfigNotFoundError extends UsageError {
|
|
890
|
-
path;
|
|
891
|
-
constructor(path) {
|
|
892
|
-
super(`--config ${path}: no such file`, "config-not-found");
|
|
893
|
-
this.path = path;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
class ConfigCodeError extends UsageError {
|
|
898
|
-
file;
|
|
899
|
-
constructor(file) {
|
|
900
|
-
super(`${file}: a .mjs config is code, and --no-plugins runs no project code; pass --config with a .json config, or drop --no-plugins`, "config-code");
|
|
901
|
-
this.file = file;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
class AmbiguousConfigError extends UsageError {
|
|
906
|
-
files;
|
|
907
|
-
constructor(root, files) {
|
|
908
|
-
super(`more than one config file at ${root}: ${files.join(", ")}`, "config-ambiguous");
|
|
909
|
-
this.files = files;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
class ConfigShapeError extends UsageError {
|
|
914
|
-
file;
|
|
915
|
-
key;
|
|
916
|
-
constructor(file, message, key = null) {
|
|
917
|
-
super(`${file}: ${message}`, "config-shape");
|
|
918
|
-
this.file = file;
|
|
919
|
-
this.key = key;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
class RuleSettingError extends UsageError {
|
|
924
|
-
file;
|
|
925
|
-
rule;
|
|
926
|
-
constructor(file, rule, message) {
|
|
927
|
-
super(`${file}: rule "${rule}" ${message}`, "rule-setting");
|
|
928
|
-
this.file = file;
|
|
929
|
-
this.rule = rule;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
class UnknownRuleError extends UsageError {
|
|
934
|
-
file;
|
|
935
|
-
rule;
|
|
936
|
-
known;
|
|
937
|
-
constructor(file, rule, known) {
|
|
938
|
-
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
939
|
-
this.file = file;
|
|
940
|
-
this.rule = rule;
|
|
941
|
-
this.known = known;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
class RuleOptionError extends UsageError {
|
|
946
|
-
file;
|
|
947
|
-
rule;
|
|
948
|
-
option;
|
|
949
|
-
constructor(file, rule, option, message) {
|
|
950
|
-
super(`${file}: ${message}`, "rule-option");
|
|
951
|
-
this.file = file;
|
|
952
|
-
this.rule = rule;
|
|
953
|
-
this.option = option;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
class FixOutsideRootError extends UsageError {
|
|
958
|
-
file;
|
|
959
|
-
target;
|
|
960
|
-
constructor(file, target, root) {
|
|
961
|
-
super(`${file} is a symbolic link to ${target}, outside the project root ${root}; --fix does not write through it. Fix the target in its own project, or run on the target path itself`, "fix-outside-root");
|
|
962
|
-
this.file = file;
|
|
963
|
-
this.target = target;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
class PackageJsonError extends MermaidLintError {
|
|
968
|
-
file;
|
|
969
|
-
constructor(file) {
|
|
970
|
-
super("package-json", `${file} is not a JSON object; fix or remove it`);
|
|
971
|
-
this.file = file;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
class FixNotConvergingError extends MermaidLintError {
|
|
976
|
-
file;
|
|
977
|
-
rules;
|
|
978
|
-
constructor(file, passes, rules) {
|
|
979
|
-
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
980
|
-
this.file = file;
|
|
981
|
-
this.rules = rules;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
class PluginLoadError extends MermaidLintError {
|
|
986
|
-
source;
|
|
987
|
-
constructor(source, reason, code = "plugin") {
|
|
988
|
-
super(code, `plugin ${source}: ${reason}`);
|
|
989
|
-
this.source = source;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
class PluginImportError extends PluginLoadError {
|
|
994
|
-
constructor(source, reason) {
|
|
995
|
-
super(source, `could not be imported: ${reason}`, "plugin-import");
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
class PluginContractError extends PluginLoadError {
|
|
1000
|
-
constructor(source, reason) {
|
|
1001
|
-
super(source, reason, "plugin-contract");
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
class PluginConflictError extends PluginLoadError {
|
|
1006
|
-
other;
|
|
1007
|
-
constructor(source, other, reason) {
|
|
1008
|
-
super(source, reason, "plugin-conflict");
|
|
1009
|
-
this.other = other;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
class PluginNotInstalledError extends PluginLoadError {
|
|
1014
|
-
root;
|
|
1015
|
-
constructor(source, root) {
|
|
1016
|
-
super(source, `is declared or listed but not installed; looked from ${root} upward. Install it, or remove it`, "plugin-not-installed");
|
|
1017
|
-
this.root = root;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
class PluginSourceError extends PluginLoadError {
|
|
1022
|
-
constructor(source, reason) {
|
|
1023
|
-
super(source, reason, "plugin-source");
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
class PluginFixerError extends MermaidLintError {
|
|
1028
|
-
rule;
|
|
1029
|
-
constructor(rule, reason) {
|
|
1030
|
-
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
1031
|
-
this.rule = rule;
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
1035
|
// src/parse/contrast.ts
|
|
1036
1036
|
import { existsSync } from "node:fs";
|
|
1037
1037
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -1827,6 +1827,15 @@ var buildRegistry = (input) => {
|
|
|
1827
1827
|
var BUILTIN = buildRegistry({ rules: RULES, fixOrder: FIX_ORDER, categories: BUILTIN_CATEGORIES });
|
|
1828
1828
|
|
|
1829
1829
|
// src/check.ts
|
|
1830
|
+
var checkRunFlags = (flags, prefix = "") => {
|
|
1831
|
+
const { preset, profile } = flags;
|
|
1832
|
+
if (preset !== undefined && canonicalPreset(preset) === null) {
|
|
1833
|
+
throw new FlagError(`${prefix}preset`, `${prefix}preset must be one of ${PRESET_NAMES.join(", ")}, not "${preset}"`);
|
|
1834
|
+
}
|
|
1835
|
+
if (profile !== undefined && !PROFILES.includes(profile)) {
|
|
1836
|
+
throw new FlagError(`${prefix}profile`, `${prefix}profile must be one of ${PROFILES.join(", ")}, not "${profile}"`);
|
|
1837
|
+
}
|
|
1838
|
+
};
|
|
1830
1839
|
var optionsFor = (rule, configured, flags) => {
|
|
1831
1840
|
const options = { ...configured };
|
|
1832
1841
|
for (const name of ["preset", "profile"]) {
|
|
@@ -1837,6 +1846,7 @@ var optionsFor = (rule, configured, flags) => {
|
|
|
1837
1846
|
return options;
|
|
1838
1847
|
};
|
|
1839
1848
|
var checkModel = async (doc, file, flags = {}, registry = BUILTIN) => {
|
|
1849
|
+
checkRunFlags(flags);
|
|
1840
1850
|
const perRule = registry.rules.map((rule) => rule.check({ doc, file, options: optionsFor(rule, registry.options.get(rule.id), flags), helpers: HELPERS }));
|
|
1841
1851
|
const findings = (await Promise.all(perRule)).flat();
|
|
1842
1852
|
return findings.sort((a, b) => a.line - b.line || a.rule.localeCompare(b.rule));
|
|
@@ -1987,6 +1997,7 @@ var overlaps = (edits) => {
|
|
|
1987
1997
|
};
|
|
1988
1998
|
var wellFormed = (e, length) => Number.isInteger(e.start) && Number.isInteger(e.end) && e.start >= 0 && e.start <= e.end && e.end <= length && typeof e.text === "string";
|
|
1989
1999
|
var fixSourceReport = async (src, file, flags = {}, registry = BUILTIN) => {
|
|
2000
|
+
checkRunFlags(flags);
|
|
1990
2001
|
const kind = documentKind(file);
|
|
1991
2002
|
const applied = [];
|
|
1992
2003
|
const refused = [];
|
|
@@ -2600,6 +2611,7 @@ Examples of every rule, with fixes: https://jpeakai.github.io/jpai-mermaidjs-lin
|
|
|
2600
2611
|
numeric threshold set in the config overrides both.
|
|
2601
2612
|
--profile NAME sets how contrast is read: ${PROFILES.join(", ")}. It overrides a profile set in the config.
|
|
2602
2613
|
auto reads mkdocs-material when any parent folder holds mkdocs.yml or mkdocs.yaml, else github.
|
|
2614
|
+
--json prints one JSON object instead of text lines.
|
|
2603
2615
|
--fix applies every fix it can prove safe, then reports what remains.
|
|
2604
2616
|
A fixer that is unsure leaves the text alone and the finding stays.
|
|
2605
2617
|
--config FILE reads that config instead of mermaidjs-lint.config.json or .mjs at the project root.
|
|
@@ -2727,12 +2739,7 @@ var run = async (argv, cwd) => {
|
|
|
2727
2739
|
}
|
|
2728
2740
|
const listing = values["list-rules"];
|
|
2729
2741
|
const validating = values["validate-plugins"];
|
|
2730
|
-
|
|
2731
|
-
throw new FlagError("--preset", `--preset must be one of ${PRESET_NAMES.join(", ")}, not "${values.preset}"`);
|
|
2732
|
-
}
|
|
2733
|
-
if (values.profile !== undefined && !PROFILES.includes(values.profile)) {
|
|
2734
|
-
throw new FlagError("--profile", `--profile must be one of ${PROFILES.join(", ")}, not "${values.profile}"`);
|
|
2735
|
-
}
|
|
2742
|
+
checkRunFlags({ preset: values.preset, profile: values.profile }, "--");
|
|
2736
2743
|
if (values["no-plugins"] && values["lenient-plugins"]) {
|
|
2737
2744
|
throw new FlagError("--lenient-plugins", "--no-plugins and --lenient-plugins contradict each other");
|
|
2738
2745
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,184 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
class MermaidLintError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = new.target.name;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class UsageError extends MermaidLintError {
|
|
15
|
+
constructor(message, code = "usage") {
|
|
16
|
+
super(code, message);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class FlagError extends UsageError {
|
|
21
|
+
flag;
|
|
22
|
+
constructor(flag, message) {
|
|
23
|
+
super(message, "flag");
|
|
24
|
+
this.flag = flag;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class ConfigFileError extends UsageError {
|
|
29
|
+
file;
|
|
30
|
+
constructor(file, message) {
|
|
31
|
+
super(`${file}: ${message}`, "config-file");
|
|
32
|
+
this.file = file;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class ConfigNotFoundError extends UsageError {
|
|
37
|
+
path;
|
|
38
|
+
constructor(path) {
|
|
39
|
+
super(`--config ${path}: no such file`, "config-not-found");
|
|
40
|
+
this.path = path;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class ConfigCodeError extends UsageError {
|
|
45
|
+
file;
|
|
46
|
+
constructor(file) {
|
|
47
|
+
super(`${file}: a .mjs config is code, and --no-plugins runs no project code; pass --config with a .json config, or drop --no-plugins`, "config-code");
|
|
48
|
+
this.file = file;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class AmbiguousConfigError extends UsageError {
|
|
53
|
+
files;
|
|
54
|
+
constructor(root, files) {
|
|
55
|
+
super(`more than one config file at ${root}: ${files.join(", ")}`, "config-ambiguous");
|
|
56
|
+
this.files = files;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class ConfigShapeError extends UsageError {
|
|
61
|
+
file;
|
|
62
|
+
key;
|
|
63
|
+
constructor(file, message, key = null) {
|
|
64
|
+
super(`${file}: ${message}`, "config-shape");
|
|
65
|
+
this.file = file;
|
|
66
|
+
this.key = key;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class RuleSettingError extends UsageError {
|
|
71
|
+
file;
|
|
72
|
+
rule;
|
|
73
|
+
constructor(file, rule, message) {
|
|
74
|
+
super(`${file}: rule "${rule}" ${message}`, "rule-setting");
|
|
75
|
+
this.file = file;
|
|
76
|
+
this.rule = rule;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class UnknownRuleError extends UsageError {
|
|
81
|
+
file;
|
|
82
|
+
rule;
|
|
83
|
+
known;
|
|
84
|
+
constructor(file, rule, known) {
|
|
85
|
+
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
86
|
+
this.file = file;
|
|
87
|
+
this.rule = rule;
|
|
88
|
+
this.known = known;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class RuleOptionError extends UsageError {
|
|
93
|
+
file;
|
|
94
|
+
rule;
|
|
95
|
+
option;
|
|
96
|
+
constructor(file, rule, option, message) {
|
|
97
|
+
super(`${file}: ${message}`, "rule-option");
|
|
98
|
+
this.file = file;
|
|
99
|
+
this.rule = rule;
|
|
100
|
+
this.option = option;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class FixOutsideRootError extends UsageError {
|
|
105
|
+
file;
|
|
106
|
+
target;
|
|
107
|
+
constructor(file, target, root) {
|
|
108
|
+
super(`${file} is a symbolic link to ${target}, outside the project root ${root}; --fix does not write through it. Fix the target in its own project, or run on the target path itself`, "fix-outside-root");
|
|
109
|
+
this.file = file;
|
|
110
|
+
this.target = target;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class PackageJsonError extends MermaidLintError {
|
|
115
|
+
file;
|
|
116
|
+
constructor(file) {
|
|
117
|
+
super("package-json", `${file} is not a JSON object; fix or remove it`);
|
|
118
|
+
this.file = file;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class FixNotConvergingError extends MermaidLintError {
|
|
123
|
+
file;
|
|
124
|
+
rules;
|
|
125
|
+
constructor(file, passes, rules) {
|
|
126
|
+
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
127
|
+
this.file = file;
|
|
128
|
+
this.rules = rules;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
class PluginLoadError extends MermaidLintError {
|
|
133
|
+
source;
|
|
134
|
+
constructor(source, reason, code = "plugin") {
|
|
135
|
+
super(code, `plugin ${source}: ${reason}`);
|
|
136
|
+
this.source = source;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
class PluginImportError extends PluginLoadError {
|
|
141
|
+
constructor(source, reason) {
|
|
142
|
+
super(source, `could not be imported: ${reason}`, "plugin-import");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
class PluginContractError extends PluginLoadError {
|
|
147
|
+
constructor(source, reason) {
|
|
148
|
+
super(source, reason, "plugin-contract");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class PluginConflictError extends PluginLoadError {
|
|
153
|
+
other;
|
|
154
|
+
constructor(source, other, reason) {
|
|
155
|
+
super(source, reason, "plugin-conflict");
|
|
156
|
+
this.other = other;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class PluginNotInstalledError extends PluginLoadError {
|
|
161
|
+
root;
|
|
162
|
+
constructor(source, root) {
|
|
163
|
+
super(source, `is declared or listed but not installed; looked from ${root} upward. Install it, or remove it`, "plugin-not-installed");
|
|
164
|
+
this.root = root;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
class PluginSourceError extends PluginLoadError {
|
|
169
|
+
constructor(source, reason) {
|
|
170
|
+
super(source, reason, "plugin-source");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class PluginFixerError extends MermaidLintError {
|
|
175
|
+
rule;
|
|
176
|
+
constructor(rule, reason) {
|
|
177
|
+
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
178
|
+
this.rule = rule;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
4
182
|
// src/model.ts
|
|
5
183
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
6
184
|
import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
|
|
@@ -776,184 +954,6 @@ var buildDocModel = (src, kind = "markdown") => {
|
|
|
776
954
|
return { src, kind, tree, diagrams };
|
|
777
955
|
};
|
|
778
956
|
|
|
779
|
-
// src/errors.ts
|
|
780
|
-
class MermaidLintError extends Error {
|
|
781
|
-
code;
|
|
782
|
-
constructor(code, message) {
|
|
783
|
-
super(message);
|
|
784
|
-
this.code = code;
|
|
785
|
-
this.name = new.target.name;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
class UsageError extends MermaidLintError {
|
|
790
|
-
constructor(message, code = "usage") {
|
|
791
|
-
super(code, message);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
class FlagError extends UsageError {
|
|
796
|
-
flag;
|
|
797
|
-
constructor(flag, message) {
|
|
798
|
-
super(message, "flag");
|
|
799
|
-
this.flag = flag;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
class ConfigFileError extends UsageError {
|
|
804
|
-
file;
|
|
805
|
-
constructor(file, message) {
|
|
806
|
-
super(`${file}: ${message}`, "config-file");
|
|
807
|
-
this.file = file;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
class ConfigNotFoundError extends UsageError {
|
|
812
|
-
path;
|
|
813
|
-
constructor(path) {
|
|
814
|
-
super(`--config ${path}: no such file`, "config-not-found");
|
|
815
|
-
this.path = path;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
class ConfigCodeError extends UsageError {
|
|
820
|
-
file;
|
|
821
|
-
constructor(file) {
|
|
822
|
-
super(`${file}: a .mjs config is code, and --no-plugins runs no project code; pass --config with a .json config, or drop --no-plugins`, "config-code");
|
|
823
|
-
this.file = file;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
class AmbiguousConfigError extends UsageError {
|
|
828
|
-
files;
|
|
829
|
-
constructor(root, files) {
|
|
830
|
-
super(`more than one config file at ${root}: ${files.join(", ")}`, "config-ambiguous");
|
|
831
|
-
this.files = files;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
class ConfigShapeError extends UsageError {
|
|
836
|
-
file;
|
|
837
|
-
key;
|
|
838
|
-
constructor(file, message, key = null) {
|
|
839
|
-
super(`${file}: ${message}`, "config-shape");
|
|
840
|
-
this.file = file;
|
|
841
|
-
this.key = key;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
class RuleSettingError extends UsageError {
|
|
846
|
-
file;
|
|
847
|
-
rule;
|
|
848
|
-
constructor(file, rule, message) {
|
|
849
|
-
super(`${file}: rule "${rule}" ${message}`, "rule-setting");
|
|
850
|
-
this.file = file;
|
|
851
|
-
this.rule = rule;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
class UnknownRuleError extends UsageError {
|
|
856
|
-
file;
|
|
857
|
-
rule;
|
|
858
|
-
known;
|
|
859
|
-
constructor(file, rule, known) {
|
|
860
|
-
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
861
|
-
this.file = file;
|
|
862
|
-
this.rule = rule;
|
|
863
|
-
this.known = known;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
class RuleOptionError extends UsageError {
|
|
868
|
-
file;
|
|
869
|
-
rule;
|
|
870
|
-
option;
|
|
871
|
-
constructor(file, rule, option, message) {
|
|
872
|
-
super(`${file}: ${message}`, "rule-option");
|
|
873
|
-
this.file = file;
|
|
874
|
-
this.rule = rule;
|
|
875
|
-
this.option = option;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
class FixOutsideRootError extends UsageError {
|
|
880
|
-
file;
|
|
881
|
-
target;
|
|
882
|
-
constructor(file, target, root) {
|
|
883
|
-
super(`${file} is a symbolic link to ${target}, outside the project root ${root}; --fix does not write through it. Fix the target in its own project, or run on the target path itself`, "fix-outside-root");
|
|
884
|
-
this.file = file;
|
|
885
|
-
this.target = target;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
class PackageJsonError extends MermaidLintError {
|
|
890
|
-
file;
|
|
891
|
-
constructor(file) {
|
|
892
|
-
super("package-json", `${file} is not a JSON object; fix or remove it`);
|
|
893
|
-
this.file = file;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
class FixNotConvergingError extends MermaidLintError {
|
|
898
|
-
file;
|
|
899
|
-
rules;
|
|
900
|
-
constructor(file, passes, rules) {
|
|
901
|
-
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
902
|
-
this.file = file;
|
|
903
|
-
this.rules = rules;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
class PluginLoadError extends MermaidLintError {
|
|
908
|
-
source;
|
|
909
|
-
constructor(source, reason, code = "plugin") {
|
|
910
|
-
super(code, `plugin ${source}: ${reason}`);
|
|
911
|
-
this.source = source;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
class PluginImportError extends PluginLoadError {
|
|
916
|
-
constructor(source, reason) {
|
|
917
|
-
super(source, `could not be imported: ${reason}`, "plugin-import");
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
class PluginContractError extends PluginLoadError {
|
|
922
|
-
constructor(source, reason) {
|
|
923
|
-
super(source, reason, "plugin-contract");
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
class PluginConflictError extends PluginLoadError {
|
|
928
|
-
other;
|
|
929
|
-
constructor(source, other, reason) {
|
|
930
|
-
super(source, reason, "plugin-conflict");
|
|
931
|
-
this.other = other;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
class PluginNotInstalledError extends PluginLoadError {
|
|
936
|
-
root;
|
|
937
|
-
constructor(source, root) {
|
|
938
|
-
super(source, `is declared or listed but not installed; looked from ${root} upward. Install it, or remove it`, "plugin-not-installed");
|
|
939
|
-
this.root = root;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
class PluginSourceError extends PluginLoadError {
|
|
944
|
-
constructor(source, reason) {
|
|
945
|
-
super(source, reason, "plugin-source");
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
class PluginFixerError extends MermaidLintError {
|
|
950
|
-
rule;
|
|
951
|
-
constructor(rule, reason) {
|
|
952
|
-
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
953
|
-
this.rule = rule;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
|
|
957
957
|
// src/parse/contrast.ts
|
|
958
958
|
import { existsSync } from "node:fs";
|
|
959
959
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -1749,6 +1749,15 @@ var buildRegistry = (input) => {
|
|
|
1749
1749
|
var BUILTIN = buildRegistry({ rules: RULES, fixOrder: FIX_ORDER, categories: BUILTIN_CATEGORIES });
|
|
1750
1750
|
|
|
1751
1751
|
// src/check.ts
|
|
1752
|
+
var checkRunFlags = (flags, prefix = "") => {
|
|
1753
|
+
const { preset, profile } = flags;
|
|
1754
|
+
if (preset !== undefined && canonicalPreset(preset) === null) {
|
|
1755
|
+
throw new FlagError(`${prefix}preset`, `${prefix}preset must be one of ${PRESET_NAMES.join(", ")}, not "${preset}"`);
|
|
1756
|
+
}
|
|
1757
|
+
if (profile !== undefined && !PROFILES.includes(profile)) {
|
|
1758
|
+
throw new FlagError(`${prefix}profile`, `${prefix}profile must be one of ${PROFILES.join(", ")}, not "${profile}"`);
|
|
1759
|
+
}
|
|
1760
|
+
};
|
|
1752
1761
|
var optionsFor = (rule, configured, flags) => {
|
|
1753
1762
|
const options = { ...configured };
|
|
1754
1763
|
for (const name of ["preset", "profile"]) {
|
|
@@ -1759,6 +1768,7 @@ var optionsFor = (rule, configured, flags) => {
|
|
|
1759
1768
|
return options;
|
|
1760
1769
|
};
|
|
1761
1770
|
var checkModel = async (doc, file, flags = {}, registry = BUILTIN) => {
|
|
1771
|
+
checkRunFlags(flags);
|
|
1762
1772
|
const perRule = registry.rules.map((rule) => rule.check({ doc, file, options: optionsFor(rule, registry.options.get(rule.id), flags), helpers: HELPERS }));
|
|
1763
1773
|
const findings = (await Promise.all(perRule)).flat();
|
|
1764
1774
|
return findings.sort((a, b) => a.line - b.line || a.rule.localeCompare(b.rule));
|
|
@@ -1771,7 +1781,7 @@ import { parseArgs } from "node:util";
|
|
|
1771
1781
|
// package.json
|
|
1772
1782
|
var package_default = {
|
|
1773
1783
|
name: "@jpeakai/mermaidjs-lint",
|
|
1774
|
-
version: "0.4.
|
|
1784
|
+
version: "0.4.1",
|
|
1775
1785
|
type: "module",
|
|
1776
1786
|
description: "Deterministic lint gates and verified fixers for Mermaid diagrams in markdown",
|
|
1777
1787
|
license: "MIT",
|
|
@@ -1986,6 +1996,7 @@ var overlaps = (edits) => {
|
|
|
1986
1996
|
};
|
|
1987
1997
|
var wellFormed = (e, length) => Number.isInteger(e.start) && Number.isInteger(e.end) && e.start >= 0 && e.start <= e.end && e.end <= length && typeof e.text === "string";
|
|
1988
1998
|
var fixSourceReport = async (src, file, flags = {}, registry = BUILTIN) => {
|
|
1999
|
+
checkRunFlags(flags);
|
|
1989
2000
|
const kind = documentKind(file);
|
|
1990
2001
|
const applied = [];
|
|
1991
2002
|
const refused = [];
|
|
@@ -2600,6 +2611,7 @@ Examples of every rule, with fixes: https://jpeakai.github.io/jpai-mermaidjs-lin
|
|
|
2600
2611
|
numeric threshold set in the config overrides both.
|
|
2601
2612
|
--profile NAME sets how contrast is read: ${PROFILES.join(", ")}. It overrides a profile set in the config.
|
|
2602
2613
|
auto reads mkdocs-material when any parent folder holds mkdocs.yml or mkdocs.yaml, else github.
|
|
2614
|
+
--json prints one JSON object instead of text lines.
|
|
2603
2615
|
--fix applies every fix it can prove safe, then reports what remains.
|
|
2604
2616
|
A fixer that is unsure leaves the text alone and the finding stays.
|
|
2605
2617
|
--config FILE reads that config instead of mermaidjs-lint.config.json or .mjs at the project root.
|
|
@@ -2727,12 +2739,7 @@ var run = async (argv, cwd) => {
|
|
|
2727
2739
|
}
|
|
2728
2740
|
const listing = values["list-rules"];
|
|
2729
2741
|
const validating = values["validate-plugins"];
|
|
2730
|
-
|
|
2731
|
-
throw new FlagError("--preset", `--preset must be one of ${PRESET_NAMES.join(", ")}, not "${values.preset}"`);
|
|
2732
|
-
}
|
|
2733
|
-
if (values.profile !== undefined && !PROFILES.includes(values.profile)) {
|
|
2734
|
-
throw new FlagError("--profile", `--profile must be one of ${PROFILES.join(", ")}, not "${values.profile}"`);
|
|
2735
|
-
}
|
|
2742
|
+
checkRunFlags({ preset: values.preset, profile: values.profile }, "--");
|
|
2736
2743
|
if (values["no-plugins"] && values["lenient-plugins"]) {
|
|
2737
2744
|
throw new FlagError("--lenient-plugins", "--no-plugins and --lenient-plugins contradict each other");
|
|
2738
2745
|
}
|
package/dist/testing.js
CHANGED
|
@@ -1,6 +1,82 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
3
3
|
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
class MermaidLintError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
constructor(code, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = new.target.name;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class UsageError extends MermaidLintError {
|
|
15
|
+
constructor(message, code = "usage") {
|
|
16
|
+
super(code, message);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class FlagError extends UsageError {
|
|
21
|
+
flag;
|
|
22
|
+
constructor(flag, message) {
|
|
23
|
+
super(message, "flag");
|
|
24
|
+
this.flag = flag;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
class UnknownRuleError extends UsageError {
|
|
28
|
+
file;
|
|
29
|
+
rule;
|
|
30
|
+
known;
|
|
31
|
+
constructor(file, rule, known) {
|
|
32
|
+
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
33
|
+
this.file = file;
|
|
34
|
+
this.rule = rule;
|
|
35
|
+
this.known = known;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class RuleOptionError extends UsageError {
|
|
40
|
+
file;
|
|
41
|
+
rule;
|
|
42
|
+
option;
|
|
43
|
+
constructor(file, rule, option, message) {
|
|
44
|
+
super(`${file}: ${message}`, "rule-option");
|
|
45
|
+
this.file = file;
|
|
46
|
+
this.rule = rule;
|
|
47
|
+
this.option = option;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class FixNotConvergingError extends MermaidLintError {
|
|
51
|
+
file;
|
|
52
|
+
rules;
|
|
53
|
+
constructor(file, passes, rules) {
|
|
54
|
+
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
55
|
+
this.file = file;
|
|
56
|
+
this.rules = rules;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class PluginLoadError extends MermaidLintError {
|
|
61
|
+
source;
|
|
62
|
+
constructor(source, reason, code = "plugin") {
|
|
63
|
+
super(code, `plugin ${source}: ${reason}`);
|
|
64
|
+
this.source = source;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class PluginContractError extends PluginLoadError {
|
|
68
|
+
constructor(source, reason) {
|
|
69
|
+
super(source, reason, "plugin-contract");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
class PluginFixerError extends MermaidLintError {
|
|
73
|
+
rule;
|
|
74
|
+
constructor(rule, reason) {
|
|
75
|
+
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
76
|
+
this.rule = rule;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
4
80
|
// src/model.ts
|
|
5
81
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
6
82
|
import { frontmatterFromMarkdown } from "mdast-util-frontmatter";
|
|
@@ -776,74 +852,6 @@ var buildDocModel = (src, kind = "markdown") => {
|
|
|
776
852
|
return { src, kind, tree, diagrams };
|
|
777
853
|
};
|
|
778
854
|
|
|
779
|
-
// src/errors.ts
|
|
780
|
-
class MermaidLintError extends Error {
|
|
781
|
-
code;
|
|
782
|
-
constructor(code, message) {
|
|
783
|
-
super(message);
|
|
784
|
-
this.code = code;
|
|
785
|
-
this.name = new.target.name;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
class UsageError extends MermaidLintError {
|
|
790
|
-
constructor(message, code = "usage") {
|
|
791
|
-
super(code, message);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
class UnknownRuleError extends UsageError {
|
|
795
|
-
file;
|
|
796
|
-
rule;
|
|
797
|
-
known;
|
|
798
|
-
constructor(file, rule, known) {
|
|
799
|
-
super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
|
|
800
|
-
this.file = file;
|
|
801
|
-
this.rule = rule;
|
|
802
|
-
this.known = known;
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
class RuleOptionError extends UsageError {
|
|
807
|
-
file;
|
|
808
|
-
rule;
|
|
809
|
-
option;
|
|
810
|
-
constructor(file, rule, option, message) {
|
|
811
|
-
super(`${file}: ${message}`, "rule-option");
|
|
812
|
-
this.file = file;
|
|
813
|
-
this.rule = rule;
|
|
814
|
-
this.option = option;
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
class FixNotConvergingError extends MermaidLintError {
|
|
818
|
-
file;
|
|
819
|
-
rules;
|
|
820
|
-
constructor(file, passes, rules) {
|
|
821
|
-
super("fix-not-converging", `${file}: --fix did not converge after ${passes} passes; ${rules.join(", ")} kept proposing edits. A fixer must propose nothing once its edit is applied`);
|
|
822
|
-
this.file = file;
|
|
823
|
-
this.rules = rules;
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
class PluginLoadError extends MermaidLintError {
|
|
828
|
-
source;
|
|
829
|
-
constructor(source, reason, code = "plugin") {
|
|
830
|
-
super(code, `plugin ${source}: ${reason}`);
|
|
831
|
-
this.source = source;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
class PluginContractError extends PluginLoadError {
|
|
835
|
-
constructor(source, reason) {
|
|
836
|
-
super(source, reason, "plugin-contract");
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
class PluginFixerError extends MermaidLintError {
|
|
840
|
-
rule;
|
|
841
|
-
constructor(rule, reason) {
|
|
842
|
-
super("plugin-fixer", `plugin rule ${rule} ${reason}`);
|
|
843
|
-
this.rule = rule;
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
855
|
// src/parse/contrast.ts
|
|
848
856
|
import { existsSync } from "node:fs";
|
|
849
857
|
import { dirname, join, resolve } from "node:path";
|
|
@@ -1638,6 +1646,15 @@ var buildRegistry = (input) => {
|
|
|
1638
1646
|
var BUILTIN = buildRegistry({ rules: RULES, fixOrder: FIX_ORDER, categories: BUILTIN_CATEGORIES });
|
|
1639
1647
|
|
|
1640
1648
|
// src/check.ts
|
|
1649
|
+
var checkRunFlags = (flags, prefix = "") => {
|
|
1650
|
+
const { preset, profile } = flags;
|
|
1651
|
+
if (preset !== undefined && canonicalPreset(preset) === null) {
|
|
1652
|
+
throw new FlagError(`${prefix}preset`, `${prefix}preset must be one of ${PRESET_NAMES.join(", ")}, not "${preset}"`);
|
|
1653
|
+
}
|
|
1654
|
+
if (profile !== undefined && !PROFILES.includes(profile)) {
|
|
1655
|
+
throw new FlagError(`${prefix}profile`, `${prefix}profile must be one of ${PROFILES.join(", ")}, not "${profile}"`);
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1641
1658
|
var optionsFor = (rule, configured, flags) => {
|
|
1642
1659
|
const options = { ...configured };
|
|
1643
1660
|
for (const name of ["preset", "profile"]) {
|
|
@@ -1648,6 +1665,7 @@ var optionsFor = (rule, configured, flags) => {
|
|
|
1648
1665
|
return options;
|
|
1649
1666
|
};
|
|
1650
1667
|
var checkModel = async (doc, file, flags = {}, registry = BUILTIN) => {
|
|
1668
|
+
checkRunFlags(flags);
|
|
1651
1669
|
const perRule = registry.rules.map((rule) => rule.check({ doc, file, options: optionsFor(rule, registry.options.get(rule.id), flags), helpers: HELPERS }));
|
|
1652
1670
|
const findings = (await Promise.all(perRule)).flat();
|
|
1653
1671
|
return findings.sort((a, b) => a.line - b.line || a.rule.localeCompare(b.rule));
|
|
@@ -1739,6 +1757,7 @@ var overlaps = (edits) => {
|
|
|
1739
1757
|
};
|
|
1740
1758
|
var wellFormed = (e, length) => Number.isInteger(e.start) && Number.isInteger(e.end) && e.start >= 0 && e.start <= e.end && e.end <= length && typeof e.text === "string";
|
|
1741
1759
|
var fixSourceReport = async (src, file, flags = {}, registry = BUILTIN) => {
|
|
1760
|
+
checkRunFlags(flags);
|
|
1742
1761
|
const kind = documentKind(file);
|
|
1743
1762
|
const applied = [];
|
|
1744
1763
|
const refused = [];
|
package/dist/types/check.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface RunFlags {
|
|
|
5
5
|
preset?: string;
|
|
6
6
|
profile?: string;
|
|
7
7
|
}
|
|
8
|
+
export declare const checkRunFlags: (flags: RunFlags, prefix?: string) => void;
|
|
8
9
|
export declare const optionsFor: (rule: Rule, configured: RuleOptions | undefined, flags: RunFlags) => RuleOptions;
|
|
9
10
|
export declare const checkModel: (doc: DocModel, file: string, flags?: RunFlags, registry?: Registry) => Promise<Finding[]>;
|
|
10
11
|
export declare const checkMarkdown: (src: string, file: string, flags?: RunFlags, registry?: Registry) => Promise<Finding[]>;
|
package/package.json
CHANGED
package/src/check.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
// them. A rule may be synchronous or return a promise, so a rule that waits on
|
|
5
5
|
// a tool or a service overlaps with the others.
|
|
6
6
|
|
|
7
|
+
import { FlagError } from "./errors.ts";
|
|
7
8
|
import { buildDocModel, type DocModel, documentKind } from "./model.ts";
|
|
9
|
+
import { PROFILES } from "./parse/contrast.ts";
|
|
10
|
+
import { canonicalPreset, PRESET_NAMES } from "./parse/metrics.ts";
|
|
8
11
|
import { BUILTIN, HELPERS, type Registry } from "./rules/registry.ts";
|
|
9
12
|
import type { Finding, Rule, RuleOptions } from "./rules/types.ts";
|
|
10
13
|
|
|
@@ -15,6 +18,22 @@ export interface RunFlags {
|
|
|
15
18
|
profile?: string;
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
// A preset or profile that no rule can take fails as a usage error, with the
|
|
22
|
+
// code the command gives a bad flag, so a library caller can branch on it. The
|
|
23
|
+
// command passes "--" as the prefix, so the message names its own flag.
|
|
24
|
+
export const checkRunFlags = (flags: RunFlags, prefix = ""): void => {
|
|
25
|
+
const { preset, profile } = flags;
|
|
26
|
+
if (preset !== undefined && canonicalPreset(preset) === null) {
|
|
27
|
+
throw new FlagError(
|
|
28
|
+
`${prefix}preset`,
|
|
29
|
+
`${prefix}preset must be one of ${PRESET_NAMES.join(", ")}, not "${preset}"`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (profile !== undefined && !PROFILES.includes(profile as (typeof PROFILES)[number])) {
|
|
33
|
+
throw new FlagError(`${prefix}profile`, `${prefix}profile must be one of ${PROFILES.join(", ")}, not "${profile}"`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
18
37
|
// The options a rule runs with: its configured options, then a flag for any
|
|
19
38
|
// option the rule declares.
|
|
20
39
|
export const optionsFor = (rule: Rule, configured: RuleOptions | undefined, flags: RunFlags): RuleOptions => {
|
|
@@ -32,6 +51,7 @@ export const checkModel = async (
|
|
|
32
51
|
flags: RunFlags = {},
|
|
33
52
|
registry: Registry = BUILTIN,
|
|
34
53
|
): Promise<Finding[]> => {
|
|
54
|
+
checkRunFlags(flags);
|
|
35
55
|
const perRule = registry.rules.map((rule) =>
|
|
36
56
|
rule.check({ doc, file, options: optionsFor(rule, registry.options.get(rule.id), flags), helpers: HELPERS }),
|
|
37
57
|
);
|
package/src/cli.ts
CHANGED
|
@@ -5,13 +5,13 @@ import { lstat, readFile, realpath, writeFile } from "node:fs/promises";
|
|
|
5
5
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
7
|
import pkg from "../package.json" with { type: "json" };
|
|
8
|
-
import { checkModel, type RunFlags } from "./check.ts";
|
|
8
|
+
import { checkModel, checkRunFlags, type RunFlags } from "./check.ts";
|
|
9
9
|
import { FixOutsideRootError, FlagError, MermaidLintError, UsageError } from "./errors.ts";
|
|
10
10
|
import { collectFiles, DIAGRAM_FILE_EXTS, SKIP_DIRS } from "./files.ts";
|
|
11
11
|
import { fixSourceReport } from "./fix/engine.ts";
|
|
12
12
|
import { buildDocModel, DIAGRAM_EXTS, documentKind } from "./model.ts";
|
|
13
13
|
import { PROFILES } from "./parse/contrast.ts";
|
|
14
|
-
import {
|
|
14
|
+
import { PRESET_NAMES } from "./parse/metrics.ts";
|
|
15
15
|
import type { PluginFailure } from "./plugins/index.ts";
|
|
16
16
|
import { setUp } from "./project.ts";
|
|
17
17
|
import { RULES } from "./rules/index.ts";
|
|
@@ -52,6 +52,7 @@ Examples of every rule, with fixes: https://jpeakai.github.io/jpai-mermaidjs-lin
|
|
|
52
52
|
numeric threshold set in the config overrides both.
|
|
53
53
|
--profile NAME sets how contrast is read: ${PROFILES.join(", ")}. It overrides a profile set in the config.
|
|
54
54
|
auto reads mkdocs-material when any parent folder holds mkdocs.yml or mkdocs.yaml, else github.
|
|
55
|
+
--json prints one JSON object instead of text lines.
|
|
55
56
|
--fix applies every fix it can prove safe, then reports what remains.
|
|
56
57
|
A fixer that is unsure leaves the text alone and the finding stays.
|
|
57
58
|
--config FILE reads that config instead of mermaidjs-lint.config.json or .mjs at the project root.
|
|
@@ -220,12 +221,7 @@ const run = async (argv: string[], cwd: string): Promise<number> => {
|
|
|
220
221
|
}
|
|
221
222
|
const listing = values["list-rules"];
|
|
222
223
|
const validating = values["validate-plugins"];
|
|
223
|
-
|
|
224
|
-
throw new FlagError("--preset", `--preset must be one of ${PRESET_NAMES.join(", ")}, not "${values.preset}"`);
|
|
225
|
-
}
|
|
226
|
-
if (values.profile !== undefined && !PROFILES.includes(values.profile as (typeof PROFILES)[number])) {
|
|
227
|
-
throw new FlagError("--profile", `--profile must be one of ${PROFILES.join(", ")}, not "${values.profile}"`);
|
|
228
|
-
}
|
|
224
|
+
checkRunFlags({ preset: values.preset, profile: values.profile }, "--");
|
|
229
225
|
if (values["no-plugins"] && values["lenient-plugins"]) {
|
|
230
226
|
throw new FlagError("--lenient-plugins", "--no-plugins and --lenient-plugins contradict each other");
|
|
231
227
|
}
|
package/src/fix/engine.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// or single edit that survives verification. Offsets are only valid for the source they were
|
|
4
4
|
// computed from, so every accepted edit restarts the pass.
|
|
5
5
|
|
|
6
|
-
import { optionsFor, type RunFlags } from "../check.ts";
|
|
6
|
+
import { checkRunFlags, optionsFor, type RunFlags } from "../check.ts";
|
|
7
7
|
import { FixNotConvergingError } from "../errors.ts";
|
|
8
8
|
import { buildDocModel, documentKind } from "../model.ts";
|
|
9
9
|
import { BUILTIN, HELPERS, type Registry } from "../rules/registry.ts";
|
|
@@ -46,6 +46,7 @@ export const fixSourceReport = async (
|
|
|
46
46
|
flags: RunFlags = {},
|
|
47
47
|
registry: Registry = BUILTIN,
|
|
48
48
|
): Promise<FixReport> => {
|
|
49
|
+
checkRunFlags(flags);
|
|
49
50
|
const kind = documentKind(file);
|
|
50
51
|
const applied: Edit[] = [];
|
|
51
52
|
const refused: Edit[] = [];
|