Observation-class finding, dormant today. Filed while implementing objectui#4854; not fixed there (out of scope, and the PR routed around it instead).
The claim, and where it stops holding
tsconfig.scripts.json's header states the invariant explicitly:
Two of the files here (scripts/vite-crypto-stub.ts, scripts/vite-maplibre-worker.ts) ARE already compiled, by apps/console/tsconfig.node.json … They are deliberately NOT excluded below … The cost of the overlap is paid by matching that project's option set (strict, ESNext, bundler resolution, and notably NO noImplicitReturns), so a shared file cannot be green in one project and red in the other.
The option sets do not in fact match. apps/console/tsconfig.node.json:30 sets "allowImportingTsExtensions": true (its header explains why: the console's config imports carry real .ts extensions so Vite's configLoader: 'native' can load them — objectui#3384). tsconfig.scripts.json sets no such option, and its compilerOptions are spelled out rather than inherited, so nothing supplies it.
Measured
tsconfig.scripts.json sets "noEmit": true, so allowImportingTsExtensions is legal there; it is simply absent. The divergence is invisible today only because no .ts file under scripts/ currently writes a specifier ending in .ts — the two named shared files import nothing relative, and scripts/__tests__/*.ts spell their imports extensionless.
It surfaces the moment anything under scripts/ reaches the console's build config. Adding a test there that statically imported apps/console/vite.config.ts produced, from pnpm type-check:scripts:
apps/console/vite.config.ts(16,32): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
apps/console/vite.config.ts(17,36): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
Lines 16 and 17 are the pre-existing imports of scripts/vite-crypto-stub.ts and scripts/vite-maplibre-worker.ts — the exact two files the header names as deliberately shared, red in one program while tsc -b apps/console/tsconfig.node.json --force stays green on the same bytes. That is the shape the header says the matched option set prevents.
Why it is filed rather than fixed
PR for objectui#4854 needed to evaluate the console's config from a scripts/__tests__ test. It assembles the specifier at runtime instead, which keeps apps/console/vite.config.ts out of the scripts program entirely — the program boundary the header is really defending. Widening a shared tsconfig as a rider on a build-hook PR is the wrong place for the decision, so it is left here.
Two options for whoever picks it up, neither obviously free:
- Add
"allowImportingTsExtensions": true to tsconfig.scripts.json, restoring the stated invariant. Cheap, and it makes the header true again. - Decide the invariant is narrower than written — the option is a property of how the console loads its config, not of the shared files — and correct the header instead, naming
allowImportingTsExtensions as a deliberate difference with its reason.
Either way, scripts/__tests__/scripts-type-check.test.ts is where the choice should be pinned; it already asserts noEmit/strict/composite on this project and would be the natural home for an option-parity assertion against apps/console/tsconfig.node.json, which is what would have caught this.
Nothing a user hits, nothing red on main — hence finding and no pm:queue.
Observation-class finding, dormant today. Filed while implementing objectui#4854; not fixed there (out of scope, and the PR routed around it instead).
The claim, and where it stops holding
tsconfig.scripts.json's header states the invariant explicitly:The option sets do not in fact match.
apps/console/tsconfig.node.json:30sets"allowImportingTsExtensions": true(its header explains why: the console's config imports carry real.tsextensions so Vite'sconfigLoader: 'native'can load them — objectui#3384).tsconfig.scripts.jsonsets no such option, and itscompilerOptionsare spelled out rather than inherited, so nothing supplies it.Measured
tsconfig.scripts.jsonsets"noEmit": true, soallowImportingTsExtensionsis legal there; it is simply absent. The divergence is invisible today only because no.tsfile underscripts/currently writes a specifier ending in.ts— the two named shared files import nothing relative, andscripts/__tests__/*.tsspell their imports extensionless.It surfaces the moment anything under
scripts/reaches the console's build config. Adding a test there that statically importedapps/console/vite.config.tsproduced, frompnpm type-check:scripts:Lines 16 and 17 are the pre-existing imports of
scripts/vite-crypto-stub.tsandscripts/vite-maplibre-worker.ts— the exact two files the header names as deliberately shared, red in one program whiletsc -b apps/console/tsconfig.node.json --forcestays green on the same bytes. That is the shape the header says the matched option set prevents.Why it is filed rather than fixed
PR for objectui#4854 needed to evaluate the console's config from a
scripts/__tests__test. It assembles the specifier at runtime instead, which keepsapps/console/vite.config.tsout of the scripts program entirely — the program boundary the header is really defending. Widening a shared tsconfig as a rider on a build-hook PR is the wrong place for the decision, so it is left here.Two options for whoever picks it up, neither obviously free:
"allowImportingTsExtensions": truetotsconfig.scripts.json, restoring the stated invariant. Cheap, and it makes the header true again.allowImportingTsExtensionsas a deliberate difference with its reason.Either way,
scripts/__tests__/scripts-type-check.test.tsis where the choice should be pinned; it already assertsnoEmit/strict/compositeon this project and would be the natural home for an option-parity assertion againstapps/console/tsconfig.node.json, which is what would have caught this.Nothing a user hits, nothing red on
main— hencefindingand nopm:queue.