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