deno fmt --check: check formatting in CI without changing files
- tool
- Deno
- versions
- Deno 2.x
- os
- any
deno fmt --check checks whether files are formatted, prints a diff for each one that is not, and exits with code 1 without changing anything. Plain deno fmt rewrites the files in place. Put deno fmt --check in CI next to deno lint so unformatted code fails the build; run deno fmt locally to fix it.
Check, fix, check again
printf 'const x = {a:1,\n b:2}\nconsole.log( x )\n' > main.ts
npx --yes deno@2 fmt --check main.ts > check.log 2>&1
echo "check exit=$?"
tail -n 1 check.log
npx --yes deno@2 fmt main.ts 2>&1
cat main.ts
npx --yes deno@2 fmt --check main.ts 2>&1
echo "check exit=$?"Output
check exit=1
error: Found 1 not formatted file in 1 file
/tmp/work/main.ts
Checked 1 file
const x = { a: 1, b: 2 };
console.log(x);
Checked 1 file
check exit=0Without file arguments, deno fmt walks the current directory, skipping files ignored by .gitignore and the fmt.exclude list in deno.json.
Useful options
| Option | Effect |
|---|---|
| --check | Report unformatted files and exit 1; write nothing |
| --fail-fast | With --check, stop at the first unformatted file |
| --ext=ts | Format stdin or extensionless files as this type |
| --line-width=100 | Maximum line width (default 80) |
| --indent-width=4 | Spaces per indent (default 2) |
| --use-tabs | Indent with tabs |
| --single-quote | Prefer single quotes (default double) |
| --no-semicolons | Omit semicolons where possible |
| --ignore=dist,vendor | Skip paths |
The same settings can live in deno.json so every developer and CI job uses them:
{
"fmt": {
"lineWidth": 100,
"singleQuote": true,
"exclude": ["dist/"]
}
}Svelte, Vue, Astro and Angular templates need --unstable-component, and SQL files --unstable-sql. Deno also reads .editorconfig for indentation settings unless you pass --no-editorconfig.
Supported files
deno fmt formats much more than TypeScript, which is why --check can fail on a file you did not expect:
| Type | Extensions |
|---|---|
| JavaScript and TypeScript | .js .cjs .mjs .jsx .ts .mts .cts .tsx |
| Data | .json .jsonc .yml .yaml |
| Markdown | .md .markdown and other Markdown extensions; fenced code blocks with a language are formatted too |
| Styles and markup | .css .scss .less .html .xml .svg |
| Notebooks | .ipynb |
| Components (unstable) | .astro .svelte .vue with --unstable-component |
| SQL (unstable) | .sql with --unstable-sql |
To keep a generated or vendored folder out of the check, list it in fmt.exclude in deno.json or pass --ignore.
In a CI workflow
Run the check before the slower steps, so a formatting mistake fails the job in seconds:
- run: deno fmt --check
- run: deno lint
- run: deno test--check reports every unformatted file with a diff; add --fail-fast in large repositories to stop at the first one. A failing check is fixed by running deno fmt locally and committing the result, so pin the same Deno version locally and in CI: formatter output can change between releases.
FAQ
Can I use deno fmt in a Node.js project?
Yes. deno fmt does not need a deno.json and formats plain JavaScript, TypeScript, JSON, Markdown, CSS and YAML files; it reads its options from deno.json only if one exists.
How do I stop deno fmt from changing one block of code?
Put a // deno-fmt-ignore comment on the line before it in JavaScript or TypeScript, or // deno-fmt-ignore-file at the top to skip the whole file. Markdown, HTML and CSS use the HTML comment form, YAML uses # deno-fmt-ignore.
Why does deno fmt --check pass locally but fail in CI?
Usually a different Deno version or a missing deno.json in the CI working directory. Pin the Deno version in CI and run the check from the folder that holds the configuration.