|
8 | 8 | const querystring = require("querystring"); |
9 | 9 | const { |
10 | 10 | AsyncSeriesBailHook, |
| 11 | + AsyncSeriesWaterfallHook, |
11 | 12 | HookMap, |
12 | 13 | SyncHook, |
13 | 14 | SyncWaterfallHook |
@@ -184,7 +185,9 @@ const recordSideEffectsBailout = (mod, moduleGraph, dep) => { |
184 | 185 | .getOptimizationBailout(mod) |
185 | 186 | .push( |
186 | 187 | () => |
187 | | - `Dependency (${dep.type}) with side effects at ${formatLocation(dep.loc)}` |
| 188 | + `Dependency (${dep.type}) with side effects at ${formatLocation( |
| 189 | + dep.loc |
| 190 | + )}` |
188 | 191 | ); |
189 | 192 | }; |
190 | 193 |
|
@@ -546,6 +549,16 @@ const createCompilationHooks = () => { |
546 | 549 | * @since 5.99.0 |
547 | 550 | */ |
548 | 551 | processResult: new SyncWaterfallHook(["result", "module"]), |
| 552 | + /** |
| 553 | + * The same, for a tap that has to await work of its own — minifying an |
| 554 | + * asset, say, which before only a loader could do. Runs after |
| 555 | + * `processResult` and is skipped entirely when nothing taps it, so a |
| 556 | + * build that does not use it keeps the synchronous path, and the stacks |
| 557 | + * of errors thrown below it stay free of the hook's own frames. |
| 558 | + * @type {AsyncSeriesWaterfallHook<[Result, NormalModule]>} |
| 559 | + * @since 5.110.0 |
| 560 | + */ |
| 561 | + processResultAsync: new AsyncSeriesWaterfallHook(["result", "module"]), |
549 | 562 | /** |
550 | 563 | * @type {AsyncSeriesBailHook<[NormalModule, NeedBuildContext], boolean>} |
551 | 564 | * @since 5.49.0 |
@@ -1052,7 +1065,9 @@ class NormalModule extends Module { |
1052 | 1065 | (parseJson(options)); |
1053 | 1066 | } catch (err) { |
1054 | 1067 | throw new Error( |
1055 | | - `Cannot parse string options: ${/** @type {Error} */ (err).message}`, |
| 1068 | + `Cannot parse string options: ${ |
| 1069 | + /** @type {Error} */ (err).message |
| 1070 | + }`, |
1056 | 1071 | { cause: err } |
1057 | 1072 | ); |
1058 | 1073 | } |
@@ -1441,7 +1456,9 @@ class NormalModule extends Module { |
1441 | 1456 | }) |
1442 | 1457 | ).catch((err) => { |
1443 | 1458 | throw new Error( |
1444 | | - `Failed to parse source map. ${/** @type {Error} */ (err).message}` |
| 1459 | + `Failed to parse source map. ${ |
| 1460 | + /** @type {Error} */ (err).message |
| 1461 | + }` |
1445 | 1462 | ); |
1446 | 1463 | }) |
1447 | 1464 | ); |
@@ -1503,7 +1520,29 @@ class NormalModule extends Module { |
1503 | 1520 | if (result.notCacheableReasons.length > 0) { |
1504 | 1521 | buildInfo.notCacheableReasons = result.notCacheableReasons; |
1505 | 1522 | } |
1506 | | - processResult(err, result.result); |
| 1523 | + // Untapped, nothing can change: call straight through, so a build |
| 1524 | + // that does not use it keeps both the synchronous path and the |
| 1525 | + // exact stack depth anything thrown below it is reported at. |
| 1526 | + if (hooks.processResultAsync.taps.length === 0) { |
| 1527 | + return processResult(err, result.result); |
| 1528 | + } |
| 1529 | + |
| 1530 | + hooks.processResultAsync.callAsync( |
| 1531 | + /** @type {Result} */ |
| 1532 | + (result.result), |
| 1533 | + this, |
| 1534 | + (hookErr, asyncResult) => { |
| 1535 | + if (hookErr) { |
| 1536 | + return processResult( |
| 1537 | + hookErr instanceof Error |
| 1538 | + ? hookErr |
| 1539 | + : new (getNonErrorEmittedError())(hookErr) |
| 1540 | + ); |
| 1541 | + } |
| 1542 | + |
| 1543 | + processResult(null, /** @type {Result} */ (asyncResult)); |
| 1544 | + } |
| 1545 | + ); |
1507 | 1546 | } |
1508 | 1547 | ); |
1509 | 1548 | } |
|
0 commit comments