mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #306] [MERGED] fix: stub node:async_hooks in client builds via virtual module #461
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#461
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/306
Author: @Divkix
Created: 3/6/2026
Status: ✅ Merged
Merged: 3/7/2026
Merged by: @james-elicx
Base:
main← Head:fix/async-hooks-client-stub📝 Commits (10+)
4044da5fix: stub node:async_hooks in client builds via virtual module2ec2f83fix: address async-hooks-stub code review feedback538973arefactor: extract async-hooks-stub plugin to plugins/ and test against real implementation09c03b0fix: add filter to load hook, evaluate stub at runtime in test0fe4052fix: remove control-char regex from load filter to fix lint4e86e0bfix: suppress no-control-regex for load filter via eslint-disable comment3916820refactor: derive load filter regex from ASYNC_HOOKS_STUB_ID constantf087b20Update build-optimization.test.ts6e3c567Update index.tsc9c8a3dfix: widen run() type signature to accept rest args and typed callback📊 Changes
3 files changed (+127 additions, -0 deletions)
View changed files
📝
packages/vinext/src/index.ts(+4 -0)➕
packages/vinext/src/plugins/async-hooks-stub.ts(+49 -0)📝
tests/build-optimization.test.ts(+74 -0)📄 Description
Summary
vinext:async-hooks-stubplugin that interceptsnode:async_hooksonly in theclientenvironment and provides a no-opAsyncLocalStoragenode:async_hooksWhy a stub is the correct fix
Several shims (
headers,cache,navigation-state,head-state,router-state,cache-runtime,fetch-cache) importAsyncLocalStoragefromnode:async_hooks. These shims are aliased globally viaresolve.aliasbecause they must resolve in every environment. In client builds, Vite externalizesnode:async_hooksto__vite-browser-external— an empty stub with no named exports — causing Rollup"AsyncLocalStorage" is not exportederrors.The shims already guard against missing ALS state with
_als.getStore() ?? fallbackpatterns, so a no-op stub (getStore()returnsundefined,run()passes through) is semantically correct for client builds.Addresses @james-elicx's feedback: the leakage is inherent — shims like
next/headersare server-only but resolve globally viaresolve.alias. Splitting aliases per-environment would require knowing which shims are client-safe and would break transitive imports. The virtual module approach is minimal (no extra files) and surgical (only affectsnode:async_hooksin client).Closes #293
Test plan
tsc --noEmitpasses (exit 0)bun run buildsucceedsbiome check— no new errors from this changeresolveIdinterception whenenvironment.name !== "client"run()passes through callback return value, matching real ALS behavior🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.