Skip to content

Commit 200ff5e

Browse files
joyeecheungmarco-ippolito
authored andcommitted
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 Backport-PR-URL: #59504 Fixes: #58945 Reviewed-By: Marco Ippolito <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Refs: #52697
1 parent b956966 commit 200ff5e

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
@@ -41,6 +41,7 @@ const {
4141
kEvaluated,
4242
kEvaluating,
4343
kInstantiated,
44+
kErrored,
4445
throwIfPromiseRejected,
4546
} = internalBinding('module_wrap');
4647
const {
@@ -350,6 +351,9 @@ class ModuleLoader {
350351
mod[kRequiredModuleSymbol] = job.module;
351352
const { namespace } = job.runSync(parent);
352353
return { wrap: job.module, namespace: namespace || job.module.getNamespace() };
354+
} else if (status === kErrored) {
355+
// If the module was previously imported and errored, throw the error.
356+
throw job.module.getError();
353357
}
354358
// When the cached async job have already encountered a linking
355359
// 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
@@ -281,7 +281,7 @@ class ModuleJob extends ModuleJobBase {
281281
assert(this.module instanceof ModuleWrap);
282282
let status = this.module.getStatus();
283283

284-
debug('ModuleJob.runSync', this.module);
284+
debug('ModuleJob.runSync()', status, this.module);
285285
// FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking
286286
// fully synchronous instead.
287287
if (status === kUninstantiated) {
@@ -316,6 +316,7 @@ class ModuleJob extends ModuleJobBase {
316316
}
317317

318318
async run() {
319+
debug('ModuleJob.run()', this.module);
319320
await this.instantiate();
320321
const timeout = -1;
321322
const breakOnSigint = false;
@@ -392,7 +393,11 @@ class ModuleJobSync extends ModuleJobBase {
392393
async run() {
393394
// This path is hit by a require'd module that is imported again.
394395
const status = this.module.getStatus();
395-
if (status > kInstantiated) {
396+
debug('ModuleJobSync.run()', status, this.module);
397+
// If the module was previously required and errored, reject from import() again.
398+
if (status === kErrored) {
399+
throw this.module.getError();
400+
} else if (status > kInstantiated) {
396401
if (this.evaluationPromise) {
397402
await this.evaluationPromise;
398403
}
@@ -413,6 +418,7 @@ class ModuleJobSync extends ModuleJobBase {
413418
}
414419

415420
runSync(parent) {
421+
debug('ModuleJobSync.runSync()', this.module);
416422
// TODO(joyeecheung): add the error decoration logic from the async instantiate.
417423
this.module.async = this.module.instantiateSync();
418424
// If --experimental-print-required-tla is true, proceeds to evaluation even
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)