test(exports): close two holes in the exports-pairing guard - #777
Merged
sroussey merged 1 commit intoAug 14, 2026
Conversation
Both are cases where the guard test does not guard what it claims. A `browser` block declaring only `types` was invisible to ALL SIX rules. `collectBranches` records a branch only when a string-valued implementation key is present, so such a block produces no branch at all — not even for the source-entry rule. Bundlers, meanwhile, enter the `browser` condition, match nothing, and fall through to the outer `import`, bundling the node build while tsc (customConditions ["browser"]) types against the browser declarations. That is exactly the browser-typed-as-node inversion this file exists to prevent, reached by omission rather than by a wrong target. `browserSplitViolations` now decides the empty-implementation case BEFORE the browser-source-entry guard. Hoisting past that guard is deliberate: the `packages/*` layout (stem `node`, no `src/node.browser.ts`) never reaches the guarded code, so the bug would otherwise go unreported for every package in the repo. Fixtures cover the openai manifest with its browser `"import"` deleted, the `packages/storage` layout with the `never` probe, and a healthy block that must stay silent — the first two assert `findViolations` and `orderViolations` are `[]` first, the go-red proof. "Source entry exists" proved only the `.d.ts` half. `build-types` runs `tsgo` over the whole `src` tree, so the file's existence is what makes the declaration appear; the `.js` comes from hand-written entry lists in each package's `build*` scripts, which nothing read. Add `providers/foo/src/ai.browser.ts` and a `browser` block, forget to append `./src/ai.browser.ts` to `build-browser`, and every check passed while `dist/ai.browser.js` was never emitted. `buildEntryViolations` collects every `.js`/`.cjs`/`.mjs` implementation target, derives its source entry from the dist stem, and requires a whole-token match in the joined text of the package's `build*` scripts — so duckdb's nested `--outdir` matches and `./src/ai.browser.ts` does not satisfy the stem `ai`. Unrecognized layouts are reported, not skipped. Zero violations across every manifest today, with exactly one exemption: `packages/workglow`, whose build is glob-driven. That exemption is pinned in `GLOB_BUILT_PACKAGES` and guarded by a test asserting the package really does hand its build to a repo-local `*.ts` program, so it dies if the package goes back to naming its entries. This lives in the test file that reviews the manifest rather than in a `publish-workspaces.ts` prepack assertion: a prepack assertion fires after review and after merge, and a guard that does not guard has to be able to fail the PR that introduces it. Also corrects the comment above "declares only targets a source entry file can emit", which claimed the source entry was the cheapest evidence the build produces the target at all — true of the declaration only.
Coverage Report
File CoverageNo changed files found. |
sroussey
merged commit Aug 14, 2026
d53f0ba
into
claude/wonderful-turing-rjtcnx-ai-types
11 checks passed
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 free
to 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.
Targets
claude/wonderful-turing-rjtcnx-ai-types(#717), notmain.Two review findings, both the same shape: a guard test that does not guard what it claims. All changes are in
packages/test/src/test/util/ExportTypesPairing.test.ts.1. A
browserblock withtypesbut no implementation defeated every checkcollectBranchesrecords a branch only when a string-valued implementation key is present, so a block likeproduces no branch at all — it is invisible to all six rules, including the source-entry rule, which can only check targets a branch declared. Meanwhile Vite/webpack enter the
browsercondition, recognize no key in it, fall through to the outer"import", and bundle the node build, whiletscundercustomConditions: ["browser"]matchesbrowser > typesand type-checks against the browser declarations. That is the browser-typed-as-node inversion this file exists to prevent, reached by omission instead of by a wrong target.browserSplitViolationsnow evaluates the empty-implementation case before the browser-source-entry guard, and the violation names the node target bundlers fall through to. Hoisting pastif (!browserEntry.some(hasSourceEntry)) continue;is deliberate: thepackages/*layout (stemnode, nosrc/node.browser.ts) never reaches the guarded code, so an emptybrowserblock there would carry the same bug unreported for every package in the repo.Three fixtures: the openai manifest with its browser
"import"deleted, thepackages/storagelayout with theneverprobe, and a healthy block that must stay silent. The first two assertfindViolationsandorderViolationsare[]first — the go-red proof this file already uses.2. "Source entry exists" proved only the
.d.tshalfbuild-typesrunstsgoover the package's wholesrctree, so the existence of a source file is exactly what makes its declaration appear. The.jshalf comes from the hand-written entry lists in each package'sbuild-code/build-browser/build-*scripts, which nothing read. Addproviders/foo/src/ai.browser.tsand abrowserblock, forget to append./src/ai.browser.tstobuild-browser, and every check passed whiledist/ai.browser.jswas never emitted — the package publishes abrowsercondition pointing at a missing file.New rule
buildEntryViolations(manifest, exportsMap, scripts):.js/.cjs/.mjsimplementation target,(^|\s)\.?/?src/<stem>\.tsx?(\s|$)in the joined text of everybuild*script.Whole-token matching is load-bearing in both directions:
bun build --outdir ./dist/storage ./src/storage/browser.tsmust satisfy the nested stemstorage/browser(duckdb, sqlite), and./src/ai.browser.tsmust not satisfy the stemai— a substring test would pass a package that builds only its browser bundle while publishing the node one. Unrecognized layouts are reported, not skipped.Verified over every workspace manifest before committing: zero violations, exactly one exemption. That exemption (
packages/workglow, whosebuild-jsisbun run build.ts) is pinned inGLOB_BUILT_PACKAGESand guarded by a test asserting the exempted package really does hand its build to a repo-local*.tsprogram — matched as the token right afterbun/bun run, so a.tshanded tobun buildas an entry does not satisfy it. The exemption therefore dies with the custom build rather than quietly exempting a hand-written list.This is a rule in this test file rather than a prepack assertion in
publish-workspaces.ts, on purpose: the defect is that a guard test does not guard what it claims, so it must be able to fail the PR that introduces it. A prepack assertion fires after review and after merge.3. Comment correction
The comment above
it("declares only targets a source entry file can emit")claimed the source entry is "the cheapest evidence that the build produces the target at all". It now says a source entry is necessary for either half and sufficient for only the declaration, and points at the rule that covers the other half.Verification
bun scripts/test.ts util vitest— 54 files, 797 passed, 10 skipped.ExportTypesPairing.test.tsalone: 40 passed (was 25).browserSplitViolationshoist fails both new browser-split fixtures and nothing else.exports: zero violations.eslintclean,prettier --checkclean on the changed file.🤖 Generated with Claude Code
Generated by Claude Code