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