Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esm: rewrite loader hooks test #46016

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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}/);
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
});
});
92 changes: 92 additions & 0 deletions test/fixtures/es-module-loaders/hooks-input.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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 { write } from 'fs';
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.deepStrictEqual(context.parentURL, undefined);
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
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(Object.keys(context), [
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
'conditions',
'importAssertions',
'parentURL',
]);
assert.ok(Array.isArray(context.conditions));
assert.deepStrictEqual(typeof next, 'function');
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved

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

await new Promise(resolve => write(1, `${JSON.stringify(returnValue)}\n`, resolve)); // For the test to read
aduh95 marked this conversation as resolved.
Show resolved Hide resolved

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.deepStrictEqual(context.format, 'test');
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved
assert.deepStrictEqual(typeof next, 'function');
GeoffreyBooth marked this conversation as resolved.
Show resolved Hide resolved

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

await new Promise(resolve => write(1, `${JSON.stringify(returnValue)}\n`, resolve)); // For the test to read

return returnValue;
}