• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import { spawnPromisified } from '../common/index.mjs';
2import * as fixtures from '../common/fixtures.js';
3import assert from 'node:assert';
4import { execPath } from 'node:process';
5import { describe, it } from 'node:test';
6
7// This test ensures that the register function can register loaders
8// programmatically.
9
10const commonArgs = [
11  '--no-warnings',
12  '--input-type=module',
13  '--loader=data:text/javascript,',
14];
15
16const commonEvals = {
17  import: (module) => `await import(${JSON.stringify(module)});`,
18  register: (loader, parentURL = 'file:///') => `register(${JSON.stringify(loader)}, ${JSON.stringify(parentURL)});`,
19  dynamicImport: (module) => `await import(${JSON.stringify(`data:text/javascript,${encodeURIComponent(module)}`)});`,
20  staticImport: (module) => `import ${JSON.stringify(`data:text/javascript,${encodeURIComponent(module)}`)};`,
21};
22
23describe('ESM: programmatically register loaders', { concurrency: true }, () => {
24  it('works with only a dummy CLI argument', async () => {
25    const parentURL = fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs');
26    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
27      ...commonArgs,
28      '--eval',
29      "import { register } from 'node:module';" +
30      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
31      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
32      `register(${JSON.stringify('./loader-resolve-passthru.mjs')}, ${JSON.stringify({ parentURL })});` +
33      `register(${JSON.stringify('./loader-load-passthru.mjs')}, ${JSON.stringify({ parentURL })});` +
34      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
35    ]);
36
37    assert.strictEqual(stderr, '');
38    assert.strictEqual(code, 0);
39    assert.strictEqual(signal, null);
40
41    const lines = stdout.split('\n');
42
43    assert.match(lines[0], /resolve passthru/);
44    assert.match(lines[1], /resolve passthru/);
45    assert.match(lines[2], /load passthru/);
46    assert.match(lines[3], /load passthru/);
47    assert.match(lines[4], /Hello from dynamic import/);
48
49    assert.strictEqual(lines[5], '');
50  });
51
52  describe('registering via --import', { concurrency: true }, () => {
53    for (const moduleType of ['mjs', 'cjs']) {
54      it(`should programmatically register a loader from a ${moduleType.toUpperCase()} file`, async () => {
55        const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
56          ...commonArgs,
57          '--import', fixtures.fileURL('es-module-loaders', `register-loader.${moduleType}`).href,
58          '--eval', commonEvals.staticImport('console.log("entry point")'),
59        ]);
60
61        assert.strictEqual(stderr, '');
62        assert.strictEqual(code, 0);
63        assert.strictEqual(signal, null);
64
65        const [
66          passthruStdout,
67          entryPointStdout,
68        ] = stdout.split('\n');
69
70        assert.match(passthruStdout, /resolve passthru/);
71        assert.match(entryPointStdout, /entry point/);
72      });
73    }
74  });
75
76  it('programmatically registered loaders are appended to an existing chaining', async () => {
77    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
78      ...commonArgs,
79      '--loader',
80      fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs'),
81      '--eval',
82      "import { register } from 'node:module';" +
83      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
84      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
85    ]);
86
87    assert.strictEqual(stderr, '');
88    assert.strictEqual(code, 0);
89    assert.strictEqual(signal, null);
90
91    const lines = stdout.split('\n');
92
93    assert.match(lines[0], /resolve passthru/);
94    assert.match(lines[1], /resolve passthru/);
95    assert.match(lines[2], /load passthru/);
96    assert.match(lines[3], /Hello from dynamic import/);
97
98    assert.strictEqual(lines[4], '');
99  });
100
101  it('works registering loaders across files', async () => {
102    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
103      ...commonArgs,
104      '--eval',
105      commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-load.mjs')) +
106      commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-resolve.mjs')) +
107      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
108    ]);
109
110    assert.strictEqual(stderr, '');
111    assert.strictEqual(code, 0);
112    assert.strictEqual(signal, null);
113
114    const lines = stdout.split('\n');
115
116    assert.match(lines[0], /resolve passthru/);
117    assert.match(lines[1], /load passthru/);
118    assert.match(lines[2], /Hello from dynamic import/);
119
120    assert.strictEqual(lines[3], '');
121  });
122
123  it('works registering loaders across virtual files', async () => {
124    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
125      ...commonArgs,
126      '--eval',
127      commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-load.mjs')) +
128      commonEvals.dynamicImport(
129        commonEvals.import(fixtures.fileURL('es-module-loaders', 'register-programmatically-loader-resolve.mjs')) +
130        commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
131      ),
132    ]);
133
134    assert.strictEqual(stderr, '');
135    assert.strictEqual(code, 0);
136    assert.strictEqual(signal, null);
137
138    const lines = stdout.split('\n');
139
140    assert.match(lines[0], /resolve passthru/);
141    assert.match(lines[1], /load passthru/);
142    assert.match(lines[2], /Hello from dynamic import/);
143
144    assert.strictEqual(lines[3], '');
145  });
146
147  it('works registering the same loaders more them once', async () => {
148    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
149      ...commonArgs,
150      '--eval',
151      "import { register } from 'node:module';" +
152      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
153      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
154      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-resolve-passthru.mjs')) +
155      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
156      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
157    ]);
158
159    assert.strictEqual(stderr, '');
160    assert.strictEqual(code, 0);
161    assert.strictEqual(signal, null);
162
163    const lines = stdout.split('\n');
164
165    assert.match(lines[0], /resolve passthru/);
166    assert.match(lines[1], /resolve passthru/);
167    assert.match(lines[2], /load passthru/);
168    assert.match(lines[3], /load passthru/);
169    assert.match(lines[4], /Hello from dynamic import/);
170
171    assert.strictEqual(lines[5], '');
172  });
173
174  it('works registering loaders as package name', async () => {
175    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
176      ...commonArgs,
177      '--eval',
178      "import { register } from 'node:module';" +
179      commonEvals.register('resolve', fixtures.fileURL('es-module-loaders', 'package.json')) +
180      commonEvals.register('load', fixtures.fileURL('es-module-loaders', 'package.json')) +
181      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
182    ]);
183
184    assert.strictEqual(stderr, '');
185    assert.strictEqual(code, 0);
186    assert.strictEqual(signal, null);
187
188    const lines = stdout.split('\n');
189
190    // Resolve occurs twice because it is first used to resolve the `load` loader
191    // _AND THEN_ the `register` module.
192    assert.match(lines[0], /resolve passthru/);
193    assert.match(lines[1], /resolve passthru/);
194    assert.match(lines[2], /load passthru/);
195    assert.match(lines[3], /Hello from dynamic import/);
196
197    assert.strictEqual(lines[4], '');
198  });
199
200  it('works without a CLI flag', async () => {
201    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
202      '--no-warnings',
203      '--input-type=module',
204      '--eval',
205      "import { register } from 'node:module';" +
206      commonEvals.register(fixtures.fileURL('es-module-loaders', 'loader-load-passthru.mjs')) +
207      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
208    ]);
209
210    assert.strictEqual(stderr, '');
211    assert.strictEqual(code, 0);
212    assert.strictEqual(signal, null);
213
214    const lines = stdout.split('\n');
215
216    assert.match(lines[0], /load passthru/);
217    assert.match(lines[1], /Hello from dynamic import/);
218
219    assert.strictEqual(lines[2], '');
220  });
221
222  it('does not work with a loader specifier that does not exist', async () => {
223    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
224      ...commonArgs,
225      '--eval',
226      "import { register } from 'node:module';" +
227      commonEvals.register('./not-found.mjs', import.meta.url) +
228      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
229    ]);
230
231    assert.strictEqual(stdout, '');
232    assert.strictEqual(code, 1);
233    assert.strictEqual(signal, null);
234    assert.match(stderr, /ERR_MODULE_NOT_FOUND/);
235  });
236
237  it('does not work with a loader that got syntax errors', async () => {
238    const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [
239      ...commonArgs,
240      '--eval',
241      "import { register } from 'node:module';" +
242      commonEvals.register(fixtures.fileURL('es-module-loaders', 'syntax-error.mjs')) +
243      commonEvals.dynamicImport('console.log("Hello from dynamic import");'),
244    ]);
245
246    assert.strictEqual(stdout, '');
247    assert.strictEqual(code, 1);
248    assert.strictEqual(signal, null);
249    assert.match(stderr, /SyntaxError/);
250  });
251});
252