@jpeakai/mermaidjs-lint 0.3.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/README.md CHANGED
@@ -5,8 +5,9 @@ Deterministic checks and verified fixes for Mermaid diagrams in markdown.
5
5
 
6
6
  **[Docs](https://jpeakai.github.io/jpai-mermaidjs-lint/)** | **[Rules](https://jpeakai.github.io/jpai-mermaidjs-lint/reference/rules/)** | **[Changelog](CHANGELOG.md)** | **[GitHub](https://github.com/jpeakai/jpai-mermaidjs-lint)** | **[npm](https://www.npmjs.com/package/@jpeakai/mermaidjs-lint)**
7
7
 
8
- This README and the docs site describe the `main` branch.
9
- A feature on `main` but not yet on npm is listed under `Unreleased` in the [changelog](CHANGELOG.md).
8
+ The docs site describes the latest release on npm.
9
+ This README, read on GitHub, describes the `main` branch.
10
+ A change on `main` that is not yet released is listed under `Unreleased` in the [changelog](CHANGELOG.md).
10
11
 
11
12
  ## Usage
12
13
 
@@ -40,14 +41,14 @@ Three tenets are guarantees about every diagram the tool passes, and the fourth
40
41
  | Extensible | Local rules and plugin packages |
41
42
 
42
43
  The [concepts page](https://jpeakai.github.io/jpai-mermaidjs-lint/concepts/) states the limit of each tenet.
43
- `mermaidjs-lint --list-rules` prints every rule.
44
+ `bunx @jpeakai/mermaidjs-lint --list-rules` prints every rule.
44
45
  The [rules page](https://jpeakai.github.io/jpai-mermaidjs-lint/reference/rules/) draws each one before and after `--fix`, and [RULES.md](RULES.md) holds the same examples as plain markdown.
45
46
 
46
47
  Every finding fails the run, and a rule you do not want is switched off in the config.
47
48
  Node, edge and subgraph counts come from mermaid's own parsers.
48
49
  Style directives are read line by line, so a style joined to other statements by a semicolon is not seen.
49
50
  Every fix is verified by parsing the document again, and a fix that fails the check is refused, so the finding stays for a human.
50
- The check holds the markdown outside the diagrams unchanged, and each diagram's type, node ids, subgraph names and counts.
51
+ The check holds the markdown outside the diagrams unchanged, and each diagram's type, node ids, subgraph names and counts, and whether it parses.
51
52
  It does not compare edge endpoints, edge labels or node text, so review the diff a fix writes.
52
53
 
53
54
  ## Requirements and compatibility
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.3.0",
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";
@@ -281,6 +459,7 @@ async function extractStats(content) {
281
459
  edges: 0,
282
460
  subgraphs: 0,
283
461
  max_subgraph_depth: 0,
462
+ max_tree_depth: 0,
284
463
  node_ids: [],
285
464
  subgraph_names: [],
286
465
  parser_used: langium ? "langium" : "mermaid-core",
@@ -330,6 +509,7 @@ async function extractLangiumStats(content, keyword) {
330
509
  const subgraphNames = [];
331
510
  let edges = 0;
332
511
  let maxDepth = 0;
512
+ let treeDepth = 0;
333
513
  switch (mapped) {
334
514
  case "architecture": {
335
515
  const services = ast.services ?? [];
@@ -376,7 +556,7 @@ async function extractLangiumStats(content, keyword) {
376
556
  const stats = indentTreeStats(nodes.map((n, i) => ({ label: n.name ?? `node_${i}`, indent: n.indent ?? 0 })));
377
557
  nodeIds.push(...stats.labels);
378
558
  edges = stats.edges;
379
- maxDepth = stats.depth;
559
+ treeDepth = stats.depth;
380
560
  break;
381
561
  }
382
562
  case "treemap": {
@@ -384,7 +564,7 @@ async function extractLangiumStats(content, keyword) {
384
564
  const stats = indentTreeStats(rows.map((r, i) => ({ label: r.item?.name ?? `row_${i}`, indent: r.indent ?? 0 })));
385
565
  nodeIds.push(...stats.labels);
386
566
  edges = stats.edges;
387
- maxDepth = stats.depth;
567
+ treeDepth = stats.depth;
388
568
  break;
389
569
  }
390
570
  case "wardley": {
@@ -404,6 +584,7 @@ async function extractLangiumStats(content, keyword) {
404
584
  edges,
405
585
  subgraphs: subgraphNames.length,
406
586
  max_subgraph_depth: maxDepth,
587
+ max_tree_depth: treeDepth,
407
588
  node_ids: [...new Set(nodeIds.filter(Boolean))].sort(),
408
589
  subgraph_names: subgraphNames,
409
590
  parser_used: "langium",
@@ -454,12 +635,13 @@ async function extractCoreStats(content, keyword) {
454
635
  if (!adapter) {
455
636
  throw new Error(`no core adapter for diagram.type="${diagramType}" (keyword="${keyword}")`);
456
637
  }
457
- const { nodeIds, edges, subgraphNames, maxDepth } = adapter(diagram.db);
638
+ const { nodeIds, edges, subgraphNames, maxDepth, treeDepth = 0 } = adapter(diagram.db);
458
639
  return {
459
640
  nodes: nodeIds.length,
460
641
  edges,
461
642
  subgraphs: subgraphNames.length,
462
643
  max_subgraph_depth: maxDepth,
644
+ max_tree_depth: treeDepth,
463
645
  node_ids: [...nodeIds].sort(),
464
646
  subgraph_names: subgraphNames,
465
647
  parser_used: "mermaid-core",
@@ -470,6 +652,7 @@ async function extractCoreStats(content, keyword) {
470
652
  var CORE_ADAPTERS = {
471
653
  "flowchart-v2": adaptFlowchart,
472
654
  flowchart: adaptFlowchart,
655
+ "flowchart-elk": adaptFlowchart,
473
656
  graph: adaptFlowchart,
474
657
  classDiagram: adaptClass,
475
658
  "classDiagram-v2": adaptClass,
@@ -593,7 +776,13 @@ function adaptIshikawa(db) {
593
776
  walk(c, depth + 1);
594
777
  };
595
778
  walk(root, 0);
596
- return { nodeIds: labels, edges: Math.max(0, labels.length - 1), subgraphNames: [], maxDepth };
779
+ return {
780
+ nodeIds: labels,
781
+ edges: Math.max(0, labels.length - 1),
782
+ subgraphNames: [],
783
+ maxDepth: 0,
784
+ treeDepth: maxDepth
785
+ };
597
786
  }
598
787
  function adaptKanban(db) {
599
788
  const data = callGetter(db, "getData");
@@ -621,7 +810,13 @@ function adaptMindmap(db) {
621
810
  walk(c, depth + 1);
622
811
  };
623
812
  walk(root, 0);
624
- return { nodeIds: labels, edges: Math.max(0, labels.length - 1), subgraphNames: [], maxDepth };
813
+ return {
814
+ nodeIds: labels,
815
+ edges: Math.max(0, labels.length - 1),
816
+ subgraphNames: [],
817
+ maxDepth: 0,
818
+ treeDepth: maxDepth
819
+ };
625
820
  }
626
821
  function adaptQuadrantChart(db) {
627
822
  const data = callGetter(db, "getQuadrantData");
@@ -837,184 +1032,6 @@ var buildDocModel = (src, kind = "markdown") => {
837
1032
  return { src, kind, tree, diagrams };
838
1033
  };
839
1034
 
840
- // src/errors.ts
841
- class MermaidLintError extends Error {
842
- code;
843
- constructor(code, message) {
844
- super(message);
845
- this.code = code;
846
- this.name = new.target.name;
847
- }
848
- }
849
-
850
- class UsageError extends MermaidLintError {
851
- constructor(message, code = "usage") {
852
- super(code, message);
853
- }
854
- }
855
-
856
- class FlagError extends UsageError {
857
- flag;
858
- constructor(flag, message) {
859
- super(message, "flag");
860
- this.flag = flag;
861
- }
862
- }
863
-
864
- class ConfigFileError extends UsageError {
865
- file;
866
- constructor(file, message) {
867
- super(`${file}: ${message}`, "config-file");
868
- this.file = file;
869
- }
870
- }
871
-
872
- class ConfigNotFoundError extends UsageError {
873
- path;
874
- constructor(path) {
875
- super(`--config ${path}: no such file`, "config-not-found");
876
- this.path = path;
877
- }
878
- }
879
-
880
- class ConfigCodeError extends UsageError {
881
- file;
882
- constructor(file) {
883
- 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");
884
- this.file = file;
885
- }
886
- }
887
-
888
- class AmbiguousConfigError extends UsageError {
889
- files;
890
- constructor(root, files) {
891
- super(`more than one config file at ${root}: ${files.join(", ")}`, "config-ambiguous");
892
- this.files = files;
893
- }
894
- }
895
-
896
- class ConfigShapeError extends UsageError {
897
- file;
898
- key;
899
- constructor(file, message, key = null) {
900
- super(`${file}: ${message}`, "config-shape");
901
- this.file = file;
902
- this.key = key;
903
- }
904
- }
905
-
906
- class RuleSettingError extends UsageError {
907
- file;
908
- rule;
909
- constructor(file, rule, message) {
910
- super(`${file}: rule "${rule}" ${message}`, "rule-setting");
911
- this.file = file;
912
- this.rule = rule;
913
- }
914
- }
915
-
916
- class UnknownRuleError extends UsageError {
917
- file;
918
- rule;
919
- known;
920
- constructor(file, rule, known) {
921
- super(`${file}: unknown rule "${rule}"; a retired or misspelt id cannot be switched off. Known rules: ${known.join(", ")}`, "unknown-rule");
922
- this.file = file;
923
- this.rule = rule;
924
- this.known = known;
925
- }
926
- }
927
-
928
- class RuleOptionError extends UsageError {
929
- file;
930
- rule;
931
- option;
932
- constructor(file, rule, option, message) {
933
- super(`${file}: ${message}`, "rule-option");
934
- this.file = file;
935
- this.rule = rule;
936
- this.option = option;
937
- }
938
- }
939
-
940
- class FixOutsideRootError extends UsageError {
941
- file;
942
- target;
943
- constructor(file, target, root) {
944
- 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 name it directly`, "fix-outside-root");
945
- this.file = file;
946
- this.target = target;
947
- }
948
- }
949
-
950
- class PackageJsonError extends MermaidLintError {
951
- file;
952
- constructor(file) {
953
- super("package-json", `${file} is not a JSON object; fix or remove it`);
954
- this.file = file;
955
- }
956
- }
957
-
958
- class FixNotConvergingError extends MermaidLintError {
959
- file;
960
- rules;
961
- constructor(file, passes, rules) {
962
- 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`);
963
- this.file = file;
964
- this.rules = rules;
965
- }
966
- }
967
-
968
- class PluginLoadError extends MermaidLintError {
969
- source;
970
- constructor(source, reason, code = "plugin") {
971
- super(code, `plugin ${source}: ${reason}`);
972
- this.source = source;
973
- }
974
- }
975
-
976
- class PluginImportError extends PluginLoadError {
977
- constructor(source, reason) {
978
- super(source, `could not be imported: ${reason}`, "plugin-import");
979
- }
980
- }
981
-
982
- class PluginContractError extends PluginLoadError {
983
- constructor(source, reason) {
984
- super(source, reason, "plugin-contract");
985
- }
986
- }
987
-
988
- class PluginConflictError extends PluginLoadError {
989
- other;
990
- constructor(source, other, reason) {
991
- super(source, reason, "plugin-conflict");
992
- this.other = other;
993
- }
994
- }
995
-
996
- class PluginNotInstalledError extends PluginLoadError {
997
- root;
998
- constructor(source, root) {
999
- super(source, `is declared or listed but not installed; looked from ${root} upward. Install it, or remove it`, "plugin-not-installed");
1000
- this.root = root;
1001
- }
1002
- }
1003
-
1004
- class PluginSourceError extends PluginLoadError {
1005
- constructor(source, reason) {
1006
- super(source, reason, "plugin-source");
1007
- }
1008
- }
1009
-
1010
- class PluginFixerError extends MermaidLintError {
1011
- rule;
1012
- constructor(rule, reason) {
1013
- super("plugin-fixer", `plugin rule ${rule} ${reason}`);
1014
- this.rule = rule;
1015
- }
1016
- }
1017
-
1018
1035
  // src/parse/contrast.ts
1019
1036
  import { existsSync } from "node:fs";
1020
1037
  import { dirname, join, resolve } from "node:path";
@@ -1356,7 +1373,7 @@ var calculateComplexity = (stats, config) => {
1356
1373
  const n = stats.nodes;
1357
1374
  const e = stats.edges;
1358
1375
  const s = stats.subgraphs;
1359
- const d = stats.max_subgraph_depth;
1376
+ const d = Math.max(stats.max_subgraph_depth, stats.max_tree_depth);
1360
1377
  const base = n + e * config.edge_weight + s * config.subgraph_weight;
1361
1378
  const vcs = base * (1 + d * config.depth_weight);
1362
1379
  const maxEdges = n > 1 ? n * (n - 1) : 0;
@@ -1694,7 +1711,7 @@ var parseFenceUnterminated = {
1694
1711
  id: RULE.FENCE_UNTERMINATED,
1695
1712
  category: "parse",
1696
1713
  summary: "a mermaid fence that is never closed",
1697
- check: (ctx) => ctx.doc.diagrams.filter((d) => !d.terminated).map((d) => finding(ctx, d, RULE.FENCE_UNTERMINATED, `mermaid fence opened at line ${d.startLine} is never closed`, "Add a closing fence after the last line of the diagram.")),
1714
+ check: (ctx) => ctx.doc.diagrams.filter((d) => !d.terminated).map((d) => finding(ctx, d, RULE.FENCE_UNTERMINATED, `mermaid fence opened at line ${d.startLine} is never closed`, "Add a closing fence directly after the last line of the diagram, with the opening line's prefix: `> ` in a quote, the item's indent in a list.")),
1698
1715
  fix: (ctx) => {
1699
1716
  const edits = [];
1700
1717
  const { src } = ctx.doc;
@@ -1810,6 +1827,15 @@ var buildRegistry = (input) => {
1810
1827
  var BUILTIN = buildRegistry({ rules: RULES, fixOrder: FIX_ORDER, categories: BUILTIN_CATEGORIES });
1811
1828
 
1812
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
+ };
1813
1839
  var optionsFor = (rule, configured, flags) => {
1814
1840
  const options = { ...configured };
1815
1841
  for (const name of ["preset", "profile"]) {
@@ -1820,6 +1846,7 @@ var optionsFor = (rule, configured, flags) => {
1820
1846
  return options;
1821
1847
  };
1822
1848
  var checkModel = async (doc, file, flags = {}, registry = BUILTIN) => {
1849
+ checkRunFlags(flags);
1823
1850
  const perRule = registry.rules.map((rule) => rule.check({ doc, file, options: optionsFor(rule, registry.options.get(rule.id), flags), helpers: HELPERS }));
1824
1851
  const findings = (await Promise.all(perRule)).flat();
1825
1852
  return findings.sort((a, b) => a.line - b.line || a.rule.localeCompare(b.rule));
@@ -1917,26 +1944,40 @@ var sameDiagrams = async (before, after) => {
1917
1944
  if (!now || was.keyword !== now.keyword)
1918
1945
  return false;
1919
1946
  const [a, b] = await Promise.all([was.stats(), now.stats()]);
1920
- const shape = (s) => [
1947
+ const shape = (s, source) => [
1948
+ parseFailed(source, s),
1921
1949
  s.diagram_type,
1922
1950
  s.nodes,
1923
1951
  s.edges,
1924
1952
  s.subgraphs,
1925
1953
  s.max_subgraph_depth,
1954
+ s.max_tree_depth,
1926
1955
  s.node_ids,
1927
1956
  s.subgraph_names
1928
1957
  ];
1929
- if (!sameJson(shape(a), shape(b)))
1958
+ if (!sameJson(shape(a, was.source), shape(b, now.source)))
1930
1959
  return false;
1931
1960
  }
1932
1961
  return true;
1933
1962
  };
1963
+ var breaksAParse = async (before, after) => {
1964
+ for (const [i, was] of before.diagrams.entries()) {
1965
+ const now = after.diagrams[i];
1966
+ if (!now)
1967
+ continue;
1968
+ const [a, b] = await Promise.all([was.stats(), now.stats()]);
1969
+ if (!parseFailed(was.source, a) && parseFailed(now.source, b))
1970
+ return true;
1971
+ }
1972
+ return false;
1973
+ };
1934
1974
  var verify = async (edit, before, after) => {
1935
1975
  if (edit.expect.kind === "same-tree") {
1936
1976
  if (!sameTree(before.tree, after.tree, {}))
1937
1977
  return "refuse";
1938
- if (before.kind === "markdown" && after.kind === "markdown")
1939
- return "accept";
1978
+ if (before.kind === "markdown" && after.kind === "markdown") {
1979
+ return await breaksAParse(before, after) ? "refuse" : "accept";
1980
+ }
1940
1981
  }
1941
1982
  return await sameDiagrams(before, after) ? "accept" : "refuse";
1942
1983
  };
@@ -1956,6 +1997,7 @@ var overlaps = (edits) => {
1956
1997
  };
1957
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";
1958
1999
  var fixSourceReport = async (src, file, flags = {}, registry = BUILTIN) => {
2000
+ checkRunFlags(flags);
1959
2001
  const kind = documentKind(file);
1960
2002
  const applied = [];
1961
2003
  const refused = [];
@@ -2569,6 +2611,7 @@ Examples of every rule, with fixes: https://jpeakai.github.io/jpai-mermaidjs-lin
2569
2611
  numeric threshold set in the config overrides both.
2570
2612
  --profile NAME sets how contrast is read: ${PROFILES.join(", ")}. It overrides a profile set in the config.
2571
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.
2572
2615
  --fix applies every fix it can prove safe, then reports what remains.
2573
2616
  A fixer that is unsure leaves the text alone and the finding stays.
2574
2617
  --config FILE reads that config instead of mermaidjs-lint.config.json or .mjs at the project root.
@@ -2696,12 +2739,7 @@ var run = async (argv, cwd) => {
2696
2739
  }
2697
2740
  const listing = values["list-rules"];
2698
2741
  const validating = values["validate-plugins"];
2699
- if (values.preset !== undefined && canonicalPreset(values.preset) === null) {
2700
- throw new FlagError("--preset", `--preset must be one of ${PRESET_NAMES.join(", ")}, not "${values.preset}"`);
2701
- }
2702
- if (values.profile !== undefined && !PROFILES.includes(values.profile)) {
2703
- throw new FlagError("--profile", `--profile must be one of ${PROFILES.join(", ")}, not "${values.profile}"`);
2704
- }
2742
+ checkRunFlags({ preset: values.preset, profile: values.profile }, "--");
2705
2743
  if (values["no-plugins"] && values["lenient-plugins"]) {
2706
2744
  throw new FlagError("--lenient-plugins", "--no-plugins and --lenient-plugins contradict each other");
2707
2745
  }