Skip to content

test(worker): prove Vite-equivalent Web Worker parity - #2892

Open
james-elicx wants to merge 7 commits into
mainfrom
codex/worker-vite-parity-31439707085
Open

test(worker): prove Vite-equivalent Web Worker parity#2892
james-elicx wants to merge 7 commits into
mainfrom
codex/worker-vite-parity-31439707085

Conversation

@james-elicx

@james-elicx james-elicx commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Proves portable Web Worker parity for the failures seen in Actions run 31439707085 without misreporting the upstream Next.js suite as passing.

The implementation fix preserves Vite's emitted Worker URL base instead of rewriting it to a source-file URL, registers a fresh image-import plugin for Worker builds, and gives dynamic image imports the Next-compatible StaticImageData shape. The checked-in equivalent now exercises classic Workers, module Workers, literal-string Workers, NEXT_DEPLOYMENT_ID, WASM, SharedWorkers, and PNG imports in a real Vite/Rolldown + Wrangler/Workerd browser path.

The deploy harness also hoists am-i-vibing into throwaway apps. vinext is materialized through a file: package there, so its runtime import is otherwise invisible while running vinext init.

Honest result accounting

  • Original run 31439707085: raw 0/7, with 6 mixed functional failures in addition to the suite-wide deployment-token assertion. The literal-string Worker was already functionally correct.
  • Current exact Next.js v16.2.6 execution: raw 0/7, with 0 non-?dpl failure messages. Every residual failure is exclusively worker.test.ts:21:50.
  • Checked-in Vite-equivalent Playwright suite: 7/7 passed.

The exact rows therefore remain visible as 0/7 and are classified Vite-equivalent required. This PR does not claim that seven exact Next.js rows pass.

Provenance

The machine audit verifies all seven ordered test bodies and routes after removing exactly the seven uses of the single suite-wide beforePageLoad hook. It also verifies the pinned suite/config snapshots, all 17 byte-exact upstream fixture assets, and every checked-in local fixture file. The only omitted behavior is Turbopack's global ?dpl= assertion on every /_next/ request.

Validation

  • Independent review: NO FINDINGS
  • Provenance, compatibility, and deploy-harness units: 14/14 passed
  • Real Cloudflare Worker Playwright suite: 7/7 passed
  • Exact targeted Next.js suite: raw 0/7, every failure only worker.test.ts:21:50, portable passing assertions 2/2/2/3/2/2/8
  • Original focused unit set: 87/87 passed after supplying the isolated worktree's existing fixture dependencies
  • vp run vinext#build: passed
  • vp run @vinext/cloudflare#build: passed
  • Targeted vp check: passed

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2892
npm i https://pkg.pr.new/create-vinext-app@2892
npm i https://pkg.pr.new/@vinext/types@2892
npm i https://pkg.pr.new/vinext@2892

commit: 45e60d9

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: fix(build): preserve Web Worker asset semantics

I reviewed the diff, ran the relevant Vitest suites (import-meta-url, image-imports, static-image-emission worker case — all green), and type-checked the vinext package. The change is well-scoped and correct. No blocking issues.

What's right

  • isImportMetaUrlBaseNode (import-meta-url.ts) correctly recognizes Vite's coerced worker base "" + import.meta.url (empty-string Literal on the left, import.meta.url chain on the right) and skips rewriting it, while still descending into the first new URL() argument. This is the precise fix for the file:///_next/static/... worker URLs. The narrow structural match (only "" + <import.meta.url>, operator +, left literal "") avoids over-matching.
  • Worker asset emission (index.ts load) — the isWorker branch uses this.emitFile because the shared writeBundle that writes static images only runs for the client environment. Without this, worker-referenced images would never be written. Good catch.
  • Dynamic image imports — the new ImportExpression AST collection mirrors the existing static-import path exactly (same .startsWith(".") vs this.resolve logic, same ?vinext-image-url / ?vinext-meta composition, same IMAGE_EXT_RE $-anchored gate). Returning StaticImageData from import("./foo.png") matches Next.js semantics.
  • worker.plugins: () => [{ ...imageImportsPlugin }] — the factory is lazy, so imageImportsPlugin is assigned by the time it runs; the test asserts a fresh instance per call and that user worker config (external/output/plugins) is preserved rather than replayed.
  • Filter regex widening to include import(...) is safe: it only gates the handler, which is AST-based and won't rewrite non-ImportExpression matches (comments/strings).

Notes (non-blocking)

  1. Shared-state clearing across the worker build. The cloned worker plugin ({...imageImportsPlugin}) shares buildStart, which calls staticImageAssets.clear() and imageImportDimCache.clear(). If the worker build's buildStart interleaves with the client build, it could wipe entries the client writeBundle expects. This is currently safe because writeBundle falls back to createStaticImageAsset(imagePath) when the map misses, and staticImageImportsByModule is not cleared — but the safety relies on that fallback. Worth a one-line comment noting the intentional cache-sharing, so a future refactor of writeBundle doesn't remove the fallback and silently break worker/client builds.

  2. Dynamic-import behavior change beyond workers. The ImportExpression rewrite fires in all environments, so import("./foo.png") in a regular (non-worker) client/SSR module now resolves to StaticImageData instead of Vite's default URL-string module. This aligns with Next.js, so it's the correct direction — just flagging that it's a broader behavioral change than the PR title ("Web Worker") implies. Consider a dedicated non-worker dynamic-import test to lock in the new contract.

Verification performed

  • vitest run tests/import-meta-url.test.ts tests/image-imports.test.ts — 73 passed
  • vitest run tests/static-image-emission.test.ts -t "worker" — passed
  • tsc --noEmit -p packages/vinext/tsconfig.json — clean

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

I reviewed the 7-file diff for PR #2892, ran the relevant test suites, and type-checked the package. No blocking issues — the change is well-scoped and correct.

Verified:

  • isImportMetaUrlBaseNode correctly matches Vite's "" + import.meta.url worker base and skips rewriting it (the core file:///_next/static/... fix), with a narrow structural match that won't over-match.
  • The isWorker branch's this.emitFile is necessary because the shared static-image writeBundle only runs for the client environment.
  • Dynamic image imports (ImportExpression) are handled consistently with the existing static-import path, returning Next-compatible StaticImageData.
  • The lazy worker.plugins factory works with the deferred imageImportsPlugin assignment; tests confirm fresh-instance-per-call and no user-config replay.
  • The widened filter regex is safe (AST-gated handler).

Non-blocking notes I raised:

  1. The cloned worker plugin shares buildStart which clears staticImageAssets/imageImportDimCache; safety relies on the writeBundle createStaticImageAsset fallback — worth a comment so a future refactor doesn't break it.
  2. The dynamic-import rewrite fires in all environments (not just workers), so non-worker import("./foo.png") now returns StaticImageData — correct per Next.js, but a broader behavior change than the title implies; a dedicated non-worker test would lock it in.

Verification: 73 tests passed (import-meta-url + image-imports), worker static-image-emission passed, tsc --noEmit clean.

github run

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 45e60d9 against base d558d62 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 135.2 KB 135.4 KB ⚫ +0.2%
Client entry size (gzip) vinext 122.7 KB 122.8 KB ⚫ +0.1%
Dev server cold start vinext 2.15 s 2.14 s ⚫ -0.2%
Production build time vinext 2.16 s 2.17 s ⚫ +0.6%
RSC entry closure size (gzip) vinext 114.2 KB 114.4 KB ⚫ +0.1%
Server bundle size (gzip) vinext 193.0 KB 193.8 KB ⚫ +0.4%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: fix(build): preserve Web Worker asset semantics

I read the full 7-file diff, ran the four relevant Vitest suites (import-meta-url, image-imports, static-image-emission, compatibility-support85 passed), type-checked and lint-checked the two source files (vp check clean), and ran vp run vinext#build (success). The change is correct and well-scoped. No blocking issues.

What's correct

  • isImportMetaUrlBaseNode (import-meta-url.ts:578) — precisely matches Vite's coerced worker base "" + import.meta.url (BinaryExpression, operator +, empty-string Literal left, import.meta.url chain right) and skips rewriting it, so the browser resolves emitted workers against the deployment origin instead of file:///_next/static/.... The structural match is narrow enough not to over-match. The regression test at import-meta-url.test.ts:78 locks it in (toBeNull).
  • Worker asset emission (index.ts:6148) — the isWorker branch's this.emitFile is necessary because the shared writeBundle returns early for any environment other than client (index.ts:6359), so a worker build would otherwise never write its referenced image into the worker output. The URL string returned is identical to the non-worker branch, which is intentional (both point at the deployment-origin /_next/static/media/...).
  • Dynamic image imports (index.ts:6297) — the new ImportExpression collection mirrors the static-import path exactly: same .startsWith(".") vs this.resolve resolution, same $-anchored IMAGE_EXT_RE gate (so import("./w.png?worker") is correctly not matched), same ?vinext-image-url / ?vinext-meta composition. Returning StaticImageData from import("./x.png") matches Next.js.
  • worker.plugins: () => [{ ...imageImportsPlugin }] — the factory is lazy so the deferred imageImportsPlugin assignment is resolved by call time. I verified mergeConfig concatenates the user's worker-plugins-factory result with vinext's, so user worker config (external/output/plugins) is preserved, not replayed. The image-imports.test.ts:24 test confirms a fresh instance per call and no user-config replay.
  • Widened code filter regex (index.ts:6192) — I fuzzed it: matches import X from '…' and import(…) (incl. whitespace), and does not match import.meta.url. Safe because the handler is AST-gated regardless.

Non-blocking observations

  1. Shared cache clearing across worker/client builds. The cloned worker plugin ({ ...imageImportsPlugin }) shares the buildStart closure that calls staticImageAssets.clear() / imageImportDimCache.clear() (index.ts:6097). Safety currently relies on writeBundle's staticImageAssets.get(imagePath) ?? createStaticImageAsset(imagePath) fallback (index.ts:6370) if a worker buildStart interleaves and wipes the map. The inline comment at index.ts:2638 documents this contract well — good. Just flagging that the fallback in writeBundle is load-bearing for that guarantee, so a future refactor removing it would silently break worker/client parity.

  2. Dynamic-import rewrite fires in all environments. The ImportExpression rewrite runs in every environment, so import("./foo.png") in a non-worker client/SSR module now also returns StaticImageData rather than Vite's default URL-string module. This is the correct Next.js direction, but it's a broader behavior change than the "Web Worker" title implies. The image-imports.test.ts:234 unit test covers the non-worker transform/resolve/load path in isolation, which addresses the earlier suggestion for a dedicated non-worker test.

Verification performed

  • vp test run tests/import-meta-url.test.ts tests/image-imports.test.ts tests/static-image-emission.test.ts tests/compatibility-support.test.ts — 85 passed (incl. the worker StaticImageData emission and file:///_next/static/ negative assertion)
  • vp check packages/vinext/src/index.ts packages/vinext/src/plugins/import-meta-url.ts — format + lint + types clean
  • vp run vinext#build — success

@ask-bonk

ask-bonk Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

I reviewed the full 7-file diff, ran the four relevant Vitest suites (85 passed), lint/type-checked the two source files (clean), and built the package (success). No blocking issues — the change is correct and well-scoped.

Verified correct:

  • isImportMetaUrlBaseNode narrowly matches Vite's coerced worker base "" + import.meta.url and skips rewriting it — the precise fix for file:///_next/static/... worker URLs. Locked in by a toBeNull regression test.
  • The worker emitFile branch is necessary because the shared writeBundle returns early for non-client environments, so a worker build would otherwise never write its referenced image.
  • Dynamic image imports (ImportExpression) mirror the static-import path exactly, with a $-anchored extension gate that correctly ignores Vite's own query-suffixed worker imports.
  • The lazy worker.plugins factory preserves user worker config (verified mergeConfig concatenates rather than replaces).
  • The widened code filter regex was fuzzed — matches static and dynamic imports, not import.meta, and is AST-gated regardless.

Non-blocking observations raised:

  1. The cloned worker plugin shares the buildStart cache-clearing closure; safety relies on the load-bearing writeBundle createStaticImageAsset fallback (documented via inline comment — good, just flagged for future refactors).
  2. The dynamic-import rewrite fires in all environments, so import("./foo.png") in non-worker modules now returns StaticImageData too — correct per Next.js, but broader than the "Web Worker" title implies; the non-worker unit test covers it.

github run

@james-elicx
james-elicx marked this pull request as ready for review August 11, 2026 04:41
@james-elicx

Copy link
Copy Markdown
Member Author

Independent exact-head review of b330e07aca8761c7bff437923499dceea83b2311: NO FINDINGS.

I reviewed the full seven-file diff independently, including:

  • Vite/Rolldown worker-constructor transform ordering and preservation of the generated "" + import.meta.url base;
  • classic/module worker chunks and dynamic imports, literal-string Workers, SharedWorkers, WASM URLs, and NEXT_DEPLOYMENT_ID substitution;
  • worker-scoped image-plugin instantiation, user worker config merging, PNG emission, and dynamic StaticImageData module shape;
  • shared image-cache/build lifecycle and client/worker emission ownership;
  • non-worker dynamic-image-import behavior and regression coverage;
  • the residual suite-wide ?dpl policy versus vinext's deliberate query-free JavaScript-host policy, which avoids duplicate native-ESM module graphs.

Fresh validation on this immutable head:

  • focused Vitest: 85/85 passed across compatibility policy, import-meta URL rewriting, image imports, and static image emission (including an actual Vite worker build);
  • vp run vinext#build: passed;
  • targeted Next.js Worker result: all seven test entries executed, and every recorded failure message points exclusively to the suite-wide assertion at worker.test.ts:21:50 (?dpl); there are zero non-?dpl failure messages;
  • CI is fully green, local/remote head SHAs match, merge state is clean, git diff --check passed, and the worktree remained clean.

I found no actionable asset-semantics gap, transform-ordering regression, deployment-token policy error, or missing high-value test at this head.

@james-elicx james-elicx changed the title fix(build): preserve Web Worker asset semantics test(worker): prove Vite-equivalent Web Worker parity Aug 12, 2026
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Summary

I reviewed the full diff for PR #2892, verified the key behaviors against Vite's type definitions and the codebase, and ran the relevant test suites. No blocking issues — the change is correct and well-scoped.

Verified correct

import-meta-url.ts — the core fix (isImportMetaUrlBaseNode)
The added BinaryExpression match is narrow and correct: it only accepts "" + import.meta.url (operator +, empty-string Literal on the left, import.meta.url/optional-chained on the right) — exactly Vite's coerced worker base. Preserving it stops the browser from resolving emitted workers against file:///_next/static/... instead of the deployment origin. The match won't over-match arbitrary string concatenations.

index.ts — worker asset emission (this.environment.config.isWorker)
Confirmed isWorker is a real property on the resolved config (vite-plus-core d.ts line 3740; this.environment.config is typed ResolvedConfig & ResolvedEnvironmentOptions). The branch is necessary because writeBundle early-returns for any non-client environment (index.ts:6359), so a worker build would otherwise never write its referenced image. load is cached per module id, so a repeated image import emits once — no duplicate-emit collision.

index.ts — dynamic image imports (ImportExpression)
Mirrors the static path exactly: same .startsWith(".") vs this.resolve resolution, same $-anchored IMAGE_EXT_RE (so import("./x.png?worker") is correctly not matched), same imageImports.add() registration for writeBundle. The \0vinext-dynamic-image: module re-imports ?vinext-image-url + ?vinext-meta, so worker emission flows through consistently. Returned { src, width, height } matches the static path's StaticImageData shape.

worker.plugins: () => [{ ...imageImportsPlugin }]
The factory is lazy, so the deferred imageImportsPlugin assignment (index.ts:6088) is resolved by call time. Same-name plugins across separate environments are fine. Tests confirm a fresh instance per call and no user-worker-config replay.

am-i-vibing deploy-harness addition
Confirmed it's a genuine runtime import in init-platform.ts:3 (isAgent), invisible through the file: package symlink — so hoisting it into throwaway apps is the correct, consistent fix.

Non-blocking observations (already noted by prior reviews, no action required)

  1. The cloned worker plugin shares the buildStart cache-clearing closure; safety relies on writeBundle's staticImageAssets.get(...) ?? createStaticImageAsset(...) fallback. Already documented via inline comment at index.ts:2638 — good.
  2. The dynamic-import rewrite fires in all environments, so import("./foo.png") in non-worker modules now returns StaticImageData. This is correct per Next.js but broader than the "Web Worker" title implies; the non-worker unit test in image-imports.test.ts covers it.

Verification performed

  • vp test run tests/import-meta-url.test.ts tests/image-imports.test.ts tests/static-image-emission.test.ts80 passed (incl. worker StaticImageData emission)
  • vp test run tests/worker-e2e-provenance.test.ts tests/compatibility-support.test.ts tests/e2e-deploy-script.test.ts14 passed
  • vp check packages/vinext/src/index.ts packages/vinext/src/plugins/import-meta-url.ts — format + lint + types clean

No findings. The PR honestly reports the exact upstream suite as 0/7 (residual ?dpl assertion only) rather than misclaiming a pass, and provides a machine-audited Vite-equivalent that is genuinely 7/7.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — test(worker): prove Vite-equivalent Web Worker parity

Reviewed the full diff at d1ae2e6, ran tests/import-meta-url.test.ts tests/image-imports.test.ts tests/static-image-emission.test.ts (80 passed, incl. the worker StaticImageData emission and the file:///_next/static/ negative assertion), and traced the deploy-harness runtime path.

The two source fixes are correct and I agree with the prior reviews on them:

  • isImportMetaUrlBaseNode (import-meta-url.ts:578) narrowly matches Vite's coerced worker base "" + import.meta.url and preserves it, so the browser resolves emitted workers against the deployment origin instead of file:///_next/static/.... The structural match won't over-match.
  • Worker asset emission (index.ts:6148) is necessary because writeBundle early-returns for non-client environments; the load cache makes it emit once per image id.
  • Dynamic image imports (index.ts:6297) mirror the static path exactly, with a $-anchored IMAGE_EXT_RE that correctly ignores import("./x.png?worker"). The import() namespace-object shape (pngModule.default) is what the fixture consumes — consistent with export default { src, width, height }.

One issue: the am-i-vibing deploy-harness addition looks unnecessary

scripts/e2e-deploy.sh:581 adds am-i-vibing to every throwaway app's devDependencies, and tests/e2e-deploy-script.test.ts was updated to assert ^0.5.0. The PR description justifies this by saying vinext's runtime am-i-vibing import is "otherwise invisible while running vinext init" through the file: package.

That rationale does not hold for the harness's actual runtime path:

  • am-i-vibing is listed under devDependencies (not dependencies) in packages/vinext/package.json:192, and it is alwaysBundled into vinext's dist (packages/vinext/vite.config.ts:77). This alwaysBundle predates this PR (last touched in #2837 on 2026-08-07).
  • In the built output, dist/init-platform.js:1 imports isAgent via a relative path (./deps/.pnpm/am-i-vibing@0.5.0/.../detector-*.js), and dist/cli.js:19 imports ./init-platform.js. There are zero bare from "am-i-vibing" specifiers anywhere in dist (verified by grep).
  • The harness runs the built CLI (${VINEXT_PKG_DIR}/dist/cli.js, e2e-deploy.sh:274) and copies the full dist tree — including dist/deps/ — into .vinext-local-package/dist (e2e-deploy.sh:481, with files: ['dist'] at :497). The generated local package.json only carries dependencies/peerDependencies (:499:500), so am-i-vibing (a devDep) would never be declared there anyway.

So the isAgent import is satisfied by the inlined copy inside dist/deps/, resolved by a relative path that does not touch the app's top-level node_modules. Adding am-i-vibing to the throwaway app is a no-op for resolution.

The comment above the loop (e2e-deploy.sh:568573) explicitly scopes it to runtime deps of vinext that pnpm doesn't hoist because they live under .vinext-local-package/node_modules and "Node's ESM resolver can't see" them from dist/.... am-i-vibing is not in that category — it's inlined, not resolved from node_modules — so it doesn't belong in this list next to genuinely-external peers like vite / react-server-dom-webpack / ipaddr.js.

This is non-blocking (the extra devDep is harmless), but it adds a misleading test assertion and a maintenance foot-gun: a future reader will assume am-i-vibing must be externally resolvable at runtime, which is not true. I'd either:

  1. Drop the am-i-vibing entry (and its test assertion), or
  2. If there is a real failing path I'm missing (e.g. a source/non-dist harness mode), keep it but correct the PR description and add a comment explaining exactly which resolution fails, since the current dist-based path does not.

If you can point me at the exact vinext init invocation that fails without it, I'll happily retract this — but from the dist I built here, the import is fully self-contained.

Non-source observations

  • The Vite-equivalent Playwright spec is registered via the cloudflare-workers project glob (playwright.config.ts:164) and is in the CI matrix (ci.yml:520), so it will actually run. Good.
  • The honest 0/7 accounting for the exact upstream rows (residual worker.test.ts:21:50 ?dpl assertion only) plus the machine-audited 7/7 equivalent is the right call and matches the VITE_EQUIVALENT_LABEL reclassification.

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

I reviewed the full diff at d1ae2e6, ran the three relevant Vitest suites (80 passed, including worker StaticImageData emission), and traced the deploy-harness runtime resolution path.

The two source fixes are correct and I agree with the prior reviews:

  • isImportMetaUrlBaseNode narrowly preserves Vite's "" + import.meta.url worker base.
  • The isWorker emitFile branch is necessary (writeBundle early-returns for non-client envs).
  • Dynamic image imports mirror the static path with a $-anchored extension gate; the import() namespace-object shape matches the fixture's pngModule.default.

One issue I flagged (non-blocking, and one the prior "NO FINDINGS" reviews all missed):

The am-i-vibing deploy-harness addition (e2e-deploy.sh:581 + the new test assertion) appears unnecessary and its stated rationale is wrong:

  • am-i-vibing is a devDependency (not a dependency) and is alwaysBundled into vinext's dist — this predates the PR.
  • In the built output, init-platform.js imports it via a relative path into dist/deps/; there are zero bare from "am-i-vibing" specifiers anywhere in dist.
  • The harness runs dist/cli.js and copies the whole dist tree (including dist/deps/), so the isAgent import is self-contained and never touches the app's top-level node_modules. The generated local package.json only carries dependencies/peerDependencies anyway.

So adding it to throwaway apps is a no-op for resolution and adds a misleading test assertion + maintenance foot-gun. I recommended either dropping it (and its assertion) or, if there's a real failing path I'm missing, keeping it with a corrected explanation.

I also confirmed the new Playwright spec is registered via the cloudflare-workers project glob and is in the CI matrix, so it will actually run.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — test(worker): prove Vite-equivalent Web Worker parity

Reviewed the full diff at e86b0c0, verified the two source fixes against the codebase, built dist and traced the deploy-harness runtime resolution path, and ran the relevant Vitest suites (94 passed total). I agree with the prior reviews that the two source fixes are correct.

Source fixes — correct

  • isImportMetaUrlBaseNode (import-meta-url.ts:578) narrowly matches Vite's coerced worker base "" + import.meta.url (operator +, empty-string Literal left, import.meta.url chain right) and preserves it, so the browser resolves emitted workers against the deployment origin instead of file:///_next/static/.... The match won't over-match arbitrary concatenations. Locked in by import-meta-url.test.ts (toBeNull).
  • Worker asset emission (index.ts:6148) is necessary because writeBundle early-returns for non-client environments; the per-module load cache emits once per image id. The dynamic-image path (\0vinext-dynamic-image: -> ?vinext-image-url) flows through this same branch, so worker emission is consistent for both static and dynamic imports.
  • Dynamic image imports (index.ts:6297) mirror the static path exactly, with a $-anchored IMAGE_EXT_RE that correctly ignores import("./x.png?worker"). The returned { src, width, height } matches the static path's StaticImageData shape.
  • worker.plugins: () => [{ ...imageImportsPlugin }] — the cloned plugin shares the buildStart closure that clears staticImageAssets, but writeBundle's staticImageAssets.get(imagePath) ?? createStaticImageAsset(imagePath) fallback (index.ts:6370) makes that safe. The inline comment documents the contract well.

One issue: the am-i-vibing deploy-harness addition is unnecessary and its rationale is wrong

scripts/e2e-deploy.sh:581 adds am-i-vibing to every throwaway app's devDependencies, and tests/e2e-deploy-script.test.ts was updated to assert ^0.5.0. The PR description justifies this by claiming vinext's runtime am-i-vibing import is "otherwise invisible while running vinext init" through the file: package.

That rationale does not hold. I built dist and verified:

  • am-i-vibing is a devDependency (packages/vinext/package.json:192) and is alwaysBundled into vinext's dist (packages/vinext/vite.config.ts:77) — this predates the PR.
  • In the built output, dist/init-platform.js:1 imports isAgent via a relative path (./deps/.pnpm/am-i-vibing@0.5.0/.../detector-*.js). There are zero bare from "am-i-vibing" specifiers anywhere in dist (verified by grep).
  • The harness runs the built CLI and fs.cpSyncs the full dist tree — including dist/deps/ — into .vinext-local-package/dist (e2e-deploy.sh:481, files: ['dist'] at :497). The generated local package.json only carries dependencies/peerDependencies (:499:500), never devDependencies.

So the isAgent import is satisfied by the inlined copy inside dist/deps/, resolved by a relative path that never touches the app's top-level node_modules. Adding am-i-vibing to the throwaway app is a no-op for resolution.

The comment above the loop (e2e-deploy.sh:568573) explicitly scopes it to runtime deps of vinext that pnpm doesn't hoist because "Node's ESM resolver can't see" them from dist/.... I confirmed the contrast: ipaddr.js (in the same list) IS referenced as a bare specifier in dist (dist/shims/image-config.js) and is NOT in alwaysBundle, so it genuinely needs to be resolvable in the app's node_modules. am-i-vibing is the opposite — inlined, not resolved from node_modules — so it doesn't belong in this list.

Non-blocking (the extra devDep is harmless), but it adds a misleading test assertion and a maintenance foot-gun: a future reader will assume am-i-vibing must be externally resolvable at runtime, which is false. I'd either:

  1. Drop the am-i-vibing entry (and its test assertion), or
  2. If there's a real failing vinext init path I'm missing, keep it but correct the PR description and add a comment explaining exactly which resolution fails — the current dist-based path is fully self-contained.

This has now been flagged in multiple reviews without being addressed. If you can point me at the exact invocation that fails without it, I'll retract; otherwise it should be dropped.

Non-source observations (no action)

  • The honest 0/7 accounting for the exact upstream rows (residual worker.test.ts:21:50 ?dpl assertion only) plus the machine-audited 7/7 Vite-equivalent is the right call. The provenance manifest correctly separates byteExactFixturePaths (upstream, hash-pinned) from the local equivalent files (package.json, vite.config.mjs, wrangler.jsonc) — no dishonest "byte-for-byte" overclaim.
  • The Vite-equivalent Playwright spec is registered via the cloudflare-workers project glob (playwright.config.ts:164) and is in the CI matrix (ci.yml:520), so it will actually run.
  • The knip.ts / vite.config.ts exclusions for the pinned fixture are appropriate.

Verification performed

  • vitest run tests/import-meta-url.test.ts tests/image-imports.test.ts tests/static-image-emission.test.ts80 passed (incl. worker StaticImageData emission + file:///_next/static/ negative assertion)
  • vitest run tests/e2e-deploy-script.test.ts tests/worker-e2e-provenance.test.ts tests/compatibility-support.test.ts14 passed
  • Built dist inspection confirming the am-i-vibing relative-import / zero-bare-specifier finding

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

I reviewed the full diff at e86b0c0, verified both source fixes against the codebase, built dist and traced the deploy-harness runtime resolution path, and ran the relevant Vitest suites (94 passed).

Source fixes — correct (I agree with prior reviews):

  • isImportMetaUrlBaseNode narrowly preserves Vite's "" + import.meta.url worker base, fixing the file:///_next/static/... resolution.
  • The isWorker emitFile branch is necessary (writeBundle early-returns for non-client envs); the dynamic-image path flows through it consistently.
  • Dynamic image imports mirror the static path with a $-anchored extension gate.
  • The cloned worker.plugins shared buildStart is safe due to writeBundle's createStaticImageAsset fallback.

One issue confirmed (non-blocking, previously flagged, still unaddressed): The am-i-vibing deploy-harness addition is unnecessary and its rationale is wrong. I validated this by building dist:

  • It's a devDependency, alwaysBundled into dist.
  • dist/init-platform.js imports it via a relative path (./deps/.pnpm/am-i-vibing@0.5.0/...); zero bare from "am-i-vibing" specifiers exist in dist.
  • The harness copies the full dist tree recursively and the generated package.json never carries devDependencies, so the import is self-contained.
  • Contrast: ipaddr.js (same list) IS a bare specifier in dist and is NOT bundled — so it genuinely belongs; am-i-vibing does not.

Recommended dropping the entry + its test assertion, or correcting the rationale if there's a real failing path.

The honest 0/7 upstream accounting + machine-audited 7/7 Vite-equivalent is the right call, and the provenance manifest correctly separates hash-pinned upstream fixtures from local equivalents.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — test(worker): prove Vite-equivalent Web Worker parity

Reviewed the full diff at 45e60d9, ran the relevant Vitest suites (94 passed), lint/type-checked the two source files (clean), and traced the fixture/provenance wiring. No blocking issues — and the one recurring non-blocking finding from prior rounds has now been fixed.

am-i-vibing finding is resolved

The last commit (45e60d9, "drop redundant harness dependency") removes am-i-vibing from the scripts/e2e-deploy.sh throwaway-app dependency loop and deletes the corresponding ^0.5.0 assertion in tests/e2e-deploy-script.test.ts. This is the correct resolution of the finding flagged across the previous reviews (the dep is alwaysBundled into dist and imported via a relative ./deps/ path, so it was never externally resolvable and the entry was a no-op + misleading assertion). Nothing else references it. Good.

Source fixes — correct

  • isImportMetaUrlBaseNode (import-meta-url.ts:578) narrowly matches Vite's coerced worker base "" + import.meta.url (BinaryExpression, operator +, empty-string Literal left, import.meta.url/optional-chained right) and preserves it, so the browser resolves emitted workers against the deployment origin instead of file:///_next/static/.... The match won't over-match arbitrary concatenations.
  • Worker asset emission (index.ts:6148) — the isWorker emitFile branch is necessary because writeBundle early-returns for any non-client environment (index.ts:6359), so a worker build would otherwise never write its referenced image. load is cached per module id, so the emit happens once per image.
  • Dynamic image imports (index.ts:6294) mirror the static path: same .startsWith(".") vs this.resolve resolution, same $-anchored IMAGE_EXT_RE gate (so import("./x.png?worker") is correctly not matched), same imageImports.add() registration for writeBundle. The \0vinext-dynamic-image: module re-emits ?vinext-image-url + ?vinext-meta and returns { src, width, height } — the exact shape png-worker.ts consumes via pngModule.default ({ src, width, height }). Consistent with the static path and with the fixture.
  • worker.plugins: () => [{ ...imageImportsPlugin }] — the lazy factory resolves the deferred imageImportsPlugin assignment by call time; the cloned plugin shares the buildStart cache-clearing closure, made safe by writeBundle's staticImageAssets.get(...) ?? createStaticImageAsset(...) fallback (documented inline at index.ts:2638).
  • Widened code filter regex (index.ts:6192) now also matches import(...); safe because the handler is AST-gated.

Honesty / provenance — sound

  • tests/worker-e2e-provenance.test.ts machine-audits the equivalence: it hashes the pinned upstream snapshot, structurally compares all seven ordered it bodies (routes + normalized AST) after removing exactly the seven beforePageLoad (?dpl) hook uses, asserts omissions === 7 and removed names only that hook, and hashes all 17 byte-exact upstream fixture assets plus every checked-in fixture file. This is a genuine audit, not a rubber stamp.
  • The compat policy reclassification (suite-support.tsVITE_EQUIVALENT_LABEL "Vite-equivalent required") honestly leaves the exact upstream rows at 0/7 and does not claim a Next.js pass. The byteExactFixturePaths manifest correctly separates hash-pinned upstream fixtures (app/, public/) from local equivalents (package.json, vite.config.mjs, wrangler.jsonc).
  • worker.spec.ts is registered via the cloudflare-workers project glob (playwright.config.ts:164) and is in the CI matrix (ci.yml:520), so it actually runs. The knip.ts / vite.config.ts fixture exclusions are appropriate.

Minor observations (non-blocking, no action required)

  1. The dynamic-import rewrite fires in all environments, so import("./foo.png") in non-worker modules also returns StaticImageData now. This is correct per Next.js but broader than the "Web Worker" title implies; image-imports.test.ts covers the non-worker path.
  2. worker.spec.ts's beforeAll symlinks examples/app-router-cloudflare/node_modules for the fixture (which has an empty package.json) and spawns its own build+wrangler on port 4201, in addition to the project's own server on 4176. It's a heavier-than-usual setup but self-contained and correct; just noting the two-build cost.

Verification performed

  • vp test run tests/import-meta-url.test.ts tests/image-imports.test.ts tests/static-image-emission.test.ts80 passed (incl. worker StaticImageData emission + file:///_next/static/ negative assertion)
  • vp test run tests/e2e-deploy-script.test.ts tests/worker-e2e-provenance.test.ts tests/compatibility-support.test.ts14 passed
  • vp check packages/vinext/src/index.ts packages/vinext/src/plugins/import-meta-url.ts — format + lint + types clean
  • Head SHA 45e60d9 matches local checkout

No findings.

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

I reviewed the full diff at head 45e60d9, ran the relevant Vitest suites (94 passed), lint/type-checked the source files (clean), and traced the fixture/provenance wiring. No blocking issues.

The one recurring finding is now fixed. The latest commit 45e60d9 drops the am-i-vibing entry from the deploy-harness dependency loop and deletes its ^0.5.0 test assertion — exactly the resolution the prior 3 reviews recommended (the dep is alwaysBundled into dist and imported via a relative ./deps/ path, so the entry was a no-op and the assertion was misleading).

Verified correct:

  • isImportMetaUrlBaseNode narrowly preserves Vite's "" + import.meta.url worker base (fixes file:///_next/static/... resolution).
  • The isWorker emitFile branch is necessary (writeBundle early-returns for non-client envs); emits once per image via the load cache.
  • Dynamic image imports mirror the static path with a $-anchored extension gate; the returned { src, width, height } matches what the png-worker.ts fixture consumes via pngModule.default.
  • The cloned worker.plugins shared buildStart cache clear is safe via the documented writeBundle fallback.

Provenance/honesty is sound: the machine audit structurally compares all 7 test bodies + routes after removing exactly the 7 ?dpl hook uses, hashes all fixtures, and honestly keeps the exact upstream rows at 0/7 (classified "Vite-equivalent required") rather than claiming a Next.js pass.

Two minor non-blocking observations noted (dynamic-import rewrite fires in all environments; the spec's symlink+two-build setup is heavy but self-contained). No findings requiring changes.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

1 similar comment
@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review — test(worker): prove Vite-equivalent Web Worker parity

Reviewed the full diff at head 45e60d9. I re-verified both source fixes against the codebase, exercised the dynamic-image traversal path, and ran the relevant Vitest suites (all green). No blocking issues. The two source fixes are correct, and the honesty/provenance framing is sound. I have one small accuracy nit that prior rounds missed.

Source fixes — correct

  • isImportMetaUrlBaseNode (import-meta-url.ts:578) narrowly matches Vite's coerced worker base "" + import.meta.url (BinaryExpression, operator +, empty-string Literal left, import.meta.url/optional-chained right) and preserves it, so emitted workers resolve against the deployment origin instead of file:///_next/static/.... The structural match won't over-match arbitrary concatenations; locked in by import-meta-url.test.ts (toBeNull).
  • Worker asset emission (index.ts:6148) — the isWorker emitFile branch is necessary because writeBundle early-returns for any non-client environment (index.ts:6359). load is cached per module id, so the emit happens once per image. Both branches call renderVinextBuiltUrl(...) identically, so the deploymentId/assetPrefix URL shape stays consistent across worker and non-worker builds.
  • Dynamic image imports (index.ts:6294)collectDynamicImageImports does a full recursive AST walk via forEachAstChild (metadata-only SKIP_CHILD_KEYS), so it finds import() nested inside functions (as in png-worker.ts). The $-anchored IMAGE_EXT_RE correctly ignores import("./x.png?worker"), and only Literal string sources are rewritten (template-literal import() is left alone, matching what any static analyzer can do). The \0vinext-dynamic-image: module re-imports ?vinext-image-url + ?vinext-meta and returns { src, width, height } — the exact shape png-worker.ts consumes via pngModule.default.
  • worker.plugins: () => [{ ...imageImportsPlugin }] — the lazy factory resolves the deferred assignment by call time; the cloned plugin shares the buildStart cache-clearing closure, made safe by writeBundle's staticImageAssets.get(...) ?? createStaticImageAsset(...) fallback. Documented inline. Good.

Honesty / provenance — sound

worker-e2e-provenance.test.ts is a genuine machine audit, not a rubber stamp: it hashes the pinned upstream snapshot, structurally compares all seven ordered it bodies (routes + metadata-stripped AST) after removing exactly the seven beforePageLoad (?dpl) hook uses, asserts omissions === 7, and hashes every fixture file. The compat reclassification (suite-support.tsVITE_EQUIVALENT_LABEL) honestly keeps the exact upstream rows at 0/7 rather than claiming a Next.js pass. Correct call.

Nit (non-blocking, not raised in prior rounds)

The knip.ts and vite.config.ts exclusion comments claim the entire worker-fixture/** glob is "Pinned byte-for-byte from Next.js v16.2.6":

// Pinned byte-for-byte from Next.js v16.2.6; provenance tests verify hashes.
"tests/e2e/cloudflare-workers/worker-fixture/**",

That is only true for app/ and public/. The provenance test itself proves the distinction (worker-e2e-provenance.test.ts:244-246 asserts byteExactFixturePaths == only files under app//public/), and the manifest lists package.json, vite.config.mjs, and wrangler.jsonc as equivalentLocalFiles — these are vinext-authored (e.g. vite.config.mjs imports vinext + @cloudflare/vite-plugin), not upstream bytes. The glob exclusion behavior is correct regardless of origin; only the justifying comment overclaims. Worth softening to e.g. "Pinned upstream fixture (app//public/ byte-exact) plus vinext-local build config" so a future reader doesn't assume those three files are immutable upstream copies.

Verification performed

  • vitest run tests/import-meta-url.test.ts tests/image-imports.test.ts74 passed (incl. fresh-per-call worker plugin + dynamic StaticImageData)
  • vitest run tests/static-image-emission.test.ts -t "worker"passed (real Vite worker build emits the image; asserts no file:///_next/static/)
  • vitest run tests/worker-e2e-provenance.test.ts tests/compatibility-support.test.ts7 passed
  • vp check packages/vinext/src/index.ts packages/vinext/src/plugins/import-meta-url.ts — format + lint + types clean

No findings requiring changes.

@ask-bonk

ask-bonk Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2892.

Summary

Reviewed the full diff at head 45e60d9, re-verified both source fixes against the codebase, exercised the dynamic-image AST traversal path, and ran the relevant Vitest suites (all green). No blocking issues.

Source fixes — correct:

  • isImportMetaUrlBaseNode narrowly preserves Vite's coerced "" + import.meta.url worker base, fixing file:///_next/static/... resolution.
  • The isWorker emitFile branch is necessary (writeBundle early-returns for non-client envs); both branches call renderVinextBuiltUrl identically so the URL shape stays consistent.
  • Dynamic image imports use a full recursive AST walk (finds nested import()), a $-anchored extension gate, and produce the exact { src, width, height } shape the fixture consumes.
  • The cloned worker.plugins shared buildStart is safe via the documented writeBundle fallback.

Provenance/honesty — sound: the machine audit genuinely compares all 7 test bodies after removing exactly the 7 ?dpl hook uses, and the compat reclassification honestly keeps upstream rows at 0/7.

One accuracy nit (non-blocking, missed by prior rounds): The knip.ts / vite.config.ts exclusion comments claim the entire worker-fixture/** glob is "Pinned byte-for-byte from Next.js v16.2.6", but only app//public/ are byte-exact. The manifest and provenance test itself (worker-e2e-provenance.test.ts:244-246) prove that package.json, vite.config.mjs, and wrangler.jsonc are vinext-authored local equivalents. The glob exclusion is correct; only the comment overclaims. Recommended softening the comment wording.

The recurring am-i-vibing finding from prior rounds is already resolved in 45e60d9.

github run

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.

1 participant