formatter-cli
v1.0.0
Published
A Node.js CLI tool for formatting CSS, HTML, JavaScript, and JSON — extracted from the Chrome DevTools frontend formatter worker.
Readme
formatter-cli
A standalone Node.js CLI that pretty-prints CSS, HTML, JavaScript
and JSON. Formatting logic is ported directly from the Chrome DevTools
frontend formatter_worker package, so the results match what you
see in the DevTools Sources panel.
Install
cd formatter-cli
npm install
npm run build
# optionally link it globally
npm linkAfter npm link, the formatter-cli command is available anywhere on your
machine.
Usage
# Format a file based on its extension (html / css / js / json)
formatter-cli index.html
# Force a language, choose indent width
formatter-cli -l json -i 2 package.json
# Read from stdin
curl -s https://example.com/style.css | formatter-cli -l css
# Format in-place
formatter-cli -l js -o script.js script.js
# CI check – exit 1 if the input is not already well-formed
formatter-cli --check -l html template.html
# With parse-error details
DEBUG=1 formatter-cli -l js broken.jsOptions
| Flag | Description |
| ----------------------- | ------------------------------------------------------------- |
| [files...] | Files or glob patterns (e.g. src/**/*.js). Language auto-detected from extension. |
| -l, --language <lang> | Force input language: css, html, js, json. |
| -i, --indent <size> | Number of spaces per indent (default 4). |
| -w, --write | Write formatted output back to each source file (in-place). |
| --check | Dry run: exit non-zero if any file is not well-formatted. |
| --color | Colorize diff-style output when --check reports mismatches. |
| -h, --help | Show the usage banner. |
| -V, --version | Print the version. |
Architecture
The port keeps the same module boundaries as the original DevTools code, with Node-only compatibility shims layered on top:
formatter-cli/
├── bin/formatter-cli.js # shebang wrapper (ESM `import(...)`)
├── src/
│ ├── index.ts # CLI arg reader, stdin/stdout I/O
│ ├── platform/
│ │ ├── ArrayUtilities.ts # lowerBound + DEFAULT_COMPARATOR
│ │ └── StringUtilities.ts # findLineEndingIndexes, isWhitespace
│ ├── text-utils/
│ │ └── TextCursor.ts # line/offset cursor
│ ├── codemirror/
│ │ └── register-codemirror.ts # loads npm CodeMirror + modes
│ ├── types/
│ │ └── acorn-estree.d.ts # augments `acorn` with `ESTree.Node`
│ └── formatter-worker/
│ ├── FormattedContentBuilder.ts # output buffer / source-mapping
│ ├── FormatterActions.ts # MIME type enum, FormatResult
│ ├── AcornTokenizer.ts # peekable Acorn token wrapper
│ ├── ESTreeWalker.ts # AST walker with parent pointers
│ ├── CSSFormatter.ts # CSS formatter
│ ├── HTMLFormatter.ts # HTML + embedded CSS/JS/JSON
│ ├── JavaScriptFormatter.ts # JavaScript (Acorn-based)
│ ├── JSONFormatter.ts # JSON
│ └── FormatterWorker.ts # `format(mime, text, indent)` entry
├── package.json
└── tsconfig.jsonDependencies
codemirror@^5.65.16— only the CommonJS CSS / XML / JavaScript / HTML-mixed mode packages plus the editor-core UMD IIFE for theCodeMirrorglobal holder (getMode,startState,StringStream,defineMode…). The DOM-dependent editor pieces are never loaded; a tiny DOM stub is installed viaregister-codemirror.tsso the UMD wrapper can execute in Node.acorn@^8.11.3— the JS/ES parser consumed byJavaScriptFormatter.
DevTools-only types (@types/estree, @types/codemirror, @types/acorn,
@types/node) are installed as devDependencies.
How it works — CodeMirror in Node
The original DevTools worker used an addon/runmode-standalone JS file that
installed a DOM-free CodeMirror global, which the worker's mode-file ESM
imports then attached to. The codemirror npm package ships only the
CommonJS/UMD mode packages, whose UMD wrapper references the full DOM editor
core during module evaluation (document.createElement, document.createRange,
document.body, etc).
register-codemirror.ts pre-installs a minimal (and non-functional) DOM
stub into globalThis long enough for those mode packages to load, at which
point the codemirror IIFE populates globalThis.CodeMirror with the
tokenizer-only subset that the formatter needs. This is exactly the same shape
the DevTools worker relies on:
// original DevTools worker entrypoint
import '../../third_party/codemirror/package/addon/runmode/runmode-standalone.mjs';
import '../../third_party/codemirror/package/mode/css/css.mjs';
import '../../third_party/codemirror/package/mode/xml/xml.mjs';
import '../../third_party/codemirror/package/mode/javascript/javascript.mjs';Becomes:
// formatter-cli/FormatterWorker.ts
import '../codemirror/register-codemirror.ts';
import 'codemirror/mode/css/css';
import 'codemirror/mode/xml/xml';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/htmlmixed/htmlmixed';
const CodeMirror = globalThis.CodeMirror;Scope and known limitations
This port covers the four public formatters that the DevTools worker exports
via its format() entrypoint:
text/css→CSSFormattertext/html→HTMLFormatterapplication/javascript/text/javascript→JavaScriptFormatterapplication/json→JSONFormatter
It does not include CSSRuleParser, ScopeParser or Substitute, which
are unrelated to pretty-printing and only consumed by the DevTools debugger /
styles sidebars. IdentityFormatter (used as a fallback for unknown media
types) is implemented implicitly — unknown MIME types are returned untouched.
The DevTools build historically patched Acorn to permit a few non-standard
patterns (e.g. the original ECMA_VERSION = 'latest' with
allowImportExportEverywhere). This port uses the upstream Acorn 8 so a few
legacy patterns (such as class private fields combined with top-level
import/export under allowImportExportEverywhere at ecmaVersion:'latest')
will instead surface as parse errors. The DevTools team's Acorn fork predates
this port; users parsing unusual code should pin a specific acorn@<X> in
their project if needed.
License
formatter-cli is BSD-3-Clause (matching the upstream DevTools license on the
ported source files). The codemirror package is MIT.
Development
npm run dev # watch mode (tsc -p tsconfig.json --watch)
npm run build # one-shot build to ./dist
node bin/formatter-cli.js --help