| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 65dfe85 commit 63aa5d7
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,7 +22,7 @@ markBootstrapComplete(); | |||
| 22 | 22 | ||
| 23 | 23 | const source = getOptionValue('--eval'); | |
| 24 | 24 | const print = getOptionValue('--print'); | |
| 25 | - const loadESM = getOptionValue('--import').length > 0; | ||
| 25 | + const loadESM = getOptionValue('--import').length > 0 || getOptionValue('--experimental-loader').length > 0; | ||
| 26 | 26 | if (getOptionValue('--input-type') === 'module') | |
| 27 | 27 | evalModule(source, print); | |
| 28 | 28 | else | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,6 +10,7 @@ const { | |||
| 10 | 10 | Promise, | |
| 11 | 11 | SafeSet, | |
| 12 | 12 | StringPrototypeSlice, | |
| 13 | + StringPrototypeStartsWith, | ||
| 13 | 14 | StringPrototypeToUpperCase, | |
| 14 | 15 | globalThis, | |
| 15 | 16 | } = primordials; | |
@@ -30,6 +31,7 @@ const { | |||
| 30 | 31 | ERR_INVALID_RETURN_PROPERTY_VALUE, | |
| 31 | 32 | ERR_INVALID_RETURN_VALUE, | |
| 32 | 33 | ERR_LOADER_CHAIN_INCOMPLETE, | |
| 34 | + ERR_UNKNOWN_BUILTIN_MODULE, | ||
| 33 | 35 | ERR_WORKER_UNSERIALIZABLE_ERROR, | |
| 34 | 36 | } = require('internal/errors').codes; | |
| 35 | 37 | const { URL } = require('internal/url'); | |
@@ -520,14 +522,14 @@ class HooksProxy { | |||
| 520 | 522 | this.#worker.on('exit', process.exit); | |
| 521 | 523 | } | |
| 522 | 524 | ||
| 523 | - #waitForWorker() { | ||
| 525 | + waitForWorker() { | ||
| 524 | 526 | if (!this.#isReady) { | |
| 525 | 527 | const { kIsOnline } = require('internal/worker'); | |
| 526 | 528 | if (!this.#worker[kIsOnline]) { | |
| 527 | 529 | debug('wait for signal from worker'); | |
| 528 | 530 | AtomicsWait(this.#lock, WORKER_TO_MAIN_THREAD_NOTIFICATION, 0); | |
| 529 | 531 | const response = this.#worker.receiveMessageSync(); | |
| 530 | - if (response.message.status === 'exit') { return; } | ||
| 532 | + if (response == null || response.message.status === 'exit') { return; } | ||
| 531 | 533 | const { preloadScripts } = this.#unwrapMessage(response); | |
| 532 | 534 | this.#executePreloadScripts(preloadScripts); | |
| 533 | 535 | } | |
@@ -537,7 +539,7 @@ class HooksProxy { | |||
| 537 | 539 | } | |
| 538 | 540 | ||
| 539 | 541 | async makeAsyncRequest(method, ...args) { | |
| 540 | - this.#waitForWorker(); | ||
| 542 | + this.waitForWorker(); | ||
| 541 | 543 | ||
| 542 | 544 | MessageChannel ??= require('internal/worker/io').MessageChannel; | |
| 543 | 545 | const asyncCommChannel = new MessageChannel(); | |
@@ -577,7 +579,7 @@ class HooksProxy { | |||
| 577 | 579 | } | |
| 578 | 580 | ||
| 579 | 581 | makeSyncRequest(method, ...args) { | |
| 580 | - this.#waitForWorker(); | ||
| 582 | + this.waitForWorker(); | ||
| 581 | 583 | ||
| 582 | 584 | // Pass work to the worker. | |
| 583 | 585 | debug('post sync message to worker', { method, args }); | |
@@ -619,35 +621,66 @@ class HooksProxy { | |||
| 619 | 621 | } | |
| 620 | 622 | } | |
| 621 | 623 | ||
| 624 | + #importMetaInitializer = require('internal/modules/esm/initialize_import_meta').initializeImportMeta; | ||
| 625 | + | ||
| 626 | + importMetaInitialize(meta, context, loader) { | ||
| 627 | + this.#importMetaInitializer(meta, context, loader); | ||
| 628 | + } | ||
| 629 | + | ||
| 622 | 630 | #executePreloadScripts(preloadScripts) { | |
| 623 | 631 | for (let i = 0; i < preloadScripts.length; i++) { | |
| 624 | 632 | const { code, port } = preloadScripts[i]; | |
| 625 | 633 | const { compileFunction } = require('vm'); | |
| 626 | 634 | const preloadInit = compileFunction( | |
| 627 | 635 | code, | |
| 628 | - ['getBuiltin', 'port'], | ||
| 636 | + ['getBuiltin', 'port', 'setImportMetaCallback'], | ||
| 629 | 637 | { | |
| 630 | 638 | filename: '<preload>', | |
| 631 | 639 | }, | |
| 632 | 640 | ); | |
| 641 | + let finished = false; | ||
| 642 | + let replacedImportMetaInitializer = false; | ||
| 643 | + let next = this.#importMetaInitializer; | ||
| 633 | 644 | const { BuiltinModule } = require('internal/bootstrap/realm'); | |
| 634 | 645 | // Calls the compiled preload source text gotten from the hook | |
| 635 | 646 | // Since the parameters are named we use positional parameters | |
| 636 | 647 | // see compileFunction above to cross reference the names | |
| 637 | - FunctionPrototypeCall( | ||
| 638 | - preloadInit, | ||
| 639 | - globalThis, | ||
| 640 | - // Param getBuiltin | ||
| 641 | - (builtinName) => { | ||
| 642 | - if (BuiltinModule.canBeRequiredByUsers(builtinName) && | ||
| 643 | - BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { | ||
| 644 | - return require(builtinName); | ||
| 645 | - } | ||
| 646 | - throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); | ||
| 647 | - }, | ||
| 648 | - // Param port | ||
| 649 | - port, | ||
| 650 | - ); | ||
| 648 | + try { | ||
| 649 | + FunctionPrototypeCall( | ||
| 650 | + preloadInit, | ||
| 651 | + globalThis, | ||
| 652 | + // Param getBuiltin | ||
| 653 | + (builtinName) => { | ||
| 654 | + if (StringPrototypeStartsWith(builtinName, 'node:')) { | ||
| 655 | + builtinName = StringPrototypeSlice(builtinName, 5); | ||
| 656 | + } else if (!BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { | ||
| 657 | + throw new ERR_UNKNOWN_BUILTIN_MODULE(builtinName); | ||
| 658 | + } | ||
| 659 | + if (BuiltinModule.canBeRequiredByUsers(builtinName)) { | ||
| 660 | + return require(builtinName); | ||
| 661 | + } | ||
| 662 | + throw new ERR_UNKNOWN_BUILTIN_MODULE(builtinName); | ||
| 663 | + }, | ||
| 664 | + // Param port | ||
| 665 | + port, | ||
| 666 | + // setImportMetaCallback | ||
| 667 | + (fn) => { | ||
| 668 | + if (finished || typeof fn !== 'function') { | ||
| 669 | + throw new ERR_INVALID_ARG_TYPE('fn', fn); | ||
| 670 | + } | ||
| 671 | + replacedImportMetaInitializer = true; | ||
| 672 | + const parent = next; | ||
| 673 | + next = (meta, context) => { | ||
| 674 | + return fn(meta, context, parent); | ||
| 675 | + }; | ||
| 676 | + }, | ||
| 677 | + ); | ||
| 678 | + } finally { | ||
| 679 | + finished = true; | ||
| 680 | + if (replacedImportMetaInitializer) { | ||
| 681 | + this.#importMetaInitializer = next; | ||
| 682 | + } | ||
| 683 | + } | ||
| 651 | 684 | } | |
| 652 | 685 | } | |
| 653 | 686 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -287,6 +287,11 @@ class DefaultModuleLoader { | |||
| 287 | 287 | meta = importMetaInitializer(meta, context, this); | |
| 288 | 288 | return meta; | |
| 289 | 289 | } | |
| 290 | + | ||
| 291 | + /** | ||
| 292 | + * No-op when no hooks have been supplied. | ||
| 293 | + */ | ||
| 294 | + forceLoadHooks() {} | ||
| 290 | 295 | } | |
| 291 | 296 | ObjectSetPrototypeOf(DefaultModuleLoader.prototype, null); | |
| 292 | 297 | ||
@@ -359,6 +364,14 @@ class CustomizedModuleLoader extends DefaultModuleLoader { | |||
| 359 | 364 | ||
| 360 | 365 | return result; | |
| 361 | 366 | } | |
| 367 | + | ||
| 368 | + importMetaInitialize(meta, context) { | ||
| 369 | + hooksProxy.importMetaInitialize(meta, context, this); | ||
| 370 | + } | ||
| 371 | + | ||
| 372 | + forceLoadHooks() { | ||
| 373 | + hooksProxy.waitForWorker(); | ||
| 374 | + } | ||
| 362 | 375 | } | |
| 363 | 376 | ||
| 364 | 377 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,6 +36,8 @@ module.exports = { | |||
| 36 | 36 | parentURL, | |
| 37 | 37 | kEmptyObject, | |
| 38 | 38 | )); | |
| 39 | + } else { | ||
| 40 | + esmLoader.forceLoadHooks(); | ||
| 39 | 41 | } | |
| 40 | 42 | await callback(esmLoader); | |
| 41 | 43 | } catch (err) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,7 @@ | |||
| 1 | 1 | import { spawnPromisified } from '../common/index.mjs'; | |
| 2 | 2 | import * as fixtures from '../common/fixtures.mjs'; | |
| 3 | 3 | import assert from 'node:assert'; | |
| 4 | + import os from 'node:os'; | ||
| 4 | 5 | import { execPath } from 'node:process'; | |
| 5 | 6 | import { describe, it } from 'node:test'; | |
| 6 | 7 | ||
@@ -370,18 +371,119 @@ describe('Loader hooks', { concurrency: true }, () => { | |||
| 370 | 371 | }); | |
| 371 | 372 | }); | |
| 372 | 373 | ||
| 373 | - it('should handle globalPreload returning undefined', async () => { | ||
| 374 | - const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 375 | - '--no-warnings', | ||
| 376 | - '--experimental-loader', | ||
| 377 | - 'data:text/javascript,export function globalPreload(){}', | ||
| 378 | - fixtures.path('empty.js'), | ||
| 379 | - ]); | ||
| 374 | + describe('globalPreload', () => { | ||
| 375 | + it('should handle globalPreload returning undefined', async () => { | ||
| 376 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 377 | + '--no-warnings', | ||
| 378 | + '--experimental-loader', | ||
| 379 | + 'data:text/javascript,export function globalPreload(){}', | ||
| 380 | + fixtures.path('empty.js'), | ||
| 381 | + ]); | ||
| 380 | 382 | ||
| 381 | - assert.strictEqual(stderr, ''); | ||
| 382 | - assert.strictEqual(stdout, ''); | ||
| 383 | - assert.strictEqual(code, 0); | ||
| 384 | - assert.strictEqual(signal, null); | ||
| 383 | + assert.strictEqual(stderr, ''); | ||
| 384 | + assert.strictEqual(stdout, ''); | ||
| 385 | + assert.strictEqual(code, 0); | ||
| 386 | + assert.strictEqual(signal, null); | ||
| 387 | + }); | ||
| 388 | + | ||
| 389 | + it('should handle loading node:test', async () => { | ||
| 390 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 391 | + '--no-warnings', | ||
| 392 | + '--experimental-loader', | ||
| 393 | + 'data:text/javascript,export function globalPreload(){return `getBuiltin("node:test")()`}', | ||
| 394 | + fixtures.path('empty.js'), | ||
| 395 | + ]); | ||
| 396 | + | ||
| 397 | + assert.strictEqual(stderr, ''); | ||
| 398 | + assert.match(stdout, /\n# pass 1\r?\n/); | ||
| 399 | + assert.strictEqual(code, 0); | ||
| 400 | + assert.strictEqual(signal, null); | ||
| 401 | + }); | ||
| 402 | + | ||
| 403 | + it('should handle loading node:os with node: prefix', async () => { | ||
| 404 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 405 | + '--no-warnings', | ||
| 406 | + '--experimental-loader', | ||
| 407 | + 'data:text/javascript,export function globalPreload(){return `console.log(getBuiltin("node:os").arch())`}', | ||
| 408 | + fixtures.path('empty.js'), | ||
| 409 | + ]); | ||
| 410 | + | ||
| 411 | + assert.strictEqual(stderr, ''); | ||
| 412 | + assert.strictEqual(stdout.trim(), os.arch()); | ||
| 413 | + assert.strictEqual(code, 0); | ||
| 414 | + assert.strictEqual(signal, null); | ||
| 415 | + }); | ||
| 416 | + | ||
| 417 | + // `os` is used here because it's simple and not mocked (the builtin module otherwise doesn't matter). | ||
| 418 | + it('should handle loading builtin module without node: prefix', async () => { | ||
| 419 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 420 | + '--no-warnings', | ||
| 421 | + '--experimental-loader', | ||
| 422 | + 'data:text/javascript,export function globalPreload(){return `console.log(getBuiltin("os").arch())`}', | ||
| 423 | + fixtures.path('empty.js'), | ||
| 424 | + ]); | ||
| 425 | + | ||
| 426 | + assert.strictEqual(stderr, ''); | ||
| 427 | + assert.strictEqual(stdout.trim(), os.arch()); | ||
| 428 | + assert.strictEqual(code, 0); | ||
| 429 | + assert.strictEqual(signal, null); | ||
| 430 | + }); | ||
| 431 | + | ||
| 432 | + it('should throw when loading node:test without node: prefix', async () => { | ||
| 433 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 434 | + '--no-warnings', | ||
| 435 | + '--experimental-loader', | ||
| 436 | + 'data:text/javascript,export function globalPreload(){return `getBuiltin("test")()`}', | ||
| 437 | + fixtures.path('empty.js'), | ||
| 438 | + ]); | ||
| 439 | + | ||
| 440 | + assert.match(stderr, /ERR_UNKNOWN_BUILTIN_MODULE/); | ||
| 441 | + assert.strictEqual(stdout, ''); | ||
| 442 | + assert.strictEqual(code, 1); | ||
| 443 | + assert.strictEqual(signal, null); | ||
| 444 | + }); | ||
| 445 | + | ||
| 446 | + it('should register globals set from globalPreload', async () => { | ||
| 447 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 448 | + '--no-warnings', | ||
| 449 | + '--experimental-loader', | ||
| 450 | + 'data:text/javascript,export function globalPreload(){return "this.myGlobal=4"}', | ||
| 451 | + '--print', 'myGlobal', | ||
| 452 | + ]); | ||
| 453 | + | ||
| 454 | + assert.strictEqual(stderr, ''); | ||
| 455 | + assert.strictEqual(stdout.trim(), '4'); | ||
| 456 | + assert.strictEqual(code, 0); | ||
| 457 | + assert.strictEqual(signal, null); | ||
| 458 | + }); | ||
| 459 | + | ||
| 460 | + it('should log console.log calls returned from globalPreload', async () => { | ||
| 461 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 462 | + '--no-warnings', | ||
| 463 | + '--experimental-loader', | ||
| 464 | + 'data:text/javascript,export function globalPreload(){return `console.log("Hello from globalPreload")`}', | ||
| 465 | + fixtures.path('empty.js'), | ||
| 466 | + ]); | ||
| 467 | + | ||
| 468 | + assert.strictEqual(stderr, ''); | ||
| 469 | + assert.strictEqual(stdout.trim(), 'Hello from globalPreload'); | ||
| 470 | + assert.strictEqual(code, 0); | ||
| 471 | + assert.strictEqual(signal, null); | ||
| 472 | + }); | ||
| 473 | + | ||
| 474 | + it('should crash if globalPreload returns code that throws', async () => { | ||
| 475 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | ||
| 476 | + '--no-warnings', | ||
| 477 | + '--experimental-loader', | ||
| 478 | + 'data:text/javascript,export function globalPreload(){return `throw new Error("error from globalPreload")`}', | ||
| 479 | + fixtures.path('empty.js'), | ||
| 480 | + ]); | ||
| 481 | + | ||
| 482 | + assert.match(stderr, /error from globalPreload/); | ||
| 483 | + assert.strictEqual(stdout, ''); | ||
| 484 | + assert.strictEqual(code, 1); | ||
| 485 | + assert.strictEqual(signal, null); | ||
| 486 | + }); | ||
| 385 | 487 | }); | |
| 386 | 488 | ||
| 387 | 489 | it('should be fine to call `process.removeAllListeners("beforeExit")` from the main thread', async () => { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,45 @@ | |||
| 1 | + // Flags: --loader ./test/fixtures/es-module-loaders/mock-loader.mjs | ||
| 2 | + import '../common/index.mjs'; | ||
| 3 | + import assert from 'assert/strict'; | ||
| 4 | + | ||
| 5 | + // This is provided by test/fixtures/es-module-loaders/mock-loader.mjs | ||
| 6 | + import mock from 'node:mock'; | ||
| 7 | + | ||
| 8 | + mock('node:events', { | ||
| 9 | + EventEmitter: 'This is mocked!' | ||
| 10 | + }); | ||
| 11 | + | ||
| 12 | + // This resolves to node:events | ||
| 13 | + // It is intercepted by mock-loader and doesn't return the normal value | ||
| 14 | + assert.deepStrictEqual(await import('events'), Object.defineProperty({ | ||
| 15 | + __proto__: null, | ||
| 16 | + EventEmitter: 'This is mocked!' | ||
| 17 | + }, Symbol.toStringTag, { | ||
| 18 | + enumerable: false, | ||
| 19 | + value: 'Module' | ||
| 20 | + })); | ||
| 21 | + | ||
| 22 | + const mutator = mock('node:events', { | ||
| 23 | + EventEmitter: 'This is mocked v2!' | ||
| 24 | + }); | ||
| 25 | + | ||
| 26 | + // It is intercepted by mock-loader and doesn't return the normal value. | ||
| 27 | + // This is resolved separately from the import above since the specifiers | ||
| 28 | + // are different. | ||
| 29 | + const mockedV2 = await import('node:events'); | ||
| 30 | + assert.deepStrictEqual(mockedV2, Object.defineProperty({ | ||
| 31 | + __proto__: null, | ||
| 32 | + EventEmitter: 'This is mocked v2!' | ||
| 33 | + }, Symbol.toStringTag, { | ||
| 34 | + enumerable: false, | ||
| 35 | + value: 'Module' | ||
| 36 | + })); | ||
| 37 | + | ||
| 38 | + mutator.EventEmitter = 'This is mocked v3!'; | ||
| 39 | + assert.deepStrictEqual(mockedV2, Object.defineProperty({ | ||
| 40 | + __proto__: null, | ||
| 41 | + EventEmitter: 'This is mocked v3!' | ||
| 42 | + }, Symbol.toStringTag, { | ||
| 43 | + enumerable: false, | ||
| 44 | + value: 'Module' | ||
| 45 | + })); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments