Skip to content
Merged
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
105 changes: 24 additions & 81 deletions test/es-module/test-esm-loader-hooks.mjs
Original file line number Diff line number Diff line change
@@ -1,83 +1,26 @@
// Flags: --expose-internals
import { mustCall } from '../common/index.mjs';
import esmLoaderModule from 'internal/modules/esm/loader';
import assert from 'assert';

const { ESMLoader } = esmLoaderModule;

/**
* Verify custom hooks are called with appropriate arguments.
*/
{
const esmLoader = new ESMLoader();

const originalSpecifier = 'foo/bar';
const importAssertions = {
__proto__: null,
type: 'json',
};
const parentURL = 'file:///entrypoint.js';
const resolvedURL = 'file:///foo/bar.js';
const suggestedFormat = 'test';

function resolve(specifier, context, defaultResolve) {
assert.strictEqual(specifier, originalSpecifier);
// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Object.keys(context), [
'conditions',
'importAssertions',
'parentURL',
]);
assert.ok(Array.isArray(context.conditions));
assert.deepStrictEqual(context.importAssertions, importAssertions);
assert.strictEqual(context.parentURL, parentURL);
assert.strictEqual(typeof defaultResolve, 'function');

return {
format: suggestedFormat,
shortCircuit: true,
url: resolvedURL,
};
}

function load(resolvedURL, context, defaultLoad) {
assert.strictEqual(resolvedURL, resolvedURL);
assert.ok(new URL(resolvedURL));
// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Object.keys(context), [
'format',
'importAssertions',
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', () => {
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(context.format, suggestedFormat);
assert.deepStrictEqual(context.importAssertions, importAssertions);
assert.strictEqual(typeof defaultLoad, 'function');

// This doesn't matter (just to avoid errors)
return {
format: 'module',
shortCircuit: true,
source: '',
};
}

const customLoader = [
{
exports: {
// Ensure ESMLoader actually calls the custom hooks
resolve: mustCall(resolve),
load: mustCall(load),
},
url: import.meta.url,
},
];

esmLoader.addCustomLoaders(customLoader);

// Manually trigger hooks (since ESMLoader is not actually running)
const job = await esmLoader.getModuleJob(
originalSpecifier,
parentURL,
importAssertions,
);
await job.modulePromise;
}
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}/);
});
});
91 changes: 91 additions & 0 deletions test/fixtures/es-module-loaders/hooks-input.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// This is expected to be used by test-esm-loader-hooks.mjs via:
// node --loader ./test/fixtures/es-module-loaders/hooks-input.mjs ./test/fixtures/es-modules/json-modules.mjs

import assert from 'assert';
import { readFile } from 'fs/promises';
import { fileURLToPath } from 'url';


let resolveCalls = 0;
let loadCalls = 0;

export async function resolve(specifier, context, next) {
resolveCalls++;
let url;

if (resolveCalls === 1) {
url = new URL(specifier).href;
assert.match(specifier, /json-modules\.mjs$/);
assert.strictEqual(context.parentURL, undefined);
assert.deepStrictEqual(context.importAssertions, {
__proto__: null,
});
} else if (resolveCalls === 2) {
url = new URL(specifier, context.parentURL).href;
assert.match(specifier, /experimental\.json$/);
assert.match(context.parentURL, /json-modules\.mjs$/);
assert.deepStrictEqual(context.importAssertions, {
__proto__: null,
type: 'json',
});
}

// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Reflect.ownKeys(context), [
'conditions',
'importAssertions',
'parentURL',
]);
assert.ok(Array.isArray(context.conditions));
assert.strictEqual(typeof next, 'function');

const returnValue = {
url,
format: 'test',
shortCircuit: true,
}

console.log(JSON.stringify(returnValue)); // For the test to validate when it parses stdout

return returnValue;
}

export async function load(url, context, next) {
loadCalls++;
const source = await readFile(fileURLToPath(url));
let format;

if (loadCalls === 1) {
assert.match(url, /json-modules\.mjs$/);
assert.deepStrictEqual(context.importAssertions, {
__proto__: null,
});
format = 'module';
} else if (loadCalls === 2) {
assert.match(url, /experimental\.json$/);
assert.deepStrictEqual(context.importAssertions, {
__proto__: null,
type: 'json',
});
format = 'json';
}

assert.ok(new URL(url));
// Ensure `context` has all and only the properties it's supposed to
assert.deepStrictEqual(Object.keys(context), [
'format',
'importAssertions',
]);
assert.strictEqual(context.format, 'test');
assert.strictEqual(typeof next, 'function');

const returnValue = {
source,
format,
shortCircuit: true,
};

console.log(JSON.stringify(returnValue)); // For the test to validate when it parses stdout

return returnValue;
}