Skip to content

dynamic import() no longer seeds AsyncLocalStorage on globalThis - #13890

Merged
proggeramlug merged 2 commits into
mainfrom
als-seed-dynamic-import
Nov 3, 2026
Merged

proggeramlug merged 2 commits into
mainfrom
als-seed-dynamic-import

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

fix(compile): dynamic import() no longer seeds globalThis.AsyncLocalStorage

Cause

crates/perry-codegen/src/codegen/entry.rs seeds globalThis.AsyncLocalStorage before module init when the
entry's Next.js path-init list (nextjs_path_init_modules) is non-empty. That rule treats the list as
"this is a Next.js program". Since #7312, run_pipeline.rs also put every module with
ModuleInitKind::Deferred (every module reached only through import()) on that list. So any program
with a dynamic import got an AsyncLocalStorage global that node does not have:

const m = await import("./x.js");
console.log(typeof globalThis.AsyncLocalStorage); // node: undefined, perry main: function

Fix

The list holds modules by where they are (.next/server/**, compilePackages directories), as the seed rule
assumes. A deferred module still gets its path anchor, now from the lazy registrar
(js_register_path_inits_lazily), just like an on-demand CommonJS module. An import() site calls its
target's __init directly. Only a program that is not closed can look a module up by path, and the lazy
registrar registers the anchors on the first lookup. No new flag and no new list.

Receipts (qb9, head vs main e8eb6bf)

  • Fixture: main prints function, head prints undefined, matching node.
  • New gap test test_gap_als_seed_dynamic_import.ts: fails on main, passes on head.
  • Gap subset: 27 tests (every test_gap file that uses import(, the dynamic_import tests and the new
    test). Head matches node on 27/27, main on 26/27. The only difference is the new test.
  • A .next/server/x.js module, imported statically or dynamically, still gets the seed on head (function, same as main).
  • cargo test -p perry-codegen --lib codegen::entry: 33 passed. These include the Next dylib seed and path-registration tests.
  • cargo test -p perry --bin perry commands::compile: 1051 passed, 1 failed. The failing test,
    fs_promises_named_glob_in_class_enables_regex_engine, fails the same way on main source.
  • Startup gate scripts/a1/irdiff.sh, six witnesses (hello, commander_help, commander, fastify_inject,
    awfy_richards, binary_trees): delta 0 Ir and 0 functions with +Ir on every witness. .text and file
    bytes are identical, and linkpad converged.
  • tests/test_next_app_route_dylib.sh does not reach the route on qb9 for head or main: both stop at
    node:http has no provider under the script's PERRY_DISABLE_WELL_KNOWN=1. This is not caused by this change.

…torage

The entry seeds globalThis.AsyncLocalStorage only when the Next.js path-init
list is non-empty, and that list also took every module whose init kind is
Deferred, i.e. every module reached only through import(). So any program
with a dynamic import got an AsyncLocalStorage global that node does not
have.

The list now holds modules by where they are (.next/server/** and
compilePackages directories), as its seed rule assumes. A deferred module
keeps its path anchor through the lazy registrar, exactly like an on-demand
CommonJS module: an import() site calls its target's init directly, and only
a program that can require by path (not closed) looks the address up, at its
first path lookup.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Ready on local verification: CI cannot run right now (setup-llvm22's wget of the apt.llvm.org key exits 4 on GitHub runners since ~14:10Z, every branch). Local on qb9: new gap test fails on main and passes here; all 27 gap tests using import() match node; codegen entry tests; startup irdiff six witnesses exact 0; nextjs-hello compiled with this head matches main and node on all 5 routes. Lint gates: same 17 failures as main, nothing new.

@proggeramlug
proggeramlug marked this pull request as ready for review November 3, 2026 16:08
@proggeramlug
proggeramlug merged commit e481252 into main Nov 3, 2026
205 of 294 checks passed
@proggeramlug
proggeramlug deleted the als-seed-dynamic-import branch November 3, 2026 16:09
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