◐ Shell
clean mode source ↗

esm: add `ERR_REQUIRE_ESM_RACE_CONDITION` · nodejs/node@ddf1f01

@@ -27,6 +27,7 @@ const {

2727

ERR_REQUIRE_ASYNC_MODULE,

2828

ERR_REQUIRE_CYCLE_MODULE,

2929

ERR_REQUIRE_ESM,

30+

ERR_REQUIRE_ESM_RACE_CONDITION,

3031

ERR_UNKNOWN_MODULE_FORMAT,

3132

} = require('internal/errors').codes;

3233

const { getOptionValue } = require('internal/options');

@@ -48,6 +49,7 @@ const {

4849

kEvaluating,

4950

kEvaluationPhase,

5051

kInstantiated,

52+

kUninstantiated,

5153

kErrored,

5254

kSourcePhase,

5355

throwIfPromiseRejected,

@@ -101,24 +103,6 @@ const { translators } = require('internal/modules/esm/translators');

101103

const { defaultResolve } = require('internal/modules/esm/resolve');

102104

const { defaultLoadSync, throwUnknownModuleFormat } = require('internal/modules/esm/load');

103105104-

/**

105-

* Generate message about potential race condition caused by requiring a cached module that has started

106-

* async linking.

107-

* @param {string} filename Filename of the module being required.

108-

* @param {string|undefined} parentFilename Filename of the module calling require().

109-

* @param {boolean} isForAsyncLoaderHookWorker Whether this is for the async loader hook worker.

110-

* @returns {string} Error message.

111-

*/

112-

function getRaceMessage(filename, parentFilename, isForAsyncLoaderHookWorker) {

113-

let raceMessage = `Cannot require() ES Module ${filename} because it is not yet fully loaded.\n`;

114-

raceMessage += 'This may be caused by a race condition if the module is simultaneously dynamically ';

115-

raceMessage += 'import()-ed via Promise.all().\n';

116-

raceMessage += 'Try await-ing the import() sequentially in a loop instead.\n';

117-

raceMessage += ` (From ${parentFilename ? `${parentFilename} in ` : ' '}`;

118-

raceMessage += `${isForAsyncLoaderHookWorker ? 'loader hook worker thread' : 'non-loader-hook thread'})`;

119-

return raceMessage;

120-

}

121-122106

/**

123107

* @typedef {import('../cjs/loader.js').Module} CJSModule

124108

*/

@@ -306,7 +290,7 @@ class ModuleLoader {

306290

const parentFilename = urlToFilename(parent?.filename);

307291

// This race should only be possible on the loader hook thread. See https://github.com/nodejs/node/issues/59666

308292

if (!job.module) {

309-

assert.fail(getRaceMessage(filename, parentFilename), this.isForAsyncLoaderHookWorker);

293+

throw new ERR_REQUIRE_ESM_RACE_CONDITION(filename, parentFilename, this.isForAsyncLoaderHookWorker);

310294

}

311295

const status = job.module.getStatus();

312296

debug('Module status', job, status);

@@ -339,8 +323,8 @@ class ModuleLoader {

339323

throwIfPromiseRejected(job.instantiated);

340324

}

341325

if (status !== kEvaluating) {

342-

assert.fail(`Unexpected module status ${status}. ` +

343-

getRaceMessage(filename, parentFilename));

326+

assert(status === kUninstantiated, `Unexpected module status ${status}`);

327+

throw new ERR_REQUIRE_ESM_RACE_CONDITION(filename, parentFilename, false);

344328

}

345329

let message = `Cannot require() ES Module ${filename} in a cycle.`;

346330

if (parentFilename) {

@@ -376,7 +360,7 @@ class ModuleLoader {

376360

#checkCachedJobForRequireESM(specifier, url, parentURL, job) {

377361

// This race should only be possible on the loader hook thread. See https://github.com/nodejs/node/issues/59666

378362

if (!job.module) {

379-

assert.fail(getRaceMessage(url, parentURL, this.isForAsyncLoaderHookWorker));

363+

throw new ERR_REQUIRE_ESM_RACE_CONDITION(url, parentURL, this.isForAsyncLoaderHookWorker);

380364

}

381365

// This module is being evaluated, which means it's imported in a previous link

382366

// in a cycle.