mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
test: split test-esm-loader-hooks
Previously whenever one of the test case fails in the CI, it barely logged anything useful in the CI and it was difficult to nail down the specific failing case with a local reproduction, especially when the test fixutre is inline JavaScript. This patch: - Puts all the inline JavaScript in on-disk fixtures so that they can be re-run easily. - Split the tests into individual files so that it's easier to nail down the failure - Use spawnSyncAndAssert which logs useful information when the child process does not behave as expected. - Rename the tests as module-hooks/test-async-loader-hooks-* because they belong to the module hooks test suite and are not esm-specific. PR-URL: https://github.com/nodejs/node/pull/61374 Refs: https://github.com/nodejs/reliability/blob/main/reports/2026-01-13.md Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
This commit is contained in:
1 parent
ae001bb4b7
commit
3da4dce296
81 files changed
+1227
-860
No files matched your search
@@ -1,860 +0,0 @@
|
||||
import { spawnPromisified } from '../common/index.mjs';
|
||||
import * as fixtures from '../common/fixtures.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
describe('Loader hooks', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
||||
it('are called with all expected arguments', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/hooks-input.mjs'),
|
||||
fixtures.path('es-modules/json-modules.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
|
||||
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
assert.strictEqual(lines[4], '');
|
||||
assert.strictEqual(lines.length, 5);
|
||||
});
|
||||
|
||||
it('are called with all expected arguments using register function', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader=data:text/javascript,',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
`register(${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-input.mjs'))});` +
|
||||
`await import(${JSON.stringify(fixtures.fileURL('es-modules/json-modules.mjs'))});`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
|
||||
const lines = stdout.split('\n');
|
||||
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
|
||||
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
assert.strictEqual(lines[4], '');
|
||||
assert.strictEqual(lines.length, 5);
|
||||
});
|
||||
|
||||
describe('should handle never-settling hooks in ESM files', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
||||
it('top-level await of a never-settling resolve without warning', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 13);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('top-level await of a never-settling resolve with warning', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /Warning: Detected unsettled top-level await at.+never-resolve\.mjs:5/);
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 13);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('top-level await of a never-settling load without warning', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 13);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('top-level await of a never-settling load with warning', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /Warning: Detected unsettled top-level await at.+never-load\.mjs:5/);
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 13);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('top-level await of a race of never-settling hooks', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/race.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^true\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('import.meta.resolve of a never-settling resolve should throw', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/import.meta.never-resolve.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should handle never-settling hooks in CJS files', { concurrency: !process.env.TEST_PARALLEL }, () => {
|
||||
it('never-settling resolve', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.cjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
|
||||
it('never-settling load', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.cjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^should be output\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('race of never-settling hooks', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/race.cjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^true\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not work without worker permission', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('empty.js'),
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /Error: Access to this API has been restricted/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should allow loader hooks to spawn workers when allowed by the CLI flags', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-worker',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
`data:text/javascript,import{Worker}from"worker_threads";new Worker(${encodeURIComponent(JSON.stringify(fixtures.path('empty.js')))}).unref()`,
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.match(stdout, /^1\r?\n2\r?\n$/);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should not allow loader hooks to spawn workers if restricted by the CLI flags', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
`data:text/javascript,import{Worker}from"worker_threads";new Worker(${encodeURIComponent(JSON.stringify(fixtures.path('empty.js')))}).unref()`,
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /code: 'ERR_ACCESS_DENIED'/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should not leak internals or expose import.meta.resolve', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-edge-cases.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should be fine to call `process.exit` from a custom async hook', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function load(a,b,next){if(a==="data:exit")process.exit(42);return next(a,b)}',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "data:exit"',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 42);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should be fine to call `process.exit` from a custom sync hook', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function resolve(a,b,next){if(a==="exit:")process.exit(42);return next(a,b)}',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "data:text/javascript,import.meta.resolve(%22exit:%22)"',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 42);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should be fine to call `process.exit` from the loader thread top-level', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,process.exit(42)',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 42);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
describe('should handle a throwing top-level body', () => {
|
||||
it('should handle regular Error object', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw new Error("error message")',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /Error: error message\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle null', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw null',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\nnull\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle undefined', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw undefined',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\nundefined\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle boolean', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw true',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\ntrue\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle empty plain object', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw {}',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\n\{\}\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle plain object', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw {fn(){},symbol:Symbol("symbol"),u:undefined}',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\n\{ fn: \[Function: fn\], symbol: Symbol\(symbol\), u: undefined \}\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle number', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw 1',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\n1\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle bigint', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw 1n',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\n1\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle string', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw "literal string"',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\nliteral string\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle symbol', async () => {
|
||||
const { code, signal, stdout } = await spawnPromisified(execPath, [
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw Symbol("symbol descriptor")',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
// Throwing a symbol doesn't produce any output
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle function', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,throw function fnName(){}',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.match(stderr, /\n\[Function: fnName\]\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('globalPreload', () => {
|
||||
it('should emit warning', async () => {
|
||||
const { stderr } = await spawnPromisified(execPath, [
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function globalPreload(){}',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function globalPreload(){return""}',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr.match(/`globalPreload` has been removed; use `initialize` instead/g).length, 1);
|
||||
});
|
||||
|
||||
it('should not emit warning when initialize is supplied', async () => {
|
||||
const { stderr } = await spawnPromisified(execPath, [
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function globalPreload(){}export function initialize(){}',
|
||||
fixtures.path('empty.js'),
|
||||
]);
|
||||
|
||||
assert.doesNotMatch(stderr, /`globalPreload` has been removed; use `initialize` instead/);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be fine to call `process.removeAllListeners("beforeExit")` from the main thread', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'data:text/javascript,export function load(a,b,c){return new Promise(d=>setTimeout(()=>d(c(a,b)),99))}',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'setInterval(() => process.removeAllListeners("beforeExit"),1).unref();await import("data:text/javascript,")',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
describe('`initialize`/`register`', () => {
|
||||
it('should invoke `initialize` correctly', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import os from "node:os";',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n'), ['hooks initialize 1', '']);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should allow communicating with loader via `register` ports', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {MessageChannel} from 'node:worker_threads';
|
||||
import {register} from 'node:module';
|
||||
import {once} from 'node:events';
|
||||
const {port1, port2} = new MessageChannel();
|
||||
port1.on('message', (msg) => {
|
||||
console.log('message', msg);
|
||||
});
|
||||
const result = register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize-port.mjs'))},
|
||||
{data: port2, transferList: [port2]},
|
||||
);
|
||||
console.log('register', result);
|
||||
|
||||
const timeout = setTimeout(() => {}, 2**31 - 1); // to keep the process alive.
|
||||
await Promise.all([
|
||||
once(port1, 'message').then(() => once(port1, 'message')),
|
||||
import('node:os'),
|
||||
]);
|
||||
clearTimeout(timeout);
|
||||
port1.close();
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n'), [ 'register undefined',
|
||||
'message initialize',
|
||||
'message resolve node:os',
|
||||
'' ]);
|
||||
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should have `register` accept URL objects as `parentURL`', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--import',
|
||||
`data:text/javascript,${encodeURIComponent(
|
||||
'import{ register } from "node:module";' +
|
||||
'import { pathToFileURL } from "node:url";' +
|
||||
'register("./hooks-initialize.mjs", pathToFileURL("./"));'
|
||||
)}`,
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'))},
|
||||
new URL('data:'),
|
||||
);
|
||||
|
||||
import('node:os').then((result) => {
|
||||
console.log(JSON.stringify(result));
|
||||
});
|
||||
`,
|
||||
], { cwd: fixtures.fileURL('es-module-loaders/') });
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort());
|
||||
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should have `register` work with cjs', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=commonjs',
|
||||
'--eval',
|
||||
`
|
||||
'use strict';
|
||||
const {register} = require('node:module');
|
||||
register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))},
|
||||
);
|
||||
register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'))},
|
||||
);
|
||||
|
||||
import('node:os').then((result) => {
|
||||
console.log(JSON.stringify(result));
|
||||
});
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort());
|
||||
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('`register` should work with `require`', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--require',
|
||||
fixtures.path('es-module-loaders/register-loader.cjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os";',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n'), ['resolve passthru', 'resolve passthru', '']);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('`register` should work with `import`', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/register-loader.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os"',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n'), ['resolve passthru', '']);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should execute `initialize` in sequence', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
console.log('result 1', register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))}
|
||||
));
|
||||
console.log('result 2', register(
|
||||
${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'))}
|
||||
));
|
||||
|
||||
await import('node:os');
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.deepStrictEqual(stdout.split('\n'), [ 'hooks initialize 1',
|
||||
'result 1 undefined',
|
||||
'hooks initialize 2',
|
||||
'result 2 undefined',
|
||||
'' ]);
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle `initialize` returning never-settling promise', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
try {
|
||||
register('data:text/javascript,export function initialize(){return new Promise(()=>{})}');
|
||||
} catch (e) {
|
||||
console.log('caught', e.code);
|
||||
}
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout.trim(), 'caught ERR_ASYNC_LOADER_REQUEST_NEVER_SETTLED');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle `initialize` returning rejecting promise', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
register('data:text/javascript,export function initialize(){return Promise.reject()}');
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.match(stderr, /undefined\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle `initialize` throwing null', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
register('data:text/javascript,export function initialize(){throw null}');
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.match(stderr, /null\r?\n/);
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should be fine to call `process.exit` from a initialize hook', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
`
|
||||
import {register} from 'node:module';
|
||||
register('data:text/javascript,export function initialize(){process.exit(42);}');
|
||||
`,
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 42);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use CJS loader to respond to require.resolve calls by default', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'),
|
||||
fixtures.path('require-resolve.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, 'resolve passthru\n');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should use ESM loader to respond to require.resolve calls when opting in', async () => {
|
||||
const readFile = async () => {};
|
||||
const fileURLToPath = () => {};
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
`data:text/javascript,import{readFile}from"node:fs/promises";import{fileURLToPath}from"node:url";export ${
|
||||
async function load(url, context, nextLoad) {
|
||||
const result = await nextLoad(url, context);
|
||||
if (url.endsWith('/common/index.js')) {
|
||||
result.source = '"use strict";module.exports=require("node:module").createRequire(' +
|
||||
`${JSON.stringify(url)})(${JSON.stringify(fileURLToPath(url))});\n`;
|
||||
} else if (url.startsWith('file:') && (context.format == null || context.format === 'commonjs')) {
|
||||
result.source = await readFile(new URL(url));
|
||||
}
|
||||
return result;
|
||||
}}`,
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'),
|
||||
fixtures.path('require-resolve.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, 'resolve passthru\n'.repeat(10));
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should use hooks', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
|
||||
'--no-experimental-require-module',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/builtin-named-exports.mjs'),
|
||||
fixtures.path('es-modules/require-esm-throws-with-loaders.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should support source maps in commonjs translator', async () => {
|
||||
const readFile = async () => {};
|
||||
const hook = `
|
||||
import { readFile } from 'node:fs/promises';
|
||||
export ${
|
||||
async function load(url, context, nextLoad) {
|
||||
const resolved = await nextLoad(url, context);
|
||||
if (context.format === 'commonjs') {
|
||||
resolved.source = await readFile(new URL(url));
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
}`;
|
||||
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--enable-source-maps',
|
||||
'--import',
|
||||
`data:text/javascript,${encodeURIComponent(`
|
||||
import{ register } from "node:module";
|
||||
register(${
|
||||
JSON.stringify('data:text/javascript,' + encodeURIComponent(hook))
|
||||
});
|
||||
`)}`,
|
||||
fixtures.path('source-map/throw-on-require.js'),
|
||||
]);
|
||||
|
||||
assert.strictEqual(stdout, '');
|
||||
assert.match(stderr, /throw-on-require\.ts:9:9/);
|
||||
assert.strictEqual(code, 1);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
|
||||
it('should handle mixed of opt-in modules and non-opt-in ones', async () => {
|
||||
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
`data:text/javascript,const fixtures=${encodeURI(JSON.stringify(fixtures.path('empty.js')))};export ${
|
||||
encodeURIComponent(function resolve(s, c, n) {
|
||||
if (s.endsWith('entry-point')) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: 'file:///c:/virtual-entry-point',
|
||||
format: 'commonjs',
|
||||
};
|
||||
}
|
||||
return n(s, c);
|
||||
})
|
||||
}export ${
|
||||
encodeURIComponent(async function load(u, c, n) {
|
||||
if (u === 'file:///c:/virtual-entry-point') {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
source: `"use strict";require(${JSON.stringify(fixtures)});console.log("Hello");`,
|
||||
format: 'commonjs',
|
||||
};
|
||||
}
|
||||
return n(u, c);
|
||||
})}`,
|
||||
'entry-point',
|
||||
]);
|
||||
|
||||
assert.strictEqual(stderr, '');
|
||||
assert.strictEqual(stdout, 'Hello\n');
|
||||
assert.strictEqual(code, 0);
|
||||
assert.strictEqual(signal, null);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
export function load(a, b, c) {
|
||||
return new Promise(d => setTimeout(() => d(c(a, b)), 99));
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export function load(a, b, next) {
|
||||
if (a === 'data:exit') process.exit(42);
|
||||
return next(a, b);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export function resolve(a, b, next) {
|
||||
if (a === 'exit:') process.exit(42);
|
||||
return next(a, b);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
process.exit(42);
|
||||
@@ -0,0 +1,2 @@
|
||||
export function globalPreload() {}
|
||||
export function initialize() {}
|
||||
@@ -0,0 +1 @@
|
||||
export function globalPreload() {}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function globalPreload() {
|
||||
return '';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function initialize() {
|
||||
process.exit(42);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function initialize() {
|
||||
return new Promise(() => {});
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function initialize() {
|
||||
return Promise.reject();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function initialize() {
|
||||
throw null;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
const result = await nextLoad(url, context);
|
||||
if (url.endsWith('/common/index.js')) {
|
||||
result.source = '"use strict";module.exports=require("node:module").createRequire(' +
|
||||
JSON.stringify(url) + ')(' + JSON.stringify(fileURLToPath(url)) + ');\n';
|
||||
} else if (url.startsWith('file:') && (context.format == null || context.format === 'commonjs')) {
|
||||
result.source = await readFile(new URL(url));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
const resolved = await nextLoad(url, context);
|
||||
if (context.format === 'commonjs') {
|
||||
resolved.source = await readFile(new URL(url));
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const fixtures = require('../../common/fixtures.js');
|
||||
|
||||
const fixturesPath = fixtures.path('empty.js');
|
||||
|
||||
export function resolve(s, c, n) {
|
||||
if (s.endsWith('entry-point')) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: 'file:///c:/virtual-entry-point',
|
||||
format: 'commonjs',
|
||||
};
|
||||
}
|
||||
return n(s, c);
|
||||
}
|
||||
|
||||
export async function load(u, c, n) {
|
||||
if (u === 'file:///c:/virtual-entry-point') {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
source: `"use strict";require(${JSON.stringify(fixturesPath)});console.log("Hello");`,
|
||||
format: 'commonjs',
|
||||
};
|
||||
}
|
||||
return n(u, c);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
throw 1n;
|
||||
@@ -0,0 +1 @@
|
||||
throw true;
|
||||
@@ -0,0 +1 @@
|
||||
throw {};
|
||||
@@ -0,0 +1 @@
|
||||
throw new Error('error message');
|
||||
@@ -0,0 +1 @@
|
||||
throw function fnName() {};
|
||||
@@ -0,0 +1 @@
|
||||
throw null;
|
||||
@@ -0,0 +1 @@
|
||||
throw 1;
|
||||
@@ -0,0 +1 @@
|
||||
throw { fn() {}, symbol: Symbol('symbol'), u: undefined };
|
||||
@@ -0,0 +1 @@
|
||||
throw 'literal string';
|
||||
@@ -0,0 +1 @@
|
||||
throw Symbol('symbol descriptor');
|
||||
@@ -0,0 +1 @@
|
||||
throw undefined;
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Worker } from 'worker_threads';
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const fixtures = require('../../common/fixtures.js');
|
||||
|
||||
new Worker(fixtures.path('empty.js')).unref();
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { register } from 'node:module';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
register('./hooks-initialize.mjs', pathToFileURL('./'));
|
||||
@@ -0,0 +1,11 @@
|
||||
import {register} from 'node:module';
|
||||
import fixtures from '../../common/fixtures.js';
|
||||
|
||||
register(
|
||||
fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'),
|
||||
new URL('data:'),
|
||||
);
|
||||
|
||||
import('node:os').then((result) => {
|
||||
console.log(JSON.stringify(result));
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import {register} from 'node:module';
|
||||
import fixtures from '../../common/fixtures.js';
|
||||
|
||||
console.log('result 1', register(
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize.mjs')
|
||||
));
|
||||
console.log('result 2', register(
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize.mjs')
|
||||
));
|
||||
|
||||
await import('node:os');
|
||||
@@ -0,0 +1,8 @@
|
||||
import {register} from 'node:module';
|
||||
import fixtures from '../../common/fixtures.js';
|
||||
|
||||
try {
|
||||
register(fixtures.fileURL('es-module-loaders/loader-initialize-never-settling.mjs'));
|
||||
} catch (e) {
|
||||
console.log('caught', e.code);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
const {register} = require('node:module');
|
||||
const fixtures = require('../../common/fixtures.js');
|
||||
|
||||
register(
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'),
|
||||
);
|
||||
register(
|
||||
fixtures.fileURL('es-module-loaders/loader-load-foo-or-42.mjs'),
|
||||
);
|
||||
|
||||
import('node:os').then((result) => {
|
||||
console.log(JSON.stringify(result));
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import {MessageChannel} from 'node:worker_threads';
|
||||
import {register} from 'node:module';
|
||||
import {once} from 'node:events';
|
||||
import fixtures from '../../common/fixtures.js';
|
||||
|
||||
const {port1, port2} = new MessageChannel();
|
||||
port1.on('message', (msg) => {
|
||||
console.log('message', msg);
|
||||
});
|
||||
const result = register(
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize-port.mjs'),
|
||||
{data: port2, transferList: [port2]},
|
||||
);
|
||||
console.log('register', result);
|
||||
|
||||
const timeout = setTimeout(() => {}, 2**31 - 1); // to keep the process alive.
|
||||
await Promise.all([
|
||||
once(port1, 'message').then(() => once(port1, 'message')),
|
||||
import('node:os'),
|
||||
]);
|
||||
clearTimeout(timeout);
|
||||
port1.close();
|
||||
@@ -0,0 +1,28 @@
|
||||
// Test that loader hooks are called with all expected arguments
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/hooks-input.mjs'),
|
||||
fixtures.path('es-modules/json-modules.mjs'),
|
||||
],
|
||||
{
|
||||
stdout(output) {
|
||||
const lines = output.split('\n');
|
||||
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
|
||||
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
assert.strictEqual(lines.length, 4);
|
||||
},
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
// Test that loader hooks are called with all expected arguments using register function
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader=data:text/javascript,',
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
"import { register } from 'node:module';" +
|
||||
`register(${JSON.stringify(fixtures.fileURL('es-module-loaders/hooks-input.mjs'))});` +
|
||||
`await import(${JSON.stringify(fixtures.fileURL('es-modules/json-modules.mjs'))});`,
|
||||
],
|
||||
{
|
||||
stdout(output) {
|
||||
const lines = output.split('\n');
|
||||
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
|
||||
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
|
||||
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
|
||||
assert.strictEqual(lines.length, 4);
|
||||
},
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// Test that `globalPreload` should not emit warning when `initialize` is supplied
|
||||
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-globalpreload-and-initialize.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
stderr(output) {
|
||||
assert.doesNotMatch(output, /`globalPreload` has been removed; use `initialize` instead/);
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
// Test that `globalPreload` should emit warning
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-globalpreload-only.mjs'),
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-globalpreload-with-return.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
stderr(output) {
|
||||
const matches = output.match(/`globalPreload` has been removed; use `initialize` instead/g);
|
||||
assert.strictEqual(matches.length, 1);
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
// Test that `initialize` should execute in sequence
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
fixtures.path('module-hooks/register-loader-in-sequence.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: 'hooks initialize 1\n' +
|
||||
'result 1 undefined\n' +
|
||||
'hooks initialize 2\n' +
|
||||
'result 2 undefined',
|
||||
trim: true,
|
||||
stderr: '',
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that `initialize` should be invoked correctly
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/hooks-initialize.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import os from "node:os";',
|
||||
],
|
||||
{
|
||||
stdout: 'hooks initialize 1',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
// Test that `initialize` returning never-settling promise should be handled
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
fixtures.path('module-hooks/register-loader-initialize-never-settling.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: 'caught ERR_ASYNC_LOADER_REQUEST_NEVER_SETTLED',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
// Test that it should be fine to call `process.exit` from a `initialize` hook
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-initialize-exit.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os"',
|
||||
],
|
||||
{
|
||||
status: 42,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test that `initialize` returning rejecting promise should be handled
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-initialize-rejecting.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os"',
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /undefined$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test that `initialize` throwing null should be handled
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-initialize-throw-null.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os"',
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /null$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that loader should handle mixed of opt-in modules and non-opt-in ones
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-mixed-opt-in.mjs'),
|
||||
'entry-point',
|
||||
],
|
||||
{
|
||||
stdout: 'Hello',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test import.meta.resolve of a never-settling resolve should throw in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/import.meta.never-resolve.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^should be output$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test never-settling load in CJS files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.cjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^should be output$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test top-level await of a never-settling load without warning in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'),
|
||||
],
|
||||
{
|
||||
status: 13,
|
||||
signal: null,
|
||||
stdout: /^should be output$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
// Test top-level await of a never-settling load with warning in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-load.mjs'),
|
||||
],
|
||||
{
|
||||
status: 13,
|
||||
signal: null,
|
||||
stdout: /^should be output$/,
|
||||
stderr: /Warning: Detected unsettled top-level await at.+never-load\.mjs:5/,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test race of never-settling hooks in CJS files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/race.cjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^true$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test top-level await of a race of never-settling hooks in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/race.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^true$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test never-settling resolve in CJS files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.cjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^should be output$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test top-level await of a never-settling resolve without warning in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'),
|
||||
],
|
||||
{
|
||||
status: 13,
|
||||
signal: null,
|
||||
stdout: /^should be output$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,21 @@
|
||||
// Test top-level await of a never-settling resolve with warning in ESM files
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/never-settling-resolve-step/loader.mjs'),
|
||||
fixtures.path('es-module-loaders/never-settling-resolve-step/never-resolve.mjs'),
|
||||
],
|
||||
{
|
||||
status: 13,
|
||||
signal: null,
|
||||
stdout: /^should be output$/,
|
||||
stderr: /Warning: Detected unsettled top-level await at.+never-resolve\.mjs:5/,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that loader hooks should not leak internals or expose import.meta.resolve
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-edge-cases.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test that it should be fine to call `process.exit` from a custom async hook
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-exit-on-load.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "data:exit"',
|
||||
],
|
||||
{
|
||||
status: 42,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test that it should be fine to call `process.exit` from a custom sync hook
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-exit-on-resolve.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "data:text/javascript,import.meta.resolve(%22exit:%22)"',
|
||||
],
|
||||
{
|
||||
status: 42,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that it should be fine to call process.exit from the loader thread top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-exit-top-level.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 42,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that `register` should work with cjs
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
fixtures.path('module-hooks/register-loader-with-cjs.cjs'),
|
||||
],
|
||||
{
|
||||
stdout(output) {
|
||||
assert.deepStrictEqual(output.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}', ''].sort());
|
||||
},
|
||||
stderr: '',
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that `register` should work with `import`
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/register-loader.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os"',
|
||||
],
|
||||
{
|
||||
stdout: 'resolve passthru',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
// Test that loader should allow communicating with loader via `register` ports
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
fixtures.path('module-hooks/register-loader-with-ports.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: 'register undefined\nmessage initialize\nmessage resolve node:os',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that `register` should work with `require`
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--require',
|
||||
fixtures.path('es-module-loaders/register-loader.cjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'import "node:os";',
|
||||
],
|
||||
{
|
||||
stdout: 'resolve passthru\nresolve passthru',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,26 @@
|
||||
// Test that `register` should accept URL objects as `parentURL`
|
||||
import '../common/index.mjs';
|
||||
import assert from 'node:assert';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/register-hooks-with-current-cwd-parent-url.mjs'),
|
||||
fixtures.path('es-module-loaders/register-loader-with-url-parenturl.mjs'),
|
||||
],
|
||||
{
|
||||
cwd: fixtures.path('es-module-loaders/'),
|
||||
},
|
||||
{
|
||||
stdout(output) {
|
||||
assert.deepStrictEqual(output.split('\n').sort(), ['hooks initialize 1', '{"default":"foo"}'].sort());
|
||||
},
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that it should be fine to call `process.removeAllListeners("beforeExit")`("beforeExit") from the main thread
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-delayed-async-load.mjs'),
|
||||
'--input-type=module',
|
||||
'--eval',
|
||||
'setInterval(() => process.removeAllListeners("beforeExit"),1).unref();await import("data:text/javascript,")',
|
||||
],
|
||||
{
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that loader should use CJS loader to respond to `require.resolve` calls by default
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'),
|
||||
fixtures.path('require-resolve.js'),
|
||||
],
|
||||
{
|
||||
stdout: 'resolve passthru',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader should use ESM loader to respond to `require.resolve` calls when opting in
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-load-commonjs-with-source.mjs'),
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-resolve-passthru.mjs'),
|
||||
fixtures.path('require-resolve.js'),
|
||||
],
|
||||
{
|
||||
stdout: 'resolve passthru\n'.repeat(10).trim(),
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
// Test that loader should support source maps in commonjs translator
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--enable-source-maps',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/loader-load-source-maps.mjs'),
|
||||
fixtures.path('source-map/throw-on-require.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /throw-on-require\.ts:9:9/,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle bigint thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-bigint.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^1$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle boolean thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-boolean.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^true$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle empty plain object thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-empty-object.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^\{\}$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle regular Error object thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-error.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^Error: error message$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle function thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-function.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^\[Function: fnName\]$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle null thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-null.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^null$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle number thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-number.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^1$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle plain object thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-object.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^\{ fn: \[Function: fn\], symbol: Symbol\(symbol\), u: undefined \}$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle string thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-string.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^literal string$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that loader hooks should handle symbol thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-symbol.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '', // Throwing a symbol doesn't produce any output
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
// Test that loader hooks should handle undefined thrown from top-level
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-throw-undefined.mjs'),
|
||||
fixtures.path('empty.js'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /^undefined$/m,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
// Test that loader should use hooks for require of ESM
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-experimental-require-module',
|
||||
'--import',
|
||||
fixtures.fileURL('es-module-loaders/builtin-named-exports.mjs'),
|
||||
fixtures.path('es-modules/require-esm-throws-with-loaders.js'),
|
||||
],
|
||||
{
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,24 @@
|
||||
// Test that loader hooks should allow spawning workers when allowed by CLI flags
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndAssert } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndAssert(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-worker',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-worker-spawn.mjs'),
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
],
|
||||
{
|
||||
stdout: /^1\n2$/,
|
||||
stderr: '',
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,25 @@
|
||||
// Test that loader hooks should not allow spawning workers if restricted by CLI flags
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('es-module-loaders/loader-worker-spawn.mjs'),
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /code: 'ERR_ACCESS_DENIED'/,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,25 @@
|
||||
// Test that loader hooks should not work without worker permission
|
||||
import '../common/index.mjs';
|
||||
import { execPath } from 'node:process';
|
||||
import fixtures from '../common/fixtures.js';
|
||||
import { spawnSyncAndExit } from '../common/child_process.js';
|
||||
|
||||
spawnSyncAndExit(
|
||||
execPath,
|
||||
[
|
||||
'--no-warnings',
|
||||
'--permission',
|
||||
'--allow-fs-read',
|
||||
'*',
|
||||
'--experimental-loader',
|
||||
fixtures.fileURL('empty.js'),
|
||||
fixtures.path('es-modules/esm-top-level-await.mjs'),
|
||||
],
|
||||
{
|
||||
status: 1,
|
||||
signal: null,
|
||||
stdout: '',
|
||||
stderr: /Error: Access to this API has been restricted/,
|
||||
trim: true,
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user