|
| 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