Skip to content

Commit 4cddada

Browse files
authored
fix: align module external remapping with webpack (#13802)
1 parent fac148c commit 4cddada

18 files changed

Lines changed: 1247 additions & 83 deletions

File tree

crates/rspack_core/src/external_module.rs

Lines changed: 253 additions & 83 deletions
Large diffs are not rendered by default.
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
it('should load node builtins via dynamic import', async () => {
2+
const load = async (modulePromise) => {
3+
const imported = await modulePromise;
4+
return imported.default || imported;
5+
};
6+
const expectIfAvailable = (imported, fn) => {
7+
if (imported) fn(imported);
8+
};
9+
10+
const assertBuiltin = await load(import('assert'));
11+
const asyncHooks = await load(import('async_hooks'));
12+
const buffer = await load(import('buffer'));
13+
const childProcess = await load(import('child_process'));
14+
const cluster = await load(import('cluster'));
15+
const consoleBuiltin = await load(import('console'));
16+
const constants = await load(import('constants'));
17+
const crypto = await load(import('crypto'));
18+
const dgram = await load(import('dgram'));
19+
const dns = await load(import('dns'));
20+
const domain = await load(import('domain'));
21+
const events = await load(import('events'));
22+
const fs = await load(import('fs'));
23+
const http = await load(import('http'));
24+
const http2 = await load(import('http2'));
25+
const https = await load(import('https'));
26+
const inspector = await load(import('inspector'));
27+
const moduleBuiltin = await load(import('module'));
28+
const net = await load(import('net'));
29+
const os = await load(import('os'));
30+
const path = await load(import('path'));
31+
const perfHooks = await load(import('perf_hooks'));
32+
const processBuiltin = await load(import('process'));
33+
const punycode = await load(import('punycode'));
34+
const querystring = await load(import('querystring'));
35+
const readline = await load(import('readline'));
36+
const repl = await load(import('repl'));
37+
const stream = await load(import('stream'));
38+
const stringDecoder = await load(import('string_decoder'));
39+
const sys = await load(import('sys'));
40+
const timers = await load(import('timers'));
41+
const tls = await load(import('tls'));
42+
const traceEvents = await load(import('trace_events'));
43+
const tty = await load(import('tty'));
44+
const url = await load(import('url'));
45+
const util = await load(import('util'));
46+
const v8 = await load(import('v8'));
47+
const vm = await load(import('vm'));
48+
const zlib = await load(import('zlib'));
49+
50+
const builtinImports = {
51+
assert: assertBuiltin,
52+
async_hooks: asyncHooks,
53+
buffer,
54+
child_process: childProcess,
55+
cluster,
56+
console: consoleBuiltin,
57+
constants,
58+
crypto,
59+
dgram,
60+
dns,
61+
domain,
62+
events,
63+
fs,
64+
http,
65+
http2,
66+
https,
67+
inspector,
68+
module: moduleBuiltin,
69+
net,
70+
os,
71+
path,
72+
perf_hooks: perfHooks,
73+
process: processBuiltin,
74+
punycode,
75+
querystring,
76+
readline,
77+
repl,
78+
stream,
79+
string_decoder: stringDecoder,
80+
sys,
81+
timers,
82+
tls,
83+
trace_events: traceEvents,
84+
tty,
85+
url,
86+
util,
87+
v8,
88+
vm,
89+
zlib
90+
};
91+
92+
const baseBuiltinCount = Object.keys(builtinImports).length;
93+
94+
const diagnosticsChannel =
95+
NODE_VERSION >= 14 ? await load(import('diagnostics_channel')) : undefined;
96+
const wasi = NODE_VERSION >= 18 ? await load(import('wasi')) : undefined;
97+
const workerThreads =
98+
NODE_VERSION >= 12 ? await load(import('worker_threads')) : undefined;
99+
100+
const pathPosix =
101+
NODE_VERSION >= 15 ? await load(import('path/posix')) : undefined;
102+
const pathWin32 =
103+
NODE_VERSION >= 15 ? await load(import('path/win32')) : undefined;
104+
const dnsPromises =
105+
NODE_VERSION >= 15 ? await load(import('dns/promises')) : undefined;
106+
const inspectorPromises =
107+
NODE_VERSION >= 19 ? await load(import('inspector/promises')) : undefined;
108+
const streamConsumers =
109+
NODE_VERSION >= 16 ? await load(import('stream/consumers')) : undefined;
110+
const streamPromises =
111+
NODE_VERSION >= 15 ? await load(import('stream/promises')) : undefined;
112+
113+
const utilTypes =
114+
NODE_VERSION >= 15 ? await load(import('util/types')) : undefined;
115+
const streamWeb =
116+
NODE_VERSION >= 16 ? await load(import('stream/web')) : undefined;
117+
const readlinePromises =
118+
NODE_VERSION >= 17 ? await load(import('readline/promises')) : undefined;
119+
const timersPromises =
120+
NODE_VERSION >= 15 ? await load(import('timers/promises')) : undefined;
121+
122+
const assertStrict =
123+
NODE_VERSION >= 15 ? await load(import('assert/strict')) : undefined;
124+
const fsPromises =
125+
NODE_VERSION >= 14 ? await load(import('fs/promises')) : undefined;
126+
127+
const optionalBuiltins = [
128+
['diagnostics_channel', diagnosticsChannel],
129+
['readline/promises', readlinePromises],
130+
['stream/consumers', streamConsumers],
131+
['stream/promises', streamPromises],
132+
['stream/web', streamWeb],
133+
['timers/promises', timersPromises],
134+
['wasi', wasi],
135+
['worker_threads', workerThreads],
136+
['inspector/promises', inspectorPromises],
137+
['dns/promises', dnsPromises],
138+
['path/posix', pathPosix],
139+
['path/win32', pathWin32],
140+
['util/types', utilTypes],
141+
['assert/strict', assertStrict],
142+
['fs/promises', fsPromises]
143+
];
144+
145+
for (const [request, imported] of optionalBuiltins) {
146+
if (imported) builtinImports[request] = imported;
147+
}
148+
149+
expect(Object.keys(builtinImports)).toHaveLength(
150+
baseBuiltinCount +
151+
optionalBuiltins.filter(([, imported]) => Boolean(imported)).length
152+
);
153+
154+
for (const [request, imported] of Object.entries(builtinImports)) {
155+
expect(imported).toBeDefined();
156+
}
157+
158+
expect(() => assertBuiltin.strictEqual(1, 1)).not.toThrow();
159+
160+
expectIfAvailable(assertStrict, (assertStrict) => {
161+
expect(() =>
162+
assertStrict.deepStrictEqual({ a: 1 }, { a: 1 })
163+
).not.toThrow();
164+
});
165+
166+
const hook = asyncHooks.createHook({});
167+
expect(hook).toBeDefined();
168+
169+
const buf = buffer.Buffer.from('hello');
170+
expect(buf.toString()).toBe('hello');
171+
172+
expect(typeof childProcess.spawn).toBe('function');
173+
expect(typeof cluster.isMaster).toBe('boolean');
174+
175+
const customConsole = new consoleBuiltin.Console(process.stdout);
176+
expect(customConsole).toBeDefined();
177+
178+
expect(constants).toBeDefined();
179+
180+
const hash = crypto.createHash('sha256').update('test').digest('hex');
181+
expect(hash).toBeDefined();
182+
183+
const socket = dgram.createSocket('udp4');
184+
expect(socket).toBeDefined();
185+
socket.close();
186+
187+
expectIfAvailable(diagnosticsChannel, (channel) => {
188+
const diagnostics = channel.channel('test');
189+
expect(diagnostics).toBeDefined();
190+
});
191+
192+
expect(typeof dns.lookup).toBe('function');
193+
194+
expectIfAvailable(dnsPromises, (dnsPromises) => {
195+
expect(typeof dnsPromises.lookup).toBe('function');
196+
});
197+
198+
const d = domain.create();
199+
expect(d).toBeDefined();
200+
201+
const emitter = new events.EventEmitter();
202+
const emitter2 = new events();
203+
expect(emitter).toBeDefined();
204+
expect(emitter2).toBeDefined();
205+
expect(events.EventEmitter).toBe(events);
206+
207+
expect(typeof fs.existsSync).toBe('function');
208+
209+
expectIfAvailable(fsPromises, (fsPromises) => {
210+
expect(typeof fsPromises.readFile).toBe('function');
211+
});
212+
213+
const httpServer = http.createServer();
214+
expect(httpServer).toBeDefined();
215+
httpServer.close();
216+
217+
expect(typeof http2.createSecureServer).toBe('function');
218+
expect(typeof https.createServer).toBe('function');
219+
expect(typeof inspector.url).toBe('function');
220+
221+
expectIfAvailable(inspectorPromises, (inspectorPromises) => {
222+
expect(typeof inspectorPromises.Session).toBe('function');
223+
});
224+
225+
expect(Array.isArray(moduleBuiltin.builtinModules)).toBe(true);
226+
227+
const netServer = net.createServer();
228+
expect(netServer).toBeDefined();
229+
netServer.close();
230+
231+
expect(typeof os.platform()).toBe('string');
232+
expect(path.extname('foo.js')).toBe('.js');
233+
234+
expectIfAvailable(pathPosix, (pathPosix) => {
235+
expect(pathPosix.join('/foo', 'bar')).toBe('/foo/bar');
236+
});
237+
238+
expectIfAvailable(pathWin32, (pathWin32) => {
239+
expect(pathWin32.join('C:\\foo', 'bar')).toBe('C:\\foo\\bar');
240+
});
241+
242+
expect(perfHooks.performance).toBeDefined();
243+
expect(typeof processBuiltin.platform).toBe('string');
244+
expect(punycode.encode('mañana')).toBe('maana-pta');
245+
246+
expect(querystring.parse('foo=bar&baz=qux')).toEqual({
247+
foo: 'bar',
248+
baz: 'qux'
249+
});
250+
251+
expect(typeof readline.createInterface).toBe('function');
252+
253+
expectIfAvailable(readlinePromises, (readlinePromises) => {
254+
expect(typeof readlinePromises.createInterface).toBe('function');
255+
});
256+
257+
expect(typeof repl.start).toBe('function');
258+
259+
const readable = new stream.Readable();
260+
expect(readable).toBeDefined();
261+
262+
expectIfAvailable(streamConsumers, (streamConsumers) => {
263+
expect(typeof streamConsumers.text).toBe('function');
264+
});
265+
266+
expectIfAvailable(streamPromises, (streamPromises) => {
267+
expect(typeof streamPromises.pipeline).toBe('function');
268+
});
269+
270+
expectIfAvailable(streamWeb, (streamWeb) => {
271+
expect(typeof streamWeb.ReadableStream).toBe('function');
272+
});
273+
274+
const decoder = new stringDecoder.StringDecoder('utf8');
275+
expect(decoder.write(Buffer.from('hello'))).toBe('hello');
276+
277+
expect(sys).toEqual(util);
278+
expect(typeof timers.setTimeout).toBe('function');
279+
280+
expectIfAvailable(timersPromises, (timersPromises) => {
281+
expect(typeof timersPromises.setTimeout).toBe('function');
282+
});
283+
284+
expect(typeof tls.createServer).toBe('function');
285+
expect(typeof traceEvents.getEnabledCategories).toBe('function');
286+
expect(typeof tty.isatty).toBe('function');
287+
288+
const parsed = url.parse('http://example.com/path');
289+
expect(parsed.hostname).toBe('example.com');
290+
291+
expect(util.format('Hello %s', 'World')).toBe('Hello World');
292+
293+
expectIfAvailable(utilTypes, (utilTypes) => {
294+
expect(utilTypes.isPromise(Promise.resolve())).toBe(true);
295+
});
296+
297+
const stats = v8.getHeapStatistics();
298+
expect(stats).toBeDefined();
299+
300+
const result = vm.runInNewContext('1 + 1');
301+
expect(result).toBe(2);
302+
303+
expectIfAvailable(wasi, (wasi) => {
304+
expect(typeof wasi.WASI).toBe('function');
305+
});
306+
307+
expectIfAvailable(workerThreads, (workerThreads) => {
308+
expect(typeof workerThreads.isMainThread).toBe('boolean');
309+
});
310+
311+
const compressed = zlib.gzipSync('test data');
312+
expect(Buffer.isBuffer(compressed)).toBe(true);
313+
});

0 commit comments

Comments
 (0)