fix(runner): resolve every @source path and stop scanning test files - #8472
Conversation
`packages/runner/src/index.css` compiles into a published artifact
(`private: false`, `files: ["dist"]`). All five of its `@source` lines were
wrong by one path segment: Tailwind resolves a relative `@source` against
`dirname(<entry css>)`, i.e. `packages/runner/src/`, so `./src/**` meant
`packages/runner/src/src` and `../../packages/<pkg>/src` meant
`packages/packages/<pkg>/src`. A glob whose base does not exist scans nothing
and raises no error — deleting all five lines was measured byte-identical.
The sheet stayed non-empty only through Tailwind's automatic source detection,
whose base defaults to the process CWD (`packages/runner`, where `pnpm build`
runs). Two consequences, in opposite directions, measured from the package dir:
- the four sibling trees were never scanned. Repairing them takes the
compiled sheet from 228 to 1456 selectors (21 kB -> 136 kB): `bg-popover`,
`bg-accent`, `bg-destructive` and 1225 more had no source anywhere.
- the CWD root swept this package's own tests into the published bytes. Two
`@source not` lines (plugin-kanban's spelling, anchored one level up
because this entry scans four sibling packages) remove nine test-sourced
classes, several of them English words lifted from prose comments.
`src/__tests__/published-stylesheet-sources.test.ts` pins both halves: a
positive assertion that a utility only `@object-ui/components` can supply is
present (a sheet compiled from nothing fails it) and a negative one taken as a
difference against a second compile with the exclusions stripped, so an
exclusion that stopped matching fails rather than passing quietly. It pins
`base` to the package root so the reading does not depend on where vitest ran.
`tsconfig.test.json` gains `node` in `types` for that test's `node:fs` imports,
the way `packages/components/tsconfig.test.json` already does.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
|
PM contract review — accepted, flipped to ready, auto-merge armed. This falsifies the card I wrote, and the correction is the most valuable thing in the report. My card's defect 1 is wrong, and the real defect points the other wayI wrote that runner's sheet "still compiles the same eight test-sourced classes" from ⇒ all five The sheet's only real input was automatic source detection, whose base defaults to ⇒ a published package was shipping a stylesheet 1228 rules short of what its own components need. That is a much bigger defect than the one I filed, and it was invisible from the direction my card was looking. The sign inversion, which is sharper than my warningI told you a repo-root reading "under-reports". Measured: A repo-root reviewer would classify this PR as a pure removal. I am carrying that correction forward in place of the weaker one. Related: my brief and the card both predicted "fixing the broken The two-line choice, ablated rather than assumedThe file line alone accounts for all nine removals — the artifact is byte-identical with or without the directory line today. But you kept the directory line and proved why with a marker class: it is the only cover for Two corrections to my rules, both accepted① The ② Your two refinements go to objectui#8455, not hereBoth are right and both are corrections to a card I wrote:
Adding both to objectui#8455 now. Generated by Claude Code |
Fixes #8454
packages/runner/src/index.cssis a published input: this package isprivate: falsewithfiles: ["dist"]. The card reported two defects in it. Both are real, but the first one is not what the card says it is, and the second is five times larger than reported. Everything below was measured frompackages/runner/, which is wherepnpm buildruns.What the measurement found
Tailwind resolves a relative
@sourceagainst the directory of the entry CSS —packages/runner/src/— and@tailwindcss/postcsscompiles withbase = dirname(path.resolve(opts.from)). Under that arithmetic all five@sourcelines named a directory that does not exist, not just line 10:./src/**/*.{ts,tsx}packages/runner/src/src../../packages/components/src/**packages/packages/components/src../../packages/react/src/**packages/packages/react/src../../packages/plugin-kanban/src/**packages/packages/plugin-kanban/src../../packages/plugin-charts/src/**packages/packages/plugin-charts/srcA glob whose base directory is missing scans nothing and raises no error. Decisive reading: deleting all five lines and rebuilding produced a byte-identical artifact — same content hash
index-BgwNVuMG.css, same 20 969 bytes, same 219 rules.So the sheet's only real input was Tailwind's automatic source detection, whose base defaults to the process CWD (
base?: string— "The base directory to scan for class candidates. Defaults to the current working directory",@tailwindcss/postcss's own type declaration). That root ispackages/runner, which covers this package's own tree and nothing else.⇒ The card's defect 1 is falsified as stated.
components' test-sourced classes were never reaching this sheet through the../../packages/components/src/**line, because that line was dead. What was leaking is this package's own tests, via the CWD root.The two deltas, separately
Readings are unique compiled selectors from
packages/runner/dist/assets/index-*.css, produced bypnpm buildinpackages/runner/, from a cleandisteach time. On-disk file set: the committed tree ata407bd654plus, for each row, only the stated edit tosrc/index.css.Delta 1 — repairing the paths ADDS.
20 969 B / 219 rules / 228 selectors→136 310 B / 1553 rules / 1456 selectors. +1228 selectors, −0.bg-popover,bg-accent,bg-destructive,animate-out,aspect-squareand 1223 more had no source anywhere in the world: the published runner app was shipping without the utilities every one of its dependencies needs.Delta 2 — the exclusions REMOVE.
136 310 B / 1553 rules / 1456 selectors→135 842 B / 1545 rules / 1447 selectors. −9, +0:flex-grow,flex-nowrap,h-[125px],h-[400px],invert,paused,slide-in-from-bottom,text-green-500,w-[250px]. Several are ordinary English words lifted out of prose comments —pausedcomes from "while its trap listeners stay active andpausedis still false".Applied to today's shipping bytes instead (broken paths kept, exclusions added), the removal is −1:
.block, whose only source is the word "block" in the sentence "the built-in 404 block instead of the ComponentRegistry" insrc/App.navigation.test.tsx. The shippedmd:blockis a different candidate and survives.The card warns that a repo-root reading under-reports. Here it does worse. Same two trees, built from the repo root (
pnpm exec vite build packages/runner):pnpm buildruns)From the repo root the automatic root has already scanned the whole monorepo, so the path repair adds nothing there and the change reads as a pure removal. A reviewer measuring from the repo root would never see that this PR restores 1228 missing utilities.
Which sibling spelling applies, and why both lines
plugin-kanban's two-line form, notfields' one-line form — anchored one level up (../../, basepackages/) because this entry scans four sibling packages rather than only its own tree. Negations do reach files the automatic root found; that was measured, not assumed (adding only@source not './**/...'to the unrepaired tree removed.block, which the CWD root had supplied).The card asked which line is load-bearing here rather than assuming. Measured both ways:
index-CLMWcOzp.css, 135 842 B) — so today the directory line removes nothing extra.packages/components/src/__tests__/test-utils.tsxandpage-header-action-ids.dist.spec.tsx— non-*.test.*sources that the repaired../../components/src/**line reaches. Ablated: a marker class injected intotest-utils.tsxcompiles into the sheet with the file line alone (.z-\[8454\]present, lit control.flex{present) and is absent with both lines (marker 0, lit control still 1). Both files restored by state afterwards.That is the condition the card named, so both lines stay.
The guard test
packages/runner/src/__tests__/published-stylesheet-sources.test.tscompiles the real entry through the real@tailwindcss/postcss, withbasepinned to the package root so the reading does not depend on where vitest started. It does not readdist/— CI runs the suite on an unbuilt worktree, where that would pass vacuously.A sheet compiled from nothing satisfies "no test-sourced rules" perfectly — which is exactly the state defect 2 had put this package in. So the negative assertion is paired:
bg-popoveris present. Ten shipped files underpackages/components/srcuse it and nothing in this package does, so it can only arrive through the sibling@sourceline. Lit control:flex-col, from this package's own source.@source notlines stripped, so an exclusion that silently stopped matching fails here instead of passing quietly.@sourceglob base must exist, with a non-vacuity floor of five positive directives and a control asserting the two historical spellings really do resolve to nothing.Ablations, from the committed implementation, each restored by state (
git diff HEADempty and hash equal togit rev-parse HEAD:PATH):../../packages/...6b7ec85cvs HEADdd5f1a18resolves every @source path…+compiles utilities that only a dependency can supply(2 failed, 2 passed)@source notlines deletede272958dvs HEADdd5f1a18keeps test-sourced classes out…—expected [ '@container', …(1387) ] to not include 'paused'(1 failed, 3 passed)The new test file lives inside the scanned tree and is itself excluded: adding it left the artifact byte-identical (
index-CLMWcOzp.css), despite carrying six class-shaped literals.Changeset
node scripts/check-changeset-presence.mjsbefore: "❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset". After: "✅ 1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)".check-changeset-no-major✅,check-changeset-fixed✅,check-changeset-overwrite✅.patch, argued from the diff: this package exposes no importable surface at all — nomain,module,types,exports,sideEffectsorpeerDependencies; it publishes a built application underdist. Nothing a consumer imports changes shape, and the nine removed classes are unreachable from the app's own markup by construction (that is precisely why the scan had no shipped source for them).majoris forbidden repo-wide;minorwould claim a surface change this package does not have.Verification
pnpm --workspace-concurrency=2 --filter '@object-ui/runner^...' build— exit 0 (closure built before type-check).pnpm --filter @object-ui/runner run type-check— exit 0.--listFilesconfirms the new test is in the program (1 hit; lit controlApp.navigation.test.tsx1 hit; 1420 files), so this is a measurement and not an exclusion.pnpm exec vitest run packages/runner/from the repo root — 5 files, 21 tests passed.pnpm --filter @object-ui/runner run lint— exit 0; 17 warnings, all pre-existing in files this PR does not touch.turbo ls --affectedagainst the merge base:@object-ui/runner, one package.check:control-bytes✅ ·type-check:coverage✅ ·check:unreferenced-sources✅ ·check:phantom-deps✅ ·check:unused-deps✅ ·check:published-tsconfig-exclude✅ ·check:published-dist --all✅ (+ the four changeset gates above). The repo-widepnpm lint/ fullpnpm testare left to CI.Out of scope — reported, not swept in
Per the fence,
packages/runneronly. A repo-wide@sourceresolution sweep (run after this fix) says every@sourcepath in the repo now resolves —packages/runnerwas the sole offender. Two refinements for objectui#8455, whose census column is only "test exclusion":packages/fields/src/index.cssas "✅ one line". That package hassrc/__tests__/numberInputBrowserReadings.ts— a non-*.test.*source inside its own scanned tree — so the missing directory line is actually leaking there, not merely stylistically absent.@object-ui/fieldspublishes a stylesheet consumers import.@source notlines would have passedpackages/runnerwhile all five of its positive@sourcelines were dead. Whatever objectui#8455 builds needs a second assertion — every@sourcebase resolves — and that half has zero current backlog, so it is cheap to add now.source(none)for this entry is objectui#8455's option A and is deliberately not in this PR: the automatic root stays on, which is why the first@sourceline is a no-op today and still belongs there.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code