feat: vite-plugin-solid-three — build-time tree-shaking for createT(THREE)#61
Closed
bigmistqke wants to merge 21 commits into
Closed
feat: vite-plugin-solid-three — build-time tree-shaking for createT(THREE)#61bigmistqke wants to merge 21 commits into
bigmistqke wants to merge 21 commits into
Conversation
A Vite plugin that makes createT(THREE) tree-shakeable by letting the bundler itself compute the used-set: pass 1 rewrites the catalogue binding to a throwaway namespace scaffold and reads renderedExports after tree-shaking; pass 2 emits createT({ ...used keys with locally-resolved providers }), keeping createT in the output.
Replaces the earlier ts-morph / alias-resolution-host / soundness-guard / unplugin approach, which reconstructed cross-file analysis the bundler already performs. Mechanism (cross-file union, sound deopt, custom literals, spread/override precedence) and the two-pass orchestration are spike-validated.
Bite-sized TDD tasks for the bundler-as-analyzer design: single-file createT analysis (parse with ts.createSourceFile, no Program), provider-map with last-write-wins, throwaway measurement scaffold, and the two-pass measure/emit orchestration. Pure layers (analyze/providers/rewrite/scaffold) are unit-tested; the orchestration and catalogue shapes are covered by real vite-build fixtures; the browser oracle is the release gate.
Also adds packages/vite-plugin-solid-three to the pnpm workspace so its magic-string dependency is installed and resolvable by vitest.
… hash, clarify filters
Getter/method catalogue entries carried their full member text, but emit prefixed it with `key:` — producing invalid syntax (`createT({ Foo: get Foo(){…} })`) and a failing build. The entry source now records `verbatim` and emit pushes such members as-is. Adds a getter fixture (the emit side was untested) and an escape fixture proving the whole-value-escape deopt keeps the full catalogue. Also clears coordination state per build and documents the one-build-per-process limitation.
…measure pass, unplugin door reopened
4 tasks
commit: |
Contributor
Author
|
Closing this one. After living with it for a bit, the two-pass "let the bundler measure usage" approach — rewriting For the cost and the moving parts, the immediate payoff just isn't there to justify carrying it. Leaving the branch up for reference in case we want to revisit the idea later, but not pursuing this path for now. |
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.
Summary
A new Vite plugin,
vite-plugin-solid-three, that makescreateT(THREE)tree-shakeable.createTreturns a runtime proxy, and passing the wholeTHREEnamespace into it defeats the bundler's tree-shaking — so all of three ships. This plugin narrows the call to only the classes your app actually reaches throughT.It lets the bundler do the analysis, in two passes on production builds:
renderedExports) exactly which keys survive — across files, dynamic imports, and code-split chunks.createT(THREE)is rewritten tocreateT({ Mesh: THREE.Mesh, … })with only the measured keys.createTstays in the output, so runtime and debugging are unchanged.Soundness is structural: any access the bundler can't resolve statically (
T[expr],Tescaping into a function,{...T}) deopts to keeping the whole catalogue; dynamic catalogue sources (createT(store), computed keys) are left untouched. No static cross-file analysis, no alias replication, no resolution guard — the bundler is the analyzer, so the result is exact rather than over-approximate. Dev is untouched (the runtime proxy is used).This supersedes the earlier ts-morph/unplugin approach (#60), which reconstructed cross-file analysis the bundler already performs.
What's covered
Fixtures build small consumer projects through the plugin and assert drop/keep:
createT(THREE), custom object literals, spread + override precedence (last-write-wins), verbatim getters{...T}spread → full catalogue kept (sound)Tis present in the narrowed catalogueTest plan
pnpm --filter vite-plugin-solid-three test(30 Node tests)pnpm --filter vite-plugin-solid-three test:oracle(browser soundness gate)pnpm --filter vite-plugin-solid-three build(clean dist)Notes / deferred
docs/superpowers/specs/2026-06-01-createt-narrowing-design.md.🤖 Generated with Claude Code