@@ -886,6 +886,32 @@ const commentSwallowPlugin = {
886886 } ,
887887} ;
888888
889+ // ───────────────────────────────────────────────────────────────────────────
890+ // The trees nothing in this repo ever lints: installed dependencies and build
891+ // output. Named ONCE and spread into the global ignore below AND into every
892+ // per-object `ignores` beside it, so "no config object can match build output"
893+ // is a property of this one array instead of seven lists that happen to agree.
894+ //
895+ // They did not agree. Four of the seven `files` objects below omitted
896+ // `**/build/**`, `**/.next/**` and `**/.turbo/**`, so TypeScript under those
897+ // directories resolved to three ENABLED rules while the same generated file
898+ // under `dist/` resolved to none — the incidental-neutrality the levelling in
899+ // #12334 replaced.
900+ //
901+ // ⚠️ The global object alone already keeps these paths out of the linted
902+ // population, so the per-object copies change no behaviour today. They are not
903+ // redundancy: they are what makes the invariant survive a future edit to the
904+ // global object by someone who has never read this history — which is exactly
905+ // how a rule came to be enabled on generated code in the first place.
906+ // ───────────────────────────────────────────────────────────────────────────
907+ const NEVER_LINTED = Object . freeze ( [
908+ '**/node_modules/**' ,
909+ '**/dist/**' ,
910+ '**/build/**' ,
911+ '**/.next/**' ,
912+ '**/.turbo/**' ,
913+ ] ) ;
914+
889915export default [
890916 // ───────────────────────────────────────────────────────────────────────────
891917 // GLOBAL ignore. This is the ONLY object in this array with no `files` key,
@@ -910,34 +936,41 @@ export default [
910936 // So the accept/reject semantics of `**/dist/**` do not move: nothing there
911937 // had a rule enabled to lose. The saving is pure enumeration + parse.
912938 //
913- // ⚠️ SCOPE IS DELIBERATELY NARROWER THAN THE PER-OBJECT LISTS BELOW. Those
914- // also carry `**/build/**`, `**/.next/**` and `**/.turbo/**`, and promoting
915- // those three here would NOT be semantics-neutral. Three of the objects
916- // below (the `packages/**` and `examples/**` ones) list only
917- // `**/node_modules/**` and `**/dist/**` in their `ignores`, so they still
918- // match TypeScript under the other three directories:
939+ // ⚠️ THE OTHER THREE PATTERNS ARE A NAMED CORRECTION, NOT A NEUTRAL ONE.
940+ // `**/build/**`, `**/.next/**` and `**/.turbo/**` join this object under a
941+ // maintainer ruling (2026-08-26, option C on #12334, verbatim 「同意」).
942+ // Three rules are enabled on TypeScript under those directories today, and
943+ // promoting the patterns DELIBERATELY TURNS THEM OFF on build output:
919944 //
920- // packages/core/build/x.ts → LINTED, 3 rules ENABLED
945+ // packages/core/build/x.ts before → LINTED, 3 rules ENABLED
921946 // (slot-lookup/no-any-assignment, no-restricted-syntax,
922947 // query-options/no-any-erasure), parser @typescript-eslint/parser
948+ // after → no config object matches: not linted
949+ //
950+ // Recorded in those words rather than under a "changes nothing" banner,
951+ // because switching a rule off on generated code and switching it off on
952+ // source are the same line in a diff. It repairs this config's own stated
953+ // intent — never lint build output: nobody authors the code under those
954+ // directories, and linting generated code is the defect being removed, not
955+ // coverage being cut.
923956 //
924- // No such path exists in this repo today — nothing emits to `build/`, and the
925- // only Next.js app is `apps/docs`, which is outside `packages/**` — so this
926- // is latent rather than live. It is still a real semantic change, and it is
927- // left out of this object pending a maintainer ruling rather than taken
928- // silently under a "changes nothing" banner. See #12304.
957+ // `**/.next/**` is LIVE, not latent (measured 2026-08-25 on #12334):
958+ // `apps/docs/.next` is 3.9 GB after `pnpm --filter @objectstack/docs build`,
959+ // and linting it is a hard OOM — `exit 134`, "Ineffective mark-compacts near
960+ // heap limit", 66 s, no eslint output — which reads as a crashed gate rather
961+ // than as 3.9 GB of chunks being parsed. A tracked-file census cannot see it:
962+ // `.next` is untracked output, and `git ls-files` matches 0 files under any
963+ // of the five patterns (control: 4,776 under `/src/`). `build/` and
964+ // `.turbo/` stay latent on that census; the levelling below is what stops
965+ // either of them becoming the next `.next`. See #12334 / #12304.
929966 // ───────────────────────────────────────────────────────────────────────────
930967 {
931- ignores : [ '**/node_modules/**' , '**/dist/**' ] ,
968+ ignores : [ ... NEVER_LINTED ] ,
932969 } ,
933970 {
934971 files : [ '**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' ] ,
935972 ignores : [
936- '**/node_modules/**' ,
937- '**/dist/**' ,
938- '**/build/**' ,
939- '**/.next/**' ,
940- '**/.turbo/**' ,
973+ ...NEVER_LINTED ,
941974 'packages/spec/**' ,
942975 // CLI/scaffold templates contain `@objectstack/spec` strings that are
943976 // emitted to user projects, not actual imports in this repo.
@@ -980,7 +1013,7 @@ export default [
9801013 // reason to suspect it.
9811014 {
9821015 files : [ 'packages/cli/src/**/*.{ts,tsx,mts,cts}' ] ,
983- ignores : [ '**/node_modules/**' , '**/dist/**' , '**/*.test.ts' ] ,
1016+ ignores : [ ... NEVER_LINTED , '**/*.test.ts' ] ,
9841017 languageOptions : {
9851018 parser : tsParser ,
9861019 parserOptions : { ecmaVersion : 'latest' , sourceType : 'module' } ,
@@ -1019,7 +1052,7 @@ export default [
10191052 // backward-compat fixture (#2089) and are intentional.
10201053 {
10211054 files : [ 'examples/**/*.{ts,tsx,mts,cts}' , 'packages/apps/**/*.{ts,tsx,mts,cts}' ] ,
1022- ignores : [ '**/node_modules/**' , '**/dist/**' , 'packages/qa/downstream-contract/**' ] ,
1055+ ignores : [ ... NEVER_LINTED , 'packages/qa/downstream-contract/**' ] ,
10231056 languageOptions : {
10241057 parser : tsParser ,
10251058 parserOptions : { ecmaVersion : 'latest' , sourceType : 'module' } ,
@@ -1068,7 +1101,7 @@ export default [
10681101 // needs type information, so it belongs to a typed-lint pass, not here.
10691102 {
10701103 files : [ 'packages/**/*.{ts,tsx,mts,cts}' ] ,
1071- ignores : [ '**/node_modules/**' , '**/dist/**' , ...SLOT_LOOKUP_UNSWEPT ] ,
1104+ ignores : [ ... NEVER_LINTED , ...SLOT_LOOKUP_UNSWEPT ] ,
10721105 languageOptions : {
10731106 parser : tsParser ,
10741107 parserOptions : { ecmaVersion : 'latest' , sourceType : 'module' } ,
@@ -1138,8 +1171,7 @@ export default [
11381171 {
11391172 files : [ 'packages/**/*.{ts,tsx,mts,cts}' ] ,
11401173 ignores : [
1141- '**/node_modules/**' ,
1142- '**/dist/**' ,
1174+ ...NEVER_LINTED ,
11431175 // First cut is non-test code (08-03 triage). The ratchet lifts this and
11441176 // holds the test residual to a count instead.
11451177 ...QUERY_OPTIONS_TEST_GLOBS ,
@@ -1178,7 +1210,7 @@ export default [
11781210 // fakes — the arm the guard is worth the most on.
11791211 {
11801212 files : [ '**/*.{ts,tsx,mts,cts}' ] ,
1181- ignores : [ '**/node_modules/**' , '**/dist/**' , '**/build/**' , '**/.next/**' , '**/.turbo/**' ] ,
1213+ ignores : [ ... NEVER_LINTED ] ,
11821214 languageOptions : {
11831215 parser : tsParser ,
11841216 parserOptions : { ecmaVersion : 'latest' , sourceType : 'module' } ,
@@ -1204,7 +1236,7 @@ export default [
12041236 // stopped being locked.
12051237 {
12061238 files : COMMENT_SWALLOW_FILES ,
1207- ignores : [ '**/node_modules/**' , '**/dist/**' , '**/build/**' , '**/.next/**' , '**/.turbo/**' ] ,
1239+ ignores : [ ... NEVER_LINTED ] ,
12081240 languageOptions : {
12091241 parser : tsParser ,
12101242 parserOptions : { ecmaVersion : 'latest' , sourceType : 'module' } ,
0 commit comments