Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/es-module/test-esm-assertionless-json-import.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Flags: --experimental-loader ./test/fixtures/es-module-loaders/assertionless-json-import.mjs
// Flags: --import ./test/fixtures/es-module-loaders/assertionless-json-import.mjs
'use strict';
const common = require('../common');
const assert = require('assert');
Expand Down
23 changes: 12 additions & 11 deletions test/es-module/test-esm-detect-ambiguous.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -147,26 +147,27 @@ describe('Module syntax detection', { concurrency: !process.env.TEST_PARALLEL },
}

it('should not hint wrong format in resolve hook', async () => {
// eslint-disable-next-line no-unassigned-vars
let writeSync;
const { stdout, stderr, code, signal } = await spawnPromisified(process.execPath, [
'--no-warnings',
'--loader',
`data:text/javascript,import { writeSync } from "node:fs"; export ${encodeURIComponent(
async function resolve(s, c, next) {
const result = await next(s, c);
writeSync(1, result.format + '\n');
return result;
}
)}`,
'--import',
'data:text/javascript,' + encodeURIComponent(`
import { writeSync } from 'node:fs';
import { registerHooks } from 'node:module';
registerHooks({
resolve(specifier, context, nextResolve) {
const result = nextResolve(specifier, context);
writeSync(1, result.format + '\\n');
return result;
},
});
`),
fixtures.path('es-modules/package-without-type/noext-esm'),
]);

assert.strictEqual(stderr, '');
assert.strictEqual(stdout, 'null\nexecuted\n');
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);

});
});

Expand Down
5 changes: 4 additions & 1 deletion test/es-module/test-esm-import-meta-resolve.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ await assert.rejects(import('data:text/javascript,export default import.meta.res
const result = await spawnPromisified(execPath, [
'--no-warnings',
'--input-type=module',
'--import', 'data:text/javascript,import{register}from"node:module";register("data:text/javascript,")',
'--import', 'data:text/javascript,' + encodeURIComponent(
'import { registerHooks } from "node:module";' +
'registerHooks({ resolve: (s, c, next) => next(s, c) });',
),
'--eval',
'console.log(import.meta.resolve(new URL("http://example.com")))',
]);
Expand Down
8 changes: 4 additions & 4 deletions test/es-module/test-esm-virtual-json.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import '../common/index.mjs';
import * as fixtures from '../common/fixtures.mjs';
import { register } from 'node:module';
import { registerHooks } from 'node:module';
import assert from 'node:assert';

async function resolve(referrer, context, next) {
const result = await next(referrer, context);
function resolve(referrer, context, next) {
const result = next(referrer, context);
const url = new URL(result.url);
url.searchParams.set('randomSeed', Math.random());
result.url = url.href;
Expand All @@ -22,7 +22,7 @@ function load(url, context, next) {
return next(url, context);
}

register(`data:text/javascript,export ${encodeURIComponent(resolve)};export ${encodeURIComponent(load)}`);
registerHooks({ resolve, load });

assert.notDeepStrictEqual(
await import(fixtures.fileURL('empty.json'), { with: { type: 'json' } }),
Expand Down
19 changes: 14 additions & 5 deletions test/fixtures/es-module-loaders/assertionless-json-import.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { registerHooks } from 'node:module';

const DATA_URL_PATTERN = /^data:application\/json(?:[^,]*?)(;base64)?,([\s\S]*)$/;
const JSON_URL_PATTERN = /^[^?]+\.json(\?[^#]*)?(#.*)?$/;

export async function resolve(specifier, context, next) {
const noAttributesSpecified = context.importAttributes.type == null;
function resolve(specifier, context, next) {
// Hooks also run for require() calls, which have no import attributes.
if (context.importAttributes === undefined) {
return next(specifier, context);
}

// Mutation from resolve hook should be discarded.
context.importAttributes.type = 'whatever';
const noAttributesSpecified = context.importAttributes.type == null;

// This fixture assumes that no other resolve hooks in the chain will error on invalid import attributes
// (as defaultResolve doesn't).
const result = await next(specifier, context);
const result = next(specifier, {
...context,
importAttributes: { ...context.importAttributes, type: 'whatever' },
});

if (noAttributesSpecified &&
(DATA_URL_PATTERN.test(result.url) || JSON_URL_PATTERN.test(result.url))) {
Expand All @@ -22,3 +29,5 @@ export async function resolve(specifier, context, next) {

return result;
}

registerHooks({ resolve });
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// This fixture is used to test that custom loaders are not enabled in the ShadowRealm.
// This fixture is used to test that customization hooks are not enabled in the ShadowRealm.

'use strict';
const assert = require('assert');

async function workInChildProcess() {
// Assert that the process is running with a custom loader.
// Assert that the process is running with custom hooks.
const moduleNamespace = await import('file:///42.mjs');
assert.strictEqual(moduleNamespace.default, 42);

Expand Down
17 changes: 17 additions & 0 deletions test/fixtures/es-module-shadow-realm/register-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';
const { registerHooks } = require('node:module');

registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier.startsWith('file:///') && specifier.includes('42')) {
return { shortCircuit: true, url: specifier };
}
return nextResolve(specifier, context);
},
load(url, context, nextLoad) {
if (url.startsWith('file:///') && url.includes('42')) {
return { shortCircuit: true, format: 'module', source: 'export default 42' };
}
return nextLoad(url, context);
},
});
2 changes: 0 additions & 2 deletions test/fixtures/module-hooks/register-logger-async-hooks.mjs

This file was deleted.

13 changes: 13 additions & 0 deletions test/fixtures/module-hooks/register-logger-hooks.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { registerHooks } from 'node:module';
import { writeSync } from 'node:fs';

registerHooks({
resolve(specifier, context, nextResolve) {
writeSync(1, `resolve ${specifier} from ${context.parentURL}\n`);
return nextResolve(specifier, context);
},
load(url, context, nextLoad) {
writeSync(1, `load ${url}\n`);
return nextLoad(url, context);
},
});
6 changes: 5 additions & 1 deletion test/fixtures/test-runner/coverage-loader/hooks.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { registerHooks } from 'node:module';

const sources = {
// Virtual file. Doesn't exist on disk
"virtual.js": `
Expand All @@ -16,10 +18,12 @@ import{describe,it}from"node:test";import assert from"node:assert";import{sum}fr
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6ImtIQUFPLFNBQVMsT0FBUSxFQUFHLENBQ3pCLEdBQUksQ0FBQyxFQUFFLE1BQU8sS0FBUSxPQUFPLE1BQVEsUUFBUSxFQUFHLE1BQU0sSUFBSSxNQUFNLGNBQWMsRUFDOUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxJQUFLLE1BQVEsSUFBTSxHQUFHLENBQ3pDLENBSGdCIiwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlcyI6WyIuL3N1bS50cyJdLCJzb3VyY2VzQ29udGVudCI6W251bGxdfQ==`,
};

export async function load(url, context, nextLoad) {
function load(url, context, nextLoad) {
const file = url.split('/').at(-1);
if (sources[file] !== undefined) {
return { format: "module", source: sources[file], shortCircuit: true };
}
return nextLoad(url, context);
}

registerHooks({ load });
4 changes: 0 additions & 4 deletions test/fixtures/test-runner/coverage-loader/register-hooks.js

This file was deleted.

11 changes: 0 additions & 11 deletions test/fixtures/typescript/ts/hook.ts

This file was deleted.

11 changes: 8 additions & 3 deletions test/fixtures/typescript/ts/test-loader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { register } from 'node:module';
import * as fixtures from '../../../common/fixtures.mjs';
import { registerHooks } from 'node:module';
import type { ResolveHookSync } from 'node:module';

register(fixtures.fileURL('typescript/ts/hook.ts'));
// Pass through
const resolve: ResolveHookSync = function resolve(specifier, context, nextResolve) {
return nextResolve(specifier, context);
};

registerHooks({ resolve });
18 changes: 7 additions & 11 deletions test/module-hooks/test-module-hooks-require-esm.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
'use strict';

// This tests that the async loader hooks can be invoked for require(esm).
// This tests that module.registerHooks() hooks are invoked for require(esm)
// and for the CommonJS and ES module graphs reachable from it.

require('../common');
const common = require('../common');
const { spawnSyncAndAssert } = require('../common/child_process');
const fixtures = require('../common/fixtures');
const assert = require('assert');

function assertSubGraph(output) {
// FIXME(node:59666): the async resolve hook invoked for require() in imported CJS only get the URL,
// not the specifier. This may be fixable if we re-implement the async loader hooks from within
// the synchronous loader hooks and use the original require() implementation.
assert.match(output, /resolve .+esm\.mjs from file:.+main\.cjs/);
assert.match(output, /resolve \.\/esm\.mjs from file:.+main\.cjs/);
assert.match(output, /load file:.+esm\.mjs/);

assert.match(output, /resolve \.\/inner\.mjs from file:.+esm\.mjs/);
Expand All @@ -21,8 +18,7 @@ function assertSubGraph(output) {
assert.match(output, /resolve \.\/cjs\.cjs from file:.+esm\.mjs/);
assert.match(output, /load file:.+cjs\.cjs/);

// FIXME(node:59666): see above.
assert.match(output, /resolve .+inner\.cjs from file:.+cjs\.cjs/);
assert.match(output, /resolve \.\/inner\.cjs from file:.+cjs\.cjs/);
assert.match(output, /load file:.+inner\.cjs/);

assert.match(output, /esmValue in main\.cjs: esm/);
Expand All @@ -31,7 +27,7 @@ function assertSubGraph(output) {

spawnSyncAndAssert(process.execPath, [
'--import',
fixtures.fileURL('module-hooks', 'register-logger-async-hooks.mjs'),
fixtures.fileURL('module-hooks', 'register-logger-hooks.mjs'),
fixtures.path('module-hooks', 'require-esm', 'main.cjs'),
], {
stdout: common.mustCall((output) => {
Expand All @@ -50,7 +46,7 @@ spawnSyncAndAssert(process.execPath, [

spawnSyncAndAssert(process.execPath, [
'--import',
fixtures.fileURL('module-hooks', 'register-logger-async-hooks.mjs'),
fixtures.fileURL('module-hooks', 'register-logger-hooks.mjs'),
fixtures.path('module-hooks', 'require-esm', 'main.mjs'),
], {
stdout: common.mustCall((output) => {
Expand All @@ -64,7 +60,7 @@ spawnSyncAndAssert(process.execPath, [
assert.match(output, /resolve .+main\.mjs from undefined/);
assert.match(output, /load file:.+main\.mjs/);

assert.match(output, /resolve .+main\.cjs from file:.+main\.mjs/);
assert.match(output, /resolve \.\/main\.cjs from file:.+main\.mjs/);
assert.match(output, /load file:.+main\.cjs/);

assertSubGraph(output);
Expand Down
29 changes: 8 additions & 21 deletions test/parallel/test-find-package-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,23 @@ describe('findPackageJSON', () => { // Throws when no arguments are provided
}));
});

it('should work within a loader', async () => {
it('should work within a resolve hook', async () => {
const specifierBase = './packages/root-types-field';
const target = fixtures.fileURL(specifierBase, 'index.js');
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
'--no-warnings',
'--loader',
'--import',
[
'data:text/javascript,',
'import fs from "node:fs";',
'import module from "node:module";',
encodeURIComponent(`fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)}));`),
'export const resolve = async (s, c, n) => n(s);',
encodeURIComponent(`module.registerHooks({
resolve(specifier, context, nextResolve) {
fs.writeSync(1, module.findPackageJSON(${JSON.stringify(target)}));
return nextResolve(specifier, context);
},
});`),
].join(''),
'--eval',
'import "node:os";', // Can be anything that triggers the resolve hook chain
Expand All @@ -173,23 +177,6 @@ describe('findPackageJSON', () => { // Throws when no arguments are provided
assert.strictEqual(code, 0);
});

it('should work with an async resolve hook registered', async () => {
const specifierBase = './packages/root-types-field';
const target = fixtures.fileURL(specifierBase, 'index.js');
const foundPjsonPath = path.toNamespacedPath(fixtures.path(specifierBase, 'package.json'));
const { code, stderr, stdout } = await common.spawnPromisified(process.execPath, [
'--no-warnings',
'--loader',
'data:text/javascript,export const resolve = async (s, c, n) => n(s);',
'--print',
`require("node:module").findPackageJSON(${JSON.stringify(target)})`,
]);

assert.strictEqual(stderr, '');
assert.ok(stdout.includes(foundPjsonPath), stdout);
assert.strictEqual(code, 0);
});

it('should work when unrecognised keys are specified within the export', async () => {
const specifierBase = './packages/unrecognised-export-keys';
const target = fixtures.fileURL(specifierBase, 'index.js');
Expand Down
4 changes: 2 additions & 2 deletions test/parallel/test-runner-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,10 @@ for (const isolation of ['none', 'process']) {
}

{
// Use test with --loader and --require.
// Use test with --import and --require.
// This case is common since vscode uses --require to load the debugger.
const args = ['--no-warnings',
'--experimental-loader', 'data:text/javascript,',
'--import', 'data:text/javascript,',
'--require', fixtures.path('empty.js'),
'--test', join(testFixtures, 'default-behavior', 'index.test.js')];
const child = spawnSync(process.execPath, args);
Expand Down
Loading
Loading