Why this case study matters
prettier is the cleanest “structural floor on top” data point in the
Wave 1+2 inventory so far: prettier is itself a code-formatter (it
dogfoods), its existing tooling is mature and tightly curated
(ESLint flat-config + 9 internal prettier-internal-rules/* + the
self-prettier --check + cspell + knip + tsc + 5 custom node
validation scripts), and yet the plugin-shape conventions that the
entire codebase architecture rests on are encoded only in code review
and folder-name memory.
For any plugin-architecture project — webpack, rollup, vite, babel, postcss — the “every plugin has shape X” rule is universal and rarely enforced. prettier shows what the structural floor looks like when it lands cleanly under a mature tool stack.
Headline catch
prettier ships eight plugins under src/language-*/ (js, css,
html, markdown, yaml, json, graphql, handlebars). Each one must
export index.js (parser/printer/options) plus languages.evaluate.js
(linguist-languages lookup table consumed at build time). A plugin
that drops languages.evaluate.js would silently be omitted from
the production bundle. Yet nothing on disk asserts this contract.
Same thing for the 1:1 mapping between plugins and
changelog_unreleased/<lang>/ category directories: a new plugin
landing without its category dir ships fine, and PR notes for it
have nowhere to go.
Alint’s for_each_dir over src/language-* is the missing
structural floor. Five net-new gates that prettier’s existing
ESLint + prettier-itself + cspell + knip + tsc stack does not
enforce today:
prettier-each-language-plugin-has-indexprettier-each-language-plugin-has-languages-evaluateprettier-each-plugin-package-shapeprettier-plugin-package-prettier-scopeprettier-changelog-categories-exist
Where alint earns its keep here
- Single declarative gate catches both directions of the plugin ↔ changelog-category drift in one rule.
- Script consolidation — prettier has 5 custom node scripts
(lint-changelog.js, check-deps.js, format-test-lint.js,
ensure-no-files-changed.js, clean-cspell.js) totalling ~330 LoC,
plus 8 yarn lint scripts, plus a 16-step GitHub workflow. Five of
six invariants in
lint-changelog.jsand one of two incheck-deps.jscollapse to declarative alint rules. That’s ~80 LoC of bespoke JS replaced by ~50 lines of declarative YAML — easier to read in PR review, easier to extend, schema-validated at config-load time. - Speed delta —
yarn lintruns the 8 lint scripts in parallel vianpm-run-all2 -p. Each script does its own fs walk; cspell- prettier + eslint + tsc each pay startup cost per process. Typical wall-clock on a clean checkout: ~30-45 s on a developer laptop. alint runs the alint-replaceable subset in ~0.5-1 s expected.
- Cross-cutting v0.10 candidate driver — prettier’s
ensure-no-files-changed.jsis the canonicalcommand_idempotentshape (most-demanded primitive in the inventory: prettier, ruff, kubernetes, airflow, turbo).format-test-lint.jsis the canonicalfor_each_leaf_dirshape (prettier, rust-lang/rust, ruff, any test-corpus tree).
Future story angles
- Plugin-architecture launch tile — generalise from prettier to webpack, rollup, vite, babel, postcss. Every plugin-architecture project has the “every plugin has shape X” universal-and-unenforced rule. prettier is the canonical demo.
- “Adopt alint to consolidate the orchestration layer” angle — pair with cpython for the case where mature tooling already exists but the structural floor is the missing piece.
command_idempotentv0.10 launch post — prettier’s idempotency check is the cleanest example of the most-demanded primitive in the inventory.for_each_leaf_dirv0.10 launch post — prettier’s format-test-lint pattern recurs in every test-corpus tree (rust-ui, ruff snapshots, deno specs).