@remyst/lint
v0.0.2
Published
Lint and reformat MyST Markdown files (prettier-style CLI)
Readme
@remyst/lint
A prettier-style CLI to lint and reformat MyST Markdown files, powered by
@remyst/parser (parse → serialize).
remyst --write . # reformat every Markdown file in place
remyst --check "docs/**" # exit non-zero if anything is unformatted
remyst file.md # print the formatted result to stdout
cat file.md | remyst # format stdinArguments are files, directories (searched recursively for *.md / *.markdown),
or globs. node_modules is ignored.
Operation
| Flag | Description |
| --------------------------------- | ---------------------------------------------------------------------------- |
| -c, --check | Check whether files are formatted, without writing. Exits 1 if any differ. |
| -w, --write | Rewrite files in place. |
| -l, --list-different | Print the paths of files that are not formatted. Exits 1 if any differ. |
| --no-error-on-unmatched-pattern | Don't error when a pattern matches no files. |
Formatting options
How the output looks. Every option has a built-in default.
| Flag | Values | Default | Description |
| ------------------------------- | ------------------- | ------- | ------------------------------------------------------ |
| --bullet <marker> | - * + | - | Unordered list marker. |
| --emphasis <marker> | _ * | _ | Italics marker. |
| --strong <marker> | * _ | * | Bold marker. |
| --rule <marker> | - * _ | - | Thematic break marker. |
| --list-item-indent <style> | one tab mixed | one | Indentation of list item content. |
| --definition-spaces <n> | integer ≥ 1 | 1 | Spaces after a definition list : marker. |
| --definition-spread | flag | off | Loose definition lists (blank line after the term). |
| --no-smartquotes | flag | — | Leave quotes as written (see below). |
| --no-ellipses | flag | — | Leave ... as-is (see below). |
| --no-dashes | flag | — | Leave -- / --- as-is (see below). |
| --no-backticks | flag | — | Leave backtick quotes ( `` / '') as-is. |
| --no-link-definitions-bottom | flag | — | Keep link definitions in place (don't move to bottom). |
| --no-link-definitions-tight | flag | — | Blank line between link definitions. |
| --footnote-definitions-bottom | flag | — | Move footnote definitions to the bottom (above links). |
| --no-math-spaces | flag | — | Drop math delimiter padding ($x$, not $ x $). |
| --no-math-one-line | flag | — | Always expand $$…$$ blocks to multiple lines. |
| --math-one-line-max <n> | integer | — | Keep a $$…$$ block on one line only when ≤ n chars. |
By default math delimiters are padded ($ x $, $$ x $$) and a $$…$$ block
keeps the line form it was written in (--math-one-line-max overrides that with
a length threshold). smartquotes (default on) curls quotes in the parsed AST
(" → “”, ' → ‘’, matching mystmd) but keeps plain straight quotes in the
serialized markdown; --no-smartquotes leaves quotes exactly as written.
ellipses (default on) works the same way for ... → …, and backticks
(default on) for backtick quotes ( ``/'') → curly — each keeping the plain
form in the serialized markdown. dashes (default on) turns --/--- into en/em
dash characters, which stay in the serialized markdown (they read fine
as-is), and it leaves any literal en/em dashes untouched. dashes and backticks
take the same values as
remark-smartypants (dashes:
"oldschool" | "inverted", backticks: "all"). Code and math are never touched.
Syntax constructs
Which MyST constructs are recognized. Citations, math, and GFM callouts are on by default.
| Flag | Default | Description |
| -------------------- | ------- | -------------------------------------------------- |
| --no-footnotes | on | GFM footnotes ([^1]). |
| --no-tasklists | on | GFM task lists (- [x]). |
| --no-tables | on | GFM pipe tables (\| a \| b \|). |
| --no-strikethrough | on | GFM strikethrough (~~x~~). |
| --no-autolink | on | Literal autolinks (bare URLs / emails). |
| --no-citations | on | Pandoc-style @key citations. |
| --no-math | on | Dollar-sign math ($…$, $$…$$). |
| --no-amsmath | on | AMS math environments (\begin{equation}…). |
| --no-attributions | on | Blockquote -- Person attribution lines. |
| --no-gfm-callout | on | GitHub-style callouts (> [!NOTE]) → admonitions. |
Configuration
Options can also come from a config file, so a project can commit a shared code style. On startup @remyst/lint walks up from the current working directory and uses the first of these it finds:
.remystrc.json.remystrc(JSON)remyst.config.json- a
"remyst"field inpackage.json
Precedence: CLI flags → config file → built-in defaults. A flag always wins over the same key in the config file, and any key you omit falls back to the default.
// .remystrc.json
{
// formatting
"bullet": "-",
"emphasis": "_",
"strong": "*",
"rule": "-",
"listItemIndent": "one",
"definitionSpaces": 1,
"definitionSpread": false,
"smartquotes": true,
"ellipses": true,
"dashes": true,
"backticks": true,
"linkDefinitionsBottom": true,
"linkDefinitionsTight": true,
"footnoteDefinitionsBottom": false,
"mathSpaces": true,
"mathOneLine": true,
// which constructs are parsed
"syntax": {
"footnotes": true,
"tasklists": true,
"tables": true,
"strikethrough": true,
"autolink": true,
"gfmCallout": true,
"citations": true,
"math": true,
"amsmath": true,
"attributions": true,
},
}…or inline in package.json:
{
"name": "my-docs",
"remyst": { "bullet": "*", "syntax": { "gfmCallout": false } }
}Formatting keys (top level)
| Key | Type | Default | Notes |
| --------------------------- | ---------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| bullet | "-" | "*" | "+" | "-" | Unordered list marker. |
| emphasis | "_" | "*" | "_" | Italics marker. |
| strong | "*" | "_" | "*" | Bold marker. |
| rule | "-" | "*" | "_" | "-" | Thematic break marker. |
| listItemIndent | "one" | "tab" | "mixed" | "one" | Indentation of list item content. |
| definitionSpaces | integer ≥ 1 | 1 | Spaces after a definition list : marker. |
| definitionSpread | boolean | false | Loose definition lists (blank line after the term). |
| smartquotes | boolean | true | Curl quotes in the AST but keep straight quotes in serialized markdown; off leaves them as written. |
| ellipses | boolean | true | Turn ... into … in the AST but keep ... in serialized markdown; off leaves it as written. |
| dashes | boolean | "oldschool" | "inverted" | true | Turn --/--- into en/em dash characters (kept in the markdown); literal en/em dashes stay. |
| backticks | boolean | "all" | true | Recognize backtick quotes ( ``/'') in the AST; serialized markdown keeps straight quotes. |
| linkDefinitionsBottom | boolean | true | Move link reference definitions ([a]: url) to the bottom of the document. |
| linkDefinitionsTight | boolean | true | Pack the link definitions tight (one line each, no blank line between). |
| footnoteDefinitionsBottom | boolean | false | Move footnote definitions to the bottom, above the link definitions. |
| mathSpaces | boolean | true | Pad math delimiters: $ x $ / $$ x $$. |
| mathOneLine | boolean | integer | true | true keeps a $$…$$ block's authored line form, false always expands, a number is a max character length for the one-line form. |
| syntax | object | — | Which constructs are parsed (below). |
syntax keys
Toggles for individual MyST constructs.
| Key | Default | Notes |
| --------------- | ------- | -------------------------------------------------------------- |
| footnotes | true | GFM footnotes ([^1]). |
| tasklists | true | GFM task lists (- [x]). |
| tables | true | GFM pipe tables (\| a \| b \|). |
| strikethrough | true | GFM strikethrough (~~x~~). |
| autolink | true | Literal autolinks (bare URLs / emails). |
| gfmCallout | true | GitHub-style callouts (> [!NOTE]). |
| citations | true | Pandoc-style @key citations. |
| math | true | Dollar-sign math ($…$, $$…$$). |
| amsmath | true | AMS math environments (\begin{equation}…). |
| attributions | true | Blockquote -- Person attribution lines → attribution node. |
| directives | true | :::{name} / ```{name} directives. |
| roles | true | {name}`…` roles. |
| targets | true | (label)= targets. |
| comments | true | % … comments. |
| blockBreaks | true | +++ block breaks. |
| indentedCode | false | CommonMark 4-space indented code (off in MyST). |
Validation
Config files are validated with Zod before use (see
src/config.ts). Invalid values and unknown keys (e.g. a
misspelled "bulet") fail fast with a readable error naming the offending file
and field, rather than being silently ignored. The configSchema and
parseConfig helpers are exported from the package if you want to validate a
config yourself.
Development
The CLI is bundled self-contained with esbuild so it can be linked globally.
npm run build # esbuild bundle → dist/cli.js
npm run link # register `remyst` on the global PATH (npm link)
npm run dev # link, then rebuild the bundle on every change (esbuild --watch)
npm run unlink # remove the global linkAfter npm run link (or dev), remyst is available as a global command.
Note: reformatting is
remyst.format(parse + serialize). remyst currently supports CommonMark + the MyST constructs it implements; running--writeover documents using syntax remyst does not yet parse can rewrite them unexpectedly.
Made with love by Curvenote. Source at github.com/curvenote/remyst.
