| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 2251242 commit 953fefe
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -815,7 +815,7 @@ export async function resolve(specifier, context, nextResolve) { | |||
| 815 | 815 | ||
| 816 | 816 | // Defer to the next hook in the chain, which would be the | |
| 817 | 817 | // Node.js default resolve if this is the last user-specified loader. | |
| 818 | - return nextResolve(specifier, context); | ||
| 818 | + return nextResolve(specifier); | ||
| 819 | 819 | } | |
| 820 | 820 | ``` | |
| 821 | 821 | ||
@@ -910,7 +910,7 @@ export async function load(url, context, nextLoad) { | |||
| 910 | 910 | } | |
| 911 | 911 | ||
| 912 | 912 | // Defer to the next hook in the chain. | |
| 913 | - return nextLoad(url, context); | ||
| 913 | + return nextLoad(url); | ||
| 914 | 914 | } | |
| 915 | 915 | ``` | |
| 916 | 916 | ||
@@ -1026,7 +1026,7 @@ export function resolve(specifier, context, nextResolve) { | |||
| 1026 | 1026 | } | |
| 1027 | 1027 | ||
| 1028 | 1028 | // Let Node.js handle all other specifiers. | |
| 1029 | - return nextResolve(specifier, context); | ||
| 1029 | + return nextResolve(specifier); | ||
| 1030 | 1030 | } | |
| 1031 | 1031 | ||
| 1032 | 1032 | export function load(url, context, nextLoad) { | |
@@ -1049,7 +1049,7 @@ export function load(url, context, nextLoad) { | |||
| 1049 | 1049 | } | |
| 1050 | 1050 | ||
| 1051 | 1051 | // Let Node.js handle all other URLs. | |
| 1052 | - return nextLoad(url, context); | ||
| 1052 | + return nextLoad(url); | ||
| 1053 | 1053 | } | |
| 1054 | 1054 | ``` | |
| 1055 | 1055 | ||
@@ -1102,7 +1102,7 @@ export async function resolve(specifier, context, nextResolve) { | |||
| 1102 | 1102 | } | |
| 1103 | 1103 | ||
| 1104 | 1104 | // Let Node.js handle all other specifiers. | |
| 1105 | - return nextResolve(specifier, context); | ||
| 1105 | + return nextResolve(specifier); | ||
| 1106 | 1106 | } | |
| 1107 | 1107 | ||
| 1108 | 1108 | export async function load(url, context, nextLoad) { | |
@@ -1143,7 +1143,7 @@ export async function load(url, context, nextLoad) { | |||
| 1143 | 1143 | } | |
| 1144 | 1144 | ||
| 1145 | 1145 | // Let Node.js handle all other URLs. | |
| 1146 | - return nextLoad(url, context); | ||
| 1146 | + return nextLoad(url); | ||
| 1147 | 1147 | } | |
| 1148 | 1148 | ||
| 1149 | 1149 | async function getPackageType(url) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -114,7 +114,7 @@ let emittedSpecifierResolutionWarning = false; | |||
| 114 | 114 | * validation within MUST throw. | |
| 115 | 115 | * @returns {function next<HookName>(...hookArgs)} The next hook in the chain. | |
| 116 | 116 | */ | |
| 117 | - function nextHookFactory(chain, meta, validate) { | ||
| 117 | + function nextHookFactory(chain, meta, { validateArgs, validateOutput }) { | ||
| 118 | 118 | // First, prepare the current | |
| 119 | 119 | const { hookName } = meta; | |
| 120 | 120 | const { | |
@@ -137,7 +137,7 @@ function nextHookFactory(chain, meta, validate) { | |||
| 137 | 137 | // factory generates the next link in the chain. | |
| 138 | 138 | meta.hookIndex--; | |
| 139 | 139 | ||
| 140 | - nextNextHook = nextHookFactory(chain, meta, validate); | ||
| 140 | + nextNextHook = nextHookFactory(chain, meta, { validateArgs, validateOutput }); | ||
| 141 | 141 | } else { | |
| 142 | 142 | // eslint-disable-next-line func-name-matching | |
| 143 | 143 | nextNextHook = function chainAdvancedTooFar() { | |
@@ -152,14 +152,28 @@ function nextHookFactory(chain, meta, validate) { | |||
| 152 | 152 | // Update only when hook is invoked to avoid fingering the wrong filePath | |
| 153 | 153 | meta.hookErrIdentifier = `${hookFilePath} '${hookName}'`; | |
| 154 | 154 | ||
| 155 | - validate(`${meta.hookErrIdentifier} hook's ${nextHookName}()`, args); | ||
| 155 | + validateArgs(`${meta.hookErrIdentifier} hook's ${nextHookName}()`, args); | ||
| 156 | + | ||
| 157 | + const outputErrIdentifier = `${chain[generatedHookIndex].url} '${hookName}' hook's ${nextHookName}()`; | ||
| 156 | 158 | ||
| 157 | 159 | // Set when next<HookName> is actually called, not just generated. | |
| 158 | 160 | if (generatedHookIndex === 0) { meta.chainFinished = true; } | |
| 159 | 161 | ||
| 162 | + // `context` is an optional argument that only needs to be passed when changed | ||
| 163 | + switch (args.length) { | ||
| 164 | + case 1: // It was omitted, so supply the cached value | ||
| 165 | + ArrayPrototypePush(args, meta.context); | ||
| 166 | + break; | ||
| 167 | + case 2: // Overrides were supplied, so update cached value | ||
| 168 | + ObjectAssign(meta.context, args[1]); | ||
| 169 | + break; | ||
| 170 | + } | ||
| 171 | + | ||
| 160 | 172 | ArrayPrototypePush(args, nextNextHook); | |
| 161 | 173 | const output = await ReflectApply(hook, undefined, args); | |
| 162 | 174 | ||
| 175 | + validateOutput(outputErrIdentifier, output); | ||
| 176 | + | ||
| 163 | 177 | if (output?.shortCircuit === true) { meta.shortCircuited = true; } | |
| 164 | 178 | return output; | |
| 165 | 179 | ||
@@ -554,13 +568,14 @@ class ESMLoader { | |||
| 554 | 568 | const chain = this.#loaders; | |
| 555 | 569 | const meta = { | |
| 556 | 570 | chainFinished: null, | |
| 571 | + context, | ||
| 557 | 572 | hookErrIdentifier: '', | |
| 558 | 573 | hookIndex: chain.length - 1, | |
| 559 | 574 | hookName: 'load', | |
| 560 | 575 | shortCircuited: false, | |
| 561 | 576 | }; | |
| 562 | 577 | ||
| 563 | - const validate = (hookErrIdentifier, { 0: nextUrl, 1: ctx }) => { | ||
| 578 | + const validateArgs = (hookErrIdentifier, { 0: nextUrl, 1: ctx }) => { | ||
| 564 | 579 | if (typeof nextUrl !== 'string') { | |
| 565 | 580 | // non-strings can be coerced to a url string | |
| 566 | 581 | // validateString() throws a less-specific error | |
@@ -584,21 +599,24 @@ class ESMLoader { | |||
| 584 | 599 | } | |
| 585 | 600 | } | |
| 586 | 601 | ||
| 587 | - validateObject(ctx, `${hookErrIdentifier} context`); | ||
| 602 | + if (ctx) validateObject(ctx, `${hookErrIdentifier} context`); | ||
| 603 | + }; | ||
| 604 | + const validateOutput = (hookErrIdentifier, output) => { | ||
| 605 | + if (typeof output !== 'object' || output === null) { // [2] | ||
| 606 | + throw new ERR_INVALID_RETURN_VALUE( | ||
| 607 | + 'an object', | ||
| 608 | + hookErrIdentifier, | ||
| 609 | + output, | ||
| 610 | + ); | ||
| 611 | + } | ||
| 588 | 612 | }; | |
| 589 | 613 | ||
| 590 | - const nextLoad = nextHookFactory(chain, meta, validate); | ||
| 614 | + const nextLoad = nextHookFactory(chain, meta, { validateArgs, validateOutput }); | ||
| 591 | 615 | ||
| 592 | 616 | const loaded = await nextLoad(url, context); | |
| 593 | 617 | const { hookErrIdentifier } = meta; // Retrieve the value after all settled | |
| 594 | 618 | ||
| 595 | - if (typeof loaded !== 'object') { // [2] | ||
| 596 | - throw new ERR_INVALID_RETURN_VALUE( | ||
| 597 | - 'an object', | ||
| 598 | - hookErrIdentifier, | ||
| 599 | - loaded, | ||
| 600 | - ); | ||
| 601 | - } | ||
| 619 | + validateOutput(hookErrIdentifier, loaded); | ||
| 602 | 620 | ||
| 603 | 621 | if (loaded?.shortCircuit === true) { meta.shortCircuited = true; } | |
| 604 | 622 | ||
@@ -797,41 +815,44 @@ class ESMLoader { | |||
| 797 | 815 | ); | |
| 798 | 816 | } | |
| 799 | 817 | const chain = this.#resolvers; | |
| 818 | + const context = { | ||
| 819 | + conditions: DEFAULT_CONDITIONS, | ||
| 820 | + importAssertions, | ||
| 821 | + parentURL, | ||
| 822 | + }; | ||
| 800 | 823 | const meta = { | |
| 801 | 824 | chainFinished: null, | |
| 825 | + context, | ||
| 802 | 826 | hookErrIdentifier: '', | |
| 803 | 827 | hookIndex: chain.length - 1, | |
| 804 | 828 | hookName: 'resolve', | |
| 805 | 829 | shortCircuited: false, | |
| 806 | 830 | }; | |
| 807 | 831 | ||
| 808 | - const context = { | ||
| 809 | - conditions: DEFAULT_CONDITIONS, | ||
| 810 | - importAssertions, | ||
| 811 | - parentURL, | ||
| 812 | - }; | ||
| 813 | - const validate = (hookErrIdentifier, { 0: suppliedSpecifier, 1: ctx }) => { | ||
| 814 | - | ||
| 832 | + const validateArgs = (hookErrIdentifier, { 0: suppliedSpecifier, 1: ctx }) => { | ||
| 815 | 833 | validateString( | |
| 816 | 834 | suppliedSpecifier, | |
| 817 | 835 | `${hookErrIdentifier} specifier`, | |
| 818 | 836 | ); // non-strings can be coerced to a url string | |
| 819 | 837 | ||
| 820 | - validateObject(ctx, `${hookErrIdentifier} context`); | ||
| 838 | + if (ctx) validateObject(ctx, `${hookErrIdentifier} context`); | ||
| 839 | + }; | ||
| 840 | + const validateOutput = (hookErrIdentifier, output) => { | ||
| 841 | + if (typeof output !== 'object' || output === null) { // [2] | ||
| 842 | + throw new ERR_INVALID_RETURN_VALUE( | ||
| 843 | + 'an object', | ||
| 844 | + hookErrIdentifier, | ||
| 845 | + output, | ||
| 846 | + ); | ||
| 847 | + } | ||
| 821 | 848 | }; | |
| 822 | 849 | ||
| 823 | - const nextResolve = nextHookFactory(chain, meta, validate); | ||
| 850 | + const nextResolve = nextHookFactory(chain, meta, { validateArgs, validateOutput }); | ||
| 824 | 851 | ||
| 825 | 852 | const resolution = await nextResolve(originalSpecifier, context); | |
| 826 | 853 | const { hookErrIdentifier } = meta; // Retrieve the value after all settled | |
| 827 | 854 | ||
| 828 | - if (typeof resolution !== 'object') { // [2] | ||
| 829 | - throw new ERR_INVALID_RETURN_VALUE( | ||
| 830 | - 'an object', | ||
| 831 | - hookErrIdentifier, | ||
| 832 | - resolution, | ||
| 833 | - ); | ||
| 834 | - } | ||
| 855 | + validateOutput(hookErrIdentifier, resolution); | ||
| 835 | 856 | ||
| 836 | 857 | if (resolution?.shortCircuit === true) { meta.shortCircuited = true; } | |
| 837 | 858 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -229,7 +229,7 @@ const commonArgs = [ | |||
| 229 | 229 | assert.strictEqual(status, 0); | |
| 230 | 230 | } | |
| 231 | 231 | ||
| 232 | - { // Verify chain does break and throws appropriately | ||
| 232 | + { // Verify resolve chain does break and throws appropriately | ||
| 233 | 233 | const { status, stderr, stdout } = spawnSync( | |
| 234 | 234 | process.execPath, | |
| 235 | 235 | [ | |
@@ -273,7 +273,7 @@ const commonArgs = [ | |||
| 273 | 273 | assert.strictEqual(status, 1); | |
| 274 | 274 | } | |
| 275 | 275 | ||
| 276 | - { // Verify chain does break and throws appropriately | ||
| 276 | + { // Verify load chain does break and throws appropriately | ||
| 277 | 277 | const { status, stderr, stdout } = spawnSync( | |
| 278 | 278 | process.execPath, | |
| 279 | 279 | [ | |
@@ -314,6 +314,27 @@ const commonArgs = [ | |||
| 314 | 314 | assert.match(stderr, /'resolve' hook's nextResolve\(\) specifier/); | |
| 315 | 315 | } | |
| 316 | 316 | ||
| 317 | + { // Verify error thrown when resolve hook is invalid | ||
| 318 | + const { status, stderr } = spawnSync( | ||
| 319 | + process.execPath, | ||
| 320 | + [ | ||
| 321 | + '--loader', | ||
| 322 | + fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'), | ||
| 323 | + '--loader', | ||
| 324 | + fixtures.fileURL('es-module-loaders', 'loader-resolve-null-return.mjs'), | ||
| 325 | + ...commonArgs, | ||
| 326 | + ], | ||
| 327 | + { encoding: 'utf8' }, | ||
| 328 | + ); | ||
| 329 | + | ||
| 330 | + assert.strictEqual(status, 1); | ||
| 331 | + assert.match(stderr, /ERR_INVALID_RETURN_VALUE/); | ||
| 332 | + assert.match(stderr, /loader-resolve-null-return\.mjs/); | ||
| 333 | + assert.match(stderr, /'resolve' hook's nextResolve\(\)/); | ||
| 334 | + assert.match(stderr, /an object/); | ||
| 335 | + assert.match(stderr, /got null/); | ||
| 336 | + } | ||
| 337 | + | ||
| 317 | 338 | { // Verify error thrown when invalid `context` argument passed to `nextResolve` | |
| 318 | 339 | const { status, stderr } = spawnSync( | |
| 319 | 340 | process.execPath, | |
@@ -333,6 +354,27 @@ const commonArgs = [ | |||
| 333 | 354 | assert.strictEqual(status, 1); | |
| 334 | 355 | } | |
| 335 | 356 | ||
| 357 | + { // Verify error thrown when load hook is invalid | ||
| 358 | + const { status, stderr } = spawnSync( | ||
| 359 | + process.execPath, | ||
| 360 | + [ | ||
| 361 | + '--loader', | ||
| 362 | + fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs'), | ||
| 363 | + '--loader', | ||
| 364 | + fixtures.fileURL('es-module-loaders', 'loader-load-null-return.mjs'), | ||
| 365 | + ...commonArgs, | ||
| 366 | + ], | ||
| 367 | + { encoding: 'utf8' }, | ||
| 368 | + ); | ||
| 369 | + | ||
| 370 | + assert.strictEqual(status, 1); | ||
| 371 | + assert.match(stderr, /ERR_INVALID_RETURN_VALUE/); | ||
| 372 | + assert.match(stderr, /loader-load-null-return\.mjs/); | ||
| 373 | + assert.match(stderr, /'load' hook's nextLoad\(\)/); | ||
| 374 | + assert.match(stderr, /an object/); | ||
| 375 | + assert.match(stderr, /got null/); | ||
| 376 | + } | ||
| 377 | + | ||
| 336 | 378 | { // Verify error thrown when invalid `url` argument passed to `nextLoad` | |
| 337 | 379 | const { status, stderr } = spawnSync( | |
| 338 | 380 | process.execPath, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + export async function load(specifier, context, next) { | ||
| 2 | + return null; | ||
| 3 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,5 +2,5 @@ export async function resolve(specifier, context, next) { | |||
| 2 | 2 | console.log('resolve 42'); // This log is deliberate | |
| 3 | 3 | console.log('next<HookName>:', next.name); // This log is deliberate | |
| 4 | 4 | ||
| 5 | - return next('file:///42.mjs', context); | ||
| 5 | + return next('file:///42.mjs'); | ||
| 6 | 6 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + export async function resolve(specifier, context, next) { | ||
| 2 | + return null; | ||
| 3 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments