| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 34a537c commit 0cd443d
36 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1349,11 +1349,14 @@ resolution algorithm. | |||
| 1349 | 1349 | added: | |
| 1350 | 1350 | - v22.0.0 | |
| 1351 | 1351 | - v20.17.0 | |
| 1352 | + changes: | ||
| 1353 | + - version: REPLACEME | ||
| 1354 | + pr-url: https://github.com/nodejs/node/pull/64154 | ||
| 1355 | + description: Print the top-level awaits without evaluating the modules. | ||
| 1352 | 1356 | --> | |
| 1353 | 1357 | ||
| 1354 | - If the ES module being `require()`'d contains top-level `await`, this flag | ||
| 1355 | - allows Node.js to evaluate the module, try to locate the | ||
| 1356 | - top-level awaits, and print their location to help users find them. | ||
| 1358 | + If the ES module graph cannot be `require()`'d because it contains any top-level `await`, | ||
| 1359 | + this flag allows Node.js to locate and print their locations. | ||
| 1357 | 1360 | ||
| 1358 | 1361 | ### `--experimental-quic` | |
| 1359 | 1362 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,6 +48,7 @@ const { | |||
| 48 | 48 | StringPrototypeEndsWith, | |
| 49 | 49 | StringPrototypeIncludes, | |
| 50 | 50 | StringPrototypeIndexOf, | |
| 51 | + StringPrototypeRepeat, | ||
| 51 | 52 | StringPrototypeSlice, | |
| 52 | 53 | StringPrototypeSplit, | |
| 53 | 54 | StringPrototypeStartsWith, | |
@@ -1707,15 +1708,26 @@ E('ERR_QUIC_STREAM_ABORTED', '%s', Error); | |||
| 1707 | 1708 | E('ERR_QUIC_STREAM_RESET', | |
| 1708 | 1709 | 'The QUIC stream was reset by the peer with error code %d', Error); | |
| 1709 | 1710 | E('ERR_QUIC_VERSION_NEGOTIATION_ERROR', 'The QUIC session requires version negotiation', Error); | |
| 1710 | - E('ERR_REQUIRE_ASYNC_MODULE', function(filename, parentFilename) { | ||
| 1711 | - let message = 'require() cannot be used on an ESM ' + | ||
| 1712 | - 'graph with top-level await. Use import() instead. To see where the' + | ||
| 1713 | - ' top-level await comes from, use --experimental-print-required-tla.'; | ||
| 1714 | - if (parentFilename) { | ||
| 1715 | - message += `\n From ${parentFilename} `; | ||
| 1711 | + E('ERR_REQUIRE_ASYNC_MODULE', function(filename, parent, locations) { | ||
| 1712 | + let message = 'require() cannot be used on an ESM graph with top-level await. Use import() instead.'; | ||
| 1713 | + const { getOptionValue } = require('internal/options'); | ||
| 1714 | + if (!getOptionValue('--experimental-print-required-tla')) { | ||
| 1715 | + message += ' To see where the top-level await comes from, use --experimental-print-required-tla.'; | ||
| 1716 | 1716 | } | |
| 1717 | - if (filename) { | ||
| 1718 | - message += `\n Requiring ${filename} `; | ||
| 1717 | + if (parent) { | ||
| 1718 | + const { getRequireStack } = require('internal/modules/helpers'); | ||
| 1719 | + const requireStack = getRequireStack(parent); | ||
| 1720 | + if (requireStack.length > 0) { | ||
| 1721 | + message += '\nRequire stack:\n- ' + | ||
| 1722 | + ArrayPrototypeJoin(requireStack, '\n- '); | ||
| 1723 | + } | ||
| 1724 | + this.requireStack = requireStack; | ||
| 1725 | + } | ||
| 1726 | + if (locations && locations.length > 0) { | ||
| 1727 | + const { urlToFilename } = require('internal/modules/helpers'); | ||
| 1728 | + const frames = ArrayPrototypeMap(locations, ({ url, line, column, sourceLine }) => | ||
| 1729 | + `${urlToFilename(url)}:${line}\n\n${sourceLine}\n${StringPrototypeRepeat(' ', column)}^\n`); | ||
| 1730 | + setArrowMessage(this, ArrayPrototypeJoin(frames, '\n')); | ||
| 1719 | 1731 | } | |
| 1720 | 1732 | return message; | |
| 1721 | 1733 | }, Error); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -168,6 +168,7 @@ const { | |||
| 168 | 168 | setHasStartedUserCJSExecution, | |
| 169 | 169 | stripBOM, | |
| 170 | 170 | toRealPath, | |
| 171 | + getRequireStack, | ||
| 171 | 172 | } = require('internal/modules/helpers'); | |
| 172 | 173 | const { | |
| 173 | 174 | convertCJSFilenameToURL, | |
@@ -1572,17 +1573,6 @@ Module._resolveFilename = function(request, parent, isMain, options) { | |||
| 1572 | 1573 | throw err; | |
| 1573 | 1574 | }; | |
| 1574 | 1575 | ||
| 1575 | - function getRequireStack(parent) { | ||
| 1576 | - const requireStack = []; | ||
| 1577 | - for (let cursor = parent; | ||
| 1578 | - cursor; | ||
| 1579 | - // TODO(joyeecheung): it makes more sense to use kLastModuleParent here. | ||
| 1580 | - cursor = cursor[kFirstModuleParent]) { | ||
| 1581 | - ArrayPrototypePush(requireStack, cursor.filename || cursor.id); | ||
| 1582 | - } | ||
| 1583 | - return requireStack; | ||
| 1584 | - } | ||
| 1585 | - | ||
| 1586 | 1576 | function getRequireStackMessage(request, requireStack) { | |
| 1587 | 1577 | let message = `Cannot find module '${request}'`; | |
| 1588 | 1578 | if (requireStack.length > 0) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,7 +25,6 @@ const { imported_cjs_symbol } = internalBinding('symbols'); | |||
| 25 | 25 | ||
| 26 | 26 | const assert = require('internal/assert'); | |
| 27 | 27 | const { | |
| 28 | - ERR_REQUIRE_ASYNC_MODULE, | ||
| 29 | 28 | ERR_REQUIRE_CYCLE_MODULE, | |
| 30 | 29 | ERR_REQUIRE_ESM, | |
| 31 | 30 | ERR_REQUIRE_ESM_RACE_CONDITION, | |
@@ -293,7 +292,7 @@ class ModuleLoader { | |||
| 293 | 292 | debug('Module status', job, status); | |
| 294 | 293 | // hasAsyncGraph is available after module been instantiated. | |
| 295 | 294 | if (status >= kInstantiated && job.module.hasAsyncGraph) { | |
| 296 | - throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename); | ||
| 295 | + job.throwAsyncGraphError(parent); | ||
| 297 | 296 | } | |
| 298 | 297 | if (status === kEvaluated) { | |
| 299 | 298 | return { wrap: job.module, namespace: job.module.getNamespace() }; | |
@@ -321,6 +320,9 @@ class ModuleLoader { | |||
| 321 | 320 | } | |
| 322 | 321 | if (status !== kEvaluating) { | |
| 323 | 322 | assert(status === kUninstantiated, `Unexpected module status ${status}`); | |
| 323 | + // A previous require() of the same graph may have bailed out before | ||
| 324 | + // instantiation because it contains top-level await. | ||
| 325 | + job.throwIfAsyncGraph(parent); | ||
| 324 | 326 | throw new ERR_REQUIRE_ESM_RACE_CONDITION(filename, parentFilename, false); | |
| 325 | 327 | } | |
| 326 | 328 | let message = `Cannot require() ES Module ${filename} in a cycle.`; | |
@@ -371,8 +373,8 @@ class ModuleLoader { | |||
| 371 | 373 | ||
| 372 | 374 | // Otherwise the module could be imported before but the evaluation may be already | |
| 373 | 375 | // completed (e.g. the require call is lazy) so it's okay. We will return the | |
| 374 | - // job and check asynchronicity of the entire graph later, after the | ||
| 375 | - // graph is instantiated. | ||
| 376 | + // job and check asynchronicity of the entire graph later, before the | ||
| 377 | + // graph is evaluated. | ||
| 376 | 378 | } | |
| 377 | 379 | ||
| 378 | 380 | /** | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,8 +4,11 @@ const { | |||
| 4 | 4 | Array, | |
| 5 | 5 | ArrayPrototypeFind, | |
| 6 | 6 | ArrayPrototypeJoin, | |
| 7 | + ArrayPrototypePop, | ||
| 7 | 8 | ArrayPrototypePush, | |
| 9 | + ArrayPrototypeSort, | ||
| 8 | 10 | FunctionPrototype, | |
| 11 | + ObjectAssign, | ||
| 9 | 12 | ObjectSetPrototypeOf, | |
| 10 | 13 | PromisePrototypeThen, | |
| 11 | 14 | PromiseResolve, | |
@@ -128,6 +131,77 @@ const explainCommonJSGlobalLikeNotDefinedError = (e, url, hasTopLevelAwait) => { | |||
| 128 | 131 | } | |
| 129 | 132 | }; | |
| 130 | 133 | ||
| 134 | + /** | ||
| 135 | + * @typedef {object} TopLevelAwaitLocation | ||
| 136 | + * @property {string} url URL of the module containing the top-level await. | ||
| 137 | + * @property {number} line 1-based line number of the top-level await. | ||
| 138 | + * @property {number} column 0-based column number of the top-level await. | ||
| 139 | + * @property {string} sourceLine The source line containing the top-level await. | ||
| 140 | + */ | ||
| 141 | + | ||
| 142 | + /** | ||
| 143 | + * Locate the top-level awaits in the given module by parsing the source with acron. | ||
| 144 | + * @param {string} source Module source code. | ||
| 145 | + * @returns {object[]} The acorn AST nodes of the top-level awaits, in source order. | ||
| 146 | + */ | ||
| 147 | + function findTopLevelAwait(source) { | ||
| 148 | + const { Parser } = require('internal/deps/acorn/acorn/dist/acorn'); | ||
| 149 | + const walk = require('internal/deps/acorn/acorn-walk/dist/walk'); | ||
| 150 | + let ast; | ||
| 151 | + try { | ||
| 152 | + ast = Parser.parse(source, { | ||
| 153 | + __proto__: null, ecmaVersion: 'latest', sourceType: 'module', locations: true, | ||
| 154 | + }); | ||
| 155 | + } catch { | ||
| 156 | + return []; // The source is not parsable, skip. | ||
| 157 | + } | ||
| 158 | + // We are looking for _top-level_ await, so we don't traverse into function bodies. | ||
| 159 | + const baseVisitor = ObjectAssign({ __proto__: null }, walk.base, { Function: noop }); | ||
| 160 | + const found = []; | ||
| 161 | + walk.simple(ast, { | ||
| 162 | + __proto__: null, | ||
| 163 | + AwaitExpression(node) { ArrayPrototypePush(found, node); }, | ||
| 164 | + // `for await (...)` is a ForOfStatement with `await: true`, not an AwaitExpression. | ||
| 165 | + ForOfStatement(node) { | ||
| 166 | + if (node.await) { ArrayPrototypePush(found, node); } | ||
| 167 | + }, | ||
| 168 | + // `await using x = ...` is a VariableDeclaration, not an AwaitExpression. | ||
| 169 | + VariableDeclaration(node) { | ||
| 170 | + if (node.kind === 'await using') { ArrayPrototypePush(found, node); } | ||
| 171 | + }, | ||
| 172 | + }, baseVisitor); | ||
| 173 | + ArrayPrototypeSort(found, (a, b) => a.start - b.start); | ||
| 174 | + return found; | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + /** | ||
| 178 | + * Locate the top-level awaits in the given modules. | ||
| 179 | + * @param {ModuleWrap[]} modules Modules that may contain top-level await. | ||
| 180 | + * @returns {TopLevelAwaitLocation[]} The locations of the top-level awaits. | ||
| 181 | + */ | ||
| 182 | + function getTopLevelAwaitLocations(modules) { | ||
| 183 | + const locations = []; | ||
| 184 | + for (let i = 0; i < modules.length; i++) { | ||
| 185 | + const module = modules[i]; | ||
| 186 | + const source = module.source; | ||
| 187 | + if (typeof source !== 'string') { continue; } // Not retained during compilation. Skip. | ||
| 188 | + const found = findTopLevelAwait(source); | ||
| 189 | + if (found.length === 0) { continue; } | ||
| 190 | + const lines = StringPrototypeSplit(source, '\n'); | ||
| 191 | + for (let j = 0; j < found.length; j++) { | ||
| 192 | + const { start } = found[j].loc; | ||
| 193 | + ArrayPrototypePush(locations, { | ||
| 194 | + __proto__: null, | ||
| 195 | + url: module.url, | ||
| 196 | + line: start.line, | ||
| 197 | + column: start.column, | ||
| 198 | + sourceLine: lines[start.line - 1], | ||
| 199 | + }); | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + return locations; | ||
| 203 | + } | ||
| 204 | + | ||
| 131 | 205 | class ModuleJobBase { | |
| 132 | 206 | constructor(loader, url, importAttributes, phase, isMain, inspectBrk) { | |
| 133 | 207 | assert(typeof phase === 'number'); | |
@@ -186,6 +260,64 @@ class ModuleJobBase { | |||
| 186 | 260 | return evaluationDepJobs; | |
| 187 | 261 | } | |
| 188 | 262 | ||
| 263 | + /** | ||
| 264 | + * Collect the modules that contain top-level await in the linked graph of | ||
| 265 | + * this job. Whether each module contains top-level await is known at | ||
| 266 | + * compilation, so for a synchronously linked graph this finds asynchronous | ||
| 267 | + * graphs before instantiation. | ||
| 268 | + * On the (deprecated) async loader hook worker thread, linking may be asynchronous, in | ||
| 269 | + * which case the subgraphs that are not synchronously linked are skipped | ||
| 270 | + * and callers should still consult hasAsyncGraph after instantiation. | ||
| 271 | + * @returns {ModuleWrap[]} | ||
| 272 | + */ | ||
| 273 | + findModulesWithTopLevelAwait() { | ||
| 274 | + const found = []; | ||
| 275 | + const seen = new SafeSet(); | ||
| 276 | + const stack = [this]; | ||
| 277 | + while (stack.length > 0) { | ||
| 278 | + const job = ArrayPrototypePop(stack); | ||
| 279 | + if (seen.has(job)) { continue; } | ||
| 280 | + seen.add(job); | ||
| 281 | + if (job.module?.hasTopLevelAwait) { | ||
| 282 | + ArrayPrototypePush(found, job.module); | ||
| 283 | + } | ||
| 284 | + // job.linked is the array of evaluation-phase dependency jobs when the | ||
| 285 | + // linking is synchronous. Skip it if it's still a promise. | ||
| 286 | + if (!isPromise(job.linked)) { | ||
| 287 | + for (let i = 0; i < job.linked.length; i++) { | ||
| 288 | + ArrayPrototypePush(stack, job.linked[i]); | ||
| 289 | + } | ||
| 290 | + } | ||
| 291 | + } | ||
| 292 | + return found; | ||
| 293 | + } | ||
| 294 | + | ||
| 295 | + /** | ||
| 296 | + * Throw the ERR_REQUIRE_ASYNC_MODULE with metadata for a require()'d graph that | ||
| 297 | + * contains top-level await. | ||
| 298 | + * @param {Module|undefined} parent CommonJS module that require()'d this, if any. | ||
| 299 | + * @param {ModuleWrap[]} [modules] Modules with top-level await, when already | ||
| 300 | + * collected by the caller, to avoid walking the graph again. | ||
| 301 | + */ | ||
| 302 | + throwAsyncGraphError(parent, modules = this.findModulesWithTopLevelAwait()) { | ||
| 303 | + const locations = getOptionValue('--experimental-print-required-tla') ? getTopLevelAwaitLocations(modules) : []; | ||
| 304 | + const filename = urlToFilename(this.url); | ||
| 305 | + throw new ERR_REQUIRE_ASYNC_MODULE(filename, parent, locations); | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + /** | ||
| 309 | + * If the a require()'d graph contains top-level await, collect the source locations | ||
| 310 | + * of the top-level awaits using source code retained during compilation and throw | ||
| 311 | + * ERR_REQUIRE_ASYNC_MODULE. This can be run before instantiation is complete. | ||
| 312 | + * @param {Module|undefined} parent CommonJS module that require()'d this, if any. | ||
| 313 | + */ | ||
| 314 | + throwIfAsyncGraph(parent) { | ||
| 315 | + const modules = this.findModulesWithTopLevelAwait(); | ||
| 316 | + if (modules.length > 0) { | ||
| 317 | + this.throwAsyncGraphError(parent, modules); | ||
| 318 | + } | ||
| 319 | + } | ||
| 320 | + | ||
| 189 | 321 | /** | |
| 190 | 322 | * Ensure that this ModuleJob is moving towards the required phase | |
| 191 | 323 | * (does not necessarily mean it is ready at that phase - run does that) | |
@@ -394,6 +526,8 @@ class ModuleJob extends ModuleJobBase { | |||
| 394 | 526 | ||
| 395 | 527 | debug('ModuleJob.runSync()', status, this.module); | |
| 396 | 528 | if (status === kUninstantiated) { | |
| 529 | + // TODO(joyeecheung): Reject graphs with top-level await _before_ instantiation, so that | ||
| 530 | + // the async graph error supersedes instantiation (mismatch export) errors in the graph. | ||
| 397 | 531 | // FIXME(joyeecheung): this cannot fully handle < kInstantiated. Make the linking | |
| 398 | 532 | // fully synchronous instead. | |
| 399 | 533 | if (this.module.getModuleRequests().length === 0) { | |
@@ -403,22 +537,18 @@ class ModuleJob extends ModuleJobBase { | |||
| 403 | 537 | status = this.module.getStatus(); | |
| 404 | 538 | } | |
| 405 | 539 | if (status === kInstantiated || status === kErrored) { | |
| 406 | - const filename = urlToFilename(this.url); | ||
| 407 | - const parentFilename = urlToFilename(parent?.filename); | ||
| 408 | - if (this.module.hasAsyncGraph && !getOptionValue('--experimental-print-required-tla')) { | ||
| 409 | - throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename); | ||
| 540 | + if (this.module.hasAsyncGraph) { | ||
| 541 | + this.throwAsyncGraphError(parent); | ||
| 410 | 542 | } | |
| 411 | 543 | if (status === kInstantiated) { | |
| 412 | 544 | setHasStartedUserESMExecution(); | |
| 413 | - const namespace = this.module.evaluateSync(filename, parentFilename); | ||
| 545 | + const namespace = this.module.evaluateSync(); | ||
| 414 | 546 | return { __proto__: null, module: this.module, namespace }; | |
| 415 | 547 | } | |
| 416 | 548 | throw this.module.getError(); | |
| 417 | 549 | } else if (status === kEvaluating || status === kEvaluated) { | |
| 418 | 550 | if (this.module.hasAsyncGraph) { | |
| 419 | - const filename = urlToFilename(this.url); | ||
| 420 | - const parentFilename = urlToFilename(parent?.filename); | ||
| 421 | - throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename); | ||
| 551 | + this.throwAsyncGraphError(parent); | ||
| 422 | 552 | } | |
| 423 | 553 | // kEvaluating can show up when this is being used to deal with CJS <-> CJS cycles. | |
| 424 | 554 | // Allow it for now, since we only need to ban ESM <-> CJS cycles which would be | |
@@ -514,9 +644,16 @@ class ModuleJobSync extends ModuleJobBase { | |||
| 514 | 644 | await this.evaluationPromise; | |
| 515 | 645 | } | |
| 516 | 646 | return { __proto__: null, module: this.module }; | |
| 517 | - } else if (status === kInstantiated) { | ||
| 518 | - // The evaluation may have been canceled because instantiate() detected TLA first. | ||
| 519 | - // But when it is imported again, it's fine to re-evaluate it asynchronously. | ||
| 647 | + } else if (status === kInstantiated || status === kUninstantiated) { | ||
| 648 | + // The require() of this (synchronously linked) module bailed out: either | ||
| 649 | + // it was rejected for containing top-level await after instantiation | ||
| 650 | + // (kInstantiated), or its instantiation failed and left it uninstantiated | ||
| 651 | + // (kUninstantiated, e.g. a missing named export). When it's reached via async | ||
| 652 | + // run() from import, finish the instantiation and evaluate it asynchronously, | ||
| 653 | + // re-throwing any instantiation error. | ||
| 654 | + if (status === kUninstantiated) { | ||
| 655 | + this.module.instantiate(); | ||
| 656 | + } | ||
| 520 | 657 | const timeout = -1; | |
| 521 | 658 | const breakOnSigint = false; | |
| 522 | 659 | this.evaluationPromise = this.module.evaluate(timeout, breakOnSigint); | |
@@ -532,23 +669,19 @@ class ModuleJobSync extends ModuleJobBase { | |||
| 532 | 669 | runSync(parent) { | |
| 533 | 670 | debug('ModuleJobSync.runSync()', this.module); | |
| 534 | 671 | assert(this.shouldRunModule(this.phase)); | |
| 672 | + // TODO(joyeecheung): Reject graphs with top-level await _before_ instantiation, so that the | ||
| 673 | + // async graph error supersedes instantiation (mismatch export) errors in the graph. | ||
| 535 | 674 | // TODO(joyeecheung): add the error decoration logic from the async instantiate. | |
| 536 | 675 | this.module.instantiate(); | |
| 537 | - // If --experimental-print-required-tla is true, proceeds to evaluation even | ||
| 538 | - // if it's async because we want to search for the TLA and help users locate | ||
| 539 | - // them. | ||
| 540 | - // TODO(joyeecheung): track the asynchroniticy using v8::Module::HasTopLevelAwait() | ||
| 541 | - // and we'll be able to throw right after compilation of the modules, using acron | ||
| 542 | - // to find and print the TLA. This requires the linking to be synchronous in case | ||
| 543 | - // it runs into cached asynchronous modules that are not yet fetched. | ||
| 544 | - const parentFilename = urlToFilename(parent?.filename); | ||
| 545 | - const filename = urlToFilename(this.url); | ||
| 546 | - if (this.module.hasAsyncGraph && !getOptionValue('--experimental-print-required-tla')) { | ||
| 547 | - throw new ERR_REQUIRE_ASYNC_MODULE(filename, parentFilename); | ||
| 676 | + // On the deprecated async loader hook worker thread, dependencies linked by an | ||
| 677 | + // earlier import may not be walkable synchronously, so double-check with | ||
| 678 | + // V8 now that the graph is instantiated. | ||
| 679 | + if (this.module.hasAsyncGraph) { | ||
| 680 | + this.throwAsyncGraphError(parent); | ||
| 548 | 681 | } | |
| 549 | 682 | setHasStartedUserESMExecution(); | |
| 550 | 683 | try { | |
| 551 | - const namespace = this.module.evaluateSync(filename, parentFilename); | ||
| 684 | + const namespace = this.module.evaluateSync(); | ||
| 552 | 685 | return { __proto__: null, module: this.module, namespace }; | |
| 553 | 686 | } catch (e) { | |
| 554 | 687 | explainCommonJSGlobalLikeNotDefinedError(e, this.module.url, this.module.hasTopLevelAwait); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -145,7 +145,7 @@ function loadCJSModuleWithSpecialRequire(module, source, url, filename, isMain, | |||
| 145 | 145 | // On the main thread, the authentic require() is used instead (fixed by #60380). | |
| 146 | 146 | const request = { specifier, attributes: importAttributes, phase: kEvaluationPhase, __proto__: null }; | |
| 147 | 147 | const job = cascadedLoader.getOrCreateModuleJob(url, request, kRequireInImportedCJS); | |
| 148 | - job.runSync(); | ||
| 148 | + job.runSync(module); | ||
| 149 | 149 | let mod = cjsCache.get(job.url); | |
| 150 | 150 | assert(job.module, `Imported CJS module ${url} failed to load module ${job.url} using require() due to race condition`); | |
| 151 | 151 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -362,6 +362,15 @@ function compileSourceTextModule(url, source, type, context = kEmptyObject) { | |||
| 362 | 362 | wrap.isMain = true; | |
| 363 | 363 | } | |
| 364 | 364 | ||
| 365 | + // Add an extra reference to the source of modules containing top-level await so that if the | ||
| 366 | + // module ends up being require()'d, we can parse the location of the top-level awaits to print | ||
| 367 | + // better errors. There will be other references to the same source in the module in V8 so this | ||
| 368 | + // only serves as a shortcut. | ||
| 369 | + if (wrap.hasTopLevelAwait && | ||
| 370 | + getOptionValue('--experimental-print-required-tla')) { | ||
| 371 | + wrap.source = source; | ||
| 372 | + } | ||
| 373 | + | ||
| 365 | 374 | // Cache the source map for the module if present. | |
| 366 | 375 | if (wrap.sourceMapURL) { | |
| 367 | 376 | maybeCacheSourceMap(url, source, wrap, false, wrap.sourceURL, wrap.sourceMapURL); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments