From e0d519619c64b1cdcab4091174cda009f96d9533 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Sat, 17 Oct 2026 18:08:11 -0400 Subject: [PATCH] module: await the cycle roots capability promise, if needed Signed-off-by: avivkeller --- lib/internal/modules/esm/module_job.js | 6 +++++- .../test-esm-tla-dynamic-import-evaluating-async.mjs | 11 +++++++++++ .../tla/dynamic-import-evaluating-async/a.mjs | 8 ++++++++ .../tla/dynamic-import-evaluating-async/b.mjs | 2 ++ .../tla/dynamic-import-evaluating-async/c.mjs | 2 ++ .../tla/dynamic-import-evaluating-async/d.mjs | 1 + .../tla/dynamic-import-evaluating-async/main.mjs | 3 +++ 7 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 test/es-module/test-esm-tla-dynamic-import-evaluating-async.mjs create mode 100644 test/fixtures/es-modules/tla/dynamic-import-evaluating-async/a.mjs create mode 100644 test/fixtures/es-modules/tla/dynamic-import-evaluating-async/b.mjs create mode 100644 test/fixtures/es-modules/tla/dynamic-import-evaluating-async/c.mjs create mode 100644 test/fixtures/es-modules/tla/dynamic-import-evaluating-async/d.mjs create mode 100644 test/fixtures/es-modules/tla/dynamic-import-evaluating-async/main.mjs diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index c8506b42ff3f..a35ed73bb1b5 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -649,10 +649,14 @@ class ModuleJobSync extends ModuleJobBase { throw this.module.getError(); } if (status > kInstantiated) { - // Already evaluated (e.g. previously require()'d and now import()'d again). if (this.evaluationPromise) { + // Evaluation was started by an earlier run() of this job and is still in flight. await this.evaluationPromise; + } else if (status === kEvaluated && this.module.hasAsyncGraph) { + // await the cycle root's capability promise. + await this.module.evaluate(-1, false); } + // Otherwise the module is already evaluated return { __proto__: null, module: this.module }; } if (status < kInstantiated) { diff --git a/test/es-module/test-esm-tla-dynamic-import-evaluating-async.mjs b/test/es-module/test-esm-tla-dynamic-import-evaluating-async.mjs new file mode 100644 index 000000000000..b22aeec6939e --- /dev/null +++ b/test/es-module/test-esm-tla-dynamic-import-evaluating-async.mjs @@ -0,0 +1,11 @@ +import '../common/index.mjs'; +import { spawnSyncAndAssert } from '../common/child_process.js'; +import * as fixtures from '../common/fixtures.mjs'; + +// Regression test for https://github.com/nodejs/node/issues/66806 +spawnSyncAndAssert(process.execPath, [ + fixtures.path('es-modules', 'tla', 'dynamic-import-evaluating-async', 'main.mjs'), +], { + stdout: /^ok 2 2\n$/, + stderr: '', +}); diff --git a/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/a.mjs b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/a.mjs new file mode 100644 index 000000000000..795c295e0f99 --- /dev/null +++ b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/a.mjs @@ -0,0 +1,8 @@ +// Suspend so that main.mjs goes on to start evaluating b.mjs +await import('./d.mjs'); +// b.mjs has been started but its body has not run yet +const { x } = await import('./b.mjs'); +// b.mjs is now fully evaluated +const again = await import('./b.mjs'); +if (again.x !== x) throw new Error(`expected ${x}, got ${again.x}`); +export const a = () => x; diff --git a/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/b.mjs b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/b.mjs new file mode 100644 index 000000000000..e4418771762f --- /dev/null +++ b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/b.mjs @@ -0,0 +1,2 @@ +import { c } from './c.mjs'; +export const x = c + 1; diff --git a/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/c.mjs b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/c.mjs new file mode 100644 index 000000000000..33cd24ccede6 --- /dev/null +++ b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/c.mjs @@ -0,0 +1,2 @@ +await new Promise((resolve) => setTimeout(resolve, 10)); +export const c = 1; diff --git a/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/d.mjs b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/d.mjs new file mode 100644 index 000000000000..328b7e8a0914 --- /dev/null +++ b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/d.mjs @@ -0,0 +1 @@ +export const d = 0; diff --git a/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/main.mjs b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/main.mjs new file mode 100644 index 000000000000..ac7cb2d30f9c --- /dev/null +++ b/test/fixtures/es-modules/tla/dynamic-import-evaluating-async/main.mjs @@ -0,0 +1,3 @@ +import { a } from './a.mjs'; +import { x } from './b.mjs'; +console.log('ok', a(), x);