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).