Prettier 14 ๐จ The –check Flag โ CI enforcement & exit codes
The --check flag is the CI counterpart to --write. It does not modify any files. It reads the files, compares the formatted output to the original content, and reports the files that would change. If any file would change, Prettier exits with a non-zero status code, which is what a CI pipeline uses to fail the build. The --write flag is for the developer’s machine; the --check flag is for the gate that prevents unformatted code from merging.
This chapter covers the exit code contract, the difference between --check and --write in a pipeline, the --list-different alias that produces the same exit code, and the CI configuration that runs the check with the project’s pinned Prettier version.
Key point: --check exits with code 1 when any file is unformatted. The output names the files and tells the developer to run --write. The exit code is the gate; the output is the diagnostic.
Why the check flag exists
The CI gate problem. A pre-commit hook can be bypassed with --no-verify. An editor integration can be misconfigured or disabled. The only check that cannot be bypassed by the developer’s local environment is the one that runs in CI, on the server, with the project’s own dependency tree . The --check flag is the mechanism that makes that check possible without modifying the repository.
The write problem. Running --write in CI would modify the files in the runner, but the modifications would be discarded when the runner exits. The pipeline would pass, and the unformatted code would still be merged. The --check flag is the correct choice because it produces a failure without producing a change.
The pinned version problem. The Prettier documentation for CI recommends using a pinned version of Prettier in the repository . The --check run in CI must use the same version as the developer’s local run, or the check may fail on formatting differences that are not the developer’s fault. The npm ci command installs the exact version from the lockfile, which is why the CI configuration should use it.
a. The exit code contract
The --check flag returns exit code 0 when every file is formatted and exit code 1 when at least one file is not. The output names the unformatted files and prints a message that tells the developer to run --write.
# Check all files in the current directory
npx prettier . --check
# Check a specific file
npx prettier src/app.js --check
A successful check produces a message that all files are formatted and exits 0. A failed check produces a list of the files that would change and exits 1. The CI pipeline step fails when the exit code is non-zero, which is the entire mechanism.
The --list-different flag is a legacy alias that produces the same behavior: it lists the files that differ and exits non-zero . The --check flag is the current name and the one that the documentation uses. Both flags are suitable for CI, but --check is the recommended form.
b. The CI configuration
The CI pipeline runs npm ci to install the exact dependencies from the lockfile, then runs the check script. The npm ci command is important because it installs the version that the developer tested against, not a version that the CI runner happens to resolve.
# .github/workflows/lint.yml (excerpt)
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run format:check
The package.json script is the abstraction that keeps the command consistent across environments:
{
"scripts": {
"format:check": "prettier . --check"
}
}
The local developer runs the same script, which means the check that passes locally is the check that runs in CI . The only difference is the environment.
A GitHub Actions workflow that runs the check on every push to the main branch and every pull request is the standard configuration. The workflow fails when the check fails, which blocks the merge .
c. The glob and the ignore file
The --check command uses the same file resolution as --write. The directory argument recurses, the glob argument is expanded by Prettier, and the .prettierignore file filters the result. A CI check that runs prettier . --check covers the entire project, subject to the ignore file.
The glob syntax for multiple extensions must not contain spaces. **/*.{js,ts,css} works; **/*.{js, ts, css} matches nothing because the space becomes part of the pattern . This is the most common reason a check script reports zero files and exits 0 even when unformatted files exist.
Complete Example Session
// ============================================
// PART 1: THE PACKAGE SCRIPT
// ============================================
{
"scripts": {
"format": "prettier . --write",
"format:check": "prettier . --check"
}
}
# ============================================
# PART 2: THE LOCAL CHECK
# ============================================
npm run format:check
# Output when all files are formatted:
# Checking formatting...
# All matched files use Prettier code style!
# ============================================
# PART 3: THE FAILING CHECK
# ============================================
npm run format:check
# Output:
# Checking formatting...
# src/app.js
# src/utils.js
# Code style issues found in 2 files. Run Prettier with --write to fix.
# Exit code: 1
# ============================================
# PART 4: THE WRITE FIX
# ============================================
npm run format
# The files are reformatted.
# The check passes on the next run.
# ============================================
# PART 5: THE CI WORKFLOW
# ============================================
name: Lint & Format
on:
push:
branches: [main]
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run format:check
# ============================================
# PART 6: THE EXIT CODE
# ============================================
# Check passes โ exit 0 โ CI step passes
# Check fails โ exit 1 โ CI step fails
# ============================================
# PART 7: THE GLOB
# ============================================
# Correct: "**/*.{js,ts,css}"
# Wrong: "**/*.{js, ts, css}" (spaces)
# ============================================
# PART 8: THE IGNORE FILE
# ============================================
# .prettierignore
# dist/
# coverage/
# node_modules/
# ============================================
# PART 9: THE PINNED VERSION
# ============================================
# package.json: "prettier": "3.9.4"
# npm ci installs the exact version.
# ============================================
# PART 10: THE DECISION
# ============================================
# Local โ --write
# CI โ --check
# Both โ same script, same version
The ten parts covered the package script, the local check, the failing check, the write fix, the CI workflow, the exit code, the glob, the ignore file, the pinned version, and the decision.
Quick Reference
The flags
| Flag | Modifies files | Exit code on unformatted |
|---|---|---|
--check | No | 1 |
--list-different | No | 1 (legacy alias) |
--write | Yes | 0 |
The CI configuration
| Step | Command |
|---|---|
| Install | npm ci |
| Check | npm run format:check |
| Fix locally | npm run format |
The exit codes
| Code | Meaning |
|---|---|
| 0 | All files formatted |
| 1 | At least one file unformatted |
The glob rule
| Pattern | Result |
|---|---|
**/*.{js,ts,css} | Matches |
**/*.{js, ts, css} | Matches nothing |
Best Practices
โ Do This:
# Use --check in CI
npm run format:check # โ
# Use npm ci to install the pinned version
npm ci # โ
// Keep the script consistent between local and CI
"format:check": "prettier . --check" // โ
// Quote the glob and omit spaces
prettier "**/*.{js,ts,css}" --check // โ
โ Don’t Do This:
# Use --write in CI
prettier . --write # โ (changes discarded)
# Rely on a global Prettier in CI
prettier . --check # โ (version drift)
// Use spaces in the glob
prettier "**/*.{js, ts, css}" --check // โ (no match)
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| CI passes despite unformatted files | --write used instead of --check | Use --check and the exit code |
| Check reports no files | Glob has spaces | Remove the spaces |
| CI fails on formatting that passes locally | Different Prettier version | Use npm ci with the lockfile |
| Check is slow | The whole node_modules is scanned | Add node_modules to .prettierignore |
| Check fails on generated files | Not ignored | Add the paths to .prettierignore |
Real-World Examples
1. The check script
{ "scripts": { "format:check": "prettier . --check" } }
2. The local run
npm run format:check
3. The CI step
- run: npm ci && npm run format:check
4. The failing output
Code style issues found in 2 files. Run Prettier with --write to fix.
5. The fix
npm run format
6. The glob
prettier "**/*.{js,ts}" --check
7. The ignore file
# .prettierignore
dist/
8. The pinned version
{ "devDependencies": { "prettier": "3.9.4" } }
9. The exit code
echo $? # 1 after a failing check
10. The decision
Local โ --write; CI โ --check
Visual
The check vs write
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ --check โ
โ Read file โโโบ format โโโบ compare โโโบ report โ
โ No file is modified. โ
โ Exit 1 if any file differs. โ
โ โ
โ --write โ
โ Read file โโโบ format โโโบ write โโโบ exit 0 โ
โ Files are modified. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The CI gate
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Push โโโบ CI runner โโโบ npm ci โโโบ format:check โ
โ โ โ
โ โโโ exit 0 โโโบ pass โ
โ โ โ
โ โโโ exit 1 โโโบ fail โ
โ โ
โ The fail blocks the merge. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The version alignment
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Developer CI โ
โ package-lock.json โโโบ npm ci โโโบ same version โ
โ โ
โ The check that passes locally is the check that runs in CI.โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The glob spaces
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ "**/*.{js,ts,css}" โ matches โ
โ "**/*.{js, ts, css}" โ no match (spaces) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Flag | --check |
| Modifies files | No |
| Exit code on unformatted | 1 |
| Legacy alias | --list-different |
| CI install | npm ci |
| CI script | format:check |
| Glob rule | No spaces in braces |
| Version requirement | Pinned in the lockfile |
Key takeaways:
--checkis the CI gate. It reports unformatted files and exits with code 1, which fails the pipeline step. It does not modify any files, which is the correct behavior for a CI runner that discards its changes.--writeis for the developer’s machine. It reformats the files in place and exits 0. The developer runs it locally, and the CI runs--checkon the same files.- The version must be pinned. The CI pipeline uses
npm cito install the exact version from the lockfile. A global Prettier or annpxrun without a local install would use a different version and produce a check that does not match the developer’s environment . - The glob must not contain spaces.
**/*.{js,ts,css}matches the three extensions.**/*.{js, ts, css}matches nothing because the space becomes part of the pattern . A check script that reports zero files is usually a glob with spaces. - The
.prettierignorefile filters the check. The check runs on the files that the ignore file does not exclude. A generated file that should not be checked must be added to the ignore file, not to a separate exclusion list.
Remember: The --check flag is the difference between a formatter that the developer uses and a formatter that the repository enforces. The local run uses --write, the CI run uses --check, and the exit code is the gate. Use npm ci to align the version, quote the glob and omit the spaces, and keep the script consistent between the developer’s machine and the CI runner. The check that passes locally is the check that runs in CI, and the exit code is the signal that the code is ready to merge.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!