Optional linters
These commands use tools installed in your repo (not bundled with Merlon Docs):
| Command | Peer tool | Purpose |
|---|---|---|
merlon lint |
markdownlint-cli2 |
Lint shards and compiled output (GFM / Markdown structure) |
merlon prose |
vale (install separately) |
Prose style lint with the Vale styles your .vale.ini names |
merlon links |
markdown-link-check |
Optional HTTP URL checks (peer; not built-in internal link validation) |
merlon fix |
prettier, markdownlint-cli2 |
Run prettier --write . then markdownlint-cli2 --fix (no config paths) |
merlon check |
markdownlint-cli2, vale, markdown-link-check |
The check gate’s lint, prose and URL stages, each run when its peer is installed and configured |
merlon fix does not bundle formatters. Install Prettier and markdownlint-cli2 in your repo first (node_modules/.bin or PATH). Each step is skipped with an info message if the peer is missing. Merlon Docs passes markdownlint-cli2 no file paths. Its fix step needs a .markdownlint-cli2.jsonc (or another markdownlint-cli2 config file) in the docs root that lists globs. Without one, markdownlint-cli2 prints its usage text and fixes nothing. merlon fix exits 0 whatever either tool returns, so read its output to see whether both steps ran.
merlon lint --require-lint # fail if markdownlint-cli2 is missingmerlon prose --require-vale # fail if Vale is missingmerlon check --require-lint --require-vale # CI gate with markdownlint + Valemerlon check --skip-vale # structural checks onlymerlon check runs built-in internal link validation by default (lint.links.enabled). Peer markdown-link-check runs only when lint.links.config is set and the peer is installed. merlon links checks the file that lint.links.target names, or else the monolith that top-level outputFile names, and exits 1 when the config sets neither. A relative lint.links.target is read from the docs root, not from outputDir, so to check a monolith under the default outputDir, write _build/guides.md. When the peer is missing, it prints an info line and exits 0.
Install npm peers with:
npm install -D prettier markdownlint-cli2 @merlon-docs/merlon-docs-presetsInstall Vale separately so vale is on your PATH, using one of the methods in Vale installation. After adding a .vale.ini, run vale sync in that directory. Merlon Docs looks for vale in node_modules/.bin, from the docs root up, before it looks on PATH. merlon prose and merlon check run Vale in the docs root and pass it the config file that vale.config names, .vale.ini by default.
lint.markdownlint.shardsConfig names the markdownlint-cli2 config file for the shard pass, and lint.markdownlint.compiledConfig names the one for the compiled pass. merlon lint and merlon check run each pass only when its key is set, and Check gate stages lists both passes in the check. A relative path is read from the docs root, so a docs/ root next to node_modules uses ../node_modules/@merlon-docs/merlon-docs-presets/..., as step 1 of Quick start shows.
A locale pack is Merlon Docs’ compile-time wording, not a Vale style. The MerlonDocs Vale style in @merlon-docs/merlon-docs-presets (vale/MerlonDocs/) holds the en-US prose cues. Its rules flag a numbered heading mention with no link, and MerlonDocs.DatedClaim flags as of or until before an ISO date. See Locale and language boundary.
Vale alert level
Section titled “Vale alert level”merlon prose passes no alert level to Vale, so the MinAlertLevel in your .vale.ini sets what it shows. merlon prose --strict and merlon check pass --minAlertLevel from vale.strictMinAlertLevel, which defaults to error. To show warnings in those runs too:
{ "vale": { "strictMinAlertLevel": "warning" }}Of Vale’s alerts, only error-level ones fail merlon prose or merlon check, so a lower vale.strictMinAlertLevel adds warnings or suggestions to the output and leaves the exit code as it was. A Vale runtime error, such as a missing style, also fails both commands. To make a rule fail the check, set its level to error in .vale.ini. A rule set to warning, such as MerlonDocs.DatedClaim = warning, no longer fails merlon check, and merlon prose still shows it when the .vale.ini MinAlertLevel is warning or lower.
In-scope guide fileset
Section titled “In-scope guide fileset”Merlon Docs knows the full fileset it manages. The guides in compileOrder resolve via guides[].path or {docsRoot}/{name}/, and standaloneGuides registers the standalone guides. Shard markdownlint lints the Markdown files in the guide directories. Vale prose lints the guide directories and the standalone guides, because a file that never compiles still reaches readers as written. Both linters skip markdown that the config does not name, such as legacy flat .md files or unregistered sibling folders under --docs-root.
| Command | Default scope | Out of scope (skipped) |
|---|---|---|
Shard markdownlint (merlon lint, merlon check) |
Markdown in compileOrder guide directories |
Legacy flat docs, unrelated subdirs under --docs-root |
Vale prose (merlon prose, merlon check) |
Guide directories plus standaloneGuides files |
Same |
| Compiled markdownlint | Compiled outputs listed in compiledConfig globs |
Shard trees (covered by the shard pass) |
Optional overrides replace the guide directories with the paths you list, so scope never reaches past what the config names:
| Config field | Purpose |
|---|---|
lint.markdownlint.shardsGlobs |
Shard markdownlint paths relative to --docs-root (default: the Markdown in compileOrder guide dirs) |
vale.scanGlobs |
Vale prose paths relative to --docs-root in place of the guide dirs; standalone guides stay in |
Both markdownlint passes run with the docs root as their cwd. For each guide directory, Merlon Docs passes the shard pass a glob for the .md and .markdown files in it, such as features/**/*.{md,markdown}, so an image or text file beside the shards stays out of the shard pass. For a guide under the docs root, the glob is relative to the docs root and leaves the checkout path out. A relative or absolute --docs-root then lints the same shards, even under a directory whose name has glob characters, such as repo (copy). A guide outside the docs root gets an absolute glob. With it, the shard preset’s exclusions still apply to the guide, and markdownlint-cli2 still finds the guide under a symlinked docs root. Merlon Docs escapes the glob characters and quotes in each path it puts in a glob.
A lint.markdownlint.shardsGlobs entry goes to markdownlint-cli2 as written, and markdownlint-cli2 resolves it against the docs root. The exceptions are ., which Merlon Docs passes as **, and an entry that starts with ../, negated or not, which Merlon Docs makes absolute for the same reasons as a guide outside the docs root. Given . as its only path, markdownlint-cli2 would lint the Markdown files at the top of the docs root and nothing below them. It lints every file under any other directory entry, and an entry that starts with ! or # excludes the files it matches. The compiled pass gets only --config from Merlon Docs, and markdownlint-cli2 resolves the globs in compiledConfig against its cwd, the docs root.
Vale lints each standalone guide once, even when it is inside a scanned directory or two standaloneGuides entries match it. The exception is a guide that a scanned directory also reaches through a symlinked subdirectory. Vale follows that link when it walks the directory, so it lints the file under both names.
Vale does not expand globs, so each vale.scanGlobs entry must be a file or directory that exists. Given more than one path, Vale stops with a runtime error on a missing one, and the merlon check hint then points at the config. When a missing path is the only one Vale gets, Vale reads it as literal text, so that run passes without linting any file.
The shard config in @merlon-docs/merlon-docs-presets supplies rules and exclusions. For the shard pass, scope always comes from the CLI, not from preset globs. A guide at . or a . entry in shardsGlobs reaches the whole docs root, and then the shard preset’s exclusions are what keep compiled outputs out of the shard pass. The compiled pass is the opposite. Its scope is the globs in the compiled config, and the compiled preset’s globs match only the monolith paths _build/guides.md and guides.md. The presets README lists what each preset excludes, lints and turns on, and when to copy one, in Shard preset highlights and Compiled preset highlights.
merlon fix is out of band: it runs prettier --write . and markdownlint-cli2 --fix from the docs root and is not part of Merlon Docs’ guide fileset gate. It reaches a standalone guide only when the file is under --docs-root.
Opt a standalone guide out of Vale
Section titled “Opt a standalone guide out of Vale”To keep Vale off one standalone guide, such as vendored text, give it a section at the end of .vale.ini, after every section that matches the file, such as [*.md] or [*.{md,mdx}]:
[*.md]BasedOnStyles = MerlonDocsMerlonDocs.DatedClaim = warning
; Last, after every section that matches the file: the last match wins.[**/CODE_OF_CONDUCT.md]BasedOnStyles =MerlonDocs.DatedClaim = NOVale matches a section against the path it is given, and merlon prose and merlon check pass absolute paths. Start the section with **/ to match them. A section such as [CODE_OF_CONDUCT.md] never matches. **/ matches the file name at any depth, and adding parent directories, as in [**/legal/CODE_OF_CONDUCT.md], narrows it.
Vale takes BasedOnStyles from the last matching section that sets it, and a rule’s level from the last matching section that sets that rule. Put the opt-out section last, after every section that matches the file, and its empty BasedOnStyles turns every style off. A rule that an earlier section sets by name, such as MerlonDocs.DatedClaim = warning, stays on until the opt-out section sets it to NO.
The file stays a standalone guide, so coverage and link validation still check it.