Skip to content

fix(optimizer): skip props destructuring on destructured-param reassign (#8638)#8647

Merged
wmertens merged 7 commits into
QwikDev:build/v2from
46ki75:v2-optimizer-destructure-reassign
May 20, 2026
Merged

fix(optimizer): skip props destructuring on destructured-param reassign (#8638)#8647
wmertens merged 7 commits into
QwikDev:build/v2from
46ki75:v2-optimizer-destructure-reassign

Conversation

@46ki75
Copy link
Copy Markdown

@46ki75 46ki75 commented May 19, 2026

What is it?

  • Bug

Description

The optimizer's transform_props_destructuring pass was rewriting component$ bodies that destructure props and then reassign one of the destructured names — including reassignments inside for-of / for-in heads — even though the rewrite is unsound in that case. The resulting code threw a ReferenceError at SSR time, which surfaced as a dev-SSR hang (request never resolved) or a 500 in production.

Two-commit fix in the Rust optimizer:

  • d17959b — base case: ReassignFinder now detects AssignExpr / UpdateExpr writes to a destructured name and refuses the rewrite.
  • 66399d3 — extension: also detects writes via for-of / for-in loop heads (for (x of …), for ({ x } of …)).

Coverage:

  • Rust snapshot tests in packages/optimizer/core/src/test.rs for both the base case and the for-of/for-in variants.
  • Playwright e2e regression under e2e/qwik-e2e/tests/qwikrouter/issue8638-document-head-helper.e2e.ts, using a DocumentHead helper that exercises the original bug pattern end-to-end (Vite → optimizer → SSR → emitted HTML). The e2e asserts: no dev-SSR hang, no pageerror, and the og:image meta is emitted with the helper's default (guards against silent dead-code drop).

Sanity-checked by temporarily reverting the optimizer change, rebuilding WASM/NAPI, and confirming the e2e fails on the og:image assertion.

Checklist

  • My code follows the developer guidelines of this project
  • I performed a self-review of my own code
  • I added a changeset with pnpm change
  • I added new tests to cover the fix / functionality

46ki75 added 5 commits May 18, 2026 21:09
… reassigned

The props-destructuring pass rewrites identifier *reads* of a destructured
parameter to `_rawProps.<name>`, but does not rewrite assignment LHS. If
the body reassigns a destructured binding (via `=`, `??=`/compound assign,
`++`/`--`, or destructuring-assignment LHS like `({foo} = …)` and
`[foo] = …`), the LHS was left as a now-undeclared identifier — silently
miscompiling the function and producing a strict-mode `ReferenceError` at
runtime. In some cases a downstream simplifier even dropped the assignment
entirely with no error.

Refuse the transform whenever the body reassigns any destructured param.
This is safe because Qwik component props are read-only, so reassigning
one is semantically meaningless — affected code is either a non-component
helper that happens to share the destructuring shape, or a bug in user
code that should be surfaced rather than silently miscompiled.

Adds regression tests covering template-literal, string-literal,
nullish-assign, update-expression (`++`), object-destructuring-assignment
(`({foo} = …)`), and array-destructuring-assignment (`[foo] = …`)
reassignment forms.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

🦋 Changeset detected

Latest commit: f1cbd63

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

This PR includes changesets to release 6 packages
Name Type
@qwik.dev/optimizer Patch
@qwik.dev/core Patch
eslint-plugin-qwik Patch
@qwik.dev/react Patch
@qwik.dev/router Patch
create-qwik 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

@46ki75 46ki75 marked this pull request as ready for review May 19, 2026 12:23
@46ki75 46ki75 requested a review from a team as a code owner May 19, 2026 12:23
Copy link
Copy Markdown
Member

@wmertens wmertens left a comment

Choose a reason for hiding this comment

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

Thank you for the PR; however, can you make the snapshots way more compact (testing several cases in a single test), and can you also add specific code that checks the result so we don't have to rely only on the snapshot (there are other tests that check this too)

We're overhauling the optimizer and adding all these snapshots is a maintenance burden.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 20, 2026

Open in StackBlitz

@qwik.dev/core

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/core@8647

@qwik.dev/router

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/router@8647

eslint-plugin-qwik

npm i https://pkg.pr.new/QwikDev/qwik/eslint-plugin-qwik@8647

create-qwik

npm i https://pkg.pr.new/QwikDev/qwik/create-qwik@8647

@qwik.dev/optimizer

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/optimizer@8647

commit: f1cbd63

Copy link
Copy Markdown
Member

@wmertens wmertens left a comment

Choose a reason for hiding this comment

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

LGTM

@wmertens wmertens enabled auto-merge (squash) May 20, 2026 22:55
@wmertens wmertens merged commit d124a92 into QwikDev:build/v2 May 20, 2026
30 checks passed
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.

2 participants