Skip to content

Commit 64342b5

Browse files
committed
esm: rewrite loader hooks test
Rewrite the test that validates that custom loader hooks are called from being a test that depends on internals to one that spawns a child process and checks its output to confirm expected behavior.
1 parent 4830a6c commit 64342b5

File tree

2 files changed

+116
-81
lines changed

2 files changed

+116
-81
lines changed
Lines changed: 24 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,26 @@
1-
// Flags: --expose-internals
2-
import { mustCall } from '../common/index.mjs';
3-
import esmLoaderModule from 'internal/modules/esm/loader';
4-
import assert from 'assert';
5-
6-
const { ESMLoader } = esmLoaderModule;
7-
8-
/**
9-
* Verify custom hooks are called with appropriate arguments.
10-
*/
11-
{
12-
const esmLoader = new ESMLoader();
13-
14-
const originalSpecifier = 'foo/bar';
15-
const importAssertions = {
16-
__proto__: null,
17-
type: 'json',
18-
};
19-
const parentURL = 'file:///entrypoint.js';
20-
const resolvedURL = 'file:///foo/bar.js';
21-
const suggestedFormat = 'test';
22-
23-
function resolve(specifier, context, defaultResolve) {
24-
assert.strictEqual(specifier, originalSpecifier);
25-
// Ensure `context` has all and only the properties it's supposed to
26-
assert.deepStrictEqual(Object.keys(context), [
27-
'conditions',
28-
'importAssertions',
29-
'parentURL',
30-
]);
31-
assert.ok(Array.isArray(context.conditions));
32-
assert.deepStrictEqual(context.importAssertions, importAssertions);
33-
assert.strictEqual(context.parentURL, parentURL);
34-
assert.strictEqual(typeof defaultResolve, 'function');
35-
36-
return {
37-
format: suggestedFormat,
38-
shortCircuit: true,
39-
url: resolvedURL,
40-
};
41-
}
42-
43-
function load(resolvedURL, context, defaultLoad) {
44-
assert.strictEqual(resolvedURL, resolvedURL);
45-
assert.ok(new URL(resolvedURL));
46-
// Ensure `context` has all and only the properties it's supposed to
47-
assert.deepStrictEqual(Object.keys(context), [
48-
'format',
49-
'importAssertions',
1+
import { spawnPromisified } from '../common/index.mjs';
2+
import * as fixtures from '../common/fixtures.mjs';
3+
import assert from 'node:assert';
4+
import { execPath } from 'node:process';
5+
import { describe, it } from 'node:test';
6+
7+
describe('Loader hooks', () => {
8+
it('are called with all expected arguments', async () => {
9+
const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
10+
'--no-warnings',
11+
'--experimental-loader',
12+
fixtures.fileURL('/es-module-loaders/hooks-input.mjs'),
13+
fixtures.path('/es-modules/json-modules.mjs'),
5014
]);
51-
assert.strictEqual(context.format, suggestedFormat);
52-
assert.deepStrictEqual(context.importAssertions, importAssertions);
53-
assert.strictEqual(typeof defaultLoad, 'function');
54-
55-
// This doesn't matter (just to avoid errors)
56-
return {
57-
format: 'module',
58-
shortCircuit: true,
59-
source: '',
60-
};
61-
}
62-
63-
const customLoader = [
64-
{
65-
exports: {
66-
// Ensure ESMLoader actually calls the custom hooks
67-
resolve: mustCall(resolve),
68-
load: mustCall(load),
69-
},
70-
url: import.meta.url,
71-
},
72-
];
73-
74-
esmLoader.addCustomLoaders(customLoader);
7515

76-
// Manually trigger hooks (since ESMLoader is not actually running)
77-
const job = await esmLoader.getModuleJob(
78-
originalSpecifier,
79-
parentURL,
80-
importAssertions,
81-
);
82-
await job.modulePromise;
83-
}
16+
assert.strictEqual(stderr, '');
17+
assert.strictEqual(code, 0);
18+
assert.strictEqual(signal, null);
19+
20+
const lines = stdout.split('\n');
21+
assert.match(lines[0], /{"url":"file:\/\/\/.*\/json-modules\.mjs","format":"test","shortCircuit":true}/);
22+
assert.match(lines[1], /{"source":{"type":"Buffer","data":\[.*\]},"format":"module","shortCircuit":true}/);
23+
assert.match(lines[2], /{"url":"file:\/\/\/.*\/experimental\.json","format":"test","shortCircuit":true}/);
24+
assert.match(lines[3], /{"source":{"type":"Buffer","data":\[.*\]},"format":"json","shortCircuit":true}/);
25+
});
26+
});
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// This is expected to be used by test-esm-loader-hooks.mjs via:
2+
// node --loader ./test/fixtures/es-module-loaders/hooks-input.mjs ./test/fixtures/es-modules/json-modules.mjs
3+
4+
import assert from 'assert';
5+
import { write } from 'fs';
6+
import { readFile } from 'fs/promises';
7+
import { fileURLToPath } from 'url';
8+
9+
10+
let resolveCalls = 0;
11+
let loadCalls = 0;
12+
13+
export async function resolve(specifier, context, next) {
14+
resolveCalls++;
15+
let url;
16+
17+
if (resolveCalls === 1) {
18+
url = new URL(specifier).href;
19+
assert.match(specifier, /json-modules\.mjs$/);
20+
assert.deepStrictEqual(context.parentURL, undefined);
21+
assert.deepStrictEqual(context.importAssertions, {
22+
__proto__: null,
23+
});
24+
} else if (resolveCalls === 2) {
25+
url = new URL(specifier, context.parentURL).href;
26+
assert.match(specifier, /experimental\.json$/);
27+
assert.match(context.parentURL, /json-modules\.mjs$/);
28+
assert.deepStrictEqual(context.importAssertions, {
29+
__proto__: null,
30+
type: 'json',
31+
});
32+
}
33+
34+
// Ensure `context` has all and only the properties it's supposed to
35+
assert.deepStrictEqual(Object.keys(context), [
36+
'conditions',
37+
'importAssertions',
38+
'parentURL',
39+
]);
40+
assert.ok(Array.isArray(context.conditions));
41+
assert.deepStrictEqual(typeof next, 'function');
42+
43+
const returnValue = {
44+
url,
45+
format: 'test',
46+
shortCircuit: true,
47+
}
48+
49+
await new Promise(resolve => write(1, `${JSON.stringify(returnValue)}\n`, resolve)); // For the test to read
50+
51+
return returnValue;
52+
}
53+
54+
export async function load(url, context, next) {
55+
loadCalls++;
56+
const source = await readFile(fileURLToPath(url));
57+
let format;
58+
59+
if (loadCalls === 1) {
60+
assert.match(url, /json-modules\.mjs$/);
61+
assert.deepStrictEqual(context.importAssertions, {
62+
__proto__: null,
63+
});
64+
format = 'module';
65+
} else if (loadCalls === 2) {
66+
assert.match(url, /experimental\.json$/);
67+
assert.deepStrictEqual(context.importAssertions, {
68+
__proto__: null,
69+
type: 'json',
70+
});
71+
format = 'json';
72+
}
73+
74+
assert.ok(new URL(url));
75+
// Ensure `context` has all and only the properties it's supposed to
76+
assert.deepStrictEqual(Object.keys(context), [
77+
'format',
78+
'importAssertions',
79+
]);
80+
assert.deepStrictEqual(context.format, 'test');
81+
assert.deepStrictEqual(typeof next, 'function');
82+
83+
const returnValue = {
84+
source,
85+
format,
86+
shortCircuit: true,
87+
};
88+
89+
await new Promise(resolve => write(1, `${JSON.stringify(returnValue)}\n`, resolve)); // For the test to read
90+
91+
return returnValue;
92+
}

0 commit comments

Comments
 (0)