Skip to content

fix: keep non-picked route exports that picked exports reference#2205

Open
brenelz wants to merge 2 commits into
mainfrom
fix/tree-shake-referenced-exports
Open

fix: keep non-picked route exports that picked exports reference#2205
brenelz wants to merge 2 commits into
mainfrom
fix/tree-shake-referenced-exports

Conversation

@brenelz

@brenelz brenelz commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Fixes #2100

What is the current behavior?

The route tree-shaker deletes the entire binding of any export not in the ?pick list. An API route like

export const hello = () => "hello";
export const GET: APIHandler = async () => hello();

is imported with ?pick=GET, hello's declaration is removed, and the handler crashes at runtime with ReferenceError: hello is not defined (500 on every request). Verified end-to-end on a production build of the basic fixture.

There is also an always-true condition in the export-specifier handling that removes picked specifiers, so export { POST as GET } never survives — the root cause of #1659.

What is the new behavior?

Non-picked exports are un-exported instead of deleted: the export keyword is stripped and the binding stays in module scope. Kept bindings are registered with the existing sweep pass, which still removes them (and their imports) when nothing references them — so no code reaches a bundle it didn't already reach, addressing the server-code-leak concern discussed in #2104.

Details:

  • Mixed declarations (export const a = ..., GET = ...) are split into one declaration per declarator, preserving evaluation order; only picked ones stay exported.
  • Named default-export function/class declarations are kept in module scope (picked exports may reference them) and swept if unreferenced; anonymous ones are removed as before.
  • export { a, b as c } specifiers are now filtered by their exported name (fixes [Bug?]: export { POST as GET } is now broken #1659).
  • export class is handled like export function.

Other information

Added packages/start/src/config/fs-routes/tree-shake.spec.ts (9 tests, 5 of which fail without this fix). Full playwright e2e suite in apps/tests passes. Supersedes the approach in #2104, which un-exported picked declarators in mixed declarations, produced invalid syntax for anonymous default exports, and kept unreferenced non-picked exports in the output.

🤖 Generated with Claude Code

The route tree-shaker removed the entire binding of any export not in
the pick list, so an API handler calling a helper exported from the
same file crashed at runtime with a ReferenceError. Instead of deleting
the declaration, strip only the export keyword and register the kept
binding with the existing sweep pass, which still removes it (and its
imports) when nothing references it - so no extra code reaches a bundle
that didn't already reach it.

Also fixes an always-true condition in the export-specifier handling
that dropped picked exports declared via specifiers, e.g.
`export { POST as GET }`.

Mixed declarations (`export const a = ..., b = ...`) are split into one
declaration per declarator, preserving evaluation order, and only the
picked ones stay exported. Named default-export declarations are kept
in module scope when other exports reference them; anonymous ones are
still removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f70faed

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@solidjs/start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploy Preview for solid-start-landing-page ready!

Name Link
🔨 Latest commit f70faed
🔍 Latest deploy log https://app.netlify.com/projects/solid-start-landing-page/deploys/6a598a32b03ad70008c39edd
😎 Deploy Preview https://deploy-preview-2205--solid-start-landing-page.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

// if opts.keep is true, we don't remove the routeData export
if (state.opts.pick && !state.opts.pick.includes("default")) {
exportNamedPath.remove();
const decl = exportNamedPath.node.declaration;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lxsmnsyc do these changes make sense?

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Jul 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@solidjs/start@2205

commit: f70faed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug?]: exports disappear [Bug?]: export { POST as GET } is now broken

1 participant