Command line¶
The mermaidjs-lint command, as installed with the package.
To use it in a pipeline, see Gate diagrams in CI.
Help¶
The text below is generated from mermaidjs-lint --help when this site is built.
usage: mermaidjs-lint <path...> [--fix | --split-plan] [--json] [--preset NAME] [--profile NAME] [--config FILE]
[--canvas-light COLOUR] [--canvas-dark COLOUR] [--no-plugins | --lenient-plugins]
[--category NAME]... [--disable-category NAME]...
mermaidjs-lint --list-rules [--config FILE] [--no-plugins | --lenient-plugins] [category flags]
mermaidjs-lint --validate-plugins [--json] [--config FILE]
mermaidjs-lint --version | -h, --help
Deterministic Mermaid diagram gates (* = autofixable):
parse:
parse-diagram-unparseable a diagram mermaid's parser rejects
parse-fence-unterminated* a mermaid fence that is never closed
complexity:
complexity-node-count-hard-limit more nodes than the preset's hard limit
complexity-node-count-cognitive-limit more nodes than a reader can follow, above the cognitive limit
complexity-node-count-acceptable-exceeded more nodes than the preset's readable threshold
complexity-visual-score-critical a visual complexity score above the preset's complex threshold
complexity-visual-score-acceptable-exceeded a visual complexity score above the preset's readable threshold
complexity-subgraph-nesting-too-deep subgraphs nested three or more levels deep
contrast:
contrast-text-below-aa* label text below 4.5:1 against its fill
contrast-border-below-ui-minimum a border below 3:1 against its fill
contrast-directive-unauditable a style directive whose contrast cannot be audited
A path is a file or a directory, and every path must exist. A file ending in
.mmd or .mermaid is read as one raw diagram, and any other file is read as markdown
whose mermaid fences are checked. A directory is walked for .mmd, .mermaid, .md, .markdown files,
skipping dot-folders and node_modules, vendor, dist, build, tmp, __pycache__. The walk does not follow a symbolic link,
and names each one it skipped. A link named as a path is read, but --fix does not write
through one whose target is outside the project root.
Examples of every rule, with fixes: https://jpeakai.github.io/jpai-mermaidjs-lint/reference/rules/
-p, --preset NAME sets the density for every complexity rule: low-density, medium-density, high-density
(aliases low, medium, high). It overrides a preset set in the config, and a
numeric threshold set in the config overrides both.
--profile NAME sets how contrast is read: auto, github, mkdocs-material. It overrides a profile set in the config.
auto reads mkdocs-material when any parent folder holds mkdocs.yml or mkdocs.yaml, else github.
--canvas-light COLOUR and --canvas-dark COLOUR set the page a host draws in each theme, under the github
profile only. A translucent fill, an edge label box and a sequence diagram's page-backed text are scored
on it. Each is an opaque CSS colour such as #faf8f9, and a side not given keeps the config's, or GitHub's
(#ffffff light, #0d1117 dark). A file scored under mkdocs-material refuses a canvas flag with a usage
error, and sets a config canvas aside when auto picked the profile.
--disable-category NAME turns off every rule in that category for this run, after the config is applied.
--category NAME turns on every rule in that category for this run, even one the config turns off.
Each may be repeated or take a comma-separated list, such as --disable-category parse,complexity.
A category that is not loaded, one named by both flags, or a run left with no rule is a usage error.
--json prints one JSON object instead of text lines.
--fix applies every fix it can prove safe, then reports what remains.
A fixer that is unsure leaves the text alone and the finding stays.
--split-plan prints, after the findings, each diagram a node count or score rule flags, split into an
overview and one detail diagram per group, as mermaid fences under a heading naming file:line and the
tier, or why no plan met the contract. It writes no file, and takes no --fix, --json, --list-rules
or --validate-plugins.
--config FILE reads that config instead of mermaidjs-lint.config.json or .mjs at the project root.
Plugins load on their own from .mermaidjs-lint/rules, from dependencies named mermaidjs-lint-plugin,
mermaidjs-lint-plugin-* or @scope/mermaidjs-lint-plugin(-*), and from the config's plugins list.
--no-plugins runs the built-in rules only and loads no plugin. It reads a .json config as usual,
and stops with a usage error rather than import a .mjs config, which is code.
--lenient-plugins skips a plugin that fails to load and says so, instead of stopping the run.
--validate-plugins imports and checks every plugin, reports every failure, and runs no check or fix.
--list-rules prints every active rule by category, including plugin rules, and where each plugin came from.
It imports the plugins to list them. It takes the category flags, to preview a run, and no path or other flag.
--version prints the version, to quote in a bug report.
Exits 0 when no findings remain, 1 when findings remain, a plugin fails to load or the run fails (a lenient run
skips a broken plugin, so it alone does not exit 1), and 2 on a usage error.
Flags¶
| Flag | Short | Value | Default | Effect |
|---|---|---|---|---|
--fix |
off | Apply every fix proven safe, write the file, then report what remains | ||
--split-plan |
off | After the report, print the split plan for each diagram a node count or score rule flags, and write no file | ||
--json |
off | Print one JSON object instead of text lines | ||
--preset |
-p |
a preset name or alias | high-density |
Set the density for every complexity rule |
--profile |
auto, github, mkdocs-material |
auto |
Set how contrast is read | |
--canvas-light |
an opaque CSS colour | #ffffff |
Set the light page colour that page-backed pairs are scored on, under github only |
|
--canvas-dark |
an opaque CSS colour | #0d1117 |
Set the dark page colour that page-backed pairs are scored on, under github only |
|
--category |
a category name, or a comma-separated list | Turn on every rule in that category for this run, even one the config turns off. May be repeated. | ||
--disable-category |
a category name, or a comma-separated list | Turn off every rule in that category for this run. May be repeated. | ||
--config |
a file path | the config at the project root | Read that config file instead | |
--no-plugins |
off | Load no plugin, refuse a .mjs config, and set aside config settings for plugin rules |
||
--lenient-plugins |
off | Skip a plugin that fails to load, and say so | ||
--validate-plugins |
off | Load every plugin, report every failure, and check no file | ||
--list-rules |
off | Print every active rule by category, then the plugins. Takes only --config, --no-plugins or --lenient-plugins, and the category flags. |
||
--version |
Print the version, for a bug report | |||
--help |
-h |
Print the help |
Some flags cannot be combined, and the run exits 2 when they are:
--no-pluginswith--lenient-plugins.--validate-pluginswith--no-plugins,--fix,--list-rules, a category flag or any path.--split-planwith--fix,--json,--list-rulesor--validate-plugins.--categoryand--disable-categorynaming the same category.--list-ruleswith any path,--fix,--json,--preset,--profile,--canvas-lightor--canvas-dark, which it would ignore.--canvas-lightor--canvas-darkwith--profile mkdocs-material.
A canvas colour that will not parse, or is translucent, is error[flag] with exit code 2.
A canvas flag beside --profile mkdocs-material is error[flag].
A file that auto or the config scores under mkdocs-material refuses a canvas flag with error[canvas-profile].
So does a config canvas run with --profile mkdocs-material.
A canvas from the config is set aside, not refused, for a file that auto scores under mkdocs-material.
One stderr line names those files.
A host with its own page sets a canvas explains what the canvas changes.
For example, a viewer with its own page colours runs:
A preset name or alias is matched without regard to case:
| Preset | Aliases |
|---|---|
low-density |
low, l, strict |
medium-density |
medium, med, m, balanced |
high-density |
high, h, permissive, default |
Precedence¶
A complexity threshold or a contrast profile can be set in four places. The first that applies wins:
- A threshold option set on the rule in the config, such as
nodeHardLimit, which beats any preset. - The
--presetor--profileflag. - The
presetorprofileoption set on the rule in the config. - The default, which is
high-densityorauto.
A flag therefore overrides a preset or profile the config sets, but not a threshold the config sets.
A canvas side follows the same order: --canvas-light or --canvas-dark, then that side of the canvas option in the config, then GitHub's colour.
A flag sets only its own side, so --canvas-dark alone keeps a light side the config sets.
Which rules run¶
The rules a run uses are settled in three steps:
- Every loaded rule starts on, built-in and plugin alike.
- The config turns off each rule set to
"off". - The category flags have the last word for this run.
--categoryturns a category's rules back on, and--disable-categoryturns them off.
A rule that --category turns back on runs with the options the config gives it, such as ["off", { "preset": "low" }].
A category is one of parse, complexity and contrast, or a category a loaded plugin declares. --list-rules takes the same flags, so it shows which rules a run would use.
| Run | Rules used |
|---|---|
mermaidjs-lint docs/ --disable-category complexity |
The parse and contrast rules the config leaves on |
mermaidjs-lint docs/ --disable-category parse,complexity |
The contrast rules the config leaves on |
mermaidjs-lint docs/ --category contrast |
Every rule the config leaves on, plus every contrast rule, even one the config turns off |
Each of these is a usage error with exit code 2, as error[flag]:
- A category that no loaded rule source declares, such as a plugin category under
--no-plugins. - One category named by both flags.
- Category flags that turn off every rule, which would pass every file unchecked.
Plugins and untrusted repositories¶
--no-plugins loads no plugin, and never imports a .mjs config, because a .mjs config is code.
When the config it would read is a .mjs file, the run stops with error[config-code] and exit code 2.
Pass --config with a .json config of your own to run anyway.
A .json config is read as usual, because it is data and runs no code.
So the linter itself imports none of the repository's code.
That does not make the repository safe.
Installing its dependencies may already have run their code.
A package runner started inside the checkout reads its bunfig.toml or .npmrc, which can run code before the linter starts.
Run an installed linter from a folder you control, and name the checkout's files by path. SECURITY.md states the full trust model.
Paths¶
| Path | What is checked |
|---|---|
A .mmd or .mermaid file |
The whole file, as one diagram |
| Any other file named explicitly | Every mermaid fence in it, read as markdown whatever its extension |
| A directory | Every .md, .markdown, .mmd and .mermaid file beneath it |
A walk leaves out dot-folders and dependency or build output folders, as --help lists, and a file named explicitly is always checked.
When the paths match no file at all, the run stops with error[flag]: no matching files and exit code 2.
A directory walk never follows a symbolic link, to a file or to a folder.
It names each link it skipped on standard error, as mermaidjs-lint: skipped symbolic link <path>; name it as a path to check it, so a gate never passes a file in silence.
A link named as a path is followed and checked. --fix refuses to write through a named link whose target is outside the project root, with error[fix-outside-root] and exit code 2.
It refuses before it writes any file.
Every path must exist.
A missing path is a usage error, error[flag]: cannot read <path>, with exit code 2.
Exit codes¶
| Code | Meaning |
|---|---|
| 0 | No findings remain |
| 1 | Findings remain, a plugin failed to load in a run that is not lenient, or the run failed, such as with package-json or fix-not-converging |
| 2 | A usage error: an unknown flag, a missing path, no matching files, or a bad config file |
Error codes¶
A failure prints error[code]: reason, and the same code is on the exported error class.
| Code | Exit code | Cause |
|---|---|---|
flag |
2 | An unknown flag, a bad flag value, flags that contradict (such as a canvas flag beside --profile mkdocs-material), a category that is not loaded, category flags that leave no rule, no paths, a path that cannot be read, or no matching files |
config-file |
2 | The config cannot be read or parsed, or is neither .json nor .mjs |
config-not-found |
2 | The file given to --config does not exist |
config-code |
2 | --no-plugins was given, and the config to read is a .mjs file, which is code |
config-ambiguous |
2 | More than one config file at the project root |
config-shape |
2 | An unknown config key, or a key of the wrong type |
rule-setting |
2 | A rule setting that is not a severity, or a severity and options |
unknown-rule |
2 | A rule id that is not loaded. Outside strict mode, a plugin rule id is set aside instead. |
rule-option |
2 | An option the rule does not declare, a value of the wrong type, a preset or profile name that does not exist, a canvas colour that is not an opaque CSS colour, or a canvas beside profile mkdocs-material on the same rule |
canvas-profile |
2 | A canvas from one source met a mkdocs-material profile from another: a canvas flag on a file that the config or auto scores under mkdocs-material, or a config canvas with --profile mkdocs-material. Material's page is its own. |
fix-outside-root |
2 | --fix was given a symbolic link whose target is outside the project root |
package-json |
1 | The project's package.json is not a JSON object. A run with plugins off, through --no-plugins or "plugins": false in the config, reads nothing from it, so it carries on. |
fix-not-converging |
1 | A fixer kept proposing edits for 1000 passes. The message names the rules still editing, and that file is not written. |
plugin-import |
1 | A plugin module could not be imported |
plugin-contract |
1 | A plugin export breaks the contract, or targets another API version |
plugin-conflict |
1 | Another plugin already claimed the namespace or a category word, or two local rule files declare one category with different descriptions |
plugin-not-installed |
1 | A declared plugin package is not installed |
plugin-source |
1 | A plugin source cannot be loaded, such as a .cts file, TypeScript on a runtime that cannot import it, or an installed plugin whose package.json is not a JSON object |
plugin-fixer |
1 | A plugin fixer threw, returned something other than a list, or returned an edit with no valid expectation |
Text output¶
One line per finding, then a summary:
docs/architecture.md:6 contrast-text-below-aa classDef warn: text 1.43:1 < 4.5 (#ffffff on #ffd400)
1 finding(s) in 1 file(s)
A line reads file:line rule message.
A finding that spans a diagram prints its first and last line, as file:3-7.
Split plans¶
--split-plan prints a section after the summary for each diagram that a node count or score rule flags.
The heading names the diagram's file:line and the tier of the plan.
An overview follows, where each group of nodes is folded into one group node.
Then one detail diagram per group shows its members, with a stub for each node outside the group that an edge reaches.
An edge in from another group starts at one stub for that whole group, since that group's own detail draws it to the exact node.
For an erDiagram or classDiagram, a ### Rows diagram follows for the attributes or members of nodes the overview keeps.
Each diagram is a mermaid fence, so the output can be pasted into a markdown file and checked:
## docs/pipeline.md:12: split plan (T2)
### Overview
```mermaid
flowchart LR
...
```
### Detail g1: ingest (+5)
```mermaid
flowchart LR
...
```
When no plan meets the contract, the section says why, and the finding keeps its subgraph hint.
A detail that is still over the target is split once more, under a heading one level down.
The plan is a recommendation, per MJL-0025: nothing is written, and the exit code is the check's.
The plan is not in the --json object, so --split-plan refuses --json rather than drop it.
Standard error carries these lines, each when it applies:
| Line | When |
|---|---|
mermaidjs-lint: loaded acme (1 rule) |
Plugins loaded |
mermaidjs-lint: plugins off; skipped mermaidjs-lint-plugin-acme |
--no-plugins, or "plugins": false in the config, found plugins and loaded none |
mermaidjs-lint: skipped plugin <source> [code]: reason |
A lenient run left a broken plugin out |
mermaidjs-lint: ignored config setting for <id>, whose plugin did not load |
A config setting named a plugin rule that is not loaded |
mermaidjs-lint: skipped symbolic link <path>; name it as a path to check it |
A directory walk met a symbolic link |
mermaidjs-lint: config canvas set aside for N files scored under mkdocs-material: <paths> |
A config canvas met files that auto scores under mkdocs-material. The --json object lists the same paths under canvasSetAside. |
JSON output¶
--json prints one object:
{
"findings": [
{
"file": "docs/architecture.md",
"line": 6,
"endLine": 6,
"rule": "contrast-text-below-aa",
"message": "classDef warn: text 1.43:1 < 4.5 (#ffffff on #ffd400)",
"remediation": "Darken or lighten the fill, or change color:, until the ratio reaches 4.5:1 in every theme."
}
],
"files": 1,
"plugins": [],
"skippedPlugins": [],
"canvasSetAside": []
}
| Key | Holds |
|---|---|
findings |
Each finding: file, line, endLine, rule, message, and remediation when the rule gives one |
files |
How many files were checked |
plugins |
Each loaded plugin: kind (local, package or config), spec, namespace and its rules ids |
skippedPlugins |
Each plugin a lenient run left out: source, kind, code and message |
canvasSetAside |
Each file whose config canvas was set aside because auto scores it under mkdocs-material, as a path relative to the working directory, or absolute when the file is outside it |
Under --no-plugins, skippedPlugins is empty, and the skipped plugins appear only on standard error. --validate-plugins --json prints { valid, plugins, failures } instead, where failures has the shape of skippedPlugins.