Skip to content

Commit 793a279

Browse files
authored
module: throw error when re-runing errored module jobs
Re-evaluating an errored ESM should lead to rejecting the rejection again - this is also the case when importing it twice. In the case of retrying with require after import, just throw the cached error. Drive-by: add some debug logs. PR-URL: #58957 Fixes: #58945 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 794a13f commit 793a279

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

lib/internal/modules/esm/loader.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const {
4343
kEvaluating,
4444
kEvaluationPhase,
4545
kInstantiated,
46+
kErrored,
4647
kSourcePhase,
4748
throwIfPromiseRejected,
4849
} = internalBinding('module_wrap');
@@ -402,6 +403,9 @@ class ModuleLoader {
402403
mod[kRequiredModuleSymbol] = job.module;
403404
const { namespace } = job.runSync(parent);
404405
return { wrap: job.module, namespace: namespace || job.module.getNamespace() };
406+
} else if (status === kErrored) {
407+
// If the module was previously imported and errored, throw the error.
408+
throw job.module.getError();
405409
}
406410
// When the cached async job have already encountered a linking
407411
// error that gets wrapped into a rejection, but is still later

lib/internal/modules/esm/module_job.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ class ModuleJob extends ModuleJobBase {
323323
assert(this.module instanceof ModuleWrap);
324324
let status = this.module.getStatus();
325325

326-
debug('ModuleJob.runSync', this.module);
326+
debug('ModuleJob.runSync()', status, this.module);
327327
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
328328
// fully synchronous instead.
329329
if (status === kUninstantiated) {
@@ -358,6 +358,7 @@ class ModuleJob extends ModuleJobBase {
358358
}
359359

360360
async run(isEntryPoint = false) {
361+
debug('ModuleJob.run()', this.module);
361362
assert(this.phase === kEvaluationPhase);
362363
await this.#instantiate();
363364
if (isEntryPoint) {
@@ -461,7 +462,11 @@ class ModuleJobSync extends ModuleJobBase {
461462
assert(this.phase === kEvaluationPhase);
462463
// This path is hit by a require'd module that is imported again.
463464
const status = this.module.getStatus();
464-
if (status > kInstantiated) {
465+
debug('ModuleJobSync.run()', status, this.module);
466+
// If the module was previously required and errored, reject from import() again.
467+
if (status === kErrored) {
468+
throw this.module.getError();
469+
} else if (status > kInstantiated) {
465470
if (this.evaluationPromise) {
466471
await this.evaluationPromise;
467472
}
@@ -482,6 +487,7 @@ class ModuleJobSync extends ModuleJobBase {
482487
}
483488

484489
runSync(parent) {
490+
debug('ModuleJobSync.runSync()', this.module);
485491
assert(this.phase === kEvaluationPhase);
486492
// TODO(joyeecheung): add the error decoration logic from the async instantiate.
487493
this.module.async = this.module.instantiateSync();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This tests that after failing to import an ESM that rejects,
2+
// retrying with require() still throws.
3+
4+
'use strict';
5+
const common = require('../common');
6+
const assert = require('assert');
7+
8+
(async () => {
9+
await assert.rejects(import('../fixtures/es-modules/throw-error.mjs'), {
10+
message: 'test',
11+
});
12+
assert.throws(() => {
13+
require('../fixtures/es-modules/throw-error.mjs');
14+
}, {
15+
message: 'test',
16+
});
17+
})().catch(common.mustNotCall());
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This tests that after failing to require an ESM that throws,
2+
// retrying with import() still rejects.
3+
4+
'use strict';
5+
const common = require('../common');
6+
const assert = require('assert');
7+
8+
assert.throws(() => {
9+
require('../fixtures/es-modules/throw-error.mjs');
10+
}, {
11+
message: 'test',
12+
});
13+
14+
assert.rejects(import('../fixtures/es-modules/throw-error.mjs'), {
15+
message: 'test',
16+
}).catch(common.mustNotCall());

0 commit comments

Comments
 (0)