Uh oh!
There was an error while loading. Please reload this page.
test(scripts): the #3944 alias-target gate now covers the 24 package-level vite.config.ts tables - #5381
Merged
Conversation
…level vite.config.ts tables (#5168) `vitest-config-alias-targets-3944.test.ts` enforced "a declared alias target must exist" for the two ROOT tables — `vitest.config.mts`'s `resolve.alias` (#3944) and `tsconfig.json`'s `compilerOptions.paths` (folded in by #4804). The package-level `vite.config.ts` tables were covered by nothing: measured on this tree, 24 configs declaring 227 entries, 200 of them `@object-ui/*`. The only other gate that reads those tables, `side-effects-declaration- consistency.test.ts`, does not go red on a dead entry, and that is correct for its own question — an unresolvable target contributes no entry form, so no `sideEffects` declaration can conflict with it. Target existence and `sideEffects` consistency are two contracts, so this lands in the file whose contract already is the former, the same absorption #4804 performed. The parse is borrowed from that gate's AST reader rather than written a fifth time: across 24 files the table is spelled four ways (bare `resolve(`, `path.resolve(`, the array `{ find, replacement }` form, double-quoted keys), and #5158 measured a regex reader that understood one of them reading 70 of 196 entries with 15 configs contributing zero — green because it never looked. A spelling-coverage case asserts each of the four is reachable, so dropping one goes red rather than silently shrinking the surface. Two adjustments the root tables did not need, both named rather than silent: existence probes Vite's `resolve.extensions` after the substitution, because an extension-less target naming a file is live here (`packages/runner`'s `@/lib/utils` is `utils.tsx`); and the root half's "an extension-less target must be a directory" case is deliberately NOT ported, because down here landing on a file is the intended spelling and porting it would redden a correct config. The new gate found one live dead entry on its first run: `packages/runner`'s `"@app"` points at `packages/runner/src/app-data`, absent in every form, with the declaration as its only occurrence in the repo. Carved out as a shrink-only ratchet with a pin asserting it is still declared AND still missing, because the fix — drop the line, or restore the DX symlink its comment names — is a maintainer call. Filed as #5380, unassigned. Note it is not an `@object-ui/*` key: a gate scoped to that prefix would have shipped green over it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
os-support-ai
marked this pull request as ready for review
August 20, 2026 03:31
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#5168
What was missing
scripts/__tests__/vitest-config-alias-targets-3944.test.tsenforces "a declared alias target must exist on disk" — for two root tables:vitest.config.mts'sresolve.alias(#3944) andtsconfig.json'scompilerOptions.paths, folded into the same file by #4804. The package-levelvite.config.tstables were covered by nothing.The only other gate that reads those tables,
side-effects-declaration-consistency.test.ts, does not go red on a dead entry —resolveAliasModule()returns undefined andresolvableEntryPaths()continues. That is correct behaviour for its own question (a dead alias contributes no entry form, so nosideEffectsdeclaration can conflict with it), not an oversight. Target existence andsideEffectsconsistency are two contracts, so this lands in the file whose contract already is the former — the same absorption #4804 performed — rather than making that gate health-check tables it only reads incidentally.Counts — measured here, not adopted
Three prior methods gave three numbers (card: 24 files / 196 entries; triage: 24 of 26; a crude
git grep -l: 26 files). Measured by this PR's own parse onmain@aff10e283:vite.config.*basenameresolve.aliastable@object-ui/*@object-ui/*targets24 matches the card and triage. 200 is not 196: the card measured
main@cdac3cc20, andapps/console(34 to 35),examples/console-starter(29 to 30) andpackages/runner(14 to 15) have each gained an entry since, plus one more elsewhere. The 26-vs-24 spread is the looser predicate — two of the 26 configs match onaliasesExclude/Object.assign, not on aresolve.aliastable.Why an AST read, and why borrowed
Across 24 files the table is spelled four ways: bare
resolve(,path.resolve(, the arrayalias: [{ find, replacement }]form, and double-quoted keys. #5158 measured the cost of a regex here — a reader requiringpath.resolve(after a single-quoted key saw 70 of 196 entries, 15 configs contributing zero: not checked-and-passed, never looked at. The parser is borrowed fromside-effects-declaration-consistency.test.ts(:397-502,resolveAliasModuleat:524) rather than written a fifth time. Borrowed, not moved or edited — that file is another gate's, and its copy answers its own question.One deliberate divergence from the borrowed copy: entries whose key is a RegExp matcher are kept. There it is right to drop them (a RegExp names no single specifier, so it yields no entry form); here the question is about the replacement, and a RegExp-keyed replacement is a path like any other —
packages/componentshas three, all pointing at real shim files.Two things that do not transfer from the root halves
existsSyncalone. A package table legitimately aliases an extension-less file:packages/runner's"@/lib/utils"points atpackages/components/src/lib/utils, which isutils.tsxon disk and resolves fine, because Vite probesresolve.extensionsafter substituting an alias. The predicate is "exists, or exists with one of those extensions appended".src/root. Down here, landing on a file is the intended spelling, and porting the case would have reddened a correct config. Named in the header rather than silently dropped.The gate found one live dead entry
packages/runner/vite.config.ts:25—"@app": path.resolve(__dirname, "./src/app-data").packages/runner/src/app-datadoes not exist in any form, and the declaration is the only occurrence of@appin the repo. Its comment readsDX: App Data Symlink, so the fix is either "drop the line" or "restore the symlink it was written for" — a maintainer call, not a rider on a gate that is only supposed to start looking. It is carved out as a shrink-only ratchet (KNOWN_MISSING_PACKAGE_ALIAS_TARGETS), the same shape #4820 has in thepathshalf: the list may not grow, and a pin asserts the entry is still declared and still missing, so resolving it either way turns this file red until the list is emptied with it. Filed unassigned as #5380 — that issue remains open and is not addressed here.Worth noting for scope:
"@app"is not an@object-ui/*key. A gate scoped to the 196-entry@object-ui/*framing would have shipped green over it. That is why the range is "every entry in the table", matching both root halves, which also check every key regardless of prefix. This is also the second time this one config has outlived a target — its own comment records #3593, where a2>/dev/nullkept a deaddata-objectqlentry quiet.Reverse verification — the whole difficulty, since the gate lands green
There are zero dead
@object-ui/*entries today, so passing proves nothing. Direction predicted before running: plain RED, and +1 case as well as +1 failure. The case count is the load-bearing half — a spelling the parser skipped would add zero cases and stay green, which is exactly the "did not look" green this card exists to close.No build artifact sits between any edit below and the thing under test: the gate reads
node:fsand parses source text, so nothing is compiled, bundled or emitted in between. Nothing was rebuilt, and nothing needed to be. All runs from the repo root (pnpm exec vitest run scripts/…), neverpnpm --filter.'@object-ui/ghost-5158': resolve(__dirname, '../ghost-5158/src')inpackages/plugin-editor(single-quoted key, bareresolve)path.resolve(inpackages/fieldspackages/runner{ find, replacement }, inpackages/componentsghostHelper('…'))parses EVERY entry in every tablepath.resolvemkdir packages/runner/src/app-dataA carved-out target now exists…Legs A–D confirm all four spellings are reachable, one probe each, in the config that natively uses that spelling. Leg E confirms the different shape the no-silent-skip case has. Leg G confirms the ratchet's obsolescence half.
Prediction vs observation differed on leg F, and the gap is the finding. I predicted at least two red cases (the spelling-coverage case and the parse-gap case). Observed five: the plausibility floor, the per-config coverage case and the carve-out pin catch the shrink too, because 94 of the 227 entries are written with
path.resolveand their disappearance drops the total under the floor, empties three configs entirely, and takes@appout of the parsed set. Five independent cases stand between a silently-narrowed parser and a green run — stronger than designed for, not weaker.The contrast that shows the hole was real, on probe A: with the dead entry in place, the two pre-existing gates were run against the gate file as it stands on
origin/main—Tests 88 passed (88), i.e. #3944 at 58/58 and side-effects at 30/30, reproducing exactly what #5168 measured. The same tree with this PR's gate goes red.Verification
Gate union re-derived against the actual diff (one file under
scripts/__tests__/, one changeset) and run on the final commitdeb273f91:pnpm exec vitest run scripts/— 57 files, 1522 tests, all passing (the gate file itself contributes 289).pnpm run type-check:scripts(tsc -p tsconfig.scripts.json) — clean.pnpm exec eslint scripts/__tests__/vitest-config-alias-targets-3944.test.ts— clean.node scripts/check-control-bytes.mjs— OK, 4762 tracked text files scanned.node scripts/check-changeset-presence.mjs— "No source of a released package changed in this range, so no changeset is owed";check-changeset-no-major.mjs— nomajordeclared.objectuihas noskip-changesetlabel, so the declaration form is an empty-frontmatter changeset — added, and correct here because the diff is one file underscripts/__tests__/with no packagesrc/touched, which is precisely what the presence check reports.Not in scope
side-effects-declaration-consistency.test.tsis read but not modified.src/, nocontent/docs/releases/.Generated by Claude Code