@sheplu/editorconfig
v0.14.1
Published
CLI to generate and validate a consistent .editorconfig across your projects
Maintainers
Readme
editorconfig
A small CLI to manage a consistent .editorconfig across your projects.
- ✅ Generate a sane default
.editorconfigin seconds - ✅ Check if your existing file matches the target setup
- ✅ Confirm before overwriting an existing
.editorconfig(or pass--overwriteto skip the prompt) - ✅ Override the built-in template with a team-shared file via
--template(local path orhttps://URL) - ✅ Validate every
.editorconfigin a monorepo at once with--recursive
Why?
Keeping .editorconfig aligned across multiple repositories is boring and error-prone:
- Some repos have no
.editorconfig - Some have outdated or partial settings
- People copy–paste from “somewhere” and drift over time
This CLI aims to provide a single source of truth for your preferred .editorconfig, and a few helpers to keep it in sync.
Installation
You can use it without installing, via npx:
npx @sheplu/editorconfig --mode=writeOr install it globally:
npm install -g @sheplu/editorconfig
editorconfigOr as a dev dependency:
npm install -D @sheplu/editorconfigQuick Start
From the root of your project:
npx @sheplu/editorconfig --mode=writeThis will:
- Create a
.editorconfigfile if it doesn’t exist - Write the default template
- Ask before overwriting an existing file (or fail in non-interactive contexts unless
--overwriteis passed)
Current Features
write
npx @sheplu/editorconfig --mode=writeCreates a base .editorconfig file in the current directory.
If a .editorconfig already exists at the target path, the CLI will:
- Prompt for confirmation in an interactive terminal (
y/yesto overwrite, anything else keeps the file). - Exit non-zero in non-interactive contexts (CI, redirected stdin) so a script never silently destroys an existing config.
Pass --overwrite (or -o) to bypass the prompt and force a rewrite:
npx @sheplu/editorconfig --mode=write --overwriteYou can also point at a custom path:
npx @sheplu/editorconfig --mode=write --path=path/to/.editorconfigTypical content (example):
root = true
[*]
indent_style = tab
indent_size = 4
tab_width = 4
end_of_line = lf
charset = utf-8
spelling_language = en
trim_trailing_whitespace = true
insert_final_newline = true
quote_type = single
spaces_around_operators = truecheck
npx @sheplu/editorconfig --mode=checkValidates your existing .editorconfig and reports any drift from the target configuration.
This command will:
- Read your existing .editorconfig
- Compare it against the tool’s canonical template
- Exit with:
0if everything matches1if differences are found
--recursive (monorepo check)
npx @sheplu/editorconfig --mode=check --recursiveWalks the current directory (or the directory passed via --path) and validates every .editorconfig it finds. Useful for monorepos where a top-level file sets defaults and sub-packages add narrower overrides.
How files are classified:
- The shortest-path
.editorconfigin each subtree is treated as the root and validated with the same rules as--mode=check(must declareroot = true, must have[*], sections must match the canonical template). - Deeper
.editorconfigfiles are treated as children. Children must NOT declareroot = true, may omit[*], and may legitimately override individual keys. - Sibling subtrees with no shared ancestor
.editorconfigare each validated as their own root.
Cross-file checks emitted on top of per-file validation:
child-root— a child declaredroot = true(fail).redundant— a child redeclares a parent key/value verbatim (warn).contradiction— a child reuses the same header as its root (e.g. both[*]) with a different value (warn). Different globs like child[*.js]overriding root[*]are silent — they are legitimate per spec.
Skipped during the walk: node_modules, .git, dist, build, coverage, .next, .cache, and symlinked directories.
Flag interactions in recursive mode:
--pathbecomes the start directory for the walk (default: cwd).--languagesis enforced on the root file only — children may add or omit language sections freely.--templateoverrides apply to the root section comparison.--strictfails on unknown headers in any file.
Exit codes: 0 on pass (warnings allowed), 1 if any file fails or any cross-file failure (e.g. child-root) is detected.
# Validate everything under cwd
npx @sheplu/editorconfig --mode=check --recursive
# Validate only one subtree
npx @sheplu/editorconfig --mode=check --recursive --path=./packages/web
# CI: enforce js/md sections on the root, anywhere else may add what they need
npx @sheplu/editorconfig --mode=check --recursive --languages=js,md--template (custom team template)
Both write and check accept a --template (or -t) flag pointing at a custom .editorconfig-syntax file. Sections in that file override the built-in defaults; languages it doesn't redefine still come from the built-ins. This lets a team host a single source of truth and reference it from every repo.
The flag accepts either a local path or an https:// URL:
# local file
npx @sheplu/editorconfig --mode=write --template=./team.editorconfig
# remote URL
npx @sheplu/editorconfig --mode=check --template=https://raw.githubusercontent.com/example/team-config/main/.editorconfigThe custom template must follow the same semantics as the built-ins:
- Use only known section headers (
[*],[*.md],[*.{js,jsx,...}], etc.). Unknown headers (like[*.proto]) are rejected. - Include
root = truein the preamble whenever the template redefines[*]. - Each header may appear at most once.
URL fetching is constrained for safety: https:// only (http:// is rejected before any network call), redirects must stay on https (max 5 hops), 10-second timeout, 1 MB response cap. Templates are fetched on every invocation — there is no local cache.
--json (machine-readable check output)
check accepts a --json flag that swaps the human-readable report for a structured JSON document on stdout. The exit code is unchanged (0 pass, 1 fail), so it stays drop-in for CI while letting dashboards and scripts parse the result.
npx @sheplu/editorconfig --mode=check --json
npx @sheplu/editorconfig --mode=check --recursive --jsonSingle-file shape:
{
"mode": "check",
"path": ".editorconfig",
"ok": true,
"summary": { "total": 1, "matched": 1, "failed": 0, "unknown": 0 },
"sections": [
{ "header": "[*]", "status": "match", "detail": "" }
]
}In --recursive mode the payload instead carries recursive: true, a files array (each with its role, summary, and sections) and a crossFileIssues array describing child-root / redundant / contradiction findings. When no JSON output is requested, the human-readable report is printed exactly as before.
--json is only meaningful for check; combining it with --mode=write is rejected with a non-zero exit.
--version
npx @sheplu/editorconfig --versionPrints the installed package version (also available as -v).
--help
npx @sheplu/editorconfig --helpPrints the full usage, the available commands, and every supported option.
Planned / Upcoming Features
1. Interactive update / replace
npx @sheplu/editorconfig --mode=fix2. Compare with target setup
npx @sheplu/editorconfig --mode=diffRoadmap
- [ ] Add diff logic and
diffcommand - [ ] Add interactive
fix/updatecommand - [ ] Expose presets or configuration options
Additional properties can be found on the editorconfig wiki.
Tests
Tests live under test/ and are split by scope:
test/unit/— fast, in-process tests of exported functions (no spawning, no I/O beyond a tmp dir).test/integration/— full CLI runs. The interactive prompt path is exercised in a real pseudoterminal vianode-pty; the rest go throughchild_process.spawnSyncwith a closed stdin.
node-pty ships a native binding that npm normally compiles via a postinstall script. This repo sets ignore-scripts=true in .npmrc, so after npm install you need to build it once:
node --run rebuild:nativeRun them with:
node --run test # everything
node --run test:unit # unit only — runs in ~50ms
node --run test:integration # integration only — spawns the CLI
node --run test:coverage # coverage with 95% threshold
node --run lint # oxlintCI runs lint, audit, and the full suite on every PR across Node 24 / 26.
