Skip to content

Tooling excludes on JavaScript, revisited: built, under the same rule

Date: 2026-10-19 Build under test: crimes 0.29.5 (shipped), re-measured on 0.29.8 Supersedes, in part: 2026-08-11 tooling excludes on the JavaScript corpus, which decided do not build.

That record measured .eslintignore, root tsconfig.exclude and .gitattributes linguist-vendored on hono, cal.com, n8n and posthog, found zero corroborated patterns, and closed the work. It also named two things it did not decide: ESLint 9’s flat-config ignores (it noted .eslintignore was a dead format, not that flat config was unreadable) and nested configs (“a separate decision with its own failure modes”).

A private Java + JavaScript estate (its frontend, a nested package) names its vendored libraries in two independent tools, in a nested package directory:

sourceentry
eslint.config.mjs{ ignores: ['js/lib/*', 'js/vendor/*', …] }
.prettierignore/js/lib/, /js/vendor/, …

js/lib/ holds old copies of third-party libraries. It produced 271 of 1,596 findings (17%) and the top-ranked file in the report — the same shape of problem as pydantic’s pydantic/v1, which is why the pyproject reader exists.

What was built, and how it keeps the old guarantees

Section titled “What was built, and how it keeps the old guarantees”

packages/core/src/manifest/js-tooling-excludes.ts and scan-tooling-excludes.ts:

  • Corroboration is unchanged: two independent tools must name the pattern. ESLint’s two sources (.eslintignore, flat config) count once.
  • Per directory, anchored. Configs are read in any directory holding scanned files and corroborate only with configs in the same directory; patterns apply anchored to it. Where ESLint (anchored) and Prettier (unanchored) differ, the narrower reading wins.
  • Flat config is read as text, never executed, and only an object whose sole key is ignores counts — the only form ESLint treats as a global ignore. A non-literal element drops the object.
  • Any negation voids its source, since dropping !keep.js and keeping the rest would exclude more than the repo does.
  • Skips stay listed under files_excluded_by_tooling, and honourToolingExcludes: false still turns the rule off.

The 2026-08-11 record’s load-bearing example is n8n’s .prettierignore, which ends **/*.ts / **/*.js under # Handled by biome. Read alone it would drop the whole codebase. Under this rule it pairs with nothing: n8n’s .prettierignore is at the root, and its eslint.config.mjs files are in packages/cli, packages/core, packages/node-dev and packages/nodes-base. Different directories do not corroborate.

Re-measured on the same corpus (current default branches, shallow)

Section titled “Re-measured on the same corpus (current default branches, shallow)”
repoJS tooling configs presentcorroboratedfiles excluded
hononone00
cal.comnone00
n8nroot .prettierignore; ESLint flat config in 4 packages00
posthog4 .prettierignore, no ESLint flat config00

The rule still excludes nothing on the corpus the original decision was measured on, and excludes exactly the vendored tree on the repository that motivated it. That is the property the corroboration rule was meant to have: silent on preference files, active on agreement.

Measured on the same shallow clones, a 0.29.7 working build (before the 0.29.8 --liftoff-only change), one machine, the four scans concurrent: hono 3.6 s, cal.com 120 s, n8n 1,602 s, posthog 2,601 s. n8n’s full scan did not finish in 60 minutes in August. Large-repository cost is still the open item in roadmap status; a 90-second profile of n8n is entirely TypeScript parsing for the IA index, not the tooling-exclude reader.