github.com/evanw/esbuild@v0.21.4/scripts/js-api-tests.js (about) 1 const { installForTests, removeRecursiveSync, writeFileAtomic } = require('./esbuild') 2 const child_process = require('child_process') 3 const assert = require('assert') 4 const path = require('path') 5 const http = require('http') 6 const https = require('https') 7 const events = require('events') 8 const fs = require('fs') 9 const vm = require('vm') 10 11 const readFileAsync = fs.promises.readFile 12 const writeFileAsync = fs.promises.writeFile 13 const mkdirAsync = fs.promises.mkdir 14 15 const repoDir = path.dirname(__dirname) 16 const rootTestDir = path.join(repoDir, 'scripts', '.js-api-tests') 17 const errorIcon = process.platform !== 'win32' ? '✘' : 'X' 18 19 let buildTests = { 20 async errorIfEntryPointsNotArray({ esbuild }) { 21 try { 22 await esbuild.build({ 23 entryPoints: 'this is not an array', 24 logLevel: 'silent', 25 }) 26 throw new Error('Expected build failure'); 27 } catch (e) { 28 if (!e.errors || !e.errors[0] || e.errors[0].text !== '"entryPoints" must be an array or an object') { 29 throw e; 30 } 31 } 32 }, 33 34 async errorIfBadWorkingDirectory({ esbuild }) { 35 try { 36 await esbuild.build({ 37 absWorkingDir: 'what is this? certainly not an absolute path', 38 logLevel: 'silent', 39 write: false, 40 }) 41 throw new Error('Expected build failure'); 42 } catch (e) { 43 if (e.message !== 'Build failed with 1 error:\nerror: The working directory ' + 44 '"what is this? certainly not an absolute path" is not an absolute path') { 45 throw e; 46 } 47 } 48 }, 49 50 // Verify that it's possible to disable a loader by setting it to "default". 51 // In particular, verify that it's possible to disable the special loader "" 52 // for extensionless files. 53 async errorIfExtensionlessLoaderIsDisabled({ esbuild, testDir }) { 54 let entry = path.join(testDir, 'entry.js'); 55 let what = path.join(testDir, 'what'); 56 await writeFileAsync(entry, 'import "./what"') 57 await writeFileAsync(what, 'foo()') 58 await esbuild.build({ 59 entryPoints: [entry], 60 bundle: true, 61 write: false, 62 }) 63 try { 64 await esbuild.build({ 65 entryPoints: [entry], 66 bundle: true, 67 write: false, 68 logLevel: 'silent', 69 loader: { '': 'default' }, 70 }) 71 throw new Error('Expected build failure'); 72 } catch (e) { 73 const relPath = path.relative(process.cwd(), what).split(path.sep).join('/') 74 if (!e.errors || !e.errors[0] || e.errors[0].text !== 'Do not know how to load path: ' + relPath) { 75 throw e; 76 } 77 } 78 }, 79 80 async mangleCacheBuild({ esbuild }) { 81 var result = await esbuild.build({ 82 stdin: { 83 contents: `x = { x_: 0, y_: 1, z_: 2 }`, 84 }, 85 mangleProps: /_/, 86 mangleCache: { x_: 'FIXED', z_: false }, 87 write: false, 88 }) 89 assert.strictEqual(result.outputFiles[0].text, 'x = { FIXED: 0, a: 1, z_: 2 };\n') 90 assert.deepStrictEqual(result.mangleCache, { x_: 'FIXED', y_: 'a', z_: false }) 91 }, 92 93 async windowsBackslashPathTest({ esbuild, testDir }) { 94 let entry = path.join(testDir, 'entry.js'); 95 let nested = path.join(testDir, 'nested.js'); 96 let outfile = path.join(testDir, 'out.js'); 97 98 // On Windows, backslash and forward slash should be treated the same 99 fs.writeFileSync(entry, ` 100 import ${JSON.stringify(nested)} 101 import ${JSON.stringify(nested.split(path.sep).join('/'))} 102 `); 103 fs.writeFileSync(nested, `console.log('once')`); 104 105 const result = await esbuild.build({ 106 entryPoints: [entry], 107 outfile, 108 bundle: true, 109 write: false, 110 minify: true, 111 format: 'esm', 112 }) 113 114 assert.strictEqual(result.outputFiles[0].text, 'console.log("once");\n') 115 }, 116 117 async workingDirTest({ esbuild, testDir }) { 118 let aDir = path.join(testDir, 'a'); 119 let bDir = path.join(testDir, 'b'); 120 let aFile = path.join(aDir, 'a-in.js'); 121 let bFile = path.join(bDir, 'b-in.js'); 122 let aOut = path.join(aDir, 'a-out.js'); 123 let bOut = path.join(bDir, 'b-out.js'); 124 fs.mkdirSync(aDir, { recursive: true }); 125 fs.mkdirSync(bDir, { recursive: true }); 126 fs.writeFileSync(aFile, 'exports.x = true'); 127 fs.writeFileSync(bFile, 'exports.y = true'); 128 129 await Promise.all([ 130 esbuild.build({ 131 entryPoints: [path.basename(aFile)], 132 outfile: path.basename(aOut), 133 absWorkingDir: aDir, 134 }), 135 esbuild.build({ 136 entryPoints: [path.basename(bFile)], 137 outfile: path.basename(bOut), 138 absWorkingDir: bDir, 139 }), 140 ]); 141 142 assert.strictEqual(require(aOut).x, true) 143 assert.strictEqual(require(bOut).y, true) 144 }, 145 146 async aliasValidity({ esbuild }) { 147 const valid = async alias => { 148 const result = await esbuild.build({ 149 stdin: { contents: 'import ' + JSON.stringify(alias) }, 150 bundle: true, 151 alias: { [alias]: 'foo' }, 152 external: ['foo'], 153 format: 'esm', 154 write: false, 155 }) 156 assert.strictEqual(result.outputFiles[0].text, '// <stdin>\nimport "foo";\n') 157 } 158 159 const invalid = async alias => { 160 try { 161 await esbuild.build({ 162 bundle: true, 163 alias: { [alias]: 'foo' }, 164 logLevel: 'silent', 165 }) 166 } catch { 167 return 168 } 169 throw new Error('Expected an error for alias: ' + alias) 170 } 171 172 await Promise.all([ 173 valid('foo'), 174 valid('foo/bar'), 175 valid('@foo'), 176 valid('@foo/bar'), 177 valid('@foo/bar/baz'), 178 179 invalid('./foo'), 180 invalid('../foo'), 181 invalid('/foo'), 182 invalid('C:\\foo'), 183 invalid('.foo'), 184 invalid('foo/'), 185 invalid('@foo/'), 186 invalid('foo/../bar'), 187 ]) 188 }, 189 190 async pathResolverEACCS({ esbuild, testDir }) { 191 let outerDir = path.join(testDir, 'outer'); 192 let innerDir = path.join(outerDir, 'inner'); 193 let pkgDir = path.join(testDir, 'node_modules', 'pkg'); 194 let entry = path.join(innerDir, 'entry.js'); 195 let sibling = path.join(innerDir, 'sibling.js'); 196 let index = path.join(pkgDir, 'index.js'); 197 let outfile = path.join(innerDir, 'out.js'); 198 fs.mkdirSync(pkgDir, { recursive: true }); 199 fs.mkdirSync(innerDir, { recursive: true }); 200 fs.writeFileSync(entry, ` 201 import a from "./sibling.js" 202 import b from "pkg" 203 export default {a, b} 204 `); 205 fs.writeFileSync(sibling, `export default 'sibling'`); 206 fs.writeFileSync(index, `export default 'pkg'`); 207 fs.chmodSync(outerDir, 0o111); 208 209 try { 210 await esbuild.build({ 211 entryPoints: [entry], 212 bundle: true, 213 outfile, 214 format: 'cjs', 215 }); 216 217 const result = require(outfile); 218 assert.deepStrictEqual(result.default, { a: 'sibling', b: 'pkg' }); 219 } 220 221 finally { 222 // Restore permission when the test ends so test cleanup works 223 fs.chmodSync(outerDir, 0o755); 224 } 225 }, 226 227 async nodePathsTest({ esbuild, testDir }) { 228 let srcDir = path.join(testDir, 'src'); 229 let pkgDir = path.join(testDir, 'pkg'); 230 let outfile = path.join(testDir, 'out.js'); 231 let entry = path.join(srcDir, 'entry.js'); 232 let other = path.join(pkgDir, 'other.js'); 233 fs.mkdirSync(srcDir, { recursive: true }); 234 fs.mkdirSync(pkgDir, { recursive: true }); 235 fs.writeFileSync(entry, `export {x} from 'other'`); 236 fs.writeFileSync(other, `export let x = 123`); 237 238 await esbuild.build({ 239 entryPoints: [entry], 240 outfile, 241 bundle: true, 242 nodePaths: [pkgDir], 243 format: 'cjs', 244 }) 245 246 assert.strictEqual(require(outfile).x, 123) 247 }, 248 249 // A local "node_modules" path should be preferred over "NODE_PATH". 250 // See: https://github.com/evanw/esbuild/issues/1117 251 async nodePathsLocalPreferredTestIssue1117({ esbuild, testDir }) { 252 let srcDir = path.join(testDir, 'src'); 253 let srcOtherDir = path.join(testDir, 'src', 'node_modules', 'other'); 254 let pkgDir = path.join(testDir, 'pkg'); 255 let outfile = path.join(testDir, 'out.js'); 256 let entry = path.join(srcDir, 'entry.js'); 257 let srcOther = path.join(srcOtherDir, 'index.js'); 258 let pkgOther = path.join(pkgDir, 'other.js'); 259 fs.mkdirSync(srcDir, { recursive: true }); 260 fs.mkdirSync(srcOtherDir, { recursive: true }); 261 fs.mkdirSync(pkgDir, { recursive: true }); 262 fs.writeFileSync(entry, `export {x} from 'other'`); 263 fs.writeFileSync(srcOther, `export let x = 234`); 264 fs.writeFileSync(pkgOther, `export let x = 123`); 265 266 await esbuild.build({ 267 entryPoints: [entry], 268 outfile, 269 bundle: true, 270 nodePaths: [pkgDir], 271 format: 'cjs', 272 }) 273 274 assert.strictEqual(require(outfile).x, 234) 275 }, 276 277 async es6_to_cjs({ esbuild, testDir }) { 278 const input = path.join(testDir, 'in.js') 279 const output = path.join(testDir, 'out.js') 280 await writeFileAsync(input, 'export default 123') 281 const value = await esbuild.build({ 282 entryPoints: [input], 283 bundle: true, 284 outfile: output, 285 format: 'cjs', 286 }) 287 assert.strictEqual(value.outputFiles, void 0) 288 const result = require(output) 289 assert.strictEqual(result.default, 123) 290 assert.strictEqual(result.__esModule, true) 291 }, 292 293 // Test recursive directory creation 294 async recursiveMkdir({ esbuild, testDir }) { 295 const input = path.join(testDir, 'in.js') 296 const output = path.join(testDir, 'a/b/c/d/out.js') 297 await writeFileAsync(input, 'export default 123') 298 await esbuild.build({ 299 entryPoints: [input], 300 bundle: true, 301 outfile: output, 302 format: 'cjs', 303 }) 304 const result = require(output) 305 assert.strictEqual(result.default, 123) 306 assert.strictEqual(result.__esModule, true) 307 }, 308 309 async outExtensionJS({ esbuild, testDir }) { 310 const input = path.join(testDir, 'in.js') 311 const output = path.join(testDir, 'in.mjs') 312 await writeFileAsync(input, 'console.log("test")') 313 await esbuild.build({ 314 entryPoints: [input], 315 outdir: testDir, 316 outExtension: { '.js': '.mjs' }, 317 }) 318 const mjs = await readFileAsync(output, 'utf8') 319 assert.strictEqual(mjs, 'console.log("test");\n') 320 }, 321 322 async outExtensionCSS({ esbuild, testDir }) { 323 const input = path.join(testDir, 'in.css') 324 const output = path.join(testDir, 'in.notcss') 325 await writeFileAsync(input, 'body {}') 326 await esbuild.build({ 327 entryPoints: [input], 328 outdir: testDir, 329 outExtension: { '.css': '.notcss' }, 330 }) 331 const notcss = await readFileAsync(output, 'utf8') 332 assert.strictEqual(notcss, 'body {\n}\n') 333 }, 334 335 async sourceMapTrue({ esbuild, testDir }) { 336 const input = path.join(testDir, 'in.js') 337 const output = path.join(testDir, 'out.js') 338 const content = 'exports.foo = 123' 339 await writeFileAsync(input, content) 340 await esbuild.build({ 341 entryPoints: [input], 342 outfile: output, 343 sourcemap: true, 344 }) 345 const result = require(output) 346 assert.strictEqual(result.foo, 123) 347 const outputFile = await readFileAsync(output, 'utf8') 348 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 349 assert.strictEqual(match[1], 'out.js.map') 350 const resultMap = await readFileAsync(output + '.map', 'utf8') 351 const json = JSON.parse(resultMap) 352 assert.strictEqual(json.version, 3) 353 assert.strictEqual(json.sources[0], path.basename(input)) 354 assert.strictEqual(json.sourcesContent[0], content) 355 }, 356 357 async sourceMapLinked({ esbuild, testDir }) { 358 const input = path.join(testDir, 'in.js') 359 const output = path.join(testDir, 'out.js') 360 const content = 'exports.foo = 123' 361 await writeFileAsync(input, content) 362 await esbuild.build({ 363 entryPoints: [input], 364 outfile: output, 365 sourcemap: 'linked', 366 }) 367 const result = require(output) 368 assert.strictEqual(result.foo, 123) 369 const outputFile = await readFileAsync(output, 'utf8') 370 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 371 assert.strictEqual(match[1], 'out.js.map') 372 const resultMap = await readFileAsync(output + '.map', 'utf8') 373 const json = JSON.parse(resultMap) 374 assert.strictEqual(json.version, 3) 375 assert.strictEqual(json.sources[0], path.basename(input)) 376 assert.strictEqual(json.sourcesContent[0], content) 377 }, 378 379 async sourceMapExternal({ esbuild, testDir }) { 380 const input = path.join(testDir, 'in.js') 381 const output = path.join(testDir, 'out.js') 382 const content = 'exports.foo = 123' 383 await writeFileAsync(input, content) 384 await esbuild.build({ 385 entryPoints: [input], 386 outfile: output, 387 sourcemap: 'external', 388 }) 389 const result = require(output) 390 assert.strictEqual(result.foo, 123) 391 const outputFile = await readFileAsync(output, 'utf8') 392 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 393 assert.strictEqual(match, null) 394 const resultMap = await readFileAsync(output + '.map', 'utf8') 395 const json = JSON.parse(resultMap) 396 assert.strictEqual(json.version, 3) 397 assert.strictEqual(json.sources[0], path.basename(input)) 398 assert.strictEqual(json.sourcesContent[0], content) 399 }, 400 401 async sourceMapInline({ esbuild, testDir }) { 402 const input = path.join(testDir, 'in.js') 403 const output = path.join(testDir, 'out.js') 404 const content = 'exports.foo = 123' 405 await writeFileAsync(input, content) 406 await esbuild.build({ 407 entryPoints: [input], 408 outfile: output, 409 sourcemap: 'inline', 410 }) 411 const result = require(output) 412 assert.strictEqual(result.foo, 123) 413 const outputFile = await readFileAsync(output, 'utf8') 414 const match = /\/\/# sourceMappingURL=data:application\/json;base64,(.*)/.exec(outputFile) 415 const json = JSON.parse(Buffer.from(match[1], 'base64').toString()) 416 assert.strictEqual(json.version, 3) 417 assert.strictEqual(json.sources[0], path.basename(input)) 418 assert.strictEqual(json.sourcesContent[0], content) 419 }, 420 421 async sourceMapBoth({ esbuild, testDir }) { 422 const input = path.join(testDir, 'in.js') 423 const output = path.join(testDir, 'out.js') 424 const content = 'exports.foo = 123' 425 await writeFileAsync(input, content) 426 await esbuild.build({ 427 entryPoints: [input], 428 outfile: output, 429 sourcemap: 'both', 430 }) 431 const result = require(output) 432 assert.strictEqual(result.foo, 123) 433 const outputFile = await readFileAsync(output, 'utf8') 434 const match = /\/\/# sourceMappingURL=data:application\/json;base64,(.*)/.exec(outputFile) 435 const json = JSON.parse(Buffer.from(match[1], 'base64').toString()) 436 assert.strictEqual(json.version, 3) 437 assert.strictEqual(json.sources[0], path.basename(input)) 438 assert.strictEqual(json.sourcesContent[0], content) 439 const outputFileMap = await readFileAsync(output + '.map', 'utf8') 440 assert.strictEqual(Buffer.from(match[1], 'base64').toString(), outputFileMap) 441 }, 442 443 async sourceMapIncludeSourcesContent({ esbuild, testDir }) { 444 const input = path.join(testDir, 'in.js') 445 const output = path.join(testDir, 'out.js') 446 const content = 'exports.foo = 123' 447 await writeFileAsync(input, content) 448 await esbuild.build({ 449 entryPoints: [input], 450 outfile: output, 451 sourcemap: true, 452 sourcesContent: true, 453 }) 454 const result = require(output) 455 assert.strictEqual(result.foo, 123) 456 const outputFile = await readFileAsync(output, 'utf8') 457 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 458 assert.strictEqual(match[1], 'out.js.map') 459 const resultMap = await readFileAsync(output + '.map', 'utf8') 460 const json = JSON.parse(resultMap) 461 assert.strictEqual(json.version, 3) 462 assert.strictEqual(json.sources[0], path.basename(input)) 463 assert.strictEqual(json.sourcesContent[0], content) 464 }, 465 466 async sourceMapExcludeSourcesContent({ esbuild, testDir }) { 467 const input = path.join(testDir, 'in.js') 468 const output = path.join(testDir, 'out.js') 469 const content = 'exports.foo = 123' 470 await writeFileAsync(input, content) 471 await esbuild.build({ 472 entryPoints: [input], 473 outfile: output, 474 sourcemap: true, 475 sourcesContent: false, 476 }) 477 const result = require(output) 478 assert.strictEqual(result.foo, 123) 479 const outputFile = await readFileAsync(output, 'utf8') 480 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 481 assert.strictEqual(match[1], 'out.js.map') 482 const resultMap = await readFileAsync(output + '.map', 'utf8') 483 const json = JSON.parse(resultMap) 484 assert.strictEqual(json.version, 3) 485 assert.strictEqual(json.sources[0], path.basename(input)) 486 assert.strictEqual(json.sourcesContent, void 0) 487 }, 488 489 async sourceMapSourceRoot({ esbuild, testDir }) { 490 const input = path.join(testDir, 'in.js') 491 const output = path.join(testDir, 'out.js') 492 const content = 'exports.foo = 123' 493 await writeFileAsync(input, content) 494 await esbuild.build({ 495 entryPoints: [input], 496 outfile: output, 497 sourcemap: true, 498 sourceRoot: 'https://example.com/' 499 }) 500 const result = require(output) 501 assert.strictEqual(result.foo, 123) 502 const outputFile = await readFileAsync(output, 'utf8') 503 const match = /\/\/# sourceMappingURL=(.*)/.exec(outputFile) 504 assert.strictEqual(match[1], 'out.js.map') 505 const resultMap = await readFileAsync(output + '.map', 'utf8') 506 const json = JSON.parse(resultMap) 507 assert.strictEqual(json.version, 3) 508 assert.strictEqual(json.sources[0], path.basename(input)) 509 assert.strictEqual(json.sourceRoot, 'https://example.com/') 510 }, 511 512 async sourceMapWithDisabledFile({ esbuild, testDir }) { 513 const input = path.join(testDir, 'in.js') 514 const disabled = path.join(testDir, 'disabled.js') 515 const packageJSON = path.join(testDir, 'package.json') 516 const output = path.join(testDir, 'out.js') 517 const content = 'exports.foo = require("./disabled")' 518 await writeFileAsync(input, content) 519 await writeFileAsync(disabled, 'module.exports = 123') 520 await writeFileAsync(packageJSON, `{"browser": {"./disabled.js": false}}`) 521 await esbuild.build({ 522 entryPoints: [input], 523 outfile: output, 524 sourcemap: true, 525 bundle: true, 526 }) 527 const result = require(output) 528 assert.strictEqual(result.foo, void 0) 529 const resultMap = await readFileAsync(output + '.map', 'utf8') 530 const json = JSON.parse(resultMap) 531 assert.strictEqual(json.version, 3) 532 assert.strictEqual(json.sources.length, 1) 533 assert.strictEqual(json.sources[0], path.basename(input)) 534 assert.strictEqual(json.sourcesContent[0], content) 535 }, 536 537 async sourceMapWithEmptyFile({ esbuild, testDir }) { 538 const input = path.join(testDir, 'in.js') 539 const empty = path.join(testDir, 'file.empty') 540 const output = path.join(testDir, 'out.js') 541 const content = 'exports.foo = require("./file.empty")' 542 await writeFileAsync(input, content) 543 await writeFileAsync(empty, 'module.exports = 123') 544 await esbuild.build({ 545 entryPoints: [input], 546 outfile: output, 547 sourcemap: true, 548 bundle: true, 549 loader: { '.empty': 'empty' }, 550 }) 551 const result = require(output) 552 assert.strictEqual(result.foo, void 0) 553 const resultMap = await readFileAsync(output + '.map', 'utf8') 554 const json = JSON.parse(resultMap) 555 assert.strictEqual(json.version, 3) 556 assert.strictEqual(json.sources.length, 1) 557 assert.strictEqual(json.sources[0], path.basename(input)) 558 assert.strictEqual(json.sourcesContent[0], content) 559 }, 560 561 async sourceMapWithDisabledModule({ esbuild, testDir }) { 562 const input = path.join(testDir, 'in.js') 563 const disabled = path.join(testDir, 'node_modules', 'disabled', 'index.js') 564 const packageJSON = path.join(testDir, 'package.json') 565 const output = path.join(testDir, 'out.js') 566 const content = 'exports.foo = require("disabled")' 567 await mkdirAsync(path.dirname(disabled), { recursive: true }) 568 await writeFileAsync(input, content) 569 await writeFileAsync(disabled, 'module.exports = 123') 570 await writeFileAsync(packageJSON, `{"browser": {"disabled": false}}`) 571 await esbuild.build({ 572 entryPoints: [input], 573 outfile: output, 574 sourcemap: true, 575 bundle: true, 576 }) 577 const result = require(output) 578 assert.strictEqual(result.foo, void 0) 579 const resultMap = await readFileAsync(output + '.map', 'utf8') 580 const json = JSON.parse(resultMap) 581 assert.strictEqual(json.version, 3) 582 assert.strictEqual(json.sources.length, 1) 583 assert.strictEqual(json.sources[0], path.basename(input)) 584 assert.strictEqual(json.sourcesContent[0], content) 585 }, 586 587 async resolveExtensionOrder({ esbuild, testDir }) { 588 const input = path.join(testDir, 'in.js'); 589 const inputBare = path.join(testDir, 'module.js') 590 const inputSomething = path.join(testDir, 'module.something.js') 591 const output = path.join(testDir, 'out.js') 592 await writeFileAsync(input, 'exports.result = require("./module").foo') 593 await writeFileAsync(inputBare, 'exports.foo = 321') 594 await writeFileAsync(inputSomething, 'exports.foo = 123') 595 await esbuild.build({ 596 entryPoints: [input], 597 outfile: output, 598 format: 'cjs', 599 bundle: true, 600 resolveExtensions: ['.something.js', '.js'], 601 }) 602 assert.strictEqual(require(output).result, 123) 603 }, 604 605 async defineObject({ esbuild, testDir }) { 606 const input = path.join(testDir, 'in.js'); 607 const output = path.join(testDir, 'out.js') 608 await writeFileAsync(input, 'export default {abc, xyz}') 609 await esbuild.build({ 610 entryPoints: [input], 611 outfile: output, 612 format: 'cjs', 613 bundle: true, 614 define: { 615 abc: '["a", "b", "c"]', 616 xyz: '{"x": 1, "y": 2, "z": 3}', 617 }, 618 }) 619 assert.deepStrictEqual(require(output).default, { 620 abc: ['a', 'b', 'c'], 621 xyz: { x: 1, y: 2, z: 3 }, 622 }) 623 }, 624 625 async inject({ esbuild, testDir }) { 626 const input = path.join(testDir, 'in.js'); 627 const inject = path.join(testDir, 'inject.js') 628 const output = path.join(testDir, 'out.js') 629 await writeFileAsync(input, 'export default foo * 10 + 4') 630 await writeFileAsync(inject, 'export let foo = 123') 631 await esbuild.build({ 632 entryPoints: [input], 633 outfile: output, 634 format: 'cjs', 635 bundle: true, 636 inject: [inject], 637 }) 638 assert.strictEqual(require(output).default, 1234) 639 }, 640 641 async minifyWithoutInject({ esbuild, testDir }) { 642 const input = path.join(testDir, 'in.js'); 643 const output = path.join(testDir, 'out.js') 644 await writeFileAsync(input, 'function exampleFn() { return Math.min(4, 3) }') 645 646 const result = await esbuild.build({ 647 entryPoints: [input], 648 outfile: output, 649 write: false, 650 minify: true, 651 }) 652 assert.strictEqual(3, new Function(result.outputFiles[0].text + '\nreturn exampleFn()')()) 653 }, 654 655 // This should be the same as "minifyWithoutInject" above 656 async minifyWithInject({ esbuild, testDir }) { 657 const input = path.join(testDir, 'in.js'); 658 const inject = path.join(testDir, 'inject.js') 659 const output = path.join(testDir, 'out.js') 660 await writeFileAsync(input, 'function exampleFn() { return Math.min(4, 3) }') 661 await writeFileAsync(inject, 'let min = Math.min; export { min as "Math.min" }') 662 663 const result = await esbuild.build({ 664 entryPoints: [input], 665 outfile: output, 666 inject: [inject], 667 write: false, 668 minify: true, 669 }) 670 assert.strictEqual(3, new Function(result.outputFiles[0].text + '\nreturn exampleFn()')()) 671 }, 672 673 async mainFields({ esbuild, testDir }) { 674 const input = path.join(testDir, 'in.js') 675 const output = path.join(testDir, 'out.js') 676 const mainFieldsDir = path.join(testDir, 'node_modules', 'main-fields-test') 677 const mainFieldsA = path.join(mainFieldsDir, 'a.js') 678 const mainFieldsB = path.join(mainFieldsDir, 'b.js') 679 const mainFieldsPackage = path.join(mainFieldsDir, 'package.json') 680 await mkdirAsync(mainFieldsDir, { recursive: true }) 681 await writeFileAsync(input, 'export * from "main-fields-test"') 682 await writeFileAsync(mainFieldsA, 'export let foo = "a"') 683 await writeFileAsync(mainFieldsB, 'export let foo = "b"') 684 await writeFileAsync(mainFieldsPackage, '{ "a": "./a.js", "b": "./b.js", "c": "./c.js" }') 685 await esbuild.build({ 686 entryPoints: [input], 687 outfile: output, 688 bundle: true, 689 format: 'cjs', 690 mainFields: ['c', 'b', 'a'], 691 }) 692 const result = require(output) 693 assert.strictEqual(result.foo, 'b') 694 }, 695 696 async requireAbsolutePath({ esbuild, testDir }) { 697 const input = path.join(testDir, 'in.js') 698 const dependency = path.join(testDir, 'dep.js') 699 const output = path.join(testDir, 'out.js') 700 await writeFileAsync(input, `import value from ${JSON.stringify(dependency)}; export default value`) 701 await writeFileAsync(dependency, `export default 123`) 702 const value = await esbuild.build({ 703 entryPoints: [input], 704 bundle: true, 705 outfile: output, 706 format: 'cjs', 707 }) 708 assert.strictEqual(value.outputFiles, void 0) 709 const result = require(output) 710 assert.strictEqual(result.default, 123) 711 assert.strictEqual(result.__esModule, true) 712 }, 713 714 async buildLoaderStdinBase64({ esbuild }) { 715 // UTF-16 716 var result = await esbuild.build({ 717 stdin: { 718 contents: `\xFF`, 719 loader: 'base64', 720 }, 721 write: false, 722 }) 723 assert.strictEqual(result.outputFiles[0].text, `module.exports = "w78=";\n`) 724 725 // Binary 726 var result = await esbuild.build({ 727 stdin: { 728 contents: new Uint8Array([0xFF]), 729 loader: 'base64', 730 }, 731 write: false, 732 }) 733 assert.strictEqual(result.outputFiles[0].text, `module.exports = "/w==";\n`) 734 }, 735 736 async fileLoader({ esbuild, testDir }) { 737 const input = path.join(testDir, 'in.js') 738 const data = path.join(testDir, 'data.bin') 739 const output = path.join(testDir, 'out.js') 740 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 741 await writeFileAsync(data, `stuff`) 742 const value = await esbuild.build({ 743 entryPoints: [input], 744 bundle: true, 745 outfile: output, 746 format: 'cjs', 747 loader: { '.bin': 'file' }, 748 }) 749 assert.strictEqual(value.outputFiles, void 0) 750 const result = require(output) 751 assert.strictEqual(result.value, './data-BYATPJRB.bin') 752 assert.strictEqual(result.__esModule, true) 753 }, 754 755 async fileLoaderEntryHash({ esbuild, testDir }) { 756 const input = path.join(testDir, 'index.js') 757 const data = path.join(testDir, 'data.bin') 758 const outdir = path.join(testDir, 'out') 759 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 760 await writeFileAsync(data, `stuff`) 761 const result1 = await esbuild.build({ 762 entryPoints: [input], 763 bundle: true, 764 outdir, 765 format: 'cjs', 766 loader: { '.bin': 'file' }, 767 entryNames: '[name]-[hash]', 768 write: false, 769 }) 770 await writeFileAsync(data, `more stuff`) 771 const result2 = await esbuild.build({ 772 entryPoints: [input], 773 bundle: true, 774 outdir, 775 format: 'cjs', 776 loader: { '.bin': 'file' }, 777 entryNames: '[name]-[hash]', 778 write: false, 779 }) 780 assert.strictEqual(result1.outputFiles.length, 2) 781 assert.strictEqual(result2.outputFiles.length, 2) 782 783 // Make sure each path is unique. This tests for a bug where the hash in 784 // the output filename corresponding to the "index.js" entry point wasn't 785 // including the filename for the "file" loader. 786 assert.strictEqual(new Set(result1.outputFiles.concat(result2.outputFiles).map(x => x.path)).size, 4) 787 }, 788 789 async fileLoaderEntryHashNoChange({ esbuild, testDir }) { 790 const input = path.join(testDir, 'index.js') 791 const data = path.join(testDir, 'data.bin') 792 const outdir = path.join(testDir, 'out') 793 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 794 await writeFileAsync(data, `stuff`) 795 const result1 = await esbuild.build({ 796 entryPoints: [input], 797 bundle: true, 798 outdir, 799 format: 'cjs', 800 loader: { '.bin': 'file' }, 801 entryNames: '[name]-[hash]', 802 assetNames: '[name]', 803 write: false, 804 }) 805 await writeFileAsync(data, `more stuff`) 806 const result2 = await esbuild.build({ 807 entryPoints: [input], 808 bundle: true, 809 outdir, 810 format: 'cjs', 811 loader: { '.bin': 'file' }, 812 entryNames: '[name]-[hash]', 813 assetNames: '[name]', 814 write: false, 815 }) 816 assert.strictEqual(result1.outputFiles.length, 2) 817 assert.strictEqual(result2.outputFiles.length, 2) 818 819 // The paths should be the same. The hash augmentation from the previous 820 // test should only be checking for a file name difference, not a difference 821 // in content, because the JS output file only contains the file name for 822 // something using the "file" loader. 823 assert.strictEqual(new Set(result1.outputFiles.concat(result2.outputFiles).map(x => x.path)).size, 2) 824 }, 825 826 async splittingPublicPath({ esbuild, testDir }) { 827 const input1 = path.join(testDir, 'a', 'in1.js') 828 const input2 = path.join(testDir, 'b', 'in2.js') 829 const shared = path.join(testDir, 'c', 'shared.js') 830 const outdir = path.join(testDir, 'out') 831 await mkdirAsync(path.dirname(input1), { recursive: true }) 832 await mkdirAsync(path.dirname(input2), { recursive: true }) 833 await mkdirAsync(path.dirname(shared), { recursive: true }) 834 await writeFileAsync(input1, `export {default as input1} from ${JSON.stringify(shared)}`) 835 await writeFileAsync(input2, `export {default as input2} from ${JSON.stringify(shared)}`) 836 await writeFileAsync(shared, `export default function foo() { return 123 }`) 837 const value = await esbuild.build({ 838 entryPoints: [input1, input2], 839 bundle: true, 840 outdir, 841 format: 'esm', 842 splitting: true, 843 publicPath: 'https://www.example.com/assets', 844 write: false, 845 }) 846 assert.deepStrictEqual(value.outputFiles.length, 3) 847 assert.deepStrictEqual(value.outputFiles[0].path, path.join(outdir, 'a', 'in1.js')) 848 assert.deepStrictEqual(value.outputFiles[1].path, path.join(outdir, 'b', 'in2.js')) 849 assert.deepStrictEqual(value.outputFiles[2].path, path.join(outdir, 'chunk-3MCOY2GR.js')) 850 assert.deepStrictEqual(value.outputFiles[0].text, `import { 851 foo 852 } from "https://www.example.com/assets/chunk-3MCOY2GR.js"; 853 export { 854 foo as input1 855 }; 856 `) 857 assert.deepStrictEqual(value.outputFiles[1].text, `import { 858 foo 859 } from "https://www.example.com/assets/chunk-3MCOY2GR.js"; 860 export { 861 foo as input2 862 }; 863 `) 864 assert.deepStrictEqual(value.outputFiles[2].text, `// scripts/.js-api-tests/splittingPublicPath/c/shared.js 865 function foo() { 866 return 123; 867 } 868 869 export { 870 foo 871 }; 872 `) 873 }, 874 875 async publicPathHashing({ esbuild, testDir }) { 876 const input = path.join(testDir, 'in.js') 877 const data = path.join(testDir, 'data.bin') 878 const outdir = path.join(testDir, 'out') 879 await writeFileAsync(input, `export {default} from ${JSON.stringify(data)}`) 880 await writeFileAsync(data, `stuff`) 881 882 const [result1, result2] = await Promise.all([ 883 esbuild.build({ 884 entryPoints: [input], 885 bundle: true, 886 outdir, 887 format: 'cjs', 888 loader: { '.bin': 'file' }, 889 entryNames: '[name]-[hash]', 890 write: false, 891 }), 892 esbuild.build({ 893 entryPoints: [input], 894 bundle: true, 895 outdir, 896 format: 'cjs', 897 loader: { '.bin': 'file' }, 898 entryNames: '[name]-[hash]', 899 publicPath: 'https://www.example.com', 900 write: false, 901 }), 902 ]) 903 904 const names1 = result1.outputFiles.map(x => path.basename(x.path)).sort() 905 const names2 = result2.outputFiles.map(x => path.basename(x.path)).sort() 906 907 // Check that the public path is included in chunk hashes but not asset hashes 908 assert.deepStrictEqual(names1, ['data-BYATPJRB.bin', 'in-6QN3TZ3A.js']) 909 assert.deepStrictEqual(names2, ['data-BYATPJRB.bin', 'in-EJERHMG4.js']) 910 }, 911 912 async fileLoaderPublicPath({ esbuild, testDir }) { 913 const input = path.join(testDir, 'in.js') 914 const data = path.join(testDir, 'data.bin') 915 const output = path.join(testDir, 'out.js') 916 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 917 await writeFileAsync(data, `stuff`) 918 const value = await esbuild.build({ 919 entryPoints: [input], 920 bundle: true, 921 outfile: output, 922 format: 'cjs', 923 loader: { '.bin': 'file' }, 924 publicPath: 'https://www.example.com/assets', 925 }) 926 assert.strictEqual(value.outputFiles, void 0) 927 const result = require(output) 928 assert.strictEqual(result.value, 'https://www.example.com/assets/data-BYATPJRB.bin') 929 assert.strictEqual(result.__esModule, true) 930 }, 931 932 async fileLoaderCSS({ esbuild, testDir }) { 933 const input = path.join(testDir, 'in.css') 934 const data = path.join(testDir, 'data.bin') 935 const output = path.join(testDir, 'out.css') 936 await writeFileAsync(input, `body { background: url(${JSON.stringify(data)}) }`) 937 await writeFileAsync(data, `stuff`) 938 const value = await esbuild.build({ 939 entryPoints: [input], 940 bundle: true, 941 outfile: output, 942 loader: { '.bin': 'file' }, 943 publicPath: 'https://www.example.com/assets', 944 }) 945 assert.strictEqual(value.outputFiles, void 0) 946 assert.strictEqual(await readFileAsync(output, 'utf8'), `/* scripts/.js-api-tests/fileLoaderCSS/in.css */ 947 body { 948 background: url("https://www.example.com/assets/data-BYATPJRB.bin"); 949 } 950 `) 951 }, 952 953 async fileLoaderWithAssetPath({ esbuild, testDir }) { 954 const input = path.join(testDir, 'in.js') 955 const data = path.join(testDir, 'data.bin') 956 const outdir = path.join(testDir, 'out') 957 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 958 await writeFileAsync(data, `stuff`) 959 const value = await esbuild.build({ 960 entryPoints: [input], 961 bundle: true, 962 outdir, 963 format: 'cjs', 964 loader: { '.bin': 'file' }, 965 assetNames: 'assets/name=[name]/hash=[hash]', 966 }) 967 assert.strictEqual(value.outputFiles, void 0) 968 const result = require(path.join(outdir, path.basename(input))) 969 assert.strictEqual(result.value, './assets/name=data/hash=BYATPJRB.bin') 970 assert.strictEqual(result.__esModule, true) 971 const stuff = fs.readFileSync(path.join(outdir, result.value), 'utf8') 972 assert.strictEqual(stuff, 'stuff') 973 }, 974 975 async fileLoaderWithAssetPathAndPublicPath({ esbuild, testDir }) { 976 const input = path.join(testDir, 'in.js') 977 const data = path.join(testDir, 'data.bin') 978 const outdir = path.join(testDir, 'out') 979 await writeFileAsync(input, `export {default as value} from ${JSON.stringify(data)}`) 980 await writeFileAsync(data, `stuff`) 981 const value = await esbuild.build({ 982 entryPoints: [input], 983 bundle: true, 984 outdir, 985 format: 'cjs', 986 loader: { '.bin': 'file' }, 987 assetNames: 'assets/name=[name]/hash=[hash]', 988 publicPath: 'https://www.example.com', 989 }) 990 assert.strictEqual(value.outputFiles, void 0) 991 const result = require(path.join(outdir, path.basename(input))) 992 assert.strictEqual(result.value, 'https://www.example.com/assets/name=data/hash=BYATPJRB.bin') 993 assert.strictEqual(result.__esModule, true) 994 const stuff = fs.readFileSync(path.join(outdir, 'assets', 'name=data', 'hash=BYATPJRB.bin'), 'utf8') 995 assert.strictEqual(stuff, 'stuff') 996 }, 997 998 async fileLoaderBinaryVsText({ esbuild, testDir }) { 999 const input = path.join(testDir, 'in.js') 1000 const valid = path.join(testDir, 'valid.bin') 1001 const invalid = path.join(testDir, 'invalid.bin') 1002 const output = path.join(testDir, 'out.js') 1003 await writeFileAsync(input, ` 1004 import valid from ${JSON.stringify(valid)} 1005 import invalid from ${JSON.stringify(invalid)} 1006 console.log(valid, invalid) 1007 `) 1008 await writeFileAsync(valid, Buffer.from([0xCF, 0x80])) 1009 await writeFileAsync(invalid, Buffer.from([0x80, 0xCF])) 1010 const value = await esbuild.build({ 1011 entryPoints: [input], 1012 bundle: true, 1013 outfile: output, 1014 format: 'cjs', 1015 loader: { '.bin': 'file' }, 1016 write: false, 1017 }) 1018 assert.strictEqual(value.outputFiles.length, 3) 1019 1020 // Valid UTF-8 should decode correctly 1021 assert.deepEqual(value.outputFiles[0].contents, new Uint8Array([207, 128])) 1022 assert.strictEqual(value.outputFiles[0].text, 'π') 1023 1024 // Invalid UTF-8 should be preserved as bytes but should be replaced by the U+FFFD replacement character when decoded 1025 assert.deepEqual(value.outputFiles[1].contents, new Uint8Array([128, 207])) 1026 assert.strictEqual(value.outputFiles[1].text, '\uFFFD\uFFFD') 1027 }, 1028 1029 async metafile({ esbuild, testDir }) { 1030 const entry = path.join(testDir, 'entry.js') 1031 const imported = path.join(testDir, 'imported.js') 1032 const text = path.join(testDir, 'text.txt') 1033 const css = path.join(testDir, 'example.css') 1034 const outputJS = path.join(testDir, 'out.js') 1035 const outputCSS = path.join(testDir, 'out.css') 1036 await writeFileAsync(entry, ` 1037 import x from "./imported" 1038 const y = require("./text.txt") 1039 import * as z from "./example.css" 1040 console.log(x, y, z) 1041 `) 1042 await writeFileAsync(imported, 'export default 123') 1043 await writeFileAsync(text, 'some text') 1044 await writeFileAsync(css, 'body { some: css; }') 1045 const result = await esbuild.build({ 1046 entryPoints: [entry], 1047 bundle: true, 1048 outfile: outputJS, 1049 metafile: true, 1050 sourcemap: true, 1051 loader: { '.txt': 'file' }, 1052 }) 1053 1054 const json = result.metafile 1055 assert.strictEqual(Object.keys(json.inputs).length, 4) 1056 assert.strictEqual(Object.keys(json.outputs).length, 5) 1057 const cwd = process.cwd() 1058 const makePath = absPath => path.relative(cwd, absPath).split(path.sep).join('/') 1059 1060 // Check inputs 1061 assert.deepStrictEqual(json.inputs[makePath(entry)].bytes, 144) 1062 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, [ 1063 { path: makePath(imported), kind: 'import-statement', original: './imported' }, 1064 { path: makePath(css), kind: 'import-statement', original: './example.css' }, 1065 { path: makePath(text), kind: 'require-call', original: './text.txt' }, 1066 ]) 1067 assert.deepStrictEqual(json.inputs[makePath(imported)].bytes, 18) 1068 assert.deepStrictEqual(json.inputs[makePath(imported)].imports, []) 1069 assert.deepStrictEqual(json.inputs[makePath(text)].bytes, 9) 1070 assert.deepStrictEqual(json.inputs[makePath(text)].imports, []) 1071 assert.deepStrictEqual(json.inputs[makePath(css)].bytes, 19) 1072 assert.deepStrictEqual(json.inputs[makePath(css)].imports, []) 1073 1074 // Check outputs 1075 assert.strictEqual(typeof json.outputs[makePath(outputJS)].bytes, 'number') 1076 assert.strictEqual(typeof json.outputs[makePath(outputCSS)].bytes, 'number') 1077 assert.strictEqual(typeof json.outputs[makePath(outputJS) + '.map'].bytes, 'number') 1078 assert.strictEqual(typeof json.outputs[makePath(outputCSS) + '.map'].bytes, 'number') 1079 assert.strictEqual(json.outputs[makePath(outputJS)].entryPoint, makePath(entry)) 1080 assert.strictEqual(json.outputs[makePath(outputCSS)].entryPoint, undefined) // This is deliberately undefined 1081 assert.deepStrictEqual(json.outputs[makePath(outputJS) + '.map'].imports, []) 1082 assert.deepStrictEqual(json.outputs[makePath(outputJS) + '.map'].exports, []) 1083 assert.deepStrictEqual(json.outputs[makePath(outputJS) + '.map'].inputs, {}) 1084 assert.deepStrictEqual(json.outputs[makePath(outputCSS) + '.map'].imports, []) 1085 assert.deepStrictEqual(json.outputs[makePath(outputCSS) + '.map'].exports, []) 1086 assert.deepStrictEqual(json.outputs[makePath(outputCSS) + '.map'].inputs, {}) 1087 1088 // Check inputs for main output 1089 const outputInputs = json.outputs[makePath(outputJS)].inputs 1090 assert.strictEqual(Object.keys(outputInputs).length, 4) 1091 assert.strictEqual(typeof outputInputs[makePath(entry)].bytesInOutput, 'number') 1092 assert.strictEqual(typeof outputInputs[makePath(imported)].bytesInOutput, 'number') 1093 assert.strictEqual(typeof outputInputs[makePath(text)].bytesInOutput, 'number') 1094 assert.strictEqual(typeof outputInputs[makePath(css)].bytesInOutput, 'number') 1095 }, 1096 1097 async metafileSplitting({ esbuild, testDir }) { 1098 const entry1 = path.join(testDir, 'entry1.js') 1099 const entry2 = path.join(testDir, 'entry2.js') 1100 const imported = path.join(testDir, 'imported.js') 1101 const outdir = path.join(testDir, 'out') 1102 await writeFileAsync(entry1, ` 1103 import x, {f1} from "./${path.basename(imported)}" 1104 console.log(1, x, f1()) 1105 export {x} 1106 `) 1107 await writeFileAsync(entry2, ` 1108 import x, {f2} from "./${path.basename(imported)}" 1109 console.log('entry 2', x, f2()) 1110 export {x as y} 1111 `) 1112 await writeFileAsync(imported, ` 1113 export default 123 1114 export function f1() {} 1115 export function f2() {} 1116 console.log('shared') 1117 `) 1118 const result = await esbuild.build({ 1119 entryPoints: [entry1, entry2], 1120 bundle: true, 1121 outdir, 1122 metafile: true, 1123 splitting: true, 1124 format: 'esm', 1125 }) 1126 1127 const json = result.metafile 1128 assert.strictEqual(Object.keys(json.inputs).length, 3) 1129 assert.strictEqual(Object.keys(json.outputs).length, 3) 1130 const cwd = process.cwd() 1131 const makeOutPath = basename => path.relative(cwd, path.join(outdir, basename)).split(path.sep).join('/') 1132 const makeInPath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1133 1134 // Check metafile 1135 const inEntry1 = makeInPath(entry1); 1136 const inEntry2 = makeInPath(entry2); 1137 const inImported = makeInPath(imported); 1138 const chunk = 'chunk-YNV25ITT.js'; 1139 const outEntry1 = makeOutPath(path.basename(entry1)); 1140 const outEntry2 = makeOutPath(path.basename(entry2)); 1141 const outChunk = makeOutPath(chunk); 1142 1143 assert.deepStrictEqual(json.inputs[inEntry1], { 1144 bytes: 94, 1145 imports: [{ path: inImported, kind: 'import-statement', original: './' + path.basename(imported) }], 1146 format: 'esm', 1147 }) 1148 assert.deepStrictEqual(json.inputs[inEntry2], { 1149 bytes: 107, 1150 imports: [{ path: inImported, kind: 'import-statement', original: './' + path.basename(imported) }], 1151 format: 'esm', 1152 }) 1153 assert.deepStrictEqual(json.inputs[inImported], { 1154 bytes: 118, 1155 imports: [], 1156 format: 'esm', 1157 }) 1158 1159 assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }]) 1160 assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }]) 1161 assert.deepStrictEqual(json.outputs[outChunk].imports, []) 1162 1163 assert.deepStrictEqual(json.outputs[outEntry1].exports, ['x']) 1164 assert.deepStrictEqual(json.outputs[outEntry2].exports, ['y']) 1165 assert.deepStrictEqual(json.outputs[outChunk].exports, ['f1', 'f2', 'imported_default']) 1166 1167 assert.deepStrictEqual(json.outputs[outEntry1].inputs, { [inEntry1]: { bytesInOutput: 40 } }) 1168 assert.deepStrictEqual(json.outputs[outEntry2].inputs, { [inEntry2]: { bytesInOutput: 48 } }) 1169 assert.deepStrictEqual(json.outputs[outChunk].inputs, { [inImported]: { bytesInOutput: 87 } }) 1170 }, 1171 1172 async metafileSplittingPublicPath({ esbuild, testDir }) { 1173 const entry1 = path.join(testDir, 'entry1.js') 1174 const entry2 = path.join(testDir, 'entry2.js') 1175 const imported = path.join(testDir, 'imported.js') 1176 const outdir = path.join(testDir, 'out') 1177 await writeFileAsync(entry1, ` 1178 import x, {f1} from "./${path.basename(imported)}" 1179 console.log(1, x, f1()) 1180 export {x} 1181 `) 1182 await writeFileAsync(entry2, ` 1183 import x, {f2} from "./${path.basename(imported)}" 1184 console.log('entry 2', x, f2()) 1185 export {x as y} 1186 `) 1187 await writeFileAsync(imported, ` 1188 export default 123 1189 export function f1() {} 1190 export function f2() {} 1191 console.log('shared') 1192 `) 1193 const result = await esbuild.build({ 1194 entryPoints: [entry1, entry2], 1195 bundle: true, 1196 outdir, 1197 metafile: true, 1198 splitting: true, 1199 format: 'esm', 1200 publicPath: 'public', 1201 }) 1202 1203 const json = result.metafile 1204 assert.strictEqual(Object.keys(json.inputs).length, 3) 1205 assert.strictEqual(Object.keys(json.outputs).length, 3) 1206 const cwd = process.cwd() 1207 const makeOutPath = basename => path.relative(cwd, path.join(outdir, basename)).split(path.sep).join('/') 1208 const makeInPath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1209 1210 // Check metafile 1211 const inEntry1 = makeInPath(entry1); 1212 const inEntry2 = makeInPath(entry2); 1213 const inImported = makeInPath(imported); 1214 const chunk = 'chunk-ELD4XOGW.js'; 1215 const outEntry1 = makeOutPath(path.basename(entry1)); 1216 const outEntry2 = makeOutPath(path.basename(entry2)); 1217 const outChunk = makeOutPath(chunk); 1218 1219 assert.deepStrictEqual(json.inputs[inEntry1], { 1220 bytes: 94, 1221 imports: [{ path: inImported, kind: 'import-statement', original: './' + path.basename(imported) }], 1222 format: 'esm', 1223 }) 1224 assert.deepStrictEqual(json.inputs[inEntry2], { 1225 bytes: 107, 1226 imports: [{ path: inImported, kind: 'import-statement', original: './' + path.basename(imported) }], 1227 format: 'esm', 1228 }) 1229 assert.deepStrictEqual(json.inputs[inImported], { 1230 bytes: 118, 1231 imports: [], 1232 format: 'esm', 1233 }) 1234 1235 assert.deepStrictEqual(json.outputs[outEntry1].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }]) 1236 assert.deepStrictEqual(json.outputs[outEntry2].imports, [{ path: makeOutPath(chunk), kind: 'import-statement' }]) 1237 assert.deepStrictEqual(json.outputs[outChunk].imports, []) 1238 1239 assert.deepStrictEqual(json.outputs[outEntry1].exports, ['x']) 1240 assert.deepStrictEqual(json.outputs[outEntry2].exports, ['y']) 1241 assert.deepStrictEqual(json.outputs[outChunk].exports, ['f1', 'f2', 'imported_default']) 1242 1243 assert.deepStrictEqual(json.outputs[outEntry1].inputs, { [inEntry1]: { bytesInOutput: 40 } }) 1244 assert.deepStrictEqual(json.outputs[outEntry2].inputs, { [inEntry2]: { bytesInOutput: 48 } }) 1245 assert.deepStrictEqual(json.outputs[outChunk].inputs, { [inImported]: { bytesInOutput: 87 } }) 1246 }, 1247 1248 async metafileSplittingDoubleDynamicImport({ esbuild, testDir }) { 1249 const entry = path.join(testDir, 'entry.js') 1250 const importDir = path.join(testDir, 'import-dir') 1251 const import1 = path.join(importDir, 'import1.js') 1252 const import2 = path.join(importDir, 'import2.js') 1253 const shared = path.join(testDir, 'shared.js') 1254 const outdir = path.join(testDir, 'out') 1255 const makeImportPath = (importing, imported) => './' + path.relative(path.dirname(importing), imported).split(path.sep).join('/') 1256 await mkdirAsync(importDir) 1257 await writeFileAsync(entry, ` 1258 import ${JSON.stringify(makeImportPath(entry, shared))} 1259 import(${JSON.stringify(makeImportPath(entry, import1))}) 1260 import(${JSON.stringify(makeImportPath(entry, import2))}) 1261 `) 1262 await writeFileAsync(import1, ` 1263 import ${JSON.stringify(makeImportPath(import1, shared))} 1264 `) 1265 await writeFileAsync(import2, ` 1266 import ${JSON.stringify(makeImportPath(import2, shared))} 1267 `) 1268 await writeFileAsync(shared, ` 1269 console.log('side effect') 1270 `) 1271 const result = await esbuild.build({ 1272 entryPoints: [entry], 1273 bundle: true, 1274 outdir, 1275 metafile: true, 1276 splitting: true, 1277 format: 'esm', 1278 }) 1279 1280 const json = result.metafile 1281 assert.strictEqual(Object.keys(json.inputs).length, 4) 1282 assert.strictEqual(Object.keys(json.outputs).length, 4) 1283 const cwd = process.cwd() 1284 const makeOutPath = basename => path.relative(cwd, path.join(outdir, basename)).split(path.sep).join('/') 1285 const makeInPath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1286 1287 // Check metafile 1288 const inEntry = makeInPath(entry); 1289 const inImport1 = makeInPath(import1); 1290 const inImport2 = makeInPath(import2); 1291 const inShared = makeInPath(shared); 1292 const chunk = 'chunk-3GRHLZ7X.js'; 1293 const outEntry = makeOutPath(path.relative(testDir, entry)); 1294 const outImport1 = makeOutPath('import1-SELM3ZIG.js'); 1295 const outImport2 = makeOutPath('import2-3GSTEHBF.js'); 1296 const outChunk = makeOutPath(chunk); 1297 1298 assert.deepStrictEqual(json.inputs[inEntry], { 1299 bytes: 112, 1300 imports: [ 1301 { path: inShared, kind: 'import-statement', original: makeImportPath(entry, shared) }, 1302 { path: inImport1, kind: 'dynamic-import', original: makeImportPath(entry, import1) }, 1303 { path: inImport2, kind: 'dynamic-import', original: makeImportPath(entry, import2) }, 1304 ], 1305 format: 'esm', 1306 }) 1307 assert.deepStrictEqual(json.inputs[inImport1], { 1308 bytes: 35, 1309 imports: [ 1310 { path: inShared, kind: 'import-statement', original: makeImportPath(import1, shared) }, 1311 ], 1312 format: 'esm', 1313 }) 1314 assert.deepStrictEqual(json.inputs[inImport2], { 1315 bytes: 35, 1316 imports: [ 1317 { path: inShared, kind: 'import-statement', original: makeImportPath(import2, shared) }, 1318 ], 1319 format: 'esm', 1320 }) 1321 assert.deepStrictEqual(json.inputs[inShared], { bytes: 38, imports: [] }) 1322 1323 assert.deepStrictEqual(Object.keys(json.outputs), [ 1324 outEntry, 1325 outImport1, 1326 outImport2, 1327 outChunk, 1328 ]) 1329 1330 assert.deepStrictEqual(json.outputs[outEntry].imports, [ 1331 { path: outChunk, kind: 'import-statement' }, 1332 { path: outImport1, kind: 'dynamic-import' }, 1333 { path: outImport2, kind: 'dynamic-import' }, 1334 ]) 1335 assert.deepStrictEqual(json.outputs[outImport1].imports, [{ path: outChunk, kind: 'import-statement' }]) 1336 assert.deepStrictEqual(json.outputs[outImport2].imports, [{ path: outChunk, kind: 'import-statement' }]) 1337 assert.deepStrictEqual(json.outputs[outChunk].imports, []) 1338 1339 assert.deepStrictEqual(json.outputs[outEntry].exports, []) 1340 assert.deepStrictEqual(json.outputs[outImport1].exports, []) 1341 assert.deepStrictEqual(json.outputs[outImport2].exports, []) 1342 assert.deepStrictEqual(json.outputs[outChunk].exports, []) 1343 1344 assert.deepStrictEqual(json.outputs[outEntry].inputs, { [inEntry]: { bytesInOutput: 66 } }) 1345 assert.deepStrictEqual(json.outputs[outImport1].inputs, { [inImport1]: { bytesInOutput: 0 } }) 1346 assert.deepStrictEqual(json.outputs[outImport2].inputs, { [inImport2]: { bytesInOutput: 0 } }) 1347 assert.deepStrictEqual(json.outputs[outChunk].inputs, { [inShared]: { bytesInOutput: 28 } }) 1348 }, 1349 1350 async metafileCJSInFormatIIFE({ esbuild, testDir }) { 1351 const entry = path.join(testDir, 'entry.js') 1352 const outfile = path.join(testDir, 'out.js') 1353 await writeFileAsync(entry, `module.exports = {}`) 1354 const result = await esbuild.build({ 1355 entryPoints: [entry], 1356 outfile, 1357 metafile: true, 1358 format: 'iife', 1359 }) 1360 const cwd = process.cwd() 1361 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1362 const json = result.metafile 1363 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1364 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1365 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, []) 1366 }, 1367 1368 async metafileCJSInFormatCJS({ esbuild, testDir }) { 1369 const entry = path.join(testDir, 'entry.js') 1370 const outfile = path.join(testDir, 'out.js') 1371 await writeFileAsync(entry, `module.exports = {}`) 1372 const result = await esbuild.build({ 1373 entryPoints: [entry], 1374 outfile, 1375 metafile: true, 1376 format: 'cjs', 1377 }) 1378 const cwd = process.cwd() 1379 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1380 const json = result.metafile 1381 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1382 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1383 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, []) 1384 assert.deepStrictEqual(json.outputs[makePath(outfile)].entryPoint, makePath(entry)) 1385 }, 1386 1387 async metafileCJSInFormatESM({ esbuild, testDir }) { 1388 const entry = path.join(testDir, 'entry.js') 1389 const outfile = path.join(testDir, 'out.js') 1390 await writeFileAsync(entry, `module.exports = {}`) 1391 const result = await esbuild.build({ 1392 entryPoints: [entry], 1393 outfile, 1394 metafile: true, 1395 format: 'esm', 1396 }) 1397 const cwd = process.cwd() 1398 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1399 const json = result.metafile 1400 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1401 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1402 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, ['default']) 1403 }, 1404 1405 async metafileESMInFormatIIFE({ esbuild, testDir }) { 1406 const entry = path.join(testDir, 'entry.js') 1407 const outfile = path.join(testDir, 'out.js') 1408 await writeFileAsync(entry, `export let a = 1, b = 2`) 1409 const result = await esbuild.build({ 1410 entryPoints: [entry], 1411 outfile, 1412 metafile: true, 1413 format: 'iife', 1414 }) 1415 const cwd = process.cwd() 1416 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1417 const json = result.metafile 1418 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1419 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1420 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, []) 1421 }, 1422 1423 async metafileESMInFormatCJS({ esbuild, testDir }) { 1424 const entry = path.join(testDir, 'entry.js') 1425 const outfile = path.join(testDir, 'out.js') 1426 await writeFileAsync(entry, `export let a = 1, b = 2`) 1427 const result = await esbuild.build({ 1428 entryPoints: [entry], 1429 outfile, 1430 metafile: true, 1431 format: 'cjs', 1432 }) 1433 const cwd = process.cwd() 1434 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1435 const json = result.metafile 1436 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1437 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1438 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, []) 1439 }, 1440 1441 async metafileESMInFormatESM({ esbuild, testDir }) { 1442 const entry = path.join(testDir, 'entry.js') 1443 const outfile = path.join(testDir, 'out.js') 1444 await writeFileAsync(entry, `export let a = 1, b = 2`) 1445 const result = await esbuild.build({ 1446 entryPoints: [entry], 1447 outfile, 1448 metafile: true, 1449 format: 'esm', 1450 }) 1451 const cwd = process.cwd() 1452 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1453 const json = result.metafile 1454 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, []) 1455 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1456 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, ['a', 'b']) 1457 }, 1458 1459 async metafileNestedExportNames({ esbuild, testDir }) { 1460 const entry = path.join(testDir, 'entry.js') 1461 const nested1 = path.join(testDir, 'nested1.js') 1462 const nested2 = path.join(testDir, 'nested2.js') 1463 const nested3 = path.join(testDir, 'nested3.js') 1464 const outfile = path.join(testDir, 'out.js') 1465 await writeFileAsync(entry, ` 1466 export {nested1} from ${JSON.stringify(nested1)} 1467 export * from ${JSON.stringify(nested2)} 1468 export let topLevel = 0 1469 `) 1470 await writeFileAsync(nested1, ` 1471 import {nested3} from ${JSON.stringify(nested3)} 1472 export default 1 1473 export let nested1 = nested3 1474 `) 1475 await writeFileAsync(nested2, ` 1476 export default 'nested2' 1477 export let nested2 = 2 1478 `) 1479 await writeFileAsync(nested3, ` 1480 export let nested3 = 3 1481 `) 1482 const result = await esbuild.build({ 1483 entryPoints: [entry], 1484 bundle: true, 1485 outfile, 1486 metafile: true, 1487 format: 'esm', 1488 }) 1489 const cwd = process.cwd() 1490 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1491 const json = result.metafile 1492 assert.deepStrictEqual(json.inputs[makePath(entry)].imports, [ 1493 { path: makePath(nested1), kind: 'import-statement', original: nested1 }, 1494 { path: makePath(nested2), kind: 'import-statement', original: nested2 }, 1495 ]) 1496 assert.deepStrictEqual(json.inputs[makePath(nested1)].imports, [ 1497 { path: makePath(nested3), kind: 'import-statement', original: nested3 }, 1498 ]) 1499 assert.deepStrictEqual(json.inputs[makePath(nested2)].imports, []) 1500 assert.deepStrictEqual(json.inputs[makePath(nested3)].imports, []) 1501 assert.deepStrictEqual(json.outputs[makePath(outfile)].imports, []) 1502 assert.deepStrictEqual(json.outputs[makePath(outfile)].exports, ['nested1', 'nested2', 'topLevel']) 1503 assert.deepStrictEqual(json.outputs[makePath(outfile)].entryPoint, makePath(entry)) 1504 }, 1505 1506 async metafileCSS({ esbuild, testDir }) { 1507 const entry = path.join(testDir, 'entry.css') 1508 const imported = path.join(testDir, 'imported.css') 1509 const image = path.join(testDir, 'example.png') 1510 const output = path.join(testDir, 'out.css') 1511 await writeFileAsync(entry, ` 1512 @import "./imported"; 1513 body { background: url(https://example.com/external.png) } 1514 `) 1515 await writeFileAsync(imported, ` 1516 a { background: url(./example.png) } 1517 `) 1518 await writeFileAsync(image, 'an image') 1519 const result = await esbuild.build({ 1520 entryPoints: [entry], 1521 bundle: true, 1522 outfile: output, 1523 metafile: true, 1524 sourcemap: true, 1525 loader: { '.png': 'dataurl' }, 1526 }) 1527 1528 const json = result.metafile 1529 assert.strictEqual(Object.keys(json.inputs).length, 3) 1530 assert.strictEqual(Object.keys(json.outputs).length, 2) 1531 const cwd = process.cwd() 1532 const makePath = absPath => path.relative(cwd, absPath).split(path.sep).join('/') 1533 1534 // Check inputs 1535 assert.deepStrictEqual(json, { 1536 inputs: { 1537 [makePath(entry)]: { 1538 bytes: 98, 1539 imports: [ 1540 { path: makePath(imported), kind: 'import-rule', original: './imported' }, 1541 { external: true, kind: 'url-token', path: 'https://example.com/external.png' }, 1542 ] 1543 }, 1544 [makePath(image)]: { bytes: 8, imports: [] }, 1545 [makePath(imported)]: { bytes: 48, imports: [{ path: makePath(image), kind: 'url-token', original: './example.png' }] }, 1546 }, 1547 outputs: { 1548 [makePath(output)]: { 1549 bytes: 253, 1550 entryPoint: makePath(entry), 1551 imports: [ 1552 { kind: 'url-token', path: 'data:image/png,an image' }, 1553 { external: true, kind: 'url-token', path: 'https://example.com/external.png' }, 1554 ], 1555 inputs: { 1556 [makePath(entry)]: { bytesInOutput: 62 }, 1557 [makePath(imported)]: { bytesInOutput: 51 }, 1558 }, 1559 }, 1560 [makePath(output + '.map')]: { 1561 bytes: 325, 1562 exports: [], 1563 imports: [], 1564 inputs: {}, 1565 }, 1566 }, 1567 }) 1568 }, 1569 1570 async metafileLoaderFileMultipleEntry({ esbuild, testDir }) { 1571 const entry1 = path.join(testDir, 'entry1.js') 1572 const entry2 = path.join(testDir, 'entry2.js') 1573 const file = path.join(testDir, 'x.file') 1574 const outdir = path.join(testDir, 'out') 1575 await writeFileAsync(entry1, ` 1576 export {default} from './x.file' 1577 `) 1578 await writeFileAsync(entry2, ` 1579 import z from './x.file' 1580 console.log(z) 1581 `) 1582 await writeFileAsync(file, `This is a file`) 1583 const result = await esbuild.build({ 1584 entryPoints: [entry1, entry2], 1585 bundle: true, 1586 loader: { '.file': 'file' }, 1587 outdir, 1588 metafile: true, 1589 format: 'cjs', 1590 }) 1591 const json = result.metafile 1592 const cwd = process.cwd() 1593 const makePath = pathname => path.relative(cwd, pathname).split(path.sep).join('/') 1594 const fileName = require(path.join(outdir, 'entry1.js')).default 1595 const fileKey = makePath(path.join(outdir, fileName)) 1596 assert.deepStrictEqual(json.outputs[fileKey].imports, []) 1597 assert.deepStrictEqual(json.outputs[fileKey].exports, []) 1598 assert.deepStrictEqual(json.outputs[fileKey].inputs, { [makePath(file)]: { bytesInOutput: 14 } }) 1599 }, 1600 1601 // Test in-memory output files 1602 async writeFalse({ esbuild, testDir }) { 1603 const input = path.join(testDir, 'in.js') 1604 const output = path.join(testDir, 'out.js') 1605 const inputCode = 'console.log()' 1606 await writeFileAsync(input, inputCode) 1607 1608 const value = await esbuild.build({ 1609 entryPoints: [input], 1610 bundle: true, 1611 outfile: output, 1612 sourcemap: true, 1613 format: 'esm', 1614 metafile: true, 1615 write: false, 1616 }) 1617 1618 assert.strictEqual(fs.existsSync(output), false) 1619 assert.notStrictEqual(value.outputFiles, void 0) 1620 assert.strictEqual(value.outputFiles.length, 2) 1621 assert.strictEqual(value.outputFiles[0].path, output + '.map') 1622 assert.strictEqual(value.outputFiles[0].contents.constructor, Uint8Array) 1623 assert.strictEqual(value.outputFiles[0].hash, 'BIjVBRZOQ5s') 1624 assert.strictEqual(value.outputFiles[1].path, output) 1625 assert.strictEqual(value.outputFiles[1].contents.constructor, Uint8Array) 1626 assert.strictEqual(value.outputFiles[1].hash, 'zvyzJPvi96o') 1627 1628 const sourceMap = JSON.parse(Buffer.from(value.outputFiles[0].contents).toString()) 1629 const js = Buffer.from(value.outputFiles[1].contents).toString() 1630 assert.strictEqual(sourceMap.version, 3) 1631 assert.strictEqual(js, `// scripts/.js-api-tests/writeFalse/in.js\nconsole.log();\n//# sourceMappingURL=out.js.map\n`) 1632 1633 const cwd = process.cwd() 1634 const makePath = file => path.relative(cwd, file).split(path.sep).join('/') 1635 const meta = value.metafile 1636 assert.strictEqual(meta.inputs[makePath(input)].bytes, inputCode.length) 1637 assert.strictEqual(meta.outputs[makePath(output)].bytes, js.length) 1638 assert.strictEqual(meta.outputs[makePath(output + '.map')].bytes, value.outputFiles[0].contents.length) 1639 }, 1640 1641 async allowOverwrite({ esbuild, testDir }) { 1642 const input = path.join(testDir, 'in.mjs') 1643 await writeFileAsync(input, `export default FOO`) 1644 1645 // Fail without "allowOverwrite" 1646 try { 1647 await esbuild.build({ 1648 entryPoints: [input], 1649 outfile: input, 1650 logLevel: 'silent', 1651 }) 1652 throw new Error('Expected build failure'); 1653 } catch (e) { 1654 if (!e || !e.errors || !e.errors.length || !e.errors[0].text.includes('Refusing to overwrite input file')) 1655 throw e 1656 } 1657 1658 // Succeed with "allowOverwrite" 1659 await esbuild.build({ 1660 entryPoints: [input], 1661 outfile: input, 1662 allowOverwrite: true, 1663 define: { FOO: '123' }, 1664 }) 1665 1666 // This needs to use relative paths to avoid breaking on Windows. 1667 // Importing by absolute path doesn't work on Windows in node. 1668 const result = await import('./' + path.relative(__dirname, input)) 1669 1670 assert.strictEqual(result.default, 123) 1671 }, 1672 1673 async splittingRelativeSameDir({ esbuild, testDir }) { 1674 const inputA = path.join(testDir, 'a.js') 1675 const inputB = path.join(testDir, 'b.js') 1676 const inputCommon = path.join(testDir, 'common.js') 1677 await writeFileAsync(inputA, ` 1678 import x from "./${path.basename(inputCommon)}" 1679 console.log('a' + x) 1680 `) 1681 await writeFileAsync(inputB, ` 1682 import x from "./${path.basename(inputCommon)}" 1683 console.log('b' + x) 1684 `) 1685 await writeFileAsync(inputCommon, ` 1686 export default 'common' 1687 `) 1688 const outdir = path.join(testDir, 'out') 1689 const value = await esbuild.build({ 1690 entryPoints: [inputA, inputB], 1691 bundle: true, 1692 outdir, 1693 format: 'esm', 1694 splitting: true, 1695 write: false, 1696 }) 1697 assert.strictEqual(value.outputFiles.length, 3) 1698 1699 // These should all use forward slashes, even on Windows 1700 const chunk = 'chunk-P3NHLAOZ.js' 1701 assert.strictEqual(Buffer.from(value.outputFiles[0].contents).toString(), `import { 1702 common_default 1703 } from "./${chunk}"; 1704 1705 // scripts/.js-api-tests/splittingRelativeSameDir/a.js 1706 console.log("a" + common_default); 1707 `) 1708 assert.strictEqual(Buffer.from(value.outputFiles[1].contents).toString(), `import { 1709 common_default 1710 } from "./${chunk}"; 1711 1712 // scripts/.js-api-tests/splittingRelativeSameDir/b.js 1713 console.log("b" + common_default); 1714 `) 1715 assert.strictEqual(Buffer.from(value.outputFiles[2].contents).toString(), `// scripts/.js-api-tests/splittingRelativeSameDir/common.js 1716 var common_default = "common"; 1717 1718 export { 1719 common_default 1720 }; 1721 `) 1722 1723 assert.strictEqual(value.outputFiles[0].path, path.join(outdir, path.basename(inputA))) 1724 assert.strictEqual(value.outputFiles[1].path, path.join(outdir, path.basename(inputB))) 1725 assert.strictEqual(value.outputFiles[2].path, path.join(outdir, chunk)) 1726 }, 1727 1728 async splittingRelativeNestedDir({ esbuild, testDir }) { 1729 const inputA = path.join(testDir, 'a/demo.js') 1730 const inputB = path.join(testDir, 'b/demo.js') 1731 const inputCommon = path.join(testDir, 'common.js') 1732 await mkdirAsync(path.dirname(inputA)).catch(x => x) 1733 await mkdirAsync(path.dirname(inputB)).catch(x => x) 1734 await writeFileAsync(inputA, ` 1735 import x from "../${path.basename(inputCommon)}" 1736 console.log('a' + x) 1737 `) 1738 await writeFileAsync(inputB, ` 1739 import x from "../${path.basename(inputCommon)}" 1740 console.log('b' + x) 1741 `) 1742 await writeFileAsync(inputCommon, ` 1743 export default 'common' 1744 `) 1745 const outdir = path.join(testDir, 'out') 1746 const value = await esbuild.build({ 1747 entryPoints: [inputA, inputB], 1748 bundle: true, 1749 outdir, 1750 format: 'esm', 1751 splitting: true, 1752 write: false, 1753 }) 1754 assert.strictEqual(value.outputFiles.length, 3) 1755 1756 // These should all use forward slashes, even on Windows 1757 const chunk = 'chunk-BPDO6GL2.js' 1758 assert.strictEqual(Buffer.from(value.outputFiles[0].contents).toString(), `import { 1759 common_default 1760 } from "../${chunk}"; 1761 1762 // scripts/.js-api-tests/splittingRelativeNestedDir/a/demo.js 1763 console.log("a" + common_default); 1764 `) 1765 assert.strictEqual(Buffer.from(value.outputFiles[1].contents).toString(), `import { 1766 common_default 1767 } from "../${chunk}"; 1768 1769 // scripts/.js-api-tests/splittingRelativeNestedDir/b/demo.js 1770 console.log("b" + common_default); 1771 `) 1772 assert.strictEqual(Buffer.from(value.outputFiles[2].contents).toString(), `// scripts/.js-api-tests/splittingRelativeNestedDir/common.js 1773 var common_default = "common"; 1774 1775 export { 1776 common_default 1777 }; 1778 `) 1779 1780 assert.strictEqual(value.outputFiles[0].path, path.join(outdir, path.relative(testDir, inputA))) 1781 assert.strictEqual(value.outputFiles[1].path, path.join(outdir, path.relative(testDir, inputB))) 1782 assert.strictEqual(value.outputFiles[2].path, path.join(outdir, chunk)) 1783 }, 1784 1785 async splittingWithChunkPath({ esbuild, testDir }) { 1786 const inputA = path.join(testDir, 'a/demo.js') 1787 const inputB = path.join(testDir, 'b/demo.js') 1788 const inputCommon = path.join(testDir, 'common.js') 1789 await mkdirAsync(path.dirname(inputA)).catch(x => x) 1790 await mkdirAsync(path.dirname(inputB)).catch(x => x) 1791 await writeFileAsync(inputA, ` 1792 import x from "../${path.basename(inputCommon)}" 1793 console.log('a' + x) 1794 `) 1795 await writeFileAsync(inputB, ` 1796 import x from "../${path.basename(inputCommon)}" 1797 console.log('b' + x) 1798 `) 1799 await writeFileAsync(inputCommon, ` 1800 export default 'common' 1801 `) 1802 const outdir = path.join(testDir, 'out') 1803 const value = await esbuild.build({ 1804 entryPoints: [inputA, inputB], 1805 bundle: true, 1806 outdir, 1807 format: 'esm', 1808 splitting: true, 1809 write: false, 1810 chunkNames: 'chunks/name=[name]/hash=[hash]', 1811 }) 1812 assert.strictEqual(value.outputFiles.length, 3) 1813 1814 // These should all use forward slashes, even on Windows 1815 const chunk = 'chunks/name=chunk/hash=7RIY4OCQ.js' 1816 assert.strictEqual(value.outputFiles[0].text, `import { 1817 common_default 1818 } from "../${chunk}"; 1819 1820 // scripts/.js-api-tests/splittingWithChunkPath/a/demo.js 1821 console.log("a" + common_default); 1822 `) 1823 assert.strictEqual(value.outputFiles[1].text, `import { 1824 common_default 1825 } from "../${chunk}"; 1826 1827 // scripts/.js-api-tests/splittingWithChunkPath/b/demo.js 1828 console.log("b" + common_default); 1829 `) 1830 assert.strictEqual(value.outputFiles[2].text, `// scripts/.js-api-tests/splittingWithChunkPath/common.js 1831 var common_default = "common"; 1832 1833 export { 1834 common_default 1835 }; 1836 `) 1837 1838 assert.strictEqual(value.outputFiles[0].path, path.join(outdir, path.relative(testDir, inputA))) 1839 assert.strictEqual(value.outputFiles[1].path, path.join(outdir, path.relative(testDir, inputB))) 1840 assert.strictEqual(value.outputFiles[2].path, path.join(outdir, chunk)) 1841 }, 1842 1843 async splittingWithEntryHashes({ esbuild, testDir }) { 1844 const inputA = path.join(testDir, 'a/demo.js') 1845 const inputB = path.join(testDir, 'b/demo.js') 1846 const inputCommon = path.join(testDir, 'common.js') 1847 await mkdirAsync(path.dirname(inputA)).catch(x => x) 1848 await mkdirAsync(path.dirname(inputB)).catch(x => x) 1849 await writeFileAsync(inputA, ` 1850 import x from "../${path.basename(inputCommon)}" 1851 console.log('a' + x.name) 1852 `) 1853 await writeFileAsync(inputB, ` 1854 import x from "../${path.basename(inputCommon)}" 1855 console.log('b' + x.name) 1856 `) 1857 await writeFileAsync(inputCommon, ` 1858 export default { name: 'common' } 1859 `) 1860 const outdir = path.join(testDir, 'out') 1861 const value = await esbuild.build({ 1862 entryPoints: [inputA, inputB], 1863 bundle: true, 1864 outdir, 1865 format: 'esm', 1866 splitting: true, 1867 write: false, 1868 entryNames: 'entry/name=[name]/hash=[hash]', 1869 chunkNames: 'chunks/name=[name]/hash=[hash]', 1870 }) 1871 assert.strictEqual(value.outputFiles.length, 3) 1872 1873 // These should all use forward slashes, even on Windows 1874 const chunk = 'chunks/name=chunk/hash=6VLJRT45.js' 1875 assert.strictEqual(value.outputFiles[0].text, `import { 1876 common_default 1877 } from "../../${chunk}"; 1878 1879 // scripts/.js-api-tests/splittingWithEntryHashes/a/demo.js 1880 console.log("a" + common_default.name); 1881 `) 1882 assert.strictEqual(value.outputFiles[1].text, `import { 1883 common_default 1884 } from "../../${chunk}"; 1885 1886 // scripts/.js-api-tests/splittingWithEntryHashes/b/demo.js 1887 console.log("b" + common_default.name); 1888 `) 1889 assert.strictEqual(value.outputFiles[2].text, `// scripts/.js-api-tests/splittingWithEntryHashes/common.js 1890 var common_default = { name: "common" }; 1891 1892 export { 1893 common_default 1894 }; 1895 `) 1896 1897 const outputA = 'entry/name=demo/hash=ZKX5HN4L.js' 1898 const outputB = 'entry/name=demo/hash=TYTZIN4P.js' 1899 assert.strictEqual(value.outputFiles[0].path, path.join(outdir, outputA)) 1900 assert.strictEqual(value.outputFiles[1].path, path.join(outdir, outputB)) 1901 assert.strictEqual(value.outputFiles[2].path, path.join(outdir, chunk)) 1902 }, 1903 1904 async splittingWithChunkPathAndCrossChunkImportsIssue899({ esbuild, testDir }) { 1905 const entry1 = path.join(testDir, 'src', 'entry1.js') 1906 const entry2 = path.join(testDir, 'src', 'entry2.js') 1907 const entry3 = path.join(testDir, 'src', 'entry3.js') 1908 const shared1 = path.join(testDir, 'src', 'shared1.js') 1909 const shared2 = path.join(testDir, 'src', 'shared2.js') 1910 const shared3 = path.join(testDir, 'src', 'shared3.js') 1911 await mkdirAsync(path.join(testDir, 'src')).catch(x => x) 1912 await writeFileAsync(entry1, ` 1913 import { shared1 } from './shared1'; 1914 import { shared2 } from './shared2'; 1915 export default async function() { 1916 return shared1() + shared2(); 1917 } 1918 `) 1919 await writeFileAsync(entry2, ` 1920 import { shared2 } from './shared2'; 1921 import { shared3 } from './shared3'; 1922 export default async function() { 1923 return shared2() + shared3(); 1924 } 1925 `) 1926 await writeFileAsync(entry3, ` 1927 import { shared3 } from './shared3'; 1928 import { shared1 } from './shared1'; 1929 export default async function() { 1930 return shared3() + shared1(); 1931 } 1932 `) 1933 await writeFileAsync(shared1, ` 1934 import { shared2 } from './shared2'; 1935 export function shared1() { 1936 return shared2().replace('2', '1'); 1937 } 1938 `) 1939 await writeFileAsync(shared2, ` 1940 import { shared3 } from './shared3' 1941 export function shared2() { 1942 return 'shared2'; 1943 } 1944 `) 1945 await writeFileAsync(shared3, ` 1946 export function shared3() { 1947 return 'shared3'; 1948 } 1949 `) 1950 const outdir = path.join(testDir, 'out') 1951 await esbuild.build({ 1952 entryPoints: [entry1, entry2, entry3], 1953 bundle: true, 1954 outdir, 1955 format: 'esm', 1956 splitting: true, 1957 outExtension: { '.js': '.mjs' }, 1958 chunkNames: 'chunks/[hash]/[name]', 1959 }) 1960 1961 // This needs to use relative paths to avoid breaking on Windows. 1962 // Importing by absolute path doesn't work on Windows in node. 1963 const result1 = await import('./' + path.relative(__dirname, path.join(outdir, 'entry1.mjs'))) 1964 const result2 = await import('./' + path.relative(__dirname, path.join(outdir, 'entry2.mjs'))) 1965 const result3 = await import('./' + path.relative(__dirname, path.join(outdir, 'entry3.mjs'))) 1966 1967 assert.strictEqual(await result1.default(), 'shared1shared2'); 1968 assert.strictEqual(await result2.default(), 'shared2shared3'); 1969 assert.strictEqual(await result3.default(), 'shared3shared1'); 1970 }, 1971 1972 async splittingStaticImportHashChange({ esbuild, testDir }) { 1973 const input1 = path.join(testDir, 'a', 'in1.js') 1974 const input2 = path.join(testDir, 'b', 'in2.js') 1975 const outdir = path.join(testDir, 'out') 1976 1977 await mkdirAsync(path.dirname(input1), { recursive: true }) 1978 await mkdirAsync(path.dirname(input2), { recursive: true }) 1979 await writeFileAsync(input1, `import ${JSON.stringify(input2)}`) 1980 await writeFileAsync(input2, `console.log(123)`) 1981 1982 const result1 = await esbuild.build({ 1983 entryPoints: [input1, input2], 1984 bundle: true, 1985 outdir, 1986 format: 'esm', 1987 splitting: true, 1988 write: false, 1989 entryNames: '[name]-[hash]', 1990 }) 1991 1992 await writeFileAsync(input2, `console.log(321)`) 1993 1994 const result2 = await esbuild.build({ 1995 entryPoints: [input1, input2], 1996 bundle: true, 1997 outdir, 1998 format: 'esm', 1999 splitting: true, 2000 write: false, 2001 entryNames: '[name]-[hash]', 2002 }) 2003 2004 assert.strictEqual(result1.outputFiles.length, 3) 2005 assert.strictEqual(result2.outputFiles.length, 3) 2006 2007 // The hashes of both output files must change. Previously there was a bug 2008 // where hash changes worked for static imports but not for dynamic imports. 2009 for (const { path: oldPath } of result1.outputFiles) 2010 for (const { path: newPath } of result2.outputFiles) 2011 assert.notStrictEqual(oldPath, newPath) 2012 }, 2013 2014 async splittingDynamicImportHashChangeIssue1076({ esbuild, testDir }) { 2015 const input1 = path.join(testDir, 'a', 'in1.js') 2016 const input2 = path.join(testDir, 'b', 'in2.js') 2017 const outdir = path.join(testDir, 'out') 2018 2019 await mkdirAsync(path.dirname(input1), { recursive: true }) 2020 await mkdirAsync(path.dirname(input2), { recursive: true }) 2021 await writeFileAsync(input1, `import(${JSON.stringify(input2)})`) 2022 await writeFileAsync(input2, `console.log(123)`) 2023 2024 const result1 = await esbuild.build({ 2025 entryPoints: [input1], 2026 bundle: true, 2027 outdir, 2028 format: 'esm', 2029 splitting: true, 2030 write: false, 2031 entryNames: '[name]-[hash]', 2032 }) 2033 2034 await writeFileAsync(input2, `console.log(321)`) 2035 2036 const result2 = await esbuild.build({ 2037 entryPoints: [input1], 2038 bundle: true, 2039 outdir, 2040 format: 'esm', 2041 splitting: true, 2042 write: false, 2043 entryNames: '[name]-[hash]', 2044 }) 2045 2046 assert.strictEqual(result1.outputFiles.length, 2) 2047 assert.strictEqual(result2.outputFiles.length, 2) 2048 2049 // The hashes of both output files must change. Previously there was a bug 2050 // where hash changes worked for static imports but not for dynamic imports. 2051 for (const { path: oldPath } of result1.outputFiles) 2052 for (const { path: newPath } of result2.outputFiles) 2053 assert.notStrictEqual(oldPath, newPath) 2054 }, 2055 2056 async stdinStdoutBundle({ esbuild, testDir }) { 2057 const auxiliary = path.join(testDir, 'auxiliary.js') 2058 await writeFileAsync(auxiliary, 'export default 123') 2059 const value = await esbuild.build({ 2060 stdin: { 2061 contents: ` 2062 import x from './auxiliary.js' 2063 console.log(x) 2064 `, 2065 resolveDir: testDir, 2066 }, 2067 bundle: true, 2068 write: false, 2069 }) 2070 assert.strictEqual(value.outputFiles.length, 1) 2071 assert.strictEqual(value.outputFiles[0].path, '<stdout>') 2072 assert.strictEqual(Buffer.from(value.outputFiles[0].contents).toString(), `(() => { 2073 // scripts/.js-api-tests/stdinStdoutBundle/auxiliary.js 2074 var auxiliary_default = 123; 2075 2076 // <stdin> 2077 console.log(auxiliary_default); 2078 })(); 2079 `) 2080 }, 2081 2082 async stdinOutfileBundle({ esbuild, testDir }) { 2083 const auxiliary = path.join(testDir, 'auxiliary.js') 2084 const outfile = path.join(testDir, 'out.js') 2085 await writeFileAsync(auxiliary, 'export default 123') 2086 const value = await esbuild.build({ 2087 stdin: { 2088 contents: ` 2089 import x from './auxiliary.js' 2090 export {x as fromStdin} 2091 `, 2092 resolveDir: testDir, 2093 }, 2094 bundle: true, 2095 outfile, 2096 format: 'cjs', 2097 }) 2098 assert.strictEqual(value.outputFiles, void 0) 2099 const result = require(outfile) 2100 assert.strictEqual(result.fromStdin, 123) 2101 }, 2102 2103 async stdinAndEntryBundle({ esbuild, testDir }) { 2104 const srcDir = path.join(testDir, 'src') 2105 const entry = path.join(srcDir, 'entry.js') 2106 const auxiliary = path.join(srcDir, 'auxiliary.js') 2107 const outdir = path.join(testDir, 'out') 2108 await mkdirAsync(srcDir) 2109 await writeFileAsync(auxiliary, 'export default 123') 2110 await writeFileAsync(entry, ` 2111 import x from './auxiliary.js' 2112 export let fromEntry = x 2113 `) 2114 const value = await esbuild.build({ 2115 entryPoints: [entry], 2116 stdin: { 2117 contents: ` 2118 import x from './src/auxiliary.js' 2119 export {x as fromStdin} 2120 `, 2121 resolveDir: testDir, 2122 }, 2123 bundle: true, 2124 outdir, 2125 format: 'cjs', 2126 }) 2127 assert.strictEqual(value.outputFiles, void 0) 2128 const entryResult = require(path.join(outdir, path.basename(entry))) 2129 assert.strictEqual(entryResult.fromEntry, 123) 2130 const stdinResult = require(path.join(outdir, path.basename('stdin.js'))) 2131 assert.strictEqual(stdinResult.fromStdin, 123) 2132 }, 2133 2134 async forceTsConfig({ esbuild, testDir }) { 2135 // ./tsconfig.json 2136 // ./a/forced-config.json 2137 // ./a/b/test-impl.js 2138 // ./a/b/c/in.js 2139 const aDir = path.join(testDir, 'a') 2140 const bDir = path.join(aDir, 'b') 2141 const cDir = path.join(bDir, 'c') 2142 await mkdirAsync(aDir).catch(x => x) 2143 await mkdirAsync(bDir).catch(x => x) 2144 await mkdirAsync(cDir).catch(x => x) 2145 const input = path.join(cDir, 'in.js') 2146 const forced = path.join(bDir, 'test-impl.js') 2147 const tsconfigIgnore = path.join(testDir, 'tsconfig.json') 2148 const tsconfigForced = path.join(aDir, 'forced-config.json') 2149 const output = path.join(testDir, 'out.js') 2150 await writeFileAsync(input, 'import "test"') 2151 await writeFileAsync(forced, 'console.log("success")') 2152 await writeFileAsync(tsconfigIgnore, '{"compilerOptions": {"baseUrl": "./a", "paths": {"test": ["./ignore.js"]}}}') 2153 await writeFileAsync(tsconfigForced, '{"compilerOptions": {"baseUrl": "./b", "paths": {"test": ["./test-impl.js"]}}}') 2154 await esbuild.build({ 2155 entryPoints: [input], 2156 bundle: true, 2157 outfile: output, 2158 tsconfig: tsconfigForced, 2159 format: 'esm', 2160 }) 2161 const result = await readFileAsync(output, 'utf8') 2162 assert.strictEqual(result, `// scripts/.js-api-tests/forceTsConfig/a/b/test-impl.js 2163 console.log("success"); 2164 `) 2165 }, 2166 2167 async forceTsConfigRaw({ esbuild, testDir }) { 2168 // ./a/tsconfig.json 2169 // ./a/b/test-impl.js 2170 // ./a/b/c/in.js 2171 const aDir = path.join(testDir, 'a') 2172 const bDir = path.join(aDir, 'b') 2173 const cDir = path.join(bDir, 'c') 2174 await mkdirAsync(aDir).catch(x => x) 2175 await mkdirAsync(bDir).catch(x => x) 2176 await mkdirAsync(cDir).catch(x => x) 2177 const input = path.join(cDir, 'in.js') 2178 const forced = path.join(bDir, 'test-impl.js') 2179 const tsconfigIgnore = path.join(aDir, 'tsconfig.json') 2180 const output = path.join(testDir, 'out.js') 2181 await writeFileAsync(input, 'import "test"') 2182 await writeFileAsync(forced, 'console.log("success")') 2183 await writeFileAsync(tsconfigIgnore, '{"compilerOptions": {"baseUrl": "./b", "paths": {"test": ["./ignore.js"]}}}') 2184 await esbuild.build({ 2185 entryPoints: [input], 2186 bundle: true, 2187 outfile: output, 2188 absWorkingDir: testDir, // "paths" are resolved relative to the current working directory 2189 tsconfigRaw: { 2190 compilerOptions: { 2191 baseUrl: './a/b', 2192 paths: { 2193 test: ['./test-impl.js'], 2194 }, 2195 }, 2196 }, 2197 format: 'esm', 2198 }) 2199 const result = await readFileAsync(output, 'utf8') 2200 assert.strictEqual(result, `// a/b/test-impl.js 2201 console.log("success"); 2202 `) 2203 }, 2204 2205 async forceTsConfigRawStdin({ esbuild, testDir }) { 2206 const input = path.join(testDir, 'in.js') 2207 const test = path.join(testDir, 'test-impl.js') 2208 const output = path.join(testDir, 'out.js') 2209 await writeFileAsync(input, 'import "test"') 2210 await writeFileAsync(test, 'console.log("success")') 2211 await esbuild.build({ 2212 stdin: { 2213 contents: `import "test"`, 2214 resolveDir: '.', 2215 }, 2216 bundle: true, 2217 outfile: output, 2218 absWorkingDir: testDir, 2219 tsconfigRaw: { 2220 compilerOptions: { 2221 paths: { 2222 test: ['./test-impl.js'], 2223 }, 2224 }, 2225 }, 2226 format: 'esm', 2227 }) 2228 const result = await readFileAsync(output, 'utf8') 2229 assert.strictEqual(result, `// test-impl.js 2230 console.log("success"); 2231 `) 2232 }, 2233 2234 async es5({ esbuild, testDir }) { 2235 const input = path.join(testDir, 'in.js') 2236 const cjs = path.join(testDir, 'cjs.js') 2237 const esm = path.join(testDir, 'esm.js') 2238 const output = path.join(testDir, 'out.js') 2239 await writeFileAsync(input, ` 2240 export {foo} from "./cjs" 2241 export * as bar from "./esm" 2242 `) 2243 await writeFileAsync(cjs, 'exports.foo = 123') 2244 await writeFileAsync(esm, 'export var foo = 123') 2245 const value = await esbuild.build({ 2246 entryPoints: [input], 2247 bundle: true, 2248 outfile: output, 2249 format: 'cjs', 2250 target: 'es5', 2251 }) 2252 assert.strictEqual(value.outputFiles, void 0) 2253 const result = require(output) 2254 assert.strictEqual(result.foo, 123) 2255 assert.strictEqual(result.bar.foo, 123) 2256 assert.strictEqual(result.__esModule, true) 2257 const contents = await readFileAsync(output, 'utf8') 2258 assert.strictEqual(contents.indexOf('=>'), -1) 2259 assert.strictEqual(contents.indexOf('const'), -1) 2260 }, 2261 2262 async outbaseImplicit({ esbuild, testDir }) { 2263 const outbase = path.join(testDir, 'pages', 'a') 2264 const b = path.join(outbase, 'b', 'index.js') 2265 const c = path.join(outbase, 'c', 'index.js') 2266 const outdir = path.join(testDir, 'outdir') 2267 await mkdirAsync(path.dirname(b), { recursive: true }) 2268 await mkdirAsync(path.dirname(c), { recursive: true }) 2269 await writeFileAsync(b, 'module.exports = "b"') 2270 await writeFileAsync(c, 'module.exports = "c"') 2271 await esbuild.build({ 2272 entryPoints: [ 2273 path.relative(process.cwd(), b), 2274 path.relative(process.cwd(), c), 2275 ], 2276 outdir, 2277 format: 'cjs', 2278 }) 2279 const outB = path.join(outdir, path.relative(outbase, b)) 2280 const outC = path.join(outdir, path.relative(outbase, c)) 2281 assert.strictEqual(require(outB), 'b') 2282 assert.strictEqual(require(outC), 'c') 2283 }, 2284 2285 async outbaseRelPath({ esbuild, testDir }) { 2286 const outbase = path.join(testDir, 'pages') 2287 const b = path.join(outbase, 'a', 'b', 'index.js') 2288 const c = path.join(outbase, 'a', 'c', 'index.js') 2289 const outdir = path.join(testDir, 'outdir') 2290 await mkdirAsync(path.dirname(b), { recursive: true }) 2291 await mkdirAsync(path.dirname(c), { recursive: true }) 2292 await writeFileAsync(b, 'module.exports = "b"') 2293 await writeFileAsync(c, 'module.exports = "c"') 2294 await esbuild.build({ 2295 entryPoints: [ 2296 path.relative(process.cwd(), b), 2297 path.relative(process.cwd(), c), 2298 ], 2299 outdir, 2300 outbase, 2301 format: 'cjs', 2302 }) 2303 const outB = path.join(outdir, path.relative(outbase, b)) 2304 const outC = path.join(outdir, path.relative(outbase, c)) 2305 assert.strictEqual(require(outB), 'b') 2306 assert.strictEqual(require(outC), 'c') 2307 }, 2308 2309 async outbaseAbsPath({ esbuild, testDir }) { 2310 const outbase = path.join(testDir, 'pages') 2311 const b = path.join(outbase, 'a', 'b', 'index.js') 2312 const c = path.join(outbase, 'a', 'c', 'index.js') 2313 const outdir = path.join(testDir, 'outdir') 2314 await mkdirAsync(path.dirname(b), { recursive: true }) 2315 await mkdirAsync(path.dirname(c), { recursive: true }) 2316 await writeFileAsync(b, 'module.exports = "b"') 2317 await writeFileAsync(c, 'module.exports = "c"') 2318 await esbuild.build({ 2319 entryPoints: [b, c], 2320 outdir, 2321 outbase, 2322 format: 'cjs', 2323 }) 2324 const outB = path.join(outdir, path.relative(outbase, b)) 2325 const outC = path.join(outdir, path.relative(outbase, c)) 2326 assert.strictEqual(require(outB), 'b') 2327 assert.strictEqual(require(outC), 'c') 2328 }, 2329 2330 async bundleTreeShakingDefault({ esbuild }) { 2331 const { outputFiles } = await esbuild.build({ 2332 stdin: { 2333 contents: ` 2334 let removeMe1 = /* @__PURE__ */ fn(); 2335 let removeMe2 = <div/>; 2336 `, 2337 loader: 'jsx', 2338 }, 2339 write: false, 2340 bundle: true, 2341 }) 2342 assert.strictEqual(outputFiles[0].text, `(() => {\n})();\n`) 2343 }, 2344 2345 async bundleTreeShakingTrue({ esbuild }) { 2346 const { outputFiles } = await esbuild.build({ 2347 stdin: { 2348 contents: ` 2349 let removeMe1 = /* @__PURE__ */ fn(); 2350 let removeMe2 = <div/>; 2351 `, 2352 loader: 'jsx', 2353 }, 2354 write: false, 2355 bundle: true, 2356 treeShaking: true, 2357 }) 2358 assert.strictEqual(outputFiles[0].text, `(() => {\n})();\n`) 2359 }, 2360 2361 async bundleTreeShakingIgnoreAnnotations({ esbuild }) { 2362 const { outputFiles } = await esbuild.build({ 2363 stdin: { 2364 contents: ` 2365 let keepMe1 = /* @__PURE__ */ fn(); 2366 let keepMe2 = <div/>; 2367 `, 2368 loader: 'jsx', 2369 }, 2370 write: false, 2371 bundle: true, 2372 ignoreAnnotations: true, 2373 }) 2374 assert.strictEqual(outputFiles[0].text, `(() => { 2375 // <stdin> 2376 var keepMe1 = fn(); 2377 var keepMe2 = React.createElement("div", null); 2378 })(); 2379 `) 2380 }, 2381 2382 async externalWithWildcard({ esbuild }) { 2383 const { outputFiles } = await esbuild.build({ 2384 stdin: { 2385 contents: `require('/assets/file.png')`, 2386 }, 2387 write: false, 2388 bundle: true, 2389 external: ['/assets/*.png'], 2390 platform: 'node', 2391 }) 2392 assert.strictEqual(outputFiles[0].text, `// <stdin> 2393 require("/assets/file.png"); 2394 `) 2395 }, 2396 2397 async externalPackages({ esbuild, testDir }) { 2398 const input = path.join(testDir, 'in.js') 2399 const pkgPath = path.join(testDir, 'node_modules', 'pkg', 'path.js') 2400 const dirPath = path.join(testDir, 'dir', 'path.js') 2401 await mkdirAsync(path.dirname(pkgPath), { recursive: true }) 2402 await mkdirAsync(path.dirname(dirPath), { recursive: true }) 2403 await writeFileAsync(input, ` 2404 import 'pkg/path.js' 2405 import './dir/path.js' 2406 import 'before/alias' 2407 `) 2408 await writeFileAsync(pkgPath, `console.log('pkg')`) 2409 await writeFileAsync(dirPath, `console.log('dir')`) 2410 const { outputFiles } = await esbuild.build({ 2411 entryPoints: [input], 2412 write: false, 2413 bundle: true, 2414 packages: 'external', 2415 format: 'esm', 2416 alias: { 'before': 'after' }, 2417 }) 2418 assert.strictEqual(outputFiles[0].text, `// scripts/.js-api-tests/externalPackages/in.js 2419 import "pkg/path.js"; 2420 2421 // scripts/.js-api-tests/externalPackages/dir/path.js 2422 console.log("dir"); 2423 2424 // scripts/.js-api-tests/externalPackages/in.js 2425 import "after/alias"; 2426 `) 2427 }, 2428 2429 async errorInvalidExternalWithTwoWildcards({ esbuild }) { 2430 try { 2431 await esbuild.build({ 2432 entryPoints: ['in.js'], 2433 external: ['a*b*c'], 2434 write: false, 2435 logLevel: 'silent', 2436 }) 2437 throw new Error('Expected build failure'); 2438 } catch (e) { 2439 if (e.message !== 'Build failed with 1 error:\nerror: External path "a*b*c" cannot have more than one "*" wildcard') { 2440 throw e; 2441 } 2442 } 2443 }, 2444 2445 async jsBannerBuild({ esbuild, testDir }) { 2446 const input = path.join(testDir, 'in.js') 2447 const outfile = path.join(testDir, 'out.js') 2448 await writeFileAsync(input, `if (!bannerDefined) throw 'fail'`) 2449 await esbuild.build({ 2450 entryPoints: [input], 2451 outfile, 2452 banner: { js: 'const bannerDefined = true' }, 2453 }) 2454 require(outfile) 2455 }, 2456 2457 async jsFooterBuild({ esbuild, testDir }) { 2458 const input = path.join(testDir, 'in.js') 2459 const outfile = path.join(testDir, 'out.js') 2460 await writeFileAsync(input, `footer()`) 2461 await esbuild.build({ 2462 entryPoints: [input], 2463 outfile, 2464 footer: { js: 'function footer() {}' }, 2465 }) 2466 require(outfile) 2467 }, 2468 2469 async jsBannerFooterBuild({ esbuild, testDir }) { 2470 const aPath = path.join(testDir, 'a.js') 2471 const bPath = path.join(testDir, 'b.js') 2472 const outdir = path.join(testDir, 'out') 2473 await writeFileAsync(aPath, `module.exports = { banner: bannerDefined, footer };`) 2474 await writeFileAsync(bPath, `module.exports = { banner: bannerDefined, footer };`) 2475 await esbuild.build({ 2476 entryPoints: [aPath, bPath], 2477 outdir, 2478 banner: { js: 'const bannerDefined = true' }, 2479 footer: { js: 'function footer() {}' }, 2480 }) 2481 const a = require(path.join(outdir, path.basename(aPath))) 2482 const b = require(path.join(outdir, path.basename(bPath))) 2483 if (!a.banner || !b.banner) throw 'fail' 2484 a.footer() 2485 b.footer() 2486 }, 2487 2488 async cssBannerFooterBuild({ esbuild, testDir }) { 2489 const input = path.join(testDir, 'in.css') 2490 const outfile = path.join(testDir, 'out.css') 2491 await writeFileAsync(input, `div { color: red }`) 2492 await esbuild.build({ 2493 entryPoints: [input], 2494 outfile, 2495 banner: { css: '/* banner */' }, 2496 footer: { css: '/* footer */' }, 2497 }) 2498 const code = await readFileAsync(outfile, 'utf8') 2499 assert.strictEqual(code, `/* banner */\ndiv {\n color: red;\n}\n/* footer */\n`) 2500 }, 2501 2502 async buildRelativeIssue693({ esbuild }) { 2503 const result = await esbuild.build({ 2504 stdin: { 2505 contents: `const x=1`, 2506 }, 2507 write: false, 2508 outfile: 'esbuild.js', 2509 }); 2510 assert.strictEqual(result.outputFiles.length, 1) 2511 assert.strictEqual(result.outputFiles[0].path, path.join(process.cwd(), 'esbuild.js')) 2512 assert.strictEqual(result.outputFiles[0].text, 'const x = 1;\n') 2513 }, 2514 2515 async rebuildBasic({ esbuild, testDir }) { 2516 const input = path.join(testDir, 'in.js') 2517 const output = path.join(testDir, 'out.js') 2518 const context = await esbuild.context({ 2519 entryPoints: [input], 2520 outfile: output, 2521 format: 'esm', 2522 }); 2523 2524 // Build 1 2525 await writeFileAsync(input, `console.log('abc')`) 2526 const result1 = await context.rebuild(); 2527 assert.strictEqual(result1.outputFiles, void 0) 2528 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log("abc");\n`) 2529 2530 // Build 2 2531 await writeFileAsync(input, `console.log('xyz')`) 2532 const result2 = await context.rebuild(); 2533 assert.strictEqual(result2.outputFiles, void 0) 2534 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log("xyz");\n`) 2535 2536 // Build 3 2537 await writeFileAsync(input, `console.log(123)`) 2538 const result3 = await context.rebuild(); 2539 assert.strictEqual(result3.outputFiles, void 0) 2540 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log(123);\n`) 2541 2542 // Further rebuilds should not be possible after a dispose 2543 context.dispose() 2544 try { 2545 await context.rebuild() 2546 throw new Error('Expected an error to be thrown') 2547 } catch (e) { 2548 assert.strictEqual(e.message, 'Cannot rebuild') 2549 } 2550 }, 2551 2552 async rebuildMerging({ esbuild, testDir }) { 2553 const input = path.join(testDir, 'in.js') 2554 const output = path.join(testDir, 'out.js') 2555 await writeFileAsync(input, `console.log(123)`) 2556 2557 let resolveWait 2558 let waitPromise = new Promise(resolve => { 2559 resolveWait = resolve 2560 }) 2561 const waitPlugin = { 2562 name: 'wait-plugin', 2563 setup(build) { 2564 build.onStart(() => waitPromise) 2565 }, 2566 } 2567 2568 const context = await esbuild.context({ 2569 entryPoints: [input], 2570 outfile: output, 2571 format: 'esm', 2572 plugins: [waitPlugin], 2573 write: false, 2574 }); 2575 2576 try { 2577 // Do two rebuilds 2578 const rebuild1 = context.rebuild() 2579 const rebuild2 = context.rebuild() 2580 2581 // Let the build end 2582 resolveWait() 2583 2584 // Get both rebuild results 2585 const result1 = await rebuild1; 2586 const result2 = await rebuild2; 2587 assert.strictEqual(result1, result2) 2588 assert.strictEqual(result1.outputFiles.length, 1) 2589 assert.strictEqual(result1.outputFiles[0].text, 'console.log(123);\n') 2590 2591 // Make an edit 2592 await writeFileAsync(input, `console.log(234)`) 2593 2594 // Do two more rebuilds 2595 waitPromise = new Promise(resolve => { 2596 resolveWait = resolve 2597 }) 2598 const rebuild3 = context.rebuild() 2599 const rebuild4 = context.rebuild() 2600 2601 // Let the build end 2602 resolveWait() 2603 2604 // Get both rebuild results 2605 const result3 = await rebuild3; 2606 const result4 = await rebuild4; 2607 assert.strictEqual(result3, result4) 2608 assert.notStrictEqual(result3, result1) 2609 assert.strictEqual(result3.outputFiles.length, 1) 2610 assert.strictEqual(result3.outputFiles[0].text, 'console.log(234);\n') 2611 } finally { 2612 await context.dispose() 2613 } 2614 }, 2615 2616 async rebuildIndependent({ esbuild, testDir }) { 2617 const inputA = path.join(testDir, 'in-a.js') 2618 const inputB = path.join(testDir, 'in-b.js') 2619 const outputA = path.join(testDir, 'out-a.js') 2620 const outputB = path.join(testDir, 'out-b.js') 2621 const contextA = await esbuild.context({ 2622 entryPoints: [inputA], 2623 outfile: outputA, 2624 format: 'esm', 2625 }) 2626 const contextB = await esbuild.context({ 2627 entryPoints: [inputB], 2628 outfile: outputB, 2629 format: 'esm', 2630 }) 2631 2632 // Build 1 2633 await writeFileAsync(inputA, `console.log('a')`) 2634 await writeFileAsync(inputB, `console.log('b')`) 2635 assert.notStrictEqual(contextA.rebuild, contextB.rebuild) 2636 const resultA1 = await contextA.rebuild() 2637 const resultB1 = await contextB.rebuild() 2638 assert.strictEqual(resultA1.outputFiles, void 0) 2639 assert.strictEqual(resultB1.outputFiles, void 0) 2640 assert.strictEqual(await readFileAsync(outputA, 'utf8'), `console.log("a");\n`) 2641 assert.strictEqual(await readFileAsync(outputB, 'utf8'), `console.log("b");\n`) 2642 2643 // Build 2 2644 await writeFileAsync(inputA, `console.log(1)`) 2645 await writeFileAsync(inputB, `console.log(2)`) 2646 const promiseA = contextA.rebuild(); 2647 const promiseB = contextB.rebuild(); 2648 const resultA2 = await promiseA; 2649 const resultB2 = await promiseB; 2650 assert.strictEqual(resultA2.outputFiles, void 0) 2651 assert.strictEqual(resultB2.outputFiles, void 0) 2652 assert.strictEqual(await readFileAsync(outputA, 'utf8'), `console.log(1);\n`) 2653 assert.strictEqual(await readFileAsync(outputB, 'utf8'), `console.log(2);\n`) 2654 2655 // Further rebuilds should not be possible after a dispose 2656 contextA.dispose() 2657 try { 2658 await contextA.rebuild() 2659 throw new Error('Expected an error to be thrown (context A)') 2660 } catch (e) { 2661 assert.strictEqual(e.message, 'Cannot rebuild') 2662 } 2663 2664 // Build 3 2665 await writeFileAsync(inputB, `console.log(3)`) 2666 const resultB3 = await contextB.rebuild() 2667 assert.strictEqual(resultB3.outputFiles, void 0) 2668 assert.strictEqual(await readFileAsync(outputB, 'utf8'), `console.log(3);\n`) 2669 2670 // Further rebuilds should not be possible after a dispose 2671 contextB.dispose() 2672 try { 2673 await contextB.rebuild() 2674 throw new Error('Expected an error to be thrown (context B)') 2675 } catch (e) { 2676 assert.strictEqual(e.message, 'Cannot rebuild') 2677 } 2678 }, 2679 2680 async rebuildParallel({ esbuild, testDir }) { 2681 const input = path.join(testDir, 'in.js') 2682 const output = path.join(testDir, 'out.js') 2683 const context = await esbuild.context({ 2684 entryPoints: [input], 2685 outfile: output, 2686 format: 'esm', 2687 }) 2688 2689 try { 2690 // Build 1 2691 await writeFileAsync(input, `console.log('abc')`) 2692 const result1 = await context.rebuild() 2693 assert.strictEqual(result1.outputFiles, void 0) 2694 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log("abc");\n`) 2695 2696 // Build 2 2697 await writeFileAsync(input, `console.log('xyz')`) 2698 const promise2A = context.rebuild(); 2699 const promise2B = context.rebuild(); 2700 const result2A = await promise2A; 2701 const result2B = await promise2B; 2702 assert.strictEqual(result2A.outputFiles, void 0) 2703 assert.strictEqual(result2B.outputFiles, void 0) 2704 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log("xyz");\n`) 2705 2706 // Build 3 2707 await writeFileAsync(input, `console.log(123)`) 2708 const promise3A = context.rebuild(); 2709 const promise3B = context.rebuild(); 2710 const result3A = await promise3A; 2711 const result3B = await promise3B; 2712 assert.strictEqual(result3A.outputFiles, void 0) 2713 assert.strictEqual(result3B.outputFiles, void 0) 2714 assert.strictEqual(await readFileAsync(output, 'utf8'), `console.log(123);\n`) 2715 } finally { 2716 context.dispose() 2717 } 2718 2719 // Further rebuilds should not be possible after a dispose 2720 try { 2721 await context.rebuild() 2722 throw new Error('Expected an error to be thrown') 2723 } catch (e) { 2724 assert.strictEqual(e.message, 'Cannot rebuild') 2725 } 2726 }, 2727 2728 async rebuildCancel({ esbuild }) { 2729 let loopForever = true 2730 let onEndResult 2731 let onLoadCallback 2732 2733 const context = await esbuild.context({ 2734 entryPoints: ['entry'], 2735 bundle: true, 2736 write: false, 2737 logLevel: 'silent', 2738 format: 'esm', 2739 plugins: [{ 2740 name: '∞', 2741 setup(build) { 2742 build.onResolve({ filter: /.*/ }, args => { 2743 return { path: args.path, namespace: '∞' } 2744 }) 2745 build.onLoad({ filter: /.*/ }, async (args) => { 2746 onLoadCallback() 2747 if (!loopForever) return { contents: 'foo()' } 2748 await new Promise(r => setTimeout(r, 10)) 2749 return { contents: 'import ' + JSON.stringify(args.path + '.') } 2750 }) 2751 build.onEnd(result => { 2752 onEndResult = result 2753 }) 2754 }, 2755 }], 2756 }) 2757 2758 try { 2759 // Build 1 2760 { 2761 // Stop the build when "onLoad" has been called at least 5 times 2762 const shouldCancelPromise = new Promise(resolve => { 2763 let count = 0 2764 onLoadCallback = () => { 2765 if (++count > 5) resolve() 2766 } 2767 }) 2768 2769 // Start a build 2770 const buildPromise = context.rebuild() 2771 2772 // Add a dummy catch handler to avoid terminating due to an unhandled exception 2773 buildPromise.catch(() => { }) 2774 2775 // Cancel the build 2776 await shouldCancelPromise 2777 await context.cancel() 2778 2779 // Check the result 2780 try { 2781 await buildPromise 2782 throw new Error('Expected an error to be thrown') 2783 } catch (error) { 2784 assert.strictEqual(error.message, `Build failed with 1 error:\nerror: The build was canceled`) 2785 assert.strictEqual(error.errors.length, 1) 2786 assert.strictEqual(error.warnings.length, 0) 2787 assert.notStrictEqual(onEndResult, undefined) 2788 assert.strictEqual(onEndResult.errors.length, 1) 2789 assert.strictEqual(onEndResult.errors[0].text, 'The build was canceled') 2790 assert.strictEqual(onEndResult.warnings.length, 0) 2791 assert.strictEqual(onEndResult.outputFiles.length, 0) 2792 } 2793 } 2794 2795 // Build 2 2796 { 2797 // Stop the build when "onLoad" has been called at least 5 times 2798 const shouldCancelPromise = new Promise(resolve => { 2799 let count = 0 2800 onLoadCallback = () => { 2801 if (++count > 5) resolve() 2802 } 2803 }) 2804 2805 // Start a build 2806 const buildPromise = context.rebuild() 2807 2808 // Add a dummy catch handler to avoid terminating due to an unhandled exception 2809 buildPromise.catch(() => { }) 2810 2811 // Cancel the build 2812 await shouldCancelPromise 2813 await context.cancel() 2814 2815 // Check the result 2816 try { 2817 await buildPromise 2818 throw new Error('Expected an error to be thrown') 2819 } catch (error) { 2820 assert.strictEqual(error.message, `Build failed with 1 error:\nerror: The build was canceled`) 2821 assert.strictEqual(error.errors.length, 1) 2822 assert.strictEqual(error.warnings.length, 0) 2823 assert.notStrictEqual(onEndResult, undefined) 2824 assert.strictEqual(onEndResult.errors.length, 1) 2825 assert.strictEqual(onEndResult.errors[0].text, 'The build was canceled') 2826 assert.strictEqual(onEndResult.warnings.length, 0) 2827 assert.strictEqual(onEndResult.outputFiles.length, 0) 2828 } 2829 } 2830 2831 // Build 3 2832 loopForever = false 2833 { 2834 const result = await context.rebuild() 2835 assert.strictEqual(result.errors.length, 0) 2836 assert.strictEqual(result.warnings.length, 0) 2837 assert.strictEqual(result.outputFiles.length, 1) 2838 assert.strictEqual(result.outputFiles[0].text, `// ∞:entry\nfoo();\n`) 2839 assert.strictEqual(onEndResult, result) 2840 } 2841 } finally { 2842 context.dispose() 2843 } 2844 }, 2845 2846 // This test checks for races between manual "rebuild()" and "cancel()". 2847 // Ideally calling "cancel()" after "rebuild()" will always cancel it even 2848 // if the Go side hasn't started running the rebuild yet. Since Go is multi- 2849 // threaded, Go can actually start running the "cancel()" before the 2850 // "rebuild()" (i.e. in the other order). The Go code does some complex stuff 2851 // with mutexes to try to make this ideal behavior happen despite multi- 2852 // threading. 2853 async rapidRebuildCancel({ esbuild }) { 2854 const context = await esbuild.context({ 2855 entryPoints: ['entry'], 2856 logLevel: 'silent', 2857 plugins: [{ 2858 name: '∞', 2859 setup(build) { 2860 build.onResolve({ filter: /.*/ }, args => { 2861 return { path: args.path, namespace: '∞' } 2862 }) 2863 build.onLoad({ filter: /.*/ }, async (args) => { 2864 await new Promise(r => setTimeout(r, 10)) 2865 return { contents: 'import ' + JSON.stringify(args.path + '.') } 2866 }) 2867 }, 2868 }], 2869 }) 2870 2871 try { 2872 const promises = [] 2873 for (let i = 0; i < 100; i++) { 2874 const promise = context.rebuild() 2875 promise.catch(() => { /* avoid termination due to an uncaught exception */ }) 2876 promises.push(promise) 2877 await context.cancel() 2878 } 2879 2880 for (let i = 0; i < promises.length; i++) { 2881 try { 2882 await promises[i] 2883 throw new Error('Expected an error to be thrown for rebuild ' + i) 2884 } catch (err) { 2885 if (!err.errors || err.errors.length !== 1 || err.errors[0].text !== 'The build was canceled') 2886 throw err 2887 } 2888 } 2889 } finally { 2890 context.dispose() 2891 } 2892 }, 2893 2894 async bundleAvoidTDZ({ esbuild }) { 2895 var { outputFiles } = await esbuild.build({ 2896 stdin: { 2897 contents: ` 2898 class Foo { 2899 // The above line will be transformed into "var". However, the 2900 // symbol "Foo" must still be defined before the class body ends. 2901 static foo = new Foo 2902 } 2903 if (!(Foo.foo instanceof Foo)) 2904 throw 'fail' 2905 `, 2906 }, 2907 bundle: true, 2908 write: false, 2909 }) 2910 assert.strictEqual(outputFiles.length, 1) 2911 new Function(outputFiles[0].text)() 2912 }, 2913 2914 async bundleTSAvoidTDZ({ esbuild }) { 2915 var { outputFiles } = await esbuild.build({ 2916 stdin: { 2917 contents: ` 2918 class Foo { 2919 // The above line will be transformed into "var". However, the 2920 // symbol "Foo" must still be defined before the class body ends. 2921 static foo = new Foo 2922 } 2923 if (!(Foo.foo instanceof Foo)) 2924 throw 'fail' 2925 `, 2926 loader: 'ts', 2927 }, 2928 bundle: true, 2929 write: false, 2930 }) 2931 assert.strictEqual(outputFiles.length, 1) 2932 new Function(outputFiles[0].text)() 2933 }, 2934 2935 async bundleTSDecoratorAvoidTDZ({ testDir, esbuild }) { 2936 const input = path.join(testDir, 'input.ts') 2937 await writeFileAsync(input, ` 2938 class Bar {} 2939 var oldFoo 2940 function swap(target) { 2941 oldFoo = target 2942 return Bar 2943 } 2944 @swap 2945 class Foo { 2946 bar() { return new Foo } 2947 static foo = new Foo 2948 } 2949 if (!(oldFoo.foo instanceof oldFoo)) 2950 throw 'fail: foo' 2951 if (!(oldFoo.foo.bar() instanceof Bar)) 2952 throw 'fail: bar' 2953 `) 2954 await writeFileAsync(path.join(testDir, 'tsconfig.json'), `{ 2955 "compilerOptions": { 2956 "experimentalDecorators": true, 2957 }, 2958 }`) 2959 var { outputFiles } = await esbuild.build({ 2960 entryPoints: [input], 2961 bundle: true, 2962 write: false, 2963 }) 2964 assert.strictEqual(outputFiles.length, 1) 2965 new Function(outputFiles[0].text)() 2966 }, 2967 2968 async automaticEntryPointOutputPathsWithDot({ esbuild, testDir }) { 2969 const input = path.join(testDir, 'in.file.ts') 2970 const css = path.join(testDir, 'file.css') 2971 await writeFileAsync(input, `import './file.css'; console.log('test')`) 2972 await writeFileAsync(css, `body { color: red }`) 2973 var { outputFiles } = await esbuild.build({ 2974 entryPoints: [input], 2975 outdir: testDir, 2976 bundle: true, 2977 write: false, 2978 }) 2979 assert.strictEqual(outputFiles.length, 2) 2980 assert.strictEqual(outputFiles[0].path, path.join(testDir, 'in.file.js')) 2981 assert.strictEqual(outputFiles[1].path, path.join(testDir, 'in.file.css')) 2982 }, 2983 2984 async customEntryPointOutputPathsWithDot({ esbuild, testDir }) { 2985 const input = path.join(testDir, 'in.file.ts') 2986 const css = path.join(testDir, 'file.css') 2987 await writeFileAsync(input, `import './file.css'; console.log('test')`) 2988 await writeFileAsync(css, `body { color: red }`) 2989 var { outputFiles } = await esbuild.build({ 2990 entryPoints: { 2991 'out.test': input, 2992 }, 2993 outdir: testDir, 2994 bundle: true, 2995 write: false, 2996 }) 2997 assert.strictEqual(outputFiles.length, 2) 2998 assert.strictEqual(outputFiles[0].path, path.join(testDir, 'out.test.js')) 2999 assert.strictEqual(outputFiles[1].path, path.join(testDir, 'out.test.css')) 3000 }, 3001 3002 async customEntryPointOutputPathsRel({ esbuild, testDir }) { 3003 const input1 = path.join(testDir, 'in1.js') 3004 const input2 = path.join(testDir, 'in2.js') 3005 const output1 = 'out/1.cjs' 3006 const output2 = 'out/2.mjs' 3007 await writeFileAsync(input1, `console.log('in1')`) 3008 await writeFileAsync(input2, `console.log('in2')`) 3009 var { outputFiles } = await esbuild.build({ 3010 entryPoints: { 3011 [output1]: input1, 3012 [output2]: input2, 3013 }, 3014 entryNames: 'entry/[dir]/[hash]-[name]', 3015 outdir: testDir, 3016 write: false, 3017 }) 3018 assert.strictEqual(outputFiles.length, 2) 3019 assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'CXHWNMAN-1.cjs.js')) 3020 assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', 'EYSNILNO-2.mjs.js')) 3021 }, 3022 3023 async customEntryPointOutputPathsAbs({ esbuild, testDir }) { 3024 const input1 = path.join(testDir, 'in1.js') 3025 const input2 = path.join(testDir, 'in2.js') 3026 const output1 = path.join(testDir, 'out/1') 3027 const output2 = path.join(testDir, 'out/2') 3028 await writeFileAsync(input1, `console.log('in1')`) 3029 await writeFileAsync(input2, `console.log('in2')`) 3030 var { outputFiles } = await esbuild.build({ 3031 entryPoints: { 3032 [output1]: input1, 3033 [output2]: input2, 3034 }, 3035 entryNames: 'entry/[dir]/[hash]-[name]', 3036 outdir: testDir, 3037 write: false, 3038 }) 3039 assert.strictEqual(outputFiles.length, 2) 3040 assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'TIORPBNU-1.js')) 3041 assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', '3KY7NOSR-2.js')) 3042 }, 3043 3044 async customEntryPointOutputPathsDuplicates({ esbuild, testDir }) { 3045 const input1 = path.join(testDir, 'foo.js') 3046 const input2 = path.join(testDir, 'bar.css') 3047 await writeFileAsync(input1, `foo()`) 3048 await writeFileAsync(input2, `.bar {}`) 3049 var { outputFiles } = await esbuild.build({ 3050 entryPoints: [ 3051 { in: input1, out: 'abc' }, 3052 { in: input2, out: 'abc' }, 3053 ], 3054 outdir: testDir, 3055 write: false, 3056 }) 3057 assert.strictEqual(outputFiles.length, 2) 3058 assert.strictEqual(outputFiles[0].path, path.join(testDir, 'abc.js')) 3059 assert.strictEqual(outputFiles[1].path, path.join(testDir, 'abc.css')) 3060 }, 3061 3062 async nodeColonPrefixImport({ esbuild }) { 3063 const tryTargetESM = async target => { 3064 const result = await esbuild.build({ 3065 stdin: { contents: `import fs from 'node:fs'; import('node:fs'); fs()` }, 3066 bundle: true, 3067 platform: 'node', 3068 target, 3069 format: 'esm', 3070 write: false, 3071 }) 3072 const code = result.outputFiles[0].text 3073 return code.slice(code.indexOf(`// <stdin>\n`)) 3074 } 3075 3076 assert.strictEqual(await tryTargetESM('node14.13.1'), `// <stdin>\nimport fs from "node:fs";\nimport("node:fs");\nfs();\n`) 3077 assert.strictEqual(await tryTargetESM('node14.13.0'), `// <stdin>\nimport fs from "fs";\nimport("fs");\nfs();\n`) 3078 assert.strictEqual(await tryTargetESM('node13'), `// <stdin>\nimport fs from "fs";\nPromise.resolve().then(() => __toESM(__require("fs")));\nfs();\n`) 3079 assert.strictEqual(await tryTargetESM('node12.99'), `// <stdin>\nimport fs from "node:fs";\nimport("node:fs");\nfs();\n`) 3080 assert.strictEqual(await tryTargetESM('node12.20'), `// <stdin>\nimport fs from "node:fs";\nimport("node:fs");\nfs();\n`) 3081 assert.strictEqual(await tryTargetESM('node12.19'), `// <stdin>\nimport fs from "fs";\nPromise.resolve().then(() => __toESM(__require("fs")));\nfs();\n`) 3082 }, 3083 3084 async nodeColonPrefixRequire({ esbuild }) { 3085 const tryTargetESM = async target => { 3086 const result = await esbuild.build({ 3087 stdin: { contents: `require('node:fs'); require.resolve('node:fs')` }, 3088 bundle: true, 3089 platform: 'node', 3090 target, 3091 format: 'cjs', 3092 write: false, 3093 }) 3094 const code = result.outputFiles[0].text 3095 return code.slice(code.indexOf(`// <stdin>\n`)) 3096 } 3097 3098 assert.strictEqual(await tryTargetESM('node16'), `// <stdin>\nrequire("node:fs");\nrequire.resolve("node:fs");\n`) 3099 assert.strictEqual(await tryTargetESM('node15.99'), `// <stdin>\nrequire("fs");\nrequire.resolve("fs");\n`) 3100 assert.strictEqual(await tryTargetESM('node15'), `// <stdin>\nrequire("fs");\nrequire.resolve("fs");\n`) 3101 assert.strictEqual(await tryTargetESM('node14.99'), `// <stdin>\nrequire("node:fs");\nrequire.resolve("node:fs");\n`) 3102 assert.strictEqual(await tryTargetESM('node14.18'), `// <stdin>\nrequire("node:fs");\nrequire.resolve("node:fs");\n`) 3103 assert.strictEqual(await tryTargetESM('node14.17'), `// <stdin>\nrequire("fs");\nrequire.resolve("fs");\n`) 3104 }, 3105 3106 async nodeColonPrefixImportTurnedIntoRequire({ esbuild }) { 3107 const tryTargetESM = async target => { 3108 const result = await esbuild.build({ 3109 stdin: { contents: `import fs from 'node:fs'; import('node:fs'); fs()` }, 3110 bundle: true, 3111 platform: 'node', 3112 target, 3113 format: 'cjs', 3114 write: false, 3115 }) 3116 const code = result.outputFiles[0].text 3117 return code.slice(code.indexOf(`// <stdin>\n`)) 3118 } 3119 3120 assert.strictEqual(await tryTargetESM('node16'), `// <stdin>\nvar import_node_fs = __toESM(require("node:fs"));\nimport("node:fs");\n(0, import_node_fs.default)();\n`) 3121 assert.strictEqual(await tryTargetESM('node15.99'), `// <stdin>\nvar import_node_fs = __toESM(require("fs"));\nimport("fs");\n(0, import_node_fs.default)();\n`) 3122 assert.strictEqual(await tryTargetESM('node15'), `// <stdin>\nvar import_node_fs = __toESM(require("fs"));\nimport("fs");\n(0, import_node_fs.default)();\n`) 3123 assert.strictEqual(await tryTargetESM('node14.99'), `// <stdin>\nvar import_node_fs = __toESM(require("node:fs"));\nimport("node:fs");\n(0, import_node_fs.default)();\n`) 3124 assert.strictEqual(await tryTargetESM('node14.18'), `// <stdin>\nvar import_node_fs = __toESM(require("node:fs"));\nimport("node:fs");\n(0, import_node_fs.default)();\n`) 3125 assert.strictEqual(await tryTargetESM('node14.17'), `// <stdin>\nvar import_node_fs = __toESM(require("fs"));\nimport("fs");\n(0, import_node_fs.default)();\n`) 3126 }, 3127 3128 async zipFile({ esbuild, testDir }) { 3129 const entry = path.join(testDir, 'entry.js') 3130 const zip = path.join(testDir, 'test.zip') 3131 3132 await writeFileAsync(entry, ` 3133 import foo from './test.zip/foo.js' 3134 import bar from './test.zip/bar/bar.js' 3135 3136 import __virtual__1 from './test.zip/__virtual__/ignored/0/foo.js' 3137 import __virtual__2 from './test.zip/ignored/__virtual__/ignored/1/foo.js' 3138 import __virtual__3 from './test.zip/__virtual__/ignored/1/test.zip/foo.js' 3139 3140 import $$virtual1 from './test.zip/$$virtual/ignored/0/foo.js' 3141 import $$virtual2 from './test.zip/ignored/$$virtual/ignored/1/foo.js' 3142 import $$virtual3 from './test.zip/$$virtual/ignored/1/test.zip/foo.js' 3143 3144 console.log({ 3145 foo, 3146 bar, 3147 3148 __virtual__1, 3149 __virtual__2, 3150 __virtual__3, 3151 3152 $$virtual1, 3153 $$virtual2, 3154 $$virtual3, 3155 }) 3156 `) 3157 3158 // This uses the real file system instead of the mock file system so that 3159 // we can check that everything works as expected on Windows, which is not 3160 // a POSIX environment. 3161 await writeFileAsync(zip, Buffer.from( 3162 `UEsDBAoAAgAAAG1qCFUSAXosFQAAABUAAAAGABwAZm9vLmpzVVQJAAOeRfFioEXxYnV4C` + 3163 `wABBPUBAAAEFAAAAGV4cG9ydCBkZWZhdWx0ICdmb28nClBLAwQKAAIAAABzaghVwuDbLR` + 3164 `UAAAAVAAAACgAcAGJhci9iYXIuanNVVAkAA6lF8WKrRfFidXgLAAEE9QEAAAQUAAAAZXh` + 3165 `wb3J0IGRlZmF1bHQgJ2JhcicKUEsBAh4DCgACAAAAbWoIVRIBeiwVAAAAFQAAAAYAGAAA` + 3166 `AAAAAQAAAKSBAAAAAGZvby5qc1VUBQADnkXxYnV4CwABBPUBAAAEFAAAAFBLAQIeAwoAA` + 3167 `gAAAHNqCFXC4NstFQAAABUAAAAKABgAAAAAAAEAAACkgVUAAABiYXIvYmFyLmpzVVQFAA` + 3168 `OpRfFidXgLAAEE9QEAAAQUAAAAUEsFBgAAAAACAAIAnAAAAK4AAAAAAA==`, 'base64')) 3169 3170 const value = await esbuild.build({ 3171 entryPoints: [entry], 3172 bundle: true, 3173 write: false, 3174 }) 3175 3176 assert.strictEqual(value.outputFiles.length, 1) 3177 assert.strictEqual(value.outputFiles[0].text, `(() => { 3178 // scripts/.js-api-tests/zipFile/test.zip/foo.js 3179 var foo_default = "foo"; 3180 3181 // scripts/.js-api-tests/zipFile/test.zip/bar/bar.js 3182 var bar_default = "bar"; 3183 3184 // scripts/.js-api-tests/zipFile/test.zip/__virtual__/ignored/0/foo.js 3185 var foo_default2 = "foo"; 3186 3187 // scripts/.js-api-tests/zipFile/test.zip/ignored/__virtual__/ignored/1/foo.js 3188 var foo_default3 = "foo"; 3189 3190 // scripts/.js-api-tests/zipFile/test.zip/__virtual__/ignored/1/test.zip/foo.js 3191 var foo_default4 = "foo"; 3192 3193 // scripts/.js-api-tests/zipFile/test.zip/$$virtual/ignored/0/foo.js 3194 var foo_default5 = "foo"; 3195 3196 // scripts/.js-api-tests/zipFile/test.zip/ignored/$$virtual/ignored/1/foo.js 3197 var foo_default6 = "foo"; 3198 3199 // scripts/.js-api-tests/zipFile/test.zip/$$virtual/ignored/1/test.zip/foo.js 3200 var foo_default7 = "foo"; 3201 3202 // scripts/.js-api-tests/zipFile/entry.js 3203 console.log({ 3204 foo: foo_default, 3205 bar: bar_default, 3206 __virtual__1: foo_default2, 3207 __virtual__2: foo_default3, 3208 __virtual__3: foo_default4, 3209 $$virtual1: foo_default5, 3210 $$virtual2: foo_default6, 3211 $$virtual3: foo_default7 3212 }); 3213 })(); 3214 `) 3215 }, 3216 3217 async yarnPnP_pnp_data_json({ esbuild, testDir }) { 3218 const entry = path.join(testDir, 'entry.js') 3219 const manifest = path.join(testDir, '.pnp.data.json') 3220 const leftPad = path.join(testDir, '.yarn', 'cache', 'left-pad-zip', 'node_modules', 'left-pad', 'index.js') 3221 3222 await writeFileAsync(entry, ` 3223 import leftPad from 'left-pad' 3224 console.log(leftPad()) 3225 `) 3226 3227 await writeFileAsync(manifest, `{ 3228 "packageRegistryData": [ 3229 [null, [ 3230 [null, { 3231 "packageLocation": "./", 3232 "packageDependencies": [ 3233 ["left-pad", "npm:1.3.0"] 3234 ], 3235 "linkType": "SOFT" 3236 }] 3237 ]], 3238 ["left-pad", [ 3239 ["npm:1.3.0", { 3240 "packageLocation": "./.yarn/cache/left-pad-zip/node_modules/left-pad/", 3241 "packageDependencies": [ 3242 ["left-pad", "npm:1.3.0"] 3243 ], 3244 "linkType": "HARD" 3245 }] 3246 ]] 3247 ] 3248 }`) 3249 3250 await mkdirAsync(path.dirname(leftPad), { recursive: true }) 3251 await writeFileAsync(leftPad, `export default function() {}`) 3252 3253 const value = await esbuild.build({ 3254 entryPoints: [entry], 3255 bundle: true, 3256 write: false, 3257 absWorkingDir: testDir, 3258 }) 3259 3260 assert.strictEqual(value.outputFiles.length, 1) 3261 assert.strictEqual(value.outputFiles[0].text, `(() => { 3262 // .yarn/cache/left-pad-zip/node_modules/left-pad/index.js 3263 function left_pad_default() { 3264 } 3265 3266 // entry.js 3267 console.log(left_pad_default()); 3268 })(); 3269 `) 3270 }, 3271 3272 async yarnPnP_pnp_js_object_literal({ esbuild, testDir }) { 3273 const entry = path.join(testDir, 'entry.js') 3274 const manifest = path.join(testDir, '.pnp.js') 3275 const leftPad = path.join(testDir, '.yarn', 'cache', 'left-pad-zip', 'node_modules', 'left-pad', 'index.js') 3276 3277 await writeFileAsync(entry, ` 3278 import leftPad from 'left-pad' 3279 console.log(leftPad()) 3280 `) 3281 3282 await writeFileAsync(manifest, `#!/usr/bin/env node 3283 /* eslint-disable */ 3284 3285 try { 3286 Object.freeze({}).detectStrictMode = true; 3287 } catch (error) { 3288 throw new Error(); 3289 } 3290 3291 function $$SETUP_STATE(hydrateRuntimeState, basePath) { 3292 return hydrateRuntimeState({ 3293 "packageRegistryData": [ 3294 [null, [ 3295 [null, { 3296 "packageLocation": "./", 3297 "packageDependencies": [ 3298 ["left-pad", "npm:1.3.0"] 3299 ], 3300 "linkType": "SOFT" 3301 }] 3302 ]], 3303 ["left-pad", [ 3304 ["npm:1.3.0", { 3305 "packageLocation": "./.yarn/cache/left-pad-zip/node_modules/left-pad/", 3306 "packageDependencies": [ 3307 ["left-pad", "npm:1.3.0"] 3308 ], 3309 "linkType": "HARD" 3310 }] 3311 ]] 3312 ] 3313 }) 3314 } 3315 `) 3316 3317 await mkdirAsync(path.dirname(leftPad), { recursive: true }) 3318 await writeFileAsync(leftPad, `export default function() {}`) 3319 3320 const value = await esbuild.build({ 3321 entryPoints: [entry], 3322 bundle: true, 3323 write: false, 3324 absWorkingDir: testDir, 3325 }) 3326 3327 assert.strictEqual(value.outputFiles.length, 1) 3328 assert.strictEqual(value.outputFiles[0].text, `(() => { 3329 // .yarn/cache/left-pad-zip/node_modules/left-pad/index.js 3330 function left_pad_default() { 3331 } 3332 3333 // entry.js 3334 console.log(left_pad_default()); 3335 })(); 3336 `) 3337 }, 3338 3339 async yarnPnP_pnp_cjs_JSON_parse_string_literal({ esbuild, testDir }) { 3340 const entry = path.join(testDir, 'entry.js') 3341 const manifest = path.join(testDir, '.pnp.cjs') 3342 const leftPad = path.join(testDir, '.yarn', 'cache', 'left-pad-zip', 'node_modules', 'left-pad', 'index.js') 3343 3344 await writeFileAsync(entry, ` 3345 import leftPad from 'left-pad' 3346 console.log(leftPad()) 3347 `) 3348 3349 await writeFileAsync(manifest, `#!/usr/bin/env node 3350 /* eslint-disable */ 3351 3352 try { 3353 Object.freeze({}).detectStrictMode = true; 3354 } catch (error) { 3355 throw new Error(); 3356 } 3357 3358 function $$SETUP_STATE(hydrateRuntimeState, basePath) { 3359 return hydrateRuntimeState(JSON.parse('{\\ 3360 "packageRegistryData": [\\ 3361 [null, [\\ 3362 [null, {\\ 3363 "packageLocation": "./",\\ 3364 "packageDependencies": [\\ 3365 ["left-pad", "npm:1.3.0"]\\ 3366 ],\\ 3367 "linkType": "SOFT"\\ 3368 }]\\ 3369 ]],\\ 3370 ["left-pad", [\\ 3371 ["npm:1.3.0", {\\ 3372 "packageLocation": "./.yarn/cache/left-pad-zip/node_modules/left-pad/",\\ 3373 "packageDependencies": [\\ 3374 ["left-pad", "npm:1.3.0"]\\ 3375 ],\\ 3376 "linkType": "HARD"\\ 3377 }]\\ 3378 ]]\\ 3379 ]\\ 3380 }')) 3381 } 3382 `) 3383 3384 await mkdirAsync(path.dirname(leftPad), { recursive: true }) 3385 await writeFileAsync(leftPad, `export default function() {}`) 3386 3387 const value = await esbuild.build({ 3388 entryPoints: [entry], 3389 bundle: true, 3390 write: false, 3391 absWorkingDir: testDir, 3392 }) 3393 3394 assert.strictEqual(value.outputFiles.length, 1) 3395 assert.strictEqual(value.outputFiles[0].text, `(() => { 3396 // .yarn/cache/left-pad-zip/node_modules/left-pad/index.js 3397 function left_pad_default() { 3398 } 3399 3400 // entry.js 3401 console.log(left_pad_default()); 3402 })(); 3403 `) 3404 }, 3405 3406 async yarnPnP_pnp_cjs_JSON_parse_identifier({ esbuild, testDir }) { 3407 const entry = path.join(testDir, 'entry.js') 3408 const manifest = path.join(testDir, '.pnp.cjs') 3409 const leftPad = path.join(testDir, '.yarn', 'cache', 'left-pad-zip', 'node_modules', 'left-pad', 'index.js') 3410 3411 await writeFileAsync(entry, ` 3412 import leftPad from 'left-pad' 3413 console.log(leftPad()) 3414 `) 3415 3416 await writeFileAsync(manifest, `#!/usr/bin/env node 3417 /* eslint-disable */ 3418 3419 try { 3420 Object.freeze({}).detectStrictMode = true; 3421 } catch (error) { 3422 throw new Error(); 3423 } 3424 3425 const RAW_RUNTIME_STATE = '{\\ 3426 "packageRegistryData": [\\ 3427 [null, [\\ 3428 [null, {\\ 3429 "packageLocation": "./",\\ 3430 "packageDependencies": [\\ 3431 ["left-pad", "npm:1.3.0"]\\ 3432 ],\\ 3433 "linkType": "SOFT"\\ 3434 }]\\ 3435 ]],\\ 3436 ["left-pad", [\\ 3437 ["npm:1.3.0", {\\ 3438 "packageLocation": "./.yarn/cache/left-pad-zip/node_modules/left-pad/",\\ 3439 "packageDependencies": [\\ 3440 ["left-pad", "npm:1.3.0"]\\ 3441 ],\\ 3442 "linkType": "HARD"\\ 3443 }]\\ 3444 ]]\\ 3445 ]\\ 3446 }' 3447 3448 function $$SETUP_STATE(hydrateRuntimeState, basePath) { 3449 return hydrateRuntimeState(JSON.parse(RAW_RUNTIME_STATE)) 3450 } 3451 `) 3452 3453 await mkdirAsync(path.dirname(leftPad), { recursive: true }) 3454 await writeFileAsync(leftPad, `export default function() {}`) 3455 3456 const value = await esbuild.build({ 3457 entryPoints: [entry], 3458 bundle: true, 3459 write: false, 3460 absWorkingDir: testDir, 3461 }) 3462 3463 assert.strictEqual(value.outputFiles.length, 1) 3464 assert.strictEqual(value.outputFiles[0].text, `(() => { 3465 // .yarn/cache/left-pad-zip/node_modules/left-pad/index.js 3466 function left_pad_default() { 3467 } 3468 3469 // entry.js 3470 console.log(left_pad_default()); 3471 })(); 3472 `) 3473 }, 3474 3475 async yarnPnP_ignoreNestedManifests({ esbuild, testDir }) { 3476 const entry = path.join(testDir, 'entry.js') 3477 const foo = path.join(testDir, 'node_modules', 'foo', 'index.js') 3478 const bar = path.join(testDir, 'node_modules', 'bar', 'index.js') 3479 const manifest = path.join(testDir, '.pnp.data.json') 3480 3481 await writeFileAsync(entry, ` 3482 import foo from 'foo' 3483 console.log(foo) 3484 `) 3485 3486 await mkdirAsync(path.dirname(foo), { recursive: true }) 3487 await writeFileAsync(foo, ` 3488 import bar from 'bar' 3489 export default 'foo' + bar 3490 `) 3491 3492 await mkdirAsync(path.dirname(bar), { recursive: true }) 3493 await writeFileAsync(bar, ` 3494 export default 'bar' 3495 `) 3496 3497 await writeFileAsync(manifest, `{ 3498 "packageRegistryData": [ 3499 [null, [ 3500 [null, { 3501 "packageLocation": "./", 3502 "packageDependencies": [ 3503 ["foo", "npm:1.0.0"], 3504 ["bar", "npm:1.0.0"] 3505 ], 3506 "linkType": "SOFT" 3507 }] 3508 ]], 3509 ["foo", [ 3510 ["npm:1.0.0", { 3511 "packageLocation": "./__virtual__/whatever/0/node_modules/foo/", 3512 "packageDependencies": [ 3513 ["bar", "npm:1.0.0"] 3514 ], 3515 "linkType": "HARD" 3516 }] 3517 ]], 3518 ["bar", [ 3519 ["npm:1.0.0", { 3520 "packageLocation": "./__virtual__/whatever/0/node_modules/bar/", 3521 "packageDependencies": [], 3522 "linkType": "HARD" 3523 }] 3524 ]] 3525 ] 3526 }`) 3527 3528 const value = await esbuild.build({ 3529 entryPoints: [entry], 3530 bundle: true, 3531 write: false, 3532 absWorkingDir: testDir, 3533 }) 3534 3535 assert.strictEqual(value.outputFiles.length, 1) 3536 assert.strictEqual(value.outputFiles[0].text, `(() => { 3537 // __virtual__/whatever/0/node_modules/bar/index.js 3538 var bar_default = "bar"; 3539 3540 // __virtual__/whatever/0/node_modules/foo/index.js 3541 var foo_default = "foo" + bar_default; 3542 3543 // entry.js 3544 console.log(foo_default); 3545 })(); 3546 `) 3547 }, 3548 3549 async yarnPnP_tsconfig({ esbuild, testDir }) { 3550 const entry = path.join(testDir, 'entry.jsx') 3551 const tsconfigExtends = path.join(testDir, 'tsconfig.json') 3552 const tsconfigBase = path.join(testDir, 'foo', 'tsconfig.json') 3553 const manifest = path.join(testDir, '.pnp.data.json') 3554 3555 await writeFileAsync(entry, ` 3556 console.log(<div/>) 3557 `) 3558 3559 await writeFileAsync(tsconfigExtends, `{ 3560 "extends": "@scope/base/tsconfig.json", 3561 }`) 3562 3563 await mkdirAsync(path.dirname(tsconfigBase), { recursive: true }) 3564 await writeFileAsync(tsconfigBase, `{ 3565 "compilerOptions": { 3566 "jsxFactory": "success" 3567 } 3568 }`) 3569 3570 await writeFileAsync(manifest, `{ 3571 "packageRegistryData": [ 3572 [null, [ 3573 [null, { 3574 "packageLocation": "./", 3575 "packageDependencies": [ 3576 ["@scope/base", "npm:1.0.0"] 3577 ], 3578 "linkType": "SOFT" 3579 }] 3580 ]], 3581 ["@scope/base", [ 3582 ["npm:1.0.0", { 3583 "packageLocation": "./foo/", 3584 "packageDependencies": [], 3585 "linkType": "HARD" 3586 }] 3587 ]] 3588 ] 3589 }`) 3590 3591 const value = await esbuild.build({ 3592 entryPoints: [entry], 3593 bundle: true, 3594 write: false, 3595 absWorkingDir: testDir, 3596 }) 3597 3598 assert.strictEqual(value.outputFiles.length, 1) 3599 assert.strictEqual(value.outputFiles[0].text, `(() => { 3600 // entry.jsx 3601 console.log(/* @__PURE__ */ success("div", null)); 3602 })(); 3603 `) 3604 }, 3605 3606 async yarnPnP_indexJs({ esbuild, testDir }) { 3607 const entry = path.join(testDir, 'entry.js') 3608 const fooIndex = path.join(testDir, 'node_modules', '@some', 'pkg', 'index.js') 3609 const fooFoo = path.join(testDir, 'node_modules', '@some', 'pkg', 'foo.js') 3610 const manifest = path.join(testDir, '.pnp.data.json') 3611 3612 await writeFileAsync(entry, ` 3613 import x from '@some/pkg' 3614 x() 3615 `) 3616 3617 await mkdirAsync(path.dirname(fooIndex), { recursive: true }) 3618 await writeFileAsync(fooIndex, `export default success`) 3619 3620 await mkdirAsync(path.dirname(fooFoo), { recursive: true }) 3621 await writeFileAsync(fooFoo, `failure!`) 3622 3623 await writeFileAsync(manifest, `{ 3624 "packageRegistryData": [ 3625 [null, [ 3626 [null, { 3627 "packageLocation": "./", 3628 "packageDependencies": [ 3629 ["@some/pkg", "npm:1.0.0"] 3630 ], 3631 "linkType": "SOFT" 3632 }] 3633 ]], 3634 ["@some/pkg", [ 3635 ["npm:1.0.0", { 3636 "packageLocation": "./node_modules/@some/pkg/", 3637 "packageDependencies": [], 3638 "linkType": "HARD" 3639 }] 3640 ]] 3641 ] 3642 }`) 3643 3644 const value = await esbuild.build({ 3645 entryPoints: [entry], 3646 bundle: true, 3647 write: false, 3648 absWorkingDir: testDir, 3649 }) 3650 3651 assert.strictEqual(value.outputFiles.length, 1) 3652 assert.strictEqual(value.outputFiles[0].text, `(() => { 3653 // node_modules/@some/pkg/index.js 3654 var pkg_default = success; 3655 3656 // entry.js 3657 pkg_default(); 3658 })(); 3659 `) 3660 }, 3661 3662 async yarnPnP_depOfVirtual({ esbuild, testDir }) { 3663 const entry = path.join(testDir, 'entry.js') 3664 const pkg = path.join(testDir, '.yarn', 'cache', 'pkg-zip', 'node_modules', 'pkg', 'index.js') 3665 const dep = path.join(testDir, '.yarn', 'cache', 'dep-zip', 'node_modules', 'dep', 'index.js') 3666 const manifest = path.join(testDir, '.pnp.data.json') 3667 3668 await writeFileAsync(entry, `import 'pkg'`) 3669 3670 await mkdirAsync(path.dirname(pkg), { recursive: true }) 3671 await writeFileAsync(pkg, `import 'dep'`) 3672 3673 await mkdirAsync(path.dirname(dep), { recursive: true }) 3674 await writeFileAsync(dep, `success()`) 3675 3676 await writeFileAsync(manifest, `{ 3677 "packageRegistryData": [ 3678 [null, [ 3679 [null, { 3680 "packageLocation": "./", 3681 "packageDependencies": [ 3682 ["pkg", "virtual:some-path"] 3683 ], 3684 "linkType": "SOFT" 3685 }] 3686 ]], 3687 ["demo", [ 3688 ["workspace:.", { 3689 "packageLocation": "./", 3690 "packageDependencies": [ 3691 ["demo", "workspace:."], 3692 ["pkg", "virtual:some-path"] 3693 ], 3694 "linkType": "SOFT" 3695 }] 3696 ]], 3697 ["pkg", [ 3698 ["npm:1.0.0", { 3699 "packageLocation": "./.yarn/cache/pkg-zip/node_modules/pkg/", 3700 "packageDependencies": [ 3701 ["pkg", "npm:1.0.0"] 3702 ], 3703 "linkType": "SOFT" 3704 }], 3705 ["virtual:some-path", { 3706 "packageLocation": "./.yarn/__virtual__/pkg-virtual/0/cache/pkg-zip/node_modules/pkg/", 3707 "packageDependencies": [ 3708 ["pkg", "virtual:some-path"], 3709 ["dep", "npm:1.0.0"] 3710 ], 3711 "linkType": "HARD" 3712 }] 3713 ]], 3714 ["dep", [ 3715 ["npm:1.0.0", { 3716 "packageLocation": "./.yarn/cache/dep-zip/node_modules/dep/", 3717 "packageDependencies": [ 3718 ["dep", "npm:1.0.0"] 3719 ], 3720 "linkType": "HARD" 3721 }] 3722 ]] 3723 ] 3724 }`) 3725 3726 const value = await esbuild.build({ 3727 entryPoints: [entry], 3728 bundle: true, 3729 write: false, 3730 absWorkingDir: testDir, 3731 }) 3732 3733 assert.strictEqual(value.outputFiles.length, 1) 3734 assert.strictEqual(value.outputFiles[0].text, `(() => { 3735 // .yarn/cache/dep-zip/node_modules/dep/index.js 3736 success(); 3737 })(); 3738 `) 3739 }, 3740 } 3741 3742 function fetch(host, port, path, { headers, method = 'GET' } = {}) { 3743 return new Promise((resolve, reject) => { 3744 http.request({ method, host, port, path, headers }, res => { 3745 const chunks = [] 3746 res.on('data', chunk => chunks.push(chunk)) 3747 res.on('end', () => { 3748 const content = Buffer.concat(chunks) 3749 if (res.statusCode < 200 || res.statusCode > 299) { 3750 const error = new Error(`${res.statusCode} when fetching "${path}": ${content}`) 3751 error.statusCode = res.statusCode 3752 reject(error) 3753 } else { 3754 content.headers = res.headers 3755 resolve(content) 3756 } 3757 }) 3758 }).on('error', reject).end() 3759 }) 3760 } 3761 3762 function fetchHTTPS(host, port, path, { certfile }) { 3763 return new Promise((resolve, reject) => { 3764 const checkServerIdentity = (hostname, cert) => { 3765 // I'm not sure why node seems to always reject the host "127.0.0.1" 3766 assert.strictEqual(hostname, '127.0.0.1') 3767 assert.strictEqual(cert.subject.CN, '127.0.0.1') 3768 } 3769 const ca = [fs.readFileSync(certfile, 'utf8')] 3770 https.get({ host, port, path, ca, checkServerIdentity }, res => { 3771 const chunks = [] 3772 res.on('data', chunk => chunks.push(chunk)) 3773 res.on('end', () => { 3774 const content = Buffer.concat(chunks) 3775 if (res.statusCode < 200 || res.statusCode > 299) { 3776 const error = new Error(`${res.statusCode} when fetching "${path}": ${content}`) 3777 error.statusCode = res.statusCode 3778 reject(error) 3779 } else { 3780 content.headers = res.headers 3781 resolve(content) 3782 } 3783 }) 3784 }).on('error', reject) 3785 }) 3786 } 3787 3788 function partialFetch(host, port, path, { headers, method = 'GET' } = {}) { 3789 return new Promise((resolve, reject) => { 3790 http.request({ method, host, port, path, headers }, res => { 3791 resolve(res) 3792 res.socket.destroy() 3793 }).on('error', reject).end() 3794 }) 3795 } 3796 3797 const makeEventStream = (host, port, path) => { 3798 return new Promise((resolve, reject) => { 3799 const headers = { 3800 'Accept': 'text/event-stream', 3801 } 3802 3803 http.get({ host, port, path, headers }, res => { 3804 if (res.statusCode !== 200) { 3805 reject(new Error(`${res.statusCode} when fetching "${path}"`)) 3806 return 3807 } 3808 3809 const stream = new events.EventEmitter 3810 let buffer = '' 3811 3812 res.on('data', chunk => { 3813 buffer += chunk 3814 while (true) { 3815 const index = buffer.indexOf('\n\n') 3816 if (index < 0) break 3817 3818 const lines = buffer.slice(0, index).split('\n') 3819 const fields = Object.create(null) 3820 buffer = buffer.slice(index + 2) 3821 3822 for (const line of lines) { 3823 const colon = line.indexOf(':') 3824 if (colon >= 0) { 3825 const key = line.slice(0, colon).trim() 3826 const value = line.slice(colon + 1).trim() 3827 fields[key] = value 3828 } 3829 } 3830 3831 if ('data' in fields) { 3832 stream.emit('event' in fields ? fields.event : 'message', fields) 3833 } 3834 } 3835 }) 3836 3837 res.on('close', () => stream.emit('close')) 3838 3839 stream.destroy = () => res.destroy() 3840 3841 stream.waitFor = name => new Promise((resolve, reject) => { 3842 const timeout = setTimeout(() => { 3843 reject(new Error('Timeout after 30 seconds')) 3844 stream.destroy() 3845 }, 30 * 1000) 3846 3847 stream.once(name, value => { 3848 clearTimeout(timeout) 3849 resolve(value) 3850 }) 3851 }) 3852 3853 resolve(stream) 3854 }).on('error', reject) 3855 }) 3856 } 3857 3858 const makeRebuildUntilPlugin = () => { 3859 let onEnd 3860 3861 return { 3862 rebuildUntil: (mutator, condition) => new Promise((resolve, reject) => { 3863 let timeout = setTimeout(() => reject(new Error('Timeout after 30 seconds')), 30 * 1000) 3864 onEnd = result => { 3865 try { if (result && condition(result)) clearTimeout(timeout), resolve(result) } 3866 catch (e) { clearTimeout(timeout), reject(e) } 3867 } 3868 mutator() 3869 }), 3870 3871 plugin: { 3872 name: 'rebuildUntil', 3873 setup(build) { 3874 build.onEnd(result => onEnd(result)) 3875 }, 3876 }, 3877 } 3878 } 3879 3880 let watchTests = { 3881 async watchTwice({ esbuild }) { 3882 const context = await esbuild.context({}) 3883 try { 3884 // Watch once 3885 await context.watch() 3886 3887 // Watch twice 3888 try { 3889 await context.watch() 3890 throw new Error('Expected an error to be thrown') 3891 } catch (err) { 3892 assert.strictEqual(err.message, 'Watch mode has already been enabled') 3893 } 3894 } finally { 3895 await context.dispose() 3896 } 3897 }, 3898 3899 async watchEditSession({ esbuild, testDir }) { 3900 const srcDir = path.join(testDir, 'src') 3901 const outfile = path.join(testDir, 'out.js') 3902 const input = path.join(srcDir, 'in.js') 3903 await mkdirAsync(srcDir, { recursive: true }) 3904 await writeFileAsync(input, `throw 1`) 3905 3906 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 3907 const context = await esbuild.context({ 3908 entryPoints: [input], 3909 outfile, 3910 format: 'esm', 3911 logLevel: 'silent', 3912 plugins: [plugin], 3913 }) 3914 3915 try { 3916 const result = await rebuildUntil( 3917 () => context.watch(), 3918 () => true, 3919 ) 3920 assert.strictEqual(result.outputFiles, void 0) 3921 assert.strictEqual(result.errors.length, 0) 3922 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 1;\n') 3923 3924 // First rebuild: edit 3925 { 3926 const result2 = await rebuildUntil( 3927 () => writeFileAtomic(input, `throw 2`), 3928 () => fs.readFileSync(outfile, 'utf8') === 'throw 2;\n', 3929 ) 3930 assert.strictEqual(result2.outputFiles, void 0) 3931 assert.strictEqual(result2.errors.length, 0) 3932 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 2;\n') 3933 } 3934 3935 // Second rebuild: edit 3936 { 3937 const result2 = await rebuildUntil( 3938 () => writeFileAtomic(input, `throw 3`), 3939 () => fs.readFileSync(outfile, 'utf8') === 'throw 3;\n', 3940 ) 3941 assert.strictEqual(result2.outputFiles, void 0) 3942 assert.strictEqual(result2.errors.length, 0) 3943 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 3;\n') 3944 } 3945 3946 // Third rebuild: syntax error 3947 { 3948 const result2 = await rebuildUntil( 3949 () => writeFileAtomic(input, `throw 1 2`), 3950 result => result.errors.length > 0, 3951 ) 3952 assert.strictEqual(result2.errors.length, 1) 3953 assert.strictEqual(result2.errors[0].text, 'Expected ";" but found "2"') 3954 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 3;\n') 3955 } 3956 3957 // Fourth rebuild: edit 3958 { 3959 const result2 = await rebuildUntil( 3960 () => writeFileAtomic(input, `throw 4`), 3961 () => fs.readFileSync(outfile, 'utf8') === 'throw 4;\n', 3962 ) 3963 assert.strictEqual(result2.outputFiles, void 0) 3964 assert.strictEqual(result2.errors.length, 0) 3965 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 4;\n') 3966 } 3967 3968 // Fifth rebuild: delete 3969 { 3970 const result2 = await rebuildUntil( 3971 () => fs.promises.unlink(input), 3972 result => result.errors.length > 0, 3973 ) 3974 assert.strictEqual(result2.errors.length, 1) 3975 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 4;\n') 3976 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 4;\n') 3977 } 3978 3979 // Sixth rebuild: restore 3980 { 3981 const result2 = await rebuildUntil( 3982 () => writeFileAtomic(input, `throw 5`), 3983 () => fs.readFileSync(outfile, 'utf8') === 'throw 5;\n', 3984 ) 3985 assert.strictEqual(result2.outputFiles, void 0) 3986 assert.strictEqual(result2.errors.length, 0) 3987 assert.strictEqual(await readFileAsync(outfile, 'utf8'), 'throw 5;\n') 3988 } 3989 } finally { 3990 await context.dispose() 3991 } 3992 }, 3993 3994 async watchWriteFalse({ esbuild, testDir }) { 3995 const srcDir = path.join(testDir, 'src') 3996 const outdir = path.join(testDir, 'out') 3997 const input = path.join(srcDir, 'in.js') 3998 const output = path.join(outdir, 'in.js') 3999 await mkdirAsync(srcDir, { recursive: true }) 4000 await writeFileAsync(input, `throw 1`) 4001 4002 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 4003 const context = await esbuild.context({ 4004 entryPoints: [input], 4005 outdir, 4006 format: 'esm', 4007 logLevel: 'silent', 4008 write: false, 4009 plugins: [plugin], 4010 }) 4011 4012 try { 4013 const result = await rebuildUntil( 4014 () => context.watch(), 4015 () => true, 4016 ) 4017 assert.strictEqual(result.errors.length, 0) 4018 assert.strictEqual(result.outputFiles[0].text, 'throw 1;\n') 4019 assert.strictEqual(fs.existsSync(output), false) 4020 4021 // First rebuild: edit 4022 { 4023 const result2 = await rebuildUntil( 4024 () => writeFileAtomic(input, `throw 2`), 4025 res => res.outputFiles[0].text === 'throw 2;\n', 4026 ) 4027 assert.strictEqual(result2.errors.length, 0) 4028 assert.strictEqual(fs.existsSync(output), false) 4029 } 4030 4031 // Second rebuild: edit 4032 { 4033 const result2 = await rebuildUntil( 4034 () => writeFileAtomic(input, `throw 3`), 4035 res => res.outputFiles[0].text === 'throw 3;\n', 4036 ) 4037 assert.strictEqual(result2.errors.length, 0) 4038 assert.strictEqual(fs.existsSync(output), false) 4039 } 4040 } finally { 4041 await context.dispose() 4042 } 4043 }, 4044 4045 async watchMetafile({ esbuild, testDir }) { 4046 const srcDir = path.join(testDir, 'src') 4047 const outdir = path.join(testDir, 'out') 4048 const input = path.join(srcDir, 'in.js') 4049 const output = path.join(outdir, 'in.js') 4050 await mkdirAsync(srcDir, { recursive: true }) 4051 await writeFileAsync(input, `foo()`) 4052 4053 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 4054 const context = await esbuild.context({ 4055 entryPoints: [input], 4056 outdir, 4057 format: 'esm', 4058 logLevel: 'silent', 4059 metafile: true, 4060 plugins: [plugin], 4061 }) 4062 4063 try { 4064 const result = await rebuildUntil( 4065 () => context.watch(), 4066 () => true, 4067 ) 4068 assert.strictEqual(result.errors.length, 0) 4069 const relInput = path.relative(process.cwd(), input).split(path.sep).join('/') 4070 assert.strictEqual(result.metafile.inputs[relInput].bytes, 5) 4071 assert.strictEqual(await readFileAsync(output, 'utf8'), 'foo();\n') 4072 4073 // Rebuild and check that the metafile has been updated 4074 { 4075 const result2 = await rebuildUntil( 4076 () => writeFileAtomic(input, `foo(123)`), 4077 () => fs.readFileSync(output, 'utf8') === 'foo(123);\n', 4078 ) 4079 assert.strictEqual(result2.errors.length, 0) 4080 assert.strictEqual(result2.metafile.inputs[relInput].bytes, 8) 4081 } 4082 } finally { 4083 await context.dispose() 4084 } 4085 }, 4086 4087 async watchMangleCache({ esbuild, testDir }) { 4088 const srcDir = path.join(testDir, 'src') 4089 const outdir = path.join(testDir, 'out') 4090 const input = path.join(srcDir, 'in.js') 4091 const output = path.join(outdir, 'in.js') 4092 await mkdirAsync(srcDir, { recursive: true }) 4093 await writeFileAsync(input, `foo()`) 4094 4095 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 4096 const context = await esbuild.context({ 4097 entryPoints: [input], 4098 outdir, 4099 format: 'esm', 4100 logLevel: 'silent', 4101 mangleProps: /./, 4102 mangleCache: {}, 4103 plugins: [plugin], 4104 }) 4105 4106 try { 4107 const result = await rebuildUntil( 4108 () => context.watch(), 4109 () => true, 4110 ) 4111 assert.strictEqual(result.errors.length, 0) 4112 assert.strictEqual(JSON.stringify(result.mangleCache), '{}') 4113 4114 // Rebuild and check that the mangle cache has been updated 4115 { 4116 const result2 = await rebuildUntil( 4117 () => writeFileAtomic(input, `foo(bar.baz)`), 4118 () => fs.readFileSync(output, 'utf8') === 'foo(bar.a);\n', 4119 ) 4120 assert.strictEqual(result2.errors.length, 0) 4121 assert.strictEqual(JSON.stringify(result2.mangleCache), '{"baz":"a"}') 4122 } 4123 } finally { 4124 await context.dispose() 4125 } 4126 }, 4127 4128 // See: https://github.com/evanw/esbuild/issues/3062 4129 async watchNodePaths({ esbuild, testDir }) { 4130 const input = path.join(testDir, 'in.js') 4131 const outfile = path.join(testDir, 'out.js') 4132 const libDir = path.join(testDir, 'lib') 4133 const libFile = path.join(libDir, 'foo.js') 4134 await mkdirAsync(libDir, { recursive: true }) 4135 await writeFileAsync(input, ` 4136 import { foo } from ${JSON.stringify(path.basename(libFile))} 4137 console.log(foo) 4138 `) 4139 4140 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 4141 const context = await esbuild.context({ 4142 entryPoints: [input], 4143 outfile, 4144 write: false, 4145 bundle: true, 4146 minifyWhitespace: true, 4147 format: 'esm', 4148 logLevel: 'silent', 4149 plugins: [plugin], 4150 nodePaths: [libDir], 4151 }) 4152 4153 try { 4154 const result = await rebuildUntil( 4155 () => { 4156 context.watch() 4157 writeFileAtomic(libFile, `export let foo = 0`) 4158 }, 4159 result => result.outputFiles.length === 1, 4160 ) 4161 assert.strictEqual(result.outputFiles[0].text, `var foo=0;console.log(foo);\n`) 4162 4163 // Make sure watch mode works for files imported via NODE_PATH 4164 for (let i = 1; i <= 3; i++) { 4165 const result2 = await rebuildUntil( 4166 () => writeFileAtomic(libFile, `export let foo = ${i}`), 4167 result => result.outputFiles.length === 1 && result.outputFiles[0].text.includes(i), 4168 ) 4169 assert.strictEqual(result2.outputFiles[0].text, `var foo=${i};console.log(foo);\n`) 4170 } 4171 } finally { 4172 await context.dispose() 4173 } 4174 }, 4175 } 4176 4177 let serveTests = { 4178 async serveTwice({ esbuild }) { 4179 const context = await esbuild.context({}) 4180 try { 4181 // Serve once 4182 await context.serve() 4183 4184 // Serve twice 4185 try { 4186 await context.serve() 4187 throw new Error('Expected an error to be thrown') 4188 } catch (err) { 4189 assert.strictEqual(err.message, 'Serve mode has already been enabled') 4190 } 4191 } finally { 4192 await context.dispose() 4193 } 4194 }, 4195 4196 async serveWatch({ esbuild, testDir }) { 4197 const input = path.join(testDir, 'in.ts') 4198 const outfile = path.join(testDir, 'out.js') 4199 4200 const { rebuildUntil, plugin } = makeRebuildUntilPlugin() 4201 const context = await esbuild.context({ 4202 entryPoints: [input], 4203 outfile, 4204 logLevel: 'silent', 4205 plugins: [plugin], 4206 }) 4207 4208 try { 4209 await context.watch() 4210 const server = await context.serve({ 4211 host: '127.0.0.1', 4212 }) 4213 4214 // Try fetching the non-existent file 4215 try { 4216 await fetch(server.host, server.port, '/' + path.basename(outfile)) 4217 throw new Error('Expected an error to be thrown') 4218 } catch (err) { 4219 if (err.statusCode !== 503) throw err 4220 } 4221 4222 // Check that watch mode works 4223 for (let i = 0; i < 5; i++) { 4224 const result = await rebuildUntil( 4225 () => writeFileAtomic(input, `throw ${i}`), 4226 () => fs.readFileSync(outfile, 'utf8') === `throw ${i};\n`, 4227 ) 4228 assert.strictEqual(result.errors.length, 0) 4229 assert.strictEqual(fs.readFileSync(outfile, 'utf8'), `throw ${i};\n`) 4230 } 4231 4232 // Try fetching the file now 4233 const data = await fetch(server.host, server.port, '/' + path.basename(outfile)) 4234 assert.strictEqual(data.toString(), 'throw 4;\n') 4235 } finally { 4236 await context.dispose() 4237 } 4238 }, 4239 4240 async serveBasic({ esbuild, testDir }) { 4241 const input = path.join(testDir, 'in.js') 4242 await writeFileAsync(input, `console.log(123)`) 4243 4244 let onRequest; 4245 4246 const context = await esbuild.context({ 4247 entryPoints: [input], 4248 format: 'esm', 4249 outdir: testDir, 4250 write: false, 4251 }); 4252 try { 4253 const result = await context.serve({ 4254 host: '127.0.0.1', 4255 onRequest: args => onRequest(args), 4256 }) 4257 assert.strictEqual(result.host, '127.0.0.1'); 4258 assert.strictEqual(typeof result.port, 'number'); 4259 4260 // GET /in.js 4261 { 4262 const singleRequestPromise = new Promise(resolve => { onRequest = resolve }); 4263 const buffer = await fetch(result.host, result.port, '/in.js') 4264 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 4265 assert.strictEqual(fs.readFileSync(input, 'utf8'), `console.log(123)`) 4266 4267 let singleRequest = await singleRequestPromise; 4268 assert.strictEqual(singleRequest.method, 'GET'); 4269 assert.strictEqual(singleRequest.path, '/in.js'); 4270 assert.strictEqual(singleRequest.status, 200); 4271 assert.strictEqual(typeof singleRequest.remoteAddress, 'string'); 4272 assert.strictEqual(typeof singleRequest.timeInMS, 'number'); 4273 } 4274 4275 // HEAD /in.js 4276 { 4277 const singleRequestPromise = new Promise(resolve => { onRequest = resolve }); 4278 const buffer = await fetch(result.host, result.port, '/in.js', { method: 'HEAD' }) 4279 assert.strictEqual(buffer.toString(), ``); // HEAD omits the content 4280 assert.strictEqual(fs.readFileSync(input, 'utf8'), `console.log(123)`) 4281 4282 let singleRequest = await singleRequestPromise; 4283 assert.strictEqual(singleRequest.method, 'HEAD'); 4284 assert.strictEqual(singleRequest.path, '/in.js'); 4285 assert.strictEqual(singleRequest.status, 200); 4286 assert.strictEqual(typeof singleRequest.remoteAddress, 'string'); 4287 assert.strictEqual(typeof singleRequest.timeInMS, 'number'); 4288 } 4289 } finally { 4290 await context.dispose(); 4291 } 4292 }, 4293 4294 async serveBasicHTTPS({ esbuild, testDir }) { 4295 const run = command => new Promise((resolve, reject) => { 4296 child_process.execFile(command.shift(), command, (error, stdout, stderr) => { 4297 if (error) reject(error) 4298 else resolve() 4299 }) 4300 }) 4301 4302 try { 4303 await run(['which', 'openssl']) 4304 } catch { 4305 console.warn('Skipping HTTPS tests because the "openssl" command was not found') 4306 return 4307 } 4308 4309 const keyfile = path.join(testDir, 'key.pem') 4310 const certfile = path.join(testDir, 'cert.pem') 4311 await run(['openssl', 'req', '-x509', '-newkey', 'rsa:4096', '-keyout', keyfile, '-out', certfile, '-days', '9999', '-nodes', '-subj', '/CN=127.0.0.1']) 4312 4313 const input = path.join(testDir, 'in.js') 4314 await writeFileAsync(input, `console.log(123)`) 4315 4316 let onRequest; 4317 let singleRequestPromise = new Promise(resolve => { 4318 onRequest = resolve; 4319 }); 4320 4321 const context = await esbuild.context({ 4322 entryPoints: [input], 4323 format: 'esm', 4324 outdir: testDir, 4325 write: false, 4326 }); 4327 try { 4328 const result = await context.serve({ 4329 host: '127.0.0.1', 4330 keyfile, 4331 certfile, 4332 onRequest, 4333 }) 4334 assert.strictEqual(result.host, '127.0.0.1'); 4335 assert.strictEqual(typeof result.port, 'number'); 4336 4337 const buffer = await fetchHTTPS(result.host, result.port, '/in.js', { certfile }) 4338 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 4339 assert.strictEqual(fs.readFileSync(input, 'utf8'), `console.log(123)`) 4340 4341 let singleRequest = await singleRequestPromise; 4342 assert.strictEqual(singleRequest.method, 'GET'); 4343 assert.strictEqual(singleRequest.path, '/in.js'); 4344 assert.strictEqual(singleRequest.status, 200); 4345 assert.strictEqual(typeof singleRequest.remoteAddress, 'string'); 4346 assert.strictEqual(typeof singleRequest.timeInMS, 'number'); 4347 } finally { 4348 await context.dispose(); 4349 } 4350 }, 4351 4352 async serveSlashRedirect({ esbuild, testDir }) { 4353 const nestedDir = path.join(testDir, 'nested', 'dir') 4354 const index = path.join(nestedDir, 'index.html') 4355 await mkdirAsync(nestedDir, { recursive: true }) 4356 await writeFileAsync(index, `<!doctype html>`) 4357 4358 const context = await esbuild.context({}); 4359 try { 4360 const result = await context.serve({ 4361 host: '127.0.0.1', 4362 servedir: testDir, 4363 }) 4364 assert.strictEqual(result.host, '127.0.0.1'); 4365 assert.strictEqual(typeof result.port, 'number'); 4366 4367 // With a trailing slash 4368 { 4369 const buffer = await fetch(result.host, result.port, '/nested/dir/index.html') 4370 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4371 } 4372 { 4373 const buffer = await fetch(result.host, result.port, '/nested/dir/') 4374 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4375 } 4376 { 4377 const buffer = await fetch(result.host, result.port, '/nested/./dir/') 4378 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4379 } 4380 { 4381 const buffer = await fetch(result.host, result.port, '/./nested/./dir/./') 4382 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4383 } 4384 { 4385 const buffer = await fetch(result.host, result.port, '/nested/dir//') 4386 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4387 } 4388 { 4389 const buffer = await fetch(result.host, result.port, '/nested//dir/') 4390 assert.strictEqual(buffer.toString(), `<!doctype html>`) 4391 } 4392 4393 // Without a trailing slash 4394 { 4395 const res = await partialFetch(result.host, result.port, '/nested') 4396 assert.strictEqual(res.statusCode, 302) 4397 assert.strictEqual(res.headers.location, '/nested/') 4398 } 4399 { 4400 const res = await partialFetch(result.host, result.port, '/nested/dir') 4401 assert.strictEqual(res.statusCode, 302) 4402 assert.strictEqual(res.headers.location, '/nested/dir/') 4403 } 4404 { 4405 const res = await partialFetch(result.host, result.port, '/nested//dir') 4406 assert.strictEqual(res.statusCode, 302) 4407 assert.strictEqual(res.headers.location, '/nested/dir/') 4408 } 4409 4410 // With leading double slashes (looks like a protocol-relative URL) 4411 { 4412 const res = await partialFetch(result.host, result.port, '//nested') 4413 assert.strictEqual(res.statusCode, 302) 4414 assert.strictEqual(res.headers.location, '/nested/') 4415 } 4416 { 4417 const res = await partialFetch(result.host, result.port, '//nested/dir') 4418 assert.strictEqual(res.statusCode, 302) 4419 assert.strictEqual(res.headers.location, '/nested/dir/') 4420 } 4421 { 4422 const res = await partialFetch(result.host, result.port, '//nested//dir') 4423 assert.strictEqual(res.statusCode, 302) 4424 assert.strictEqual(res.headers.location, '/nested/dir/') 4425 } 4426 } finally { 4427 await context.dispose(); 4428 } 4429 }, 4430 4431 async serveOutfile({ esbuild, testDir }) { 4432 const input = path.join(testDir, 'in.js') 4433 const outfile = path.join(testDir, 'out.js') 4434 await writeFileAsync(input, `console.log(123)`) 4435 4436 let onRequest; 4437 let singleRequestPromise = new Promise(resolve => { 4438 onRequest = resolve; 4439 }); 4440 4441 const context = await esbuild.context({ 4442 entryPoints: [input], 4443 format: 'esm', 4444 outfile, 4445 }); 4446 try { 4447 const result = await context.serve({ 4448 host: '127.0.0.1', 4449 onRequest, 4450 }) 4451 assert.strictEqual(result.host, '127.0.0.1'); 4452 assert.strictEqual(typeof result.port, 'number'); 4453 4454 const buffer = await fetch(result.host, result.port, '/out.js') 4455 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 4456 4457 let singleRequest = await singleRequestPromise; 4458 assert.strictEqual(singleRequest.method, 'GET'); 4459 assert.strictEqual(singleRequest.path, '/out.js'); 4460 assert.strictEqual(singleRequest.status, 200); 4461 assert.strictEqual(typeof singleRequest.remoteAddress, 'string'); 4462 assert.strictEqual(typeof singleRequest.timeInMS, 'number'); 4463 4464 try { 4465 await fetch(result.host, result.port, '/in.js') 4466 throw new Error('Expected a 404 error for "/in.js"') 4467 } catch (err) { 4468 if (err.message !== '404 when fetching "/in.js": 404 - Not Found') 4469 throw err 4470 } 4471 } finally { 4472 await context.dispose(); 4473 } 4474 }, 4475 4476 async serveWithServedir({ esbuild, testDir }) { 4477 const input = path.join(testDir, 'in.js') 4478 const wwwDir = path.join(testDir, 'www') 4479 const index = path.join(wwwDir, 'index.html') 4480 await mkdirAsync(wwwDir, { recursive: true }) 4481 await writeFileAsync(input, `console.log(123)`) 4482 await writeFileAsync(index, `<!doctype html>`) 4483 4484 let onRequest; 4485 let nextRequestPromise; 4486 4487 (function generateNewPromise() { 4488 nextRequestPromise = new Promise(resolve => { 4489 onRequest = args => { 4490 generateNewPromise(); 4491 resolve(args); 4492 }; 4493 }); 4494 })(); 4495 4496 const context = await esbuild.context({ 4497 entryPoints: [input], 4498 format: 'esm', 4499 outdir: wwwDir, 4500 write: false, 4501 }); 4502 try { 4503 const result = await context.serve({ 4504 host: '127.0.0.1', 4505 onRequest: args => onRequest(args), 4506 servedir: wwwDir, 4507 }) 4508 assert.strictEqual(result.host, '127.0.0.1'); 4509 assert.strictEqual(typeof result.port, 'number'); 4510 4511 let promise, buffer, req; 4512 4513 promise = nextRequestPromise; 4514 buffer = await fetch(result.host, result.port, '/in.js') 4515 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 4516 assert.strictEqual(fs.existsSync(path.join(wwwDir, path.basename(input))), false); 4517 req = await promise; 4518 assert.strictEqual(req.method, 'GET'); 4519 assert.strictEqual(req.path, '/in.js'); 4520 assert.strictEqual(req.status, 200); 4521 assert.strictEqual(typeof req.remoteAddress, 'string'); 4522 assert.strictEqual(typeof req.timeInMS, 'number'); 4523 4524 promise = nextRequestPromise; 4525 buffer = await fetch(result.host, result.port, '/') 4526 assert.strictEqual(buffer.toString(), `<!doctype html>`); 4527 req = await promise; 4528 assert.strictEqual(req.method, 'GET'); 4529 assert.strictEqual(req.path, '/'); 4530 assert.strictEqual(req.status, 200); 4531 assert.strictEqual(typeof req.remoteAddress, 'string'); 4532 assert.strictEqual(typeof req.timeInMS, 'number'); 4533 } finally { 4534 await context.dispose(); 4535 } 4536 }, 4537 4538 async serveWithServedirAndSiblingOutputDir({ esbuild, testDir }) { 4539 const context = await esbuild.context({ 4540 entryPoints: [path.join(testDir, 'in.js')], 4541 outdir: 'out', 4542 }); 4543 try { 4544 await context.serve({ 4545 servedir: 'www', 4546 }); 4547 throw new Error('Expected an error to be thrown') 4548 } catch (e) { 4549 assert.strictEqual(e.message, `Output directory "out" must be contained in serve directory "www"`) 4550 } finally { 4551 await context.dispose(); 4552 } 4553 }, 4554 4555 async serveWithServedirAndParentOutputDir({ esbuild, testDir }) { 4556 const context = await esbuild.context({ 4557 entryPoints: [path.join(testDir, 'in.js')], 4558 outdir: testDir, 4559 absWorkingDir: testDir, 4560 }); 4561 try { 4562 await context.serve({ 4563 servedir: path.join(testDir, 'www'), 4564 }) 4565 throw new Error('Expected an error to be thrown') 4566 } catch (e) { 4567 assert.strictEqual(e.message, `Output directory "." must be contained in serve directory "www"`) 4568 } finally { 4569 await context.dispose(); 4570 } 4571 }, 4572 4573 async serveWithServedirAndOutputDir({ esbuild, testDir }) { 4574 const input = path.join(testDir, 'in.js') 4575 const outputDir = path.join(testDir, 'www/out') 4576 const wwwDir = path.join(testDir, 'www') 4577 const index = path.join(wwwDir, 'index.html') 4578 await mkdirAsync(wwwDir, { recursive: true }) 4579 await writeFileAsync(input, `console.log(123)`) 4580 await writeFileAsync(index, `<!doctype html>`) 4581 4582 let onRequest; 4583 let nextRequestPromise; 4584 4585 (function generateNewPromise() { 4586 nextRequestPromise = new Promise(resolve => { 4587 onRequest = args => { 4588 generateNewPromise(); 4589 resolve(args); 4590 }; 4591 }); 4592 })(); 4593 4594 const context = await esbuild.context({ 4595 entryPoints: [input], 4596 format: 'esm', 4597 outdir: outputDir, 4598 }) 4599 try { 4600 const result = await context.serve({ 4601 host: '127.0.0.1', 4602 onRequest: args => onRequest(args), 4603 servedir: wwwDir, 4604 }) 4605 assert.strictEqual(result.host, '127.0.0.1'); 4606 assert.strictEqual(typeof result.port, 'number'); 4607 4608 let promise, buffer, req; 4609 4610 promise = nextRequestPromise; 4611 buffer = await fetch(result.host, result.port, '/out/in.js') 4612 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 4613 req = await promise; 4614 assert.strictEqual(req.method, 'GET'); 4615 assert.strictEqual(req.path, '/out/in.js'); 4616 assert.strictEqual(req.status, 200); 4617 assert.strictEqual(typeof req.remoteAddress, 'string'); 4618 assert.strictEqual(typeof req.timeInMS, 'number'); 4619 4620 promise = nextRequestPromise; 4621 buffer = await fetch(result.host, result.port, '/') 4622 assert.strictEqual(buffer.toString(), `<!doctype html>`); 4623 req = await promise; 4624 assert.strictEqual(req.method, 'GET'); 4625 assert.strictEqual(req.path, '/'); 4626 assert.strictEqual(req.status, 200); 4627 assert.strictEqual(typeof req.remoteAddress, 'string'); 4628 assert.strictEqual(typeof req.timeInMS, 'number'); 4629 4630 // Make sure the output directory prefix requires a slash separator 4631 promise = nextRequestPromise; 4632 try { 4633 await fetch(result.host, result.port, '/outin.js') 4634 throw new Error('Expected an error to be thrown') 4635 } catch (err) { 4636 if (err.statusCode !== 404) throw err 4637 } 4638 req = await promise; 4639 assert.strictEqual(req.method, 'GET'); 4640 assert.strictEqual(req.path, '/outin.js'); 4641 assert.strictEqual(req.status, 404); 4642 assert.strictEqual(typeof req.remoteAddress, 'string'); 4643 assert.strictEqual(typeof req.timeInMS, 'number'); 4644 } finally { 4645 await context.dispose(); 4646 } 4647 }, 4648 4649 async serveWithServedirNoEntryPoints({ esbuild, testDir }) { 4650 const index = path.join(testDir, 'index.html') 4651 await writeFileAsync(index, `<!doctype html>`) 4652 4653 let onRequest; 4654 let nextRequestPromise; 4655 4656 (function generateNewPromise() { 4657 nextRequestPromise = new Promise(resolve => { 4658 onRequest = args => { 4659 generateNewPromise(); 4660 resolve(args); 4661 }; 4662 }); 4663 })(); 4664 4665 const context = await esbuild.context({}); 4666 try { 4667 const result = await context.serve({ 4668 host: '127.0.0.1', 4669 onRequest: args => onRequest(args), 4670 servedir: testDir, 4671 }) 4672 assert.strictEqual(result.host, '127.0.0.1'); 4673 assert.strictEqual(typeof result.port, 'number'); 4674 4675 let promise, buffer, req; 4676 4677 promise = nextRequestPromise; 4678 buffer = await fetch(result.host, result.port, '/') 4679 assert.strictEqual(buffer.toString(), `<!doctype html>`); 4680 req = await promise; 4681 assert.strictEqual(req.method, 'GET'); 4682 assert.strictEqual(req.path, '/'); 4683 assert.strictEqual(req.status, 200); 4684 assert.strictEqual(typeof req.remoteAddress, 'string'); 4685 assert.strictEqual(typeof req.timeInMS, 'number'); 4686 4687 // Check that removing the file removes it from the directory listing (i.e. the 4688 // "fs.FS" object in Go does not cache the result of calling "ReadDirectory") 4689 await fs.promises.unlink(index) 4690 promise = nextRequestPromise; 4691 buffer = await fetch(result.host, result.port, '/') 4692 assert.notStrictEqual(buffer.toString(), '<!doctype html>') 4693 req = await promise; 4694 assert.strictEqual(req.method, 'GET'); 4695 assert.strictEqual(req.path, '/'); 4696 assert.strictEqual(req.status, 200); 4697 assert.strictEqual(typeof req.remoteAddress, 'string'); 4698 assert.strictEqual(typeof req.timeInMS, 'number'); 4699 } finally { 4700 await context.dispose(); 4701 } 4702 }, 4703 4704 async serveRange({ esbuild, testDir }) { 4705 const big = path.join(testDir, 'big.txt') 4706 const byteCount = 16 * 1024 * 1024 4707 const buffer = require('crypto').randomBytes(byteCount) 4708 await writeFileAsync(big, buffer) 4709 4710 const context = await esbuild.context({}); 4711 try { 4712 const result = await context.serve({ 4713 host: '127.0.0.1', 4714 servedir: testDir, 4715 }) 4716 4717 // Test small to big ranges 4718 const minLength = 1 4719 const maxLength = buffer.length 4720 4721 for (let i = 0, n = 16; i < n; i++) { 4722 const length = Math.round(minLength + (maxLength - minLength) * i / (n - 1)) 4723 const start = Math.floor(Math.random() * (buffer.length - length)) 4724 const headers = { 4725 // Subtract 1 because range headers are inclusive on both ends 4726 Range: `bytes=${start}-${start + length - 1}`, 4727 } 4728 const fetched = await fetch(result.host, result.port, '/big.txt', { headers }) 4729 delete fetched.headers.date // This changes every time 4730 delete fetched.headers.connection // Node v19+ no longer sends this 4731 const expected = buffer.slice(start, start + length) 4732 expected.headers = { 4733 'access-control-allow-origin': '*', 4734 'content-length': `${length}`, 4735 'content-range': `bytes ${start}-${start + length - 1}/${byteCount}`, 4736 'content-type': 'application/octet-stream', 4737 } 4738 assert.deepStrictEqual(fetched, expected) 4739 } 4740 } finally { 4741 await context.dispose(); 4742 } 4743 }, 4744 4745 async serveWatchCopyLoaderWithEntryPoint({ esbuild, testDir }) { 4746 const ts = path.join(testDir, 'script.ts') 4747 const html = path.join(testDir, 'index.html') 4748 const outdir = path.join(testDir, 'out') 4749 await writeFileAsync(ts, `console.log(123)`) 4750 await writeFileAsync(html, `<script src=script.js></script>`) 4751 4752 const context = await esbuild.context({ 4753 entryPoints: [ts, html], 4754 outdir, 4755 loader: { '.html': 'copy' }, 4756 }); 4757 try { 4758 const server = await context.serve({ 4759 host: '127.0.0.1', 4760 servedir: testDir, 4761 }) 4762 await context.watch() 4763 4764 const js = await fetch(server.host, server.port, '/out/script.js') 4765 assert.strictEqual(js.toString(), `console.log(123);\n`); 4766 4767 const explicitHTML = await fetch(server.host, server.port, '/out/index.html') 4768 assert.strictEqual(explicitHTML.toString(), `<script src=script.js></script>`); 4769 4770 // The server should support implicit "index.html" extensions on entry point files 4771 const implicitHTML = await fetch(server.host, server.port, '/out/') 4772 assert.strictEqual(implicitHTML.toString(), `<script src=script.js></script>`); 4773 4774 // Make a change to the HTML 4775 await writeFileAsync(html, `<!DOCTYPE html><script src=script.js></script>`) 4776 4777 // Wait for watch mode to rebuild 4778 const start = Date.now() 4779 while (true) { 4780 if (Date.now() - start > 30 * 1000) throw new Error('Timeout after 30 seconds') 4781 const toCheck = fs.readFileSync(path.join(outdir, 'index.html'), 'utf8') 4782 if (toCheck === `<!DOCTYPE html><script src=script.js></script>`) break 4783 } 4784 } finally { 4785 await context.dispose(); 4786 } 4787 }, 4788 4789 async serveWithoutServedirWatchLiveReload({ esbuild, testDir }) { 4790 const js = path.join(testDir, 'app.js') 4791 const css = path.join(testDir, 'app.css') 4792 const outdir = path.join(testDir, 'out') 4793 await writeFileAsync(css, ``) 4794 4795 let endPromise 4796 const context = await esbuild.context({ 4797 entryPoints: [js], 4798 outdir, 4799 bundle: true, 4800 logLevel: 'silent', 4801 }); 4802 4803 try { 4804 const server = await context.serve({ 4805 host: '127.0.0.1', 4806 }) 4807 const stream = await makeEventStream(server.host, server.port, '/esbuild') 4808 await context.rebuild().then( 4809 () => Promise.reject(new Error('Expected an error to be thrown')), 4810 () => { /* Ignore the build error due to the missing JS file */ }, 4811 ) 4812 4813 // Event 1: a new JavaScript file 4814 var eventPromise = stream.waitFor('change') 4815 await writeFileAsync(js, ``) 4816 await context.rebuild() 4817 var data = JSON.parse((await eventPromise).data) 4818 assert.deepStrictEqual(data, { added: ['/app.js'], removed: [], updated: [] }) 4819 4820 // Event 2: edit the JavaScript file 4821 var eventPromise = stream.waitFor('change') 4822 await writeFileAsync(js, `foo()`) 4823 await context.rebuild() 4824 var data = JSON.parse((await eventPromise).data) 4825 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['/app.js'] }) 4826 4827 // Event 3: a new CSS file 4828 var eventPromise = stream.waitFor('change') 4829 await writeFileAsync(js, `import "./app.css"; foo()`) 4830 await context.rebuild() 4831 var data = JSON.parse((await eventPromise).data) 4832 assert.deepStrictEqual(data, { added: ['/app.css'], removed: [], updated: [] }) 4833 4834 // Event 4: edit the CSS file 4835 var eventPromise = stream.waitFor('change') 4836 await writeFileAsync(css, `a { color: red }`) 4837 await context.rebuild() 4838 var data = JSON.parse((await eventPromise).data) 4839 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['/app.css'] }) 4840 4841 // Event 5: remove the CSS file 4842 var eventPromise = stream.waitFor('change') 4843 await writeFileAsync(js, `bar()`) 4844 await context.rebuild() 4845 var data = JSON.parse((await eventPromise).data) 4846 assert.deepStrictEqual(data, { added: [], removed: ['/app.css'], updated: ['/app.js'] }) 4847 4848 // Wait for the stream to end once we call "dispose()" below 4849 endPromise = stream.waitFor('close') 4850 } 4851 4852 finally { 4853 await context.dispose(); 4854 } 4855 4856 // This stream should end once "dispose()" is called above 4857 await endPromise 4858 }, 4859 4860 async serveWithServedirWatchLiveReload({ esbuild, testDir }) { 4861 const js = path.join(testDir, 'app.js') 4862 const css = path.join(testDir, 'app.css') 4863 const outdir = path.join(testDir, 'out') 4864 await writeFileAsync(css, ``) 4865 4866 let endPromise 4867 const context = await esbuild.context({ 4868 entryPoints: [js], 4869 outdir, 4870 bundle: true, 4871 logLevel: 'silent', 4872 }); 4873 4874 try { 4875 const server = await context.serve({ 4876 host: '127.0.0.1', 4877 servedir: testDir, 4878 }) 4879 const stream = await makeEventStream(server.host, server.port, '/esbuild') 4880 await context.rebuild().then( 4881 () => Promise.reject(new Error('Expected an error to be thrown')), 4882 () => { /* Ignore the build error due to the missing JS file */ }, 4883 ) 4884 4885 // Event 1: a new JavaScript file 4886 var eventPromise = stream.waitFor('change') 4887 await writeFileAsync(js, ``) 4888 await context.rebuild() 4889 var data = JSON.parse((await eventPromise).data) 4890 assert.deepStrictEqual(data, { added: ['/out/app.js'], removed: [], updated: [] }) 4891 4892 // Event 2: edit the JavaScript file 4893 var eventPromise = stream.waitFor('change') 4894 await writeFileAsync(js, `foo()`) 4895 await context.rebuild() 4896 var data = JSON.parse((await eventPromise).data) 4897 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['/out/app.js'] }) 4898 4899 // Event 3: a new CSS file 4900 var eventPromise = stream.waitFor('change') 4901 await writeFileAsync(js, `import "./app.css"; foo()`) 4902 await context.rebuild() 4903 var data = JSON.parse((await eventPromise).data) 4904 assert.deepStrictEqual(data, { added: ['/out/app.css'], removed: [], updated: [] }) 4905 4906 // Event 4: edit the CSS file 4907 var eventPromise = stream.waitFor('change') 4908 await writeFileAsync(css, `a { color: red }`) 4909 await context.rebuild() 4910 var data = JSON.parse((await eventPromise).data) 4911 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['/out/app.css'] }) 4912 4913 // Event 5: remove the CSS file 4914 var eventPromise = stream.waitFor('change') 4915 await writeFileAsync(js, `bar()`) 4916 await context.rebuild() 4917 var data = JSON.parse((await eventPromise).data) 4918 assert.deepStrictEqual(data, { added: [], removed: ['/out/app.css'], updated: ['/out/app.js'] }) 4919 4920 // Wait for the stream to end once we call "dispose()" below 4921 endPromise = stream.waitFor('close') 4922 } 4923 4924 finally { 4925 await context.dispose(); 4926 } 4927 4928 // This stream should end once "dispose()" is called above 4929 await endPromise 4930 }, 4931 4932 async serveWithoutServedirWatchLiveReloadPublicPath({ esbuild, testDir }) { 4933 const js = path.join(testDir, 'app.js') 4934 const css = path.join(testDir, 'app.css') 4935 const outdir = path.join(testDir, 'out') 4936 await writeFileAsync(css, ``) 4937 4938 let endPromise 4939 const context = await esbuild.context({ 4940 entryPoints: [js], 4941 outdir, 4942 bundle: true, 4943 logLevel: 'silent', 4944 publicPath: 'http://example.com/about', 4945 }); 4946 4947 try { 4948 const server = await context.serve({ 4949 host: '127.0.0.1', 4950 }) 4951 const stream = await makeEventStream(server.host, server.port, '/esbuild') 4952 await context.rebuild().then( 4953 () => Promise.reject(new Error('Expected an error to be thrown')), 4954 () => { /* Ignore the build error due to the missing JS file */ }, 4955 ) 4956 4957 // Event 1: a new JavaScript file 4958 var eventPromise = stream.waitFor('change') 4959 await writeFileAsync(js, ``) 4960 await context.rebuild() 4961 var data = JSON.parse((await eventPromise).data) 4962 assert.deepStrictEqual(data, { added: ['http://example.com/about/app.js'], removed: [], updated: [] }) 4963 4964 // Event 2: edit the JavaScript file 4965 var eventPromise = stream.waitFor('change') 4966 await writeFileAsync(js, `foo()`) 4967 await context.rebuild() 4968 var data = JSON.parse((await eventPromise).data) 4969 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['http://example.com/about/app.js'] }) 4970 4971 // Event 3: a new CSS file 4972 var eventPromise = stream.waitFor('change') 4973 await writeFileAsync(js, `import "./app.css"; foo()`) 4974 await context.rebuild() 4975 var data = JSON.parse((await eventPromise).data) 4976 assert.deepStrictEqual(data, { added: ['http://example.com/about/app.css'], removed: [], updated: [] }) 4977 4978 // Event 4: edit the CSS file 4979 var eventPromise = stream.waitFor('change') 4980 await writeFileAsync(css, `a { color: red }`) 4981 await context.rebuild() 4982 var data = JSON.parse((await eventPromise).data) 4983 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['http://example.com/about/app.css'] }) 4984 4985 // Event 5: remove the CSS file 4986 var eventPromise = stream.waitFor('change') 4987 await writeFileAsync(js, `bar()`) 4988 await context.rebuild() 4989 var data = JSON.parse((await eventPromise).data) 4990 assert.deepStrictEqual(data, { added: [], removed: ['http://example.com/about/app.css'], updated: ['http://example.com/about/app.js'] }) 4991 4992 // Wait for the stream to end once we call "dispose()" below 4993 endPromise = stream.waitFor('close') 4994 } 4995 4996 finally { 4997 await context.dispose(); 4998 } 4999 5000 // This stream should end once "dispose()" is called above 5001 await endPromise 5002 }, 5003 5004 async serveWithServedirWatchLiveReloadPublicPath({ esbuild, testDir }) { 5005 const js = path.join(testDir, 'app.js') 5006 const css = path.join(testDir, 'app.css') 5007 const outdir = path.join(testDir, 'out') 5008 await writeFileAsync(css, ``) 5009 5010 let endPromise 5011 const context = await esbuild.context({ 5012 entryPoints: [js], 5013 outdir, 5014 bundle: true, 5015 logLevel: 'silent', 5016 publicPath: 'http://example.com/about', 5017 }); 5018 5019 try { 5020 const server = await context.serve({ 5021 host: '127.0.0.1', 5022 servedir: testDir, 5023 }) 5024 const stream = await makeEventStream(server.host, server.port, '/esbuild') 5025 await context.rebuild().then( 5026 () => Promise.reject(new Error('Expected an error to be thrown')), 5027 () => { /* Ignore the build error due to the missing JS file */ }, 5028 ) 5029 5030 // Event 1: a new JavaScript file 5031 var eventPromise = stream.waitFor('change') 5032 await writeFileAsync(js, ``) 5033 await context.rebuild() 5034 var data = JSON.parse((await eventPromise).data) 5035 assert.deepStrictEqual(data, { added: ['http://example.com/about/out/app.js'], removed: [], updated: [] }) 5036 5037 // Event 2: edit the JavaScript file 5038 var eventPromise = stream.waitFor('change') 5039 await writeFileAsync(js, `foo()`) 5040 await context.rebuild() 5041 var data = JSON.parse((await eventPromise).data) 5042 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['http://example.com/about/out/app.js'] }) 5043 5044 // Event 3: a new CSS file 5045 var eventPromise = stream.waitFor('change') 5046 await writeFileAsync(js, `import "./app.css"; foo()`) 5047 await context.rebuild() 5048 var data = JSON.parse((await eventPromise).data) 5049 assert.deepStrictEqual(data, { added: ['http://example.com/about/out/app.css'], removed: [], updated: [] }) 5050 5051 // Event 4: edit the CSS file 5052 var eventPromise = stream.waitFor('change') 5053 await writeFileAsync(css, `a { color: red }`) 5054 await context.rebuild() 5055 var data = JSON.parse((await eventPromise).data) 5056 assert.deepStrictEqual(data, { added: [], removed: [], updated: ['http://example.com/about/out/app.css'] }) 5057 5058 // Event 5: remove the CSS file 5059 var eventPromise = stream.waitFor('change') 5060 await writeFileAsync(js, `bar()`) 5061 await context.rebuild() 5062 var data = JSON.parse((await eventPromise).data) 5063 assert.deepStrictEqual(data, { added: [], removed: ['http://example.com/about/out/app.css'], updated: ['http://example.com/about/out/app.js'] }) 5064 5065 // Wait for the stream to end once we call "dispose()" below 5066 endPromise = stream.waitFor('close') 5067 } 5068 5069 finally { 5070 await context.dispose(); 5071 } 5072 5073 // This stream should end once "dispose()" is called above 5074 await endPromise 5075 }, 5076 5077 async serveWithFallback({ esbuild, testDir }) { 5078 const input = path.join(testDir, 'in.js') 5079 const wwwDir = path.join(testDir, 'www') 5080 const index = path.join(wwwDir, 'app', 'index.html') 5081 const fallback = path.join(testDir, 'fallback.html') 5082 await mkdirAsync(path.dirname(index), { recursive: true }) 5083 await writeFileAsync(input, `console.log(123)`) 5084 await writeFileAsync(index, `<p>index</p>`) 5085 await writeFileAsync(fallback, `<p>fallback</p>`) 5086 5087 const context = await esbuild.context({ 5088 entryPoints: [input], 5089 format: 'esm', 5090 outdir: wwwDir, 5091 write: false, 5092 }); 5093 try { 5094 const result = await context.serve({ 5095 host: '127.0.0.1', 5096 servedir: wwwDir, 5097 fallback, 5098 }) 5099 assert.strictEqual(result.host, '127.0.0.1'); 5100 assert.strictEqual(typeof result.port, 'number'); 5101 5102 let buffer; 5103 5104 buffer = await fetch(result.host, result.port, '/in.js') 5105 assert.strictEqual(buffer.toString(), `console.log(123);\n`); 5106 5107 buffer = await fetch(result.host, result.port, '/') 5108 assert.strictEqual(buffer.toString(), `<p>fallback</p>`); 5109 5110 buffer = await fetch(result.host, result.port, '/app/') 5111 assert.strictEqual(buffer.toString(), `<p>index</p>`); 5112 5113 buffer = await fetch(result.host, result.port, '/app/?foo') 5114 assert.strictEqual(buffer.toString(), `<p>index</p>`); 5115 5116 buffer = await fetch(result.host, result.port, '/app/foo') 5117 assert.strictEqual(buffer.toString(), `<p>fallback</p>`); 5118 } finally { 5119 await context.dispose(); 5120 } 5121 }, 5122 } 5123 5124 async function futureSyntax(esbuild, js, targetBelow, targetAbove) { 5125 failure: { 5126 try { await esbuild.transform(js, { target: targetBelow }) } 5127 catch { break failure } 5128 throw new Error(`Expected failure for ${targetBelow}: ${js}`) 5129 } 5130 5131 try { await esbuild.transform(js, { target: targetAbove }) } 5132 catch (e) { throw new Error(`Expected success for ${targetAbove}: ${js}\n${e}`) } 5133 } 5134 5135 let transformTests = { 5136 async transformWithNonString({ esbuild }) { 5137 try { 5138 await esbuild.transform(Object.create({ toString() { return '1+2' } })) 5139 throw new Error('Expected an error to be thrown'); 5140 } catch (e) { 5141 assert.strictEqual(e.errors ? e.errors[0].text : e + '', 'The input to "transform" must be a string or a Uint8Array') 5142 } 5143 }, 5144 5145 async version({ esbuild }) { 5146 const version = fs.readFileSync(path.join(repoDir, 'version.txt'), 'utf8').trim() 5147 assert.strictEqual(esbuild.version, version); 5148 }, 5149 5150 async ignoreUndefinedOptions({ esbuild }) { 5151 // This should not throw 5152 await esbuild.transform(``, { jsxFactory: void 0 }) 5153 }, 5154 5155 async throwOnBadOptions({ esbuild }) { 5156 // This should throw 5157 try { 5158 await esbuild.transform(``, { jsxFactory: ['React', 'createElement'] }) 5159 throw new Error('Expected transform failure'); 5160 } catch (e) { 5161 if (!e.errors || !e.errors[0] || e.errors[0].text !== '"jsxFactory" must be a string') { 5162 throw e; 5163 } 5164 } 5165 }, 5166 5167 async transformLoaderBase64({ esbuild }) { 5168 // UTF-16 5169 var result = await esbuild.transform(`\xFF`, { loader: 'base64' }) 5170 assert.strictEqual(result.code, `module.exports = "w78=";\n`) 5171 5172 // Binary 5173 var result = await esbuild.transform(new Uint8Array([0xFF]), { loader: 'base64' }) 5174 assert.strictEqual(result.code, `module.exports = "/w==";\n`) 5175 }, 5176 5177 async avoidTDZ({ esbuild }) { 5178 var { code } = await esbuild.transform(` 5179 class Foo { 5180 // The above line will be transformed into "var". However, the 5181 // symbol "Foo" must still be defined before the class body ends. 5182 static foo = new Foo 5183 } 5184 if (!(Foo.foo instanceof Foo)) 5185 throw 'fail' 5186 `) 5187 new Function(code)() 5188 }, 5189 5190 async tsAvoidTDZ({ esbuild }) { 5191 var { code } = await esbuild.transform(` 5192 class Foo { 5193 // The above line will be transformed into "var". However, the 5194 // symbol "Foo" must still be defined before the class body ends. 5195 static foo = new Foo 5196 } 5197 if (!(Foo.foo instanceof Foo)) 5198 throw 'fail' 5199 `, { 5200 loader: 'ts', 5201 }) 5202 new Function(code)() 5203 }, 5204 5205 // Note: The TypeScript compiler's transformer doesn't handle this case. 5206 // Using "this" like this is a syntax error instead. However, we can handle 5207 // it without too much trouble so we handle it here. This is defensive in 5208 // case the TypeScript compiler team fixes this in the future. 5209 async tsAvoidTDZThis({ esbuild }) { 5210 var { code } = await esbuild.transform(` 5211 class Foo { 5212 static foo = 123 5213 static bar = this.foo // "this" must be rewritten when the property is relocated 5214 } 5215 if (Foo.bar !== 123) throw 'fail' 5216 `, { 5217 loader: 'ts', 5218 tsconfigRaw: { 5219 compilerOptions: { 5220 useDefineForClassFields: false, 5221 }, 5222 }, 5223 }) 5224 new Function(code)() 5225 }, 5226 5227 async tsDecoratorAvoidTDZ({ esbuild }) { 5228 var { code } = await esbuild.transform(` 5229 class Bar {} 5230 var oldFoo 5231 function swap(target) { 5232 oldFoo = target 5233 return Bar 5234 } 5235 @swap 5236 class Foo { 5237 bar() { return new Foo } 5238 static foo = new Foo 5239 } 5240 if (!(oldFoo.foo instanceof oldFoo)) 5241 throw 'fail: foo' 5242 if (!(oldFoo.foo.bar() instanceof Bar)) 5243 throw 'fail: bar' 5244 `, { 5245 loader: 'ts', 5246 tsconfigRaw: { 5247 compilerOptions: { 5248 useDefineForClassFields: false, 5249 experimentalDecorators: true, 5250 }, 5251 }, 5252 }) 5253 new Function(code)() 5254 }, 5255 5256 async mangleQuotedTransform({ esbuild }) { 5257 var { code } = await esbuild.transform(`x.foo_ = 'foo_' in x`, { 5258 mangleProps: /_/, 5259 mangleQuoted: true, 5260 }) 5261 assert.strictEqual(code, 'x.a = "a" in x;\n') 5262 }, 5263 5264 async mangleCacheTransform({ esbuild }) { 5265 var { code, mangleCache } = await esbuild.transform(`x = { x_: 0, y_: 1, z_: 2 }`, { 5266 mangleProps: /_/, 5267 mangleCache: { x_: 'FIXED', z_: false }, 5268 }) 5269 assert.strictEqual(code, 'x = { FIXED: 0, a: 1, z_: 2 };\n') 5270 assert.deepStrictEqual(mangleCache, { x_: 'FIXED', y_: 'a', z_: false }) 5271 }, 5272 5273 async jsBannerTransform({ esbuild }) { 5274 var { code } = await esbuild.transform(` 5275 if (!bannerDefined) throw 'fail' 5276 `, { 5277 banner: 'const bannerDefined = true', 5278 }) 5279 new Function(code)() 5280 }, 5281 5282 async jsFooterTransform({ esbuild }) { 5283 var { code } = await esbuild.transform(` 5284 footer() 5285 `, { 5286 footer: 'function footer() {}', 5287 }) 5288 new Function(code)() 5289 new Function(code)() 5290 }, 5291 5292 async jsBannerFooterTransform({ esbuild }) { 5293 var { code } = await esbuild.transform(` 5294 return { banner: bannerDefined, footer }; 5295 `, { 5296 banner: 'const bannerDefined = true', 5297 footer: 'function footer() {}', 5298 }) 5299 const result = new Function(code)() 5300 if (!result.banner) throw 'fail' 5301 result.footer() 5302 }, 5303 5304 async cssBannerFooterTransform({ esbuild }) { 5305 for (const loader of ['css', 'local-css', 'global-css']) { 5306 var { code } = await esbuild.transform(` 5307 div { color: red } 5308 `, { 5309 loader, 5310 banner: '/* banner */', 5311 footer: '/* footer */', 5312 }) 5313 assert.strictEqual(code, `/* banner */\ndiv {\n color: red;\n}\n/* footer */\n`) 5314 } 5315 }, 5316 5317 async transformDirectEval({ esbuild }) { 5318 var { code } = await esbuild.transform(` 5319 export let abc = 123 5320 eval('console.log(abc)') 5321 `, { 5322 minify: true, 5323 }) 5324 assert.strictEqual(code, `export let abc=123;eval("console.log(abc)");\n`) 5325 }, 5326 5327 async tsconfigRawImportsNotUsedAsValuesDefault({ esbuild }) { 5328 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5329 tsconfigRaw: { 5330 compilerOptions: {}, 5331 }, 5332 loader: 'ts', 5333 }) 5334 assert.strictEqual(code, ``) 5335 }, 5336 5337 async tsconfigRawImportsNotUsedAsValuesRemove({ esbuild }) { 5338 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5339 tsconfigRaw: { 5340 compilerOptions: { 5341 importsNotUsedAsValues: 'remove', 5342 }, 5343 }, 5344 loader: 'ts', 5345 }) 5346 assert.strictEqual(code, ``) 5347 }, 5348 5349 async tsconfigRawImportsNotUsedAsValuesPreserve({ esbuild }) { 5350 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5351 tsconfigRaw: { 5352 compilerOptions: { 5353 importsNotUsedAsValues: 'preserve', 5354 }, 5355 }, 5356 loader: 'ts', 5357 }) 5358 assert.strictEqual(code, `import "path";\n`) 5359 }, 5360 5361 async tsconfigRawImportsNotUsedAsValuesError({ esbuild }) { 5362 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5363 tsconfigRaw: { 5364 compilerOptions: { 5365 importsNotUsedAsValues: 'error', 5366 }, 5367 }, 5368 loader: 'ts', 5369 }) 5370 assert.strictEqual(code, `import "path";\n`) 5371 }, 5372 5373 async tsconfigRawImportsNotUsedAsValuesRemoveJS({ esbuild }) { 5374 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5375 tsconfigRaw: { 5376 compilerOptions: { 5377 importsNotUsedAsValues: 'remove', 5378 }, 5379 }, 5380 }) 5381 assert.strictEqual(code, `import { T } from "path";\n`) 5382 }, 5383 5384 async tsconfigRawImportsNotUsedAsValuesPreserveJS({ esbuild }) { 5385 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5386 tsconfigRaw: { 5387 compilerOptions: { 5388 importsNotUsedAsValues: 'preserve', 5389 }, 5390 }, 5391 }) 5392 assert.strictEqual(code, `import { T } from "path";\n`) 5393 }, 5394 5395 async tsconfigRawPreserveValueImportsDefault({ esbuild }) { 5396 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5397 tsconfigRaw: { 5398 compilerOptions: {}, 5399 }, 5400 loader: 'ts', 5401 }) 5402 assert.strictEqual(code, ``) 5403 }, 5404 5405 async tsconfigRawPreserveValueImportsFalse({ esbuild }) { 5406 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5407 tsconfigRaw: { 5408 compilerOptions: { 5409 preserveValueImports: false, 5410 }, 5411 }, 5412 loader: 'ts', 5413 }) 5414 assert.strictEqual(code, ``) 5415 }, 5416 5417 async tsconfigRawPreserveValueImportsTrue({ esbuild }) { 5418 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5419 tsconfigRaw: { 5420 compilerOptions: { 5421 preserveValueImports: true, 5422 }, 5423 }, 5424 loader: 'ts', 5425 }) 5426 assert.strictEqual(code, `import { T } from "path";\n`) 5427 }, 5428 5429 async tsconfigRawPreserveValueImportsTrueMinifyIdentifiers({ esbuild }) { 5430 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5431 tsconfigRaw: { 5432 compilerOptions: { 5433 preserveValueImports: true, 5434 }, 5435 }, 5436 loader: 'ts', 5437 minifyIdentifiers: true, 5438 }) 5439 assert.strictEqual(code, `import "path";\n`) 5440 }, 5441 5442 async tsconfigRawPreserveValueImportsTrueImportsNotUsedAsValuesRemove({ esbuild }) { 5443 const { code } = await esbuild.transform(`import {T} from 'path'`, { 5444 tsconfigRaw: { 5445 compilerOptions: { 5446 importsNotUsedAsValues: 'remove', 5447 preserveValueImports: true, 5448 }, 5449 }, 5450 loader: 'ts', 5451 }) 5452 assert.strictEqual(code, `import { T } from "path";\n`) 5453 }, 5454 5455 async tsconfigRawCommentsInJSON({ esbuild }) { 5456 // Can use a string, which allows weird TypeScript pseudo-JSON with comments and trailing commas 5457 const { code: code5 } = await esbuild.transform(`import {T} from 'path'`, { 5458 tsconfigRaw: `{ 5459 "compilerOptions": { 5460 "preserveValueImports": true, // there is a trailing comment here 5461 }, 5462 }`, 5463 loader: 'ts', 5464 }) 5465 assert.strictEqual(code5, `import { T } from "path";\n`) 5466 }, 5467 5468 async tsconfigRawUseDefineForClassFields({ esbuild }) { 5469 const { code: code1 } = await esbuild.transform(`class Foo { foo }`, { 5470 tsconfigRaw: { 5471 compilerOptions: { 5472 useDefineForClassFields: false, 5473 }, 5474 }, 5475 loader: 'ts', 5476 }) 5477 assert.strictEqual(code1, `class Foo {\n}\n`) 5478 5479 const { code: code2 } = await esbuild.transform(`class Foo { foo }`, { 5480 tsconfigRaw: { 5481 compilerOptions: { 5482 useDefineForClassFields: true, 5483 }, 5484 }, 5485 loader: 'ts', 5486 }) 5487 assert.strictEqual(code2, `class Foo {\n foo;\n}\n`) 5488 }, 5489 5490 async tsconfigRawAlwaysStrict({ esbuild }) { 5491 const input = `console.log(123)` 5492 5493 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { strict: false } } })).code, `console.log(123);\n`) 5494 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { alwaysStrict: false } } })).code, `console.log(123);\n`) 5495 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { alwaysStrict: false, strict: true } } })).code, `console.log(123);\n`) 5496 5497 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { strict: true } } })).code, `"use strict";\nconsole.log(123);\n`) 5498 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { alwaysStrict: true } } })).code, `"use strict";\nconsole.log(123);\n`) 5499 assert.strictEqual((await esbuild.transform(input, { loader: 'ts', tsconfigRaw: { compilerOptions: { alwaysStrict: true, strict: false } } })).code, `"use strict";\nconsole.log(123);\n`) 5500 }, 5501 5502 async tsconfigRawTarget({ esbuild }) { 5503 // The "target" from "tsconfig.json" should not apply, but esbuild's "target" should apply 5504 5505 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', tsconfigRaw: { compilerOptions: { target: 'ES6' } } })).code, `(x) => x;\n`) 5506 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', tsconfigRaw: { compilerOptions: { target: 'ES5' } } })).code, `(x) => x;\n`) 5507 5508 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', target: 'es6' })).code, `(x) => x;\n`) 5509 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', target: 'es5' })).code, `(function(x) {\n return x;\n});\n`) 5510 5511 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', target: 'es5', tsconfigRaw: { compilerOptions: { target: 'ES6' } } })).code, `(function(x) {\n return x;\n});\n`) 5512 assert.strictEqual((await esbuild.transform('x => x', { loader: 'ts', target: 'es6', tsconfigRaw: { compilerOptions: { target: 'ES5' } } })).code, `(x) => x;\n`) 5513 }, 5514 5515 async tsconfigRawExtends({ esbuild }) { 5516 // Uses of "extends" in "tsconfigRaw" should be ignored for the transform API 5517 const result = await esbuild.transform('let foo: bar', { 5518 loader: 'ts', 5519 logOverride: { 'tsconfig.json': 'error' }, 5520 tsconfigRaw: { 5521 extends: __filename, 5522 }, 5523 }) 5524 assert.strictEqual(result.code, `let foo;\n`) 5525 }, 5526 5527 async tsImplicitUseDefineForClassFields({ esbuild }) { 5528 var { code } = await esbuild.transform(`class Foo { foo }`, { 5529 loader: 'ts', 5530 }) 5531 assert.strictEqual(code, `class Foo {\n foo;\n}\n`) 5532 5533 var { code } = await esbuild.transform(`class Foo { foo }`, { 5534 target: 'es2021', 5535 loader: 'ts', 5536 }) 5537 assert.strictEqual(code, `var __defProp = Object.defineProperty; 5538 var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; 5539 var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); 5540 class Foo { 5541 constructor() { 5542 __publicField(this, "foo"); 5543 } 5544 } 5545 `) 5546 5547 var { code } = await esbuild.transform(`class Foo { foo }`, { 5548 target: 'es2022', 5549 loader: 'ts', 5550 }) 5551 assert.strictEqual(code, `class Foo {\n foo;\n}\n`) 5552 5553 var { code } = await esbuild.transform(`class Foo { foo }`, { 5554 target: 'esnext', 5555 loader: 'ts', 5556 }) 5557 assert.strictEqual(code, `class Foo {\n foo;\n}\n`) 5558 }, 5559 5560 async tsconfigRawJSX({ esbuild }) { 5561 const { code: code1 } = await esbuild.transform(`<><div/></>`, { 5562 tsconfigRaw: { 5563 compilerOptions: { 5564 }, 5565 }, 5566 loader: 'jsx', 5567 }) 5568 assert.strictEqual(code1, `/* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", null));\n`) 5569 5570 const { code: code2 } = await esbuild.transform(`<><div/></>`, { 5571 tsconfigRaw: { 5572 compilerOptions: { 5573 jsxFactory: 'factory', 5574 jsxFragmentFactory: 'fragment', 5575 }, 5576 }, 5577 loader: 'jsx', 5578 }) 5579 assert.strictEqual(code2, `/* @__PURE__ */ factory(fragment, null, /* @__PURE__ */ factory("div", null));\n`) 5580 5581 const { code: code3 } = await esbuild.transform(`<><div/></>`, { 5582 tsconfigRaw: { 5583 compilerOptions: { 5584 jsx: 'react-jsx' 5585 }, 5586 }, 5587 loader: 'jsx', 5588 }) 5589 assert.strictEqual(code3, `import { Fragment, jsx } from "react/jsx-runtime";\n/* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {}) });\n`) 5590 5591 const { code: code4 } = await esbuild.transform(`<><div/></>`, { 5592 tsconfigRaw: { 5593 compilerOptions: { 5594 jsx: 'react-jsx', 5595 jsxImportSource: 'notreact' 5596 }, 5597 }, 5598 loader: 'jsx', 5599 }) 5600 assert.strictEqual(code4, `import { Fragment, jsx } from "notreact/jsx-runtime";\n/* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", {}) });\n`) 5601 5602 const { code: code5 } = await esbuild.transform(`<><div/></>`, { 5603 tsconfigRaw: { 5604 compilerOptions: { 5605 jsx: 'react-jsxdev' 5606 }, 5607 }, 5608 loader: 'jsx', 5609 }) 5610 assert.strictEqual(code5, `import { Fragment, jsxDEV } from "react/jsx-dev-runtime";\n/* @__PURE__ */ jsxDEV(Fragment, { children: /* @__PURE__ */ jsxDEV("div", {}, void 0, false, {\n fileName: "<stdin>",\n lineNumber: 1,\n columnNumber: 3\n}, this) }, void 0, false, {\n fileName: "<stdin>",\n lineNumber: 1,\n columnNumber: 1\n}, this);\n`) 5611 }, 5612 5613 // Note: tree shaking is disabled when the output format isn't IIFE 5614 async treeShakingDefault({ esbuild }) { 5615 const { code } = await esbuild.transform(` 5616 var unused = 123 5617 var used = 234 5618 export { used } 5619 `, { 5620 loader: 'jsx', 5621 format: 'esm', 5622 treeShaking: undefined, 5623 }) 5624 assert.strictEqual(code, `var unused = 123;\nvar used = 234;\nexport {\n used\n};\n`) 5625 }, 5626 5627 async treeShakingFalse({ esbuild }) { 5628 const { code } = await esbuild.transform(` 5629 var unused = 123 5630 var used = 234 5631 export { used } 5632 `, { 5633 loader: 'jsx', 5634 format: 'esm', 5635 treeShaking: false, 5636 }) 5637 assert.strictEqual(code, `var unused = 123;\nvar used = 234;\nexport {\n used\n};\n`) 5638 }, 5639 5640 async treeShakingTrue({ esbuild }) { 5641 const { code } = await esbuild.transform(` 5642 var unused = 123 5643 var used = 234 5644 export { used } 5645 `, { 5646 loader: 'jsx', 5647 format: 'esm', 5648 treeShaking: true, 5649 }) 5650 assert.strictEqual(code, `var used = 234;\nexport {\n used\n};\n`) 5651 }, 5652 5653 // Note: tree shaking is enabled when the output format is IIFE 5654 async treeShakingDefaultIIFE({ esbuild }) { 5655 const { code } = await esbuild.transform(`var unused = 123`, { 5656 loader: 'jsx', 5657 format: 'iife', 5658 treeShaking: undefined, 5659 }) 5660 assert.strictEqual(code, `(() => {\n})();\n`) 5661 }, 5662 5663 async treeShakingFalseIIFE({ esbuild }) { 5664 const { code } = await esbuild.transform(`var unused = 123`, { 5665 loader: 'jsx', 5666 format: 'iife', 5667 treeShaking: false, 5668 }) 5669 assert.strictEqual(code, `(() => {\n var unused = 123;\n})();\n`) 5670 }, 5671 5672 async treeShakingTrueIIFE({ esbuild }) { 5673 const { code } = await esbuild.transform(`var unused = 123`, { 5674 loader: 'jsx', 5675 format: 'iife', 5676 treeShaking: true, 5677 }) 5678 assert.strictEqual(code, `(() => {\n})();\n`) 5679 }, 5680 5681 async ignoreAnnotationsDefault({ esbuild }) { 5682 const { code } = await esbuild.transform(`/* @__PURE__ */ fn(); <div/>`, { 5683 loader: 'jsx', 5684 minifySyntax: true, 5685 }) 5686 assert.strictEqual(code, ``) 5687 }, 5688 5689 async ignoreAnnotationsFalse({ esbuild }) { 5690 const { code } = await esbuild.transform(`/* @__PURE__ */ fn(); <div/>`, { 5691 loader: 'jsx', 5692 minifySyntax: true, 5693 ignoreAnnotations: false, 5694 }) 5695 assert.strictEqual(code, ``) 5696 }, 5697 5698 async ignoreAnnotationsTrue({ esbuild }) { 5699 const { code } = await esbuild.transform(`/* @__PURE__ */ fn(); <div/>`, { 5700 loader: 'jsx', 5701 minifySyntax: true, 5702 ignoreAnnotations: true, 5703 }) 5704 assert.strictEqual(code, `fn(), React.createElement("div", null);\n`) 5705 }, 5706 5707 async jsCharsetDefault({ esbuild }) { 5708 const { code } = await esbuild.transform(`let π = 'π'`, {}) 5709 assert.strictEqual(code, `let \\u03C0 = "\\u03C0";\n`) 5710 }, 5711 5712 async jsCharsetASCII({ esbuild }) { 5713 const { code } = await esbuild.transform(`let π = 'π'`, { charset: 'ascii' }) 5714 assert.strictEqual(code, `let \\u03C0 = "\\u03C0";\n`) 5715 }, 5716 5717 async jsCharsetUTF8({ esbuild }) { 5718 const { code } = await esbuild.transform(`let π = 'π'`, { charset: 'utf8' }) 5719 assert.strictEqual(code, `let π = "π";\n`) 5720 }, 5721 5722 async cssCharsetDefault({ esbuild }) { 5723 const { code } = await esbuild.transform(`.π:after { content: 'π' }`, { loader: 'css' }) 5724 assert.strictEqual(code, `.\\3c0:after {\n content: "\\3c0";\n}\n`) 5725 }, 5726 5727 async cssCharsetASCII({ esbuild }) { 5728 const { code } = await esbuild.transform(`.π:after { content: 'π' }`, { loader: 'css', charset: 'ascii' }) 5729 assert.strictEqual(code, `.\\3c0:after {\n content: "\\3c0";\n}\n`) 5730 }, 5731 5732 async cssCharsetUTF8({ esbuild }) { 5733 const { code } = await esbuild.transform(`.π:after { content: 'π' }`, { loader: 'css', charset: 'utf8' }) 5734 assert.strictEqual(code, `.π:after {\n content: "π";\n}\n`) 5735 }, 5736 5737 async cssSyntaxErrorWarning({ esbuild }) { 5738 const { code } = await esbuild.transform(`. {}`, { loader: 'css' }) 5739 assert.strictEqual(code, `. {\n}\n`) 5740 }, 5741 5742 async cssSyntaxErrorWarningOverride({ esbuild }) { 5743 try { 5744 await esbuild.transform(`. {}`, { loader: 'css', logOverride: { 'css-syntax-error': 'error' } }) 5745 throw new Error('Expected a transform failure') 5746 } catch (e) { 5747 assert.strictEqual((e && e.message || e) + '', `Transform failed with 1 error:\n<stdin>:1:1: ERROR: Expected identifier but found whitespace`) 5748 } 5749 }, 5750 5751 async cssMinify({ esbuild }) { 5752 const { code } = await esbuild.transform(`div { color: #abcd }`, { loader: 'css', minify: true }) 5753 assert.strictEqual(code, `div{color:#abcd}\n`) 5754 }, 5755 5756 // Using an "es" target shouldn't affect CSS 5757 async cssMinifyTargetES6({ esbuild }) { 5758 const { code } = await esbuild.transform(`div { color: #abcd }`, { loader: 'css', minify: true, target: 'es6' }) 5759 assert.strictEqual(code, `div{color:#abcd}\n`) 5760 }, 5761 5762 // Using a "node" target shouldn't affect CSS 5763 async cssMinifyTargetNode({ esbuild }) { 5764 const { code } = await esbuild.transform(`div { color: #abcd }`, { loader: 'css', minify: true, target: 'node8' }) 5765 assert.strictEqual(code, `div{color:#abcd}\n`) 5766 }, 5767 5768 // Using an older browser target should affect CSS 5769 async cssMinifyTargetChrome8({ esbuild }) { 5770 const { code } = await esbuild.transform(`div { color: #abcd }`, { loader: 'css', minify: true, target: 'chrome8' }) 5771 assert.strictEqual(code, `div{color:rgba(170,187,204,.867)}\n`) 5772 }, 5773 5774 // Using a newer browser target shouldn't affect CSS 5775 async cssMinifyTargetChrome80({ esbuild }) { 5776 const { code } = await esbuild.transform(`div { color: #abcd }`, { loader: 'css', minify: true, target: 'chrome80' }) 5777 assert.strictEqual(code, `div{color:#abcd}\n`) 5778 }, 5779 5780 async cjs_require({ esbuild }) { 5781 const { code } = await esbuild.transform(`const {foo} = require('path')`, {}) 5782 assert.strictEqual(code, `const { foo } = require("path");\n`) 5783 }, 5784 5785 async cjs_exports({ esbuild }) { 5786 const { code } = await esbuild.transform(`exports.foo = 123`, {}) 5787 assert.strictEqual(code, `exports.foo = 123;\n`) 5788 }, 5789 5790 async es6_import({ esbuild }) { 5791 const { code } = await esbuild.transform(`import {foo} from 'path'`, {}) 5792 assert.strictEqual(code, `import { foo } from "path";\n`) 5793 }, 5794 5795 async es6_export({ esbuild }) { 5796 const { code } = await esbuild.transform(`export const foo = 123`, {}) 5797 assert.strictEqual(code, `export const foo = 123;\n`) 5798 }, 5799 5800 async es6_import_to_iife({ esbuild }) { 5801 const { code } = await esbuild.transform(`import {exists} from "fs"; if (!exists) throw 'fail'`, { format: 'iife' }) 5802 new Function('require', code)(require) 5803 }, 5804 5805 async es6_import_star_to_iife({ esbuild }) { 5806 const { code } = await esbuild.transform(`import * as fs from "fs"; if (!fs.exists) throw 'fail'`, { format: 'iife' }) 5807 new Function('require', code)(require) 5808 }, 5809 5810 async es6_export_to_iife({ esbuild }) { 5811 const { code } = await esbuild.transform(`export {exists} from "fs"`, { format: 'iife', globalName: 'out' }) 5812 const out = new Function('require', code + ';return out')(require) 5813 if (out.exists !== fs.exists) throw 'fail' 5814 }, 5815 5816 async es6_export_star_to_iife({ esbuild }) { 5817 const { code } = await esbuild.transform(`export * from "fs"`, { format: 'iife', globalName: 'out' }) 5818 const out = new Function('require', code + ';return out')(require) 5819 if (out.exists !== fs.exists) throw 'fail' 5820 }, 5821 5822 async es6_export_star_as_to_iife({ esbuild }) { 5823 const { code } = await esbuild.transform(`export * as fs from "fs"`, { format: 'iife', globalName: 'out' }) 5824 const out = new Function('require', code + ';return out')(require) 5825 if (out.fs.exists !== fs.exists) throw 'fail' 5826 }, 5827 5828 async es6_import_to_cjs({ esbuild }) { 5829 const { code } = await esbuild.transform(`import {exists} from "fs"; if (!exists) throw 'fail'`, { format: 'cjs' }) 5830 new Function('require', code)(require) 5831 }, 5832 5833 async es6_import_star_to_cjs({ esbuild }) { 5834 const { code } = await esbuild.transform(`import * as fs from "fs"; if (!fs.exists) throw 'fail'`, { format: 'cjs' }) 5835 new Function('require', code)(require) 5836 }, 5837 5838 async es6_export_to_cjs({ esbuild }) { 5839 const { code } = await esbuild.transform(`export {exists} from "fs"`, { format: 'cjs' }) 5840 const module = { exports: {} } 5841 new Function('module', 'exports', 'require', code)(module, module.exports, require) 5842 if (module.exports.exists !== fs.exists) throw 'fail' 5843 }, 5844 5845 async es6_export_star_to_cjs({ esbuild }) { 5846 const { code } = await esbuild.transform(`export * from "fs"`, { format: 'cjs' }) 5847 const module = { exports: {} } 5848 new Function('module', 'exports', 'require', code)(module, module.exports, require) 5849 if (module.exports.exists !== fs.exists) throw 'fail' 5850 }, 5851 5852 async es6_export_star_as_to_cjs({ esbuild }) { 5853 const { code } = await esbuild.transform(`export * as fs from "fs"`, { format: 'cjs' }) 5854 const module = { exports: {} } 5855 new Function('module', 'exports', 'require', code)(module, module.exports, require) 5856 if (module.exports.fs.exists !== fs.exists) throw 'fail' 5857 }, 5858 5859 async es6_import_to_esm({ esbuild }) { 5860 const { code } = await esbuild.transform(`import {exists} from "fs"; if (!exists) throw 'fail'`, { format: 'esm' }) 5861 assert.strictEqual(code, `import { exists } from "fs";\nif (!exists) throw "fail";\n`) 5862 }, 5863 5864 async es6_import_star_to_esm({ esbuild }) { 5865 const { code } = await esbuild.transform(`import * as fs from "fs"; if (!fs.exists) throw 'fail'`, { format: 'esm' }) 5866 assert.strictEqual(code, `import * as fs from "fs";\nif (!fs.exists) throw "fail";\n`) 5867 }, 5868 5869 async es6_export_to_esm({ esbuild }) { 5870 const { code } = await esbuild.transform(`export {exists} from "fs"`, { format: 'esm' }) 5871 assert.strictEqual(code, `import { exists } from "fs";\nexport {\n exists\n};\n`) 5872 }, 5873 5874 async es6_export_star_to_esm({ esbuild }) { 5875 const { code } = await esbuild.transform(`export * from "fs"`, { format: 'esm' }) 5876 assert.strictEqual(code, `export * from "fs";\n`) 5877 }, 5878 5879 async es6_export_star_as_to_esm({ esbuild }) { 5880 const { code } = await esbuild.transform(`export * as fs from "fs"`, { format: 'esm' }) 5881 assert.strictEqual(code, `import * as fs from "fs";\nexport {\n fs\n};\n`) 5882 }, 5883 5884 async iifeGlobalName({ esbuild }) { 5885 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'testName' }) 5886 const globals = {} 5887 vm.createContext(globals) 5888 vm.runInContext(code, globals) 5889 assert.strictEqual(globals.testName.default, 123) 5890 }, 5891 5892 async iifeGlobalNameCompound({ esbuild }) { 5893 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'test.name' }) 5894 const globals = {} 5895 vm.createContext(globals) 5896 vm.runInContext(code, globals) 5897 assert.strictEqual(globals.test.name.default, 123) 5898 }, 5899 5900 async iifeGlobalNameString({ esbuild }) { 5901 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'test["some text"]' }) 5902 const globals = {} 5903 vm.createContext(globals) 5904 vm.runInContext(code, globals) 5905 assert.strictEqual(globals.test['some text'].default, 123) 5906 }, 5907 5908 async iifeGlobalNameUnicodeEscape({ esbuild }) { 5909 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]' }) 5910 const globals = {} 5911 vm.createContext(globals) 5912 vm.runInContext(code, globals) 5913 assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123) 5914 assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var \\u03C0; 5915 (((\\u03C0 ||= {})["\\u03C0 \\uD800\\uDC00"] ||= {})["\\uD800\\uDC00"] ||= {})["\\uD800\\uDC00 \\u03C0"] = `) 5916 }, 5917 5918 async iifeGlobalNameUnicodeNoEscape({ esbuild }) { 5919 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', charset: 'utf8' }) 5920 const globals = {} 5921 vm.createContext(globals) 5922 vm.runInContext(code, globals) 5923 assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123) 5924 assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var π; 5925 (((π ||= {})["π 𐀀"] ||= {})["𐀀"] ||= {})["𐀀 π"] = `) 5926 }, 5927 5928 async iifeGlobalNameUnicodeEscapeNoLogicalAssignment({ esbuild }) { 5929 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', supported: { 'logical-assignment': false } }) 5930 const globals = {} 5931 vm.createContext(globals) 5932 vm.runInContext(code, globals) 5933 assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123) 5934 assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `var \\u03C0 = \\u03C0 || {}; 5935 \\u03C0["\\u03C0 \\uD800\\uDC00"] = \\u03C0["\\u03C0 \\uD800\\uDC00"] || {}; 5936 \\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"] = \\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"] || {}; 5937 \\u03C0["\\u03C0 \\uD800\\uDC00"]["\\uD800\\uDC00"]["\\uD800\\uDC00 \\u03C0"] = `) 5938 }, 5939 5940 async iifeGlobalNameUnicodeNoEscapeNoLogicalAssignment({ esbuild }) { 5941 const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'π["π 𐀀"].𐀀["𐀀 π"]', supported: { 'logical-assignment': false }, charset: 'utf8' }) 5942 const globals = {} 5943 vm.createContext(globals) 5944 vm.runInContext(code, globals) 5945 assert.strictEqual(globals.π["π 𐀀"].𐀀["𐀀 π"].default, 123) 5946 assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), 5947 `var π = π || {}; 5948 π["π 𐀀"] = π["π 𐀀"] || {}; 5949 π["π 𐀀"]["𐀀"] = π["π 𐀀"]["𐀀"] || {}; 5950 π["π 𐀀"]["𐀀"]["𐀀 π"] = `) 5951 }, 5952 5953 async jsx({ esbuild }) { 5954 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx' }) 5955 assert.strictEqual(code, `console.log(/* @__PURE__ */ React.createElement("div", null));\n`) 5956 }, 5957 5958 async jsxTransform({ esbuild }) { 5959 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx', jsx: 'transform' }) 5960 assert.strictEqual(code, `console.log(/* @__PURE__ */ React.createElement("div", null));\n`) 5961 }, 5962 5963 async jsxPreserve({ esbuild }) { 5964 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx', jsx: 'preserve' }) 5965 assert.strictEqual(code, `console.log(<div />);\n`) 5966 }, 5967 5968 async jsxRuntimeAutomatic({ esbuild }) { 5969 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx', jsx: 'automatic' }) 5970 assert.strictEqual(code, `import { jsx } from "react/jsx-runtime";\nconsole.log(/* @__PURE__ */ jsx("div", {}));\n`) 5971 }, 5972 5973 async jsxDev({ esbuild }) { 5974 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx', jsx: 'automatic', jsxDev: true }) 5975 assert.strictEqual(code, `import { jsxDEV } from "react/jsx-dev-runtime";\nconsole.log(/* @__PURE__ */ jsxDEV("div", {}, void 0, false, {\n fileName: "<stdin>",\n lineNumber: 1,\n columnNumber: 13\n}, this));\n`) 5976 }, 5977 5978 async jsxImportSource({ esbuild }) { 5979 const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx', jsx: 'automatic', jsxImportSource: 'notreact' }) 5980 assert.strictEqual(code, `import { jsx } from "notreact/jsx-runtime";\nconsole.log(/* @__PURE__ */ jsx("div", {}));\n`) 5981 }, 5982 5983 async jsxSideEffects({ esbuild }) { 5984 const { code } = await esbuild.transform(`<b/>`, { loader: 'jsx', jsxSideEffects: true }) 5985 assert.strictEqual(code, `React.createElement("b", null);\n`) 5986 }, 5987 5988 async ts({ esbuild }) { 5989 const { code } = await esbuild.transform(`enum Foo { FOO }`, { loader: 'ts' }) 5990 assert.strictEqual(code, `var Foo = /* @__PURE__ */ ((Foo2) => {\n Foo2[Foo2["FOO"] = 0] = "FOO";\n return Foo2;\n})(Foo || {});\n`) 5991 }, 5992 5993 async tsx({ esbuild }) { 5994 const { code } = await esbuild.transform(`console.log(<Foo<T>/>)`, { loader: 'tsx' }) 5995 assert.strictEqual(code, `console.log(/* @__PURE__ */ React.createElement(Foo, null));\n`) 5996 }, 5997 5998 async minify({ esbuild }) { 5999 const { code } = await esbuild.transform(`console.log("a" + "b" + c)`, { minify: true }) 6000 assert.strictEqual(code, `console.log("ab"+c);\n`) 6001 }, 6002 6003 async keepConsole({ esbuild }) { 6004 const { code } = await esbuild.transform(`console.log('foo')`, { drop: [] }) 6005 assert.strictEqual(code, `console.log("foo");\n`) 6006 }, 6007 6008 async dropConsole({ esbuild }) { 6009 const { code } = await esbuild.transform(` 6010 console('foo') 6011 console.log('foo') 6012 console.log(foo()) 6013 x = console.log(bar()) 6014 console.abc.xyz('foo') 6015 console['log']('foo') 6016 console[abc][xyz]('foo') 6017 console[foo()][bar()]('foo') 6018 `, { drop: ['console'] }) 6019 assert.strictEqual(code, `console("foo");\nx = void 0;\n`) 6020 }, 6021 6022 async keepDebugger({ esbuild }) { 6023 const { code } = await esbuild.transform(`if (x) debugger`, { drop: [] }) 6024 assert.strictEqual(code, `if (x) debugger;\n`) 6025 }, 6026 6027 async dropDebugger({ esbuild }) { 6028 const { code } = await esbuild.transform(`if (x) debugger`, { drop: ['debugger'] }) 6029 assert.strictEqual(code, `if (x) ;\n`) 6030 }, 6031 6032 async define({ esbuild }) { 6033 const define = { 'process.env.NODE_ENV': '"something"' } 6034 6035 const { code: code1 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { define }) 6036 assert.strictEqual(code1, `console.log("something");\n`) 6037 6038 const { code: code2 } = await esbuild.transform(`console.log(process.env['NODE_ENV'])`, { define }) 6039 assert.strictEqual(code2, `console.log("something");\n`) 6040 6041 const { code: code3 } = await esbuild.transform(`console.log(process['env'].NODE_ENV)`, { define }) 6042 assert.strictEqual(code3, `console.log("something");\n`) 6043 6044 const { code: code4 } = await esbuild.transform(`console.log(process['env']['NODE_ENV'])`, { define }) 6045 assert.strictEqual(code4, `console.log("something");\n`) 6046 6047 const { code: code5 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, {}) 6048 assert.strictEqual(code5, `console.log(process.env.NODE_ENV);\n`) 6049 6050 const { code: code6 } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { platform: 'browser' }) 6051 assert.strictEqual(code6, `console.log(process.env.NODE_ENV);\n`) 6052 }, 6053 6054 async defineBuiltInConstants({ esbuild }) { 6055 const define = { a: 'NaN', b: 'Infinity', c: 'undefined', d: 'something', e: 'null' } 6056 const { code } = await esbuild.transform(`console.log([typeof a, typeof b, typeof c, typeof d, typeof e])`, { define }) 6057 assert.strictEqual(code, `console.log(["number", "number", "undefined", typeof something, "object"]);\n`) 6058 }, 6059 6060 async defineArray({ esbuild }) { 6061 const define = { 'process.env.NODE_ENV': '[1,2,3]', 'something.else': '[2,3,4]' } 6062 const { code } = await esbuild.transform(`console.log(process.env.NODE_ENV)`, { define }) 6063 assert.strictEqual(code, `var define_process_env_NODE_ENV_default = [1, 2, 3];\nconsole.log(define_process_env_NODE_ENV_default);\n`) 6064 }, 6065 6066 async defineThis({ esbuild }) { 6067 const { code } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'this', b: 'this.foo' }, format: 'esm' }) 6068 assert.strictEqual(code, `console.log(void 0, (void 0).foo);\n`) 6069 }, 6070 6071 async defineImportMetaESM({ esbuild }) { 6072 const { code } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'import.meta', b: 'import.meta.foo' }, format: 'esm' }) 6073 assert.strictEqual(code, `console.log(import.meta, import.meta.foo);\n`) 6074 }, 6075 6076 async defineImportMetaIIFE({ esbuild }) { 6077 const { code } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'import.meta', b: 'import.meta.foo' }, format: 'iife' }) 6078 assert.strictEqual(code, `(() => {\n const import_meta = {};\n console.log(import_meta, import_meta.foo);\n})();\n`) 6079 }, 6080 6081 async defineIdentifierVsStringWarningIssue466Transform({ esbuild }) { 6082 const { warnings } = await esbuild.transform(``, { define: { 'process.env.NODE_ENV': 'production' } }) 6083 const formatted = await esbuild.formatMessages(warnings, { kind: 'warning' }) 6084 assert.strictEqual(formatted[0], 6085 `▲ [WARNING] "process.env.NODE_ENV" is defined as an identifier instead of a string (surround "production" with quotes to get a string) [suspicious-define] 6086 6087 <js>:1:34: 6088 1 │ define: { 'process.env.NODE_ENV': 'production' } 6089 │ ~~~~~~~~~~~~ 6090 ╵ '"production"' 6091 6092 `) 6093 }, 6094 6095 async defineIdentifierVsStringWarningIssue466Build({ esbuild }) { 6096 const { warnings } = await esbuild.build({ define: { 'process.env.NODE_ENV': 'production' }, logLevel: 'silent' }) 6097 const formatted = await esbuild.formatMessages(warnings, { kind: 'warning' }) 6098 assert.strictEqual(formatted[0], 6099 `▲ [WARNING] "process.env.NODE_ENV" is defined as an identifier instead of a string (surround "production" with quotes to get a string) [suspicious-define] 6100 6101 <js>:1:34: 6102 1 │ define: { 'process.env.NODE_ENV': 'production' } 6103 │ ~~~~~~~~~~~~ 6104 ╵ '"production"' 6105 6106 `) 6107 }, 6108 6109 async json({ esbuild }) { 6110 const { code } = await esbuild.transform(`{ "x": "y" }`, { loader: 'json' }) 6111 assert.strictEqual(code, `module.exports = { x: "y" };\n`) 6112 }, 6113 6114 async jsonMinified({ esbuild }) { 6115 const { code } = await esbuild.transform(`{ "x": "y" }`, { loader: 'json', minify: true }) 6116 const module = {} 6117 new Function('module', code)(module) 6118 assert.deepStrictEqual(module.exports, { x: 'y' }) 6119 }, 6120 6121 async jsonESM({ esbuild }) { 6122 const { code } = await esbuild.transform(`{ "x": "y" }`, { loader: 'json', format: 'esm' }) 6123 assert.strictEqual(code, `var x = "y";\nvar stdin_default = { x };\nexport {\n stdin_default as default,\n x\n};\n`) 6124 }, 6125 6126 async jsonInvalidIdentifierStart({ esbuild }) { 6127 // This character is a valid "ID_Continue" but not a valid "ID_Start" so it must be quoted 6128 const { code } = await esbuild.transform(`{ "\\uD835\\uDFCE": "y" }`, { loader: 'json' }) 6129 assert.strictEqual(code, `module.exports = { "\\u{1D7CE}": "y" };\n`) 6130 }, 6131 6132 async text({ esbuild }) { 6133 const { code } = await esbuild.transform(`This is some text`, { loader: 'text' }) 6134 assert.strictEqual(code, `module.exports = "This is some text";\n`) 6135 }, 6136 6137 async textESM({ esbuild }) { 6138 const { code } = await esbuild.transform(`This is some text`, { loader: 'text', format: 'esm' }) 6139 assert.strictEqual(code, `var stdin_default = "This is some text";\nexport {\n stdin_default as default\n};\n`) 6140 }, 6141 6142 async base64({ esbuild }) { 6143 const { code } = await esbuild.transform(`\x00\x01\x02`, { loader: 'base64' }) 6144 assert.strictEqual(code, `module.exports = "AAEC";\n`) 6145 }, 6146 6147 async dataurl({ esbuild }) { 6148 const { code: code1 } = await esbuild.transform(`\x00\x01\x02`, { loader: 'dataurl' }) 6149 assert.strictEqual(code1, `module.exports = "data:application/octet-stream,%00%01%02";\n`) 6150 6151 const { code: code2 } = await esbuild.transform(`\xFD\xFE\xFF`, { loader: 'dataurl' }) 6152 assert.strictEqual(code2, `module.exports = "data:text/plain;charset=utf-8,\\xFD\\xFE\\xFF";\n`) 6153 6154 const { code: code3 } = await esbuild.transform(new Uint8Array([0xFD, 0xFE, 0xFF]), { loader: 'dataurl' }) 6155 assert.strictEqual(code3, `module.exports = "data:text/plain;charset=utf-8;base64,/f7/";\n`) 6156 }, 6157 6158 async sourceMapTrueWithName({ esbuild }) { 6159 const { code, map } = await esbuild.transform(`let x`, { sourcemap: true, sourcefile: 'afile.js' }) 6160 assert.strictEqual(code, `let x;\n`) 6161 await assertSourceMap(map, 'afile.js') 6162 }, 6163 6164 async sourceMapLinkedWithName({ esbuild }) { 6165 try { 6166 await esbuild.transform(`let x`, { sourcemap: 'linked', sourcefile: 'afile.js' }) 6167 throw new Error('Expected a transform failure') 6168 } catch (e) { 6169 assert.strictEqual(e + '', `Error: Transform failed with 1 error:\nerror: Cannot transform with linked source maps`) 6170 } 6171 }, 6172 6173 async sourceMapExternalWithName({ esbuild }) { 6174 const { code, map } = await esbuild.transform(`let x`, { sourcemap: 'external', sourcefile: 'afile.js' }) 6175 assert.strictEqual(code, `let x;\n`) 6176 await assertSourceMap(map, 'afile.js') 6177 }, 6178 6179 async sourceMapInlineWithName({ esbuild }) { 6180 const { code, map } = await esbuild.transform(`let x`, { sourcemap: 'inline', sourcefile: 'afile.js' }) 6181 assert(code.startsWith(`let x;\n//# sourceMappingURL=`)) 6182 assert.strictEqual(map, '') 6183 const base64 = code.slice(code.indexOf('base64,') + 'base64,'.length) 6184 await assertSourceMap(Buffer.from(base64.trim(), 'base64').toString(), 'afile.js') 6185 }, 6186 6187 async sourceMapBothWithName({ esbuild }) { 6188 const { code, map } = await esbuild.transform(`let x`, { sourcemap: 'both', sourcefile: 'afile.js' }) 6189 assert(code.startsWith(`let x;\n//# sourceMappingURL=`)) 6190 await assertSourceMap(map, 'afile.js') 6191 const base64 = code.slice(code.indexOf('base64,') + 'base64,'.length) 6192 await assertSourceMap(Buffer.from(base64.trim(), 'base64').toString(), 'afile.js') 6193 }, 6194 6195 async sourceMapRoot({ esbuild }) { 6196 const { code, map } = await esbuild.transform(`let x`, { sourcemap: true, sourcefile: 'afile.js', sourceRoot: "https://example.com/" }) 6197 assert.strictEqual(code, `let x;\n`) 6198 assert.strictEqual(JSON.parse(map).sourceRoot, 'https://example.com/'); 6199 }, 6200 6201 async numericLiteralPrinting({ esbuild }) { 6202 async function checkLiteral(text) { 6203 const { code } = await esbuild.transform(`return ${text}`, { minify: true }) 6204 assert.strictEqual(+text, new Function(code)()) 6205 } 6206 const promises = [] 6207 for (let i = 0; i < 10; i++) { 6208 for (let j = 0; j < 10; j++) { 6209 promises.push(checkLiteral(`0.${'0'.repeat(i)}${'123456789'.slice(0, j)}`)) 6210 promises.push(checkLiteral(`1${'0'.repeat(i)}.${'123456789'.slice(0, j)}`)) 6211 promises.push(checkLiteral(`1${'123456789'.slice(0, j)}${'0'.repeat(i)}`)) 6212 } 6213 } 6214 await Promise.all(promises) 6215 }, 6216 6217 async tryCatchScopeMerge({ esbuild }) { 6218 const code = ` 6219 var x = 1 6220 if (x !== 1) throw 'fail' 6221 try { 6222 throw 2 6223 } catch (x) { 6224 if (x !== 2) throw 'fail' 6225 { 6226 if (x !== 2) throw 'fail' 6227 var x = 3 6228 if (x !== 3) throw 'fail' 6229 } 6230 if (x !== 3) throw 'fail' 6231 } 6232 if (x !== 1) throw 'fail' 6233 `; 6234 new Function(code)(); // Verify that the code itself is correct 6235 new Function((await esbuild.transform(code)).code)(); 6236 }, 6237 6238 async nestedFunctionHoist({ esbuild }) { 6239 const code = ` 6240 if (x !== void 0) throw 'fail' 6241 { 6242 if (x !== void 0) throw 'fail' 6243 { 6244 x() 6245 function x() {} 6246 x() 6247 } 6248 x() 6249 } 6250 x() 6251 `; 6252 new Function(code)(); // Verify that the code itself is correct 6253 new Function((await esbuild.transform(code)).code)(); 6254 }, 6255 6256 async nestedFunctionHoistBefore({ esbuild }) { 6257 const code = ` 6258 var x = 1 6259 if (x !== 1) throw 'fail' 6260 { 6261 if (x !== 1) throw 'fail' 6262 { 6263 x() 6264 function x() {} 6265 x() 6266 } 6267 x() 6268 } 6269 x() 6270 `; 6271 new Function(code)(); // Verify that the code itself is correct 6272 new Function((await esbuild.transform(code)).code)(); 6273 }, 6274 6275 async nestedFunctionHoistAfter({ esbuild }) { 6276 const code = ` 6277 if (x !== void 0) throw 'fail' 6278 { 6279 if (x !== void 0) throw 'fail' 6280 { 6281 x() 6282 function x() {} 6283 x() 6284 } 6285 x() 6286 } 6287 x() 6288 var x = 1 6289 `; 6290 new Function(code)(); // Verify that the code itself is correct 6291 new Function((await esbuild.transform(code)).code)(); 6292 }, 6293 6294 async nestedFunctionShadowBefore({ esbuild }) { 6295 const code = ` 6296 let x = 1 6297 if (x !== 1) throw 'fail' 6298 { 6299 if (x !== 1) throw 'fail' 6300 { 6301 x() 6302 function x() {} 6303 x() 6304 } 6305 if (x !== 1) throw 'fail' 6306 } 6307 if (x !== 1) throw 'fail' 6308 `; 6309 new Function(code)(); // Verify that the code itself is correct 6310 new Function((await esbuild.transform(code)).code)(); 6311 }, 6312 6313 async nestedFunctionShadowAfter({ esbuild }) { 6314 const code = ` 6315 try { x; throw 'fail' } catch (e) { if (!(e instanceof ReferenceError)) throw e } 6316 { 6317 try { x; throw 'fail' } catch (e) { if (!(e instanceof ReferenceError)) throw e } 6318 { 6319 x() 6320 function x() {} 6321 x() 6322 } 6323 try { x; throw 'fail' } catch (e) { if (!(e instanceof ReferenceError)) throw e } 6324 } 6325 try { x; throw 'fail' } catch (e) { if (!(e instanceof ReferenceError)) throw e } 6326 let x = 1 6327 `; 6328 new Function(code)(); // Verify that the code itself is correct 6329 new Function((await esbuild.transform(code)).code)(); 6330 }, 6331 6332 async sourceMapControlCharacterEscapes({ esbuild }) { 6333 let chars = '' 6334 for (let i = 0; i < 32; i++) chars += String.fromCharCode(i); 6335 const input = `return \`${chars}\``; 6336 const { code, map } = await esbuild.transform(input, { sourcemap: true, sourcefile: 'afile.code' }) 6337 const fn = new Function(code) 6338 assert.strictEqual(fn(), chars.replace('\r', '\n')) 6339 const json = JSON.parse(map) 6340 assert.strictEqual(json.version, 3) 6341 assert.strictEqual(json.sourcesContent.length, 1) 6342 assert.strictEqual(json.sourcesContent[0], input) 6343 }, 6344 6345 async transformLegalCommentsJS({ esbuild }) { 6346 assert.strictEqual((await esbuild.transform(`//!x\ny()`, { legalComments: 'none' })).code, `y();\n`) 6347 assert.strictEqual((await esbuild.transform(`//!x\ny()`, { legalComments: 'inline' })).code, `//!x\ny();\n`) 6348 6349 const eofResult = await esbuild.transform(`//!x\ny()`, { legalComments: 'eof' }) 6350 assert.strictEqual(eofResult.code, `y();\n//!x\n`) 6351 assert.strictEqual(eofResult.legalComments, undefined) 6352 6353 const externalResult = await esbuild.transform(`//!x\ny()`, { legalComments: 'external' }) 6354 assert.strictEqual(externalResult.code, `y();\n`) 6355 assert.strictEqual(externalResult.legalComments, `//!x\n`) 6356 6357 try { 6358 await esbuild.transform(``, { legalComments: 'linked' }) 6359 throw new Error('Expected a transform failure') 6360 } catch (e) { 6361 if (!e || !e.errors || !e.errors[0] || e.errors[0].text !== 'Cannot transform with linked legal comments') 6362 throw e 6363 } 6364 }, 6365 6366 async transformLegalCommentsCSS({ esbuild }) { 6367 assert.strictEqual((await esbuild.transform(`/*!x*/\ny{}`, { loader: 'css', legalComments: 'none' })).code, `y {\n}\n`) 6368 assert.strictEqual((await esbuild.transform(`/*!x*/\ny{}`, { loader: 'css', legalComments: 'inline' })).code, `/*!x*/\ny {\n}\n`) 6369 6370 const eofResult = await esbuild.transform(`/*!x*/\ny{}`, { loader: 'css', legalComments: 'eof' }) 6371 assert.strictEqual(eofResult.code, `y {\n}\n/*!x*/\n`) 6372 assert.strictEqual(eofResult.legalComments, undefined) 6373 6374 const externalResult = await esbuild.transform(`/*!x*/\ny{}`, { loader: 'css', legalComments: 'external' }) 6375 assert.strictEqual(externalResult.code, `y {\n}\n`) 6376 assert.strictEqual(externalResult.legalComments, `/*!x*/\n`) 6377 6378 try { 6379 await esbuild.transform(``, { legalComments: 'linked' }) 6380 throw new Error('Expected a transform failure') 6381 } catch (e) { 6382 if (!e || !e.errors || !e.errors[0] || e.errors[0].text !== 'Cannot transform with linked legal comments') 6383 throw e 6384 } 6385 }, 6386 6387 async tsDecorators({ esbuild }) { 6388 const { code } = await esbuild.transform(` 6389 let observed = []; 6390 let on = key => (...args) => { 6391 observed.push({ key, args }); 6392 }; 6393 6394 @on('class') 6395 class Foo { 6396 @on('field') field; 6397 @on('method') method() { } 6398 @on('staticField') static staticField; 6399 @on('staticMethod') static staticMethod() { } 6400 fn(@on('param') x) { } 6401 static staticFn(@on('staticParam') x) { } 6402 } 6403 6404 // This is what the TypeScript compiler itself generates 6405 let expected = [ 6406 { key: 'field', args: [Foo.prototype, 'field', undefined] }, 6407 { key: 'method', args: [Foo.prototype, 'method', { value: Foo.prototype.method, writable: true, enumerable: false, configurable: true }] }, 6408 { key: 'param', args: [Foo.prototype, 'fn', 0] }, 6409 { key: 'staticField', args: [Foo, 'staticField', undefined] }, 6410 { key: 'staticMethod', args: [Foo, 'staticMethod', { value: Foo.staticMethod, writable: true, enumerable: false, configurable: true }] }, 6411 { key: 'staticParam', args: [Foo, 'staticFn', 0] }, 6412 { key: 'class', args: [Foo] } 6413 ]; 6414 6415 return {observed, expected}; 6416 `, { 6417 loader: 'ts', 6418 tsconfigRaw: { 6419 compilerOptions: { 6420 experimentalDecorators: true, 6421 }, 6422 }, 6423 }); 6424 const { observed, expected } = new Function(code)(); 6425 assert.deepStrictEqual(observed, expected); 6426 }, 6427 6428 async pureCallPrint({ esbuild }) { 6429 const { code: code1 } = await esbuild.transform(`print(123, foo)`, { minifySyntax: true, pure: [] }) 6430 assert.strictEqual(code1, `print(123, foo);\n`) 6431 6432 const { code: code2 } = await esbuild.transform(`print(123, foo)`, { minifySyntax: true, pure: ['print'] }) 6433 assert.strictEqual(code2, `foo;\n`) 6434 }, 6435 6436 async pureCallConsoleLog({ esbuild }) { 6437 const { code: code1 } = await esbuild.transform(`console.log(123, foo)`, { minifySyntax: true, pure: [] }) 6438 assert.strictEqual(code1, `console.log(123, foo);\n`) 6439 6440 const { code: code2 } = await esbuild.transform(`console.log(123, foo)`, { minifySyntax: true, pure: ['console.log'] }) 6441 assert.strictEqual(code2, `foo;\n`) 6442 }, 6443 6444 async nameCollisionEvalRename({ esbuild }) { 6445 const { code } = await esbuild.transform(` 6446 // "arg" must not be renamed to "arg2" 6447 return function(arg2) { 6448 function foo(arg) { 6449 return arg + arg2; 6450 } 6451 // "eval" prevents "arg2" from being renamed 6452 // "arg" below causes "arg" above to be renamed 6453 return eval(foo(1)) + arg 6454 }(2); 6455 `) 6456 const result = new Function('arg', code)(10) 6457 assert.strictEqual(result, 13) 6458 }, 6459 6460 async nameCollisionEvalMinify({ esbuild }) { 6461 const { code } = await esbuild.transform(` 6462 // "arg" must not be renamed to "$" 6463 return function($) { 6464 function foo(arg) { 6465 return arg + $; 6466 } 6467 // "eval" prevents "$" from being renamed 6468 // Repeated "$" puts "$" at the top of the character frequency histogram 6469 return eval(foo($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$)) 6470 }(2); 6471 `, { minifyIdentifiers: true }) 6472 const result = new Function('$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$', code)(1) 6473 assert.strictEqual(result, 3) 6474 }, 6475 6476 async singleUseExpressionSubstitution({ esbuild }) { 6477 function run(code) { 6478 try { 6479 return JSON.stringify(new Function(code)()) 6480 } catch (error) { 6481 return error + '' 6482 } 6483 } 6484 let bugs = '' 6485 for (let input of [ 6486 `let fn = () => { throw new Error }; let x = undef; return fn() + x`, 6487 `let fn = () => { throw new Error }; let x = fn(); return undef + x`, 6488 6489 `let fn = () => arg0 = 0; let x = fn(); return arg0 + x`, 6490 `let fn = () => arg0 = 0; let x = fn(); return arg0 = x`, 6491 `let fn = () => arg0 = 0; let x = fn(); return arg0 += x`, 6492 `let fn = () => arg0 = 0; let x = fn(); return arg0 ||= x`, 6493 `let fn = () => arg0 = 0; let x = fn(); return arg0 &&= x`, 6494 6495 `let fn = () => arg0 = 0; let obj = [1]; let x = arg0; return obj[fn()] + x`, 6496 `let fn = () => arg0 = 0; let obj = [1]; let x = arg0; return obj[fn()] = x`, 6497 `let fn = () => arg0 = 0; let obj = [1]; let x = arg0; return obj[fn()] += x`, 6498 `let fn = () => arg0 = 0; let obj = [1]; let x = arg0; return obj[fn()] ||= x`, 6499 `let fn = () => arg0 = 0; let obj = [1]; let x = arg0; return obj[fn()] &&= x`, 6500 6501 `let obj = { get y() { arg0 = 0; return 1 } }; let x = obj.y; return arg0 + x`, 6502 `let obj = { get y() { arg0 = 0; return 1 } }; let x = arg0; return obj.y + x`, 6503 6504 `let x = undef; return arg0 || x`, 6505 `let x = undef; return arg0 && x`, 6506 `let x = undef; return arg0 ? x : 1`, 6507 `let x = undef; return arg0 ? 1 : x`, 6508 6509 `let fn = () => { throw new Error }; let x = fn(); return arg0 || x`, 6510 `let fn = () => { throw new Error }; let x = fn(); return arg0 && x`, 6511 `let fn = () => { throw new Error }; let x = fn(); return arg0 ? x : 1`, 6512 `let fn = () => { throw new Error }; let x = fn(); return arg0 ? 1 : x`, 6513 ]) { 6514 input = `function f(arg0) { ${input} } return f(123)` 6515 const { code: minified } = await esbuild.transform(input, { minify: true }) 6516 if (run(input) !== run(minified)) bugs += '\n ' + input 6517 } 6518 if (bugs !== '') throw new Error('Single-use expression substitution bugs:' + bugs) 6519 }, 6520 6521 async platformNode({ esbuild }) { 6522 const { code } = await esbuild.transform(`export let foo = 123`, { format: 'cjs', platform: 'node' }) 6523 assert(code.slice(code.indexOf('let foo')), `let foo = 123; 6524 // Annotate the CommonJS export names for ESM import in node: 6525 0 && (module.exports = { 6526 foo 6527 }); 6528 `) 6529 }, 6530 6531 async dynamicImportString({ esbuild }) { 6532 const { code } = await esbuild.transform(`import('foo')`, { target: 'chrome63' }) 6533 assert.strictEqual(code, `import("foo");\n`) 6534 }, 6535 6536 async dynamicImportStringES6({ esbuild }) { 6537 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6538 const { code } = await esbuild.transform(`import('foo')`, { target: 'chrome62' }) 6539 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(() => __toESM(require("foo")));\n`) 6540 }, 6541 6542 async dynamicImportStringES5({ esbuild }) { 6543 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6544 const { code } = await esbuild.transform(`import('foo')`, { target: 'chrome48' }) 6545 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(function() {\n return __toESM(require("foo"));\n});\n`) 6546 }, 6547 6548 async dynamicImportStringES5Minify({ esbuild }) { 6549 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6550 const { code } = await esbuild.transform(`import('foo')`, { target: 'chrome48', minifyWhitespace: true }) 6551 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(function(){return __toESM(require("foo"))});\n`) 6552 }, 6553 6554 async dynamicImportStringNode12_19({ esbuild }) { 6555 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6556 const { code } = await esbuild.transform(`import('foo')`, { target: 'node12.19' }) 6557 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(() => __toESM(require("foo")));\n`) 6558 }, 6559 6560 async dynamicImportStringNode12_20({ esbuild }) { 6561 const { code } = await esbuild.transform(`import('foo')`, { target: 'node12.20' }) 6562 assert.strictEqual(code, `import("foo");\n`) 6563 }, 6564 6565 async dynamicImportStringNode13({ esbuild }) { 6566 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6567 const { code } = await esbuild.transform(`import('foo')`, { target: 'node13' }) 6568 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(() => __toESM(require("foo")));\n`) 6569 }, 6570 6571 async dynamicImportStringNode13_1({ esbuild }) { 6572 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6573 const { code } = await esbuild.transform(`import('foo')`, { target: 'node13.1' }) 6574 assert.strictEqual(fromPromiseResolve(code), `Promise.resolve().then(() => __toESM(require("foo")));\n`) 6575 }, 6576 6577 async dynamicImportStringNode13_2({ esbuild }) { 6578 const { code } = await esbuild.transform(`import('foo')`, { target: 'node13.2' }) 6579 assert.strictEqual(code, `import("foo");\n`) 6580 }, 6581 6582 async dynamicImportExpression({ esbuild }) { 6583 const { code } = await esbuild.transform(`import(foo)`, { target: 'chrome63' }) 6584 assert.strictEqual(code, `import(foo);\n`) 6585 }, 6586 6587 async dynamicImportExpressionES6({ esbuild }) { 6588 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6589 const { code: code2 } = await esbuild.transform(`import(foo)`, { target: 'chrome62' }) 6590 assert.strictEqual(fromPromiseResolve(code2), `Promise.resolve().then(() => __toESM(require(foo)));\n`) 6591 }, 6592 6593 async dynamicImportExpressionES5({ esbuild }) { 6594 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6595 const { code: code3 } = await esbuild.transform(`import(foo)`, { target: 'chrome48' }) 6596 assert.strictEqual(fromPromiseResolve(code3), `Promise.resolve().then(function() {\n return __toESM(require(foo));\n});\n`) 6597 }, 6598 6599 async dynamicImportExpressionES5Minify({ esbuild }) { 6600 const fromPromiseResolve = text => text.slice(text.indexOf('Promise.resolve')) 6601 const { code: code4 } = await esbuild.transform(`import(foo)`, { target: 'chrome48', minifyWhitespace: true }) 6602 assert.strictEqual(fromPromiseResolve(code4), `Promise.resolve().then(function(){return __toESM(require(foo))});\n`) 6603 }, 6604 6605 async classStaticBlocks({ esbuild }) { 6606 // Check that empty static class blocks are still lowered (this was a regression in version 0.18.2) 6607 assert.strictEqual((await esbuild.transform(`class Foo { static {} }`, { supported: { 'class-static-blocks': false } })).code, `class Foo {\n}\n`) 6608 assert.strictEqual((await esbuild.transform(`class Foo { static { x } }`, { supported: { 'class-static-blocks': false } })).code, `class Foo {\n}\nx;\n`) 6609 }, 6610 6611 async keepNamesUnsupported({ esbuild }) { 6612 try { 6613 await esbuild.transform(``, { keepNames: true, target: 'chrome36' }) 6614 throw new Error('Expected an error to be thrown') 6615 } catch (e) { 6616 assert.strictEqual(e.errors[0].text, 6617 'The "keep names" setting cannot be used with the configured target environment ("chrome36")') 6618 } 6619 6620 try { 6621 await esbuild.transform(``, { keepNames: true, target: 'chrome46', supported: { 'function-name-configurable': false } }) 6622 throw new Error('Expected an error to be thrown') 6623 } catch (e) { 6624 assert.strictEqual(e.errors[0].text, 6625 'The "keep names" setting cannot be used with the configured target environment ("chrome46" + 1 override)') 6626 } 6627 6628 await esbuild.transform(``, { keepNames: true, target: 'chrome46' }) 6629 }, 6630 6631 async inlineScript({ esbuild }) { 6632 let p 6633 assert.strictEqual((await esbuild.transform(`x = '</script>'`, {})).code, `x = "<\\/script>";\n`) 6634 assert.strictEqual((await esbuild.transform(`x = '</script> inline'`, { supported: { 'inline-script': true } })).code, `x = "<\\/script> inline";\n`) 6635 assert.strictEqual((await esbuild.transform(`x = '</script> noinline'`, { supported: { 'inline-script': false } })).code, `x = "</script> noinline";\n`) 6636 6637 p = { platform: 'browser' } 6638 assert.strictEqual((await esbuild.transform(`x = '</script> browser'`, { ...p })).code, `x = "<\\/script> browser";\n`) 6639 assert.strictEqual((await esbuild.transform(`x = '</script> browser inline'`, { ...p, supported: { 'inline-script': true } })).code, `x = "<\\/script> browser inline";\n`) 6640 assert.strictEqual((await esbuild.transform(`x = '</script> browser noinline'`, { ...p, supported: { 'inline-script': false } })).code, `x = "</script> browser noinline";\n`) 6641 6642 p = { platform: 'node' } 6643 assert.strictEqual((await esbuild.transform(`x = '</script> node'`, { ...p })).code, `x = "</script> node";\n`) 6644 assert.strictEqual((await esbuild.transform(`x = '</script> node inline'`, { ...p, supported: { 'inline-script': true } })).code, `x = "<\\/script> node inline";\n`) 6645 assert.strictEqual((await esbuild.transform(`x = '</script> node noinline'`, { ...p, supported: { 'inline-script': false } })).code, `x = "</script> node noinline";\n`) 6646 6647 p = { platform: 'neutral' } 6648 assert.strictEqual((await esbuild.transform(`x = '</script> neutral'`, { ...p })).code, `x = "</script> neutral";\n`) 6649 assert.strictEqual((await esbuild.transform(`x = '</script> neutral inline'`, { ...p, supported: { 'inline-script': true } })).code, `x = "<\\/script> neutral inline";\n`) 6650 assert.strictEqual((await esbuild.transform(`x = '</script> neutral noinline'`, { ...p, supported: { 'inline-script': false } })).code, `x = "</script> neutral noinline";\n`) 6651 6652 assert.strictEqual((await esbuild.transform(`x = '</script>'`, { target: 'esnext' })).code, `x = "<\\/script>";\n`) 6653 assert.strictEqual((await esbuild.transform(`x = '</script>'`, { target: 'es2020' })).code, `x = "<\\/script>";\n`) 6654 assert.strictEqual((await esbuild.transform(`x = '</script>'`, { target: 'es6' })).code, `x = "<\\/script>";\n`) 6655 assert.strictEqual((await esbuild.transform(`x = '</script>'`, { target: 'chrome999' })).code, `x = "<\\/script>";\n`) 6656 assert.strictEqual((await esbuild.transform(`x = '</script>'`, { target: 'chrome0' })).code, `x = "<\\/script>";\n`) 6657 }, 6658 6659 async inlineStyle({ esbuild }) { 6660 let p = { loader: 'css' } 6661 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p })).code, `x {\n y: "<\\/style>";\n}\n`) 6662 assert.strictEqual((await esbuild.transform(`x { y: '</style> inline' }`, { ...p, supported: { 'inline-style': true } })).code, `x {\n y: "<\\/style> inline";\n}\n`) 6663 assert.strictEqual((await esbuild.transform(`x { y: '</style> noinline' }`, { ...p, supported: { 'inline-style': false } })).code, `x {\n y: "</style> noinline";\n}\n`) 6664 6665 p = { loader: 'css', platform: 'browser' } 6666 assert.strictEqual((await esbuild.transform(`x { y: '</style> browser' }`, { ...p })).code, `x {\n y: "<\\/style> browser";\n}\n`) 6667 assert.strictEqual((await esbuild.transform(`x { y: '</style> browser inline' }`, { ...p, supported: { 'inline-style': true } })).code, `x {\n y: "<\\/style> browser inline";\n}\n`) 6668 assert.strictEqual((await esbuild.transform(`x { y: '</style> browser noinline' }`, { ...p, supported: { 'inline-style': false } })).code, `x {\n y: "</style> browser noinline";\n}\n`) 6669 6670 p = { loader: 'css', platform: 'node' } 6671 assert.strictEqual((await esbuild.transform(`x { y: '</style> node' }`, { ...p })).code, `x {\n y: "</style> node";\n}\n`) 6672 assert.strictEqual((await esbuild.transform(`x { y: '</style> node inline' }`, { ...p, supported: { 'inline-style': true } })).code, `x {\n y: "<\\/style> node inline";\n}\n`) 6673 assert.strictEqual((await esbuild.transform(`x { y: '</style> node noinline' }`, { ...p, supported: { 'inline-style': false } })).code, `x {\n y: "</style> node noinline";\n}\n`) 6674 6675 p = { loader: 'css', platform: 'neutral' } 6676 assert.strictEqual((await esbuild.transform(`x { y: '</style> neutral' }`, { ...p })).code, `x {\n y: "</style> neutral";\n}\n`) 6677 assert.strictEqual((await esbuild.transform(`x { y: '</style> neutral inline' }`, { ...p, supported: { 'inline-style': true } })).code, `x {\n y: "<\\/style> neutral inline";\n}\n`) 6678 assert.strictEqual((await esbuild.transform(`x { y: '</style> neutral noinline' }`, { ...p, supported: { 'inline-style': false } })).code, `x {\n y: "</style> neutral noinline";\n}\n`) 6679 6680 p = { loader: 'css' } 6681 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p, target: 'esnext' })).code, `x {\n y: "<\\/style>";\n}\n`) 6682 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p, target: 'es2020' })).code, `x {\n y: "<\\/style>";\n}\n`) 6683 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p, target: 'es6' })).code, `x {\n y: "<\\/style>";\n}\n`) 6684 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p, target: 'chrome999' })).code, `x {\n y: "<\\/style>";\n}\n`) 6685 assert.strictEqual((await esbuild.transform(`x { y: '</style>' }`, { ...p, target: 'chrome0' })).code, `x {\n y: "<\\/style>";\n}\n`) 6686 }, 6687 6688 async typeofEqualsUndefinedTarget({ esbuild }) { 6689 assert.strictEqual((await esbuild.transform(`a = typeof b !== 'undefined'`, { minify: true })).code, `a=typeof b<"u";\n`) 6690 assert.strictEqual((await esbuild.transform(`a = typeof b !== 'undefined'`, { minify: true, target: 'es2020' })).code, `a=typeof b<"u";\n`) 6691 assert.strictEqual((await esbuild.transform(`a = typeof b !== 'undefined'`, { minify: true, target: 'chrome11' })).code, `a=typeof b<"u";\n`) 6692 6693 assert.strictEqual((await esbuild.transform(`a = typeof b !== 'undefined'`, { minify: true, target: 'es2019' })).code, `a=typeof b!="undefined";\n`) 6694 assert.strictEqual((await esbuild.transform(`a = typeof b !== 'undefined'`, { minify: true, target: 'ie11' })).code, `a=typeof b!="undefined";\n`) 6695 }, 6696 6697 async caseInsensitiveTarget({ esbuild }) { 6698 assert.strictEqual((await esbuild.transform(`a ||= b`, { target: 'eS5' })).code, `a || (a = b);\n`) 6699 assert.strictEqual((await esbuild.transform(`a ||= b`, { target: 'eSnExT' })).code, `a ||= b;\n`) 6700 }, 6701 6702 async propertyAccessBugWorkaroundForWebKit({ esbuild }) { 6703 const check = async (target, input, expected) => 6704 assert.strictEqual((await esbuild.transform(input, { target })).code, expected) 6705 await Promise.all([ 6706 check('safari16.2', `x(class{}.y=z)`, `x((class {\n}).y = z);\n`), 6707 check('safari16.3', `x(class{}.y=z)`, `x(class {\n}.y = z);\n`), 6708 6709 check('safari16.2', `x(function(){}.y=z)`, `x((function() {\n}).y = z);\n`), 6710 check('safari16.3', `x(function(){}.y=z)`, `x(function() {\n}.y = z);\n`), 6711 6712 check('safari16.2', `x(function*(){}.y=z)`, `x((function* () {\n}).y = z);\n`), 6713 check('safari16.3', `x(function*(){}.y=z)`, `x(function* () {\n}.y = z);\n`), 6714 6715 check('safari16.2', `x(async function(){}.y=z)`, `x((async function() {\n}).y = z);\n`), 6716 check('safari16.3', `x(async function(){}.y=z)`, `x(async function() {\n}.y = z);\n`), 6717 6718 check('safari16.2', `x(async function*(){}.y=z)`, `x((async function* () {\n}).y = z);\n`), 6719 check('safari16.3', `x(async function*(){}.y=z)`, `x(async function* () {\n}.y = z);\n`), 6720 6721 // This should not be enabled by default 6722 check('esnext', `x(class{}.y=z)`, `x(class {\n}.y = z);\n`), 6723 6724 // This doesn't need to be applied for methods in object literals 6725 check('safari16.2', `x({f(a=-1){}}.y=z)`, `x({ f(a = -1) {\n} }.y = z);\n`), 6726 ]) 6727 }, 6728 6729 async multipleEngineTargets({ esbuild }) { 6730 const check = async (target, expected) => 6731 assert.strictEqual((await esbuild.transform(`foo(a ?? b)`, { target })).code, expected) 6732 await Promise.all([ 6733 check('es2020', `foo(a ?? b);\n`), 6734 check('es2019', `foo(a != null ? a : b);\n`), 6735 6736 check('chrome80', `foo(a ?? b);\n`), 6737 check('chrome79', `foo(a != null ? a : b);\n`), 6738 6739 check(['es2020', 'chrome80'], `foo(a ?? b);\n`), 6740 check(['es2020', 'chrome79'], `foo(a != null ? a : b);\n`), 6741 check(['es2019', 'chrome80'], `foo(a != null ? a : b);\n`), 6742 ]) 6743 }, 6744 6745 async multipleEngineTargetsNotSupported({ esbuild }) { 6746 try { 6747 await esbuild.transform(`0n`, { target: ['es5', 'chrome1', 'safari2', 'firefox3'] }) 6748 throw new Error('Expected an error to be thrown') 6749 } catch (e) { 6750 assert.strictEqual(e.errors[0].text, 6751 'Big integer literals are not available in the configured target environment ("chrome1", "es5", "firefox3", "safari2")') 6752 } 6753 }, 6754 6755 async supported({ esbuild }) { 6756 const check = async (options, input, expected) => { 6757 try { 6758 assert.strictEqual((await esbuild.transform(input, options)).code, expected) 6759 } catch (e) { 6760 if (e.errors) assert.strictEqual(e.errors[0].text, expected) 6761 else throw e 6762 } 6763 } 6764 6765 await Promise.all([ 6766 // JS: lower 6767 check({ supported: { arrow: true } }, `x = () => y`, `x = () => y;\n`), 6768 check({ supported: { arrow: false } }, `x = () => y`, `x = function() {\n return y;\n};\n`), 6769 check({ supported: { arrow: true }, target: 'es5' }, `x = () => y`, `x = () => y;\n`), 6770 check({ supported: { arrow: false }, target: 'es5' }, `x = () => y`, `x = function() {\n return y;\n};\n`), 6771 check({ supported: { arrow: true }, target: 'es2022' }, `x = () => y`, `x = () => y;\n`), 6772 check({ supported: { arrow: false }, target: 'es2022' }, `x = () => y`, `x = function() {\n return y;\n};\n`), 6773 6774 // JS: error 6775 check({ supported: { bigint: true } }, `x = 1n`, `x = 1n;\n`), 6776 check({ supported: { bigint: false } }, `x = 1n`, `Big integer literals are not available in the configured target environment`), 6777 check({ supported: { bigint: true }, target: 'es5' }, `x = 1n`, `x = 1n;\n`), 6778 check({ supported: { bigint: false }, target: 'es5' }, `x = 1n`, `Big integer literals are not available in the configured target environment ("es5" + 1 override)`), 6779 check({ supported: { bigint: true }, target: 'es2022' }, `x = 1n`, `x = 1n;\n`), 6780 check({ supported: { bigint: false }, target: 'es2022' }, `x = 1n`, `Big integer literals are not available in the configured target environment ("es2022" + 1 override)`), 6781 6782 // CSS: lower 6783 check({ supported: { 'hex-rgba': true }, loader: 'css' }, `a { color: #1234 }`, `a {\n color: #1234;\n}\n`), 6784 check({ supported: { 'hex-rgba': false }, loader: 'css' }, `a { color: #1234 }`, `a {\n color: rgba(17, 34, 51, .267);\n}\n`), 6785 check({ target: 'safari15.3', loader: 'css' }, `a { mask-image: url(x.png) }`, `a {\n -webkit-mask-image: url(x.png);\n mask-image: url(x.png);\n}\n`), 6786 check({ target: 'safari15.4', loader: 'css' }, `a { mask-image: url(x.png) }`, `a {\n mask-image: url(x.png);\n}\n`), 6787 6788 // Check for "+ 2 overrides" 6789 check({ supported: { bigint: false, arrow: true }, target: 'es2022' }, `x = 1n`, `Big integer literals are not available in the configured target environment ("es2022" + 2 overrides)`), 6790 ]) 6791 }, 6792 6793 async regExpFeatures({ esbuild }) { 6794 const check = async (target, input, expected) => 6795 assert.strictEqual((await esbuild.transform(input, { target })).code, expected) 6796 6797 await Promise.all([ 6798 // RegExpStickyAndUnicodeFlags 6799 check('es6', `x1 = /./y`, `x1 = /./y;\n`), 6800 check('es6', `x2 = /./u`, `x2 = /./u;\n`), 6801 check('es5', `x3 = /./y`, `x3 = new RegExp(".", "y");\n`), 6802 check('es5', `x4 = /./u`, `x4 = new RegExp(".", "u");\n`), 6803 6804 // RegExpDotAllFlag 6805 check('es2018', `x1 = /a.b/s`, `x1 = /a.b/s;\n`), 6806 check('es2017', `x2 = /a.b/s`, `x2 = new RegExp("a.b", "s");\n`), 6807 6808 // RegExpLookbehindAssertions 6809 check('es2018', `x1 = /(?<=x)/`, `x1 = /(?<=x)/;\n`), 6810 check('es2018', `x2 = /(?<!x)/`, `x2 = /(?<!x)/;\n`), 6811 check('es2017', `x3 = /(?<=x)/`, `x3 = new RegExp("(?<=x)");\n`), 6812 check('es2017', `x4 = /(?<!x)/`, `x4 = new RegExp("(?<!x)");\n`), 6813 6814 // RegExpNamedCaptureGroups 6815 check('es2018', `x1 = /(?<a>b)/`, `x1 = /(?<a>b)/;\n`), 6816 check('es2017', `x2 = /(?<a>b)/`, `x2 = new RegExp("(?<a>b)");\n`), 6817 6818 // RegExpUnicodePropertyEscapes 6819 check('es2018', `x1 = /\\p{Emoji}/u`, `x1 = /\\p{Emoji}/u;\n`), 6820 check('es2017', `x2 = /\\p{Emoji}/u`, `x2 = new RegExp("\\\\p{Emoji}", "u");\n`), 6821 6822 // RegExpMatchIndices 6823 check('es2022', `x1 = /y/d`, `x1 = /y/d;\n`), 6824 check('es2021', `x2 = /y/d`, `x2 = new RegExp("y", "d");\n`), 6825 6826 // RegExpSetNotation 6827 check('esnext', `x1 = /[\\p{White_Space}&&\\p{ASCII}]/v`, `x1 = /[\\p{White_Space}&&\\p{ASCII}]/v;\n`), 6828 check('es2022', `x2 = /[\\p{White_Space}&&\\p{ASCII}]/v`, `x2 = new RegExp("[\\\\p{White_Space}&&\\\\p{ASCII}]", "v");\n`), 6829 ]) 6830 }, 6831 6832 // Future syntax 6833 bigInt: ({ esbuild }) => futureSyntax(esbuild, '123n', 'es2019', 'es2020'), 6834 bigIntKey: ({ esbuild }) => futureSyntax(esbuild, '({123n: 0})', 'es2019', 'es2020'), 6835 bigIntPattern: ({ esbuild }) => futureSyntax(esbuild, 'let {123n: x} = y', 'es2019', 'es2020'), 6836 nonIdArrayRest: ({ esbuild }) => futureSyntax(esbuild, 'let [...[x]] = y', 'es2015', 'es2016'), 6837 topLevelAwait: ({ esbuild }) => futureSyntax(esbuild, 'await foo', 'es2020', 'esnext'), 6838 topLevelForAwait: ({ esbuild }) => futureSyntax(esbuild, 'for await (foo of bar) ;', 'es2020', 'esnext'), 6839 } 6840 6841 function registerClassPrivateTests(target) { 6842 let contents = ` 6843 class Field { #foo = 123; bar = this.#foo } 6844 if (new Field().bar !== 123) throw 'fail: field' 6845 6846 class Method { bar = this.#foo(); #foo() { return 123 } } 6847 if (new Method().bar !== 123) throw 'fail: method' 6848 6849 class Accessor { bar = this.#foo; get #foo() { return 123 } } 6850 if (new Accessor().bar !== 123) throw 'fail: accessor' 6851 6852 class StaticField { static #foo = 123; static bar = StaticField.#foo } 6853 if (StaticField.bar !== 123) throw 'fail: static field' 6854 6855 class StaticMethod { static bar = StaticMethod.#foo(); static #foo() { return 123 } } 6856 if (StaticMethod.bar !== 123) throw 'fail: static method' 6857 6858 class StaticAccessor { static bar = StaticAccessor.#foo; static get #foo() { return 123 } } 6859 if (StaticAccessor.bar !== 123) throw 'fail: static accessor' 6860 6861 class StaticFieldThis { static #foo = 123; static bar = this.#foo } 6862 if (StaticFieldThis.bar !== 123) throw 'fail: static field' 6863 6864 class StaticMethodThis { static bar = this.#foo(); static #foo() { return 123 } } 6865 if (StaticMethodThis.bar !== 123) throw 'fail: static method' 6866 6867 class StaticAccessorThis { static bar = this.#foo; static get #foo() { return 123 } } 6868 if (StaticAccessorThis.bar !== 123) throw 'fail: static accessor' 6869 6870 class FieldFromStatic { #foo = 123; static bar = new FieldFromStatic().#foo } 6871 if (FieldFromStatic.bar !== 123) throw 'fail: field from static' 6872 6873 class MethodFromStatic { static bar = new MethodFromStatic().#foo(); #foo() { return 123 } } 6874 if (MethodFromStatic.bar !== 123) throw 'fail: method from static' 6875 6876 class AccessorFromStatic { static bar = new AccessorFromStatic().#foo; get #foo() { return 123 } } 6877 if (AccessorFromStatic.bar !== 123) throw 'fail: accessor from static' 6878 ` 6879 6880 // Test this code as JavaScript 6881 let buildOptions = { 6882 stdin: { contents }, 6883 bundle: true, 6884 write: false, 6885 target, 6886 } 6887 transformTests[`transformClassPrivate_${target[0]}`] = async ({ esbuild }) => 6888 new Function((await esbuild.transform(contents, { target })).code)() 6889 buildTests[`buildClassPrivate_${target[0]}`] = async ({ esbuild }) => 6890 new Function((await esbuild.build(buildOptions)).outputFiles[0].text)() 6891 6892 // Test this code as TypeScript 6893 let buildOptionsTS = { 6894 stdin: { contents, loader: 'ts' }, 6895 bundle: true, 6896 write: false, 6897 } 6898 transformTests[`tsTransformClassPrivate_${target[0]}`] = async ({ esbuild }) => 6899 new Function((await esbuild.transform(contents, { target, loader: 'ts' })).code)() 6900 buildTests[`tsBuildClassPrivate_${target[0]}`] = async ({ esbuild }) => 6901 new Function((await esbuild.build(buildOptionsTS)).outputFiles[0].text)() 6902 } 6903 6904 for (let es of ['es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'esnext']) 6905 registerClassPrivateTests([es]) 6906 for (let chrome = 49; chrome < 100; chrome++) 6907 registerClassPrivateTests([`chrome${chrome}`]) 6908 for (let firefox = 45; firefox < 100; firefox++) 6909 registerClassPrivateTests([`firefox${firefox}`]) 6910 for (let edge = 13; edge < 100; edge++) 6911 registerClassPrivateTests([`edge${edge}`]) 6912 for (let safari = 10; safari < 20; safari++) 6913 registerClassPrivateTests([`safari${safari}`]) 6914 6915 let formatTests = { 6916 async formatMessages({ esbuild }) { 6917 const messages = await esbuild.formatMessages([ 6918 { text: 'This is an error' }, 6919 { text: 'Another error', location: { file: 'file.js' } }, 6920 ], { 6921 kind: 'error', 6922 }) 6923 assert.strictEqual(messages.length, 2) 6924 assert.strictEqual(messages[0], `${errorIcon} [ERROR] This is an error\n\n`) 6925 assert.strictEqual(messages[1], `${errorIcon} [ERROR] Another error\n\n file.js:0:0:\n 0 │ \n ╵ ^\n\n`) 6926 }, 6927 } 6928 6929 let analyzeTests = { 6930 async analyzeMetafile({ esbuild }) { 6931 const metafile = { 6932 "inputs": { 6933 "entry.js": { 6934 "bytes": 50, 6935 "imports": [ 6936 { 6937 "path": "lib.js", 6938 "kind": "import-statement" 6939 } 6940 ] 6941 }, 6942 "lib.js": { 6943 "bytes": 200, 6944 "imports": [] 6945 } 6946 }, 6947 "outputs": { 6948 "out.js": { 6949 "imports": [], 6950 "exports": [], 6951 "entryPoint": "entry.js", 6952 "inputs": { 6953 "entry.js": { 6954 "bytesInOutput": 25 6955 }, 6956 "lib.js": { 6957 "bytesInOutput": 50 6958 } 6959 }, 6960 "bytes": 100 6961 } 6962 } 6963 } 6964 assert.strictEqual(await esbuild.analyzeMetafile(metafile), ` 6965 out.js 100b 100.0% 6966 ├ lib.js 50b 50.0% 6967 └ entry.js 25b 25.0% 6968 `) 6969 assert.strictEqual(await esbuild.analyzeMetafile(metafile, { verbose: true }), ` 6970 out.js ────── 100b ── 100.0% 6971 ├ lib.js ──── 50b ─── 50.0% 6972 │ └ entry.js 6973 └ entry.js ── 25b ─── 25.0% 6974 `) 6975 }, 6976 } 6977 6978 let functionScopeCases = [ 6979 'function x() {} { var x }', 6980 'function* x() {} { var x }', 6981 'async function x() {} { var x }', 6982 'async function* x() {} { var x }', 6983 '{ var x } function x() {}', 6984 '{ var x } function* x() {}', 6985 '{ var x } async function x() {}', 6986 '{ var x } async function* x() {}', 6987 6988 '{ function x() {} { var x } }', 6989 '{ function* x() {} { var x } }', 6990 '{ async function x() {} { var x } }', 6991 '{ async function* x() {} { var x } }', 6992 '{ { var x } function x() {} }', 6993 '{ { var x } function* x() {} }', 6994 '{ { var x } async function x() {} }', 6995 '{ { var x } async function* x() {} }', 6996 6997 'function f() { function x() {} { var x } }', 6998 'function f() { function* x() {} { var x } }', 6999 'function f() { async function x() {} { var x } }', 7000 'function f() { async function* x() {} { var x } }', 7001 'function f() { { var x } function x() {} }', 7002 'function f() { { var x } function* x() {} }', 7003 'function f() { { var x } async function x() {} }', 7004 'function f() { { var x } async function* x() {} }', 7005 7006 'function f() { { function x() {} { var x } }}', 7007 'function f() { { function* x() {} { var x } }}', 7008 'function f() { { async function x() {} { var x } }}', 7009 'function f() { { async function* x() {} { var x } }}', 7010 'function f() { { { var x } function x() {} }}', 7011 'function f() { { { var x } function* x() {} }}', 7012 'function f() { { { var x } async function x() {} }}', 7013 'function f() { { { var x } async function* x() {} }}', 7014 ]; 7015 7016 { 7017 let counter = 0; 7018 for (let kind of ['var', 'let', 'const']) { 7019 for (let code of functionScopeCases) { 7020 code = code.replace('var', kind) 7021 transformTests['functionScope' + counter++] = async ({ esbuild }) => { 7022 let esbuildError 7023 let nodeError 7024 try { await esbuild.transform(code) } catch (e) { esbuildError = e } 7025 try { new Function(code)() } catch (e) { nodeError = e } 7026 if (!esbuildError !== !nodeError) { 7027 throw new Error(` 7028 code: ${code} 7029 esbuild: ${esbuildError} 7030 node: ${nodeError} 7031 `) 7032 } 7033 } 7034 } 7035 } 7036 } 7037 7038 let apiSyncTests = { 7039 async defaultExport({ esbuild }) { 7040 assert.strictEqual(typeof esbuild.version, 'string') 7041 assert.strictEqual(esbuild.version, esbuild.default.version) 7042 assert.strictEqual(esbuild.version, esbuild.default.default.version) 7043 assert.strictEqual(esbuild.version, esbuild.default.default.default.version) 7044 }, 7045 7046 async buildSync({ esbuild, testDir }) { 7047 const input = path.join(testDir, 'in.js') 7048 const output = path.join(testDir, 'out.js') 7049 await writeFileAsync(input, 'export default 123') 7050 esbuild.buildSync({ entryPoints: [input], bundle: true, outfile: output, format: 'cjs' }) 7051 const result = require(output) 7052 assert.strictEqual(result.default, 123) 7053 assert.strictEqual(result.__esModule, true) 7054 }, 7055 7056 async buildSyncOutputFiles({ esbuild, testDir }) { 7057 const input = path.join(testDir, 'in.js') 7058 const output = path.join(testDir, 'out.js') 7059 await writeFileAsync(input, 'module.exports = 123') 7060 let prettyPath = path.relative(process.cwd(), input).split(path.sep).join('/') 7061 let text = `// ${prettyPath}\nmodule.exports = 123;\n` 7062 let result = esbuild.buildSync({ entryPoints: [input], bundle: true, outfile: output, format: 'cjs', write: false }) 7063 assert.strictEqual(result.outputFiles.length, 1) 7064 assert.strictEqual(result.outputFiles[0].path, output) 7065 assert.strictEqual(result.outputFiles[0].text, text) 7066 assert.deepStrictEqual(result.outputFiles[0].contents, new Uint8Array(Buffer.from(text))) 7067 assert.strictEqual(result.outputFiles[0].hash, 'H4KMzZ07fA0') 7068 }, 7069 7070 async transformSyncJSMap({ esbuild }) { 7071 const { code, map } = esbuild.transformSync(`1+2`, { sourcemap: true }) 7072 assert.strictEqual(code, `1 + 2;\n`) 7073 assert.strictEqual(map, `{ 7074 "version": 3, 7075 "sources": ["<stdin>"], 7076 "sourcesContent": ["1+2"], 7077 "mappings": "AAAA,IAAE;", 7078 "names": [] 7079 } 7080 `) 7081 }, 7082 7083 async transformSyncJSMapNoContent({ esbuild }) { 7084 const { code, map } = esbuild.transformSync(`1+2`, { sourcemap: true, sourcesContent: false }) 7085 assert.strictEqual(code, `1 + 2;\n`) 7086 assert.strictEqual(map, `{ 7087 "version": 3, 7088 "sources": ["<stdin>"], 7089 "mappings": "AAAA,IAAE;", 7090 "names": [] 7091 } 7092 `) 7093 }, 7094 7095 async transformSyncCSS({ esbuild }) { 7096 const { code, map } = esbuild.transformSync(`a{b:c}`, { loader: 'css' }) 7097 assert.strictEqual(code, `a {\n b: c;\n}\n`) 7098 assert.strictEqual(map, '') 7099 }, 7100 7101 async transformSyncWithNonString({ esbuild }) { 7102 try { 7103 esbuild.transformSync(Object.create({ toString() { return '1+2' } })) 7104 throw new Error('Expected an error to be thrown'); 7105 } catch (e) { 7106 assert.strictEqual(e.errors ? e.errors[0].text : e + '', 'The input to "transform" must be a string or a Uint8Array') 7107 } 7108 }, 7109 7110 async transformSync100x({ esbuild }) { 7111 for (let i = 0; i < 100; i++) { 7112 const { code } = esbuild.transformSync(`console.log(1+${i})`, {}) 7113 assert.strictEqual(code, `console.log(1 + ${i});\n`) 7114 } 7115 }, 7116 7117 async buildSyncThrow({ esbuild, testDir }) { 7118 const input = path.join(testDir, 'in.js') 7119 try { 7120 const output = path.join(testDir, 'out.js') 7121 await writeFileAsync(input, '1+') 7122 esbuild.buildSync({ entryPoints: [input], bundle: true, outfile: output, format: 'cjs', logLevel: 'silent' }) 7123 const result = require(output) 7124 assert.strictEqual(result.default, 123) 7125 assert.strictEqual(result.__esModule, true) 7126 throw new Error('Expected an error to be thrown'); 7127 } catch (error) { 7128 assert(error instanceof Error, 'Must be an Error object'); 7129 assert.strictEqual(error.message, `Build failed with 1 error: 7130 ${path.relative(process.cwd(), input).split(path.sep).join('/')}:1:2: ERROR: Unexpected end of file`); 7131 assert.strictEqual(error.errors.length, 1); 7132 assert.strictEqual(error.warnings.length, 0); 7133 } 7134 }, 7135 7136 async transformThrow({ esbuild }) { 7137 try { 7138 await esbuild.transform(`1+`, {}) 7139 throw new Error('Expected an error to be thrown'); 7140 } catch (error) { 7141 assert(error instanceof Error, 'Must be an Error object'); 7142 assert.strictEqual(error.message, `Transform failed with 1 error:\n<stdin>:1:2: ERROR: Unexpected end of file`); 7143 assert.strictEqual(error.errors.length, 1); 7144 assert.strictEqual(error.warnings.length, 0); 7145 } 7146 }, 7147 7148 async formatMessagesSync({ esbuild }) { 7149 const messages = esbuild.formatMessagesSync([ 7150 { text: 'This is an error' }, 7151 { text: 'Another error', location: { file: 'file.js' } }, 7152 ], { 7153 kind: 'error', 7154 }) 7155 assert.strictEqual(messages.length, 2) 7156 assert.strictEqual(messages[0], `${errorIcon} [ERROR] This is an error\n\n`) 7157 assert.strictEqual(messages[1], `${errorIcon} [ERROR] Another error\n\n file.js:0:0:\n 0 │ \n ╵ ^\n\n`) 7158 }, 7159 7160 async analyzeMetafileSync({ esbuild }) { 7161 const metafile = { 7162 "inputs": { 7163 "entry.js": { 7164 "bytes": 50, 7165 "imports": [ 7166 { 7167 "path": "lib.js", 7168 "kind": "import-statement" 7169 } 7170 ] 7171 }, 7172 "lib.js": { 7173 "bytes": 200, 7174 "imports": [] 7175 } 7176 }, 7177 "outputs": { 7178 "out.js": { 7179 "imports": [], 7180 "exports": [], 7181 "entryPoint": "entry.js", 7182 "inputs": { 7183 "entry.js": { 7184 "bytesInOutput": 25 7185 }, 7186 "lib.js": { 7187 "bytesInOutput": 50 7188 } 7189 }, 7190 "bytes": 100 7191 } 7192 } 7193 } 7194 assert.strictEqual(esbuild.analyzeMetafileSync(metafile), ` 7195 out.js 100b 100.0% 7196 ├ lib.js 50b 50.0% 7197 └ entry.js 25b 25.0% 7198 `) 7199 assert.strictEqual(esbuild.analyzeMetafileSync(metafile, { verbose: true }), ` 7200 out.js ────── 100b ── 100.0% 7201 ├ lib.js ──── 50b ─── 50.0% 7202 │ └ entry.js 7203 └ entry.js ── 25b ─── 25.0% 7204 `) 7205 }, 7206 } 7207 7208 let childProcessTests = { 7209 // More info about this test case: https://github.com/evanw/esbuild/issues/2727 7210 async testIncrementalChildProcessExit({ testDir, esbuild }) { 7211 const file = path.join(testDir, 'build.js') 7212 7213 await writeFileAsync(file, ` 7214 const esbuild = require(${JSON.stringify(esbuild.ESBUILD_PACKAGE_PATH)}) 7215 esbuild.context({ 7216 entryPoints: [], 7217 }) 7218 .then(context => context.rebuild()) 7219 .then(() => { 7220 console.log('success') 7221 process.exit(0) 7222 }) 7223 `) 7224 7225 let timeout 7226 const detectHangPromise = new Promise((_, reject) => { 7227 timeout = setTimeout(() => { 7228 reject(new Error('Timed out waiting for keep-alive check to terminate')) 7229 }, 5 * 60 * 1000) 7230 }) 7231 7232 const testKeepAlivePingPromise = new Promise((resolve, reject) => { 7233 child_process.execFile('node', [file], { 7234 stdio: [ 7235 'inherit', 7236 'inherit', 7237 'pipe', // This is important for the test to check for the hang 7238 ], 7239 }, (error, stdout, stderr) => { 7240 clearTimeout(timeout) 7241 if (error) reject(error) 7242 else if (stdout !== 'success\n') reject(new Error('Unexpected stdout: ' + JSON.stringify(stdout))) 7243 else if (stderr !== '') reject(new Error('Unexpected stderr: ' + JSON.stringify(stderr))) 7244 else resolve() 7245 }) 7246 }) 7247 7248 await Promise.race([ 7249 detectHangPromise, 7250 testKeepAlivePingPromise, 7251 ]) 7252 }, 7253 7254 async testWatchStdoutChildProcess({ testDir, esbuild }) { 7255 const sequence = [ 7256 { 7257 input: 'console.log(1+2)', 7258 stdout: ['console.log(1 + 2);'], 7259 stderr: ['[watch] build finished, watching for changes...'], 7260 }, 7261 { 7262 input: 'console.log(2+3)', 7263 stdout: ['console.log(2 + 3);'], 7264 stderr: ['[watch] build started (change: "in.js")', '[watch] build finished'], 7265 }, 7266 { 7267 input: 'console.log(3+4)', 7268 stdout: ['console.log(3 + 4);'], 7269 stderr: ['[watch] build started (change: "in.js")', '[watch] build finished'], 7270 }, 7271 ] 7272 7273 const infile = path.join(testDir, 'in.js') 7274 const file = path.join(testDir, 'build.js') 7275 await writeFileAsync(infile, sequence[0].input) 7276 await writeFileAsync(file, ` 7277 const esbuild = require(${JSON.stringify(esbuild.ESBUILD_PACKAGE_PATH)}) 7278 esbuild.context({ 7279 entryPoints: [${JSON.stringify(infile)}], 7280 logLevel: 'info', 7281 }).then(ctx => ctx.watch()) 7282 `) 7283 7284 // Start the child 7285 const maxSeconds = 60 7286 const child = child_process.spawn('node', [file], { 7287 cwd: testDir, 7288 stdio: ['inherit', 'pipe', 'pipe'], 7289 timeout: maxSeconds * 1000, 7290 }) 7291 7292 // Make sure the child is always killed 7293 try { 7294 for (const { input, stdout: expectedStdout, stderr: expectedStderr } of sequence) { 7295 let totalStdout = '' 7296 let totalStderr = '' 7297 let stdoutBuffer = '' 7298 let stderrBuffer = '' 7299 const onstdout = data => { 7300 totalStdout += data 7301 stdoutBuffer += data 7302 check() 7303 } 7304 const onstderr = data => { 7305 totalStderr += data 7306 stderrBuffer += data 7307 check() 7308 } 7309 let check = () => { } 7310 7311 child.stdout.on('data', onstdout) 7312 child.stderr.on('data', onstderr) 7313 7314 await new Promise((resolve, reject) => { 7315 const seconds = 30 7316 const timeout = setTimeout(() => reject(new Error( 7317 `Watch mode + stdout test failed to match expected output after ${seconds} seconds 7318 input: ${JSON.stringify(input)} 7319 stdout: ${JSON.stringify(totalStdout)} 7320 stderr: ${JSON.stringify(totalStderr)} 7321 `)), seconds * 1000) 7322 7323 check = () => { 7324 let index 7325 7326 while ((index = stdoutBuffer.indexOf('\n')) >= 0) { 7327 const line = stdoutBuffer.slice(0, index) 7328 stdoutBuffer = stdoutBuffer.slice(index + 1) 7329 if (line === expectedStdout[0]) expectedStdout.shift() 7330 } 7331 7332 while ((index = stderrBuffer.indexOf('\n')) >= 0) { 7333 const line = stderrBuffer.slice(0, index) 7334 stderrBuffer = stderrBuffer.slice(index + 1) 7335 if (line === expectedStderr[0]) expectedStderr.shift() 7336 } 7337 7338 if (!expectedStdout.length && !expectedStderr.length) { 7339 clearTimeout(timeout) 7340 resolve() 7341 } 7342 } 7343 7344 writeFileAtomic(infile, input) 7345 }) 7346 7347 child.stdout.off('data', onstdout) 7348 child.stderr.off('data', onstderr) 7349 } 7350 } finally { 7351 child.kill() 7352 } 7353 }, 7354 } 7355 7356 let syncTests = { 7357 async startStop({ esbuild }) { 7358 for (let i = 0; i < 3; i++) { 7359 let result1 = await esbuild.transform('1+2') 7360 assert.strictEqual(result1.code, '1 + 2;\n') 7361 7362 let result2 = esbuild.transformSync('2+3') 7363 assert.strictEqual(result2.code, '2 + 3;\n') 7364 7365 let result3 = await esbuild.build({ stdin: { contents: '1+2' }, write: false }) 7366 assert.strictEqual(result3.outputFiles[0].text, '1 + 2;\n') 7367 7368 let result4 = esbuild.buildSync({ stdin: { contents: '2+3' }, write: false }) 7369 assert.strictEqual(result4.outputFiles[0].text, '2 + 3;\n') 7370 7371 esbuild.stop() 7372 } 7373 }, 7374 } 7375 7376 async function assertSourceMap(jsSourceMap, source) { 7377 jsSourceMap = JSON.parse(jsSourceMap) 7378 assert.deepStrictEqual(jsSourceMap.version, 3) 7379 assert.deepStrictEqual(jsSourceMap.sources, [source]) 7380 assert.deepStrictEqual(jsSourceMap.sourcesContent, ['let x']) 7381 assert.deepStrictEqual(jsSourceMap.mappings, 'AAAA,IAAU;') 7382 } 7383 7384 async function main() { 7385 const esbuild = installForTests() 7386 7387 // Create a fresh test directory 7388 removeRecursiveSync(rootTestDir) 7389 fs.mkdirSync(rootTestDir) 7390 7391 // Time out these tests after 5 minutes. This exists to help debug test hangs in CI. 7392 let minutes = 5 7393 let timeout = setTimeout(() => { 7394 console.error(`❌ js api tests timed out after ${minutes} minutes, exiting...`) 7395 process.exit(1) 7396 }, minutes * 60 * 1000) 7397 7398 // Run all tests concurrently 7399 const runTest = async (name, fn) => { 7400 let testDir = path.join(rootTestDir, name) 7401 try { 7402 await mkdirAsync(testDir) 7403 await fn({ esbuild, testDir }) 7404 removeRecursiveSync(testDir) 7405 return true 7406 } catch (e) { 7407 console.error(`❌ ${name}: ${e && e.message || e}`) 7408 return false 7409 } 7410 } 7411 7412 const tests = [ 7413 ...Object.entries(buildTests), 7414 ...Object.entries(watchTests), 7415 ...Object.entries(serveTests), 7416 ...Object.entries(transformTests), 7417 ...Object.entries(formatTests), 7418 ...Object.entries(analyzeTests), 7419 ...Object.entries(apiSyncTests), 7420 ...Object.entries(childProcessTests), 7421 ] 7422 7423 let allTestsPassed = (await Promise.all(tests.map(([name, fn]) => { 7424 const promise = runTest(name, fn) 7425 7426 // Time out each individual test after 3 minutes. This exists to help debug test hangs in CI. 7427 const minutes = 3 7428 const timeout = setTimeout(() => { 7429 console.error(`❌ the test "${name}" timed out after ${minutes} minutes, exiting...`) 7430 process.exit(1) 7431 }, minutes * 60 * 1000) 7432 return promise.finally(() => clearTimeout(timeout)) 7433 }))).every(success => success) 7434 7435 for (let [name, fn] of Object.entries(syncTests)) { 7436 if (!await runTest(name, fn)) { 7437 allTestsPassed = false 7438 } 7439 } 7440 7441 if (!allTestsPassed) { 7442 console.error(`❌ js api tests failed`) 7443 process.exit(1) 7444 } else { 7445 console.log(`✅ js api tests passed`) 7446 removeRecursiveSync(rootTestDir) 7447 } 7448 7449 clearTimeout(timeout); 7450 } 7451 7452 main().catch(e => setTimeout(() => { throw e }))