github.com/evanw/esbuild@v0.21.4/scripts/end-to-end-tests.js (about)

     1  const childProcess = require('child_process')
     2  const { buildBinary, dirname, removeRecursiveSync, writeFileAtomic } = require('./esbuild.js')
     3  const assert = require('assert')
     4  const path = require('path')
     5  const util = require('util')
     6  const url = require('url')
     7  const fs = require('fs').promises
     8  
     9  const execFileAsync = util.promisify(childProcess.execFile)
    10  const execAsync = util.promisify(childProcess.exec)
    11  
    12  const nodeMajorVersion = +process.versions.node.split('.')[0]
    13  const testDir = path.join(dirname, '.end-to-end-tests')
    14  const errorIcon = process.platform !== 'win32' ? '✘' : 'X'
    15  const esbuildPath = buildBinary()
    16  const tests = []
    17  let testCount = 0
    18  
    19  // Tests for "--define"
    20  tests.push(
    21    test(['--define:foo=null', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== null) throw 'fail'` }),
    22    test(['--define:foo=true', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== true) throw 'fail'` }),
    23    test(['--define:foo=false', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== false) throw 'fail'` }),
    24    test(['--define:foo="abc"', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== "abc") throw 'fail'` }),
    25    test(['--define:foo=123.456', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== 123.456) throw 'fail'` }),
    26    test(['--define:foo=-123.456', 'in.js', '--outfile=node.js'], { 'in.js': `if (foo !== -123.456) throw 'fail'` }),
    27    test(['--define:foo=global', 'in.js', '--outfile=node.js'], { 'in.js': `foo.bar = 123; if (bar !== 123) throw 'fail'` }),
    28    test(['--define:foo=bar', 'in.js', '--outfile=node.js'], { 'in.js': `let bar = {x: 123}; if (foo.x !== 123) throw 'fail'` }),
    29    test(['--define:a.x=1', 'in.js', '--outfile=node.js'], { 'in.js': `if (a.x !== 1) throw 'fail'` }),
    30    test(['--define:a.x=1', '--define:a.y=2', 'in.js', '--outfile=node.js'], { 'in.js': `if (a.x + a.y !== 3) throw 'fail'` }),
    31    test(['--define:a.x=1', '--define:b.y=2', 'in.js', '--outfile=node.js'], { 'in.js': `if (a.x + b.y !== 3) throw 'fail'` }),
    32    test(['--define:a.x=1', '--define:b.x=2', 'in.js', '--outfile=node.js'], { 'in.js': `if (a.x + b.x !== 3) throw 'fail'` }),
    33    test(['--define:x=y', '--define:y=x', 'in.js', '--outfile=node.js'], {
    34      'in.js': `eval('var x="x",y="y"'); if (x + y !== 'yx') throw 'fail'`,
    35    }),
    36  )
    37  
    38  // Test recursive directory creation
    39  tests.push(
    40    test(['entry.js', '--outfile=a/b/c/d/index.js'], {
    41      'entry.js': `exports.foo = 123`,
    42      'node.js': `const ns = require('./a/b/c/d'); if (ns.foo !== 123) throw 'fail'`,
    43    }),
    44  )
    45  
    46  // Test bogus paths with a file as a parent directory (this happens when you use "pnpx esbuild")
    47  tests.push(
    48    test(['entry.js', '--bundle'], {
    49      'entry.js': `import "./file.js/what/is/this"`,
    50      'file.js': `some file`,
    51    }, {
    52      expectedStderr: `${errorIcon} [ERROR] Could not resolve "./file.js/what/is/this"
    53  
    54      entry.js:1:7:
    55        1 │ import "./file.js/what/is/this"
    56          ╵        ~~~~~~~~~~~~~~~~~~~~~~~~
    57  
    58  `,
    59    }),
    60  )
    61  
    62  // Test resolving paths with a question mark (an invalid path on Windows)
    63  tests.push(
    64    test(['entry.js', '--bundle', '--outfile=node.js'], {
    65      'entry.js': `
    66        import x from "./file.js?ignore-me"
    67        if (x !== 123) throw 'fail'
    68      `,
    69      'file.js': `export default 123`,
    70    }),
    71  )
    72  
    73  // Test TypeScript enum stuff
    74  tests.push(
    75    // Scope merging
    76    test(['entry.ts', '--bundle', '--minify', '--outfile=node.js'], {
    77      'entry.ts': `
    78        const id = x => x
    79        enum a { b = 1 }
    80        enum a { c = 2 }
    81        if (id(a).c !== 2 || id(a)[2] !== 'c' || id(a).b !== 1 || id(a)[1] !== 'b') throw 'fail'
    82      `,
    83    }),
    84    test(['entry.ts', '--bundle', '--minify', '--outfile=node.js'], {
    85      'entry.ts': `
    86        const id = x => x
    87        {
    88          enum a { b = 1 }
    89        }
    90        {
    91          enum a { c = 2 }
    92          if (id(a).c !== 2 || id(a)[2] !== 'c' || id(a).b !== void 0 || id(a)[1] !== void 0) throw 'fail'
    93        }
    94      `,
    95    }),
    96    test(['entry.ts', '--bundle', '--minify', '--outfile=node.js'], {
    97      'entry.ts': `
    98        const id = x => x
    99        enum a { b = 1 }
   100        namespace a {
   101          if (id(a).b !== 1 || id(a)[1] !== 'b') throw 'fail'
   102        }
   103      `,
   104    }),
   105    test(['entry.ts', '--bundle', '--minify', '--outfile=node.js'], {
   106      'entry.ts': `
   107        const id = x => x
   108        namespace a {
   109          export function foo() {
   110            if (id(a).b !== 1 || id(a)[1] !== 'b') throw 'fail'
   111          }
   112        }
   113        enum a { b = 1 }
   114        a.foo()
   115      `,
   116    }),
   117    test(['entry.ts', '--bundle', '--minify', '--outfile=node.js'], {
   118      'entry.ts': `
   119        import './enum-to-namespace'
   120        import './namespace-to-enum'
   121        import './namespace-to-namespace'
   122      `,
   123      'enum-to-namespace.ts': `
   124        let foo, bar, y = 2, z = 4
   125        enum x { y = 1 }
   126        namespace x { foo = y }
   127        enum x { z = y * 3 }
   128        namespace x { bar = z }
   129        if (foo !== 2 || bar !== 4) throw 'fail'
   130      `,
   131      'namespace-to-enum.ts': `
   132        let y = 2, z = 4
   133        namespace x { export let y = 1 }
   134        enum x { foo = y }
   135        namespace x { export let z = y * 3 }
   136        enum x { bar = z }
   137        if (x.foo !== 2 || x.bar !== 4) throw 'fail'
   138      `,
   139      'namespace-to-namespace.ts': `
   140        let foo, bar, y = 2, z = 4
   141        namespace x { export const y = 1 }
   142        namespace x { foo = y }
   143        namespace x { export const z = y * 3 }
   144        namespace x { bar = z }
   145        if (foo !== 1 || bar !== 3) throw 'fail'
   146      `,
   147    }),
   148  
   149    // https://github.com/evanw/esbuild/issues/3205
   150    test(['entry.ts', '--outfile=node.js'], {
   151      'entry.ts': `
   152        // Note: The parentheses are important here
   153        let x = (() => {
   154          const enum E { a = 123 }
   155          return () => E.a
   156        })
   157        if (x()() !== 123) throw 'fail'
   158      `,
   159    }),
   160  
   161    // https://github.com/evanw/esbuild/issues/3210
   162    test(['entry.ts', '--bundle', '--outfile=node.js'], {
   163      'entry.ts': `
   164        import { MyEnum } from './enums';
   165        enum MyEnum2 {
   166          'A.A' = 'a',
   167          'aa' = 'aa',
   168        }
   169        if (
   170          MyEnum['A.A'] !== 'a' || MyEnum2['A.A'] !== 'a' ||
   171          MyEnum.aa !== 'aa' || MyEnum2['aa'] !== 'aa' ||
   172          MyEnum['aa'] !== 'aa' || MyEnum2.aa !== 'aa'
   173        ) throw 'fail'
   174      `,
   175      'enums.ts': `
   176        export enum MyEnum {
   177          'A.A' = 'a',
   178          'aa' = 'aa',
   179        }
   180      `,
   181    }),
   182  )
   183  
   184  // Check "tsconfig.json" behavior
   185  tests.push(
   186    // See: https://github.com/evanw/esbuild/issues/2481
   187    test(['main.ts', '--bundle', '--outfile=node.js'], {
   188      'main.ts': `
   189        import { foo } from 'js-pkg'
   190        import { bar } from 'ts-pkg'
   191        import { foo as shimFoo, bar as shimBar } from 'pkg'
   192        if (foo !== 'foo') throw 'fail: foo'
   193        if (bar !== 'bar') throw 'fail: bar'
   194        if (shimFoo !== 'shimFoo') throw 'fail: shimFoo'
   195        if (shimBar !== 'shimBar') throw 'fail: shimBar'
   196      `,
   197      'shim.ts': `
   198        export let foo = 'shimFoo'
   199        export let bar = 'shimBar'
   200      `,
   201      'tsconfig.json': `{
   202        "compilerOptions": {
   203          "paths": {
   204            "pkg": ["./shim"],
   205          },
   206        },
   207      }`,
   208      'node_modules/js-pkg/index.js': `
   209        import { foo as pkgFoo } from 'pkg'
   210        export let foo = pkgFoo
   211      `,
   212      'node_modules/ts-pkg/index.ts': `
   213        import { bar as pkgBar } from 'pkg'
   214        export let bar = pkgBar
   215      `,
   216      'node_modules/pkg/index.js': `
   217        export let foo = 'foo'
   218        export let bar = 'bar'
   219      `,
   220    }),
   221  
   222    // See: https://github.com/evanw/esbuild/issues/3767
   223    test(['apps/client/src/index.ts', '--bundle', '--outfile=node.js'], {
   224      'apps/client/src/index.ts': `
   225        import { foo } from '~/foo'
   226        if (foo !== 'foo') throw 'fail'
   227      `,
   228      'apps/client/src/foo.ts': `
   229        export const foo = 'foo'
   230      `,
   231      'apps/client/tsconfig.json': `{
   232        "extends": "@repo/tsconfig/base"
   233      }`,
   234      'apps/client/node_modules/@repo/tsconfig': {
   235        symlink: `../../../../tooling/typescript`,
   236      },
   237      'tooling/typescript/base.json': `{
   238        "compilerOptions": {
   239          "paths": {
   240            "~/*": ["../../apps/client/src/*"]
   241          }
   242        }
   243      }`,
   244    }),
   245  )
   246  
   247  // Test coverage for a special JSX error message
   248  tests.push(
   249    test(['example.jsx', '--outfile=node.js'], {
   250      'example.jsx': `let button = <Button content="some so-called \\"button text\\"" />`,
   251    }, {
   252      expectedStderr: `${errorIcon} [ERROR] Unexpected backslash in JSX element
   253  
   254      example.jsx:1:58:
   255        1 │ let button = <Button content="some so-called \\"button text\\"" />
   256          ╵                                                           ^
   257  
   258    Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes:
   259  
   260      example.jsx:1:45:
   261        1 │ let button = <Button content="some so-called \\"button text\\"" />
   262          │                                              ~~
   263          ╵                                              &quot;
   264  
   265    Consider using a JavaScript string inside {...} instead of a quoted JSX attribute:
   266  
   267      example.jsx:1:29:
   268        1 │ let button = <Button content="some so-called \\"button text\\"" />
   269          │                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   270          ╵                              {"some so-called \\"button text\\""}
   271  
   272  `,
   273    }),
   274    test(['example.jsx', '--outfile=node.js'], {
   275      'example.jsx': `let button = <Button content='some so-called \\'button text\\'' />`,
   276    }, {
   277      expectedStderr: `${errorIcon} [ERROR] Unexpected backslash in JSX element
   278  
   279      example.jsx:1:58:
   280        1 │ let button = <Button content='some so-called \\'button text\\'' />
   281          ╵                                                           ^
   282  
   283    Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes:
   284  
   285      example.jsx:1:45:
   286        1 │ let button = <Button content='some so-called \\'button text\\'' />
   287          │                                              ~~
   288          ╵                                              &apos;
   289  
   290    Consider using a JavaScript string inside {...} instead of a quoted JSX attribute:
   291  
   292      example.jsx:1:29:
   293        1 │ let button = <Button content='some so-called \\'button text\\'' />
   294          │                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   295          ╵                              {'some so-called \\'button text\\''}
   296  
   297  `,
   298    }),
   299  )
   300  
   301  // Test the "browser" field in "package.json"
   302  tests.push(
   303    test(['entry.js', '--bundle', '--outfile=node.js'], {
   304      'entry.js': `require('foo')`,
   305      'package.json': `{ "browser": { "./foo": "./file" } }`,
   306      'file.js': `var works = true`,
   307    }),
   308    test(['entry.js', '--bundle', '--outfile=node.js'], {
   309      'entry.js': `require('foo')`,
   310      'package.json': `{ "browser": { "foo": "./file" } }`,
   311      'file.js': `var works = true`,
   312    }),
   313    test(['entry.js', '--bundle', '--outfile=node.js'], {
   314      'entry.js': `require('./foo')`,
   315      'package.json': `{ "browser": { "./foo": "./file" } }`,
   316      'file.js': `var works = true`,
   317    }),
   318    test(['entry.js', '--bundle', '--outfile=node.js'], {
   319      'entry.js': `require('./foo')`,
   320      'package.json': `{ "browser": { "foo": "./file" } }`,
   321      'file.js': `var works = true`,
   322    }),
   323    test(['entry.js', '--bundle', '--outfile=node.js'], {
   324      'entry.js': `require('pkg/foo/bar')`,
   325      'node_modules/pkg/package.json': `{ "browser": { "./foo/bar": "./file" } }`,
   326      'node_modules/pkg/foo/bar.js': `invalid syntax`,
   327      'node_modules/pkg/file.js': `var works = true`,
   328    }),
   329    test(['entry.js', '--bundle', '--outfile=node.js'], {
   330      'entry.js': `require('pkg/foo/bar')`,
   331      'node_modules/pkg/package.json': `{ "browser": { "foo/bar": "./file" } }`,
   332      'node_modules/pkg/foo/bar.js': `invalid syntax`,
   333      'node_modules/pkg/file.js': `var works = true`,
   334    }),
   335    test(['entry.js', '--bundle', '--outfile=node.js'], {
   336      'entry.js': `require('pkg/foo/bar')`,
   337      'node_modules/pkg/package.json': `{ "browser": { "./foo/bar": "./file" } }`,
   338      'node_modules/pkg/file.js': `var works = true`,
   339    }),
   340    test(['entry.js', '--bundle', '--outfile=node.js'], {
   341      'entry.js': `require('pkg/foo/bar')`,
   342      'node_modules/pkg/package.json': `{ "browser": { "foo/bar": "./file" } }`,
   343      'node_modules/pkg/file.js': `var works = true`,
   344    }),
   345    test(['entry.js', '--bundle', '--outfile=node.js'], {
   346      'entry.js': `require('pkg')`,
   347      'node_modules/pkg/index.js': `require('foo/bar')`,
   348      'node_modules/pkg/package.json': `{ "browser": { "./foo/bar": "./file" } }`,
   349      'node_modules/pkg/file.js': `var works = true`,
   350    }),
   351    test(['entry.js', '--bundle', '--outfile=node.js'], {
   352      'entry.js': `require('pkg')`,
   353      'node_modules/pkg/index.js': `require('foo/bar')`,
   354      'node_modules/pkg/package.json': `{ "browser": { "foo/bar": "./file" } }`,
   355      'node_modules/pkg/file.js': `var works = true`,
   356    }),
   357    test(['entry.js', '--bundle', '--outfile=node.js'], {
   358      'entry.js': `require('pkg')`,
   359      'node_modules/pkg/index.js': `throw 'fail'`,
   360      'node_modules/pkg/package.json': `{ "browser": { "./index.js": "./file" } }`,
   361      'node_modules/pkg/file.js': `var works = true`,
   362    }),
   363    test(['entry.js', '--bundle', '--outfile=node.js'], {
   364      'entry.js': `require('pkg')`,
   365      'node_modules/pkg/package.json': `{ "browser": { "./index.js": "./file" } }`,
   366      'node_modules/pkg/file.js': `var works = true`,
   367    }),
   368    test(['entry.js', '--bundle', '--outfile=node.js'], {
   369      'entry.js': `require('pkg')`,
   370      'node_modules/pkg/index.js': `throw 'fail'`,
   371      'node_modules/pkg/package.json': `{ "browser": { "./index": "./file" } }`,
   372      'node_modules/pkg/file.js': `var works = true`,
   373    }),
   374    test(['entry.js', '--bundle', '--outfile=node.js'], {
   375      'entry.js': `require('pkg')`,
   376      'node_modules/pkg/package.json': `{ "browser": { "./index": "./file" } }`,
   377      'node_modules/pkg/file.js': `var works = true`,
   378    }),
   379    test(['entry.js', '--bundle', '--outfile=node.js'], {
   380      'entry.js': `require('pkg')`,
   381      'node_modules/pkg/main.js': `throw 'fail'`,
   382      'node_modules/pkg/package.json': `{ "main": "./main",\n  "browser": { "./main.js": "./file" } }`,
   383      'node_modules/pkg/file.js': `var works = true`,
   384    }),
   385    test(['entry.js', '--bundle', '--outfile=node.js'], {
   386      'entry.js': `require('pkg')`,
   387      'node_modules/pkg/package.json': `{ "main": "./main",\n  "browser": { "./main.js": "./file" } }`,
   388      'node_modules/pkg/file.js': `var works = true`,
   389    }),
   390    test(['entry.js', '--bundle', '--outfile=node.js'], {
   391      'entry.js': `require('pkg')`,
   392      'package.json': `{ "browser": { "pkg2": "pkg3" } }`,
   393      'node_modules/pkg/index.js': `require('pkg2')`,
   394      'node_modules/pkg/package.json': `{ "browser": { "pkg2": "./file" } }`,
   395      'node_modules/pkg/file.js': `var works = true`,
   396    }),
   397    test(['entry.js', '--bundle', '--outfile=node.js'], {
   398      'entry.js': `require('pkg')`,
   399      'package.json': `{ "browser": { "pkg2": "pkg3" } }`,
   400      'node_modules/pkg/index.js': `require('pkg2')`,
   401      'node_modules/pkg2/index.js': `throw 'fail'`,
   402      'node_modules/pkg3/index.js': `var works = true`,
   403    }),
   404    test(['entry.js', '--bundle', '--outfile=node.js'], {
   405      'entry.js': `require('pkg')`,
   406      'package.json': `{ "browser": { "pkg2": "pkg3" } }`,
   407      'node_modules/pkg/index.js': `require('pkg2')`,
   408      'node_modules/pkg/package.json': `{ "browser": { "./pkg2": "./file" } }`,
   409      'node_modules/pkg/file.js': `var works = true`,
   410    }),
   411  )
   412  
   413  // Test arbitrary module namespace identifier names
   414  // See https://github.com/tc39/ecma262/pull/2154
   415  tests.push(
   416    test(['entry.js', '--bundle', '--outfile=node.js'], {
   417      'entry.js': `import {'*' as star} from './export.js'; if (star !== 123) throw 'fail'`,
   418      'export.js': `let foo = 123; export {foo as '*'}`,
   419    }),
   420    test(['entry.js', '--bundle', '--outfile=node.js'], {
   421      'entry.js': `import {'\\0' as bar} from './export.js'; if (bar !== 123) throw 'fail'`,
   422      'export.js': `let foo = 123; export {foo as '\\0'}`,
   423    }),
   424    test(['entry.js', '--bundle', '--outfile=node.js'], {
   425      'entry.js': `import {'\\uD800\\uDC00' as bar} from './export.js'; if (bar !== 123) throw 'fail'`,
   426      'export.js': `let foo = 123; export {foo as '\\uD800\\uDC00'}`,
   427    }),
   428    test(['entry.js', '--bundle', '--outfile=node.js'], {
   429      'entry.js': `import {'🍕' as bar} from './export.js'; if (bar !== 123) throw 'fail'`,
   430      'export.js': `let foo = 123; export {foo as '🍕'}`,
   431    }),
   432    test(['entry.js', '--bundle', '--outfile=node.js'], {
   433      'entry.js': `import {' ' as bar} from './export.js'; if (bar !== 123) throw 'fail'`,
   434      'export.js': `export let foo = 123; export {foo as ' '} from './export.js'`,
   435    }),
   436    test(['entry.js', '--bundle', '--outfile=node.js'], {
   437      'entry.js': `import {'' as ab} from './export.js'; if (ab.foo !== 123 || ab.bar !== 234) throw 'fail'`,
   438      'export.js': `export let foo = 123, bar = 234; export * as '' from './export.js'`,
   439    }),
   440  )
   441  
   442  // Tests for symlinks
   443  //
   444  // Note: These are disabled on Windows because they fail when run with GitHub
   445  // Actions. I'm not sure what the issue is because they pass for me when run in
   446  // my Windows VM (Windows 10 in VirtualBox on macOS).
   447  if (process.platform !== 'win32') {
   448    tests.push(
   449      // Without preserve symlinks
   450      test(['--bundle', 'in.js', '--outfile=node.js'], {
   451        'in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   452        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   453        'registry/node_modules/bar/index.js': `export const bar = 123`,
   454        'node_modules/foo': { symlink: `../registry/node_modules/foo` },
   455      }),
   456      test(['--bundle', 'in.js', '--outfile=node.js'], {
   457        'in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   458        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   459        'registry/node_modules/bar/index.js': `export const bar = 123`,
   460        'node_modules/foo/index.js': { symlink: `../../registry/node_modules/foo/index.js` },
   461      }),
   462      test(['--bundle', 'in.js', '--outfile=node.js'], {
   463        'in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   464        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   465        'registry/node_modules/bar/index.js': `export const bar = 123`,
   466        'node_modules/foo': { symlink: `TEST_DIR_ABS_PATH/registry/node_modules/foo` },
   467      }),
   468      test(['--bundle', 'in.js', '--outfile=node.js'], {
   469        'in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   470        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   471        'registry/node_modules/bar/index.js': `export const bar = 123`,
   472        'node_modules/foo/index.js': { symlink: `TEST_DIR_ABS_PATH/registry/node_modules/foo/index.js` },
   473      }),
   474  
   475      // With preserve symlinks
   476      test(['--bundle', 'src/in.js', '--outfile=node.js', '--preserve-symlinks'], {
   477        'src/in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   478        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   479        'src/node_modules/bar/index.js': `export const bar = 123`,
   480        'src/node_modules/foo': { symlink: `../../registry/node_modules/foo` },
   481      }),
   482      test(['--bundle', 'src/in.js', '--outfile=node.js', '--preserve-symlinks'], {
   483        'src/in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   484        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   485        'src/node_modules/bar/index.js': `export const bar = 123`,
   486        'src/node_modules/foo/index.js': { symlink: `../../../registry/node_modules/foo/index.js` },
   487      }),
   488      test(['--bundle', 'src/in.js', '--outfile=node.js', '--preserve-symlinks'], {
   489        'src/in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   490        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   491        'src/node_modules/bar/index.js': `export const bar = 123`,
   492        'src/node_modules/foo': { symlink: `TEST_DIR_ABS_PATH/registry/node_modules/foo` },
   493      }),
   494      test(['--bundle', 'src/in.js', '--outfile=node.js', '--preserve-symlinks'], {
   495        'src/in.js': `import {foo} from 'foo'; if (foo !== 123) throw 'fail'`,
   496        'registry/node_modules/foo/index.js': `export {bar as foo} from 'bar'`,
   497        'src/node_modules/bar/index.js': `export const bar = 123`,
   498        'src/node_modules/foo/index.js': { symlink: `TEST_DIR_ABS_PATH/registry/node_modules/foo/index.js` },
   499      }),
   500  
   501      // This is a test for https://github.com/evanw/esbuild/issues/222
   502      test(['--bundle', 'src/in.js', '--outfile=out/node.js', '--metafile=out/meta.json', '--platform=node', '--format=cjs'], {
   503        'a/b/src/in.js': `
   504          import {metafile} from './load'
   505          const assert = require('assert')
   506          assert.deepStrictEqual(Object.keys(metafile.inputs), ['src/load.js', 'src/in.js'])
   507          assert.strictEqual(metafile.inputs['src/in.js'].imports[0].path, 'src/load.js')
   508        `,
   509        'a/b/src/load.js': `
   510          export var metafile
   511          // Hide the import path from the bundler
   512          try {
   513            let path = './meta.json'
   514            metafile = require(path)
   515          } catch (e) {
   516          }
   517        `,
   518        'node.js': `
   519          require('./a/b/out/node')
   520        `,
   521        'c': { symlink: `a/b` },
   522      }, { cwd: 'c' }),
   523  
   524      // This is a test for https://github.com/evanw/esbuild/issues/766
   525      test(['--bundle', 'impl/index.mjs', '--outfile=node.js', '--format=cjs', '--resolve-extensions=.mjs'], {
   526        'config/yarn/link/@monorepo-source/a': { symlink: `../../../../monorepo-source/packages/a` },
   527        'config/yarn/link/@monorepo-source/b': { symlink: `../../../../monorepo-source/packages/b` },
   528        'impl/node_modules/@monorepo-source/b': { symlink: `../../../config/yarn/link/@monorepo-source/b` },
   529        'impl/index.mjs': `
   530          import { fn } from '@monorepo-source/b';
   531          if (fn() !== 123) throw 'fail';
   532        `,
   533        'monorepo-source/packages/a/index.mjs': `
   534          export function foo() { return 123; }
   535        `,
   536        'monorepo-source/packages/b/node_modules/@monorepo-source/a': { symlink: `../../../../../config/yarn/link/@monorepo-source/a` },
   537        'monorepo-source/packages/b/index.mjs': `
   538          import { foo } from '@monorepo-source/a';
   539          export function fn() { return foo(); }
   540        `,
   541      }),
   542  
   543      // These tests are for https://github.com/evanw/esbuild/issues/2773
   544      test(['--bundle', 'in.js', '--outfile=node.js'], {
   545        'in.js': `import {foo} from './baz/bar/foo'; if (foo !== 444) throw 'fail'`,
   546        'foo/index.js': `import {qux} from '../qux'; export const foo = 123 + qux`,
   547        'qux/index.js': `export const qux = 321`,
   548        'bar/foo': { symlink: `../foo` },
   549        'baz/bar': { symlink: `../bar` },
   550      }),
   551      test(['--bundle', 'in.js', '--outfile=node.js'], {
   552        'in.js': `import {foo} from './baz/bar/foo'; if (foo !== 444) throw 'fail'`,
   553        'foo/index.js': `import {qux} from '../qux'; export const foo = 123 + qux`,
   554        'qux/index.js': `export const qux = 321`,
   555        'bar/foo': { symlink: `TEST_DIR_ABS_PATH/foo` },
   556        'baz/bar': { symlink: `TEST_DIR_ABS_PATH/bar` },
   557      }),
   558    )
   559  }
   560  
   561  // Test custom output paths
   562  tests.push(
   563    test(['node=entry.js', '--outdir=.'], {
   564      'entry.js': ``,
   565    }),
   566  )
   567  
   568  // Make sure that the "asm.js" directive is removed
   569  tests.push(
   570    test(['in.js', '--outfile=node.js'], {
   571      'in.js': `
   572        function foo() { 'use asm'; eval("/* not asm.js */") }
   573        let emitWarning = process.emitWarning
   574        let failed = false
   575        try {
   576          process.emitWarning = () => failed = true
   577          foo()
   578        } finally {
   579          process.emitWarning = emitWarning
   580        }
   581        if (failed) throw 'fail'
   582      `,
   583    }),
   584  )
   585  
   586  // Check async generator lowering
   587  for (const flags of [[], ['--target=es6', '--target=es2017', '--supported:async-generator=false', '--supported:async-await=false']]) {
   588    tests.push(
   589      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   590        'in.js': `
   591          function* x() {
   592            yield 1
   593            yield 2
   594            return 3
   595          }
   596          async function y(arg) {
   597            return -(await Promise.resolve(arg))
   598          }
   599          async function* z(arg) {
   600            yield 1
   601            yield Promise.resolve(2)
   602            yield* [3, Promise.resolve(4)]
   603            yield* {
   604              [Symbol.iterator]() {
   605                var value = 5
   606                return { next: () => ({ value, done: value++ > 6 }) }
   607              }
   608            }
   609            yield* {
   610              [Symbol.asyncIterator]() {
   611                var value = 7
   612                return { next: async () => ({ value, done: value++ > 8 }) }
   613              }
   614            }
   615            return -(await Promise.resolve(arg))
   616          }
   617          export let async = async () => {
   618            let state
   619  
   620            const X = x()
   621            if (X[Symbol.iterator]() !== X) throw 'fail: x Symbol.iterator'
   622            if (Symbol.asyncIterator in X) throw 'fail: x Symbol.asyncIterator'
   623            state = X.next(); if (state.done !== false || state.value !== 1) throw 'fail: x 1: ' + JSON.stringify(state)
   624            state = X.next(); if (state.done !== false || state.value !== 2) throw 'fail: x 2: ' + JSON.stringify(state)
   625            state = X.next(); if (state.done !== true || state.value !== 3) throw 'fail: x 3: ' + JSON.stringify(state)
   626  
   627            const Y = y(123)
   628            if (Symbol.iterator in Y) throw 'fail: y Symbol.iterator'
   629            if (Symbol.asyncIterator in Y) throw 'fail: y Symbol.asyncIterator'
   630            if (await Y !== -123) throw 'fail: y'
   631  
   632            const Z = z(123)
   633            if (Symbol.iterator in Z) throw 'fail: z Symbol.iterator'
   634            if (Z[Symbol.asyncIterator]() !== Z) throw 'fail: z Symbol.asyncIterator'
   635            state = await Z.next(); if (state.done !== false || state.value !== 1) throw 'fail: z 1: ' + JSON.stringify(state)
   636            state = await Z.next(); if (state.done !== false || state.value !== 2) throw 'fail: z 2: ' + JSON.stringify(state)
   637            state = await Z.next(); if (state.done !== false || state.value !== 3) throw 'fail: z 3: ' + JSON.stringify(state)
   638            state = await Z.next(); if (state.done !== false || state.value !== 4) throw 'fail: z 4: ' + JSON.stringify(state)
   639            state = await Z.next(); if (state.done !== false || state.value !== 5) throw 'fail: z 5: ' + JSON.stringify(state)
   640            state = await Z.next(); if (state.done !== false || state.value !== 6) throw 'fail: z 6: ' + JSON.stringify(state)
   641            state = await Z.next(); if (state.done !== false || state.value !== 7) throw 'fail: z 7: ' + JSON.stringify(state)
   642            state = await Z.next(); if (state.done !== false || state.value !== 8) throw 'fail: z 8: ' + JSON.stringify(state)
   643            state = await Z.next(); if (state.done !== true || state.value !== -123) throw 'fail: z 123: ' + JSON.stringify(state)
   644          }
   645        `,
   646      }, { async: true }),
   647      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   648        'in.js': `
   649          async function* f() {
   650            yield* {
   651              [Symbol.asyncIterator]: () => ({ next() { throw 'f' } })
   652            }
   653          }
   654          export let async = async () => {
   655            let it, state
   656            it = f()
   657            try { await it.next(); throw 'fail: f: next' } catch (err) { if (err !== 'f') throw err }
   658            state = await it.next()
   659            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   660          }
   661        `,
   662      }, { async: true }),
   663      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   664        'in.js': `
   665          async function* f() {
   666            yield* {
   667              [Symbol.asyncIterator]: () => ({ get next() { throw 'f' } })
   668            }
   669          }
   670          export let async = async () => {
   671            let it, state
   672            it = f()
   673            try { await it.next(); throw 'fail: f: next' } catch (err) { if (err !== 'f') throw err }
   674            state = await it.next()
   675            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   676          }
   677        `,
   678      }, { async: true }),
   679      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   680        'in.js': `
   681          async function* f() {
   682            yield* {
   683              [Symbol.asyncIterator]: () => ({ async next() { throw 'f' } })
   684            }
   685          }
   686          export let async = async () => {
   687            let it, state
   688            it = f()
   689            try { await it.next(); throw 'fail: f: next' } catch (err) { if (err !== 'f') throw err }
   690            state = await it.next()
   691            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   692          }
   693        `,
   694      }, { async: true }),
   695      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   696        'in.js': `
   697          async function* f() {
   698            try {
   699              yield* {
   700                [Symbol.asyncIterator]: () => ({
   701                  next: () => ({
   702                    done: false,
   703                    get value() { throw 'f' }
   704                  })
   705                }),
   706              }
   707            } catch (e) {
   708              return e
   709            }
   710          }
   711          export let async = async () => {
   712            let it, state
   713            it = f()
   714            state = await it.next()
   715            if (state.done !== true || state.value !== 'f') throw 'fail: f: next'
   716          }
   717        `,
   718      }, { async: true }),
   719      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   720        'in.js': `
   721          async function* f() {
   722            yield* [
   723              Promise.reject('f.x'),
   724              'f.y',
   725            ]
   726          }
   727          export let async = async () => {
   728            let it, state
   729            it = f()
   730            try { await it.next(); throw 'fail: f: next' } catch (err) { if (err !== 'f.x') throw err }
   731            state = await it.next()
   732            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   733          }
   734        `,
   735      }, { async: true }),
   736      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   737        'in.js': `
   738          async function* f() {
   739            yield* {
   740              [Symbol.iterator]: () => ({ next: () => 123 }),
   741            }
   742            return 'f'
   743          }
   744          export let async = async () => {
   745            let it, state
   746            it = f()
   747            try { await it.next(); throw 'fail: f: next' } catch (err) { if (!(err instanceof TypeError)) throw err }
   748            state = await it.next()
   749            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   750          }
   751        `,
   752      }, { async: true }),
   753      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   754        'in.js': `
   755          async function* f() {
   756            yield* {
   757              [Symbol.asyncIterator]: () => ({ next: () => 123 }),
   758            }
   759            return 'f'
   760          }
   761          export let async = async () => {
   762            let it, state
   763            it = f()
   764            try { await it.next(); throw 'fail: f: next' } catch (err) { if (!(err instanceof TypeError)) throw err }
   765            state = await it.next()
   766            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   767          }
   768        `,
   769      }, { async: true }),
   770      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   771        'in.js': `
   772          async function* f() {
   773            yield* [
   774              'f.x',
   775              'f.y',
   776            ]
   777            return 'f'
   778          }
   779          export let async = async () => {
   780            let it, state
   781            it = f()
   782            state = await it.next()
   783            if (state.done !== false || state.value !== 'f.x') throw 'fail: f: next'
   784            try { await it.throw('f: throw') } catch (err) { var error = err }
   785            if (error !== 'f: throw') throw 'fail: f: ' + error
   786            state = await it.next()
   787            if (state.done !== true || state.value !== void 0) throw 'fail: f: done'
   788          }
   789        `,
   790      }, { async: true }),
   791      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   792        'in.js': `
   793          async function* f() {
   794            yield* {
   795              [Symbol.iterator]: () => ({
   796                next: a => ({ value: 'f.x.' + a, done: false }),
   797                return: a => ({ value: 'f.y.' + a, done: true }),
   798              })
   799            }
   800          }
   801          export let async = async () => {
   802            let it, state
   803            it = f()
   804            state = await it.next('A')
   805            if (state.done !== false || state.value !== 'f.x.undefined') throw 'fail: f: next'
   806            state = await it.return('B')
   807            if (state.done !== true || state.value !== 'f.y.B') throw 'fail: f: return'
   808          }
   809        `,
   810      }, { async: true }),
   811      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   812        'in.js': `
   813          async function* f() {
   814            yield* {
   815              [Symbol.asyncIterator]: () => ({
   816                next: a => Promise.resolve({ value: 'f.x.' + a, done: false }),
   817                return: a => Promise.resolve({ value: 'f.y.' + a, done: true }),
   818              })
   819            }
   820          }
   821          export let async = async () => {
   822            let it, state
   823            it = f()
   824            state = await it.next('A')
   825            if (state.done !== false || state.value !== 'f.x.undefined') throw 'fail: f: next'
   826            state = await it.return('B')
   827            if (state.done !== true || state.value !== 'f.y.B') throw 'fail: f: return'
   828          }
   829        `,
   830      }, { async: true }),
   831      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   832        'in.js': `
   833          async function* f() {
   834            yield* {
   835              [Symbol.iterator]: () => ({
   836                next: a => ({ value: 'f.x.' + a, done: false }),
   837                throw: a => ({ value: 'f.y.' + a, done: true }),
   838              })
   839            }
   840          }
   841          export let async = async () => {
   842            let it, state
   843            it = f()
   844            state = await it.next('A')
   845            if (state.done !== false || state.value !== 'f.x.undefined') throw 'fail: f: next'
   846            state = await it.throw('B')
   847            if (state.done !== true || state.value !== undefined) throw 'fail: f: throw'
   848          }
   849        `,
   850      }, { async: true }),
   851      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   852        'in.js': `
   853          async function* f() {
   854            yield* {
   855              [Symbol.asyncIterator]: () => ({
   856                next: a => Promise.resolve({ value: 'f.x.' + a, done: false }),
   857                throw: a => Promise.resolve({ value: 'f.y.' + a, done: true }),
   858              })
   859            }
   860          }
   861          export let async = async () => {
   862            let it, state
   863            it = f()
   864            state = await it.next('A')
   865            if (state.done !== false || state.value !== 'f.x.undefined') throw 'fail: f: next'
   866            state = await it.throw('B')
   867            if (state.done !== true || state.value !== undefined) throw 'fail: f: throw'
   868          }
   869        `,
   870      }, { async: true }),
   871      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   872        'in.js': `
   873          async function* f() {
   874            var value = 0
   875            yield* {
   876              [Symbol.iterator]: () => ({ next: () => ({ done: value > 10, value: value += 100 }) }),
   877              get [Symbol.asyncIterator]() { value += 10; return undefined },
   878            }
   879            return value
   880          }
   881          export let async = async () => {
   882            let it, state
   883            it = f()
   884            state = await it.next(); if (state.done !== false || state.value !== 110) throw 'fail: f 110: ' + JSON.stringify(state)
   885            state = await it.next(); if (state.done !== true || state.value !== 210) throw 'fail: f 210: ' + JSON.stringify(state)
   886          }
   887        `,
   888      }, { async: true }),
   889      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   890        'in.js': `
   891          async function* f() {
   892            var value = 0
   893            yield* {
   894              [Symbol.iterator]: () => ({ next: () => ({ done: value > 10, value: value += 100 }) }),
   895              get [Symbol.asyncIterator]() { value += 10; return null },
   896            }
   897            return value
   898          }
   899          export let async = async () => {
   900            let it, state
   901            it = f()
   902            state = await it.next(); if (state.done !== false || state.value !== 110) throw 'fail: f 110: ' + JSON.stringify(state)
   903            state = await it.next(); if (state.done !== true || state.value !== 210) throw 'fail: f 210: ' + JSON.stringify(state)
   904          }
   905        `,
   906      }, { async: true }),
   907      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   908        'in.js': `
   909          async function* f() {
   910            var value = 0
   911            yield* {
   912              [Symbol.iterator]: () => ({ next: () => ({ done: value > 10, value: value += 100 }) }),
   913              get [Symbol.asyncIterator]() { value += 10; return false },
   914            }
   915            return value
   916          }
   917          export let async = async () => {
   918            let it, state
   919            it = f()
   920            try { await it.next() } catch (e) { var error = e }
   921            if (!(error instanceof TypeError)) throw 'fail: f'
   922          }
   923        `,
   924      }, { async: true }),
   925      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   926        'in.js': `
   927          async function* f() {
   928            var value = 0
   929            yield* {
   930              [Symbol.iterator]: () => ({ next: () => ({ done: value > 10, value: value += 100 }) }),
   931              get [Symbol.asyncIterator]() { value += 10; return 0 },
   932            }
   933            return value
   934          }
   935          export let async = async () => {
   936            let it, state
   937            it = f()
   938            try { await it.next() } catch (e) { var error = e }
   939            if (!(error instanceof TypeError)) throw 'fail: f'
   940          }
   941        `,
   942      }, { async: true }),
   943    )
   944  }
   945  
   946  // Check "for await" lowering
   947  for (const flags of [[], ['--target=es6', '--target=es2017', '--supported:for-await=false']]) {
   948    tests.push(
   949      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   950        'in.js': `
   951          export let async = async () => {
   952            const log = []
   953            const it = {
   954              [Symbol.iterator]() { return this },
   955              next() { log.push(this === it && 'next'); return { value: 123, done: false } },
   956              return() { log.push(this === it && 'return') },
   957            }
   958            try {
   959              for await (const x of it) {
   960                if (x !== 123) throw 'fail: ' + x
   961                throw 'foo'
   962              }
   963            } catch (err) {
   964              if (err !== 'foo') throw err
   965            }
   966            if (log + '' !== 'next,return') throw 'fail: ' + log
   967          }
   968        `,
   969      }, { async: true }),
   970      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
   971        'in.js': `
   972          export let async = async () => {
   973            const log = []
   974            const it = {
   975              [Symbol.asyncIterator]() { return this },
   976              async next() { log.push(this === it && 'next'); return { value: 123, done: false } },
   977              async return() { log.push(this === it && 'return') },
   978            }
   979            try {
   980              for await (const x of it) {
   981                if (x !== 123) throw 'fail: ' + x
   982                throw 'foo'
   983              }
   984            } catch (err) {
   985              if (err !== 'foo') throw err
   986            }
   987            if (log + '' !== 'next,return') throw 'fail: ' + log
   988          }
   989        `,
   990      }, { async: true }),
   991  
   992      // return() must not be called in this case (TypeScript has this bug: https://github.com/microsoft/TypeScript/issues/50525)
   993      test(['in.js', '--outfile=node.js'].concat(flags), {
   994        'in.js': `
   995          let pass = true
   996          async function f() {
   997            const y = {
   998              [Symbol.asyncIterator]() {
   999                let count = 0
  1000                return {
  1001                  async next() {
  1002                    count++
  1003                    if (count === 2) throw 'error'
  1004                    return { value: count }
  1005                  },
  1006                  async return() {
  1007                    pass = false
  1008                  },
  1009                }
  1010              },
  1011            }
  1012            for await (let x of y) {
  1013            }
  1014          }
  1015          f().catch(() => {
  1016            if (!pass) throw 'fail'
  1017          })
  1018        `,
  1019      }),
  1020  
  1021      // return() must be called in this case
  1022      test(['in.js', '--outfile=node.js'].concat(flags), {
  1023        'in.js': `
  1024          let pass = false
  1025          async function f() {
  1026            const y = {
  1027              [Symbol.asyncIterator]() {
  1028                let count = 0
  1029                return {
  1030                  async next() {
  1031                    count++
  1032                    return { value: count }
  1033                  },
  1034                  async return() {
  1035                    pass = true
  1036                  },
  1037                }
  1038              },
  1039            }
  1040            for await (let x of y) {
  1041              throw 'error'
  1042            }
  1043          }
  1044          f().catch(() => {
  1045            if (!pass) throw 'fail'
  1046          })
  1047        `,
  1048      }),
  1049    )
  1050  }
  1051  
  1052  // Check object rest lowering
  1053  // https://github.com/evanw/esbuild/issues/956
  1054  tests.push(
  1055    test(['in.js', '--outfile=node.js', '--target=es6'], {
  1056      'in.js': `
  1057        let v, o = {b: 3, c: 5}, e = ({b: v, ...o} = o);
  1058        if (o === e || o.b !== void 0 || o.c !== 5 || e.b !== 3 || e.c !== 5 || v !== 3) throw 'fail'
  1059      `,
  1060    }),
  1061  )
  1062  
  1063  // Check object spread lowering
  1064  // https://github.com/evanw/esbuild/issues/1017
  1065  const objectAssignSemantics = `
  1066    var a, b, c, p, s = Symbol('s')
  1067  
  1068    // Getter
  1069    a = { x: 1 }
  1070    b = { get x() {}, ...a }
  1071    if (b.x !== a.x) throw 'fail: 1'
  1072  
  1073    // Symbol getter
  1074    a = {}
  1075    a[s] = 1
  1076    p = {}
  1077    Object.defineProperty(p, s, { get: () => {} })
  1078    b = { __proto__: p, ...a }
  1079    if (b[s] !== a[s]) throw 'fail: 2'
  1080  
  1081    // Non-enumerable
  1082    a = {}
  1083    Object.defineProperty(a, 'x', { value: 1 })
  1084    b = { ...a }
  1085    if (b.x === a.x) throw 'fail: 3'
  1086  
  1087    // Symbol non-enumerable
  1088    a = {}
  1089    Object.defineProperty(a, s, { value: 1 })
  1090    b = { ...a }
  1091    if (b[s] === a[s]) throw 'fail: 4'
  1092  
  1093    // Prototype
  1094    a = Object.create({ x: 1 })
  1095    b = { ...a }
  1096    if (b.x === a.x) throw 'fail: 5'
  1097  
  1098    // Symbol prototype
  1099    p = {}
  1100    p[s] = 1
  1101    a = Object.create(p)
  1102    b = { ...a }
  1103    if (b[s] === a[s]) throw 'fail: 6'
  1104  
  1105    // Getter evaluation 1
  1106    a = 1
  1107    b = 10
  1108    p = { get x() { return a++ }, ...{ get y() { return b++ } } }
  1109    if (
  1110      p.x !== 1 || p.x !== 2 || p.x !== 3 ||
  1111      p.y !== 10 || p.y !== 10 || p.y !== 10
  1112    ) throw 'fail: 7'
  1113  
  1114    // Getter evaluation 2
  1115    a = 1
  1116    b = 10
  1117    p = { ...{ get x() { return a++ } }, get y() { return b++ } }
  1118    if (
  1119      p.x !== 1 || p.x !== 1 || p.x !== 1 ||
  1120      p.y !== 10 || p.y !== 11 || p.y !== 12
  1121    ) throw 'fail: 8'
  1122  
  1123    // Getter evaluation 3
  1124    a = 1
  1125    b = 10
  1126    c = 100
  1127    p = { ...{ get x() { return a++ } }, get y() { return b++ }, ...{ get z() { return c++ } } }
  1128    if (
  1129      p.x !== 1 || p.x !== 1 || p.x !== 1 ||
  1130      p.y !== 10 || p.y !== 11 || p.y !== 12 ||
  1131      p.z !== 100 || p.z !== 100 || p.z !== 100
  1132    ) throw 'fail: 9'
  1133  
  1134    // Inline prototype property
  1135    p = { ...{ __proto__: null } }
  1136    if (Object.prototype.hasOwnProperty.call(p, '__proto__') || Object.getPrototypeOf(p) === null) throw 'fail: 10'
  1137  `
  1138  tests.push(
  1139    test(['in.js', '--outfile=node.js'], {
  1140      'in.js': objectAssignSemantics,
  1141    }),
  1142    test(['in.js', '--outfile=node.js', '--target=es6'], {
  1143      'in.js': objectAssignSemantics,
  1144    }),
  1145    test(['in.js', '--outfile=node.js', '--target=es5'], {
  1146      'in.js': objectAssignSemantics,
  1147    }),
  1148    test(['in.js', '--outfile=node.js', '--minify-syntax'], {
  1149      'in.js': objectAssignSemantics,
  1150    }),
  1151  )
  1152  
  1153  // Check template literal lowering
  1154  for (const target of ['--target=es5', '--target=es6', '--target=es2020']) {
  1155    tests.push(
  1156      // Untagged template literals
  1157      test(['in.js', '--outfile=node.js', target], {
  1158        'in.js': `
  1159          var obj = {
  1160            toString: () => 'b',
  1161            valueOf: () => 0,
  1162          }
  1163          if (\`\${obj}\` !== 'b') throw 'fail'
  1164          if (\`a\${obj}\` !== 'ab') throw 'fail'
  1165          if (\`\${obj}c\` !== 'bc') throw 'fail'
  1166          if (\`a\${obj}c\` !== 'abc') throw 'fail'
  1167        `,
  1168      }),
  1169      test(['in.js', '--outfile=node.js', target], {
  1170        'in.js': `
  1171          var obj = {}
  1172          obj[Symbol.toPrimitive] = hint => {
  1173            if (hint !== 'string') throw 'fail'
  1174            return 'b'
  1175          }
  1176          if (\`\${obj}\` !== 'b') throw 'fail'
  1177          if (\`a\${obj}\` !== 'ab') throw 'fail'
  1178          if (\`\${obj}c\` !== 'bc') throw 'fail'
  1179          if (\`a\${obj}c\` !== 'abc') throw 'fail'
  1180        `,
  1181      }),
  1182      test(['in.js', '--outfile=node.js', target], {
  1183        'in.js': `
  1184          var list = []
  1185          var trace = x => list.push(x)
  1186          var obj2 = { toString: () => trace(2) };
  1187          var obj4 = { toString: () => trace(4) };
  1188          \`\${trace(1), obj2}\${trace(3), obj4}\`
  1189          if (list.join('') !== '1234') throw 'fail'
  1190        `,
  1191      }),
  1192      test(['in.js', '--outfile=node.js', target], {
  1193        'in.js': `
  1194          x: {
  1195            try {
  1196              \`\${Symbol('y')}\`
  1197            } catch {
  1198              break x
  1199            }
  1200            throw 'fail'
  1201          }
  1202        `,
  1203      }),
  1204  
  1205      // Tagged template literals
  1206      test(['in.js', '--outfile=node.js', target], {
  1207        'in.js': `
  1208          if ((x => x[0] === 'y' && x.raw[0] === 'y')\`y\` !== true) throw 'fail'
  1209          if ((x => x[0] === 'y' && x.raw[0] === 'y')\`y\${0}\` !== true) throw 'fail'
  1210          if ((x => x[1] === 'y' && x.raw[1] === 'y')\`\${0}y\` !== true) throw 'fail'
  1211        `,
  1212      }),
  1213      test(['in.js', '--outfile=node.js', target], {
  1214        'in.js': `
  1215          if ((x => x[0] === '\\xFF' && x.raw[0] === '\\\\xFF')\`\\xFF\` !== true) throw 'fail'
  1216          if ((x => x[0] === '\\xFF' && x.raw[0] === '\\\\xFF')\`\\xFF\${0}\` !== true) throw 'fail'
  1217          if ((x => x[1] === '\\xFF' && x.raw[1] === '\\\\xFF')\`\${0}\\xFF\` !== true) throw 'fail'
  1218        `,
  1219      }),
  1220      test(['in.js', '--outfile=node.js', target], {
  1221        'in.js': `
  1222          if ((x => x[0] === void 0 && x.raw[0] === '\\\\u')\`\\u\` !== true) throw 'fail'
  1223          if ((x => x[0] === void 0 && x.raw[0] === '\\\\u')\`\\u\${0}\` !== true) throw 'fail'
  1224          if ((x => x[1] === void 0 && x.raw[1] === '\\\\u')\`\${0}\\u\` !== true) throw 'fail'
  1225        `,
  1226      }),
  1227      test(['in.js', '--outfile=node.js', target], {
  1228        'in.js': `
  1229          if ((x => x !== x.raw)\`y\` !== true) throw 'fail'
  1230        `,
  1231      }),
  1232      test(['in.js', '--outfile=node.js', target], {
  1233        'in.js': `
  1234          if ((x => (x.length = 2, x.length))\`y\` !== 1) throw 'fail'
  1235        `,
  1236      }),
  1237      test(['in.js', '--outfile=node.js', target], {
  1238        'in.js': `
  1239          if ((x => (x.raw.length = 2, x.raw.length))\`y\` !== 1) throw 'fail'
  1240        `,
  1241      }),
  1242      test(['in.js', '--outfile=node.js', target], {
  1243        'in.js': `
  1244          var count = 0
  1245          var foo = () => (() => ++count)\`y\`;
  1246          if (foo() !== 1 || foo() !== 2) throw 'fail'
  1247        `,
  1248      }),
  1249      test(['in.js', '--outfile=node.js', target], {
  1250        'in.js': `
  1251          var foo = () => (x => x)\`y\`;
  1252          if (foo() !== foo()) throw 'fail'
  1253        `,
  1254      }),
  1255      test(['in.js', '--outfile=node.js', target], {
  1256        'in.js': `
  1257          var foo = () => (x => x)\`y\`;
  1258          var bar = () => (x => x)\`y\`;
  1259          if (foo() === bar()) throw 'fail'
  1260        `,
  1261      }),
  1262      test(['in.js', '--outfile=node.js', target], {
  1263        'in.js': `
  1264          var count = 0;
  1265          var obj = {
  1266            foo: function() {
  1267              if (this === obj) count++;
  1268            }
  1269          };
  1270          var bar = 'foo';
  1271          (obj?.foo)\`\`;
  1272          (obj?.[bar])\`\`;
  1273          var other = { obj };
  1274          (other?.obj.foo)\`\`;
  1275          (other?.obj[bar])\`\`;
  1276          if (count !== 4) throw 'fail';
  1277        `,
  1278      }),
  1279  
  1280      // Unused minified template literals. See this for more info:
  1281      // https://github.com/terser/terser/issues/1128#issuecomment-994209801
  1282      test(['in.js', '--outfile=node.js', '--minify', target], {
  1283        'in.js': `
  1284          var text = '';
  1285          var foo = {
  1286            toString: () => text += 'toString',
  1287            valueOf: () => text += 'valueOf',
  1288          };
  1289          \`\${foo}\`;
  1290          if (text !== 'toString') throw 'fail: ' + text + ' !== toString'
  1291        `,
  1292      }),
  1293      test(['in.js', '--outfile=node.js', '--minify', target], {
  1294        'in.js': `
  1295          var text = '';
  1296          var foo = {
  1297            toString: () => text += 'toString',
  1298          };
  1299          \`abc \${text += 'A', foo} xyz \${text += 'B', foo} 123\`;
  1300          if (text !== 'AtoStringBtoString') throw 'fail: ' + text + ' !== AtoStringBtoString'
  1301        `,
  1302      }),
  1303    )
  1304  }
  1305  
  1306  let simpleCyclicImportTestCase542 = {
  1307    'in.js': `
  1308      import {Test} from './lib';
  1309      export function fn() {
  1310        return 42;
  1311      }
  1312      export const foo = [Test];
  1313      if (Test.method() !== 42) throw 'fail'
  1314    `,
  1315    'lib.js': `
  1316      import {fn} from './in';
  1317      export class Test {
  1318        static method() {
  1319          return fn();
  1320        }
  1321      }
  1322    `,
  1323  }
  1324  
  1325  // Test internal import order
  1326  tests.push(
  1327    // See https://github.com/evanw/esbuild/issues/421
  1328    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1329      'in.js': `
  1330        import {foo} from './cjs'
  1331        import {bar} from './esm'
  1332        if (foo !== 1 || bar !== 2) throw 'fail'
  1333      `,
  1334      'cjs.js': `exports.foo = 1; global.internal_import_order_test1 = 2`,
  1335      'esm.js': `export let bar = global.internal_import_order_test1`,
  1336    }),
  1337    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1338      'in.js': `
  1339        if (foo !== 3 || bar !== 4) throw 'fail'
  1340        import {foo} from './cjs'
  1341        import {bar} from './esm'
  1342      `,
  1343      'cjs.js': `exports.foo = 3; global.internal_import_order_test2 = 4`,
  1344      'esm.js': `export let bar = global.internal_import_order_test2`,
  1345    }),
  1346  
  1347    // See https://github.com/evanw/esbuild/issues/542
  1348    test(['--bundle', 'in.js', '--outfile=node.js'], simpleCyclicImportTestCase542),
  1349    test(['--bundle', 'in.js', '--outfile=node.js', '--format=iife'], simpleCyclicImportTestCase542),
  1350    test(['--bundle', 'in.js', '--outfile=node.js', '--format=iife', '--global-name=someName'], simpleCyclicImportTestCase542),
  1351  )
  1352  
  1353  // Test CommonJS semantics
  1354  tests.push(
  1355    // "module.require" should work with internal modules
  1356    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1357      'in.js': `export {foo, req} from './foo'`,
  1358      'foo.js': `exports.req = module.require; exports.foo = module.require('./bar')`,
  1359      'bar.js': `exports.bar = 123`,
  1360      'node.js': `if (require('./out').foo.bar !== 123 || require('./out').req !== undefined) throw 'fail'`,
  1361    }),
  1362    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1363      'in.js': `export {foo, req} from './foo'`,
  1364      'foo.js': `exports.req = module['require']; exports.foo = module['require']('./bar')`,
  1365      'bar.js': `exports.bar = 123`,
  1366      'node.js': `if (require('./out').foo.bar !== 123 || require('./out').req !== undefined) throw 'fail'`,
  1367    }),
  1368  
  1369    // "module.require" should work with external modules
  1370    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  1371      'in.js': `export {foo} from './foo'`,
  1372      'foo.js': `exports.foo = module.require('fs').exists`,
  1373      'node.js': `if (require('./out').foo !== require('fs').exists) throw 'fail'`,
  1374    }),
  1375    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1376      'in.js': `export {foo} from './foo'`,
  1377      'foo.js': `let fn = (m, p) => m.require(p); exports.foo = fn(module, 'fs').exists`,
  1378      'node.js': `try { require('./out') } catch (e) { return } throw 'fail'`,
  1379    }),
  1380  
  1381    // "module.exports" should behave like a normal property
  1382    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1383      'in.js': `export {foo} from './foo'`,
  1384      'foo.js': `exports.foo = module.exports`,
  1385      'node.js': `if (require('./out').foo !== require('./out').foo.foo) throw 'fail'`,
  1386    }),
  1387    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1388      'in.js': `export {default} from './foo'`,
  1389      'foo.js': `module.exports = 123`,
  1390      'node.js': `if (require('./out').default !== 123) throw 'fail'`,
  1391    }),
  1392    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1393      'in.js': `export {default} from './foo'`,
  1394      'foo.js': `let m = module; m.exports = 123`,
  1395      'node.js': `if (require('./out').default !== 123) throw 'fail'`,
  1396    }),
  1397    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1398      'in.js': `export {default} from './foo'`,
  1399      'foo.js': `let fn = (m, x) => m.exports = x; fn(module, 123)`,
  1400      'node.js': `if (require('./out').default !== 123) throw 'fail'`,
  1401    }),
  1402  
  1403    // Deferred require shouldn't affect import
  1404    test(['--bundle', 'in.js', '--outfile=node.js', '--format=cjs'], {
  1405      'in.js': `
  1406        import { foo } from './a'
  1407        import './b'
  1408        if (foo !== 123) throw 'fail'
  1409      `,
  1410      'a.js': `
  1411        export let foo = 123
  1412      `,
  1413      'b.js': `
  1414        setTimeout(() => require('./a'), 0)
  1415      `,
  1416    }),
  1417  
  1418    // Test the run-time value of "typeof require"
  1419    test(['--bundle', 'in.js', '--outfile=out.js', '--format=iife'], {
  1420      'in.js': `check(typeof require)`,
  1421      'node.js': `
  1422        const out = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  1423        const check = x => value = x
  1424        let value
  1425        new Function('check', 'require', out)(check)
  1426        if (value !== 'function') throw 'fail'
  1427      `,
  1428    }),
  1429    test(['--bundle', 'in.js', '--outfile=out.js', '--format=esm'], {
  1430      'in.js': `check(typeof require)`,
  1431      'node.js': `
  1432        import fs from 'fs'
  1433        import path from 'path'
  1434        import url from 'url'
  1435        const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
  1436        const out = fs.readFileSync(__dirname + '/out.js', 'utf8')
  1437        const check = x => value = x
  1438        let value
  1439        new Function('check', 'require', out)(check)
  1440        if (value !== 'function') throw 'fail'
  1441      `,
  1442    }),
  1443    test(['--bundle', 'in.js', '--outfile=out.js', '--format=cjs'], {
  1444      'in.js': `check(typeof require)`,
  1445      'node.js': `
  1446        const out = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  1447        const check = x => value = x
  1448        let value
  1449        new Function('check', 'require', out)(check)
  1450        if (value !== 'undefined') throw 'fail'
  1451      `,
  1452    }),
  1453  )
  1454  
  1455  // Test internal CommonJS export
  1456  tests.push(
  1457    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1458      'in.js': `const out = require('./foo'); if (out.__esModule || out.foo !== 123) throw 'fail'`,
  1459      'foo.js': `exports.foo = 123`,
  1460    }),
  1461    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1462      'in.js': `const out = require('./foo'); if (out.__esModule || out !== 123) throw 'fail'`,
  1463      'foo.js': `module.exports = 123`,
  1464    }),
  1465    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1466      'in.js': `const out = require('./foo'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  1467      'foo.js': `export const foo = 123`,
  1468    }),
  1469    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1470      'in.js': `const out = require('./foo'); if (!out.__esModule || out.default !== 123) throw 'fail'`,
  1471      'foo.js': `export default 123`,
  1472    }),
  1473    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1474      'in.js': `const out = require('./foo'); if (!out.__esModule || out.default !== null) throw 'fail'`,
  1475      'foo.js': `export default function x() {} x = null`,
  1476    }),
  1477    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1478      'in.js': `const out = require('./foo'); if (!out.__esModule || out.default !== null) throw 'fail'`,
  1479      'foo.js': `export default class x {} x = null`,
  1480    }),
  1481    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1482      'in.js': `
  1483        // This is the JavaScript generated by "tsc" for the following TypeScript:
  1484        //
  1485        //   import fn from './foo'
  1486        //   if (typeof fn !== 'function') throw 'fail'
  1487        //
  1488        "use strict";
  1489        var __importDefault = (this && this.__importDefault) || function (mod) {
  1490          return (mod && mod.__esModule) ? mod : { "default": mod };
  1491        };
  1492        Object.defineProperty(exports, "__esModule", { value: true });
  1493        const foo_1 = __importDefault(require("./foo"));
  1494        if (typeof foo_1.default !== 'function')
  1495          throw 'fail';
  1496      `,
  1497      'foo.js': `export default function fn() {}`,
  1498    }),
  1499  
  1500    // Self export
  1501    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1502      'in.js': `exports.foo = 123; const out = require('./in'); if (out.__esModule || out.foo !== 123) throw 'fail'`,
  1503    }),
  1504    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1505      'in.js': `module.exports = 123; const out = require('./in'); if (out.__esModule || out !== 123) throw 'fail'`,
  1506    }),
  1507    test(['--bundle', 'in.js', '--outfile=node.js', '--format=cjs'], {
  1508      'in.js': `export const foo = 123; const out = require('./in'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  1509    }),
  1510    test(['--bundle', 'in.js', '--outfile=node.js', '--format=cjs', '--minify'], {
  1511      'in.js': `export const foo = 123; const out = require('./in'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  1512    }),
  1513    test(['--bundle', 'in.js', '--outfile=node.js', '--format=cjs'], {
  1514      'in.js': `export default 123; const out = require('./in'); if (!out.__esModule || out.default !== 123) throw 'fail'`,
  1515    }),
  1516    test(['--bundle', 'in.js', '--outfile=node.js', '--format=esm'], {
  1517      'in.js': `export const foo = 123; const out = require('./in'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  1518    }),
  1519    test(['--bundle', 'in.js', '--outfile=node.js', '--format=esm', '--minify'], {
  1520      'in.js': `export const foo = 123; const out = require('./in'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  1521    }),
  1522    test(['--bundle', 'in.js', '--outfile=node.js', '--format=esm'], {
  1523      'in.js': `export default 123; const out = require('./in'); if (!out.__esModule || out.default !== 123) throw 'fail'`,
  1524    }),
  1525  
  1526    // Test bundled and non-bundled double export star
  1527    test(['node.ts', '--bundle', '--format=cjs', '--outdir=.'], {
  1528      'node.ts': `
  1529        import {a, b} from './re-export'
  1530        if (a !== 'a' || b !== 'b') throw 'fail'
  1531      `,
  1532      're-export.ts': `
  1533        export * from './a'
  1534        export * from './b'
  1535      `,
  1536      'a.ts': `
  1537        export let a = 'a'
  1538      `,
  1539      'b.ts': `
  1540        export let b = 'b'
  1541      `,
  1542    }),
  1543    test(['node.ts', '--bundle', '--format=cjs', '--outdir=.'], {
  1544      'node.ts': `
  1545        import {a, b} from './re-export'
  1546        if (a !== 'a' || b !== 'b') throw 'fail'
  1547  
  1548        // Try forcing all of these modules to be wrappers
  1549        require('./node')
  1550        require('./re-export')
  1551        require('./a')
  1552        require('./b')
  1553      `,
  1554      're-export.ts': `
  1555        export * from './a'
  1556        export * from './b'
  1557      `,
  1558      'a.ts': `
  1559        export let a = 'a'
  1560      `,
  1561      'b.ts': `
  1562        export let b = 'b'
  1563      `,
  1564    }),
  1565    test(['node.ts', '--bundle', '--format=cjs', '--outdir=.'], {
  1566      'node.ts': `
  1567        import {a, b, c, d} from './re-export'
  1568        if (a !== 'a' || b !== 'b' || c !== 'c' || d !== 'd') throw 'fail'
  1569  
  1570        // Try forcing all of these modules to be wrappers
  1571        require('./node')
  1572        require('./re-export')
  1573        require('./a')
  1574        require('./b')
  1575      `,
  1576      're-export.ts': `
  1577        export * from './a'
  1578        export * from './b'
  1579        export * from './d'
  1580      `,
  1581      'a.ts': `
  1582        export let a = 'a'
  1583      `,
  1584      'b.ts': `
  1585        exports.b = 'b'
  1586      `,
  1587      'c.ts': `
  1588        exports.c = 'c'
  1589      `,
  1590      'd.ts': `
  1591        export * from './c'
  1592        export let d = 'd'
  1593      `,
  1594    }),
  1595    test(['node.ts', 're-export.ts', 'a.ts', 'b.ts', '--format=cjs', '--outdir=.'], {
  1596      'node.ts': `
  1597        import {a, b} from './re-export'
  1598        if (a !== 'a' || b !== 'b') throw 'fail'
  1599      `,
  1600      're-export.ts': `
  1601        export * from './a'
  1602        export * from './b'
  1603      `,
  1604      'a.ts': `
  1605        export let a = 'a'
  1606      `,
  1607      'b.ts': `
  1608        export let b = 'b'
  1609      `,
  1610    }),
  1611    test(['entry1.js', 'entry2.js', '--splitting', '--bundle', '--format=esm', '--outdir=out'], {
  1612      'entry1.js': `
  1613        import { abc, def, xyz } from './a'
  1614        export default [abc, def, xyz]
  1615      `,
  1616      'entry2.js': `
  1617        import * as x from './b'
  1618        export default x
  1619      `,
  1620      'a.js': `
  1621        export let abc = 'abc'
  1622        export * from './b'
  1623      `,
  1624      'b.js': `
  1625        export * from './c'
  1626        export const def = 'def'
  1627      `,
  1628      'c.js': `
  1629        exports.xyz = 'xyz'
  1630      `,
  1631      'node.js': `
  1632        import entry1 from './out/entry1.js'
  1633        import entry2 from './out/entry2.js'
  1634        if (entry1[0] !== 'abc' || entry1[1] !== 'def' || entry1[2] !== 'xyz') throw 'fail'
  1635        if (entry2.def !== 'def' || entry2.xyz !== 'xyz') throw 'fail'
  1636      `,
  1637    }),
  1638  
  1639    // Complex circular bundled and non-bundled import case (https://github.com/evanw/esbuild/issues/758)
  1640    test(['node.ts', '--bundle', '--format=cjs', '--outdir=.'], {
  1641      'node.ts': `
  1642        import {a} from './re-export'
  1643        let fn = a()
  1644        if (fn === a || fn() !== a) throw 'fail'
  1645      `,
  1646      're-export.ts': `
  1647        export * from './a'
  1648      `,
  1649      'a.ts': `
  1650        import {b} from './b'
  1651        export let a = () => b
  1652      `,
  1653      'b.ts': `
  1654        import {a} from './re-export'
  1655        export let b = () => a
  1656      `,
  1657    }),
  1658    test(['node.ts', '--bundle', '--format=cjs', '--outdir=.'], {
  1659      'node.ts': `
  1660        import {a} from './re-export'
  1661        let fn = a()
  1662        if (fn === a || fn() !== a) throw 'fail'
  1663  
  1664        // Try forcing all of these modules to be wrappers
  1665        require('./node')
  1666        require('./re-export')
  1667        require('./a')
  1668        require('./b')
  1669      `,
  1670      're-export.ts': `
  1671        export * from './a'
  1672      `,
  1673      'a.ts': `
  1674        import {b} from './b'
  1675        export let a = () => b
  1676      `,
  1677      'b.ts': `
  1678        import {a} from './re-export'
  1679        export let b = () => a
  1680      `,
  1681    }),
  1682    test(['node.ts', 're-export.ts', 'a.ts', 'b.ts', '--format=cjs', '--outdir=.'], {
  1683      'node.ts': `
  1684        import {a} from './re-export'
  1685        let fn = a()
  1686        if (fn === a || fn() !== a) throw 'fail'
  1687      `,
  1688      're-export.ts': `
  1689        export * from './a'
  1690      `,
  1691      'a.ts': `
  1692        import {b} from './b'
  1693        export let a = () => b
  1694      `,
  1695      'b.ts': `
  1696        import {a} from './re-export'
  1697        export let b = () => a
  1698      `,
  1699    }),
  1700  
  1701    // Failure case due to a bug in https://github.com/evanw/esbuild/pull/2059
  1702    test(['in.ts', '--bundle', '--format=cjs', '--outfile=out.js', '--external:*.cjs'], {
  1703      'in.ts': `
  1704        export * from './a.cjs'
  1705        import * as inner from './inner.js'
  1706        export { inner }
  1707      `,
  1708      'inner.ts': `export * from './b.cjs'`,
  1709      'a.cjs': `exports.a = 'a'`,
  1710      'b.cjs': `exports.b = 'b'`,
  1711      'node.js': `
  1712        const out = require('./out.js')
  1713        if (out.a !== 'a' || out.inner === void 0 || out.inner.b !== 'b' || out.b !== void 0) throw 'fail'
  1714      `,
  1715    }),
  1716  
  1717    // Validate internal and external export correctness regarding "__esModule".
  1718    // An ES module importing itself should not see "__esModule". But a CommonJS
  1719    // module importing an ES module should see "__esModule".
  1720    test(['in.ts', '--bundle', '--format=cjs', '--outfile=out.js', '--external:*.cjs'], {
  1721      'in.ts': `
  1722        export * from './a.cjs'
  1723        import * as us from './in.js'
  1724        if (us.a !== 'a' || us.__esModule !== void 0) throw 'fail'
  1725      `,
  1726      'a.cjs': `exports.a = 'a'`,
  1727      'node.js': `
  1728        const out = require('./out.js')
  1729        if (out.a !== 'a' || out.__esModule !== true) throw 'fail'
  1730      `,
  1731    }),
  1732  
  1733    // Use "eval" to access CommonJS variables
  1734    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1735      'in.js': `if (require('./eval').foo !== 123) throw 'fail'`,
  1736      'eval.js': `eval('exports.foo = 123')`,
  1737    }),
  1738    test(['--bundle', 'in.js', '--outfile=node.js'], {
  1739      'in.js': `if (require('./eval').foo !== 123) throw 'fail'`,
  1740      'eval.js': `eval('module.exports = {foo: 123}')`,
  1741    }),
  1742  )
  1743  
  1744  // Test internal ES6 export
  1745  for (const minify of [[], ['--minify']]) {
  1746    for (const target of ['es5', 'es6']) {
  1747      tests.push(
  1748        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1749          'in.js': `import * as out from './foo'; if (out.foo !== 123) throw 'fail'`,
  1750          'foo.js': `exports.foo = 123`,
  1751        }),
  1752        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1753          'in.js': `import * as out from './foo'; if (out.default !== 123) throw 'fail'`,
  1754          'foo.js': `module.exports = 123`,
  1755        }),
  1756        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1757          'in.js': `import * as out from './foo'; if (out.default !== null) throw 'fail'`,
  1758          'foo.js': `module.exports = null`,
  1759        }),
  1760        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1761          'in.js': `import * as out from './foo'; if (out.default !== void 0) throw 'fail'`,
  1762          'foo.js': `module.exports = void 0`,
  1763        }),
  1764        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1765          'in.js': `import * as out from './foo'; if (out.foo !== 123) throw 'fail'`,
  1766          'foo.js': `export var foo = 123`,
  1767        }),
  1768        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1769          'in.js': `import * as out from './foo'; if (out.default !== 123) throw 'fail'`,
  1770          'foo.js': `export default 123`,
  1771        }),
  1772  
  1773        // Self export
  1774        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1775          // Exporting like this doesn't work, but that's ok
  1776          'in.js': `exports.foo = 123; import * as out from './in'; if (out.foo !== undefined) throw 'fail'`,
  1777        }),
  1778        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1779          // Exporting like this doesn't work, but that's ok
  1780          'in.js': `module.exports = {foo: 123}; import * as out from './in'; if (out.foo !== undefined) throw 'fail'`,
  1781        }),
  1782        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1783          'in.js': `export var foo = 123; import * as out from './in'; if (out.foo !== 123) throw 'fail'`,
  1784        }),
  1785        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1786          'in.js': `export default 123; import * as out from './in'; if (out.default !== 123) throw 'fail'`,
  1787        }),
  1788  
  1789        // Check the value of "this"
  1790        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1791          'in.js': `import {foo} from './foo'; if (foo() !== (function() { return this })()) throw 'fail'`,
  1792          'foo.js': `export function foo() { return this }`,
  1793        }),
  1794        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1795          'in.js': `import foo from './foo'; if (foo() !== (function() { return this })()) throw 'fail'`,
  1796          'foo.js': `export default function() { return this }`,
  1797        }),
  1798        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1799          'in.js': `import {foo} from './foo'; require('./foo'); if (foo() !== (function() { return this })()) throw 'fail'`,
  1800          'foo.js': `export function foo() { return this }`,
  1801        }),
  1802        test(['--bundle', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1803          'in.js': `import foo from './foo'; require('./foo'); if (foo() !== (function() { return this })()) throw 'fail'`,
  1804          'foo.js': `export default function() { return this }`,
  1805        }),
  1806        test(['--bundle', '--external:./foo', '--format=cjs', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1807          'in.js': `import {foo} from './foo'; if (foo() !== (function() { return this })()) throw 'fail'`,
  1808          'foo.js': `exports.foo = function() { return this }`,
  1809        }),
  1810        test(['--bundle', '--external:./foo', '--format=cjs', 'in.js', '--outfile=node.js', '--target=' + target].concat(minify), {
  1811          'in.js': `import foo from './foo'; if (foo() !== (function() { return this })()) throw 'fail'`,
  1812          'foo.js': `module.exports = function() { return this }`,
  1813        }),
  1814      )
  1815    }
  1816  
  1817    tests.push(
  1818      // Make sure entry points where a dependency has top-level await are awaited
  1819      test(['--bundle', 'in.js', '--outfile=out.js', '--format=esm'].concat(minify), {
  1820        'in.js': `import './foo'; import('./in.js'); throw 'fail'`,
  1821        'foo.js': `throw await 'stop'`,
  1822        'node.js': `export let async = async () => { try { await import('./out.js') } catch (e) { if (e === 'stop') return } throw 'fail' }`,
  1823      }, { async: true }),
  1824  
  1825      // Self export
  1826      test(['--bundle', 'in.js', '--outfile=node.js', '--format=esm'].concat(minify), {
  1827        'in.js': `export default 123; export let async = async () => { const out = await import('./in'); if (out.default !== 123) throw 'fail' }`,
  1828      }, { async: true }),
  1829      test(['--bundle', 'in.js', '--outfile=node.js', '--format=esm'].concat(minify), {
  1830        'in.js': `export default 123; import * as out from './in'; export let async = async () => { await import('./in'); if (out.default !== 123) throw 'fail' }`,
  1831      }, { async: true }),
  1832  
  1833      // Inject
  1834      test(['--bundle', 'node.ts', 'node2.ts', '--outdir=.', '--format=esm', '--inject:foo.js', '--splitting'].concat(minify), {
  1835        'node.ts': `if (foo.bar !== 123) throw 'fail'`,
  1836        'node2.ts': `throw [foo.bar, require('./node2.ts')] // Force this file to be lazily initialized so foo.js is lazily initialized`,
  1837        'foo.js': `export let foo = {bar: 123}`,
  1838      }),
  1839  
  1840      // https://github.com/evanw/esbuild/issues/2793
  1841      test(['--bundle', 'src/index.js', '--outfile=node.js', '--format=esm'].concat(minify), {
  1842        'src/a.js': `
  1843          export const A = 42;
  1844        `,
  1845        'src/b.js': `
  1846          export const B = async () => (await import(".")).A
  1847        `,
  1848        'src/index.js': `
  1849          export * from "./a"
  1850          export * from "./b"
  1851          import { B } from '.'
  1852          export let async = async () => { if (42 !== await B()) throw 'fail' }
  1853        `,
  1854      }, { async: true }),
  1855      test(['--bundle', 'src/node.js', '--outdir=.', '--format=esm', '--splitting'].concat(minify), {
  1856        'src/a.js': `
  1857          export const A = 42;
  1858        `,
  1859        'src/b.js': `
  1860          export const B = async () => (await import("./node")).A
  1861        `,
  1862        'src/node.js': `
  1863          export * from "./a"
  1864          export * from "./b"
  1865          import { B } from './node'
  1866          export let async = async () => { if (42 !== await B()) throw 'fail' }
  1867        `,
  1868      }, { async: true }),
  1869    )
  1870  }
  1871  
  1872  // Check that duplicate top-level exports don't collide in the presence of "eval"
  1873  tests.push(
  1874    test(['--bundle', '--format=esm', 'in.js', '--outfile=node.js'], {
  1875      'in.js': `
  1876        import a from './a'
  1877        if (a !== 'runner1.js') throw 'fail'
  1878        import b from './b'
  1879        if (b !== 'runner2.js') throw 'fail'
  1880      `,
  1881      'a.js': `
  1882        import { run } from './runner1'
  1883        export default run()
  1884      `,
  1885      'runner1.js': `
  1886        let data = eval('"runner1" + ".js"')
  1887        export function run() { return data }
  1888      `,
  1889      'b.js': `
  1890        import { run } from './runner2'
  1891        export default run()
  1892      `,
  1893      'runner2.js': `
  1894        let data = eval('"runner2" + ".js"')
  1895        export function run() { return data }
  1896      `,
  1897    }, {
  1898      // There are two possible output orders due to log output order non-determinism
  1899      expectedStderr: [
  1900        `▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1901  
  1902      runner1.js:2:17:
  1903        2 │       let data = eval('"runner1" + ".js"')
  1904          ╵                  ~~~~
  1905  
  1906    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1907  
  1908  ▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1909  
  1910      runner2.js:2:17:
  1911        2 │       let data = eval('"runner2" + ".js"')
  1912          ╵                  ~~~~
  1913  
  1914    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1915  
  1916  `, `▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1917  
  1918      runner2.js:2:17:
  1919        2 │       let data = eval('"runner2" + ".js"')
  1920          ╵                  ~~~~
  1921  
  1922    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1923  
  1924  ▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1925  
  1926      runner1.js:2:17:
  1927        2 │       let data = eval('"runner1" + ".js"')
  1928          ╵                  ~~~~
  1929  
  1930    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1931  
  1932  `,
  1933      ],
  1934    }),
  1935    test(['--bundle', '--format=esm', '--splitting', 'in.js', 'in2.js', '--outdir=out'], {
  1936      'in.js': `
  1937        import a from './a'
  1938        import b from './b'
  1939        export default [a, b]
  1940      `,
  1941      'a.js': `
  1942        import { run } from './runner1'
  1943        export default run()
  1944      `,
  1945      'runner1.js': `
  1946        let data = eval('"runner1" + ".js"')
  1947        export function run() { return data }
  1948      `,
  1949      'b.js': `
  1950        import { run } from './runner2'
  1951        export default run()
  1952      `,
  1953      'runner2.js': `
  1954        let data = eval('"runner2" + ".js"')
  1955        export function run() { return data }
  1956      `,
  1957      'in2.js': `
  1958        import { run } from './runner2'
  1959        export default run()
  1960      `,
  1961      'node.js': `
  1962        import ab from './out/in.js'
  1963        if (ab[0] !== 'runner1.js' || ab[1] !== 'runner2.js') throw 'fail'
  1964      `,
  1965    }, {
  1966      // There are two possible output orders due to log output order non-determinism
  1967      expectedStderr: [
  1968        `▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1969  
  1970      runner1.js:2:17:
  1971        2 │       let data = eval('"runner1" + ".js"')
  1972          ╵                  ~~~~
  1973  
  1974    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1975  
  1976  ▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1977  
  1978      runner2.js:2:17:
  1979        2 │       let data = eval('"runner2" + ".js"')
  1980          ╵                  ~~~~
  1981  
  1982    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1983  
  1984  `, `▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1985  
  1986      runner2.js:2:17:
  1987        2 │       let data = eval('"runner2" + ".js"')
  1988          ╵                  ~~~~
  1989  
  1990    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1991  
  1992  ▲ [WARNING] Using direct eval with a bundler is not recommended and may cause problems [direct-eval]
  1993  
  1994      runner1.js:2:17:
  1995        2 │       let data = eval('"runner1" + ".js"')
  1996          ╵                  ~~~~
  1997  
  1998    You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval
  1999  
  2000  `,
  2001      ],
  2002    }),
  2003  )
  2004  
  2005  // Test "default" exports in ESM-to-CommonJS conversion scenarios
  2006  tests.push(
  2007    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2008      'in.js': `import def from './foo'; if (def !== 123) throw 'fail'`,
  2009      'foo.js': `exports.__esModule = true; exports.default = 123`,
  2010    }),
  2011    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2012      'in.js': `import * as ns from './foo'; if (ns.default !== 123) throw 'fail'`,
  2013      'foo.js': `exports.__esModule = true; exports.default = 123`,
  2014    }),
  2015    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2016      'in.js': `import def from './foo'; if (def !== void 0) throw 'fail'`,
  2017      'foo.js': `exports.__esModule = true; exports.foo = 123`,
  2018    }),
  2019    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2020      'in.js': `import * as ns from './foo'; if (ns.default !== void 0 || ns.foo !== 123) throw 'fail'`,
  2021      'foo.js': `exports.__esModule = true; exports.foo = 123`,
  2022    }),
  2023    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2024      'in.js': `import def from './foo'; if (!def || def.foo !== 123) throw 'fail'`,
  2025      'foo.js': `exports.__esModule = false; exports.foo = 123`,
  2026    }),
  2027    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2028      'in.js': `import * as ns from './foo'; if (!ns.default || ns.default.foo !== 123) throw 'fail'`,
  2029      'foo.js': `exports.__esModule = false; exports.foo = 123`,
  2030    }),
  2031    test(['in.mjs', '--outfile=node.js', '--format=cjs'], {
  2032      'in.mjs': `import def from './foo'; if (!def || def.foo !== 123) throw 'fail'`,
  2033      'foo.js': `exports.__esModule = true; exports.foo = 123`,
  2034    }),
  2035    test(['in.mjs', '--outfile=node.js', '--format=cjs'], {
  2036      'in.mjs': `import * as ns from './foo'; if (!ns.default || ns.default.foo !== 123) throw 'fail'`,
  2037      'foo.js': `exports.__esModule = true; exports.foo = 123`,
  2038    }),
  2039  
  2040    // Make sure "import {} from; export {}" behaves like "export {} from"
  2041    // https://github.com/evanw/esbuild/issues/1890
  2042    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2043      'node.ts': `import * as foo from './foo.js'; if (foo.bar !== 123) throw 'fail'`,
  2044      'foo.ts': `import bar from './lib.js'; export { bar }`,
  2045      'lib.js': `module.exports = 123`,
  2046    }),
  2047    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2048      'node.ts': `import * as foo from './foo.js'; if (foo.bar !== 123) throw 'fail'`,
  2049      'foo.ts': `import { default as bar } from './lib.js'; export { bar }`,
  2050      'lib.js': `module.exports = 123`,
  2051    }),
  2052    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2053      'node.ts': `import * as foo from './foo.js'; if (foo.bar !== 123) throw 'fail'`,
  2054      'foo.ts': `export { default as bar } from './lib.js'`,
  2055      'lib.js': `module.exports = 123`,
  2056    }),
  2057    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2058      'node.ts': `import { foo } from './foo.js'; if (foo.default !== 123) throw 'fail'`,
  2059      'foo.ts': `import * as foo from './lib.js'; export { foo }`,
  2060      'lib.js': `module.exports = 123`,
  2061    }),
  2062    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2063      'node.ts': `import { foo } from './foo.js'; if (foo.default !== 123) throw 'fail'`,
  2064      'foo.ts': `export * as foo from './lib.js'`,
  2065      'lib.js': `module.exports = 123`,
  2066    }),
  2067    test(['node.ts', 'foo.ts', '--outdir=.', '--format=cjs'], {
  2068      'node.ts': `import * as foo from './foo.js'; if (foo.default !== void 0) throw 'fail'`,
  2069      'foo.ts': `export * from './lib.js'`,
  2070      'lib.js': `module.exports = 123`,
  2071    }),
  2072  )
  2073  
  2074  // Test external wildcards
  2075  tests.push(
  2076    test(['--bundle', 'src/foo.js', '--outfile=node.js', '--external:./src/dir/*', '--format=cjs'], {
  2077      'src/foo.js': `
  2078        function foo() {
  2079          require('./dir/bar')
  2080        }
  2081        let worked = false
  2082        try {
  2083          foo()
  2084          worked = true
  2085        } catch (e) {
  2086        }
  2087        if (worked) throw 'fail'
  2088      `,
  2089    }),
  2090    test(['--bundle', 'src/foo.js', '--outfile=node.js', '--external:./src/dir/*', '--format=cjs'], {
  2091      'src/foo.js': `
  2092        require('./dir/bar')
  2093      `,
  2094      'src/dir/bar.js': ``,
  2095    }),
  2096  )
  2097  
  2098  // Test external CommonJS export
  2099  tests.push(
  2100    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs'], {
  2101      'foo.js': `exports.foo = 123`,
  2102      'node.js': `const out = require('./out'); if (out.__esModule || out.foo !== 123) throw 'fail'`,
  2103    }),
  2104    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs'], {
  2105      'foo.js': `module.exports = 123`,
  2106      'node.js': `const out = require('./out'); if (out.__esModule || out !== 123) throw 'fail'`,
  2107    }),
  2108    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm'], {
  2109      'foo.js': `exports.foo = 123`,
  2110      'node.js': `import out from './out.js'; if (out.foo !== 123) throw 'fail'`,
  2111    }),
  2112    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm'], {
  2113      'foo.js': `module.exports = 123`,
  2114      'node.js': `import out from './out.js'; if (out !== 123) throw 'fail'`,
  2115    }),
  2116  )
  2117  
  2118  // Test external ES6 export
  2119  tests.push(
  2120    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs'], {
  2121      'foo.js': `export const foo = 123`,
  2122      'node.js': `const out = require('./out'); if (!out.__esModule || out.foo !== 123) throw 'fail'`,
  2123    }),
  2124    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs'], {
  2125      'foo.js': `export default 123`,
  2126      'node.js': `const out = require('./out'); if (!out.__esModule || out.default !== 123) throw 'fail'`,
  2127    }),
  2128    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm'], {
  2129      'foo.js': `export const foo = 123`,
  2130      'node.js': `import {foo} from './out.js'; if (foo !== 123) throw 'fail'`,
  2131    }),
  2132    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm'], {
  2133      'foo.js': `export default 123`,
  2134      'node.js': `import out from './out.js'; if (out !== 123) throw 'fail'`,
  2135    }),
  2136    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--platform=node'], {
  2137      'foo.js': `
  2138        export function confuseNode(exports) {
  2139          // If this local is called "exports", node incorrectly
  2140          // thinks this file has an export called "notAnExport".
  2141          // We must make sure that it doesn't have that name
  2142          // when targeting Node with CommonJS. See also:
  2143          // https://github.com/evanw/esbuild/issues/3544
  2144          exports.notAnExport = function() {
  2145          };
  2146        }
  2147      `,
  2148      'node.js': `
  2149        exports.async = async () => {
  2150          const foo = await import('./out.js')
  2151          if (typeof foo.confuseNode !== 'function') throw 'fail: confuseNode'
  2152          if ('notAnExport' in foo) throw 'fail: notAnExport'
  2153        }
  2154      `,
  2155    }, { async: true }),
  2156  
  2157    // External package
  2158    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  2159      'foo.js': `import {exists} from "fs"; export {exists}`,
  2160      'node.js': `const out = require('./out'); if (!out.__esModule || out.exists !== require('fs').exists) throw 'fail'`,
  2161    }),
  2162    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm', '--external:fs'], {
  2163      'foo.js': `import {exists} from "fs"; export {exists}`,
  2164      'node.js': `import {exists} from "./out.js"; import * as fs from "fs"; if (exists !== fs.exists) throw 'fail'`,
  2165    }),
  2166    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  2167      'foo.js': `import * as fs from "fs"; export let exists = fs.exists`,
  2168      'node.js': `const out = require('./out'); if (!out.__esModule || out.exists !== require('fs').exists) throw 'fail'`,
  2169    }),
  2170    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm', '--external:fs'], {
  2171      'foo.js': `import * as fs from "fs"; export let exists = fs.exists`,
  2172      'node.js': `import {exists} from "./out.js"; import * as fs from "fs"; if (exists !== fs.exists) throw 'fail'`,
  2173    }),
  2174    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  2175      'foo.js': `export {exists} from "fs"`,
  2176      'node.js': `const out = require('./out'); if (!out.__esModule || out.exists !== require('fs').exists) throw 'fail'`,
  2177    }),
  2178    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm', '--external:fs'], {
  2179      'foo.js': `export {exists} from "fs"`,
  2180      'node.js': `import {exists} from "./out.js"; import * as fs from "fs"; if (exists !== fs.exists) throw 'fail'`,
  2181    }),
  2182    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  2183      'foo.js': `export * from "fs"`,
  2184      'node.js': `const out = require('./out'); if (!out.__esModule || out.exists !== require('fs').exists) throw 'fail'`,
  2185    }),
  2186    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm', '--external:fs'], {
  2187      'foo.js': `export * from "fs"`,
  2188      'node.js': `import {exists} from "./out.js"; import * as fs from "fs"; if (exists !== fs.exists) throw 'fail'`,
  2189    }),
  2190    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=cjs', '--external:fs'], {
  2191      'foo.js': `export * as star from "fs"`,
  2192      'node.js': `const out = require('./out'); if (!out.__esModule || out.star.exists !== require('fs').exists) throw 'fail'`,
  2193    }),
  2194    test(['--bundle', 'foo.js', '--outfile=out.js', '--format=esm', '--external:fs'], {
  2195      'foo.js': `export * as star from "fs"`,
  2196      'node.js': `import {star} from "./out.js"; import * as fs from "fs"; if (star.exists !== fs.exists) throw 'fail'`,
  2197    }),
  2198  )
  2199  
  2200  // ES6 export star of CommonJS module
  2201  tests.push(
  2202    // Internal
  2203    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  2204      'entry.js': `import * as ns from './re-export'; if (ns.foo !== 123) throw 'fail'`,
  2205      're-export.js': `export * from './commonjs'`,
  2206      'commonjs.js': `exports.foo = 123`,
  2207    }),
  2208    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  2209      'entry.js': `import {foo} from './re-export'; if (foo !== 123) throw 'fail'`,
  2210      're-export.js': `export * from './commonjs'`,
  2211      'commonjs.js': `exports.foo = 123`,
  2212    }),
  2213  
  2214    // External
  2215    test(['--bundle', 'entry.js', '--outfile=node.js', '--external:fs'], {
  2216      'entry.js': `import * as ns from './re-export'; if (typeof ns.exists !== 'function') throw 'fail'`,
  2217      're-export.js': `export * from 'fs'`,
  2218    }),
  2219    test(['--bundle', 'entry.js', '--outfile=node.js', '--external:fs'], {
  2220      'entry.js': `import {exists} from './re-export'; if (typeof exists !== 'function') throw 'fail'`,
  2221      're-export.js': `export * from 'fs'`,
  2222    }),
  2223  
  2224    // External (masked)
  2225    test(['--bundle', 'entry.js', '--outfile=node.js', '--external:fs'], {
  2226      'entry.js': `import * as ns from './re-export'; if (ns.exists !== 123) throw 'fail'`,
  2227      're-export.js': `export * from 'fs'; export let exists = 123`,
  2228    }),
  2229    test(['--bundle', 'entry.js', '--outfile=node.js', '--external:fs'], {
  2230      'entry.js': `import {exists} from './re-export'; if (exists !== 123) throw 'fail'`,
  2231      're-export.js': `export * from 'fs'; export let exists = 123`,
  2232    }),
  2233  
  2234    // Export CommonJS export from ES6 module
  2235    test(['--bundle', 'entry.js', '--outfile=out.js', '--format=cjs'], {
  2236      'entry.js': `export {bar} from './foo'`,
  2237      'foo.js': `exports.bar = 123`,
  2238      'node.js': `const out = require('./out.js'); if (out.bar !== 123) throw 'fail'`,
  2239    }),
  2240    test(['--bundle', 'entry.js', '--outfile=out.js', '--format=esm'], {
  2241      'entry.js': `export {bar} from './foo'`,
  2242      'foo.js': `exports.bar = 123`,
  2243      'node.js': `import {bar} from './out.js'; if (bar !== 123) throw 'fail'`,
  2244    }),
  2245  )
  2246  
  2247  // Test imports from modules without any imports
  2248  tests.push(
  2249    test(['in.js', '--outfile=node.js', '--bundle'], {
  2250      'in.js': `
  2251        import * as ns from 'pkg'
  2252        if (ns.default === void 0) throw 'fail'
  2253      `,
  2254      'node_modules/pkg/index.js': ``,
  2255    }),
  2256    test(['in.js', '--outfile=node.js', '--bundle'], {
  2257      'in.js': `
  2258        import * as ns from 'pkg/index.cjs'
  2259        if (ns.default === void 0) throw 'fail'
  2260      `,
  2261      'node_modules/pkg/index.cjs': ``,
  2262    }),
  2263    test(['in.js', '--outfile=node.js', '--bundle'], {
  2264      'in.js': `
  2265        import * as ns from 'pkg/index.cts'
  2266        if (ns.default === void 0) throw 'fail'
  2267      `,
  2268      'node_modules/pkg/index.cts': ``,
  2269    }),
  2270    test(['in.js', '--outfile=node.js', '--bundle'], {
  2271      'in.js': `
  2272        import * as ns from 'pkg/index.mjs'
  2273        if (ns.default !== void 0) throw 'fail'
  2274      `,
  2275      'node_modules/pkg/index.mjs': ``,
  2276    }, {
  2277      expectedStderr: `▲ [WARNING] Import "default" will always be undefined because there is no matching export in "node_modules/pkg/index.mjs" [import-is-undefined]
  2278  
  2279      in.js:3:13:
  2280        3 │       if (ns.default !== void 0) throw 'fail'
  2281          ╵              ~~~~~~~
  2282  
  2283  `,
  2284    }),
  2285    test(['in.js', '--outfile=node.js', '--bundle'], {
  2286      'in.js': `
  2287        import * as ns from 'pkg/index.mts'
  2288        if (ns.default !== void 0) throw 'fail'
  2289      `,
  2290      'node_modules/pkg/index.mts': ``,
  2291    }, {
  2292      expectedStderr: `▲ [WARNING] Import "default" will always be undefined because there is no matching export in "node_modules/pkg/index.mts" [import-is-undefined]
  2293  
  2294      in.js:3:13:
  2295        3 │       if (ns.default !== void 0) throw 'fail'
  2296          ╵              ~~~~~~~
  2297  
  2298  `,
  2299    }),
  2300    test(['in.js', '--outfile=node.js', '--bundle'], {
  2301      'in.js': `
  2302        import * as ns from 'pkg'
  2303        if (ns.default === void 0) throw 'fail'
  2304      `,
  2305      'node_modules/pkg/package.json': `{
  2306        "type": "commonjs"
  2307      }`,
  2308      'node_modules/pkg/index.js': ``,
  2309    }),
  2310    test(['in.js', '--outfile=node.js', '--bundle'], {
  2311      'in.js': `
  2312        import * as ns from 'pkg'
  2313        if (ns.default !== void 0) throw 'fail'
  2314      `,
  2315      'node_modules/pkg/package.json': `{
  2316        "type": "module"
  2317      }`,
  2318      'node_modules/pkg/index.js': ``,
  2319    }, {
  2320      expectedStderr: `▲ [WARNING] Import "default" will always be undefined because there is no matching export in "node_modules/pkg/index.js" [import-is-undefined]
  2321  
  2322      in.js:3:13:
  2323        3 │       if (ns.default !== void 0) throw 'fail'
  2324          ╵              ~~~~~~~
  2325  
  2326  `,
  2327    }),
  2328    test(['in.js', '--outfile=node.js', '--bundle', '--external:pkg'], {
  2329      'in.js': `
  2330        import * as ns from 'pkg'
  2331        if (ns.default === void 0) throw 'fail'
  2332      `,
  2333      'node_modules/pkg/index.js': ``,
  2334    }),
  2335    test(['in.js', '--outfile=node.js', '--bundle', '--external:pkg'], {
  2336      'in.js': `
  2337        import * as ns from 'pkg'
  2338        if (ns.foo !== void 0) throw 'fail'
  2339      `,
  2340      'node_modules/pkg/index.js': ``,
  2341    }),
  2342  )
  2343  
  2344  // Test imports not being able to access the namespace object
  2345  tests.push(
  2346    test(['in.js', '--outfile=node.js', '--bundle'], {
  2347      'in.js': `
  2348        import {foo} from './esm'
  2349        if (foo !== 123) throw 'fail'
  2350      `,
  2351      'esm.js': `Object.defineProperty(exports, 'foo', {value: 123, enumerable: false})`,
  2352    }),
  2353    test(['in.js', '--outfile=node.js', '--bundle'], {
  2354      'in.js': `
  2355        import * as ns from './esm'
  2356        if (ns[Math.random() < 2 && 'foo'] !== 123) throw 'fail'
  2357      `,
  2358      'esm.js': `Object.defineProperty(exports, 'foo', {value: 123, enumerable: false})`,
  2359    }),
  2360  )
  2361  
  2362  // Test imports of properties from the prototype chain of "module.exports" for Webpack compatibility
  2363  tests.push(
  2364    // Imports
  2365    test(['in.js', '--outfile=node.js', '--bundle'], {
  2366      'in.js': `
  2367        import def from './cjs-proto'
  2368        import {prop} from './cjs-proto'
  2369        if (def.prop !== 123 || prop !== 123) throw 'fail'
  2370      `,
  2371      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2372    }),
  2373    test(['in.js', '--outfile=node.js', '--bundle'], {
  2374      'in.js': `
  2375        import def, {prop} from './cjs-proto' // The TypeScript compiler fails with this syntax
  2376        if (def.prop !== 123 || prop !== 123) throw 'fail'
  2377      `,
  2378      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2379    }),
  2380    test(['in.js', '--outfile=node.js', '--bundle'], {
  2381      'in.js': `
  2382        import * as star from './cjs-proto'
  2383        if (!star.default || star.default.prop !== 123 || star.prop !== 123) throw 'fail'
  2384      `,
  2385      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2386    }),
  2387  
  2388    // Re-exports
  2389    test(['in.js', '--outfile=node.js', '--bundle'], {
  2390      'in.js': `
  2391        import * as test from './reexport'
  2392        if (test.def.prop !== 123 || test.prop !== 123) throw 'fail'
  2393      `,
  2394      'reexport.js': `
  2395        export {default as def} from './cjs-proto'
  2396        export {prop} from './cjs-proto'
  2397      `,
  2398      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2399    }),
  2400    test(['in.js', '--outfile=node.js', '--bundle'], {
  2401      'in.js': `
  2402        import * as test from './reexport'
  2403        if (test.def.prop !== 123 || test.prop !== 123) throw 'fail'
  2404      `,
  2405      'reexport.js': `
  2406        export {default as def, prop} from './cjs-proto' // The TypeScript compiler fails with this syntax
  2407      `,
  2408      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2409    }),
  2410    test(['in.js', '--outfile=node.js', '--bundle'], {
  2411      'in.js': `
  2412        import * as test from './reexport'
  2413        // Note: the specification says to ignore default exports in "export * from"
  2414        // Note: re-exporting prototype properties using "export * from" is not supported
  2415        if (test.default || test.prop !== void 0) throw 'fail'
  2416      `,
  2417      'reexport.js': `
  2418        export * from './cjs-proto'
  2419      `,
  2420      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2421    }),
  2422    test(['in.js', '--outfile=node.js', '--bundle'], {
  2423      'in.js': `
  2424        import {star} from './reexport'
  2425        if (!star.default || star.default.prop !== 123 || star.prop !== 123) throw 'fail'
  2426      `,
  2427      'reexport.js': `
  2428        export * as star from './cjs-proto'
  2429      `,
  2430      'cjs-proto.js': `module.exports = Object.create({prop: 123})`,
  2431    }),
  2432  )
  2433  
  2434  // Test for format conversion without bundling
  2435  tests.push(
  2436    // ESM => ESM
  2437    test(['in.js', '--outfile=node.js', '--format=esm'], {
  2438      'in.js': `
  2439        import {exists} from 'fs'
  2440        if (!exists) throw 'fail'
  2441      `,
  2442    }),
  2443    test(['in.js', '--outfile=node.js', '--format=esm'], {
  2444      'in.js': `
  2445        import fs from 'fs'
  2446        if (!fs.exists) throw 'fail'
  2447      `,
  2448    }),
  2449    test(['in.js', '--outfile=node.js', '--format=esm'], {
  2450      'in.js': `
  2451        import * as fs from 'fs'
  2452        if (!fs.exists) throw 'fail'
  2453      `,
  2454    }),
  2455    test(['in.js', '--outfile=node.js', '--format=esm'], {
  2456      'in.js': `
  2457        let fn = async () => {
  2458          let fs = await import('fs')
  2459          if (!fs.exists) throw 'fail'
  2460        }
  2461        export {fn as async}
  2462      `,
  2463    }, { async: true }),
  2464    test(['in.js', '--outfile=out.js', '--format=esm'], {
  2465      'in.js': `
  2466        export let foo = 'abc'
  2467        export default function() {
  2468          return 123
  2469        }
  2470      `,
  2471      'node.js': `
  2472        import * as out from './out.js'
  2473        if (out.foo !== 'abc' || out.default() !== 123) throw 'fail'
  2474      `,
  2475    }),
  2476  
  2477    // ESM => CJS
  2478    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2479      'in.js': `
  2480        import {exists} from 'fs'
  2481        if (!exists) throw 'fail'
  2482      `,
  2483    }),
  2484    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2485      'in.js': `
  2486        import fs from 'fs'
  2487        if (!fs.exists) throw 'fail'
  2488      `,
  2489    }),
  2490    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2491      'in.js': `
  2492        import * as fs from 'fs'
  2493        if (!fs.exists) throw 'fail'
  2494      `,
  2495    }),
  2496    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2497      'in.js': `
  2498        let fn = async () => {
  2499          let fs = await import('fs')
  2500          if (!fs.exists) throw 'fail'
  2501        }
  2502        export {fn as async}
  2503      `,
  2504    }, { async: true }),
  2505    test(['in.js', '--outfile=out.js', '--format=cjs'], {
  2506      'in.js': `
  2507        export let foo = 'abc'
  2508        export default function() {
  2509          return 123
  2510        }
  2511      `,
  2512      'node.js': `
  2513        const out = require('./out.js')
  2514        if (out.foo !== 'abc' || out.default() !== 123) throw 'fail'
  2515      `,
  2516    }),
  2517    test(['in.js', '--outfile=out.cjs', '--format=cjs', '--platform=node'], {
  2518      'in.js': `
  2519        export let foo = 123
  2520        let bar = 234
  2521        export { bar as if }
  2522        export default 345
  2523      `,
  2524      'node.js': `
  2525        exports.async = async () => {
  2526          let out = await import('./out.cjs')
  2527          let keys = Object.keys(out)
  2528          if (
  2529            !keys.includes('default') || !keys.includes('foo') || !keys.includes('if') ||
  2530            out.foo !== 123 || out.if !== 234 ||
  2531            out.default.foo !== 123 || out.default.if !== 234 || out.default.default !== 345
  2532          ) throw 'fail'
  2533        }
  2534      `,
  2535    }, { async: true }),
  2536  
  2537    // https://github.com/evanw/esbuild/issues/3029
  2538    test([
  2539      'node_modules/util-ex/src/index.js',
  2540      'node_modules/util-ex/src/fn1.js',
  2541      'node_modules/util-ex/src/fn2.js',
  2542      '--outdir=node_modules/util-ex/lib',
  2543      '--format=cjs',
  2544      '--platform=node',
  2545    ], {
  2546      'node_modules/util-ex/src/index.js': `
  2547        export * from './fn1'
  2548        export * from './fn2'
  2549      `,
  2550      'node_modules/util-ex/src/fn1.js': `
  2551        export function fn1() { return 1 }
  2552        export default fn1
  2553      `,
  2554      'node_modules/util-ex/src/fn2.js': `
  2555        export function fn2() { return 2 }
  2556        export default fn2
  2557      `,
  2558      'node_modules/util-ex/package.json': `{
  2559        "main": "./lib/index.js",
  2560        "type": "commonjs"
  2561      }`,
  2562      'node.js': `
  2563        import { fn1, fn2 } from 'util-ex'
  2564        if (fn1() !== 1) throw 'fail 1'
  2565        if (fn2() !== 2) throw 'fail 2'
  2566      `,
  2567      'package.json': `{
  2568        "type": "module"
  2569      }`,
  2570    }),
  2571  
  2572    // ESM => IIFE
  2573    test(['in.js', '--outfile=node.js', '--format=iife'], {
  2574      'in.js': `
  2575        import {exists} from 'fs'
  2576        if (!exists) throw 'fail'
  2577      `,
  2578    }),
  2579    test(['in.js', '--outfile=node.js', '--format=iife'], {
  2580      'in.js': `
  2581        import fs from 'fs'
  2582        if (!fs.exists) throw 'fail'
  2583      `,
  2584    }),
  2585    test(['in.js', '--outfile=node.js', '--format=iife'], {
  2586      'in.js': `
  2587        import * as fs from 'fs'
  2588        if (!fs.exists) throw 'fail'
  2589      `,
  2590    }),
  2591    test(['in.js', '--outfile=out.js', '--format=iife', '--global-name=test'], {
  2592      'in.js': `
  2593        let fn = async () => {
  2594          let fs = await import('fs')
  2595          if (!fs.exists) throw 'fail'
  2596        }
  2597        export {fn as async}
  2598      `,
  2599      'node.js': `
  2600        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2601        const out = new Function('require', code + '; return test')(require)
  2602        exports.async = out.async
  2603      `,
  2604    }, { async: true }),
  2605    test(['in.js', '--outfile=out.js', '--format=iife', '--global-name=test'], {
  2606      'in.js': `
  2607        export let foo = 'abc'
  2608        export default function() {
  2609          return 123
  2610        }
  2611      `,
  2612      'node.js': `
  2613        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2614        const out = new Function(code + '; return test')()
  2615        if (out.foo !== 'abc' || out.default() !== 123) throw 'fail'
  2616      `,
  2617    }),
  2618  
  2619    // JSON
  2620    test(['in.json', '--outfile=out.js', '--format=esm'], {
  2621      'in.json': `{"foo": 123}`,
  2622      'node.js': `
  2623        import def from './out.js'
  2624        import {foo} from './out.js'
  2625        if (foo !== 123 || def.foo !== 123) throw 'fail'
  2626      `,
  2627    }),
  2628    test(['in.json', '--outfile=out.js', '--format=cjs'], {
  2629      'in.json': `{"foo": 123}`,
  2630      'node.js': `
  2631        const out = require('./out.js')
  2632        if (out.foo !== 123) throw 'fail'
  2633      `,
  2634    }),
  2635    test(['in.json', '--outfile=out.js', '--format=iife', '--global-name=test'], {
  2636      'in.json': `{"foo": 123}`,
  2637      'node.js': `
  2638        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2639        const out = new Function(code + '; return test')()
  2640        if (out.foo !== 123) throw 'fail'
  2641      `,
  2642    }),
  2643  
  2644    // CJS => CJS
  2645    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2646      'in.js': `
  2647        const {exists} = require('fs')
  2648        if (!exists) throw 'fail'
  2649      `,
  2650    }),
  2651    test(['in.js', '--outfile=node.js', '--format=cjs'], {
  2652      'in.js': `
  2653        const fs = require('fs')
  2654        if (!fs.exists) throw 'fail'
  2655      `,
  2656    }),
  2657    test(['in.js', '--outfile=out.js', '--format=cjs'], {
  2658      'in.js': `
  2659        module.exports = 123
  2660      `,
  2661      'node.js': `
  2662        const out = require('./out.js')
  2663        if (out !== 123) throw 'fail'
  2664      `,
  2665    }),
  2666    test(['in.js', '--outfile=out.js', '--format=cjs'], {
  2667      'in.js': `
  2668        exports.foo = 123
  2669      `,
  2670      'node.js': `
  2671        const out = require('./out.js')
  2672        if (out.foo !== 123) throw 'fail'
  2673      `,
  2674    }),
  2675  
  2676    // CJS => IIFE
  2677    test(['in.js', '--outfile=out.js', '--format=iife'], {
  2678      'in.js': `
  2679        const {exists} = require('fs')
  2680        if (!exists) throw 'fail'
  2681      `,
  2682      'node.js': `
  2683        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2684        new Function('require', code)(require)
  2685      `,
  2686    }),
  2687    test(['in.js', '--outfile=out.js', '--format=iife'], {
  2688      'in.js': `
  2689        const fs = require('fs')
  2690        if (!fs.exists) throw 'fail'
  2691      `,
  2692      'node.js': `
  2693        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2694        new Function('require', code)(require)
  2695      `,
  2696    }),
  2697    test(['in.js', '--outfile=out.js', '--format=iife', '--global-name=test'], {
  2698      'in.js': `
  2699        module.exports = 123
  2700      `,
  2701      'node.js': `
  2702        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2703        const out = new Function(code + '; return test')()
  2704        if (out !== 123) throw 'fail'
  2705      `,
  2706    }),
  2707    test(['in.js', '--outfile=out.js', '--format=iife', '--global-name=test'], {
  2708      'in.js': `
  2709        exports.foo = 123
  2710      `,
  2711      'node.js': `
  2712        const code = require('fs').readFileSync(__dirname + '/out.js', 'utf8')
  2713        const out = new Function(code + '; return test')()
  2714        if (out.foo !== 123) throw 'fail'
  2715      `,
  2716    }),
  2717  
  2718    // CJS => ESM
  2719    test(['in.js', '--outfile=out.js', '--format=esm'], {
  2720      'in.js': `
  2721        const {exists} = require('fs')
  2722        if (!exists) throw 'fail'
  2723      `,
  2724      'node.js': `
  2725        let fn = async () => {
  2726          let error
  2727          await import('./out.js').catch(x => error = x)
  2728          if (!error || !error.message.includes('require is not defined')) throw 'fail'
  2729        }
  2730        export {fn as async}
  2731      `,
  2732    }, {
  2733      async: true,
  2734      expectedStderr: `▲ [WARNING] Converting "require" to "esm" is currently not supported [unsupported-require-call]
  2735  
  2736      in.js:2:23:
  2737        2 │       const {exists} = require('fs')
  2738          ╵                        ~~~~~~~
  2739  
  2740  `,
  2741    }),
  2742    test(['in.js', '--outfile=out.js', '--format=esm'], {
  2743      'in.js': `
  2744        const fs = require('fs')
  2745        if (!fs.exists) throw 'fail'
  2746      `,
  2747      'node.js': `
  2748        let fn = async () => {
  2749          let error
  2750          await import('./out.js').catch(x => error = x)
  2751          if (!error || !error.message.includes('require is not defined')) throw 'fail'
  2752        }
  2753        export {fn as async}
  2754      `,
  2755    }, {
  2756      async: true,
  2757      expectedStderr: `▲ [WARNING] Converting "require" to "esm" is currently not supported [unsupported-require-call]
  2758  
  2759      in.js:2:17:
  2760        2 │       const fs = require('fs')
  2761          ╵                  ~~~~~~~
  2762  
  2763  `,
  2764    }),
  2765    test(['in.js', '--outfile=out.js', '--format=esm'], {
  2766      'in.js': `
  2767        module.exports = 123
  2768      `,
  2769      'node.js': `
  2770        import out from './out.js'
  2771        if (out !== 123) throw 'fail'
  2772      `,
  2773    }),
  2774    test(['in.js', '--outfile=out.js', '--format=esm'], {
  2775      'in.js': `
  2776        exports.foo = 123
  2777      `,
  2778      'node.js': `
  2779        import out from './out.js'
  2780        if (out.foo !== 123) throw 'fail'
  2781      `,
  2782    }),
  2783  )
  2784  
  2785  // This shouldn't cause a syntax error
  2786  // https://github.com/evanw/esbuild/issues/1082
  2787  tests.push(
  2788    test(['in.js', '--outfile=node.js', '--minify', '--bundle'], {
  2789      'in.js': `
  2790        return import('./in.js')
  2791      `,
  2792    }),
  2793  )
  2794  
  2795  // Check for file names of wrapped modules in non-minified stack traces (for profiling)
  2796  // Context: https://github.com/evanw/esbuild/pull/1236
  2797  tests.push(
  2798    test(['entry.js', '--outfile=node.js', '--bundle'], {
  2799      'entry.js': `
  2800        try {
  2801          require('./src/a')
  2802        } catch (e) {
  2803          if (!e.stack.includes('at __require') || !e.stack.includes('at src/a.ts') || !e.stack.includes('at src/b.ts'))
  2804            throw new Error(e.stack)
  2805        }
  2806      `,
  2807      'src/a.ts': `require('./b')`,
  2808      'src/b.ts': `throw new Error('fail')`,
  2809    }),
  2810    test(['entry.js', '--outfile=node.js', '--bundle', '--minify-identifiers'], {
  2811      'entry.js': `
  2812        try {
  2813          require('./src/a')
  2814        } catch (e) {
  2815          if (e.stack.includes('at __require') || e.stack.includes('at src/a.ts') || e.stack.includes('at src/b.ts'))
  2816            throw new Error(e.stack)
  2817        }
  2818      `,
  2819      'src/a.ts': `require('./b')`,
  2820      'src/b.ts': `throw new Error('fail')`,
  2821    }),
  2822    test(['entry.js', '--outfile=node.js', '--bundle'], {
  2823      'entry.js': `
  2824        try {
  2825          require('./src/a')
  2826        } catch (e) {
  2827          if (!e.stack.includes('at __init') || !e.stack.includes('at src/a.ts') || !e.stack.includes('at src/b.ts'))
  2828            throw new Error(e.stack)
  2829        }
  2830      `,
  2831      'src/a.ts': `export let esm = true; require('./b')`,
  2832      'src/b.ts': `export let esm = true; throw new Error('fail')`,
  2833    }),
  2834    test(['entry.js', '--outfile=node.js', '--bundle', '--minify-identifiers'], {
  2835      'entry.js': `
  2836        try {
  2837          require('./src/a')
  2838        } catch (e) {
  2839          if (e.stack.includes('at __init') || e.stack.includes('at src/a.ts') || e.stack.includes('at src/b.ts'))
  2840            throw new Error(e.stack)
  2841        }
  2842      `,
  2843      'src/a.ts': `export let esm = true; require('./b')`,
  2844      'src/b.ts': `export let esm = true; throw new Error('fail')`,
  2845    }),
  2846  )
  2847  
  2848  // This shouldn't crash
  2849  // https://github.com/evanw/esbuild/issues/1080
  2850  tests.push(
  2851    // Various CommonJS cases
  2852    test(['in.js', '--outfile=node.js', '--define:foo={"x":0}', '--bundle'], {
  2853      'in.js': `if (foo.x !== 0) throw 'fail'; return`,
  2854    }),
  2855    test(['in.js', '--outfile=node.js', '--define:foo.bar={"x":0}', '--bundle'], {
  2856      'in.js': `if (foo.bar.x !== 0) throw 'fail'; return`,
  2857    }),
  2858    test(['in.js', '--outfile=node.js', '--define:module={"x":0}', '--bundle'], {
  2859      'in.js': `if (module.x !== void 0) throw 'fail'; return`,
  2860    }),
  2861    test(['in.js', '--outfile=node.js', '--define:module.foo={"x":0}', '--bundle'], {
  2862      'in.js': `if (module.foo !== void 0) throw 'fail'; return`,
  2863    }),
  2864    test(['in.js', '--outfile=node.js', '--define:exports={"x":0}', '--bundle'], {
  2865      'in.js': `if (exports.x !== void 0) throw 'fail'; return`,
  2866    }),
  2867    test(['in.js', '--outfile=node.js', '--define:exports.foo={"x":0}', '--bundle'], {
  2868      'in.js': `if (exports.foo !== void 0) throw 'fail'; return`,
  2869    }),
  2870    test(['in.js', '--outfile=node.js', '--define:foo=["x"]', '--bundle'], {
  2871      'in.js': `if (foo[0] !== 'x') throw 'fail'; return`,
  2872    }),
  2873    test(['in.js', '--outfile=node.js', '--define:foo.bar=["x"]', '--bundle'], {
  2874      'in.js': `if (foo.bar[0] !== 'x') throw 'fail'; return`,
  2875    }),
  2876    test(['in.js', '--outfile=node.js', '--define:module=["x"]', '--bundle'], {
  2877      'in.js': `if (module[0] !== void 0) throw 'fail'; return`,
  2878    }),
  2879    test(['in.js', '--outfile=node.js', '--define:module.foo=["x"]', '--bundle'], {
  2880      'in.js': `if (module.foo !== void 0) throw 'fail'; return`,
  2881    }),
  2882    test(['in.js', '--outfile=node.js', '--define:exports=["x"]', '--bundle'], {
  2883      'in.js': `if (exports[0] !== void 0) throw 'fail'; return`,
  2884    }),
  2885    test(['in.js', '--outfile=node.js', '--define:exports.foo=["x"]', '--bundle'], {
  2886      'in.js': `if (exports.foo !== void 0) throw 'fail'; return`,
  2887    }),
  2888  
  2889    // Various ESM cases
  2890    test(['in.js', '--outfile=node.js', '--bundle', '--log-level=error'], {
  2891      'in.js': `import "pkg"`,
  2892      'node_modules/pkg/package.json': `{ "sideEffects": false }`,
  2893      'node_modules/pkg/index.js': `module.exports = null; throw 'fail'`,
  2894    }),
  2895    test(['in.js', '--outfile=node.js', '--define:foo={"x":0}', '--bundle'], {
  2896      'in.js': `if (foo.x !== 0) throw 'fail'; export {}`,
  2897    }),
  2898    test(['in.js', '--outfile=node.js', '--define:foo.bar={"x":0}', '--bundle'], {
  2899      'in.js': `if (foo.bar.x !== 0) throw 'fail'; export {}`,
  2900    }),
  2901    test(['in.js', '--outfile=node.js', '--define:module={"x":0}', '--bundle'], {
  2902      'in.js': `if (module.x !== 0) throw 'fail'; export {}`,
  2903    }),
  2904    test(['in.js', '--outfile=node.js', '--define:module.foo={"x":0}', '--bundle'], {
  2905      'in.js': `if (module.foo.x !== 0) throw 'fail'; export {}`,
  2906    }),
  2907    test(['in.js', '--outfile=node.js', '--define:exports={"x":0}', '--bundle'], {
  2908      'in.js': `if (exports.x !== 0) throw 'fail'; export {}`,
  2909    }),
  2910    test(['in.js', '--outfile=node.js', '--define:exports.foo={"x":0}', '--bundle'], {
  2911      'in.js': `if (exports.foo.x !== 0) throw 'fail'; export {}`,
  2912    }),
  2913    test(['in.js', '--outfile=node.js', '--define:foo=["x"]', '--bundle'], {
  2914      'in.js': `if (foo[0] !== 'x') throw 'fail'; export {}`,
  2915    }),
  2916    test(['in.js', '--outfile=node.js', '--define:foo.bar=["x"]', '--bundle'], {
  2917      'in.js': `if (foo.bar[0] !== 'x') throw 'fail'; export {}`,
  2918    }),
  2919    test(['in.js', '--outfile=node.js', '--define:module=["x"]', '--bundle'], {
  2920      'in.js': `if (module[0] !== 'x') throw 'fail'; export {}`,
  2921    }),
  2922    test(['in.js', '--outfile=node.js', '--define:module.foo=["x"]', '--bundle'], {
  2923      'in.js': `if (module.foo[0] !== 'x') throw 'fail'; export {}`,
  2924    }),
  2925    test(['in.js', '--outfile=node.js', '--define:exports=["x"]', '--bundle'], {
  2926      'in.js': `if (exports[0] !== 'x') throw 'fail'; export {}`,
  2927    }),
  2928    test(['in.js', '--outfile=node.js', '--define:exports.foo=["x"]', '--bundle'], {
  2929      'in.js': `if (exports.foo[0] !== 'x') throw 'fail'; export {}`,
  2930    }),
  2931  )
  2932  
  2933  // Check for "sideEffects: false" wrapper handling
  2934  // https://github.com/evanw/esbuild/issues/1088
  2935  for (const pkgJSON of [`{}`, `{"sideEffects": false}`]) {
  2936    for (const entry of [
  2937      `export let async = async () => { if (require("pkg").foo() !== 123) throw 'fail' }`,
  2938      `export let async = () => import("pkg").then(x => { if (x.foo() !== 123) throw 'fail' })`,
  2939    ]) {
  2940      for (const index of [`export {foo} from "./foo.js"`, `import {foo} from "./foo.js"; export {foo}`]) {
  2941        for (const foo of [`export let foo = () => 123`, `exports.foo = () => 123`]) {
  2942          tests.push(test(['in.js', '--outfile=node.js', '--bundle'], {
  2943            'in.js': entry,
  2944            'node_modules/pkg/package.json': pkgJSON,
  2945            'node_modules/pkg/index.js': index,
  2946            'node_modules/pkg/foo.js': foo,
  2947          }, { async: true }))
  2948        }
  2949      }
  2950    }
  2951    for (const entry of [
  2952      `export let async = async () => { try { require("pkg") } catch (e) { return } throw 'fail' }`,
  2953      `export let async = () => import("pkg").then(x => { throw 'fail' }, () => {})`,
  2954    ]) {
  2955      tests.push(test(['in.js', '--outfile=node.js', '--bundle'], {
  2956        'in.js': entry,
  2957        'node_modules/pkg/package.json': pkgJSON,
  2958        'node_modules/pkg/index.js': `
  2959          export {foo} from './b.js'
  2960        `,
  2961        'node_modules/pkg/b.js': `
  2962          export {foo} from './c.js'
  2963          throw 'stop'
  2964        `,
  2965        'node_modules/pkg/c.js': `
  2966          export let foo = () => 123
  2967        `,
  2968      }, { async: true }))
  2969    }
  2970  }
  2971  
  2972  // Tests for "arguments" scope issues
  2973  tests.push(
  2974    test(['in.js', '--outfile=node.js', '--minify'], {
  2975      'in.js': `
  2976        function arguments() {
  2977          return arguments.length
  2978        }
  2979        if (arguments(0, 1) !== 2) throw 'fail'
  2980      `,
  2981    }),
  2982    test(['in.js', '--outfile=node.js', '--minify'], {
  2983      'in.js': `
  2984        let value = (function arguments() {
  2985          return arguments.length
  2986        })(0, 1)
  2987        if (value !== 2) throw 'fail'
  2988      `,
  2989    }),
  2990  )
  2991  
  2992  // Tests for catch scope issues
  2993  tests.push(
  2994    test(['in.js', '--outfile=node.js', '--minify'], {
  2995      'in.js': `
  2996        var x = 0, y = []
  2997        try {
  2998          throw 1
  2999        } catch (x) {
  3000          y.push(x)
  3001          var x = 2
  3002          y.push(x)
  3003        }
  3004        y.push(x)
  3005        if (y + '' !== '1,2,0') throw 'fail: ' + y
  3006      `,
  3007    }),
  3008    test(['in.js', '--outfile=node.js', '--minify'], {
  3009      'in.js': `
  3010        var x = 0, y = []
  3011        try {
  3012          throw 1
  3013        } catch (x) {
  3014          y.push(x)
  3015          var x = 2
  3016          y.push(x)
  3017        }
  3018        finally { x = 3 }
  3019        y.push(x)
  3020        if (y + '' !== '1,2,3') throw 'fail: ' + y
  3021      `,
  3022    }),
  3023    test(['in.js', '--outfile=node.js', '--minify'], {
  3024      'in.js': `
  3025        var y = []
  3026        try {
  3027          throw 1
  3028        } catch (x) {
  3029          y.push(x)
  3030          var x = 2
  3031          y.push(x)
  3032        }
  3033        y.push(x)
  3034        if (y + '' !== '1,2,') throw 'fail: ' + y
  3035      `,
  3036    }),
  3037    test(['in.js', '--outfile=node.js', '--minify'], {
  3038      'in.js': `
  3039        var y = []
  3040        try {
  3041          throw 1
  3042        } catch (x) {
  3043          y.push(x)
  3044          x = 2
  3045          y.push(x)
  3046        }
  3047        y.push(typeof x)
  3048        if (y + '' !== '1,2,undefined') throw 'fail: ' + y
  3049      `,
  3050    }),
  3051    test(['in.js', '--outfile=node.js', '--minify'], {
  3052      'in.js': `
  3053        var y = []
  3054        try {
  3055          throw 1
  3056        } catch (x) {
  3057          y.push(x)
  3058          try {
  3059            throw 2
  3060          } catch (x) {
  3061            y.push(x)
  3062            var x = 3
  3063            y.push(x)
  3064          }
  3065          y.push(x)
  3066        }
  3067        y.push(x)
  3068        if (y + '' !== '1,2,3,1,') throw 'fail: ' + y
  3069      `,
  3070    }),
  3071    test(['in.js', '--outfile=node.js', '--minify'], {
  3072      'in.js': `
  3073        var y = []
  3074        try { x; y.push('fail') } catch (e) {}
  3075        try {
  3076          throw 1
  3077        } catch (x) {
  3078          y.push(x)
  3079        }
  3080        try { x; y.push('fail') } catch (e) {}
  3081        if (y + '' !== '1') throw 'fail: ' + y
  3082      `,
  3083    }),
  3084  
  3085    // https://github.com/evanw/esbuild/issues/1812
  3086    test(['in.js', '--outfile=node.js'], {
  3087      'in.js': `
  3088        let a = 1;
  3089        let def = "PASS2";
  3090        try {
  3091          throw [ "FAIL2", "PASS1" ];
  3092        } catch ({ [a]: b, 3: d = def }) {
  3093          let a = 0, def = "FAIL3";
  3094          if (b !== 'PASS1' || d !== 'PASS2') throw 'fail: ' + b + ' ' + d
  3095        }
  3096      `,
  3097    }),
  3098    test(['in.js', '--outfile=node.js', '--bundle'], {
  3099      'in.js': `
  3100        let a = 1;
  3101        let def = "PASS2";
  3102        try {
  3103          throw [ "FAIL2", "PASS1" ];
  3104        } catch ({ [a]: b, 3: d = def }) {
  3105          let a = 0, def = "FAIL3";
  3106          if (b !== 'PASS1' || d !== 'PASS2') throw 'fail: ' + b + ' ' + d
  3107        }
  3108      `,
  3109    }),
  3110    test(['in.js', '--outfile=node.js'], {
  3111      'in.js': `
  3112        try {
  3113          throw { x: 'z', z: 123 }
  3114        } catch ({ x, [x]: y }) {
  3115          if (y !== 123) throw 'fail'
  3116        }
  3117      `,
  3118    }),
  3119  )
  3120  
  3121  // Test cyclic import issues (shouldn't crash on evaluation)
  3122  tests.push(
  3123    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  3124      'entry.js': `import * as foo from './foo'; export default {foo, bar: require('./bar')}`,
  3125      'foo.js': `import * as a from './entry'; import * as b from './bar'; export default {a, b}`,
  3126      'bar.js': `const entry = require('./entry'); export function foo() { return entry }`,
  3127    }),
  3128  )
  3129  
  3130  // Test import attributes
  3131  tests.push(
  3132    test(['--bundle', 'entry.js', '--outfile=node.js', '--format=esm'], {
  3133      'entry.js': `
  3134        import * as foo from './package.json' with { type: 'json' }
  3135        if (foo.default.type !== 'module' || 'type' in foo) throw 'fail: static'
  3136  
  3137        const bar = await import('./package.json', { with: { type: 'json' } })
  3138        if (bar.default.type !== 'module' || 'type' in bar) throw 'fail: dynamic'
  3139      `,
  3140      'package.json': `{ "type": "module" }`,
  3141    }),
  3142  )
  3143  
  3144  // Test directive preservation
  3145  tests.push(
  3146    // The "__pow" symbol must not be hoisted above "use strict"
  3147    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3148      'entry.js': `
  3149        'use strict'
  3150        function f(a) {
  3151          a **= 2
  3152          return [a, arguments[0]]
  3153        }
  3154        let pair = f(2)
  3155        if (pair[0] !== 4 || pair[1] !== 2) throw 'fail'
  3156      `,
  3157    }),
  3158    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3159      'entry.js': `
  3160        //! @legal comment
  3161        'use strict'
  3162        function f(a) {
  3163          a **= 2
  3164          return [a, arguments[0]]
  3165        }
  3166        let pair = f(2)
  3167        if (pair[0] !== 4 || pair[1] !== 2) throw 'fail'
  3168      `,
  3169    }),
  3170  )
  3171  
  3172  // Test comments inside expressions
  3173  tests.push(
  3174    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3175      'entry.js': `
  3176        let foo;
  3177        (
  3178          /* x */
  3179          {
  3180            y() {
  3181              foo = this.y.name
  3182            }
  3183          }
  3184        ).y();
  3185        if (foo !== 'y') throw 'fail'
  3186      `,
  3187    }),
  3188  
  3189    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3190      'entry.js': `
  3191        let foo;
  3192        (
  3193          /* x */
  3194          function y() {
  3195            foo = y.name
  3196          }
  3197        )();
  3198        if (foo !== 'y') throw 'fail'
  3199      `,
  3200    }),
  3201  
  3202    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3203      'entry.js': `
  3204        let foo;
  3205        (
  3206          /* x */
  3207          class y {
  3208            static z() {
  3209              foo = y.name
  3210            }
  3211          }
  3212        ).z();
  3213        if (foo !== 'y') throw 'fail'
  3214      `,
  3215    }),
  3216  
  3217    test(['entry.js', '--outfile=node.js', '--target=es6'], {
  3218      'entry.js': `
  3219        let foo;
  3220        (/* @__PURE__ */ (() => foo = 'y')());
  3221        if (foo !== 'y') throw 'fail'
  3222      `,
  3223    }),
  3224  )
  3225  
  3226  // Test certain minification transformations
  3227  for (const minify of [[], ['--minify-syntax']]) {
  3228    tests.push(
  3229      test(['in.js', '--outfile=node.js'].concat(minify), {
  3230        'in.js': `let fn = (x) => { if (x && y) return; function y() {} throw 'fail' }; fn(fn)`,
  3231      }),
  3232      test(['in.js', '--outfile=node.js'].concat(minify), {
  3233        'in.js': `let fn = (a, b) => { if (a && (x = () => y) && b) return; var x; let y = 123; if (x() !== 123) throw 'fail' }; fn(fn)`,
  3234      }),
  3235      test(['in.js', '--outfile=node.js'].concat(minify), {
  3236        'in.js': `
  3237          var x = { [-0]: 1 }; if (x['0'] !== 1 || x['-0'] !== void 0) throw 'fail: -0'
  3238          var x = { [-1]: 1 }; if (x['-1'] !== 1) throw 'fail: -1'
  3239          var x = { [NaN]: 1 }; if (x['NaN'] !== 1) throw 'fail: NaN'
  3240          var x = { [Infinity]: 1 }; if (x['Infinity'] !== 1) throw 'fail: Infinity'
  3241          var x = { [-Infinity]: 1 }; if (x['-Infinity'] !== 1) throw 'fail: -Infinity'
  3242          var x = { [1e5]: 1 }; if (x['100000'] !== 1) throw 'fail: 1e5'
  3243          var x = { [-1e5]: 1 }; if (x['-100000'] !== 1) throw 'fail: -1e5'
  3244          var x = { [1e100]: 1 }; if (x['1e+100'] !== 1) throw 'fail: 1e100'
  3245          var x = { [-1e100]: 1 }; if (x['-1e+100'] !== 1) throw 'fail: -1e100'
  3246          var x = { [0xFFFF_FFFF_FFFF]: 1 }; if (x['281474976710655'] !== 1) throw 'fail: 0xFFFF_FFFF_FFFF'
  3247          var x = { [-0xFFFF_FFFF_FFFF]: 1 }; if (x['-281474976710655'] !== 1) throw 'fail: -0xFFFF_FFFF_FFFF'
  3248        `,
  3249      }),
  3250      test(['in.js', '--outfile=node.js'].concat(minify), {
  3251        'in.js': `
  3252          var x = class { static [-0] = 1 }; if (x['0'] !== 1 || x['-0'] !== void 0) throw 'fail: -0'
  3253          var x = class { static [-1] = 1 }; if (x['-1'] !== 1) throw 'fail: -1'
  3254          var x = class { static [NaN] = 1 }; if (x['NaN'] !== 1) throw 'fail: NaN'
  3255          var x = class { static [Infinity] = 1 }; if (x['Infinity'] !== 1) throw 'fail: Infinity'
  3256          var x = class { static [-Infinity] = 1 }; if (x['-Infinity'] !== 1) throw 'fail: -Infinity'
  3257          var x = class { static [1e5] = 1 }; if (x['100000'] !== 1) throw 'fail: 1e5'
  3258          var x = class { static [-1e5] = 1 }; if (x['-100000'] !== 1) throw 'fail: -1e5'
  3259          var x = class { static [1e100] = 1 }; if (x['1e+100'] !== 1) throw 'fail: 1e100'
  3260          var x = class { static [-1e100] = 1 }; if (x['-1e+100'] !== 1) throw 'fail: -1e100'
  3261          var x = class { static [0xFFFF_FFFF_FFFF] = 1 }; if (x['281474976710655'] !== 1) throw 'fail: 0xFFFF_FFFF_FFFF'
  3262          var x = class { static [-0xFFFF_FFFF_FFFF] = 1 }; if (x['-281474976710655'] !== 1) throw 'fail: -0xFFFF_FFFF_FFFF'
  3263        `,
  3264      }),
  3265  
  3266      // See: https://github.com/evanw/esbuild/issues/3195
  3267      test(['in.js', '--outfile=node.js', '--keep-names'].concat(minify), {
  3268        'in.js': `
  3269          const log = [];
  3270          const sideEffect = x => log.push(x);
  3271          (() => {
  3272            function f() {}
  3273            sideEffect(1, f());
  3274          })();
  3275          (() => {
  3276            function g() {}
  3277            debugger;
  3278            sideEffect(2, g());
  3279          })();
  3280          if (log + '' !== '1,2') throw 'fail: ' + log;
  3281        `,
  3282      }),
  3283    )
  3284  
  3285    // Check property access simplification
  3286    for (const access of [['.a'], ['["a"]']]) {
  3287      tests.push(
  3288        test(['in.js', '--outfile=node.js'].concat(minify), {
  3289          'in.js': `if ({a: 1}${access} !== 1) throw 'fail'`,
  3290        }),
  3291        test(['in.js', '--outfile=node.js'].concat(minify), {
  3292          'in.js': `if ({a: {a: 1}}${access}${access} !== 1) throw 'fail'`,
  3293        }),
  3294        test(['in.js', '--outfile=node.js'].concat(minify), {
  3295          'in.js': `if ({a: {b: 1}}${access}.b !== 1) throw 'fail'`,
  3296        }),
  3297        test(['in.js', '--outfile=node.js'].concat(minify), {
  3298          'in.js': `if ({b: {a: 1}}.b${access} !== 1) throw 'fail'`,
  3299        }),
  3300        test(['in.js', '--outfile=node.js', '--log-level=error'].concat(minify), {
  3301          'in.js': `if ({a: 1, a: 2}${access} !== 2) throw 'fail'`,
  3302        }),
  3303        test(['in.js', '--outfile=node.js', '--log-level=error'].concat(minify), {
  3304          'in.js': `if ({a: 1, [String.fromCharCode(97)]: 2}${access} !== 2) throw 'fail'`,
  3305        }),
  3306        test(['in.js', '--outfile=node.js'].concat(minify), {
  3307          'in.js': `let a = {a: 1}; if ({...a}${access} !== 1) throw 'fail'`,
  3308        }),
  3309        test(['in.js', '--outfile=node.js'].concat(minify), {
  3310          'in.js': `if ({ get a() { return 1 } }${access} !== 1) throw 'fail'`,
  3311        }),
  3312        test(['in.js', '--outfile=node.js'].concat(minify), {
  3313          'in.js': `if ({ __proto__: {a: 1} }${access} !== 1) throw 'fail'`,
  3314        }),
  3315        test(['in.js', '--outfile=node.js'].concat(minify), {
  3316          'in.js': `if ({ __proto__: null, a: 1 }${access} !== 1) throw 'fail'`,
  3317        }),
  3318        test(['in.js', '--outfile=node.js'].concat(minify), {
  3319          'in.js': `if ({ __proto__: null, b: 1 }${access} !== void 0) throw 'fail'`,
  3320        }),
  3321        test(['in.js', '--outfile=node.js'].concat(minify), {
  3322          'in.js': `if ({ __proto__: null }.__proto__ !== void 0) throw 'fail'`,
  3323        }),
  3324        test(['in.js', '--outfile=node.js'].concat(minify), {
  3325          'in.js': `if ({ ['__proto__']: null }.__proto__ !== null) throw 'fail'`,
  3326        }),
  3327        test(['in.js', '--outfile=node.js'].concat(minify), {
  3328          'in.js': `let x = 100; if ({ b: ++x, a: 1 }${access} !== 1 || x !== 101) throw 'fail'`,
  3329        }),
  3330        test(['in.js', '--outfile=node.js'].concat(minify), {
  3331          'in.js': `if ({ a: function() { return this.b }, b: 1 }${access}() !== 1) throw 'fail'`,
  3332        }),
  3333        test(['in.js', '--outfile=node.js'].concat(minify), {
  3334          'in.js': `if ({ a: function() { return this.b }, b: 1 }${access}\`\` !== 1) throw 'fail'`,
  3335        }),
  3336        test(['in.js', '--outfile=node.js'].concat(minify), {
  3337          'in.js': `if (({a: 2}${access} = 1) !== 1) throw 'fail'`,
  3338        }),
  3339        test(['in.js', '--outfile=node.js'].concat(minify), {
  3340          'in.js': `if ({a: 1}${access}++ !== 1) throw 'fail'`,
  3341        }),
  3342        test(['in.js', '--outfile=node.js'].concat(minify), {
  3343          'in.js': `if (++{a: 1}${access} !== 2) throw 'fail'`,
  3344        }),
  3345        test(['in.js', '--outfile=node.js'].concat(minify), {
  3346          'in.js': `
  3347            Object.defineProperty(Object.prototype, 'MIN_OBJ_LIT', {value: 1})
  3348            if ({}.MIN_OBJ_LIT !== 1) throw 'fail'
  3349          `,
  3350        }),
  3351        test(['in.js', '--outfile=node.js'].concat(minify), {
  3352          'in.js': `
  3353            let x = false
  3354            function y() { x = true }
  3355            if ({ b: y(), a: 1 }${access} !== 1 || !x) throw 'fail'
  3356          `,
  3357        }),
  3358        test(['in.js', '--outfile=node.js'].concat(minify), {
  3359          'in.js': `
  3360            try { new ({ a() {} }${access}); throw 'fail' }
  3361            catch (e) { if (e === 'fail') throw e }
  3362          `,
  3363        }),
  3364        test(['in.js', '--outfile=node.js'].concat(minify), {
  3365          'in.js': `
  3366            let x = 1;
  3367            ({ set a(y) { x = y } }${access} = 2);
  3368            if (x !== 2) throw 'fail'
  3369          `,
  3370        }),
  3371      )
  3372    }
  3373  
  3374    // Check try/catch simplification
  3375    tests.push(
  3376      test(['in.js', '--outfile=node.js'].concat(minify), {
  3377        'in.js': `
  3378          try {
  3379            try {
  3380              throw 0
  3381            } finally {
  3382              var x = 1
  3383            }
  3384          } catch {
  3385          }
  3386          if (x !== 1) throw 'fail'
  3387        `,
  3388      }),
  3389      test(['in.js', '--outfile=node.js'].concat(minify), {
  3390        'in.js': `
  3391          let y
  3392          try {
  3393            throw 1
  3394          } catch (x) {
  3395            eval('y = x')
  3396          }
  3397          if (y !== 1) throw 'fail'
  3398        `,
  3399      }),
  3400      test(['in.js', '--outfile=node.js'].concat(minify), {
  3401        'in.js': `
  3402          try {
  3403            throw 0
  3404          } catch (x) {
  3405            var x = 1
  3406          }
  3407          if (x !== void 0) throw 'fail'
  3408        `,
  3409      }),
  3410      test(['in.js', '--outfile=node.js'].concat(minify), {
  3411        'in.js': `
  3412          let works
  3413          try {
  3414            throw { get a() { works = true } }
  3415          } catch ({ a }) {}
  3416          if (!works) throw 'fail'
  3417        `,
  3418      }),
  3419      test(['in.js', '--outfile=node.js'].concat(minify), {
  3420        'in.js': `
  3421          let works
  3422          try {
  3423            throw { *[Symbol.iterator]() { works = true } }
  3424          } catch ([x]) {
  3425          }
  3426          if (!works) throw 'fail'
  3427        `,
  3428      }),
  3429    )
  3430  
  3431    // Check variable initializer inlining
  3432    tests.push(
  3433      test(['in.js', '--outfile=node.js'].concat(minify), {
  3434        'in.js': `
  3435          function foo() {
  3436            if (this !== globalThis) throw 'fail'
  3437          }
  3438          function main() {
  3439            let obj = { bar: foo };
  3440            let fn = obj.bar;
  3441            (0, fn)();
  3442          }
  3443          main()
  3444        `,
  3445      }),
  3446    );
  3447  
  3448    // Check global constructor behavior
  3449    tests.push(
  3450      test(['in.js', '--outfile=node.js'].concat(minify), {
  3451        'in.js': `
  3452          const check = (before, after) => {
  3453            if (Boolean(before) !== after) throw 'fail: Boolean(' + before + ') should not be ' + Boolean(before)
  3454            if (new Boolean(before) === after) throw 'fail: new Boolean(' + before + ') should not be ' + new Boolean(before)
  3455            if (new Boolean(before).valueOf() !== after) throw 'fail: new Boolean(' + before + ').valueOf() should not be ' + new Boolean(before).valueOf()
  3456          }
  3457          check(false, false); check(0, false); check(0n, false)
  3458          check(true, true); check(1, true); check(1n, true)
  3459          check(null, false); check(undefined, false)
  3460          check('', false); check('x', true)
  3461  
  3462          const checkSpread = (before, after) => {
  3463            if (Boolean(...before) !== after) throw 'fail: Boolean(...' + before + ') should not be ' + Boolean(...before)
  3464            if (new Boolean(...before) === after) throw 'fail: new Boolean(...' + before + ') should not be ' + new Boolean(...before)
  3465            if (new Boolean(...before).valueOf() !== after) throw 'fail: new Boolean(...' + before + ').valueOf() should not be ' + new Boolean(...before).valueOf()
  3466          }
  3467          checkSpread([0], false); check([1], true)
  3468          checkSpread([], false)
  3469        `,
  3470      }),
  3471      test(['in.js', '--outfile=node.js'].concat(minify), {
  3472        'in.js': `
  3473          class ToPrimitive { [Symbol.toPrimitive]() { return '100.001' } }
  3474          const someObject = { toString: () => 123, valueOf: () => 321 }
  3475  
  3476          const check = (before, after) => {
  3477            if (Number(before) !== after) throw 'fail: Number(' + before + ') should not be ' + Number(before)
  3478            if (new Number(before) === after) throw 'fail: new Number(' + before + ') should not be ' + new Number(before)
  3479            if (new Number(before).valueOf() !== after) throw 'fail: new Number(' + before + ').valueOf() should not be ' + new Number(before).valueOf()
  3480          }
  3481          check(-1.23, -1.23)
  3482          check('-1.23', -1.23)
  3483          check(123n, 123)
  3484          check(null, 0)
  3485          check(false, 0)
  3486          check(true, 1)
  3487          check(someObject, 321)
  3488          check(new ToPrimitive(), 100.001)
  3489  
  3490          const checkSpread = (before, after) => {
  3491            if (Number(...before) !== after) throw 'fail: Number(...' + before + ') should not be ' + Number(...before)
  3492            if (new Number(...before) === after) throw 'fail: new Number(...' + before + ') should not be ' + new Number(...before)
  3493            if (new Number(...before).valueOf() !== after) throw 'fail: new Number(...' + before + ').valueOf() should not be ' + new Number(...before).valueOf()
  3494          }
  3495          checkSpread(['123'], 123)
  3496          checkSpread([], 0)
  3497        `,
  3498      }),
  3499      test(['in.js', '--outfile=node.js'].concat(minify), {
  3500        'in.js': `
  3501          class ToPrimitive { [Symbol.toPrimitive]() { return 100.001 } }
  3502          const someObject = { toString: () => 123, valueOf: () => 321 }
  3503  
  3504          const check = (before, after) => {
  3505            if (String(before) !== after) throw 'fail: String(' + before + ') should not be ' + String(before)
  3506            if (new String(before) === after) throw 'fail: new String(' + before + ') should not be ' + new String(before)
  3507            if (new String(before).valueOf() !== after) throw 'fail: new String(' + before + ').valueOf() should not be ' + new String(before).valueOf()
  3508          }
  3509          check('', '')
  3510          check('x', 'x')
  3511          check(null, 'null')
  3512          check(false, 'false')
  3513          check(1.23, '1.23')
  3514          check(-123n, '-123')
  3515          check(someObject, '123')
  3516          check(new ToPrimitive(), '100.001')
  3517  
  3518          const checkSpread = (before, after) => {
  3519            if (String(...before) !== after) throw 'fail: String(...' + before + ') should not be ' + String(...before)
  3520            if (new String(...before) === after) throw 'fail: new String(...' + before + ') should not be ' + new String(...before)
  3521            if (new String(...before).valueOf() !== after) throw 'fail: new String(...' + before + ').valueOf() should not be ' + new String(...before).valueOf()
  3522          }
  3523          checkSpread([123], '123')
  3524          checkSpread([], '')
  3525  
  3526          const checkAndExpectNewToThrow = (before, after) => {
  3527            if (String(before) !== after) throw 'fail: String(...) should not be ' + String(before)
  3528            try {
  3529              new String(before)
  3530            } catch (e) {
  3531              return
  3532            }
  3533            throw 'fail: new String(...) should not succeed'
  3534          }
  3535          checkAndExpectNewToThrow(Symbol('abc'), 'Symbol(abc)')
  3536        `,
  3537      }),
  3538    );
  3539  
  3540    // https://github.com/evanw/esbuild/issues/3125
  3541    tests.push(
  3542      test(['in.js', '--outfile=node.js'].concat(minify), {
  3543        'in.js': `
  3544          let y
  3545          {
  3546            // There was a bug where this incorrectly turned into "y = (() => x)()"
  3547            const f = () => x;
  3548            const x = 0;
  3549            y = f()
  3550          }
  3551          if (y !== 0) throw 'fail'
  3552        `,
  3553      }),
  3554    )
  3555  
  3556    // https://github.com/evanw/esbuild/issues/3700
  3557    tests.push(
  3558      test(['in.js', '--bundle', '--outfile=node.js'].concat(minify), {
  3559        'in.js': `
  3560          import imported from './data.json'
  3561          const native = JSON.parse(\`{
  3562            "hello": "world",
  3563            "__proto__": {
  3564              "sky": "universe"
  3565            }
  3566          }\`)
  3567          const literal1 = {
  3568            "hello": "world",
  3569            "__proto__": {
  3570              "sky": "universe"
  3571            }
  3572          }
  3573          const literal2 = {
  3574            "hello": "world",
  3575            ["__proto__"]: {
  3576              "sky": "universe"
  3577            }
  3578          }
  3579          if (Object.getPrototypeOf(native)?.sky) throw 'fail: native'
  3580          if (!Object.getPrototypeOf(literal1)?.sky) throw 'fail: literal1'
  3581          if (Object.getPrototypeOf(literal2)?.sky) throw 'fail: literal2'
  3582          if (Object.getPrototypeOf(imported)?.sky) throw 'fail: imported'
  3583        `,
  3584        'data.json': `{
  3585          "hello": "world",
  3586          "__proto__": {
  3587            "sky": "universe"
  3588          }
  3589        }`,
  3590      }),
  3591    )
  3592  }
  3593  
  3594  // Test minification of top-level symbols
  3595  tests.push(
  3596    test(['in.js', '--outfile=node.js', '--minify'], {
  3597      // Top-level names should not be minified
  3598      'in.js': `function foo() {} if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3599    }),
  3600    test(['in.js', '--outfile=node.js', '--minify'], {
  3601      // Nested names should be minified
  3602      'in.js': `(() => { function foo() {} if (foo.name === 'foo') throw 'fail: ' + foo.name })()`,
  3603    }),
  3604    test(['in.js', '--outfile=node.js', '--minify', '--target=es6'], {
  3605      // Importing the "__pow()" runtime function should not affect top-level name minification
  3606      'in.js': `let _8 = 2 ** 3; function foo8() {} if (foo8.name !== 'foo' + _8) throw 'fail: ' + foo8.name`,
  3607    }),
  3608  )
  3609  
  3610  // Test name preservation
  3611  for (let flags of [[], ['--minify', '--keep-names']]) {
  3612    tests.push(
  3613      // Arrow functions
  3614      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3615        'in.js': `(() => { let fn = () => {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3616      }),
  3617      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3618        'in.js': `(() => { let fn; fn = () => {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3619      }),
  3620      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3621        'in.js': `(() => { let [fn = () => {}] = []; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3622      }),
  3623      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3624        'in.js': `(() => { let fn; [fn = () => {}] = []; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3625      }),
  3626      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3627        'in.js': `(() => { let {fn = () => {}} = {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3628      }),
  3629      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3630        'in.js': `(() => { let {prop: fn = () => {}} = {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3631      }),
  3632      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3633        'in.js': `(() => { let fn; ({fn = () => {}} = {}); if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3634      }),
  3635      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3636        'in.js': `(() => { let fn; ({prop: fn = () => {}} = {}); if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3637      }),
  3638      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3639        'in.js': `(() => { let obj = {}; obj.fn = () => {}; if (obj.fn.name !== '') throw 'fail: ' + obj.fn.name })()`,
  3640      }),
  3641      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3642        'in.js': `(() => { let obj = {}; obj['fn'] = () => {}; if (obj.fn.name !== '') throw 'fail: ' + obj.fn.name })()`,
  3643      }),
  3644  
  3645      // Functions
  3646      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3647        'in.js': `(() => { function foo() {} if (foo.name !== 'foo') throw 'fail: ' + foo.name })()`,
  3648      }),
  3649      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3650        'in.js': `(() => { let fn = function foo() {}; if (fn.name !== 'foo') throw 'fail' })()`,
  3651      }),
  3652      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3653        'in.js': `(() => { let fn = function() {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3654      }),
  3655      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3656        'in.js': `(() => { let fn; fn = function() {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3657      }),
  3658      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3659        'in.js': `(() => { let [fn = function() {}] = []; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3660      }),
  3661      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3662        'in.js': `(() => { let fn; [fn = function() {}] = []; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3663      }),
  3664      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3665        'in.js': `(() => { let {fn = function() {}} = {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3666      }),
  3667      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3668        'in.js': `(() => { let {prop: fn = function() {}} = {}; if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3669      }),
  3670      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3671        'in.js': `(() => { let fn; ({fn = function() {}} = {}); if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3672      }),
  3673      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3674        'in.js': `(() => { let fn; ({prop: fn = function() {}} = {}); if (fn.name !== 'fn') throw 'fail: ' + fn.name })()`,
  3675      }),
  3676      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3677        'in.js': `(() => { let obj = {}; obj.fn = function() {}; if (obj.fn.name !== '') throw 'fail: ' + obj.fn.name })()`,
  3678      }),
  3679      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3680        'in.js': `(() => { let obj = {}; obj['fn'] = function() {}; if (obj.fn.name !== '') throw 'fail: ' + obj.fn.name })()`,
  3681      }),
  3682  
  3683      // Classes
  3684      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3685        'in.js': `(() => { class foo {} if (foo.name !== 'foo') throw 'fail: ' + foo.name })()`,
  3686      }),
  3687      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3688        'in.js': `(() => { let cls = class foo {}; if (cls.name !== 'foo') throw 'fail: ' + cls.name })()`,
  3689      }),
  3690      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3691        'in.js': `(() => { let cls = class {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3692      }),
  3693      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3694        'in.js': `(() => { let cls; cls = class {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3695      }),
  3696      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3697        'in.js': `(() => { let [cls = class {}] = []; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3698      }),
  3699      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3700        'in.js': `(() => { let cls; [cls = class {}] = []; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3701      }),
  3702      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3703        'in.js': `(() => { let {cls = class {}} = {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3704      }),
  3705      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3706        'in.js': `(() => { let {prop: cls = class {}} = {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3707      }),
  3708      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3709        'in.js': `(() => { let cls; ({cls = class {}} = {}); if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3710      }),
  3711      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3712        'in.js': `(() => { let cls; ({prop: cls = class {}} = {}); if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3713      }),
  3714      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3715        'in.js': `(() => { let obj = {}; obj.cls = class {}; if (obj.cls.name !== '') throw 'fail: ' + obj.cls.name })()`,
  3716      }),
  3717      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3718        'in.js': `(() => { let obj = {}; obj['cls'] = class {}; if (obj.cls.name !== '') throw 'fail: ' + obj.cls.name })()`,
  3719      }),
  3720      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3721        'in.js': `(() => { class Foo { static foo } if (Foo.name !== 'Foo') throw 'fail: ' + Foo.name })()`,
  3722      }),
  3723      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3724        'in.js': `(() => { class Foo { static name = 123 } if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3725      }),
  3726      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3727        'in.js': `(() => { class Foo { static name() { return 123 } } if (Foo.name() !== 123) throw 'fail: ' + Foo.name })()`,
  3728      }),
  3729      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3730        'in.js': `(() => { class Foo { static get name() { return 123 } } if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3731      }),
  3732      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3733        'in.js': `(() => { class Foo { static ['name'] = 123 } if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3734      }),
  3735      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3736        'in.js': `(() => { let Foo = class Bar { static foo }; if (Foo.name !== 'Bar') throw 'fail: ' + Foo.name })()`,
  3737      }),
  3738      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3739        'in.js': `(() => { let Foo = class Bar { static name = 123 }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3740      }),
  3741      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3742        'in.js': `(() => { let Foo = class Bar { static name() { return 123 } }; if (Foo.name() !== 123) throw 'fail: ' + Foo.name })()`,
  3743      }),
  3744      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3745        'in.js': `(() => { let Foo = class Bar { static get name() { return 123 } }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3746      }),
  3747      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3748        'in.js': `(() => { let Foo = class Bar { static ['name'] = 123 }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3749      }),
  3750      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3751        'in.js': `(() => { let Foo = class { static foo }; if (Foo.name !== 'Foo') throw 'fail: ' + Foo.name })()`,
  3752      }),
  3753      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3754        'in.js': `(() => { let Foo = class { static name = 123 }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3755      }),
  3756      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3757        'in.js': `(() => { let Foo = class { static name() { return 123 } }; if (Foo.name() !== 123) throw 'fail: ' + Foo.name })()`,
  3758      }),
  3759      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3760        'in.js': `(() => { let Foo = class { static get name() { return 123 } }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3761      }),
  3762      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3763        'in.js': `(() => { let Foo = class { static ['name'] = 123 }; if (Foo.name !== 123) throw 'fail: ' + Foo.name })()`,
  3764      }),
  3765  
  3766      // Methods
  3767      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3768        'in.js': `(() => { let obj = { foo() {} }; if (obj.foo.name !== 'foo') throw 'fail: ' + obj.foo.name })()`,
  3769      }),
  3770      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3771        'in.js': `(() => { let obj = { foo: () => {} }; if (obj.foo.name !== 'foo') throw 'fail: ' + obj.foo.name })()`,
  3772      }),
  3773      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3774        'in.js': `(() => { class Foo { foo() {} }; if (new Foo().foo.name !== 'foo') throw 'fail: ' + new Foo().foo.name })()`,
  3775      }),
  3776      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3777        'in.js': `(() => { class Foo { static foo() {} }; if (Foo.foo.name !== 'foo') throw 'fail: ' + Foo.foo.name })()`,
  3778      }),
  3779      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3780        'in.js': `(() => { let Foo = class { foo() {} }; if (new Foo().foo.name !== 'foo') throw 'fail: ' + new Foo().foo.name })()`,
  3781      }),
  3782      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3783        'in.js': `(() => { let Foo = class { static foo() {} }; if (Foo.foo.name !== 'foo') throw 'fail: ' + Foo.foo.name })()`,
  3784      }),
  3785  
  3786      // See: https://github.com/evanw/esbuild/issues/3199
  3787      test(['in.ts', '--outfile=node.js', '--target=es6'].concat(flags), {
  3788        'in.ts': `
  3789          namespace foo { export class Foo {} }
  3790          if (foo.Foo.name !== 'Foo') throw 'fail: ' + foo.Foo.name
  3791        `,
  3792      }),
  3793      test(['in.ts', '--outfile=node.js', '--target=esnext'].concat(flags), {
  3794        'in.ts': `
  3795          namespace foo { export class Foo {} }
  3796          if (foo.Foo.name !== 'Foo') throw 'fail: ' + foo.Foo.name
  3797        `,
  3798      }),
  3799  
  3800      // See: https://github.com/evanw/esbuild/issues/3756
  3801      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3802        'in.js': `(() => { let obj = { fn() {} }; if (obj.fn.name !== 'fn') throw 'fail: ' + obj.fn.name })()`,
  3803      }),
  3804      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3805        'in.js': `(() => { let obj = { *fn() {} }; if (obj.fn.name !== 'fn') throw 'fail: ' + obj.fn.name })()`,
  3806      }),
  3807      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3808        'in.js': `(() => { let obj = { async fn() {} }; if (obj.fn.name !== 'fn') throw 'fail: ' + obj.fn.name })()`,
  3809      }),
  3810      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3811        'in.js': `(() => {
  3812          let obj = { get fn() {} }, { get } = Object.getOwnPropertyDescriptor(obj, 'fn')
  3813          if (get.name !== 'get fn') throw 'fail: ' + get.name
  3814        })()`,
  3815      }),
  3816      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  3817        'in.js': `(() => {
  3818          let obj = { set fn(_) {} }, { set } = Object.getOwnPropertyDescriptor(obj, 'fn')
  3819          if (set.name !== 'set fn') throw 'fail: ' + set.name
  3820        })()`,
  3821      }),
  3822    )
  3823  }
  3824  tests.push(
  3825    // Arrow functions
  3826    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3827      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3828      'other.js': `export default () => {}`,
  3829    }),
  3830  
  3831    // Functions
  3832    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3833      'in.js': `import foo from './other'; if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3834      'other.js': `export default function foo() {}`,
  3835    }),
  3836    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3837      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3838      'other.js': `export default function() {}`,
  3839    }),
  3840    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3841      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3842      'other.js': `export default (function() {})`,
  3843    }),
  3844  
  3845    // Classes
  3846    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3847      'in.js': `import foo from './other'; if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3848      'other.js': `export default class foo {}`,
  3849    }),
  3850    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3851      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3852      'other.js': `export default class {}`,
  3853    }),
  3854    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle'], {
  3855      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3856      'other.js': `export default (class {})`,
  3857    }),
  3858    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm'], {
  3859      'node.js': `import foo from './out.js'; if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3860      'in.js': `export default class foo {}`,
  3861    }),
  3862    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm'], {
  3863      'node.js': `import foo from './out.js'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3864      'in.js': `export default class {}`,
  3865    }),
  3866    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm'], {
  3867      'node.js': `import foo from './out.js'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3868      'in.js': `export default (class {})`,
  3869    }),
  3870  
  3871    // Class fields
  3872    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3873      'in.js': `(() => { class Foo { foo = () => {} } if (new Foo().foo.name !== 'foo') throw 'fail: ' + new Foo().foo.name })()`,
  3874    }),
  3875    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3876      'in.js': `(() => { class Foo { static foo = () => {} } if (Foo.foo.name !== 'foo') throw 'fail: ' + Foo.foo.name })()`,
  3877    }),
  3878  
  3879    // Private methods
  3880    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3881      'in.js': `(() => { class foo { a() { return this.#b } #b() {} } if (foo.name !== 'foo') throw 'fail: ' + foo.name })()`,
  3882    }),
  3883    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3884      'in.js': `(() => { let cls = class foo { a() { return this.#b } #b() {} }; if (cls.name !== 'foo') throw 'fail: ' + cls.name })()`,
  3885    }),
  3886    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3887      'in.js': `(() => { let cls = class { a() { return this.#b } #b() {} }; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3888    }),
  3889    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3890      'in.js': `(() => { let cls; cls = class { a() { return this.#b } #b() {} }; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3891    }),
  3892    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3893      'in.js': `(() => { let [cls = class { a() { return this.#b } #b() {} }] = []; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3894    }),
  3895    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3896      'in.js': `(() => { let cls; [cls = class { a() { return this.#b } #b() {} }] = []; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3897    }),
  3898    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3899      'in.js': `(() => { let {cls = class { a() { return this.#b } #b() {} }} = {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3900    }),
  3901    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3902      'in.js': `(() => { let {prop: cls = class { a() { return this.#b } #b() {} }} = {}; if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3903    }),
  3904    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3905      'in.js': `(() => { let cls; ({cls = class { a() { return this.#b } #b() {} }} = {}); if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3906    }),
  3907    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3908      'in.js': `(() => { let cls; ({prop: cls = class { a() { return this.#b } #b() {} }} = {}); if (cls.name !== 'cls') throw 'fail: ' + cls.name })()`,
  3909    }),
  3910    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3911      'in.js': `import foo from './other'; if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3912      'other.js': `export default class foo { a() { return this.#b } #b() {} }`,
  3913    }),
  3914    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3915      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3916      'other.js': `export default class { a() { return this.#b } #b() {} }`,
  3917    }),
  3918    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--bundle', '--target=es6'], {
  3919      'in.js': `import foo from './other'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3920      'other.js': `export default (class { a() { return this.#b } #b() {} })`,
  3921    }),
  3922    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3923      'node.js': `import foo from './out.js'; if (foo.name !== 'foo') throw 'fail: ' + foo.name`,
  3924      'in.js': `export default class foo { a() { return this.#b } #b() {} }`,
  3925    }),
  3926    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3927      'node.js': `import foo from './out.js'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3928      'in.js': `export default class { a() { return this.#b } #b() {} }`,
  3929    }),
  3930    test(['in.js', '--outfile=out.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3931      'node.js': `import foo from './out.js'; if (foo.name !== 'default') throw 'fail: ' + foo.name`,
  3932      'in.js': `export default (class { a() { return this.#b } #b() {} })`,
  3933    }),
  3934  
  3935    // Private fields
  3936    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3937      'in.js': `class Foo { foo = this.#foo; #foo() {} } if (new Foo().foo.name !== '#foo') throw 'fail: ' + new Foo().foo.name`,
  3938    }),
  3939    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3940      'in.js': `class Foo { static foo = this.#foo; static #foo() {} } if (Foo.foo.name !== '#foo') throw 'fail: ' + Foo.foo.name`,
  3941    }),
  3942    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3943      'in.js': `class Foo { #foo = function() {}; foo = this.#foo } if (new Foo().foo.name !== '#foo') throw 'fail: ' + new Foo().foo.name`,
  3944    }),
  3945    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3946      'in.js': `class Foo { static #foo = function() {}; static foo = this.#foo } if (Foo.foo.name !== '#foo') throw 'fail: ' + Foo.foo.name`,
  3947    }),
  3948    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3949      'in.js': `class Foo { #foo = () => {}; foo = this.#foo } if (new Foo().foo.name !== '#foo') throw 'fail: ' + new Foo().foo.name`,
  3950    }),
  3951    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3952      'in.js': `class Foo { static #foo = () => {}; static foo = this.#foo } if (Foo.foo.name !== '#foo') throw 'fail: ' + Foo.foo.name`,
  3953    }),
  3954    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3955      'in.js': `class Foo { #foo = class {}; foo = this.#foo } if (new Foo().foo.name !== '#foo') throw 'fail: ' + new Foo().foo.name`,
  3956    }),
  3957    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3958      'in.js': `class Foo { static #foo = class {}; static foo = this.#foo } if (Foo.foo.name !== '#foo') throw 'fail: ' + Foo.foo.name`,
  3959    }),
  3960    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3961      'in.js': `class Foo { #foo = class { #bar = 123; bar = this.#bar }; foo = this.#foo } if (new Foo().foo.name !== '#foo') throw 'fail: ' + new Foo().foo.name`,
  3962    }),
  3963    test(['in.js', '--outfile=node.js', '--minify', '--keep-names', '--format=esm', '--target=es6'], {
  3964      'in.js': `class Foo { static #foo = class { #bar = 123; bar = this.#bar }; static foo = this.#foo } if (Foo.foo.name !== '#foo') throw 'fail: ' + Foo.foo.name`,
  3965    }),
  3966  
  3967    // https://github.com/evanw/esbuild/issues/2149
  3968    test(['in.js', '--outfile=node.js', '--target=es6', '--keep-names'], {
  3969      'in.js': `
  3970        class Foo {
  3971          static get #foo() { return Foo.name }
  3972          static get foo() { return this.#foo }
  3973        }
  3974        let Bar = Foo
  3975        if (Foo.name !== 'Foo') throw 'fail: ' + Foo.name
  3976        if (Bar.foo !== 'Foo') throw 'fail: ' + Bar.foo
  3977        Foo = { name: 'Bar' }
  3978        if (Foo.name !== 'Bar') throw 'fail: ' + Foo.name
  3979        if (Bar.foo !== 'Foo') throw 'fail: ' + Bar.foo
  3980      `,
  3981    }),
  3982  )
  3983  
  3984  // Test minification of mangled properties (class and object) with a keyword before them
  3985  tests.push(
  3986    test(['in.js', '--outfile=node.js', '--minify', '--mangle-props=.'], {
  3987      'in.js': `
  3988        class Foo {
  3989          static bar = { get baz() { return 123 } }
  3990        }
  3991        if (Foo.bar.baz !== 123) throw 'fail'
  3992      `,
  3993    }),
  3994  )
  3995  
  3996  // Test minification of hoisted top-level symbols declared in nested scopes.
  3997  // Previously this code was incorrectly transformed into this, which crashes:
  3998  //
  3999  //   var c = false;
  4000  //   var d = function a() {
  4001  //     b[a]();
  4002  //   };
  4003  //   for (var a = 0, b = [() => c = true]; a < b.length; a++) {
  4004  //     d();
  4005  //   }
  4006  //   export default c;
  4007  //
  4008  // The problem is that "var i" is declared in a nested scope but hoisted to
  4009  // the top-level scope. So it's accidentally assigned a nested scope slot
  4010  // even though it's a top-level symbol, not a nested scope symbol.
  4011  tests.push(
  4012    test(['in.js', '--outfile=out.js', '--format=esm', '--minify', '--bundle'], {
  4013      'in.js': `
  4014        var worked = false
  4015        var loop = function fn() {
  4016          array[i]();
  4017        };
  4018        for (var i = 0, array = [() => worked = true]; i < array.length; i++) {
  4019          loop();
  4020        }
  4021        export default worked
  4022      `,
  4023      'node.js': `
  4024        import worked from './out.js'
  4025        if (!worked) throw 'fail'
  4026      `,
  4027    }),
  4028  )
  4029  
  4030  // Check for an obscure bug with minification, symbol renaming, and sloppy
  4031  // nested function declarations: https://github.com/evanw/esbuild/issues/2809.
  4032  // Previously esbuild generated the following code:
  4033  //
  4034  //   let f = 0;
  4035  //   for (let l of [1, 2]) {
  4036  //     let t = function(o) {
  4037  //       return o;
  4038  //     };
  4039  //     var f = t;
  4040  //     f += t(l);
  4041  //   }
  4042  //   if (f !== 3)
  4043  //     throw "fail";
  4044  //
  4045  // Notice how "f" is declared twice, leading to a syntax error.
  4046  for (const flags of [[], ['--minify']]) {
  4047    tests.push(
  4048      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  4049        'in.js': `
  4050          let total = 0
  4051          for (let value of [1, 2]) {
  4052            function f(x) { return x }
  4053            total += f(value)
  4054          }
  4055          if (total !== 3) throw 'fail'
  4056        `,
  4057      }),
  4058    )
  4059  }
  4060  
  4061  // Test hoisting variables inside for loop initializers outside of lazy ESM
  4062  // wrappers. Previously this didn't work due to a bug that considered for
  4063  // loop initializers to already be in the top-level scope. For more info
  4064  // see: https://github.com/evanw/esbuild/issues/1455.
  4065  tests.push(
  4066    test(['in.js', '--outfile=node.js', '--bundle'], {
  4067      'in.js': `
  4068        if (require('./nested').foo() !== 10) throw 'fail'
  4069      `,
  4070      'nested.js': `
  4071        for (var i = 0; i < 10; i++) ;
  4072        export function foo() { return i }
  4073      `,
  4074    }),
  4075    test(['in.js', '--outfile=node.js', '--bundle'], {
  4076      'in.js': `
  4077        if (require('./nested').foo() !== 'c') throw 'fail'
  4078      `,
  4079      'nested.js': `
  4080        for (var i in {a: 1, b: 2, c: 3}) ;
  4081        export function foo() { return i }
  4082      `,
  4083    }),
  4084    test(['in.js', '--outfile=node.js', '--bundle'], {
  4085      'in.js': `
  4086        if (require('./nested').foo() !== 3) throw 'fail'
  4087      `,
  4088      'nested.js': `
  4089        for (var i of [1, 2, 3]) ;
  4090        export function foo() { return i }
  4091      `,
  4092    }),
  4093    test(['in.js', '--outfile=node.js', '--bundle', '--target=es6'], {
  4094      'in.js': `
  4095        if (JSON.stringify(require('./nested').foo()) !== '{"b":2,"c":3}') throw 'fail'
  4096      `,
  4097      'nested.js': `
  4098        for (var {a, ...i} = {a: 1, b: 2, c: 3}; 0; ) ;
  4099        export function foo() { return i }
  4100      `,
  4101    }),
  4102    test(['in.js', '--outfile=node.js', '--bundle', '--target=es6'], {
  4103      'in.js': `
  4104        if (JSON.stringify(require('./nested').foo()) !== '{"0":"c"}') throw 'fail'
  4105      `,
  4106      'nested.js': `
  4107        for (var {a, ...i} in {a: 1, b: 2, c: 3}) ;
  4108        export function foo() { return i }
  4109      `,
  4110    }),
  4111    test(['in.js', '--outfile=node.js', '--bundle', '--target=es6'], {
  4112      'in.js': `
  4113        if (JSON.stringify(require('./nested').foo()) !== '{"b":2,"c":3}') throw 'fail'
  4114      `,
  4115      'nested.js': `
  4116        for (var {a, ...i} of [{a: 1, b: 2, c: 3}]) ;
  4117        export function foo() { return i }
  4118      `,
  4119    }),
  4120  )
  4121  
  4122  // Test tree shaking
  4123  tests.push(
  4124    // Keep because used (ES6)
  4125    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4126      'entry.js': `import * as foo from './foo'; if (global.dce0 !== 123 || foo.abc !== 'abc') throw 'fail'`,
  4127      'foo/index.js': `global.dce0 = 123; export const abc = 'abc'`,
  4128      'foo/package.json': `{ "sideEffects": false }`,
  4129    }),
  4130  
  4131    // Remove because unused (ES6)
  4132    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4133      'entry.js': `import * as foo from './foo'; if (global.dce1 !== void 0) throw 'fail'`,
  4134      'foo/index.js': `global.dce1 = 123; export const abc = 'abc'`,
  4135      'foo/package.json': `{ "sideEffects": false }`,
  4136    }),
  4137  
  4138    // Keep because side effects (ES6)
  4139    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4140      'entry.js': `import * as foo from './foo'; if (global.dce2 !== 123) throw 'fail'`,
  4141      'foo/index.js': `global.dce2 = 123; export const abc = 'abc'`,
  4142      'foo/package.json': `{ "sideEffects": true }`,
  4143    }),
  4144  
  4145    // Keep because used (CommonJS)
  4146    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4147      'entry.js': `import foo from './foo'; if (global.dce3 !== 123 || foo.abc !== 'abc') throw 'fail'`,
  4148      'foo/index.js': `global.dce3 = 123; exports.abc = 'abc'`,
  4149      'foo/package.json': `{ "sideEffects": false }`,
  4150    }),
  4151  
  4152    // Remove because unused (CommonJS)
  4153    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4154      'entry.js': `import foo from './foo'; if (global.dce4 !== void 0) throw 'fail'`,
  4155      'foo/index.js': `global.dce4 = 123; exports.abc = 'abc'`,
  4156      'foo/package.json': `{ "sideEffects": false }`,
  4157    }),
  4158  
  4159    // Keep because side effects (CommonJS)
  4160    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4161      'entry.js': `import foo from './foo'; if (global.dce5 !== 123) throw 'fail'`,
  4162      'foo/index.js': `global.dce5 = 123; exports.abc = 'abc'`,
  4163      'foo/package.json': `{ "sideEffects": true }`,
  4164    }),
  4165  
  4166    // Note: Tree shaking this could technically be considered incorrect because
  4167    // the import is for a property whose getter in this case has a side effect.
  4168    // However, this is very unlikely and the vast majority of the time people
  4169    // would likely rather have the code be tree-shaken. This test case enforces
  4170    // the technically incorrect behavior as documentation that this edge case
  4171    // is being ignored.
  4172    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4173      'entry.js': `import {foo, bar} from './foo'; let unused = foo; if (bar) throw 'expected "foo" to be tree-shaken'`,
  4174      'foo.js': `module.exports = {get foo() { module.exports.bar = 1 }, bar: 0}`,
  4175    }),
  4176  
  4177    // Test for an implicit and explicit "**/" prefix (see https://github.com/evanw/esbuild/issues/1184)
  4178    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4179      'entry.js': `import './foo'; if (global.dce6 !== 123) throw 'fail'`,
  4180      'foo/dir/x.js': `global.dce6 = 123`,
  4181      'foo/package.json': `{ "main": "dir/x", "sideEffects": ["x.*"] }`,
  4182    }),
  4183    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4184      'entry.js': `import './foo'; if (global.dce6 !== 123) throw 'fail'`,
  4185      'foo/dir/x.js': `global.dce6 = 123`,
  4186      'foo/package.json': `{ "main": "dir/x", "sideEffects": ["**/x.*"] }`,
  4187    }),
  4188  
  4189    // Test side effect detection for destructuring
  4190    test(['--bundle', 'entry.js', '--outfile=out.js'], {
  4191      'entry.js': `
  4192        let [a] = {}; // This must not be tree-shaken
  4193      `,
  4194      'node.js': `
  4195        pass: {
  4196          try {
  4197            require('./out.js')
  4198          } catch (e) {
  4199            break pass
  4200          }
  4201          throw 'fail'
  4202        }
  4203      `,
  4204    }),
  4205    test(['--bundle', 'entry.js', '--outfile=node.js'], {
  4206      'entry.js': `
  4207        let sideEffect = false
  4208        let { a } = { // This must not be tree-shaken
  4209          get a() {
  4210            sideEffect = true
  4211          },
  4212        };
  4213        if (!sideEffect) throw 'fail'
  4214      `,
  4215    }),
  4216  
  4217    // Keep because side effects (decorators)
  4218    test(['--bundle', 'entry.ts', '--outfile=node.js', '--target=es2022'], {
  4219      'entry.ts': `
  4220        import { order } from './decorator'
  4221        import './class'
  4222        import './field'
  4223        import './method'
  4224        import './accessor'
  4225        import './parameter'
  4226        import './static-field'
  4227        import './static-method'
  4228        import './static-accessor'
  4229        import './static-parameter'
  4230        if (order + '' !== ',field,method,accessor,parameter,staticField,staticMethod,staticAccessor,staticParameter') throw 'fail: ' + order
  4231      `,
  4232      'decorator.ts': `
  4233        export const order = []
  4234        export const fn = (_, name) => {
  4235          order.push(name)
  4236        }
  4237      `,
  4238      'class.ts': `import { fn } from './decorator'; @fn class Foo {}`,
  4239      'field.ts': `import { fn } from './decorator'; class Foo { @fn field }`,
  4240      'method.ts': `import { fn } from './decorator'; class Foo { @fn method() {} }`,
  4241      'accessor.ts': `import { fn } from './decorator'; class Foo { @fn accessor accessor }`,
  4242      'parameter.ts': `import { fn } from './decorator'; class Foo { parameter(@fn arg) {} }`,
  4243      'static-field.ts': `import { fn } from './decorator'; class Foo { @fn static staticField }`,
  4244      'static-method.ts': `import { fn } from './decorator'; class Foo { @fn static staticMethod() {} }`,
  4245      'static-accessor.ts': `import { fn } from './decorator'; class Foo { @fn static accessor staticAccessor }`,
  4246      'static-parameter.ts': `import { fn } from './decorator'; class Foo { static staticParameter(@fn arg) {} }`,
  4247      'tsconfig.json': `{
  4248        "compilerOptions": {
  4249          "experimentalDecorators": true
  4250        }
  4251      }`,
  4252    }),
  4253  )
  4254  
  4255  // Test obscure CommonJS symbol edge cases
  4256  tests.push(
  4257    test(['in.js', '--outfile=node.js', '--bundle'], {
  4258      'in.js': `const ns = require('./foo'); if (ns.foo !== 123 || ns.bar !== 123) throw 'fail'`,
  4259      'foo.js': `var exports, module; module.exports.foo = 123; exports.bar = exports.foo`,
  4260    }),
  4261    test(['in.js', '--outfile=node.js', '--bundle'], {
  4262      'in.js': `require('./foo'); require('./bar')`,
  4263      'foo.js': `let exports; if (exports !== void 0) throw 'fail'`,
  4264      'bar.js': `let module; if (module !== void 0) throw 'fail'`,
  4265    }),
  4266    test(['in.js', '--outfile=node.js', '--bundle'], {
  4267      'in.js': `const ns = require('./foo'); if (ns.foo !== void 0 || ns.default.foo !== 123) throw 'fail'`,
  4268      'foo.js': `var exports = {foo: 123}; export default exports`,
  4269    }),
  4270    test(['in.js', '--outfile=node.js', '--bundle'], {
  4271      'in.js': `const ns = require('./foo'); if (ns !== 123) throw 'fail'`,
  4272      'foo.ts': `let module = 123; export = module`,
  4273    }),
  4274    test(['in.js', '--outfile=node.js', '--bundle'], {
  4275      'in.js': `require('./foo')`,
  4276      'foo.js': `var require; if (require !== void 0) throw 'fail'`,
  4277    }),
  4278    test(['in.js', '--outfile=node.js', '--bundle'], {
  4279      'in.js': `require('./foo')`,
  4280      'foo.js': `var require = x => x; if (require('does not exist') !== 'does not exist') throw 'fail'`,
  4281    }),
  4282    test(['in.js', '--outfile=node.js', '--bundle'], {
  4283      'in.js': `const ns = require('./foo'); if (ns.a !== 123 || ns.b.a !== 123) throw 'fail'`,
  4284      'foo.js': `exports.a = 123; exports.b = this`,
  4285    }),
  4286    test(['in.js', '--outfile=node.js', '--bundle', '--log-level=error'], {
  4287      'in.js': `const ns = require('./foo'); if (ns.a !== 123 || ns.b !== void 0) throw 'fail'`,
  4288      'foo.js': `export let a = 123, b = this`,
  4289    }),
  4290  )
  4291  
  4292  // Optional chain lowering tests
  4293  for (let [code, expected] of [
  4294    ['array?.map?.(x => -x).filter', '[].filter'],
  4295    ['array?.map?.(x => -x)["filter"]', '[].filter'],
  4296    ['array?.map?.(x => -x).filter(x => x < -1)', '[-2, -3]'],
  4297    ['array?.map?.(x => -x)["filter"](x => x < -1)', '[-2, -3]'],
  4298  ]) {
  4299    tests.push(
  4300      test(['in.js', '--outfile=node.js', '--target=es6', '--format=esm'], {
  4301        'in.js': `
  4302          import * as assert from 'assert';
  4303          let array = [1, 2, 3];
  4304          let result = ${code};
  4305          assert.deepStrictEqual(result, ${expected});
  4306        `,
  4307      }),
  4308      test(['in.js', '--outfile=node.js', '--target=es6', '--format=esm'], {
  4309        'in.js': `
  4310          import * as assert from 'assert';
  4311          function test(array, result = ${code}) {
  4312            return result
  4313          }
  4314          assert.deepStrictEqual(test([1, 2, 3]), ${expected});
  4315        `,
  4316      }),
  4317    )
  4318  }
  4319  
  4320  // Class lowering tests
  4321  for (let flags of [['--target=es2022'], ['--target=es6'], ['--bundle', '--target=es2022'], ['--bundle', '--target=es6']]) {
  4322    // Skip running these tests untransformed. I believe V8 actually has a bug
  4323    // here and esbuild is correct, both because SpiderMonkey and JavaScriptCore
  4324    // run this code fine and because the specification says that the left operand
  4325    // of the assignment operator should be evaluated first but V8 appears to be
  4326    // evaluating it later on. The bug with V8 has been filed here for reference:
  4327    // https://bugs.chromium.org/p/v8/issues/detail?id=12352
  4328    if (flags.includes('--target=es6')) {
  4329      tests.push(
  4330        test(['in.js', '--outfile=node.js'].concat(flags), {
  4331          'in.js': `
  4332            let bar
  4333            class Foo {
  4334              get #foo() { bar = new Foo; return this.result }
  4335              set #foo(x) { this.result = x }
  4336              bar() {
  4337                bar = this
  4338                bar.result = 2
  4339                bar.#foo *= 3
  4340              }
  4341            }
  4342            let foo = new Foo()
  4343            foo.bar()
  4344            if (foo === bar || foo.result !== 6 || bar.result !== void 0) throw 'fail'
  4345          `,
  4346        }),
  4347        test(['in.js', '--outfile=node.js'].concat(flags), {
  4348          'in.js': `
  4349            let bar
  4350            class Foo {
  4351              get #foo() { bar = new Foo; return this.result }
  4352              set #foo(x) { this.result = x }
  4353              bar() {
  4354                bar = this
  4355                bar.result = 2
  4356                bar.#foo **= 3
  4357              }
  4358            }
  4359            let foo = new Foo()
  4360            foo.bar()
  4361            if (foo === bar || foo.result !== 8 || bar.result !== void 0) throw 'fail'
  4362          `,
  4363        }),
  4364      )
  4365    }
  4366  
  4367    // This log message is only an error during bundling
  4368    const assignToConstantMessage = flags.includes('--bundle')
  4369      ? `${errorIcon} [ERROR] Cannot assign to "Foo" because it is a constant`
  4370      : `▲ [WARNING] This assignment will throw because "Foo" is a constant [assign-to-constant]`
  4371  
  4372    tests.push(
  4373      test(['in.js', '--outfile=node.js'].concat(flags), {
  4374        'in.js': `
  4375          class Foo {
  4376            foo = 123
  4377            self = this
  4378            #method() {
  4379              if (this.foo !== 123) throw 'fail'
  4380            }
  4381            bar() {
  4382              let that = () => this
  4383              that().#method()
  4384              that().#method?.()
  4385              that()?.#method()
  4386              that()?.#method?.()
  4387              that().self.#method()
  4388              that().self.#method?.()
  4389              that().self?.#method()
  4390              that().self?.#method?.()
  4391              that()?.self.#method()
  4392              that()?.self.#method?.()
  4393              that()?.self?.#method()
  4394              that()?.self?.#method?.()
  4395            }
  4396          }
  4397          new Foo().bar()
  4398        `,
  4399      }),
  4400      test(['in.js', '--outfile=node.js'].concat(flags), {
  4401        'in.js': `
  4402          class Foo {
  4403            foo = 123
  4404            get #bar() { return this.foo }
  4405            set #bar(x) { this.foo = x }
  4406            bar() {
  4407              let that = () => this
  4408              that().#bar **= 2
  4409              if (this.foo !== 15129) throw 'fail'
  4410            }
  4411          }
  4412          new Foo().bar()
  4413        `,
  4414      }),
  4415      test(['in.js', '--outfile=node.js'].concat(flags), {
  4416        'in.js': `
  4417          let bar
  4418          class Foo {
  4419            get #foo() { bar = new Foo; return this.result }
  4420            set #foo(x) { this.result = x }
  4421            bar() {
  4422              bar = this
  4423              bar.result = 2
  4424              ++bar.#foo
  4425            }
  4426          }
  4427          let foo = new Foo()
  4428          foo.bar()
  4429          if (foo === bar || foo.result !== 3 || bar.result !== void 0) throw 'fail'
  4430        `,
  4431      }),
  4432      test(['in.js', '--outfile=node.js'].concat(flags), {
  4433        'in.js': `
  4434          function print(x) {
  4435            return typeof x + ':' + x
  4436          }
  4437  
  4438          function check(before, op, after) {
  4439            let result = new Foo(before)[op]()
  4440            if (result !== after) throw before + ' ' + op + ' should be ' + after + ' but was ' + result
  4441          }
  4442  
  4443          class Foo {
  4444            #foo
  4445            constructor(foo) { this.#foo = foo }
  4446            preInc = () => print(++this.#foo) + ' ' + print(this.#foo)
  4447            preDec = () => print(--this.#foo) + ' ' + print(this.#foo)
  4448            postInc = () => print(this.#foo++) + ' ' + print(this.#foo)
  4449            postDec = () => print(this.#foo--) + ' ' + print(this.#foo)
  4450          }
  4451  
  4452          check(123, 'preInc', 'number:124 number:124')
  4453          check(123, 'preDec', 'number:122 number:122')
  4454          check(123, 'postInc', 'number:123 number:124')
  4455          check(123, 'postDec', 'number:123 number:122')
  4456  
  4457          check('123', 'preInc', 'number:124 number:124')
  4458          check('123', 'preDec', 'number:122 number:122')
  4459          check('123', 'postInc', 'number:123 number:124')
  4460          check('123', 'postDec', 'number:123 number:122')
  4461  
  4462          check('x', 'preInc', 'number:NaN number:NaN')
  4463          check('x', 'preDec', 'number:NaN number:NaN')
  4464          check('x', 'postInc', 'number:NaN number:NaN')
  4465          check('x', 'postDec', 'number:NaN number:NaN')
  4466  
  4467          check(BigInt(123), 'preInc', 'bigint:124 bigint:124')
  4468          check(BigInt(123), 'preDec', 'bigint:122 bigint:122')
  4469          check(BigInt(123), 'postInc', 'bigint:123 bigint:124')
  4470          check(BigInt(123), 'postDec', 'bigint:123 bigint:122')
  4471        `,
  4472      }),
  4473      test(['in.js', '--outfile=node.js'].concat(flags), {
  4474        'in.js': `
  4475          function print(x) {
  4476            return typeof x + ':' + x
  4477          }
  4478  
  4479          function check(before, op, after) {
  4480            let result = new Foo(before)[op]()
  4481            if (result !== after) throw before + ' ' + op + ' should be ' + after + ' but was ' + result
  4482          }
  4483  
  4484          class Foo {
  4485            get #foo() { return this.__foo }
  4486            set #foo(x) { this.__foo = x }
  4487            constructor(foo) { this.#foo = foo }
  4488            preInc = () => print(++this.#foo) + ' ' + print(this.#foo)
  4489            preDec = () => print(--this.#foo) + ' ' + print(this.#foo)
  4490            postInc = () => print(this.#foo++) + ' ' + print(this.#foo)
  4491            postDec = () => print(this.#foo--) + ' ' + print(this.#foo)
  4492          }
  4493  
  4494          check(123, 'preInc', 'number:124 number:124')
  4495          check(123, 'preDec', 'number:122 number:122')
  4496          check(123, 'postInc', 'number:123 number:124')
  4497          check(123, 'postDec', 'number:123 number:122')
  4498  
  4499          check('123', 'preInc', 'number:124 number:124')
  4500          check('123', 'preDec', 'number:122 number:122')
  4501          check('123', 'postInc', 'number:123 number:124')
  4502          check('123', 'postDec', 'number:123 number:122')
  4503  
  4504          check('x', 'preInc', 'number:NaN number:NaN')
  4505          check('x', 'preDec', 'number:NaN number:NaN')
  4506          check('x', 'postInc', 'number:NaN number:NaN')
  4507          check('x', 'postDec', 'number:NaN number:NaN')
  4508  
  4509          check(BigInt(123), 'preInc', 'bigint:124 bigint:124')
  4510          check(BigInt(123), 'preDec', 'bigint:122 bigint:122')
  4511          check(BigInt(123), 'postInc', 'bigint:123 bigint:124')
  4512          check(BigInt(123), 'postDec', 'bigint:123 bigint:122')
  4513        `,
  4514      }),
  4515      test(['in.js', '--outfile=node.js'].concat(flags), {
  4516        'in.js': `
  4517          function print(x) {
  4518            return typeof x + ':' + x
  4519          }
  4520  
  4521          function check(before, op, after) {
  4522            Foo.setup(before)
  4523            let result = Foo[op]()
  4524            if (result !== after) throw before + ' ' + op + ' should be ' + after + ' but was ' + result
  4525          }
  4526  
  4527          class Foo {
  4528            static #foo
  4529            static setup(x) { Foo.#foo = x }
  4530            static preInc = () => print(++Foo.#foo) + ' ' + print(Foo.#foo)
  4531            static preDec = () => print(--Foo.#foo) + ' ' + print(Foo.#foo)
  4532            static postInc = () => print(Foo.#foo++) + ' ' + print(Foo.#foo)
  4533            static postDec = () => print(Foo.#foo--) + ' ' + print(Foo.#foo)
  4534          }
  4535  
  4536          check(123, 'preInc', 'number:124 number:124')
  4537          check(123, 'preDec', 'number:122 number:122')
  4538          check(123, 'postInc', 'number:123 number:124')
  4539          check(123, 'postDec', 'number:123 number:122')
  4540  
  4541          check('123', 'preInc', 'number:124 number:124')
  4542          check('123', 'preDec', 'number:122 number:122')
  4543          check('123', 'postInc', 'number:123 number:124')
  4544          check('123', 'postDec', 'number:123 number:122')
  4545  
  4546          check('x', 'preInc', 'number:NaN number:NaN')
  4547          check('x', 'preDec', 'number:NaN number:NaN')
  4548          check('x', 'postInc', 'number:NaN number:NaN')
  4549          check('x', 'postDec', 'number:NaN number:NaN')
  4550  
  4551          check(BigInt(123), 'preInc', 'bigint:124 bigint:124')
  4552          check(BigInt(123), 'preDec', 'bigint:122 bigint:122')
  4553          check(BigInt(123), 'postInc', 'bigint:123 bigint:124')
  4554          check(BigInt(123), 'postDec', 'bigint:123 bigint:122')
  4555        `,
  4556      }),
  4557      test(['in.js', '--outfile=node.js'].concat(flags), {
  4558        'in.js': `
  4559          function print(x) {
  4560            return typeof x + ':' + x
  4561          }
  4562  
  4563          function check(before, op, after) {
  4564            Foo.setup(before)
  4565            let result = Foo[op]()
  4566            if (result !== after) throw before + ' ' + op + ' should be ' + after + ' but was ' + result
  4567          }
  4568  
  4569          class Foo {
  4570            static get #foo() { return this.__foo }
  4571            static set #foo(x) { this.__foo = x }
  4572            static setup(x) { this.#foo = x }
  4573            static preInc = () => print(++this.#foo) + ' ' + print(this.#foo)
  4574            static preDec = () => print(--this.#foo) + ' ' + print(this.#foo)
  4575            static postInc = () => print(this.#foo++) + ' ' + print(this.#foo)
  4576            static postDec = () => print(this.#foo--) + ' ' + print(this.#foo)
  4577          }
  4578  
  4579          check(123, 'preInc', 'number:124 number:124')
  4580          check(123, 'preDec', 'number:122 number:122')
  4581          check(123, 'postInc', 'number:123 number:124')
  4582          check(123, 'postDec', 'number:123 number:122')
  4583  
  4584          check('123', 'preInc', 'number:124 number:124')
  4585          check('123', 'preDec', 'number:122 number:122')
  4586          check('123', 'postInc', 'number:123 number:124')
  4587          check('123', 'postDec', 'number:123 number:122')
  4588  
  4589          check('x', 'preInc', 'number:NaN number:NaN')
  4590          check('x', 'preDec', 'number:NaN number:NaN')
  4591          check('x', 'postInc', 'number:NaN number:NaN')
  4592          check('x', 'postDec', 'number:NaN number:NaN')
  4593  
  4594          check(BigInt(123), 'preInc', 'bigint:124 bigint:124')
  4595          check(BigInt(123), 'preDec', 'bigint:122 bigint:122')
  4596          check(BigInt(123), 'postInc', 'bigint:123 bigint:124')
  4597          check(BigInt(123), 'postDec', 'bigint:123 bigint:122')
  4598        `,
  4599      }),
  4600      test(['in.js', '--outfile=node.js'].concat(flags), {
  4601        'in.js': `
  4602          function expect(fn, msg) {
  4603            try {
  4604              fn()
  4605            } catch (e) {
  4606              ${flags.includes('--target=es6')
  4607            // Only check the exact error message for esbuild
  4608            ? `if (e instanceof TypeError && e.message === msg) return`
  4609            // For node, just check whether a type error is thrown
  4610            : `if (e instanceof TypeError) return`
  4611          }
  4612            }
  4613            throw 'expected ' + msg
  4614          }
  4615          class Foo {
  4616            #foo
  4617            #method() {}
  4618            get #getter() {}
  4619            set #setter(x) {}
  4620            bar() {
  4621              let obj = {}
  4622              expect(() => obj.#foo, 'Cannot read from private field')
  4623              expect(() => obj.#foo = 1, 'Cannot write to private field')
  4624              expect(() => obj.#getter, 'Cannot read from private field')
  4625              expect(() => obj.#setter = 1, 'Cannot write to private field')
  4626              expect(() => obj.#method, 'Cannot access private method')
  4627              expect(() => obj.#method = 1, 'Cannot write to private field')
  4628              expect(() => this.#setter, 'member.get is not a function')
  4629              expect(() => this.#getter = 1, 'member.set is not a function')
  4630              expect(() => this.#method = 1, 'member.set is not a function')
  4631            }
  4632          }
  4633          new Foo().bar()
  4634        `,
  4635      }, {
  4636        expectedStderr: `▲ [WARNING] Writing to read-only method "#method" will throw [private-name-will-throw]
  4637  
  4638      in.js:22:29:
  4639        22 │             expect(() => obj.#method = 1, 'Cannot write to private...
  4640           ╵                              ~~~~~~~
  4641  
  4642  ▲ [WARNING] Reading from setter-only property "#setter" will throw [private-name-will-throw]
  4643  
  4644      in.js:23:30:
  4645        23 │             expect(() => this.#setter, 'member.get is not a functi...
  4646           ╵                               ~~~~~~~
  4647  
  4648  ▲ [WARNING] Writing to getter-only property "#getter" will throw [private-name-will-throw]
  4649  
  4650      in.js:24:30:
  4651        24 │             expect(() => this.#getter = 1, 'member.set is not a fu...
  4652           ╵                               ~~~~~~~
  4653  
  4654  ▲ [WARNING] Writing to read-only method "#method" will throw [private-name-will-throw]
  4655  
  4656      in.js:25:30:
  4657        25 │             expect(() => this.#method = 1, 'member.set is not a fu...
  4658           ╵                               ~~~~~~~
  4659  
  4660  `,
  4661      }),
  4662      test(['in.js', '--outfile=node.js'].concat(flags), {
  4663        'in.js': `
  4664          let setterCalls = 0
  4665          class Foo {
  4666            key
  4667            set key(x) { setterCalls++ }
  4668          }
  4669          let foo = new Foo()
  4670          if (setterCalls !== 0 || !foo.hasOwnProperty('key') || foo.key !== void 0) throw 'fail'
  4671        `,
  4672      }, {
  4673        expectedStderr: `▲ [WARNING] Duplicate member "key" in class body [duplicate-class-member]
  4674  
  4675      in.js:5:14:
  4676        5 │           set key(x) { setterCalls++ }
  4677          ╵               ~~~
  4678  
  4679    The original member "key" is here:
  4680  
  4681      in.js:4:10:
  4682        4 │           key
  4683          ╵           ~~~
  4684  
  4685  `,
  4686      }),
  4687      test(['in.js', '--outfile=node.js'].concat(flags), {
  4688        'in.js': `
  4689          let setterCalls = 0
  4690          class Foo {
  4691            key = 123
  4692            set key(x) { setterCalls++ }
  4693          }
  4694          let foo = new Foo()
  4695          if (setterCalls !== 0 || !foo.hasOwnProperty('key') || foo.key !== 123) throw 'fail'
  4696        `,
  4697      }, {
  4698        expectedStderr: `▲ [WARNING] Duplicate member "key" in class body [duplicate-class-member]
  4699  
  4700      in.js:5:14:
  4701        5 │           set key(x) { setterCalls++ }
  4702          ╵               ~~~
  4703  
  4704    The original member "key" is here:
  4705  
  4706      in.js:4:10:
  4707        4 │           key = 123
  4708          ╵           ~~~
  4709  
  4710  `,
  4711      }),
  4712      test(['in.js', '--outfile=node.js'].concat(flags), {
  4713        'in.js': `
  4714          let toStringCalls = 0
  4715          let setterCalls = 0
  4716          class Foo {
  4717            [{toString() {
  4718              toStringCalls++
  4719              return 'key'
  4720            }}]
  4721            set key(x) { setterCalls++ }
  4722          }
  4723          let foo = new Foo()
  4724          if (setterCalls !== 0 || toStringCalls !== 1 || !foo.hasOwnProperty('key') || foo.key !== void 0) throw 'fail'
  4725        `,
  4726      }),
  4727      test(['in.js', '--outfile=node.js'].concat(flags), {
  4728        'in.js': `
  4729          let toStringCalls = 0
  4730          let setterCalls = 0
  4731          class Foo {
  4732            [{toString() {
  4733              toStringCalls++
  4734              return 'key'
  4735            }}] = 123
  4736            set key(x) { setterCalls++ }
  4737          }
  4738          let foo = new Foo()
  4739          if (setterCalls !== 0 || toStringCalls !== 1 || !foo.hasOwnProperty('key') || foo.key !== 123) throw 'fail'
  4740        `,
  4741      }),
  4742      test(['in.js', '--outfile=node.js'].concat(flags), {
  4743        'in.js': `
  4744          let key = Symbol('key')
  4745          let setterCalls = 0
  4746          class Foo {
  4747            [key]
  4748            set [key](x) { setterCalls++ }
  4749          }
  4750          let foo = new Foo()
  4751          if (setterCalls !== 0 || !foo.hasOwnProperty(key) || foo[key] !== void 0) throw 'fail'
  4752        `,
  4753      }),
  4754      test(['in.js', '--outfile=node.js'].concat(flags), {
  4755        'in.js': `
  4756          let key = Symbol('key')
  4757          let setterCalls = 0
  4758          class Foo {
  4759            [key] = 123
  4760            set [key](x) { setterCalls++ }
  4761          }
  4762          let foo = new Foo()
  4763          if (setterCalls !== 0 || !foo.hasOwnProperty(key) || foo[key] !== 123) throw 'fail'
  4764        `,
  4765      }),
  4766  
  4767      // Test class re-assignment
  4768      test(['in.js', '--outfile=node.js'].concat(flags), {
  4769        'in.js': `
  4770          class Foo {
  4771            foo = () => this
  4772          }
  4773          let foo = new Foo()
  4774          if (foo.foo() !== foo) throw 'fail'
  4775        `,
  4776      }),
  4777      test(['in.js', '--outfile=node.js'].concat(flags), {
  4778        'in.js': `
  4779          class Foo {
  4780            static foo = () => this
  4781          }
  4782          let old = Foo
  4783          let foo = Foo.foo
  4784          Foo = class Bar {}
  4785          if (foo() !== old) throw 'fail'
  4786        `,
  4787      }),
  4788      test(['in.js', '--outfile=node.js'].concat(flags), {
  4789        'in.js': `
  4790          class Foo {
  4791            bar = 'works'
  4792            foo = () => class {
  4793              [this.bar]
  4794            }
  4795          }
  4796          let foo = new Foo().foo
  4797          if (!('works' in new (foo()))) throw 'fail'
  4798        `,
  4799      }),
  4800      test(['in.js', '--outfile=node.js'].concat(flags), {
  4801        'in.js': `
  4802          class Foo {
  4803            static bar = 'works'
  4804            static foo = () => class {
  4805              [this.bar]
  4806            }
  4807          }
  4808          let foo = Foo.foo
  4809          Foo = class Bar {}
  4810          if (!('works' in new (foo()))) throw 'fail'
  4811        `,
  4812      }),
  4813      test(['in.js', '--outfile=node.js'].concat(flags), {
  4814        'in.js': `
  4815          class Foo {
  4816            static foo() { return this.#foo }
  4817            static #foo = Foo
  4818          }
  4819          let old = Foo
  4820          Foo = class Bar {}
  4821          if (old.foo() !== old) throw 'fail'
  4822        `,
  4823      }),
  4824      test(['in.js', '--outfile=node.js'].concat(flags), {
  4825        'in.js': `
  4826          class Foo {
  4827            static foo() { return this.#foo() }
  4828            static #foo() { return Foo }
  4829          }
  4830          let old = Foo
  4831          Foo = class Bar {}
  4832          if (old.foo() !== old) throw 'fail'
  4833        `,
  4834      }),
  4835      test(['in.js', '--outfile=node.js'].concat(flags), {
  4836        'in.js': `
  4837          try {
  4838            class Foo {
  4839              static foo() { return this.#foo }
  4840              static #foo = Foo = class Bar {}
  4841            }
  4842            throw 'fail'
  4843          } catch (e) {
  4844            if (!(e instanceof TypeError))
  4845              throw e
  4846          }
  4847        `,
  4848      }, {
  4849        expectedStderr: assignToConstantMessage + `
  4850  
  4851      in.js:5:26:
  4852        5 │             static #foo = Foo = class Bar {}
  4853          ╵                           ~~~
  4854  
  4855    The symbol "Foo" was declared a constant here:
  4856  
  4857      in.js:3:16:
  4858        3 │           class Foo {
  4859          ╵                 ~~~
  4860  
  4861  `,
  4862      }),
  4863      test(['in.js', '--outfile=node.js'].concat(flags), {
  4864        'in.js': `
  4865          class Foo {
  4866            static foo() { return this.#foo() }
  4867            static #foo() { Foo = class Bar{} }
  4868          }
  4869          try {
  4870            Foo.foo()
  4871            throw 'fail'
  4872          } catch (e) {
  4873            if (!(e instanceof TypeError))
  4874              throw e
  4875          }
  4876        `,
  4877      }, {
  4878        expectedStderr: assignToConstantMessage + `
  4879  
  4880      in.js:4:26:
  4881        4 │           static #foo() { Foo = class Bar{} }
  4882          ╵                           ~~~
  4883  
  4884    The symbol "Foo" was declared a constant here:
  4885  
  4886      in.js:2:14:
  4887        2 │         class Foo {
  4888          ╵               ~~~
  4889  
  4890  `,
  4891      }),
  4892  
  4893      // Issue: https://github.com/evanw/esbuild/issues/901
  4894      test(['in.js', '--outfile=node.js'].concat(flags), {
  4895        'in.js': `
  4896          class A {
  4897            pub = this.#priv;
  4898            #priv() {
  4899              return 'Inside #priv';
  4900            }
  4901          }
  4902          if (new A().pub() !== 'Inside #priv') throw 'fail';
  4903        `,
  4904      }),
  4905      test(['in.js', '--outfile=node.js'].concat(flags), {
  4906        'in.js': `
  4907          class A {
  4908            static pub = this.#priv;
  4909            static #priv() {
  4910              return 'Inside #priv';
  4911            }
  4912          }
  4913          if (A.pub() !== 'Inside #priv') throw 'fail';
  4914        `,
  4915      }),
  4916  
  4917      // Issue: https://github.com/evanw/esbuild/issues/1066
  4918      test(['in.js', '--outfile=node.js'].concat(flags), {
  4919        'in.js': `
  4920          class Test {
  4921            #x = 2;
  4922            #y = [];
  4923            z = 2;
  4924  
  4925            get x() { return this.#x; }
  4926            get y() { return this.#y; }
  4927  
  4928            world() {
  4929              return [1,[2,3],4];
  4930            }
  4931  
  4932            hello() {
  4933              [this.#x,this.#y,this.z] = this.world();
  4934            }
  4935          }
  4936  
  4937          var t = new Test();
  4938          t.hello();
  4939          if (t.x !== 1 || t.y[0] !== 2 || t.y[1] !== 3 || t.z !== 4) throw 'fail';
  4940        `,
  4941      }),
  4942      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  4943        'in.js': `
  4944          import x from './class'
  4945          if (x.bar !== 123) throw 'fail'
  4946        `,
  4947        'class.js': `
  4948          class Foo {
  4949            static foo = 123
  4950          }
  4951          export default class extends Foo {
  4952            static #foo = super.foo
  4953            static bar = this.#foo
  4954          }
  4955        `,
  4956      }),
  4957      test(['in.js', '--outfile=node.js', '--bundle', '--keep-names'].concat(flags), {
  4958        'in.js': `
  4959          import x from './class'
  4960          if (x.bar !== 123) throw 'fail'
  4961          if (x.name !== 'default') throw 'fail: ' + x.name
  4962        `,
  4963        'class.js': `
  4964          class Foo {
  4965            static foo = 123
  4966          }
  4967          export default class extends Foo {
  4968            static #foo = super.foo
  4969            static bar = this.#foo
  4970          }
  4971        `,
  4972      }),
  4973      test(['in.js', '--outfile=node.js'].concat(flags), {
  4974        'in.js': `
  4975          class Foo {
  4976            #a
  4977            #b
  4978            #c
  4979            foo() {
  4980              [this.#a, this.#b, this.#c] = {
  4981                [Symbol.iterator]() {
  4982                  let value = 0
  4983                  return {
  4984                    next() {
  4985                      return { value: ++value, done: false }
  4986                    }
  4987                  }
  4988                }
  4989              }
  4990              return [this.#a, this.#b, this.#c].join(' ')
  4991            }
  4992          }
  4993          if (new Foo().foo() !== '1 2 3') throw 'fail'
  4994        `,
  4995      }),
  4996      test(['in.js', '--outfile=node.js'].concat(flags), {
  4997        'in.js': `
  4998          class Foo {
  4999            #a
  5000            #b
  5001            #c
  5002            #d
  5003            #e
  5004            #f
  5005            foo() {
  5006              [
  5007                {x: this.#a},
  5008                [[, this.#b, ,]],
  5009                {y: this.#c = 3},
  5010                {x: this.x, y: this.y, ...this.#d},
  5011                [, , ...this.#e],
  5012                [{x: [{y: [this.#f]}]}],
  5013              ] = [
  5014                {x: 1},
  5015                [[1, 2, 3]],
  5016                {},
  5017                {x: 2, y: 3, z: 4, w: 5},
  5018                [4, 5, 6, 7, 8],
  5019                [{x: [{y: [9]}]}],
  5020              ]
  5021              return JSON.stringify([
  5022                this.#a,
  5023                this.#b,
  5024                this.#c,
  5025                this.#d,
  5026                this.#e,
  5027                this.#f,
  5028              ])
  5029            }
  5030          }
  5031          if (new Foo().foo() !== '[1,2,3,{"z":4,"w":5},[6,7,8],9]') throw 'fail'
  5032        `,
  5033      }),
  5034      test(['in.js', '--outfile=node.js'].concat(flags), {
  5035        'in.js': `
  5036          class Foo {
  5037            values = []
  5038            set #a(a) { this.values.push(a) }
  5039            set #b(b) { this.values.push(b) }
  5040            set #c(c) { this.values.push(c) }
  5041            set #d(d) { this.values.push(d) }
  5042            set #e(e) { this.values.push(e) }
  5043            set #f(f) { this.values.push(f) }
  5044            foo() {
  5045              [
  5046                {x: this.#a},
  5047                [[, this.#b, ,]],
  5048                {y: this.#c = 3},
  5049                {x: this.x, y: this.y, ...this.#d},
  5050                [, , ...this.#e],
  5051                [{x: [{y: [this.#f]}]}],
  5052              ] = [
  5053                {x: 1},
  5054                [[1, 2, 3]],
  5055                {},
  5056                {x: 2, y: 3, z: 4, w: 5},
  5057                [4, 5, 6, 7, 8],
  5058                [{x: [{y: [9]}]}],
  5059              ]
  5060              return JSON.stringify(this.values)
  5061            }
  5062          }
  5063          if (new Foo().foo() !== '[1,2,3,{"z":4,"w":5},[6,7,8],9]') throw 'fail'
  5064        `,
  5065      }),
  5066      test(['in.js', '--outfile=node.js'].concat(flags), {
  5067        'in.js': `
  5068          class Foo {
  5069            #a
  5070            #b
  5071            #c
  5072            #d
  5073            #e
  5074            #f
  5075            foo() {
  5076              for ([
  5077                {x: this.#a},
  5078                [[, this.#b, ,]],
  5079                {y: this.#c = 3},
  5080                {x: this.x, y: this.y, ...this.#d},
  5081                [, , ...this.#e],
  5082                [{x: [{y: [this.#f]}]}],
  5083              ] of [[
  5084                {x: 1},
  5085                [[1, 2, 3]],
  5086                {},
  5087                {x: 2, y: 3, z: 4, w: 5},
  5088                [4, 5, 6, 7, 8],
  5089                [{x: [{y: [9]}]}],
  5090              ]]) ;
  5091              return JSON.stringify([
  5092                this.#a,
  5093                this.#b,
  5094                this.#c,
  5095                this.#d,
  5096                this.#e,
  5097                this.#f,
  5098              ])
  5099            }
  5100          }
  5101          if (new Foo().foo() !== '[1,2,3,{"z":4,"w":5},[6,7,8],9]') throw 'fail'
  5102        `,
  5103      }),
  5104      test(['in.js', '--outfile=node.js'].concat(flags), {
  5105        'in.js': `
  5106          class Foo {
  5107            #a
  5108            #b() {}
  5109            get #c() {}
  5110            set #d(x) {}
  5111            bar(x) {
  5112              return #a in x && #b in x && #c in x && #d in x
  5113            }
  5114          }
  5115          let foo = new Foo()
  5116          if (foo.bar(foo) !== true || foo.bar(Foo) !== false) throw 'fail'
  5117        `,
  5118      }),
  5119      test(['in.js', '--outfile=node.js'].concat(flags), {
  5120        'in.js': `
  5121          class Foo {
  5122            #a
  5123            #b() {}
  5124            get #c() {}
  5125            set #d(x) {}
  5126            bar(x) {
  5127              return #a in x && #b in x && #c in x && #d in x
  5128            }
  5129          }
  5130          function mustFail(x) {
  5131            let foo = new Foo()
  5132            try {
  5133              foo.bar(x)
  5134            } catch (e) {
  5135              if (e instanceof TypeError) return
  5136              throw e
  5137            }
  5138            throw 'fail'
  5139          }
  5140          mustFail(null)
  5141          mustFail(void 0)
  5142          mustFail(0)
  5143          mustFail('')
  5144          mustFail(Symbol('x'))
  5145        `,
  5146      }),
  5147  
  5148      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5149        'in.ts': `
  5150          let b = 0
  5151          class Foo {
  5152            a
  5153            [(() => ++b)()]
  5154            declare c
  5155            declare [(() => ++b)()]
  5156          }
  5157          const foo = new Foo
  5158          if (b !== 1 || 'a' in foo || 1 in foo || 'c' in foo || 2 in foo) throw 'fail'
  5159        `,
  5160        'tsconfig.json': `{
  5161  				"compilerOptions": {
  5162  					"useDefineForClassFields": false
  5163  				}
  5164  			}`,
  5165      }),
  5166      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5167        'in.ts': `
  5168          let b = 0
  5169          class Foo {
  5170            a
  5171            [(() => ++b)()]
  5172            declare c
  5173            declare [(() => ++b)()]
  5174          }
  5175          const foo = new Foo
  5176          if (b !== 1 || !('a' in foo) || !(1 in foo) || 'c' in foo || 2 in foo) throw 'fail'
  5177        `,
  5178        'tsconfig.json': `{
  5179          "compilerOptions": {
  5180            "useDefineForClassFields": true
  5181          }
  5182        }`
  5183      }),
  5184  
  5185      // Validate "branding" behavior
  5186      test(['in.js', '--outfile=node.js'].concat(flags), {
  5187        'in.js': `
  5188          class Base { constructor(x) { return x } }
  5189          class Derived extends Base { #y = true; static is(z) { return z.#y } }
  5190          const foo = {}
  5191          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5192          new Derived(foo)
  5193          if (Derived.is(foo) !== true) throw 'fail 2'
  5194          try { new Derived(foo); throw 'fail 3' } catch (e) { if (e === 'fail 3') throw e }
  5195        `,
  5196      }),
  5197      test(['in.js', '--outfile=node.js'].concat(flags), {
  5198        'in.js': `
  5199          class Base { constructor(x) { return x } }
  5200          class Derived extends Base { #y = true; static is(z) { return z.#y } }
  5201          const foo = 123
  5202          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5203          new Derived(foo)
  5204          try { Derived.is(foo); throw 'fail 2' } catch (e) { if (e === 'fail 2') throw e }
  5205          new Derived(foo)
  5206        `,
  5207      }),
  5208      test(['in.js', '--outfile=node.js'].concat(flags), {
  5209        'in.js': `
  5210          class Base { constructor(x) { return x } }
  5211          class Derived extends Base { #y = true; static is(z) { return z.#y } }
  5212          const foo = null
  5213          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5214          new Derived(foo)
  5215          try { Derived.is(foo); throw 'fail 2' } catch (e) { if (e === 'fail 2') throw e }
  5216          new Derived(foo)
  5217        `,
  5218      }),
  5219      test(['in.js', '--outfile=node.js'].concat(flags), {
  5220        'in.js': `
  5221          class Base { constructor(x) { return x } }
  5222          class Derived extends Base { #y() { return true } static is(z) { return z.#y } }
  5223          const foo = {}
  5224          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5225          new Derived(foo)
  5226          if (Derived.is(foo)() !== true) throw 'fail 2'
  5227          try { new Derived(foo); throw 'fail 3' } catch (e) { if (e === 'fail 3') throw e }
  5228        `,
  5229      }),
  5230      test(['in.js', '--outfile=node.js'].concat(flags), {
  5231        'in.js': `
  5232          class Base { constructor(x) { return x } }
  5233          class Derived extends Base { #y() {} static is(z) { return z.#y } }
  5234          const foo = 123
  5235          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5236          new Derived(foo)
  5237          try { Derived.is(foo); throw 'fail 2' } catch (e) { if (e === 'fail 2') throw e }
  5238          new Derived(foo)
  5239        `,
  5240      }),
  5241      test(['in.js', '--outfile=node.js'].concat(flags), {
  5242        'in.js': `
  5243          class Base { constructor(x) { return x } }
  5244          class Derived extends Base { #y() {} static is(z) { return z.#y } }
  5245          const foo = null
  5246          try { Derived.is(foo); throw 'fail 1' } catch (e) { if (e === 'fail 1') throw e }
  5247          new Derived(foo)
  5248          try { Derived.is(foo); throw 'fail 2' } catch (e) { if (e === 'fail 2') throw e }
  5249          new Derived(foo)
  5250        `,
  5251      }),
  5252      test(['in.js', '--outfile=node.js'].concat(flags), {
  5253        'in.js': `
  5254          let a, b, c, x = 123
  5255          class Foo {
  5256            #a() { a = { this: this, args: arguments } }
  5257            get #b() { return function () { b = { this: this, args: arguments } } }
  5258            #c = function () { c = { this: this, args: arguments } }
  5259            bar() { (this.#a)\`a\${x}aa\`; (this.#b)\`b\${x}bb\`; (this.#c)\`c\${x}cc\` }
  5260          }
  5261          new Foo().bar()
  5262          if (!(a.this instanceof Foo) || !(b.this instanceof Foo) || !(c.this instanceof Foo)) throw 'fail'
  5263          if (JSON.stringify([...a.args, ...b.args, ...c.args]) !== JSON.stringify([['a', 'aa'], 123, ['b', 'bb'], 123, ['c', 'cc'], 123])) throw 'fail'
  5264        `,
  5265      }),
  5266      test(['in.js', '--outfile=node.js'].concat(flags), {
  5267        'in.js': `
  5268          let a, b, c, x = 123
  5269          class Foo {
  5270            #a() { a = { this: this, args: arguments } }
  5271            get #b() { return function () { b = { this: this, args: arguments } } }
  5272            #c = function () { c = { this: this, args: arguments } }
  5273            bar() { (0, this.#a)\`a\${x}aa\`; (0, this.#b)\`b\${x}bb\`; (0, this.#c)\`c\${x}cc\` }
  5274          }
  5275          new Foo().bar()
  5276          if (a.this instanceof Foo || b.this instanceof Foo || c.this instanceof Foo) throw 'fail'
  5277          if (JSON.stringify([...a.args, ...b.args, ...c.args]) !== JSON.stringify([['a', 'aa'], 123, ['b', 'bb'], 123, ['c', 'cc'], 123])) throw 'fail'
  5278        `,
  5279      }),
  5280      test(['in.js', '--outfile=node.js'].concat(flags), {
  5281        'in.js': `
  5282          let it
  5283          class Foo {
  5284            constructor() { it = this; it = it.#fn\`\` }
  5285            get #fn() { it = null; return function() { return this } }
  5286          }
  5287          new Foo
  5288          if (!(it instanceof Foo)) throw 'fail'
  5289        `,
  5290      }),
  5291      test(['in.js', '--outfile=node.js'].concat(flags), {
  5292        'in.js': `
  5293          let it
  5294          class Foo {
  5295            constructor() { it = this; it = it.#fn() }
  5296            get #fn() { it = null; return function() { return this } }
  5297          }
  5298          new Foo
  5299          if (!(it instanceof Foo)) throw 'fail'
  5300        `,
  5301      }),
  5302      test(['in.js', '--outfile=node.js'].concat(flags), {
  5303        'in.js': `
  5304          const order = []
  5305          class Test {
  5306            static first = order.push(1)
  5307            static { order.push(2) }
  5308            static third = order.push(3)
  5309          }
  5310          if ('' + order !== '1,2,3') throw 'fail: ' + order
  5311        `,
  5312      }),
  5313  
  5314      // Check side effect order of computed properties with assign semantics
  5315      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5316        'in.ts': `
  5317          const order = []
  5318          const check = x => {
  5319            order.push(x)
  5320            return x
  5321          }
  5322          class Foo {
  5323            [check('a')]() {}
  5324            [check('b')];
  5325            [check('c')] = 1;
  5326            [check('d')]() {}
  5327            static [check('e')];
  5328            static [check('f')] = 2;
  5329            static [check('g')]() {}
  5330            [check('h')];
  5331          }
  5332          class Bar {
  5333            // Use a class with a single static field to check that the computed
  5334            // key isn't deferred outside of the class body while the initializer
  5335            // is left inside.
  5336            static [check('i')] = 3
  5337          }
  5338          if (order + '' !== 'a,b,c,d,e,f,g,h,i') throw 'fail: ' + order
  5339          const foo = new Foo
  5340          if (typeof foo.a !== 'function') throw 'fail: a'
  5341          if ('b' in foo) throw 'fail: b'
  5342          if (foo.c !== 1) throw 'fail: c'
  5343          if (typeof foo.d !== 'function') throw 'fail: d'
  5344          if ('e' in Foo) throw 'fail: e'
  5345          if (Foo.f !== 2) throw 'fail: f'
  5346          if (typeof Foo.g !== 'function') throw 'fail: g'
  5347          if ('h' in foo) throw 'fail: h'
  5348          if (Bar.i !== 3) throw 'fail: i'
  5349        `,
  5350        'tsconfig.json': `{
  5351          "compilerOptions": {
  5352            "useDefineForClassFields": false,
  5353          },
  5354        }`,
  5355      }),
  5356  
  5357      // Check for the specific reference behavior of TypeScript's implementation
  5358      // of "experimentalDecorators" with class decorators, which mutate the class
  5359      // binding itself. This test passes on TypeScript's implementation.
  5360      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5361        'in.ts': `
  5362          let oldFoo: any
  5363          let e: any
  5364          let decorate = (foo: any): any => {
  5365            oldFoo = foo
  5366            return { foo }
  5367          }
  5368          @decorate
  5369          class newFoo {
  5370            a(): any { return [newFoo, () => newFoo] }
  5371            b: any = [newFoo, () => newFoo]
  5372            static c(): any { return [newFoo, () => newFoo] }
  5373            static d: any = [newFoo, () => newFoo]
  5374            static { e = [newFoo, () => newFoo] }
  5375          }
  5376          const fail: string[] = []
  5377          if ((newFoo as any).foo !== oldFoo) fail.push('decorate')
  5378          if (new oldFoo().a()[0] !== newFoo) fail.push('a[0]')
  5379          if (new oldFoo().a()[1]() !== newFoo) fail.push('a[1]')
  5380          if (new oldFoo().b[0] !== newFoo) fail.push('b[0]')
  5381          if (new oldFoo().b[1]() !== newFoo) fail.push('b[1]')
  5382          if (oldFoo.c()[0] !== newFoo) fail.push('c[0]')
  5383          if (oldFoo.c()[1]() !== newFoo) fail.push('c[1]')
  5384          if (oldFoo.d[0] !== oldFoo) fail.push('d[0]')
  5385          if (oldFoo.d[1]() !== newFoo) fail.push('d[1]')
  5386          if (e[0] !== oldFoo) fail.push('e[0]')
  5387          if (e[1]() !== newFoo) fail.push('e[1]')
  5388          if (fail.length) throw 'fail: ' + fail
  5389        `,
  5390        'tsconfig.json': `{
  5391          "compilerOptions": {
  5392            "experimentalDecorators": true,
  5393          },
  5394        }`,
  5395      }),
  5396  
  5397      // https://github.com/evanw/esbuild/issues/2800
  5398      test(['in.js', '--outfile=node.js'].concat(flags), {
  5399        'in.js': `
  5400          class Baz {
  5401            static thing = "value"
  5402            static {
  5403              this.prototype.thing = "value"
  5404            }
  5405          }
  5406          if (new Baz().thing !== 'value') throw 'fail'
  5407        `,
  5408      }),
  5409  
  5410      // https://github.com/evanw/esbuild/issues/2950
  5411      test(['in.js', '--outfile=node.js'].concat(flags), {
  5412        'in.js': `
  5413          class SomeClass {
  5414            static { this.One = 1; }
  5415            static { this.Two = SomeClass.One * 2; }
  5416          }
  5417          if (SomeClass.Two !== 2) throw 'fail'
  5418        `,
  5419      }),
  5420  
  5421      // https://github.com/evanw/esbuild/issues/3025
  5422      test(['in.js', '--outfile=node.js'].concat(flags), {
  5423        'in.js': `
  5424          class Foo {
  5425            static {
  5426              Foo.prototype.foo = 'foo'
  5427            }
  5428          }
  5429          if (new Foo().foo !== 'foo') throw 'fail'
  5430        `,
  5431      }),
  5432  
  5433      // https://github.com/evanw/esbuild/issues/2389
  5434      test(['in.js', '--outfile=node.js', '--minify', '--keep-names'].concat(flags), {
  5435        'in.js': `
  5436          class DirectlyReferenced { static type = DirectlyReferenced.name }
  5437          class ReferencedViaThis { static type = this.name }
  5438          class StaticBlockViaThis { static { if (this.name !== 'StaticBlockViaThis') throw 'fail StaticBlockViaThis: ' + this.name } }
  5439          class StaticBlockDirectly { static { if (StaticBlockDirectly.name !== 'StaticBlockDirectly') throw 'fail StaticBlockDirectly: ' + StaticBlockDirectly.name } }
  5440          if (DirectlyReferenced.type !== 'DirectlyReferenced') throw 'fail DirectlyReferenced: ' + DirectlyReferenced.type
  5441          if (ReferencedViaThis.type !== 'ReferencedViaThis') throw 'fail ReferencedViaThis: ' + ReferencedViaThis.type
  5442        `,
  5443      }),
  5444      test(['in.js', '--outfile=node.js', '--minify', '--keep-names'].concat(flags), {
  5445        'in.js': `
  5446          let ReferencedViaThis = class { static type = this.name }
  5447          let StaticBlockViaThis = class { static { if (this.name !== 'StaticBlockViaThis') throw 'fail StaticBlockViaThis: ' + this.name } }
  5448          if (ReferencedViaThis.type !== 'ReferencedViaThis') throw 'fail ReferencedViaThis: ' + ReferencedViaThis.type
  5449        `,
  5450      }),
  5451      test(['in.js', '--outfile=node.js', '--keep-names', '--format=esm'].concat(flags), {
  5452        'in.js': `
  5453          // Cause the names in the inner scope to be renamed
  5454          if (
  5455            typeof DirectlyReferenced !== 'undefined' ||
  5456            typeof ReferencedViaThis !== 'undefined' ||
  5457            typeof StaticBlockViaThis !== 'undefined' ||
  5458            typeof StaticBlockDirectly !== 'undefined'
  5459          ) {
  5460            throw 'fail'
  5461          }
  5462          function innerScope() {
  5463            class DirectlyReferenced { static type = DirectlyReferenced.name }
  5464            class ReferencedViaThis { static type = this.name }
  5465            class StaticBlockViaThis { static { if (this.name !== 'StaticBlockViaThis') throw 'fail StaticBlockViaThis: ' + this.name } }
  5466            class StaticBlockDirectly { static { if (StaticBlockDirectly.name !== 'StaticBlockDirectly') throw 'fail StaticBlockDirectly: ' + StaticBlockDirectly.name } }
  5467            if (DirectlyReferenced.type !== 'DirectlyReferenced') throw 'fail DirectlyReferenced: ' + DirectlyReferenced.type
  5468            if (ReferencedViaThis.type !== 'ReferencedViaThis') throw 'fail ReferencedViaThis: ' + ReferencedViaThis.type
  5469          }
  5470          innerScope()
  5471        `,
  5472      }),
  5473  
  5474      // https://github.com/evanw/esbuild/issues/2629
  5475      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5476        'in.ts': `
  5477          // Stub out the decorator so TSC doesn't complain.
  5478          const someDecorator = (): PropertyDecorator => () => {};
  5479  
  5480          class Foo {
  5481            static message = 'Hello world!';
  5482            static msgLength = Foo.message.length;
  5483  
  5484            @someDecorator()
  5485            foo() {}
  5486          }
  5487  
  5488          if (Foo.message !== 'Hello world!' || Foo.msgLength !== 12) throw 'fail'
  5489        `,
  5490        'tsconfig.json': `{
  5491          "compilerOptions": {
  5492            "experimentalDecorators": true,
  5493          },
  5494        }`,
  5495      }),
  5496  
  5497      // https://github.com/evanw/esbuild/issues/2045
  5498      test(['in.js', '--bundle', '--outfile=node.js', '--log-override:class-name-will-throw=silent'].concat(flags), {
  5499        'in.js': `
  5500          let A = {a: 'a'} // This should not be used
  5501  
  5502          let field
  5503          try { class A { static a = 1; [A.a] = 2 } } catch (err) { field = err }
  5504          if (!field) throw 'fail: field'
  5505  
  5506          let staticField
  5507          try { class A { static a = 1; static [A.a] = 2 } } catch (err) { staticField = err }
  5508          if (!staticField) throw 'fail: staticField'
  5509  
  5510          let method
  5511          try { class A { static a = 1; [A.a]() { return 2 } } } catch (err) { method = err }
  5512          if (!method) throw 'fail: method'
  5513  
  5514          let staticMethod
  5515          try { class A { static a = 1; static [A.a]() { return 2 } } } catch (err) { staticMethod = err }
  5516          if (!staticMethod) throw 'fail: staticMethod'
  5517        `,
  5518      }),
  5519      test(['in.js', '--bundle', '--outfile=node.js', '--log-override:class-name-will-throw=silent'].concat(flags), {
  5520        'in.js': `
  5521          let A = {a: 'a'} // This should not be used
  5522  
  5523          let field
  5524          try { class A { capture = () => A; static a = 1; [A.a] = 2 } } catch (err) { field = err }
  5525          if (!field) throw 'fail: field'
  5526  
  5527          let staticField
  5528          try { class A { capture = () => A; static a = 1; static [A.a] = 2 } } catch (err) { staticField = err }
  5529          if (!staticField) throw 'fail: staticField'
  5530  
  5531          let method
  5532          try { class A { capture = () => A; static a = 1; [A.a]() { return 2 } } } catch (err) { method = err }
  5533          if (!method) throw 'fail: method'
  5534  
  5535          let staticMethod
  5536          try { class A { capture = () => A; static a = 1; static [A.a]() { return 2 } } } catch (err) { staticMethod = err }
  5537          if (!staticMethod) throw 'fail: staticMethod'
  5538        `,
  5539      }),
  5540      test(['in.js', '--bundle', '--outfile=node.js', '--log-override:class-name-will-throw=silent'].concat(flags), {
  5541        'in.js': `
  5542          let A = {a: 'a'} // This should not be used
  5543          let temp
  5544  
  5545          let field
  5546          try { temp = (class A { static a = 1; [A.a] = 2 }) } catch (err) { field = err }
  5547          if (!field) throw 'fail: field'
  5548  
  5549          let staticField
  5550          try { temp = (class A { static a = 1; static [A.a] = 2 }) } catch (err) { staticField = err }
  5551          if (!staticField) throw 'fail: staticField'
  5552  
  5553          let method
  5554          try { temp = (class A { static a = 1; [A.a]() { return 2 } }) } catch (err) { method = err }
  5555          if (!method) throw 'fail: method'
  5556  
  5557          let staticMethod
  5558          try { temp = (class A { static a = 1; static [A.a]() { return 2 } }) } catch (err) { staticMethod = err }
  5559          if (!staticMethod) throw 'fail: staticMethod'
  5560        `,
  5561      }),
  5562      test(['in.js', '--bundle', '--outfile=node.js', '--log-override:class-name-will-throw=silent'].concat(flags), {
  5563        'in.js': `
  5564          let A = {a: 'a'} // This should not be used
  5565          let temp
  5566  
  5567          let field
  5568          try { temp = (class A { capture = () => A; static a = 1; [A.a] = 2 }) } catch (err) { field = err }
  5569          if (!field) throw 'fail: field'
  5570  
  5571          let staticField
  5572          try { temp = (class A { capture = () => A; static a = 1; static [A.a] = 2 }) } catch (err) { staticField = err }
  5573          if (!staticField) throw 'fail: staticField'
  5574  
  5575          let method
  5576          try { temp = (class A { capture = () => A; static a = 1; [A.a]() { return 2 } }) } catch (err) { method = err }
  5577          if (!method) throw 'fail: method'
  5578  
  5579          let staticMethod
  5580          try { temp = (class A { capture = () => A; static a = 1; static [A.a]() { return 2 } }) } catch (err) { staticMethod = err }
  5581          if (!staticMethod) throw 'fail: staticMethod'
  5582        `,
  5583      }),
  5584  
  5585      // https://github.com/evanw/esbuild/issues/3326
  5586      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5587        'in.ts': `
  5588          const log: string[] = []
  5589          class Test1 {
  5590            static deco(target: any, key: any, desc: any): any { log.push('Test1') }
  5591            @Test1.deco static test(): void { }
  5592          }
  5593          class Test2 {
  5594            static deco(target: any, key: any, desc: any): any { log.push('Test2') }
  5595            @Test2.deco static test(): Test2 { return new Test2(); }
  5596          }
  5597          @Test3.deco
  5598          class Test3 {
  5599            static deco(target: any): any { log.push('Test3') }
  5600          }
  5601          if (log + '' !== 'Test1,Test2,Test3') throw 'fail: ' + log
  5602        `,
  5603        'tsconfig.json': `{
  5604          "compilerOptions": {
  5605            "experimentalDecorators": true,
  5606          },
  5607        }`,
  5608      }),
  5609  
  5610      // https://github.com/evanw/esbuild/issues/3394
  5611      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5612        'in.ts': `
  5613          const dec = (arg: number): ParameterDecorator => () => { answer = arg }
  5614          let answer = 0
  5615  
  5616          class Foo {
  5617            static #foo = 123
  5618            static bar = 234
  5619            method(@dec(Foo.#foo + Foo.bar) arg: any) {
  5620            }
  5621          }
  5622  
  5623          if (answer !== 357) throw 'fail: ' + answer
  5624        `,
  5625        'tsconfig.json': `{
  5626          "compilerOptions": {
  5627            "experimentalDecorators": true,
  5628          },
  5629        }`,
  5630      }),
  5631  
  5632      // https://github.com/evanw/esbuild/issues/3538
  5633      test(['in.js', '--outfile=node.js'].concat(flags), {
  5634        'in.js': `
  5635          class Foo extends Array {
  5636            pass = false
  5637            constructor() {
  5638              let base = super()
  5639              this.pass = base === this &&
  5640                base instanceof Array &&
  5641                base instanceof Foo
  5642            }
  5643          }
  5644          if (!new Foo().pass) throw 'fail'
  5645        `,
  5646      }),
  5647      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5648        'in.ts': `
  5649          class Foo extends Array {
  5650            pass: boolean = false
  5651            constructor() {
  5652              let base = super()
  5653              this.pass = base === this &&
  5654                base instanceof Array &&
  5655                base instanceof Foo
  5656            }
  5657          }
  5658          if (!new Foo().pass) throw 'fail'
  5659        `,
  5660        'tsconfig.json': `{
  5661          "compilerOptions": {
  5662            "useDefineForClassFields": false,
  5663          },
  5664        }`,
  5665      }),
  5666      test(['in.js', '--outfile=node.js'].concat(flags), {
  5667        'in.js': `
  5668          class Bar {
  5669            constructor(x) {
  5670              return x
  5671            }
  5672          }
  5673          class Foo extends Bar {
  5674            pass = false
  5675            constructor() {
  5676              let base = super([])
  5677              this.pass = base === this &&
  5678                base instanceof Array &&
  5679                !(base instanceof Foo)
  5680            }
  5681          }
  5682          if (!new Foo().pass) throw 'fail'
  5683        `,
  5684      }),
  5685      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5686        'in.ts': `
  5687          class Bar {
  5688            constructor(x) {
  5689              return x
  5690            }
  5691          }
  5692          class Foo extends Bar {
  5693            pass: boolean = false
  5694            constructor() {
  5695              let base = super([])
  5696              this.pass = base === this &&
  5697                base instanceof Array &&
  5698                !(base instanceof Foo)
  5699            }
  5700          }
  5701          if (!new Foo().pass) throw 'fail'
  5702        `,
  5703        'tsconfig.json': `{
  5704          "compilerOptions": {
  5705            "useDefineForClassFields": false,
  5706          },
  5707        }`,
  5708      }),
  5709  
  5710      // https://github.com/evanw/esbuild/issues/3559
  5711      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5712        'in.ts': `
  5713          class Foo extends Array {
  5714            #private: any
  5715            pass: any
  5716            constructor() {
  5717              super()
  5718              this.pass = true
  5719            }
  5720          }
  5721          if (!new Foo().pass) throw 'fail'
  5722        `,
  5723        'tsconfig.json': `{
  5724          "compilerOptions": {
  5725            "useDefineForClassFields": false,
  5726          },
  5727        }`,
  5728      }),
  5729      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5730        'in.ts': `
  5731          class Foo extends Array {
  5732            #private: any
  5733            pass = true
  5734            constructor() {
  5735              super()
  5736            }
  5737          }
  5738          if (!new Foo().pass) throw 'fail'
  5739        `,
  5740        'tsconfig.json': `{
  5741          "compilerOptions": {
  5742            "useDefineForClassFields": false,
  5743          },
  5744        }`,
  5745      }),
  5746      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5747        'in.ts': `
  5748          class Foo extends Array {
  5749            #private = 123
  5750            pass: any
  5751            constructor() {
  5752              super()
  5753              this.pass = true
  5754            }
  5755          }
  5756          if (!new Foo().pass) throw 'fail'
  5757        `,
  5758        'tsconfig.json': `{
  5759          "compilerOptions": {
  5760            "useDefineForClassFields": false,
  5761          },
  5762        }`,
  5763      }),
  5764      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5765        'in.ts': `
  5766          class Foo extends Array {
  5767            #private = 123
  5768            pass: any = true
  5769            constructor() {
  5770              super()
  5771            }
  5772          }
  5773          if (!new Foo().pass) throw 'fail'
  5774        `,
  5775        'tsconfig.json': `{
  5776          "compilerOptions": {
  5777            "useDefineForClassFields": false,
  5778          },
  5779        }`,
  5780      }),
  5781  
  5782      // Check various combinations of flags
  5783      test(['in.ts', '--outfile=node.js', '--supported:class-field=false'].concat(flags), {
  5784        'in.ts': `
  5785          class Foo {
  5786            accessor foo = 1
  5787            static accessor bar = 2
  5788          }
  5789          if (new Foo().foo !== 1 || Foo.bar !== 2) throw 'fail'
  5790        `,
  5791      }),
  5792      test(['in.ts', '--outfile=node.js', '--supported:class-static-field=false'].concat(flags), {
  5793        'in.ts': `
  5794          class Foo {
  5795            accessor foo = 1
  5796            static accessor bar = 2
  5797          }
  5798          if (new Foo().foo !== 1 || Foo.bar !== 2) throw 'fail'
  5799        `,
  5800      }),
  5801  
  5802      // Make sure class body side effects aren't reordered
  5803      test(['in.ts', '--outfile=node.js', '--supported:class-field=false'].concat(flags), {
  5804        'in.ts': `
  5805          const log = []
  5806          class Foo extends (log.push(1), Object) {
  5807            [log.push(2)] = 123;
  5808            [log.push(3)] = 123;
  5809          }
  5810          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5811        `,
  5812      }),
  5813      test(['in.ts', '--outfile=node.js', '--supported:class-static-field=false'].concat(flags), {
  5814        'in.ts': `
  5815          const log = []
  5816          class Foo extends (log.push(1), Object) {
  5817            static [log.push(2)] = 123;
  5818            static [log.push(3)] = 123;
  5819          }
  5820          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5821        `,
  5822      }),
  5823      test(['in.ts', '--outfile=node.js', '--supported:class-field=false'].concat(flags), {
  5824        'in.ts': `
  5825          const log = []
  5826          class Foo {
  5827            static [log.push(1)]() {}
  5828            [log.push(2)] = 123;
  5829            static [log.push(3)]() {}
  5830          }
  5831          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5832        `,
  5833      }),
  5834      test(['in.ts', '--outfile=node.js', '--supported:class-static-field=false'].concat(flags), {
  5835        'in.ts': `
  5836          const log = []
  5837          class Foo {
  5838            [log.push(1)]() {}
  5839            static [log.push(2)] = 123;
  5840            [log.push(3)]() {}
  5841          }
  5842          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5843        `,
  5844      }),
  5845      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5846        'in.ts': `
  5847          const log = []
  5848          class Foo {
  5849            @(() => { log.push(3) }) [log.push(1)]() {}
  5850            [log.push(2)] = 123;
  5851          }
  5852          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5853        `,
  5854        'tsconfig.json': `{
  5855          "compilerOptions": {
  5856            "experimentalDecorators": true
  5857          }
  5858        }`,
  5859      }),
  5860      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5861        'in.ts': `
  5862          const log = []
  5863          class Foo {
  5864            @(() => { log.push(3) }) static [log.push(1)]() {}
  5865            static [log.push(2)] = 123;
  5866          }
  5867          if (log + '' !== '1,2,3') throw 'fail: ' + log
  5868        `,
  5869        'tsconfig.json': `{
  5870          "compilerOptions": {
  5871            "experimentalDecorators": true
  5872          }
  5873        }`,
  5874      }),
  5875  
  5876      // Check "await" in computed property names
  5877      test(['in.ts', '--outfile=node.js', '--format=cjs', '--supported:class-field=false'].concat(flags), {
  5878        'in.ts': `
  5879          exports.async = async () => {
  5880            class Foo {
  5881              [await Promise.resolve('foo')] = 123
  5882            }
  5883            if (new Foo().foo !== 123) throw 'fail'
  5884          }
  5885        `,
  5886      }, { async: true }),
  5887      test(['in.ts', '--outfile=node.js', '--format=cjs', '--supported:class-static-field=false'].concat(flags), {
  5888        'in.ts': `
  5889          exports.async = async () => {
  5890            class Foo {
  5891              static [await Promise.resolve('foo')] = 123
  5892            }
  5893            if (Foo.foo !== 123) throw 'fail'
  5894          }
  5895        `,
  5896      }, { async: true }),
  5897    )
  5898  
  5899    // https://github.com/evanw/esbuild/issues/3177
  5900    const input3177 = `
  5901      const props: Record<number, string> = {}
  5902      const dec = (n: number) => (_: any, prop: string): void => {
  5903        props[n] = prop
  5904      }
  5905      class Foo {
  5906        @dec(1) prop1: any
  5907        @dec(2) prop2_: any
  5908        @dec(3) ['prop3']: any
  5909        @dec(4) ['prop4_']: any
  5910        @dec(5) [/* @__KEY__ */ 'prop5']: any
  5911        @dec(6) [/* @__KEY__ */ 'prop6_']: any
  5912      }
  5913      if (props[1] !== 'prop1') throw 'fail 1: ' + props[1]
  5914      if (props[2] !== /* @__KEY__ */ 'prop2_') throw 'fail 2: ' + props[2]
  5915      if (props[3] !== 'prop3') throw 'fail 3: ' + props[3]
  5916      if (props[4] !== 'prop4_') throw 'fail 4: ' + props[4]
  5917      if (props[5] !== 'prop5') throw 'fail 5: ' + props[5]
  5918      if (props[6] !== /* @__KEY__ */ 'prop6_') throw 'fail 6: ' + props[6]
  5919    `
  5920    tests.push(
  5921      test(['in.ts', '--outfile=node.js', '--mangle-props=_'].concat(flags), {
  5922        'in.ts': input3177,
  5923        'tsconfig.json': `{
  5924          "compilerOptions": {
  5925            "experimentalDecorators": true,
  5926            "useDefineForClassFields": true,
  5927          },
  5928        }`,
  5929      }),
  5930      test(['in.ts', '--outfile=node.js', '--mangle-props=_'].concat(flags), {
  5931        'in.ts': input3177,
  5932        'tsconfig.json': `{
  5933          "compilerOptions": {
  5934            "experimentalDecorators": true,
  5935            "useDefineForClassFields": false,
  5936          },
  5937        }`,
  5938      }),
  5939    )
  5940  
  5941    // Test TypeScript experimental decorators and accessors
  5942    const experimentalDecoratorsAndAccessors = `
  5943      const log: string[] = []
  5944      const decorate = (target: any, key: string, descriptor: PropertyDescriptor): any => {
  5945        if (descriptor.get === void 0) throw 'fail: get ' + key
  5946        if (descriptor.set === void 0) throw 'fail: set ' + key
  5947        return {
  5948          get() {
  5949            const value = descriptor.get!.call(this)
  5950            log.push('get ' + key + ' ' + value)
  5951            return value
  5952          },
  5953          set(value: any) {
  5954            descriptor.set!.call(this, value)
  5955            log.push('set ' + key + ' ' + value)
  5956          },
  5957        }
  5958      }
  5959  
  5960      // With esbuild's accessor syntax
  5961      class Foo {
  5962        @decorate accessor x = 1
  5963        @decorate static accessor y = 2
  5964      }
  5965      const foo = new Foo
  5966      if (++foo.x !== 2) throw 'fail: foo.x'
  5967      if (++Foo.y !== 3) throw 'fail: foo.y'
  5968      if (log + '' !== 'get x 1,set x 2,get y 2,set y 3') throw 'fail: foo ' + log
  5969  
  5970      log.length = 0
  5971  
  5972      // Without esbuild's accessor syntax (should be the same)
  5973      class Bar {
  5974        #x = 1
  5975        @decorate get x() { return this.#x }
  5976        set x(_) { this.#x = _ }
  5977        static #y = 2
  5978        @decorate static get y() { return this.#y }
  5979        static set y(_) { this.#y = _ }
  5980      }
  5981      const bar = new Bar
  5982      if (++bar.x !== 2) throw 'fail: bar.x'
  5983      if (++Bar.y !== 3) throw 'fail: Bar.y'
  5984      if (log + '' !== 'get x 1,set x 2,get y 2,set y 3') throw 'fail: bar ' + log
  5985    `
  5986    tests.push(
  5987      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5988        'in.ts': experimentalDecoratorsAndAccessors,
  5989        'tsconfig.json': `{
  5990          "compilerOptions": {
  5991            "experimentalDecorators": true,
  5992            "useDefineForClassFields": true,
  5993          },
  5994        }`,
  5995      }),
  5996      test(['in.ts', '--outfile=node.js'].concat(flags), {
  5997        'in.ts': experimentalDecoratorsAndAccessors,
  5998        'tsconfig.json': `{
  5999          "compilerOptions": {
  6000            "experimentalDecorators": true,
  6001            "useDefineForClassFields": false,
  6002          },
  6003        }`,
  6004      }),
  6005    )
  6006  
  6007    // Test class accessors
  6008    const classAccessorTest = `
  6009      const checkAccessor = (obj, key, value) => {
  6010        if (obj[key] !== value) throw 'fail: ' + key + ' get'
  6011        obj[key] = null
  6012        if (obj[key] !== null) throw 'fail: ' + key + ' set'
  6013      }
  6014  
  6015      checkAccessor(new class { accessor undef }, 'undef')
  6016      checkAccessor(new class { accessor undef2; x = 0 }, 'undef2')
  6017      checkAccessor(new class { accessor def = 123 }, 'def', 123)
  6018      checkAccessor(new class { accessor def2 = 123; x = 0 }, 'def2', 123)
  6019  
  6020      checkAccessor(class { static accessor staticUndef }, 'staticUndef')
  6021      checkAccessor(class { static accessor staticUndef2; x = 0 }, 'staticUndef2')
  6022      checkAccessor(class { static accessor staticDef = 123 }, 'staticDef', 123)
  6023      checkAccessor(class { static accessor staticDef2 = 123; x = 0 }, 'staticDef2', 123)
  6024  
  6025      checkAccessor(new class { accessor #x; get privateUndef() { return this.#x } set privateUndef(_) { this.#x = _ } }, 'privateUndef')
  6026      checkAccessor(new class { accessor #x; get privateUndef2() { return this.#x } set privateUndef2(_) { this.#x = _ } x = 0 }, 'privateUndef2')
  6027      checkAccessor(new class { accessor #x = 123; get privateDef() { return this.#x } set privateDef(_) { this.#x = _ } }, 'privateDef', 123)
  6028      checkAccessor(new class { accessor #x = 123; get privateDef2() { return this.#x } set privateDef2(_) { this.#x = _ } x = 0 }, 'privateDef2', 123)
  6029  
  6030      checkAccessor(class { static accessor #x; static get staticPrivateUndef() { return this.#x } static set staticPrivateUndef(_) { this.#x = _ } }, 'staticPrivateUndef')
  6031      checkAccessor(class { static accessor #x; static get staticPrivateUndef2() { return this.#x } static set staticPrivateUndef2(_) { this.#x = _ } x = 0 }, 'staticPrivateUndef2')
  6032      checkAccessor(class { static accessor #x = 123; static get staticPrivateDef() { return this.#x } static set staticPrivateDef(_) { this.#x = _ } }, 'staticPrivateDef', 123)
  6033      checkAccessor(class { static accessor #x = 123; static get staticPrivateDef2() { return this.#x } static set staticPrivateDef2(_) { this.#x = _ } x = 0 }, 'staticPrivateDef2', 123)
  6034  
  6035      const order = []
  6036      const checkOrder = x => {
  6037        order.push(x)
  6038        return x
  6039      }
  6040      class Foo {
  6041        a = checkOrder(8)
  6042        #a = checkOrder(9)
  6043        accessor b = checkOrder(10)
  6044        accessor #b = checkOrder(11)
  6045        accessor [checkOrder(1)] = checkOrder(12)
  6046        static c = checkOrder(3)
  6047        static #c = checkOrder(4)
  6048        static accessor d = checkOrder(5)
  6049        static accessor #d = checkOrder(6)
  6050        static accessor [checkOrder(2)] = checkOrder(7)
  6051        'get#a'() { return this.#a }
  6052        'get#b'() { return this.#b }
  6053        static 'get#c'() { return this.#c }
  6054        static 'get#d'() { return this.#d }
  6055      }
  6056      const foo = new Foo
  6057      if (order + '' !== '1,2,3,4,5,6,7,8,9,10,11,12') throw 'fail: ' + order
  6058      if (foo.a !== 8) throw 'fail: a'
  6059      if (foo['get#a']() !== 9) throw 'fail: #a'
  6060      if (foo.b !== 10) throw 'fail: b'
  6061      if (foo['get#b']() !== 11) throw 'fail: #b'
  6062      if (foo[1] !== 12) throw 'fail: 1'
  6063      if (Foo.c !== 3) throw 'fail: c'
  6064      if (Foo['get#c']() !== 4) throw 'fail: #c'
  6065      if (Foo.d !== 5) throw 'fail: d'
  6066      if (Foo['get#d']() !== 6) throw 'fail: #d'
  6067      if (Foo[2] !== 7) throw 'fail: 2'
  6068    `
  6069    tests.push(
  6070      test(['in.js', '--outfile=node.js'].concat(flags), {
  6071        'in.js': classAccessorTest,
  6072      }),
  6073      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6074        'in.ts': classAccessorTest,
  6075        'tsconfig.json': `{
  6076          "compilerOptions": {
  6077            "useDefineForClassFields": true,
  6078          }
  6079        }`,
  6080      }),
  6081      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6082        'in.ts': classAccessorTest,
  6083        'tsconfig.json': `{
  6084          "compilerOptions": {
  6085            "useDefineForClassFields": false,
  6086          }
  6087        }`,
  6088      }),
  6089    )
  6090  
  6091    // https://github.com/evanw/esbuild/issues/3768
  6092    tests.push(
  6093      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6094        'in.ts': `
  6095          const bar = x => x
  6096          class Foo {
  6097            @bar baz() { return Foo }
  6098          }
  6099          if (new Foo().baz() !== Foo) throw 'fail'
  6100        `,
  6101      }),
  6102      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6103        'in.ts': `
  6104          class Foo {}
  6105          const bar = x => x
  6106          class Baz extends Foo {
  6107            @bar baz() { return Baz }
  6108          }
  6109          if (new Baz().baz() !== Baz) throw 'fail'
  6110        `,
  6111      }),
  6112      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6113        'in.ts': `
  6114          const bar = () => x => x
  6115          class Foo {
  6116            @bar baz = Foo
  6117          }
  6118          if (new Foo().baz !== Foo) throw 'fail'
  6119        `,
  6120      }),
  6121      test(['in.ts', '--outfile=node.js'].concat(flags), {
  6122        'in.ts': `
  6123          class Foo {}
  6124          const bar = () => x => x
  6125          class Baz extends Foo {
  6126            @bar baz = Baz
  6127          }
  6128          if (new Baz().baz !== Baz) throw 'fail'
  6129        `,
  6130      }),
  6131    )
  6132  }
  6133  
  6134  // Async lowering tests
  6135  for (let flags of [[], ['--target=es2017'], ['--target=es6']]) {
  6136    tests.push(
  6137      test(['in.js', '--outfile=node.js'].concat(flags), {
  6138        'in.js': `
  6139          exports.async = async () => {
  6140            const value = await Promise.resolve(123)
  6141            if (value !== 123) throw 'fail'
  6142  
  6143            let uncaught = false
  6144            let caught = false
  6145            try {
  6146              await Promise.reject(234)
  6147              uncaught = true
  6148            } catch (error) {
  6149              if (error !== 234) throw 'fail'
  6150              caught = true
  6151            }
  6152            if (uncaught || !caught) throw 'fail'
  6153          }
  6154        `,
  6155      }, { async: true }),
  6156      test(['in.js', '--outfile=node.js'].concat(flags), {
  6157        'in.js': `
  6158          async function throws() {
  6159            throw 123
  6160          }
  6161          exports.async = () => throws().then(
  6162            () => {
  6163              throw 'fail'
  6164            },
  6165            error => {
  6166              if (error !== 123) throw 'fail'
  6167            }
  6168          )
  6169        `,
  6170      }, { async: true }),
  6171      test(['in.js', '--outfile=node.js'].concat(flags), {
  6172        'in.js': `
  6173          exports.async = async () => {
  6174            "use strict"
  6175            async function foo() {
  6176              return [this, arguments]
  6177            }
  6178            let [t, a] = await foo.call(0, 1, 2, 3)
  6179            if (t !== 0 || a.length !== 3 || a[0] !== 1 || a[1] !== 2 || a[2] !== 3) throw 'fail'
  6180          }
  6181        `,
  6182      }, { async: true }),
  6183      test(['in.js', '--outfile=node.js'].concat(flags), {
  6184        'in.js': `
  6185          let couldThrow = () => 'b'
  6186          exports.async = async () => {
  6187            "use strict"
  6188            async function f0() {
  6189              let bar = async (x, y) => [x, y, this, arguments]
  6190              return await bar('a', 'b')
  6191            }
  6192            async function f1() {
  6193              let bar = async (x, ...y) => [x, y[0], this, arguments]
  6194              return await bar('a', 'b')
  6195            }
  6196            async function f2() {
  6197              let bar = async (x, y = 'b') => [x, y, this, arguments]
  6198              return await bar('a')
  6199            }
  6200            async function f3() {
  6201              let bar = async (x, y = couldThrow()) => [x, y, this, arguments]
  6202              return await bar('a')
  6203            }
  6204            async function f4() {
  6205              let bar = async (x, y = couldThrow()) => (() => [x, y, this, arguments])()
  6206              return await bar('a')
  6207            }
  6208            async function f5() {
  6209              let bar = () => async (x, y = couldThrow()) => [x, y, this, arguments]
  6210              return await bar()('a')
  6211            }
  6212            async function f6() {
  6213              let bar = async () => async (x, y = couldThrow()) => [x, y, this, arguments]
  6214              return await (await bar())('a')
  6215            }
  6216            for (let foo of [f0, f1, f2, f3, f4, f5, f6]) {
  6217              let [x, y, t, a] = await foo.call(0, 1, 2, 3)
  6218              if (x !== 'a' || y !== 'b' || t !== 0 || a.length !== 3 || a[0] !== 1 || a[1] !== 2 || a[2] !== 3) throw 'fail'
  6219            }
  6220          }
  6221        `,
  6222      }, { async: true }),
  6223      test(['in.js', '--outfile=node.js'].concat(flags), {
  6224        // The async transform must not change the argument count
  6225        'in.js': `
  6226          async function a(x, y) {}
  6227          if (a.length !== 2) throw 'fail: a'
  6228  
  6229          async function b(x, y = x(), z) {}
  6230          if (b.length !== 1) throw 'fail: b'
  6231  
  6232          async function c(x, y, ...z) {}
  6233          if (c.length !== 2) throw 'fail: c'
  6234  
  6235          let d = async function(x, y) {}
  6236          if (d.length !== 2) throw 'fail: d'
  6237  
  6238          let e = async function(x, y = x(), z) {}
  6239          if (e.length !== 1) throw 'fail: e'
  6240  
  6241          let f = async function(x, y, ...z) {}
  6242          if (f.length !== 2) throw 'fail: f'
  6243  
  6244          let g = async (x, y) => {}
  6245          if (g.length !== 2) throw 'fail: g'
  6246  
  6247          let h = async (x, y = x(), z) => {}
  6248          if (h.length !== 1) throw 'fail: h'
  6249  
  6250          let i = async (x, y, ...z) => {}
  6251          if (i.length !== 2) throw 'fail: i'
  6252        `,
  6253      }),
  6254      test(['in.js', '--outfile=node.js'].concat(flags), {
  6255        // Functions must be able to access default arguments past the last non-default argument
  6256        'in.js': `
  6257          exports.async = async () => {
  6258            async function a(x, y = 0) { return y }
  6259            let b = async function(x, y = 0) { return y }
  6260            let c = async (x, y = 0) => y
  6261            for (let fn of [a, b, c]) {
  6262              if ((await fn('x', 'y')) !== 'y') throw 'fail: ' + fn
  6263            }
  6264          }
  6265        `,
  6266      }, { async: true }),
  6267      test(['in.js', '--outfile=node.js'].concat(flags), {
  6268        // Functions must be able to access arguments past the argument count using "arguments"
  6269        'in.js': `
  6270          exports.async = async () => {
  6271            async function a() { return arguments[2] }
  6272            async function b(x, y) { return arguments[2] }
  6273            async function c(x, y = x) { return arguments[2] }
  6274            let d = async function() { return arguments[2] }
  6275            let e = async function(x, y) { return arguments[2] }
  6276            let f = async function(x, y = x) { return arguments[2] }
  6277            for (let fn of [a, b, c, d, e, f]) {
  6278              if ((await fn('x', 'y', 'z')) !== 'z') throw 'fail: ' + fn
  6279            }
  6280          }
  6281        `,
  6282      }, { async: true }),
  6283      test(['in.js', '--outfile=node.js'].concat(flags), {
  6284        // Functions must be able to access arguments past the argument count using a rest argument
  6285        'in.js': `
  6286          exports.async = async () => {
  6287            async function a(...rest) { return rest[3] }
  6288            async function b(x, y, ...rest) { return rest[1] }
  6289            async function c(x, y = x, ...rest) { return rest[1] }
  6290            let d = async function(...rest) { return rest[3] }
  6291            let e = async function(x, y, ...rest) { return rest[1] }
  6292            let f = async function(x, y = x, ...rest) { return rest[1] }
  6293            let g = async (...rest) => rest[3]
  6294            let h = async (x, y, ...rest) => rest[1]
  6295            let i = async (x, y = x, ...rest) => rest[1]
  6296            for (let fn of [a, b, c, d, e, f, g, h, i]) {
  6297              if ((await fn(11, 22, 33, 44)) !== 44) throw 'fail: ' + fn
  6298            }
  6299          }
  6300        `,
  6301      }, { async: true }),
  6302      test(['in.js', '--outfile=node.js'].concat(flags), {
  6303        // Functions must be able to modify arguments using "arguments"
  6304        'in.js': `
  6305          exports.async = async () => {
  6306            async function a(x) { let y = [x, arguments[0]]; arguments[0] = 'y'; return y.concat(x, arguments[0]) }
  6307            let b = async function(x) { let y = [x, arguments[0]]; arguments[0] = 'y'; return y.concat(x, arguments[0]) }
  6308            for (let fn of [a, b]) {
  6309              let values = (await fn('x')) + ''
  6310              if (values !== 'x,x,y,y') throw 'fail: ' + values
  6311            }
  6312          }
  6313        `,
  6314      }, { async: true }),
  6315      test(['in.js', '--outfile=node.js'].concat(flags), {
  6316        // Errors in the evaluation of async function arguments should reject the resulting promise
  6317        'in.js': `
  6318          exports.async = async () => {
  6319            let expected = new Error('You should never see this error')
  6320            let throws = () => { throw expected }
  6321            async function a(x, y = throws()) {}
  6322            async function b({ [throws()]: x }) {}
  6323            let c = async function (x, y = throws()) {}
  6324            let d = async function ({ [throws()]: x }) {}
  6325            let e = async (x, y = throws()) => {}
  6326            let f = async ({ [throws()]: x }) => {}
  6327            for (let fn of [a, b, c, d, e, f]) {
  6328              let promise = fn({})
  6329              try {
  6330                await promise
  6331              } catch (e) {
  6332                if (e === expected) continue
  6333              }
  6334              throw 'fail: ' + fn
  6335            }
  6336          }
  6337        `,
  6338      }, { async: true }),
  6339      test(['in.js', '--outfile=node.js'].concat(flags), {
  6340        // Functions handle "super" property accesses in classes
  6341        'in.js': `
  6342          exports.async = async () => {
  6343            let counter = 0
  6344            let returnsBar = () => (++counter, 'bar')
  6345            class Base {
  6346              foo(x, y) {
  6347                return x + y
  6348              }
  6349              get bar() { return this._bar }
  6350              set bar(x) { this._bar = x }
  6351            }
  6352            class Derived extends Base {
  6353              get bar() { throw 'fail' }
  6354              set bar(x) { throw 'fail' }
  6355              async test(foo, bar) {
  6356                return [
  6357                  await super.foo,
  6358                  await super[foo],
  6359  
  6360                  ([super.bar] = [BigInt('1')])[0],
  6361                  ([super[bar]] = [BigInt('2')])[0],
  6362                  ([super[returnsBar()]] = [BigInt('3')])[0],
  6363  
  6364                  (super.bar = BigInt('4')),
  6365                  (super.bar += BigInt('2')),
  6366                  super.bar++,
  6367                  ++super.bar,
  6368  
  6369                  (super[bar] = BigInt('9')),
  6370                  (super[bar] += BigInt('2')),
  6371                  super[bar]++,
  6372                  ++super[bar],
  6373  
  6374                  (super[returnsBar()] = BigInt('14')),
  6375                  (super[returnsBar()] += BigInt('2')),
  6376                  super[returnsBar()]++,
  6377                  ++super[returnsBar()],
  6378  
  6379                  await super.foo.name,
  6380                  await super[foo].name,
  6381                  await super.foo?.name,
  6382                  await super[foo]?.name,
  6383                  await super._foo?.name,
  6384                  await super['_' + foo]?.name,
  6385  
  6386                  await super.foo(1, 2),
  6387                  await super[foo](1, 2),
  6388                  await super.foo?.(1, 2),
  6389                  await super[foo]?.(1, 2),
  6390                  await super._foo?.(1, 2),
  6391                  await super['_' + foo]?.(1, 2),
  6392                ]
  6393              }
  6394            }
  6395            let d = new Derived
  6396            let observed = await d.test('foo', 'bar')
  6397            let expected = [
  6398              d.foo, d.foo,
  6399              BigInt('1'), BigInt('2'), BigInt('3'),
  6400              BigInt('4'), BigInt('6'), BigInt('6'), BigInt('8'),
  6401              BigInt('9'), BigInt('11'), BigInt('11'), BigInt('13'),
  6402              BigInt('14'), BigInt('16'), BigInt('16'), BigInt('18'),
  6403              d.foo.name, d.foo.name, d.foo.name, d.foo.name, void 0, void 0,
  6404              3, 3, 3, 3, void 0, void 0,
  6405            ]
  6406            observed.push(d._bar, Base.prototype._bar, counter)
  6407            expected.push(BigInt('18'), undefined, 5)
  6408            for (let i = 0; i < expected.length; i++) {
  6409              if (observed[i] !== expected[i]) {
  6410                console.log(i, observed[i], expected[i])
  6411                throw 'fail'
  6412              }
  6413            }
  6414          }
  6415        `,
  6416      }, { async: true }),
  6417      test(['in.js', '--outfile=node.js'].concat(flags), {
  6418        // Functions handle "super" property accesses in objects
  6419        'in.js': `
  6420          exports.async = async () => {
  6421            let counter = 0
  6422            let returnsBar = () => (++counter, 'bar')
  6423            let b = {
  6424              foo(x, y) {
  6425                return x + y
  6426              },
  6427              get bar() { return this._bar },
  6428              set bar(x) { this._bar = x },
  6429            }
  6430            let d = {
  6431              get bar() { throw 'fail' },
  6432              set bar(x) { throw 'fail' },
  6433              async test(foo, bar) {
  6434                return [
  6435                  await super.foo,
  6436                  await super[foo],
  6437  
  6438                  ([super.bar] = [BigInt('1')])[0],
  6439                  ([super[bar]] = [BigInt('2')])[0],
  6440                  ([super[returnsBar()]] = [BigInt('3')])[0],
  6441  
  6442                  (super.bar = BigInt('4')),
  6443                  (super.bar += BigInt('2')),
  6444                  super.bar++,
  6445                  ++super.bar,
  6446  
  6447                  (super[bar] = BigInt('9')),
  6448                  (super[bar] += BigInt('2')),
  6449                  super[bar]++,
  6450                  ++super[bar],
  6451  
  6452                  (super[returnsBar()] = BigInt('14')),
  6453                  (super[returnsBar()] += BigInt('2')),
  6454                  super[returnsBar()]++,
  6455                  ++super[returnsBar()],
  6456  
  6457                  await super.foo.name,
  6458                  await super[foo].name,
  6459                  await super.foo?.name,
  6460                  await super[foo]?.name,
  6461                  await super._foo?.name,
  6462                  await super['_' + foo]?.name,
  6463  
  6464                  await super.foo(1, 2),
  6465                  await super[foo](1, 2),
  6466                  await super.foo?.(1, 2),
  6467                  await super[foo]?.(1, 2),
  6468                  await super._foo?.(1, 2),
  6469                  await super['_' + foo]?.(1, 2),
  6470                ]
  6471              },
  6472            }
  6473            Object.setPrototypeOf(d, b)
  6474            let observed = await d.test('foo', 'bar')
  6475            let expected = [
  6476              d.foo, d.foo,
  6477              BigInt('1'), BigInt('2'), BigInt('3'),
  6478              BigInt('4'), BigInt('6'), BigInt('6'), BigInt('8'),
  6479              BigInt('9'), BigInt('11'), BigInt('11'), BigInt('13'),
  6480              BigInt('14'), BigInt('16'), BigInt('16'), BigInt('18'),
  6481              d.foo.name, d.foo.name, d.foo.name, d.foo.name, void 0, void 0,
  6482              3, 3, 3, 3, void 0, void 0,
  6483            ]
  6484            observed.push(d._bar, b._bar, counter)
  6485            expected.push(BigInt('18'), undefined, 5)
  6486            for (let i = 0; i < expected.length; i++) {
  6487              if (observed[i] !== expected[i]) {
  6488                console.log(i, observed[i], expected[i])
  6489                throw 'fail'
  6490              }
  6491            }
  6492          }
  6493        `,
  6494      }, { async: true }),
  6495      test(['in.js', '--outfile=node.js'].concat(flags), {
  6496        // Handle "super" property accesses in static class fields
  6497        'in.js': `
  6498          exports.async = async () => {
  6499            let counter = 0
  6500            let returnsBar = () => (++counter, 'bar')
  6501            class Base {
  6502              static foo(x, y) {
  6503                return x + y
  6504              }
  6505              static get bar() { return this._bar }
  6506              static set bar(x) { this._bar = x }
  6507            }
  6508            class Derived extends Base {
  6509              static get bar() { throw 'fail' }
  6510              static set bar(x) { throw 'fail' }
  6511              static test = async (foo, bar) => {
  6512                return [
  6513                  await super.foo,
  6514                  await super[foo],
  6515  
  6516                  ([super.bar] = [BigInt('1')])[0],
  6517                  ([super[bar]] = [BigInt('2')])[0],
  6518                  ([super[returnsBar()]] = [BigInt('3')])[0],
  6519  
  6520                  (super.bar = BigInt('4')),
  6521                  (super.bar += BigInt('2')),
  6522                  super.bar++,
  6523                  ++super.bar,
  6524  
  6525                  (super[bar] = BigInt('9')),
  6526                  (super[bar] += BigInt('2')),
  6527                  super[bar]++,
  6528                  ++super[bar],
  6529  
  6530                  (super[returnsBar()] = BigInt('14')),
  6531                  (super[returnsBar()] += BigInt('2')),
  6532                  super[returnsBar()]++,
  6533                  ++super[returnsBar()],
  6534  
  6535                  await super.foo.name,
  6536                  await super[foo].name,
  6537                  await super.foo?.name,
  6538                  await super[foo]?.name,
  6539                  await super._foo?.name,
  6540                  await super['_' + foo]?.name,
  6541  
  6542                  await super.foo(1, 2),
  6543                  await super[foo](1, 2),
  6544                  await super.foo?.(1, 2),
  6545                  await super[foo]?.(1, 2),
  6546                  await super._foo?.(1, 2),
  6547                  await super['_' + foo]?.(1, 2),
  6548                ]
  6549              }
  6550            }
  6551            let observed = await Derived.test('foo', 'bar')
  6552            let expected = [
  6553              Derived.foo, Derived.foo,
  6554              BigInt('1'), BigInt('2'), BigInt('3'),
  6555              BigInt('4'), BigInt('6'), BigInt('6'), BigInt('8'),
  6556              BigInt('9'), BigInt('11'), BigInt('11'), BigInt('13'),
  6557              BigInt('14'), BigInt('16'), BigInt('16'), BigInt('18'),
  6558              Derived.foo.name, Derived.foo.name, Derived.foo.name, Derived.foo.name, void 0, void 0,
  6559              3, 3, 3, 3, void 0, void 0,
  6560            ]
  6561            observed.push(Derived._bar, Base._bar, counter)
  6562            expected.push(BigInt('18'), undefined, 5)
  6563            for (let i = 0; i < expected.length; i++) {
  6564              if (observed[i] !== expected[i]) {
  6565                console.log(i, observed[i], expected[i])
  6566                throw 'fail'
  6567              }
  6568            }
  6569          }
  6570        `,
  6571      }, { async: true }),
  6572      test(['in.js', '--outfile=node.js'].concat(flags), {
  6573        // Handle "super" property accesses in async arrow functions
  6574        'in.js': `
  6575          exports.async = async () => {
  6576            const log = [];
  6577            class Base {
  6578              foo(x) { log.push(x) }
  6579            }
  6580            class Derived extends Base {
  6581              foo1() { return async () => super.foo('foo1') }
  6582              foo2() { return async () => () => super.foo('foo2') }
  6583              foo3() { return () => async () => super.foo('foo3') }
  6584              foo4() { return async () => async () => super.foo('foo4') }
  6585              bar1 = async () => super.foo('bar1')
  6586              bar2 = async () => () => super.foo('bar2')
  6587              bar3 = () => async () => super.foo('bar3')
  6588              bar4 = async () => async () => super.foo('bar4')
  6589              async baz1() { return () => super.foo('baz1') }
  6590              async baz2() { return () => () => super.foo('baz2') }
  6591  
  6592              #foo1() { return async () => super.foo('foo1') }
  6593              #foo2() { return async () => () => super.foo('foo2') }
  6594              #foo3() { return () => async () => super.foo('foo3') }
  6595              #foo4() { return async () => async () => super.foo('foo4') }
  6596              #bar1 = async () => super.foo('bar1')
  6597              #bar2 = async () => () => super.foo('bar2')
  6598              #bar3 = () => async () => super.foo('bar3')
  6599              #bar4 = async () => async () => super.foo('bar4')
  6600              async #baz1() { return () => super.foo('baz1') }
  6601              async #baz2() { return () => () => super.foo('baz2') }
  6602  
  6603              async run() {
  6604                await derived.foo1()();
  6605                (await derived.foo2()())();
  6606                await derived.foo3()()();
  6607                await (await derived.foo4()())();
  6608                await derived.bar1();
  6609                (await derived.bar2())();
  6610                await derived.bar3()();
  6611                await (await derived.bar4())();
  6612                (await derived.baz1())();
  6613                (await derived.baz2())()();
  6614  
  6615                await this.#foo1()();
  6616                (await this.#foo2()())();
  6617                await this.#foo3()()();
  6618                await (await this.#foo4()())();
  6619                await this.#bar1();
  6620                (await this.#bar2())();
  6621                await this.#bar3()();
  6622                await (await this.#bar4())();
  6623                (await this.#baz1())();
  6624                (await this.#baz2())()();
  6625              }
  6626            }
  6627            let derived = new Derived;
  6628            await derived.run();
  6629            let observed = log.join(',');
  6630            let expected = 'foo1,foo2,foo3,foo4,bar1,bar2,bar3,bar4,baz1,baz2';
  6631            expected += ',' + expected;
  6632            if (observed !== expected) throw 'fail: ' + observed + ' != ' + expected;
  6633          }
  6634        `,
  6635      }, { async: true }),
  6636      test(['in.js', '--outfile=node.js'].concat(flags), {
  6637        // Handle "super" property writes in async arrow functions
  6638        'in.js': `
  6639          exports.async = async () => {
  6640            const log = [];
  6641            class Base {
  6642              set foo(x) { log.push(x) }
  6643            }
  6644            class Derived extends Base {
  6645              foo1() { return async () => super.foo = 'foo1' }
  6646              foo2() { return async () => () => super.foo = 'foo2' }
  6647              foo3() { return () => async () => super.foo = 'foo3' }
  6648              foo4() { return async () => async () => super.foo = 'foo4' }
  6649              bar1 = async () => super.foo = 'bar1'
  6650              bar2 = async () => () => super.foo = 'bar2'
  6651              bar3 = () => async () => super.foo = 'bar3'
  6652              bar4 = async () => async () => super.foo = 'bar4'
  6653              async baz1() { return () => super.foo = 'baz1' }
  6654              async baz2() { return () => () => super.foo = 'baz2' }
  6655  
  6656              #foo1() { return async () => super.foo = 'foo1' }
  6657              #foo2() { return async () => () => super.foo = 'foo2' }
  6658              #foo3() { return () => async () => super.foo = 'foo3' }
  6659              #foo4() { return async () => async () => super.foo = 'foo4' }
  6660              #bar1 = async () => super.foo = 'bar1'
  6661              #bar2 = async () => () => super.foo = 'bar2'
  6662              #bar3 = () => async () => super.foo = 'bar3'
  6663              #bar4 = async () => async () => super.foo = 'bar4'
  6664              async #baz1() { return () => super.foo = 'baz1' }
  6665              async #baz2() { return () => () => super.foo = 'baz2' }
  6666  
  6667              async run() {
  6668                await this.foo1()();
  6669                (await this.foo2()())();
  6670                await this.foo3()()();
  6671                await (await this.foo4()())();
  6672                await this.bar1();
  6673                (await this.bar2())();
  6674                await this.bar3()();
  6675                await (await this.bar4())();
  6676                (await this.baz1())();
  6677                (await this.baz2())()();
  6678  
  6679                await this.#foo1()();
  6680                (await this.#foo2()())();
  6681                await this.#foo3()()();
  6682                await (await this.#foo4()())();
  6683                await this.#bar1();
  6684                (await this.#bar2())();
  6685                await this.#bar3()();
  6686                await (await this.#bar4())();
  6687                (await this.#baz1())();
  6688                (await this.#baz2())()();
  6689              }
  6690            }
  6691            let derived = new Derived;
  6692            await derived.run();
  6693            let observed = log.join(',');
  6694            let expected = 'foo1,foo2,foo3,foo4,bar1,bar2,bar3,bar4,baz1,baz2';
  6695            expected += ',' + expected;
  6696            if (observed !== expected) throw 'fail: ' + observed + ' != ' + expected;
  6697          }
  6698        `,
  6699      }, { async: true }),
  6700      test(['in.js', '--outfile=node.js'].concat(flags), {
  6701        // Handle static "super" property accesses in async arrow functions
  6702        'in.js': `
  6703          exports.async = async () => {
  6704            const log = [];
  6705            class Base {
  6706              static foo(x) { log.push(x) }
  6707            }
  6708            class Derived extends Base {
  6709              static foo1() { return async () => super.foo('foo1') }
  6710              static foo2() { return async () => () => super.foo('foo2') }
  6711              static foo3() { return () => async () => super.foo('foo3') }
  6712              static foo4() { return async () => async () => super.foo('foo4') }
  6713              static bar1 = async () => super.foo('bar1')
  6714              static bar2 = async () => () => super.foo('bar2')
  6715              static bar3 = () => async () => super.foo('bar3')
  6716              static bar4 = async () => async () => super.foo('bar4')
  6717              static async baz1() { return () => super.foo('baz1') }
  6718              static async baz2() { return () => () => super.foo('baz2') }
  6719  
  6720              static #foo1() { return async () => super.foo('foo1') }
  6721              static #foo2() { return async () => () => super.foo('foo2') }
  6722              static #foo3() { return () => async () => super.foo('foo3') }
  6723              static #foo4() { return async () => async () => super.foo('foo4') }
  6724              static #bar1 = async () => super.foo('bar1')
  6725              static #bar2 = async () => () => super.foo('bar2')
  6726              static #bar3 = () => async () => super.foo('bar3')
  6727              static #bar4 = async () => async () => super.foo('bar4')
  6728              static async #baz1() { return () => super.foo('baz1') }
  6729              static async #baz2() { return () => () => super.foo('baz2') }
  6730  
  6731              static async run() {
  6732                await this.foo1()();
  6733                (await this.foo2()())();
  6734                await this.foo3()()();
  6735                await (await this.foo4()())();
  6736                await this.bar1();
  6737                (await this.bar2())();
  6738                await this.bar3()();
  6739                await (await this.bar4())();
  6740                (await this.baz1())();
  6741                (await this.baz2())()();
  6742  
  6743                await this.#foo1()();
  6744                (await this.#foo2()())();
  6745                await this.#foo3()()();
  6746                await (await this.#foo4()())();
  6747                await this.#bar1();
  6748                (await this.#bar2())();
  6749                await this.#bar3()();
  6750                await (await this.#bar4())();
  6751                (await this.#baz1())();
  6752                (await this.#baz2())()();
  6753              }
  6754            }
  6755            await Derived.run();
  6756            let observed = log.join(',');
  6757            let expected = 'foo1,foo2,foo3,foo4,bar1,bar2,bar3,bar4,baz1,baz2';
  6758            expected += ',' + expected;
  6759            if (observed !== expected) throw 'fail: ' + observed + ' != ' + expected;
  6760          }
  6761        `,
  6762      }, { async: true }),
  6763      test(['in.js', '--outfile=node.js'].concat(flags), {
  6764        // Handle static "super" property writes in async arrow functions
  6765        'in.js': `
  6766          exports.async = async () => {
  6767            const log = [];
  6768            class Base {
  6769              static set foo(x) { log.push(x) }
  6770            }
  6771            class Derived extends Base {
  6772              static foo1() { return async () => super.foo = 'foo1' }
  6773              static foo2() { return async () => () => super.foo = 'foo2' }
  6774              static foo3() { return () => async () => super.foo = 'foo3' }
  6775              static foo4() { return async () => async () => super.foo = 'foo4' }
  6776              static bar1 = async () => super.foo = 'bar1'
  6777              static bar2 = async () => () => super.foo = 'bar2'
  6778              static bar3 = () => async () => super.foo = 'bar3'
  6779              static bar4 = async () => async () => super.foo = 'bar4'
  6780              static async baz1() { return () => super.foo = 'baz1' }
  6781              static async baz2() { return () => () => super.foo = 'baz2' }
  6782  
  6783              static #foo1() { return async () => super.foo = 'foo1' }
  6784              static #foo2() { return async () => () => super.foo = 'foo2' }
  6785              static #foo3() { return () => async () => super.foo = 'foo3' }
  6786              static #foo4() { return async () => async () => super.foo = 'foo4' }
  6787              static #bar1 = async () => super.foo = 'bar1'
  6788              static #bar2 = async () => () => super.foo = 'bar2'
  6789              static #bar3 = () => async () => super.foo = 'bar3'
  6790              static #bar4 = async () => async () => super.foo = 'bar4'
  6791              static async #baz1() { return () => super.foo = 'baz1' }
  6792              static async #baz2() { return () => () => super.foo = 'baz2' }
  6793  
  6794              static async run() {
  6795                await this.foo1()();
  6796                (await this.foo2()())();
  6797                await this.foo3()()();
  6798                await (await this.foo4()())();
  6799                await this.bar1();
  6800                (await this.bar2())();
  6801                await this.bar3()();
  6802                await (await this.bar4())();
  6803                (await this.baz1())();
  6804                (await this.baz2())()();
  6805  
  6806                await this.#foo1()();
  6807                (await this.#foo2()())();
  6808                await this.#foo3()()();
  6809                await (await this.#foo4()())();
  6810                await this.#bar1();
  6811                (await this.#bar2())();
  6812                await this.#bar3()();
  6813                await (await this.#bar4())();
  6814                (await this.#baz1())();
  6815                (await this.#baz2())()();
  6816              }
  6817            }
  6818            await Derived.run();
  6819            let observed = log.join(',');
  6820            let expected = 'foo1,foo2,foo3,foo4,bar1,bar2,bar3,bar4,baz1,baz2';
  6821            expected += ',' + expected;
  6822            if (observed !== expected) throw 'fail: ' + observed + ' != ' + expected;
  6823          }
  6824        `,
  6825      }, { async: true }),
  6826      test(['in.js', '--outfile=node.js'].concat(flags), {
  6827        // Check various "super" edge cases
  6828        'in.js': `
  6829          exports.async = async () => {
  6830            const log = [];
  6831            let o, Base, Derived;
  6832  
  6833            ({
  6834              __proto__: { foo() { log.push(1) } },
  6835              bar() { super.foo() },
  6836            }.bar());
  6837  
  6838            o = { bar() { super.foo() } };
  6839            o.__proto__ = { foo() { log.push(2) } };
  6840            o.bar();
  6841  
  6842            o = {
  6843              __proto__: { foo() { log.push(3) } },
  6844              bar() { super.foo() },
  6845            };
  6846            ({ bar: o.bar }).bar();
  6847  
  6848            Base = class { foo() { log.push(4) } };
  6849            Derived = class extends Base { bar() { super.foo() } };
  6850            new Derived().bar();
  6851  
  6852            Base = class {};
  6853            Derived = class extends Base { bar() { super.foo() } };
  6854            Derived.prototype.__proto__ = { foo() { log.push(5) } };
  6855            new Derived().bar();
  6856  
  6857            Base = class { foo() { log.push(6) } };
  6858            Derived = class extends Base { bar() { super.foo() } };
  6859            ({ bar: Derived.prototype.bar }).bar();
  6860  
  6861            Base = class { foo() { log.push(7) } };
  6862            Derived = class extends Base { bar() { super.foo() } };
  6863            Derived.prototype.foo = () => log.push(false);
  6864            new Derived().bar();
  6865  
  6866            Base = class { foo() { log.push(8) } };
  6867            Derived = class extends Base { bar = () => super.foo() };
  6868            new Derived().bar();
  6869  
  6870            Base = class { foo() { log.push(9) } };
  6871            Derived = class extends Base { bar = () => super.foo() };
  6872            o = new Derived();
  6873            o.__proto__ = {};
  6874            o.bar();
  6875  
  6876            Base = class { static foo() { log.push(10) } };
  6877            Derived = class extends Base { static bar() { super.foo() } };
  6878            Derived.bar();
  6879  
  6880            Base = class { static foo() { log.push(11) } };
  6881            Derived = class extends Base { static bar() { super.foo() } };
  6882            ({ bar: Derived.bar }).bar();
  6883  
  6884            Base = class {};
  6885            Derived = class extends Base { static bar() { super.foo() } };
  6886            Derived.__proto__ = { foo() { log.push(12) } };
  6887            Derived.bar();
  6888  
  6889            Base = class { static foo() { log.push(13) } };
  6890            Derived = class extends Base { static bar = () => super.foo() };
  6891            Derived.bar();
  6892  
  6893            Base = class { static foo() { log.push(14) } };
  6894            Derived = class extends Base { static bar = () => super.foo() };
  6895            ({ bar: Derived.bar }).bar();
  6896  
  6897            Base = class {};
  6898            Derived = class extends Base { static bar = () => super.foo() };
  6899            Derived.__proto__ = { foo() { log.push(15) } };
  6900            Derived.bar();
  6901  
  6902            Base = class { foo() { return 'bar' } };
  6903            Derived = class extends Base { async x() { return class { [super.foo()] = 123 } } };
  6904            if (new (await new Derived().x())().bar === 123) log.push(16);
  6905  
  6906            Base = class { foo() { return 'bar' } };
  6907            Derived = class extends Base { x = async () => class { [super.foo()] = 123 } };
  6908            if (new (await new Derived().x())().bar === 123) log.push(17);
  6909  
  6910            Base = class { static foo() { return 'bar' } };
  6911            Derived = class extends Base { static async x() { return class { [super.foo()] = 123 } } };
  6912            if (new (await Derived.x())().bar === 123) log.push(18);
  6913  
  6914            Base = class { static foo() { return 'bar' } };
  6915            Derived = class extends Base { static x = async () => class { [super.foo()] = 123 } };
  6916            if (new (await Derived.x())().bar === 123) log.push(19);
  6917  
  6918            // Check that an captured temporary for object methods has the correct scope
  6919            o = [];
  6920            for (let i = 0; i < 3; i++) o.push({
  6921              __proto__: { foo() { return i } },
  6922              async bar() { return super.foo() },
  6923            })
  6924            for (const x of o) log.push(20 + await x.bar());
  6925  
  6926            const observed = log.join(',');
  6927            const expected = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22';
  6928            if (observed !== expected) throw 'fail: ' + observed + ' != ' + expected;
  6929          }
  6930        `,
  6931      }, { async: true }),
  6932      test(['in.js', '--outfile=node.js', '--bundle'].concat(flags), {
  6933        // Check edge case in https://github.com/evanw/esbuild/issues/2158
  6934        'in.js': `
  6935        class Foo {
  6936          constructor(x) {
  6937            this.base = x
  6938          }
  6939        }
  6940        class Bar extends Foo {
  6941          static FOO = 1
  6942          constructor() {
  6943            super(2)
  6944            this.derived = this.#foo + Bar.FOO
  6945          }
  6946          #foo = 3
  6947        }
  6948        let bar = new Bar
  6949        if (bar.base !== 2 || bar.derived !== 4) throw 'fail'
  6950        `,
  6951      }),
  6952      test(['in.js', '--outfile=node.js', '--keep-names', '--bundle'].concat(flags), {
  6953        // Check default export name preservation with lowered "super" inside lowered "async"
  6954        'in.js': `
  6955          import fn from './export'
  6956          import pfn from './export-private'
  6957          if (fn.name !== 'default') throw 'fail: ' + fn.name
  6958          if (pfn.name !== 'default') throw 'fail: ' + pfn.name
  6959        `,
  6960        'export.js': `
  6961          export default class extends Object {
  6962            async foo() { super.bar() }
  6963          }
  6964        `,
  6965        'export-private.js': `
  6966          export default class extends Object {
  6967            async #foo() { super.bar() }
  6968          }
  6969        `,
  6970      }),
  6971      test(['in.js', '--outfile=node.js', '--keep-names', '--bundle', '--minify'].concat(flags), {
  6972        // (minified) Check default export name preservation with lowered "super" inside lowered "async"
  6973        'in.js': `
  6974          import fn from './export'
  6975          import pfn from './export-private'
  6976          if (fn.name !== 'default') throw 'fail: ' + fn.name
  6977          if (pfn.name !== 'default') throw 'fail: ' + pfn.name
  6978        `,
  6979        'export.js': `
  6980          export default class extends Object {
  6981            async foo() { super.bar() }
  6982          }
  6983        `,
  6984        'export-private.js': `
  6985          export default class extends Object {
  6986            async #foo() { super.bar() }
  6987          }
  6988        `,
  6989      }),
  6990      test(['in.js', '--outfile=node.js'].concat(flags), {
  6991        // Test coverage for a TypeScript bug: https://github.com/microsoft/TypeScript/issues/46580
  6992        'in.js': `
  6993          class A {
  6994            static x = 1
  6995          }
  6996          class B extends A {
  6997            static y = () => super.x
  6998          }
  6999          class C {
  7000            static x = 2
  7001          }
  7002          if (B.y() !== 1) throw 'fail'
  7003          Object.setPrototypeOf(B, C)
  7004          if (B.y() !== 2) throw 'fail'
  7005        `,
  7006      }),
  7007      test(['in.js', '--outfile=node.js'].concat(flags), {
  7008        // Check the behavior of method tear-off
  7009        'in.js': `
  7010          exports.async = async () => {
  7011            class Base {
  7012              set x(y) {
  7013                this.foo = 'Base'
  7014              }
  7015            }
  7016            class Derived extends Base {
  7017              set x(y) {
  7018                this.foo = 'Derived'
  7019              }
  7020              async set(z) {
  7021                super.x = z
  7022              }
  7023            }
  7024            let base = {
  7025              set x(y) {
  7026                this.foo = 'base'
  7027              }
  7028            }
  7029            let derived = Object.create(base)
  7030            derived.set = new Derived().set
  7031            await derived.set(123)
  7032            if (base.foo !== void 0 || derived.foo !== 'Base') throw 'fail'
  7033          }
  7034        `,
  7035      }, { async: true }),
  7036      test(['in.js', '--outfile=node.js'].concat(flags), {
  7037        // Check the behavior of static method tear-off
  7038        'in.js': `
  7039          exports.async = async () => {
  7040            class Base {
  7041              static set x(y) {
  7042                this.foo = 'Base'
  7043              }
  7044            }
  7045            class Derived extends Base {
  7046              static set x(y) {
  7047                this.foo = 'Derived'
  7048              }
  7049              static async set(z) {
  7050                super.x = z
  7051              }
  7052            }
  7053            let base = {
  7054              set x(y) {
  7055                this.foo = 'base'
  7056              }
  7057            }
  7058            let derived = Object.create(base)
  7059            derived.set = Derived.set
  7060            await derived.set(123)
  7061            if (base.foo !== void 0 || derived.foo !== 'Base') throw 'fail'
  7062          }
  7063        `,
  7064      }, { async: true }),
  7065      test(['in.js', '--outfile=node.js'].concat(flags), {
  7066        // Check the behavior of static field tear-off (no async)
  7067        'in.js': `
  7068          exports.async = async () => {
  7069            class Base {
  7070              static set x(y) {
  7071                this.foo = 'Base'
  7072              }
  7073            }
  7074            class Derived extends Base {
  7075              static set x(y) {
  7076                this.foo = 'Derived'
  7077              }
  7078              static set = z => {
  7079                super.x = z
  7080              }
  7081            }
  7082            let base = {
  7083              set x(y) {
  7084                this.foo = 'base'
  7085              }
  7086            }
  7087            let derived = Object.create(base)
  7088            derived.set = Derived.set
  7089            derived.set(123)
  7090            if (base.foo !== void 0 || Derived.foo !== 'Base') throw 'fail'
  7091          }
  7092        `,
  7093      }, { async: true }),
  7094      test(['in.js', '--outfile=node.js'].concat(flags), {
  7095        // Check the behavior of static field tear-off (async)
  7096        'in.js': `
  7097          exports.async = async () => {
  7098            class Base {
  7099              static set x(y) {
  7100                this.foo = 'Base'
  7101              }
  7102            }
  7103            class Derived extends Base {
  7104              static set x(y) {
  7105                this.foo = 'Derived'
  7106              }
  7107              static set = async (z) => {
  7108                super.x = z
  7109              }
  7110            }
  7111            let base = {
  7112              set x(y) {
  7113                this.foo = 'base'
  7114              }
  7115            }
  7116            let derived = Object.create(base)
  7117            derived.set = Derived.set
  7118            await derived.set(123)
  7119            if (base.foo !== void 0 || Derived.foo !== 'Base') throw 'fail'
  7120          }
  7121        `,
  7122      }, { async: true }),
  7123      test(['in.js', '--outfile=node.js'].concat(flags), {
  7124        'in.js': `
  7125          class Foo extends (class {
  7126            foo() {
  7127              return this
  7128            }
  7129          }) {
  7130            x = async (foo) => [
  7131              super.foo(),
  7132              super.foo\`\`,
  7133              super[foo](),
  7134              super[foo]\`\`,
  7135              super['foo'](),
  7136              super['foo']\`\`,
  7137              this.#bar(),
  7138              this.#bar\`\`,
  7139            ]
  7140            #bar() {
  7141              return this
  7142            }
  7143          }
  7144          exports.async = async () => {
  7145            const foo = new Foo
  7146            for (const bar of await foo.x('foo'))
  7147              if (foo !== bar)
  7148                throw 'fail'
  7149          }
  7150        `,
  7151      }, { async: true }),
  7152  
  7153      // https://github.com/arogozine/LinqToTypeScript/issues/29
  7154      test(['in.js', '--outfile=node.js'].concat(flags), {
  7155        'in.js': `
  7156          exports.async = async () => {
  7157            let total = 0
  7158          outer:
  7159            for await (const n of [Promise.resolve(1), Promise.resolve(2), Promise.resolve(5)]) {
  7160              for (let i = 1; i <= n; i++) {
  7161                if (i === 4) continue outer
  7162                total += i
  7163              }
  7164            }
  7165            if (total !== 1 + (1 + 2) + (1 + 2 + 3)) throw 'fail'
  7166          }
  7167        `,
  7168      }, { async: true }),
  7169    )
  7170  }
  7171  
  7172  // Function hoisting tests
  7173  tests.push(
  7174    test(['in.js', '--outfile=node.js'], {
  7175      'in.js': `
  7176        if (1) {
  7177          function f() {
  7178            return f
  7179          }
  7180          f = null
  7181        }
  7182        if (typeof f !== 'function' || f() !== null) throw 'fail'
  7183      `,
  7184    }),
  7185    test(['in.js', '--outfile=node.js'], {
  7186      'in.js': `
  7187        'use strict'
  7188        if (1) {
  7189          function f() {
  7190            return f
  7191          }
  7192          f = null
  7193        }
  7194        if (typeof f !== 'undefined') throw 'fail'
  7195      `,
  7196    }),
  7197    test(['in.js', '--outfile=node.js', '--format=esm'], {
  7198      'in.js': `
  7199        export {}
  7200        if (1) {
  7201          function f() {
  7202            return f
  7203          }
  7204          f = null
  7205        }
  7206        if (typeof f !== 'undefined') throw 'fail'
  7207      `,
  7208    }),
  7209    test(['in.js', '--bundle', '--outfile=node.js'], {
  7210      'in.js': `
  7211        if (1) {
  7212          function f() {
  7213            return f
  7214          }
  7215          f = null
  7216        }
  7217        if (typeof f !== 'function' || f() !== null) throw 'fail'
  7218      `,
  7219    }),
  7220    test(['in.js', '--bundle', '--outfile=node.js'], {
  7221      'in.js': `
  7222        var f
  7223        if (1) {
  7224          function f() {
  7225            return f
  7226          }
  7227          f = null
  7228        }
  7229        if (typeof f !== 'function' || f() !== null) throw 'fail'
  7230      `,
  7231    }),
  7232    test(['in.js', '--bundle', '--outfile=node.js'], {
  7233      'in.js': `
  7234        'use strict'
  7235        if (1) {
  7236          function f() {
  7237            return f
  7238          }
  7239        }
  7240        if (typeof f !== 'undefined') throw 'fail'
  7241      `,
  7242    }),
  7243    test(['in.js', '--bundle', '--outfile=node.js'], {
  7244      'in.js': `
  7245        export {}
  7246        if (1) {
  7247          function f() {
  7248            return f
  7249          }
  7250        }
  7251        if (typeof f !== 'undefined') throw 'fail'
  7252      `,
  7253    }),
  7254    test(['in.js', '--bundle', '--outfile=node.js'], {
  7255      'in.js': `
  7256        var f = 1
  7257        if (1) {
  7258          function f() {
  7259            return f
  7260          }
  7261          f = null
  7262        }
  7263        if (typeof f !== 'function' || f() !== null) throw 'fail'
  7264      `,
  7265    }),
  7266    test(['in.js', '--bundle', '--outfile=node.js'], {
  7267      'in.js': `
  7268        'use strict'
  7269        var f = 1
  7270        if (1) {
  7271          function f() {
  7272            return f
  7273          }
  7274        }
  7275        if (f !== 1) throw 'fail'
  7276      `,
  7277    }),
  7278    test(['in.js', '--bundle', '--outfile=node.js'], {
  7279      'in.js': `
  7280        export {}
  7281        var f = 1
  7282        if (1) {
  7283          function f() {
  7284            return f
  7285          }
  7286        }
  7287        if (f !== 1) throw 'fail'
  7288      `,
  7289    }),
  7290    test(['in.js', '--bundle', '--outfile=node.js'], {
  7291      'in.js': `
  7292        import {f, g} from './other'
  7293        if (f !== void 0 || g !== 'g') throw 'fail'
  7294      `,
  7295      'other.js': `
  7296        'use strict'
  7297        var f
  7298        if (1) {
  7299          function f() {
  7300            return f
  7301          }
  7302        }
  7303        exports.f = f
  7304        exports.g = 'g'
  7305      `,
  7306    }),
  7307    test(['in.js', '--bundle', '--outfile=node.js'], {
  7308      'in.js': `
  7309        let f = 1
  7310        // This should not be turned into "if (1) let f" because that's a syntax error
  7311        if (1)
  7312          function f() {
  7313            return f
  7314          }
  7315        if (f !== 1) throw 'fail'
  7316      `,
  7317    }),
  7318    test(['in.js', '--bundle', '--outfile=node.js'], {
  7319      'in.js': `
  7320        x: function f() { return 1 }
  7321        if (f() !== 1) throw 'fail'
  7322      `,
  7323    }),
  7324    test(['in.ts', '--outfile=node.js'], {
  7325      'in.ts': `
  7326        if (1) {
  7327          var a = 'a'
  7328          for (var b = 'b'; 0; ) ;
  7329          for (var c in { c: 0 }) ;
  7330          for (var d of ['d']) ;
  7331          for (var e = 'e' in {}) ;
  7332          function f() { return 'f' }
  7333        }
  7334        const observed = JSON.stringify({ a, b, c, d, e, f: f() })
  7335        const expected = JSON.stringify({ a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' })
  7336        if (observed !== expected) throw observed
  7337      `,
  7338    }),
  7339    test(['in.ts', '--bundle', '--outfile=node.js'], {
  7340      'in.ts': `
  7341        if (1) {
  7342          var a = 'a'
  7343          for (var b = 'b'; 0; ) ;
  7344          for (var c in { c: 0 }) ;
  7345          for (var d of ['d']) ;
  7346          for (var e = 'e' in {}) ;
  7347          function f() { return 'f' }
  7348        }
  7349        const observed = JSON.stringify({ a, b, c, d, e, f: f() })
  7350        const expected = JSON.stringify({ a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' })
  7351        if (observed !== expected) throw observed
  7352      `,
  7353    }),
  7354    test(['in.js', '--outfile=node.js', '--keep-names'], {
  7355      'in.js': `
  7356        var f
  7357        if (1) function f() { return f }
  7358        if (typeof f !== 'function' || f.name !== 'f') throw 'fail: ' + f.name
  7359      `,
  7360    }),
  7361    test(['in.js', '--bundle', '--outfile=node.js', '--keep-names'], {
  7362      'in.js': `
  7363        var f
  7364        if (1) function f() { return f }
  7365        if (typeof f !== 'function' || f.name !== 'f') throw 'fail: ' + f.name
  7366      `,
  7367    }),
  7368    test(['in.ts', '--outfile=node.js', '--keep-names'], {
  7369      'in.ts': `
  7370        if (1) {
  7371          var a = 'a'
  7372          for (var b = 'b'; 0; ) ;
  7373          for (var c in { c: 0 }) ;
  7374          for (var d of ['d']) ;
  7375          for (var e = 'e' in {}) ;
  7376          function f() {}
  7377        }
  7378        const observed = JSON.stringify({ a, b, c, d, e, f: f.name })
  7379        const expected = JSON.stringify({ a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' })
  7380        if (observed !== expected) throw observed
  7381      `,
  7382    }),
  7383    test(['in.ts', '--bundle', '--outfile=node.js', '--keep-names'], {
  7384      'in.ts': `
  7385        if (1) {
  7386          var a = 'a'
  7387          for (var b = 'b'; 0; ) ;
  7388          for (var c in { c: 0 }) ;
  7389          for (var d of ['d']) ;
  7390          for (var e = 'e' in {}) ;
  7391          function f() {}
  7392        }
  7393        const observed = JSON.stringify({ a, b, c, d, e, f: f.name })
  7394        const expected = JSON.stringify({ a: 'a', b: 'b', c: 'c', d: 'd', e: 'e', f: 'f' })
  7395        if (observed !== expected) throw observed
  7396      `,
  7397    }),
  7398  )
  7399  
  7400  // Object rest pattern tests
  7401  tests.push(
  7402    // Test the correctness of side effect order for the TypeScript namespace exports
  7403    test(['in.ts', '--outfile=node.js'], {
  7404      'in.ts': `
  7405        function fn() {
  7406          let trail = []
  7407          let t = k => (trail.push(k), k)
  7408          let [
  7409            { [t('a')]: a } = { a: t('x') },
  7410            { [t('b')]: b, ...c } = { b: t('y') },
  7411            { [t('d')]: d } = { d: t('z') },
  7412          ] = [{ a: 1 }, { b: 2, bb: 3 }]
  7413          return JSON.stringify({a, b, c, d, trail})
  7414        }
  7415        namespace ns {
  7416          let trail = []
  7417          let t = k => (trail.push(k), k)
  7418          export let [
  7419            { [t('a')]: a } = { a: t('x') },
  7420            { [t('b')]: b, ...c } = { b: t('y') },
  7421            { [t('d')]: d } = { d: t('z') },
  7422          ] = [{ a: 1 }, { b: 2, bb: 3 }]
  7423          export let result = JSON.stringify({a, b, c, d, trail})
  7424        }
  7425        if (fn() !== ns.result) throw 'fail'
  7426      `,
  7427    }),
  7428  
  7429    // Test the array and object rest patterns in TypeScript namespace exports
  7430    test(['in.ts', '--outfile=node.js'], {
  7431      'in.ts': `
  7432        let obj = {};
  7433        ({a: obj.a, ...obj.b} = {a: 1, b: 2, c: 3});
  7434        [obj.c, , ...obj.d] = [1, 2, 3];
  7435        ({e: obj.e, f: obj.f = 'f'} = {e: 'e'});
  7436        [obj.g, , obj.h = 'h'] = ['g', 'gg'];
  7437        namespace ns {
  7438          export let {a, ...b} = {a: 1, b: 2, c: 3};
  7439          export let [c, , ...d] = [1, 2, 3];
  7440          export let {e, f = 'f'} = {e: 'e'};
  7441          export let [g, , h = 'h'] = ['g', 'gg'];
  7442        }
  7443        if (JSON.stringify(obj) !== JSON.stringify(ns)) throw 'fail'
  7444      `,
  7445    }),
  7446  
  7447    // Test the initializer being overwritten
  7448    test(['in.ts', '--outfile=node.js', '--target=es6'], {
  7449      'in.ts': `
  7450        var z = {x: {z: 'z'}, y: 'y'}, {x: z, ...y} = z
  7451        if (y.y !== 'y' || z.z !== 'z') throw 'fail'
  7452      `,
  7453    }),
  7454    test(['in.ts', '--outfile=node.js', '--target=es6'], {
  7455      'in.ts': `
  7456        var z = {x: {x: 'x'}, y: 'y'}, {[(z = {z: 'z'}, 'x')]: x, ...y} = z
  7457        if (x.x !== 'x' || y.y !== 'y' || z.z !== 'z') throw 'fail'
  7458      `,
  7459    }),
  7460  )
  7461  
  7462  // Code splitting tests
  7463  tests.push(
  7464    // Code splitting via sharing
  7465    test(['a.js', 'b.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7466      'a.js': `
  7467        import * as ns from './common'
  7468        export let a = 'a' + ns.foo
  7469      `,
  7470      'b.js': `
  7471        import * as ns from './common'
  7472        export let b = 'b' + ns.foo
  7473      `,
  7474      'common.js': `
  7475        export let foo = 123
  7476      `,
  7477      'node.js': `
  7478        import {a} from './out/a.js'
  7479        import {b} from './out/b.js'
  7480        if (a !== 'a123' || b !== 'b123') throw 'fail'
  7481      `,
  7482    }),
  7483  
  7484    // Code splitting via sharing with name templates
  7485    test([
  7486      'a.js', 'b.js', '--outdir=out', '--splitting', '--format=esm', '--bundle',
  7487      '--entry-names=[name][dir]x', '--chunk-names=[name]/[hash]',
  7488    ], {
  7489      'a.js': `
  7490        import * as ns from './common'
  7491        export let a = 'a' + ns.foo
  7492      `,
  7493      'b.js': `
  7494        import * as ns from './common'
  7495        export let b = 'b' + ns.foo
  7496      `,
  7497      'common.js': `
  7498        export let foo = 123
  7499      `,
  7500      'node.js': `
  7501        import {a} from './out/a/x.js'
  7502        import {b} from './out/b/x.js'
  7503        if (a !== 'a123' || b !== 'b123') throw 'fail'
  7504      `,
  7505    }),
  7506  
  7507    // Code splitting via sharing with name templates
  7508    test([
  7509      'pages/a/index.js', 'pages/b/index.js', '--outbase=.',
  7510      '--outdir=out', '--splitting', '--format=esm', '--bundle',
  7511      '--entry-names=[name][dir]y', '--chunk-names=[name]/[hash]',
  7512    ], {
  7513      'pages/a/index.js': `
  7514        import * as ns from '../common'
  7515        export let a = 'a' + ns.foo
  7516      `,
  7517      'pages/b/index.js': `
  7518        import * as ns from '../common'
  7519        export let b = 'b' + ns.foo
  7520      `,
  7521      'pages/common.js': `
  7522        export let foo = 123
  7523      `,
  7524      'node.js': `
  7525        import {a} from './out/index/pages/ay.js'
  7526        import {b} from './out/index/pages/by.js'
  7527        if (a !== 'a123' || b !== 'b123') throw 'fail'
  7528      `,
  7529    }),
  7530  
  7531    // Code splitting via ES6 module double-imported with sync and async imports
  7532    test(['a.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7533      'a.js': `
  7534        import * as ns1 from './b'
  7535        export default async function () {
  7536          const ns2 = await import('./b')
  7537          return [ns1.foo, -ns2.foo]
  7538        }
  7539      `,
  7540      'b.js': `
  7541        export let foo = 123
  7542      `,
  7543      'node.js': `
  7544        export let async = async () => {
  7545          const {default: fn} = await import('./out/a.js')
  7546          const [a, b] = await fn()
  7547          if (a !== 123 || b !== -123) throw 'fail'
  7548        }
  7549      `,
  7550    }, { async: true }),
  7551  
  7552    // Code splitting via CommonJS module double-imported with sync and async imports
  7553    test(['a.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7554      'a.js': `
  7555        import * as ns1 from './b.cjs'
  7556        export default async function () {
  7557          const ns2 = await import('./b.cjs')
  7558          return [ns1.foo, -ns2.default.foo]
  7559        }
  7560      `,
  7561      'b.cjs': `
  7562        exports.foo = 123
  7563      `,
  7564      'node.js': `
  7565        export let async = async () => {
  7566          const {default: fn} = await import('./out/a.js')
  7567          const [a, b] = await fn()
  7568          if (a !== 123 || b !== -123) throw 'fail'
  7569        }
  7570      `,
  7571    }, { async: true }),
  7572  
  7573    // Identical output chunks should not be shared
  7574    test(['a.js', 'b.js', 'c.js', '--outdir=out', '--splitting', '--format=esm', '--bundle', '--minify'], {
  7575      'a.js': `
  7576        import {foo as common1} from './common1'
  7577        import {foo as common2} from './common2'
  7578        export let a = [common1, common2]
  7579      `,
  7580      'b.js': `
  7581        import {foo as common2} from './common2'
  7582        import {foo as common3} from './common3'
  7583        export let b = [common2, common3]
  7584      `,
  7585      'c.js': `
  7586        import {foo as common3} from './common3'
  7587        import {foo as common1} from './common1'
  7588        export let c = [common3, common1]
  7589      `,
  7590      'common1.js': `
  7591        export let foo = {}
  7592      `,
  7593      'common2.js': `
  7594        export let foo = {}
  7595      `,
  7596      'common3.js': `
  7597        export let foo = {}
  7598      `,
  7599      'node.js': `
  7600        import {a} from './out/a.js'
  7601        import {b} from './out/b.js'
  7602        import {c} from './out/c.js'
  7603        if (a[0] === a[1]) throw 'fail'
  7604        if (b[0] === b[1]) throw 'fail'
  7605        if (c[0] === c[1]) throw 'fail'
  7606      `,
  7607    }),
  7608    test(['a.js', 'b.js', 'c.js', '--outdir=out', '--splitting', '--format=esm', '--bundle', '--minify'], {
  7609      'a.js': `
  7610        export {a} from './common'
  7611      `,
  7612      'b.js': `
  7613        export {b} from './common'
  7614      `,
  7615      'c.js': `
  7616        export {a as ca, b as cb} from './common'
  7617      `,
  7618      'common.js': `
  7619        export let a = {}
  7620        export let b = {}
  7621      `,
  7622      'node.js': `
  7623        import {a} from './out/a.js'
  7624        import {b} from './out/b.js'
  7625        import {ca, cb} from './out/c.js'
  7626        if (a === b || ca === cb || a !== ca || b !== cb) throw 'fail'
  7627      `,
  7628    }),
  7629  
  7630    // "sideEffects": false
  7631    // https://github.com/evanw/esbuild/issues/1081
  7632    test(['entry.js', '--outdir=out', '--splitting', '--format=esm', '--bundle', '--chunk-names=[name]'], {
  7633      'entry.js': `import('./a'); import('./b')`,
  7634      'a.js': `import { bar } from './shared'; bar()`,
  7635      'b.js': `import './shared'`,
  7636      'shared.js': `import { foo } from './foo'; export let bar = foo`,
  7637      'foo/index.js': `export let foo = () => {}`,
  7638      'foo/package.json': `{ "sideEffects": false }`,
  7639      'node.js': `
  7640        import path from 'path'
  7641        import url from 'url'
  7642        const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
  7643  
  7644        // Read the output files
  7645        import fs from 'fs'
  7646        const a = fs.readFileSync(path.join(__dirname, 'out', 'a.js'), 'utf8')
  7647        const chunk = fs.readFileSync(path.join(__dirname, 'out', 'chunk.js'), 'utf8')
  7648  
  7649        // Make sure the two output files don't import each other
  7650        import assert from 'assert'
  7651        assert.notStrictEqual(chunk.includes('a.js'), a.includes('chunk.js'), 'chunks must not import each other')
  7652      `,
  7653    }),
  7654    test(['entry.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7655      'entry.js': `await import('./a'); await import('./b')`,
  7656      'a.js': `import { bar } from './shared'; bar()`,
  7657      'b.js': `import './shared'`,
  7658      'shared.js': `import { foo } from './foo'; export let bar = foo`,
  7659      'foo/index.js': `export let foo = () => {}`,
  7660      'foo/package.json': `{ "sideEffects": false }`,
  7661      'node.js': `
  7662        // This must not crash
  7663        import './out/entry.js'
  7664      `,
  7665    }),
  7666  
  7667    // Code splitting where only one entry point uses the runtime
  7668    // https://github.com/evanw/esbuild/issues/1123
  7669    test(['a.js', 'b.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7670      'a.js': `
  7671        import * as foo from './shared'
  7672        export default foo
  7673      `,
  7674      'b.js': `
  7675        import {bar} from './shared'
  7676        export default bar
  7677      `,
  7678      'shared.js': `
  7679        export function foo() {
  7680          return 'foo'
  7681        }
  7682        export function bar() {
  7683          return 'bar'
  7684        }
  7685      `,
  7686      'node.js': `
  7687        import a from './out/a.js'
  7688        import b from './out/b.js'
  7689        if (a.foo() !== 'foo') throw 'fail'
  7690        if (b() !== 'bar') throw 'fail'
  7691      `,
  7692    }),
  7693  
  7694    // Code splitting with a dynamic import that imports a CSS file
  7695    // https://github.com/evanw/esbuild/issues/1125
  7696    test(['parent.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7697      'parent.js': `
  7698        // This should import the primary JS chunk, not the secondary CSS chunk
  7699        await import('./child')
  7700      `,
  7701      'child.js': `
  7702        import './foo.css'
  7703      `,
  7704      'foo.css': `
  7705        body {
  7706          color: black;
  7707        }
  7708      `,
  7709      'node.js': `
  7710        import './out/parent.js'
  7711      `,
  7712    }),
  7713  
  7714    // Code splitting with an entry point that exports two different
  7715    // symbols with the same original name (minified and not minified)
  7716    // https://github.com/evanw/esbuild/issues/1201
  7717    test(['entry1.js', 'entry2.js', '--outdir=out', '--splitting', '--format=esm', '--bundle'], {
  7718      'test1.js': `export const sameName = { test: 1 }`,
  7719      'test2.js': `export const sameName = { test: 2 }`,
  7720      'entry1.js': `
  7721        export { sameName } from './test1.js'
  7722        export { sameName as renameVar } from './test2.js'
  7723      `,
  7724      'entry2.js': `export * from './entry1.js'`,
  7725      'node.js': `
  7726        import { sameName as a, renameVar as b } from './out/entry1.js'
  7727        import { sameName as c, renameVar as d } from './out/entry2.js'
  7728        if (a.test !== 1 || b.test !== 2 || c.test !== 1 || d.test !== 2) throw 'fail'
  7729      `,
  7730    }),
  7731    test(['entry1.js', 'entry2.js', '--outdir=out', '--splitting', '--format=esm', '--bundle', '--minify'], {
  7732      'test1.js': `export const sameName = { test: 1 }`,
  7733      'test2.js': `export const sameName = { test: 2 }`,
  7734      'entry1.js': `
  7735        export { sameName } from './test1.js'
  7736        export { sameName as renameVar } from './test2.js'
  7737      `,
  7738      'entry2.js': `export * from './entry1.js'`,
  7739      'node.js': `
  7740        import { sameName as a, renameVar as b } from './out/entry1.js'
  7741        import { sameName as c, renameVar as d } from './out/entry2.js'
  7742        if (a.test !== 1 || b.test !== 2 || c.test !== 1 || d.test !== 2) throw 'fail'
  7743      `,
  7744    }),
  7745  
  7746    // https://github.com/evanw/esbuild/issues/1252
  7747    test(['client.js', 'utilities.js', '--splitting', '--bundle', '--format=esm', '--outdir=out'], {
  7748      'client.js': `export { Observable } from './utilities'`,
  7749      'utilities.js': `export { Observable } from './observable'`,
  7750      'observable.js': `
  7751        import Observable from './zen-observable'
  7752        export { Observable }
  7753      `,
  7754      'zen-observable.js': `module.exports = 123`,
  7755      'node.js': `
  7756        import {Observable as x} from './out/client.js'
  7757        import {Observable as y} from './out/utilities.js'
  7758        if (x !== 123 || y !== 123) throw 'fail'
  7759      `,
  7760    })
  7761  )
  7762  
  7763  // Test the binary loader
  7764  for (const length of [0, 1, 2, 3, 4, 5, 6, 7, 8, 256]) {
  7765    const code = `
  7766      import bytes from './data.bin'
  7767      if (!(bytes instanceof Uint8Array)) throw 'not Uint8Array'
  7768      if (bytes.length !== ${length}) throw 'Uint8Array.length !== ${length}'
  7769      if (bytes.buffer.byteLength !== ${length}) throw 'ArrayBuffer.byteLength !== ${length}'
  7770      for (let i = 0; i < ${length}; i++) if (bytes[i] !== (i ^ 0x55)) throw 'bad element ' + i
  7771    `
  7772    const data = Buffer.from([...' '.repeat(length)].map((_, i) => i ^ 0x55))
  7773    tests.push(
  7774      test(['entry.js', '--bundle', '--outfile=node.js', '--loader:.bin=binary', '--platform=browser'], {
  7775        'entry.js': code,
  7776        'data.bin': data,
  7777      }),
  7778      test(['entry.js', '--bundle', '--outfile=node.js', '--loader:.bin=binary', '--platform=node'], {
  7779        'entry.js': code,
  7780        'data.bin': data,
  7781      }),
  7782    )
  7783  }
  7784  
  7785  // Test file handle errors other than ENOENT
  7786  {
  7787    const errorText = process.platform === 'win32' ? 'Incorrect function.' : 'is a directory';
  7788    tests.push(
  7789      test(['src/entry.js', '--bundle', '--outfile=node.js', '--sourcemap'], {
  7790        'src/entry.js': `
  7791          //# sourceMappingURL=entry.js.map
  7792        `,
  7793        'src/entry.js.map/x': ``,
  7794      }, {
  7795        expectedStderr: `▲ [WARNING] Cannot read file "src/entry.js.map": ${errorText} [missing-source-map]
  7796  
  7797      src/entry.js:2:29:
  7798        2 │         //# sourceMappingURL=entry.js.map
  7799          ╵                              ~~~~~~~~~~~~
  7800  
  7801  `,
  7802      }),
  7803      test(['src/entry.js', '--bundle', '--outfile=node.js'], {
  7804        'src/entry.js': ``,
  7805        'src/tsconfig.json': `{"extends": "./base.json"}`,
  7806        'src/base.json/x': ``,
  7807      }, {
  7808        expectedStderr: `${errorIcon} [ERROR] Cannot read file "src/base.json": ${errorText}
  7809  
  7810      src/tsconfig.json:1:12:
  7811        1 │ {"extends": "./base.json"}
  7812          ╵             ~~~~~~~~~~~~~
  7813  
  7814  `,
  7815      }),
  7816      test(['src/entry.js', '--bundle', '--outfile=node.js'], {
  7817        'src/entry.js': ``,
  7818        'src/tsconfig.json': `{"extends": "foo"}`,
  7819        'node_modules/foo/tsconfig.json/x': ``,
  7820      }, {
  7821        expectedStderr: `▲ [WARNING] Cannot find base config file "foo" [tsconfig.json]
  7822  
  7823      src/tsconfig.json:1:12:
  7824        1 │ {"extends": "foo"}
  7825          ╵             ~~~~~
  7826  
  7827  `,
  7828      }),
  7829      test(['src/entry.js', '--bundle', '--outfile=node.js'], {
  7830        'src/entry.js': ``,
  7831  
  7832        // These missing directories shouldn't cause any errors on Windows
  7833        'package.json': `{
  7834          "main": "dist/cjs/index.js",
  7835          "module": "dist/esm/index.js"
  7836        }`,
  7837      }),
  7838      test(['src/entry.js', '--bundle', '--outfile=node.js'], {
  7839        'src/entry.js': ``,
  7840        'src/tsconfig.json': `{"extends": "./lib"}`,
  7841        'src/lib.json': `{"compilerOptions": {"target": "1"}}`, // We should get a warning about this file
  7842        'src/lib/index.json': `{"compilerOptions": {"target": "2"}}`, // Not about this file
  7843      }, {
  7844        expectedStderr: `▲ [WARNING] Unrecognized target environment "1" [tsconfig.json]
  7845  
  7846      src/lib.json:1:31:
  7847        1 │ {"compilerOptions": {"target": "1"}}
  7848          ╵                                ~~~
  7849  
  7850  `,
  7851      }),
  7852    )
  7853  }
  7854  
  7855  // Test a special-case error message for people trying to use "'--" on Windows
  7856  tests.push(
  7857    test(['in.js', `'--define:process.env.NODE_ENV="production"'`], {
  7858      'in.js': ``,
  7859    }, {
  7860      expectedStderr: `${errorIcon} [ERROR] Unexpected single quote character before flag: '--define:process.env.NODE_ENV="production"'
  7861  
  7862    This typically happens when attempting to use single quotes to quote arguments with a shell that doesn't recognize single quotes. `+
  7863        `Try using double quote characters to quote arguments instead.
  7864  
  7865  `,
  7866    }),
  7867  )
  7868  
  7869  // Test injecting banner and footer
  7870  tests.push(
  7871    test(['in.js', '--outfile=node.js', '--banner:js=const bannerDefined = true;'], {
  7872      'in.js': `if (!bannerDefined) throw 'fail'`
  7873    }),
  7874    test(['in.js', '--outfile=node.js', '--footer:js=function footer() { }'], {
  7875      'in.js': `footer()`
  7876    }),
  7877    test(['a.js', 'b.js', '--outdir=out', '--bundle', '--format=cjs', '--banner:js=const bannerDefined = true;', '--footer:js=function footer() { }'], {
  7878      'a.js': `
  7879        module.exports = { banner: bannerDefined, footer };
  7880      `,
  7881      'b.js': `
  7882        module.exports = { banner: bannerDefined, footer };
  7883      `,
  7884      'node.js': `
  7885        const a = require('./out/a');
  7886        const b = require('./out/b');
  7887  
  7888        if (!a.banner || !b.banner) throw 'fail';
  7889        a.footer();
  7890        b.footer();
  7891      `
  7892    }),
  7893  )
  7894  
  7895  // Test "imports" and "exports" in package.json
  7896  for (const flags of [[], ['--bundle']]) {
  7897    tests.push(
  7898      // "imports"
  7899      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7900        'in.js': `import abc from '#pkg'; if (abc !== 123) throw 'fail'`,
  7901        'package.json': `{
  7902          "type": "module",
  7903          "imports": {
  7904            "#pkg": "./foo.js"
  7905          }
  7906        }`,
  7907        'foo.js': `export default 123`,
  7908      }),
  7909      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7910        'in.js': `import abc from '#pkg/bar.js'; if (abc !== 123) throw 'fail'`,
  7911        'package.json': `{
  7912          "type": "module",
  7913          "imports": {
  7914            "#pkg/*": "./foo/*"
  7915          }
  7916        }`,
  7917        'foo/bar.js': `export default 123`,
  7918      }),
  7919      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7920        'in.js': `import abc from '#pkg'; if (abc !== 123) throw 'fail'`,
  7921        'package.json': `{
  7922          "type": "module",
  7923          "imports": {
  7924            "#pkg": {
  7925              "import": "./yes.js",
  7926              "default": "./no.js"
  7927            }
  7928          }
  7929        }`,
  7930        'yes.js': `export default 123`,
  7931      }),
  7932      test(['in.js', '--outfile=node.js', '--format=cjs'].concat(flags), {
  7933        'in.js': `const abc = require('#pkg'); if (abc !== 123) throw 'fail'`,
  7934        'package.json': `{
  7935          "type": "commonjs",
  7936          "imports": {
  7937            "#pkg": {
  7938              "require": "./yes.js",
  7939              "default": "./no.js"
  7940            }
  7941          }
  7942        }`,
  7943        'yes.js': `module.exports = 123`,
  7944      }),
  7945  
  7946      // "exports"
  7947      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7948        'in.js': `import abc from 'pkg'; if (abc !== 123) throw 'fail'`,
  7949        'package.json': `{ "type": "module" }`,
  7950        'node_modules/pkg/subdir/foo.js': `export default 123`,
  7951        'node_modules/pkg/package.json': `{
  7952          "type": "module",
  7953          "exports": {
  7954            ".": "./subdir/foo.js"
  7955          }
  7956        }`,
  7957      }),
  7958      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7959        'in.js': `import abc from 'pkg'; if (abc !== 123) throw 'fail'`,
  7960        'package.json': `{ "type": "module" }`,
  7961        'node_modules/pkg/subdir/foo.js': `export default 123`,
  7962        'node_modules/pkg/package.json': `{
  7963          "type": "module",
  7964          "exports": {
  7965            ".": {
  7966              "default": "./subdir/foo.js"
  7967            }
  7968          }
  7969        }`,
  7970      }),
  7971      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7972        'in.js': `import abc from 'pkg'; if (abc !== 123) throw 'fail'`,
  7973        'package.json': `{ "type": "module" }`,
  7974        'node_modules/pkg/subdir/foo.js': `export default 123`,
  7975        'node_modules/pkg/package.json': `{
  7976          "type": "module",
  7977          "exports": {
  7978            "default": "./subdir/foo.js"
  7979          }
  7980        }`,
  7981      }),
  7982      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7983        'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`,
  7984        'package.json': `{ "type": "module" }`,
  7985        'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  7986        'node_modules/@scope/pkg/package.json': `{
  7987          "type": "module",
  7988          "exports": {
  7989            ".": "./subdir/foo.js"
  7990          }
  7991        }`,
  7992      }),
  7993      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  7994        'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`,
  7995        'package.json': `{ "type": "module" }`,
  7996        'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  7997        'node_modules/@scope/pkg/package.json': `{
  7998          "type": "module",
  7999          "exports": {
  8000            ".": {
  8001              "default": "./subdir/foo.js"
  8002            }
  8003          }
  8004        }`,
  8005      }),
  8006      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8007        'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`,
  8008        'package.json': `{ "type": "module" }`,
  8009        'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  8010        'node_modules/@scope/pkg/package.json': `{
  8011          "type": "module",
  8012          "exports": {
  8013            "default": "./subdir/foo.js"
  8014          }
  8015        }`,
  8016      }),
  8017      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8018        'in.js': `import abc from 'pkg/dirwhat'; if (abc !== 123) throw 'fail'`,
  8019        'package.json': `{ "type": "module" }`,
  8020        'node_modules/pkg/sub/what/dirwhat/foo.js': `export default 123`,
  8021        'node_modules/pkg/package.json': `{
  8022          "type": "module",
  8023          "exports": {
  8024            "./di*": "./nope.js",
  8025            "./dir*": "./sub/*/dir*/foo.js",
  8026            "./long*": "./nope.js",
  8027            "./d*": "./nope.js"
  8028          }
  8029        }`,
  8030      }),
  8031      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8032        'in.js': `import abc from 'pkg/foo'; if (abc !== 123) throw 'fail'`,
  8033        'package.json': `{ "type": "module" }`,
  8034        'node_modules/pkg/yes.js': `export default 123`,
  8035        'node_modules/pkg/package.json': `{
  8036          "type": "module",
  8037          "exports": {
  8038            "./foo": [
  8039              { "unused": "./no.js" },
  8040              "./yes.js"
  8041            ]
  8042          }
  8043        }`,
  8044      }),
  8045      test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8046        'in.js': `import abc from 'pkg/foo'; if (abc !== 123) throw 'fail'`,
  8047        'package.json': `{ "type": "module" }`,
  8048        'node_modules/pkg/yes.js': `export default 123`,
  8049        'node_modules/pkg/package.json': `{
  8050          "type": "module",
  8051          "exports": {
  8052            "./foo": [
  8053              { "default": "./yes.js" },
  8054              "./no.js"
  8055            ]
  8056          }
  8057        }`,
  8058      }),
  8059      test(['in.js', '--outfile=node.js', '--bundle', '--platform=browser'].concat(flags), {
  8060        'in.js': `import abc from 'pkg'; if (abc !== 'module') throw 'fail'`,
  8061        'node_modules/pkg/default.js': `module.exports = 'default'`,
  8062        'node_modules/pkg/module.js': `export default 'module'`,
  8063        'node_modules/pkg/package.json': `{
  8064          "exports": {
  8065            ".": {
  8066              "module": "./module.js",
  8067              "default": "./default.js"
  8068            }
  8069          }
  8070        }`,
  8071      }),
  8072      test(['in.js', '--outfile=node.js', '--bundle', '--platform=node'].concat(flags), {
  8073        'in.js': `import abc from 'pkg'; if (abc !== 'module') throw 'fail'`,
  8074        'node_modules/pkg/default.js': `module.exports = 'default'`,
  8075        'node_modules/pkg/module.js': `export default 'module'`,
  8076        'node_modules/pkg/package.json': `{
  8077          "exports": {
  8078            ".": {
  8079              "module": "./module.js",
  8080              "default": "./default.js"
  8081            }
  8082          }
  8083        }`,
  8084      }),
  8085      test(['in.js', '--outfile=node.js', '--bundle', '--platform=neutral'].concat(flags), {
  8086        'in.js': `import abc from 'pkg'; if (abc !== 'default') throw 'fail'`,
  8087        'node_modules/pkg/default.js': `module.exports = 'default'`,
  8088        'node_modules/pkg/module.js': `export default 'module'`,
  8089        'node_modules/pkg/package.json': `{
  8090          "exports": {
  8091            ".": {
  8092              "module": "./module.js",
  8093              "default": "./default.js"
  8094            }
  8095          }
  8096        }`,
  8097      }),
  8098      test(['in.js', '--outfile=node.js', '--bundle', '--conditions='].concat(flags), {
  8099        'in.js': `import abc from 'pkg'; if (abc !== 'default') throw 'fail'`,
  8100        'node_modules/pkg/default.js': `module.exports = 'default'`,
  8101        'node_modules/pkg/module.js': `export default 'module'`,
  8102        'node_modules/pkg/package.json': `{
  8103          "exports": {
  8104            ".": {
  8105              "module": "./module.js",
  8106              "default": "./default.js"
  8107            }
  8108          }
  8109        }`,
  8110      }),
  8111  
  8112      // This is an edge case for extensionless files. The file should be treated
  8113      // as CommonJS even though package.json says "type": "module" because that
  8114      // only applies to ".js" files in node, not to all JavaScript files.
  8115      test(['in.js', '--outfile=node.js', '--bundle'], {
  8116        'in.js': `
  8117          const fn = require('yargs/yargs')
  8118          if (fn() !== 123) throw 'fail'
  8119        `,
  8120        'node_modules/yargs/package.json': `{
  8121          "main": "./index.cjs",
  8122          "exports": {
  8123            "./package.json": "./package.json",
  8124            ".": [
  8125              {
  8126                "import": "./index.mjs",
  8127                "require": "./index.cjs"
  8128              },
  8129              "./index.cjs"
  8130            ],
  8131            "./yargs": [
  8132              {
  8133                "import": "./yargs.mjs",
  8134                "require": "./yargs"
  8135              },
  8136              "./yargs"
  8137            ]
  8138          },
  8139          "type": "module",
  8140          "module": "./index.mjs"
  8141        }`,
  8142        'node_modules/yargs/index.cjs': ``,
  8143        'node_modules/yargs/index.mjs': ``,
  8144        'node_modules/yargs/yargs.mjs': ``,
  8145        'node_modules/yargs/yargs': `
  8146          module.exports = function() {
  8147            return 123
  8148          }
  8149        `,
  8150      }),
  8151    )
  8152  
  8153    // Node 17+ deliberately broke backward compatibility with packages using mappings
  8154    // ending in "/". See https://github.com/nodejs/node/pull/40121 for more info.
  8155    if (flags.length === 0 && nodeMajorVersion >= 17) {
  8156      console.log(`Skipping tests with path mappings ending in "/" since you are running node 17+`)
  8157    } else {
  8158      tests.push(
  8159        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8160          'in.js': `import abc from '#pkg/bar.js'; if (abc !== 123) throw 'fail'`,
  8161          'package.json': `{
  8162            "type": "module",
  8163            "imports": {
  8164              "#pkg/": "./foo/"
  8165            }
  8166          }`,
  8167          'foo/bar.js': `export default 123`,
  8168        }),
  8169        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8170          'in.js': `import abc from 'pkg/foo.js'; if (abc !== 123) throw 'fail'`,
  8171          'package.json': `{ "type": "module" }`,
  8172          'node_modules/pkg/subdir/foo.js': `export default 123`,
  8173          'node_modules/pkg/package.json': `{
  8174            "type": "module",
  8175            "exports": {
  8176              "./": "./subdir/"
  8177            }
  8178          }`,
  8179        }),
  8180        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8181          'in.js': `import abc from 'pkg/foo.js'; if (abc !== 123) throw 'fail'`,
  8182          'package.json': `{ "type": "module" }`,
  8183          'node_modules/pkg/subdir/foo.js': `export default 123`,
  8184          'node_modules/pkg/package.json': `{
  8185            "type": "module",
  8186            "exports": {
  8187              "./": {
  8188                "default": "./subdir/"
  8189              }
  8190            }
  8191          }`,
  8192        }),
  8193        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8194          'in.js': `import abc from 'pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`,
  8195          'package.json': `{ "type": "module" }`,
  8196          'node_modules/pkg/subdir/foo.js': `export default 123`,
  8197          'node_modules/pkg/package.json': `{
  8198            "type": "module",
  8199            "exports": {
  8200              "./dir/": "./subdir/"
  8201            }
  8202          }`,
  8203        }),
  8204        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8205          'in.js': `import abc from 'pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`,
  8206          'package.json': `{ "type": "module" }`,
  8207          'node_modules/pkg/subdir/foo.js': `export default 123`,
  8208          'node_modules/pkg/package.json': `{
  8209            "type": "module",
  8210            "exports": {
  8211              "./dir/": {
  8212                "default": "./subdir/"
  8213              }
  8214            }
  8215          }`,
  8216        }),
  8217        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8218          'in.js': `import abc from '@scope/pkg/foo.js'; if (abc !== 123) throw 'fail'`,
  8219          'package.json': `{ "type": "module" }`,
  8220          'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  8221          'node_modules/@scope/pkg/package.json': `{
  8222            "type": "module",
  8223            "exports": {
  8224              "./": "./subdir/"
  8225            }
  8226          }`,
  8227        }),
  8228        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8229          'in.js': `import abc from '@scope/pkg/foo.js'; if (abc !== 123) throw 'fail'`,
  8230          'package.json': `{ "type": "module" }`,
  8231          'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  8232          'node_modules/@scope/pkg/package.json': `{
  8233            "type": "module",
  8234            "exports": {
  8235              "./": {
  8236                "default": "./subdir/"
  8237              }
  8238            }
  8239          }`,
  8240        }),
  8241        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8242          'in.js': `import abc from '@scope/pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`,
  8243          'package.json': `{ "type": "module" }`,
  8244          'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  8245          'node_modules/@scope/pkg/package.json': `{
  8246            "type": "module",
  8247            "exports": {
  8248              "./dir/": "./subdir/"
  8249            }
  8250          }`,
  8251        }),
  8252        test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), {
  8253          'in.js': `import abc from '@scope/pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`,
  8254          'package.json': `{ "type": "module" }`,
  8255          'node_modules/@scope/pkg/subdir/foo.js': `export default 123`,
  8256          'node_modules/@scope/pkg/package.json': `{
  8257            "type": "module",
  8258            "exports": {
  8259              "./dir/": {
  8260                "default": "./subdir/"
  8261              }
  8262            }
  8263          }`,
  8264        }),
  8265        test(['in.js', '--outfile=node.js', '--format=cjs'].concat(flags), {
  8266          'in.js': `const abc = require('pkg/dir/test'); if (abc !== 123) throw 'fail'`,
  8267          'package.json': `{ "type": "commonjs" }`,
  8268          'node_modules/pkg/sub/test.js': `module.exports = 123`,
  8269          'node_modules/pkg/package.json': `{
  8270            "exports": {
  8271              "./dir/": "./sub/"
  8272            }
  8273          }`,
  8274        }),
  8275        test(['in.js', '--outfile=node.js', '--format=cjs'].concat(flags), {
  8276          'in.js': `const abc = require('pkg/dir/test'); if (abc !== 123) throw 'fail'`,
  8277          'package.json': `{ "type": "commonjs" }`,
  8278          'node_modules/pkg/sub/test/index.js': `module.exports = 123`,
  8279          'node_modules/pkg/package.json': `{
  8280            "exports": {
  8281              "./dir/": "./sub/"
  8282            }
  8283          }`,
  8284        }),
  8285      )
  8286    }
  8287  }
  8288  
  8289  // Top-level await tests
  8290  tests.push(
  8291    test(['in.js', '--outdir=out', '--format=esm', '--bundle'], {
  8292      'in.js': `
  8293        function foo() {
  8294          globalThis.tlaTrace.push(2)
  8295          return import('./a.js')
  8296        }
  8297  
  8298        globalThis.tlaTrace = []
  8299        globalThis.tlaTrace.push(1)
  8300        const it = (await foo()).default
  8301        globalThis.tlaTrace.push(6)
  8302        if (it !== 123 || globalThis.tlaTrace.join(',') !== '1,2,3,4,5,6') throw 'fail'
  8303      `,
  8304      'a.js': `
  8305        globalThis.tlaTrace.push(5)
  8306        export { default } from './b.js'
  8307      `,
  8308      'b.js': `
  8309        globalThis.tlaTrace.push(3)
  8310        export default await Promise.resolve(123)
  8311        globalThis.tlaTrace.push(4)
  8312      `,
  8313      'node.js': `
  8314        import './out/in.js'
  8315      `,
  8316    }),
  8317  )
  8318  
  8319  // Test the alias feature
  8320  tests.push(
  8321    test(['in.js', '--outfile=node.js', '--bundle', '--alias:foo=./bar/baz'], {
  8322      'in.js': `import "foo"`,
  8323      'node_modules/foo/index.js': `test failure`,
  8324      'bar/baz.js': ``,
  8325    }),
  8326    test(['in.js', '--outfile=node.js', '--bundle', '--alias:foo=./bar/../baz'], {
  8327      'in.js': `import "foo"`,
  8328      'node_modules/foo/index.js': `test failure`,
  8329      'baz.js': ``,
  8330    }),
  8331    test(['in.js', '--outfile=node.js', '--bundle', '--alias:@scope=./bar'], {
  8332      'in.js': `import "@scope/foo"`,
  8333      'node_modules/@scope/foo/index.js': `test failure`,
  8334      'bar/foo.js': ``,
  8335    }),
  8336  )
  8337  
  8338  // Tests for CSS modules
  8339  tests.push(
  8340    test(['in.js', '--outfile=node.js', '--bundle', '--loader:.css=local-css'], {
  8341      'in.js': `
  8342        import * as ns from './styles.css'
  8343        if (ns.buton !== void 0) throw 'fail'
  8344      `,
  8345      'styles.css': `
  8346        .bu\\74 ton { color: red }
  8347      `,
  8348    }, {
  8349      expectedStderr: `▲ [WARNING] Import "buton" will always be undefined because there is no matching export in "styles.css" [import-is-undefined]
  8350  
  8351      in.js:3:13:
  8352        3 │       if (ns.buton !== void 0) throw 'fail'
  8353          │              ~~~~~
  8354          ╵              button
  8355  
  8356    Did you mean to import "button" instead?
  8357  
  8358      styles.css:2:7:
  8359        2 │       .bu\\74 ton { color: red }
  8360          ╵        ~~~~~~~~~
  8361  
  8362  `,
  8363    }),
  8364    test(['in.js', '--outfile=node.js', '--bundle'], {
  8365      'in.js': `
  8366        import * as foo_styles from "./foo.css"
  8367        import * as bar_styles from "./bar"
  8368        const { foo } = foo_styles
  8369        const { bar } = bar_styles
  8370        if (foo !== void 0) throw 'fail: foo=' + foo
  8371        if (bar !== void 0) throw 'fail: bar=' + bar
  8372      `,
  8373      'foo.css': `.foo { color: red }`,
  8374      'bar.css': `.bar { color: green }`,
  8375    }),
  8376    test(['in.js', '--outfile=node.js', '--bundle'], {
  8377      'in.js': `
  8378        import * as foo_styles from "./foo.module.css"
  8379        import * as bar_styles from "./bar.module"
  8380        const { foo } = foo_styles
  8381        const { bar } = bar_styles
  8382        if (foo !== 'foo_foo') throw 'fail: foo=' + foo
  8383        if (bar !== 'bar_bar') throw 'fail: bar=' + bar
  8384      `,
  8385      'foo.module.css': `.foo { color: red }`,
  8386      'bar.module.css': `.bar { color: green }`,
  8387    }),
  8388    test(['in.js', '--outfile=node.js', '--bundle', '--loader:.module.css=css'], {
  8389      'in.js': `
  8390        import * as foo_styles from "./foo.module.css"
  8391        import * as bar_styles from "./bar.module"
  8392        const { foo } = foo_styles
  8393        const { bar } = bar_styles
  8394        if (foo !== void 0) throw 'fail: foo=' + foo
  8395        if (bar !== void 0) throw 'fail: bar=' + bar
  8396      `,
  8397      'foo.module.css': `.foo { color: red }`,
  8398      'bar.module.css': `.bar { color: green }`,
  8399    }),
  8400  )
  8401  
  8402  // Test writing to stdout
  8403  tests.push(
  8404    // These should succeed
  8405    testStdout('exports.foo = 123', [], async (build) => {
  8406      const stdout = await build()
  8407      assert.strictEqual(stdout, `exports.foo = 123;\n`)
  8408    }),
  8409    testStdout('exports.foo = 123', ['--bundle', '--format=cjs'], async (build) => {
  8410      const stdout = await build()
  8411      assert.strictEqual(stdout, `// example.js\nexports.foo = 123;\n`)
  8412    }),
  8413    testStdout('exports.foo = 123', ['--sourcemap'], async (build) => {
  8414      const stdout = await build()
  8415      const start = `exports.foo = 123;\n//# sourceMappingURL=data:application/json;base64,`
  8416      assert(stdout.startsWith(start))
  8417      const json = JSON.parse(Buffer.from(stdout.slice(start.length), 'base64').toString())
  8418      assert.strictEqual(json.version, 3)
  8419      assert.deepStrictEqual(json.sources, ['example.js'])
  8420    }),
  8421    testStdout('exports.foo = 123', ['--bundle', '--format=cjs', '--sourcemap'], async (build) => {
  8422      const stdout = await build()
  8423      const start = `// example.js\nexports.foo = 123;\n//# sourceMappingURL=data:application/json;base64,`
  8424      assert(stdout.startsWith(start))
  8425      const json = JSON.parse(Buffer.from(stdout.slice(start.length), 'base64').toString())
  8426      assert.strictEqual(json.version, 3)
  8427      assert.deepStrictEqual(json.sources, ['example.js'])
  8428    }),
  8429    testStdout('stuff', ['--loader:.js=text'], async (build) => {
  8430      const stdout = await build()
  8431      assert.strictEqual(stdout, `module.exports = "stuff";\n`)
  8432    }),
  8433  
  8434    // These should fail
  8435    testStdout('exports.foo = 123', ['--metafile=graph.json'], async (build) => {
  8436      try { await build() } catch (e) { return }
  8437      throw new Error('Expected build failure for "--metafile"')
  8438    }),
  8439    testStdout('exports.foo = 123', ['--sourcemap=external'], async (build) => {
  8440      try { await build() } catch (e) { return }
  8441      throw new Error('Expected build failure for "--metafile"')
  8442    }),
  8443    testStdout('exports.foo = 123', ['--loader:.js=file'], async (build) => {
  8444      try { await build() } catch (e) { return }
  8445      throw new Error('Expected build failure for "--metafile"')
  8446    }),
  8447  )
  8448  
  8449  // Test for a Windows-specific issue where paths starting with "/" could be
  8450  // treated as relative paths, leading to inconvenient cross-platform failures:
  8451  // https://github.com/evanw/esbuild/issues/822
  8452  tests.push(
  8453    test(['in.js', '--bundle'], {
  8454      'in.js': `
  8455        import "/file.js"
  8456      `,
  8457      'file.js': `This file should not be imported on Windows`,
  8458    }, {
  8459      expectedStderr: `${errorIcon} [ERROR] Could not resolve "/file.js"
  8460  
  8461      in.js:2:13:
  8462        2 │       import "/file.js"
  8463          ╵              ~~~~~~~~~~
  8464  
  8465  `,
  8466    }),
  8467  )
  8468  
  8469  // Test that importing a path with the wrong case works ok. This is necessary
  8470  // to handle case-insensitive file systems.
  8471  if (process.platform === 'darwin' || process.platform === 'win32') {
  8472    tests.push(
  8473      test(['in.js', '--bundle', '--outfile=node.js'], {
  8474        'in.js': `
  8475          import x from "./File1.js"
  8476          import y from "./file2.js"
  8477          if (x !== 123 || y !== 234) throw 'fail'
  8478        `,
  8479        'file1.js': `export default 123`,
  8480        'File2.js': `export default 234`,
  8481      }, {
  8482        expectedStderr: `▲ [WARNING] Use "file1.js" instead of "File1.js" to avoid issues with case-sensitive file systems [different-path-case]
  8483  
  8484      in.js:2:22:
  8485        2 │         import x from "./File1.js"
  8486          ╵                       ~~~~~~~~~~~~
  8487  
  8488  ▲ [WARNING] Use "File2.js" instead of "file2.js" to avoid issues with case-sensitive file systems [different-path-case]
  8489  
  8490      in.js:3:22:
  8491        3 │         import y from "./file2.js"
  8492          ╵                       ~~~~~~~~~~~~
  8493  
  8494  `,
  8495      }),
  8496      test(['in.js', '--bundle', '--outfile=node.js'], {
  8497        'in.js': `
  8498          import x from "./Dir1/file.js"
  8499          import y from "./dir2/file.js"
  8500          if (x !== 123 || y !== 234) throw 'fail'
  8501        `,
  8502        'dir1/file.js': `export default 123`,
  8503        'Dir2/file.js': `export default 234`,
  8504      }),
  8505  
  8506      // Warn when importing something inside node_modules
  8507      test(['in.js', '--bundle', '--outfile=node.js'], {
  8508        'in.js': `
  8509          import x from "pkg/File1.js"
  8510          import y from "pkg/file2.js"
  8511          if (x !== 123 || y !== 234) throw 'fail'
  8512        `,
  8513        'node_modules/pkg/file1.js': `export default 123`,
  8514        'node_modules/pkg/File2.js': `export default 234`,
  8515      }, {
  8516        expectedStderr: `▲ [WARNING] Use "node_modules/pkg/file1.js" instead of "node_modules/pkg/File1.js" to avoid issues with case-sensitive file systems [different-path-case]
  8517  
  8518      in.js:2:22:
  8519        2 │         import x from "pkg/File1.js"
  8520          ╵                       ~~~~~~~~~~~~~~
  8521  
  8522  ▲ [WARNING] Use "node_modules/pkg/File2.js" instead of "node_modules/pkg/file2.js" to avoid issues with case-sensitive file systems [different-path-case]
  8523  
  8524      in.js:3:22:
  8525        3 │         import y from "pkg/file2.js"
  8526          ╵                       ~~~~~~~~~~~~~~
  8527  
  8528  `,
  8529      }),
  8530  
  8531      // Don't warn when the importer is inside node_modules
  8532      test(['in.js', '--bundle', '--outfile=node.js'], {
  8533        'in.js': `
  8534          import {x, y} from "pkg"
  8535          if (x !== 123 || y !== 234) throw 'fail'
  8536        `,
  8537        'node_modules/pkg/index.js': `
  8538          export {default as x} from "./File1.js"
  8539          export {default as y} from "./file2.js"
  8540        `,
  8541        'node_modules/pkg/file1.js': `export default 123`,
  8542        'node_modules/pkg/File2.js': `export default 234`,
  8543      }),
  8544    )
  8545  }
  8546  
  8547  // Test glob import behavior
  8548  for (const ext of ['.js', '.ts']) {
  8549    tests.push(
  8550      test(['./src/*' + ext, '--outdir=out', '--bundle', '--format=cjs'], {
  8551        'node.js': `
  8552          if (require('./out/a.js') !== 10) throw 'fail: a'
  8553          if (require('./out/b.js') !== 11) throw 'fail: b'
  8554          if (require('./out/c.js') !== 12) throw 'fail: c'
  8555        `,
  8556        ['src/a' + ext]: `module.exports = 10`,
  8557        ['src/b' + ext]: `module.exports = 11`,
  8558        ['src/c' + ext]: `module.exports = 12`,
  8559      }),
  8560      test(['in' + ext, '--outfile=node.js', '--bundle'], {
  8561        ['in' + ext]: `
  8562          for (let i = 0; i < 3; i++) {
  8563            const value = require('./' + i + '${ext}')
  8564            if (value !== i + 10) throw 'fail: ' + i
  8565          }
  8566        `,
  8567        ['0' + ext]: `module.exports = 10`,
  8568        ['1' + ext]: `module.exports = 11`,
  8569        ['2' + ext]: `module.exports = 12`,
  8570      }),
  8571      test(['in' + ext, '--outfile=node.js', '--bundle'], {
  8572        ['in' + ext]: `
  8573          for (let i = 0; i < 3; i++) {
  8574            const value = require(\`./\${i}${ext}\`)
  8575            if (value !== i + 10) throw 'fail: ' + i
  8576          }
  8577        `,
  8578        ['0' + ext]: `module.exports = 10`,
  8579        ['1' + ext]: `module.exports = 11`,
  8580        ['2' + ext]: `module.exports = 12`,
  8581      }),
  8582      test(['in' + ext, '--outfile=node.js', '--bundle'], {
  8583        ['in' + ext]: `
  8584          export let async = async () => {
  8585            for (let i = 0; i < 3; i++) {
  8586              const { default: value } = await import('./' + i + '${ext}')
  8587              if (value !== i + 10) throw 'fail: ' + i
  8588            }
  8589          }
  8590        `,
  8591        ['0' + ext]: `export default 10`,
  8592        ['1' + ext]: `export default 11`,
  8593        ['2' + ext]: `export default 12`,
  8594      }, { async: true }),
  8595      test(['in' + ext, '--outfile=node.js', '--bundle'], {
  8596        ['in' + ext]: `
  8597          export let async = async () => {
  8598            for (let i = 0; i < 3; i++) {
  8599              const { default: value } = await import(\`./\${i}${ext}\`)
  8600              if (value !== i + 10) throw 'fail: ' + i
  8601            }
  8602          }
  8603        `,
  8604        ['0' + ext]: `export default 10`,
  8605        ['1' + ext]: `export default 11`,
  8606        ['2' + ext]: `export default 12`,
  8607      }, { async: true }),
  8608    )
  8609  }
  8610  
  8611  // Test "using" declarations
  8612  for (const flags of [[], '--supported:async-await=false']) {
  8613    tests.push(
  8614      test(['in.js', '--outfile=node.js', '--supported:using=false'].concat(flags), {
  8615        'in.js': `
  8616          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8617          const log = []
  8618          {
  8619            using x = { [Symbol.dispose]() { log.push('x') } }
  8620            using y = { [Symbol.dispose]() { log.push('y') } }
  8621            using z1 = null
  8622            using z2 = undefined
  8623            try {
  8624              using no = 0
  8625            } catch {
  8626              log.push('no')
  8627            }
  8628            log.push('z')
  8629          }
  8630          if (log + '' !== 'no,z,y,x') throw 'fail: ' + log
  8631        `,
  8632      }),
  8633      test(['in.js', '--outfile=node.js', '--supported:using=false', '--format=esm'].concat(flags), {
  8634        'in.js': `
  8635          Symbol.asyncDispose ||= Symbol.for('Symbol.asyncDispose')
  8636          export let async = async () => {
  8637            const log = []
  8638            {
  8639              await using x = { [Symbol.asyncDispose]() {
  8640                log.push('x1')
  8641                Promise.resolve().then(() => log.push('x2'))
  8642                return Promise.resolve()
  8643              } }
  8644              await using y = { [Symbol.asyncDispose]() {
  8645                log.push('y1')
  8646                Promise.resolve().then(() => log.push('y2'))
  8647                return Promise.resolve()
  8648              } }
  8649              await using z1 = null
  8650              await using z2 = undefined
  8651              try {
  8652                await using no = 0
  8653              } catch {
  8654                log.push('no')
  8655              }
  8656              log.push('z')
  8657            }
  8658            if (log + '' !== 'no,z,y1,y2,x1,x2') throw 'fail: ' + log
  8659          }
  8660        `,
  8661      }, { async: true }),
  8662      test(['in.js', '--outfile=node.js', '--supported:using=false'].concat(flags), {
  8663        'in.js': `
  8664          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8665          const log = []
  8666          for (using x of [
  8667            { [Symbol.dispose]() { log.push('x') } },
  8668            null,
  8669            { [Symbol.dispose]() { log.push('y') } },
  8670            undefined,
  8671          ]) {
  8672            try {
  8673              using no = 0
  8674            } catch {
  8675              log.push('no')
  8676            }
  8677            log.push('z')
  8678          }
  8679          if (log + '' !== 'no,z,x,no,z,no,z,y,no,z') throw 'fail: ' + log
  8680        `,
  8681      }),
  8682      test(['in.js', '--outfile=node.js', '--supported:using=false', '--format=esm'].concat(flags), {
  8683        'in.js': `
  8684          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8685          Symbol.asyncDispose ||= Symbol.for('Symbol.asyncDispose')
  8686          export let async = async () => {
  8687            const log = []
  8688            for (await using x of [
  8689              { [Symbol.dispose]() { log.push('x') } },
  8690              null,
  8691              { [Symbol.asyncDispose]() {
  8692                log.push('y1')
  8693                Promise.resolve().then(() => log.push('y2'))
  8694                return Promise.resolve()
  8695              } },
  8696              undefined,
  8697            ]) {
  8698              try {
  8699                using no = 0
  8700              } catch {
  8701                log.push('no')
  8702              }
  8703              log.push('z')
  8704            }
  8705            if (log + '' !== 'no,z,x,no,z,no,z,y1,y2,no,z') throw 'fail: ' + log
  8706          }
  8707        `,
  8708      }, { async: true }),
  8709      test(['in.js', '--outfile=node.js', '--supported:using=false', '--format=esm'].concat(flags), {
  8710        'in.js': `
  8711          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8712          export let async = async () => {
  8713            const log = []
  8714            for await (using x of [
  8715              { [Symbol.dispose]() { log.push('x1') } },
  8716              Promise.resolve({ [Symbol.dispose]() { log.push('x2') } }),
  8717              null,
  8718              Promise.resolve(null),
  8719              undefined,
  8720              Promise.resolve(undefined),
  8721            ]) {
  8722              try {
  8723                using no = 0
  8724              } catch {
  8725                log.push('no')
  8726              }
  8727              log.push('z')
  8728            }
  8729            if (log + '' !== 'no,z,x1,no,z,x2,no,z,no,z,no,z,no,z') throw 'fail: ' + log
  8730          }
  8731        `,
  8732      }, { async: true }),
  8733      test(['in.js', '--outfile=node.js', '--supported:using=false', '--format=esm'].concat(flags), {
  8734        'in.js': `
  8735          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8736          Symbol.asyncDispose ||= Symbol.for('Symbol.asyncDispose')
  8737          export let async = async () => {
  8738            const log = []
  8739            for await (await using x of [
  8740              { [Symbol.dispose]() { log.push('x1') } },
  8741              Promise.resolve({ [Symbol.dispose]() { log.push('x2') } }),
  8742              { [Symbol.asyncDispose]() { log.push('y1') } },
  8743              Promise.resolve({ [Symbol.asyncDispose]() { log.push('y2') } }),
  8744              null,
  8745              Promise.resolve(null),
  8746              undefined,
  8747              Promise.resolve(undefined),
  8748            ]) {
  8749              try {
  8750                using no = 0
  8751              } catch {
  8752                log.push('no')
  8753              }
  8754              log.push('z')
  8755            }
  8756            if (log + '' !== 'no,z,x1,no,z,x2,no,z,y1,no,z,y2,no,z,no,z,no,z,no,z') throw 'fail: ' + log
  8757          }
  8758        `,
  8759      }, { async: true }),
  8760      test(['in.js', '--outfile=node.js', '--supported:using=false'].concat(flags), {
  8761        'in.js': `
  8762          Symbol.dispose ||= Symbol.for('Symbol.dispose')
  8763          class Foo { [Symbol.dispose]() { throw new Error('x') } }
  8764          try {
  8765            using x = new Foo
  8766            throw new Error('y')
  8767          } catch (err) {
  8768            var result = err
  8769          }
  8770          if (result.name !== 'SuppressedError') throw 'fail: SuppressedError'
  8771          if (result.error.message !== 'x') throw 'fail: x'
  8772          if (result.suppressed.message !== 'y') throw 'fail: y'
  8773          try {
  8774            using x = new Foo
  8775          } catch (err) {
  8776            var result = err
  8777          }
  8778          if (result.message !== 'x') throw 'fail: x (2)'
  8779        `,
  8780      }),
  8781      test(['in.js', '--outfile=node.js', '--supported:using=false', '--format=esm'].concat(flags), {
  8782        'in.js': `
  8783          Symbol.asyncDispose ||= Symbol.for('Symbol.asyncDispose')
  8784          class Foo { [Symbol.asyncDispose]() { throw new Error('x') } }
  8785          export let async = async () => {
  8786            try {
  8787              await using x = new Foo
  8788              throw new Error('y')
  8789            } catch (err) {
  8790              var result = err
  8791            }
  8792            if (result.name !== 'SuppressedError') throw 'fail: SuppressedError'
  8793            if (result.error.message !== 'x') throw 'fail: x'
  8794            if (result.suppressed.message !== 'y') throw 'fail: y'
  8795            try {
  8796              await using x = new Foo
  8797            } catch (err) {
  8798              var result = err
  8799            }
  8800            if (result.message !== 'x') throw 'fail: x (2)'
  8801          }
  8802        `,
  8803      }, { async: true }),
  8804    )
  8805  }
  8806  
  8807  // End-to-end watch mode tests
  8808  tests.push(
  8809    // Validate that the CLI watch mode correctly updates the metafile
  8810    testWatch({ metafile: true }, async ({ infile, outfile, metafile }) => {
  8811      await waitForCondition(
  8812        'initial build',
  8813        20,
  8814        () => fs.writeFile(infile, 'foo()'),
  8815        async () => {
  8816          assert.strictEqual(await fs.readFile(outfile, 'utf8'), 'foo();\n')
  8817          assert.strictEqual(JSON.parse(await fs.readFile(metafile, 'utf8')).inputs[path.basename(infile)].bytes, 5)
  8818        },
  8819      )
  8820  
  8821      await waitForCondition(
  8822        'subsequent build',
  8823        20,
  8824        () => fs.writeFile(infile, 'foo(123)'),
  8825        async () => {
  8826          assert.strictEqual(await fs.readFile(outfile, 'utf8'), 'foo(123);\n')
  8827          assert.strictEqual(JSON.parse(await fs.readFile(metafile, 'utf8')).inputs[path.basename(infile)].bytes, 8)
  8828        },
  8829      )
  8830    }),
  8831  
  8832    // Validate that the CLI watch mode correctly updates the mangle cache
  8833    testWatch({ args: ['--mangle-props=.'], mangleCache: true }, async ({ infile, outfile, mangleCache }) => {
  8834      await waitForCondition(
  8835        'initial build',
  8836        20,
  8837        () => fs.writeFile(infile, 'foo()'),
  8838        async () => {
  8839          assert.strictEqual(await fs.readFile(outfile, 'utf8'), 'foo();\n')
  8840          assert.strictEqual(await fs.readFile(mangleCache, 'utf8'), '{}\n')
  8841        },
  8842      )
  8843  
  8844      await waitForCondition(
  8845        'subsequent build',
  8846        20,
  8847        () => fs.writeFile(infile, 'foo(bar.baz)'),
  8848        async () => {
  8849          assert.strictEqual(await fs.readFile(outfile, 'utf8'), 'foo(bar.a);\n')
  8850          assert.strictEqual(await fs.readFile(mangleCache, 'utf8'), '{\n  "baz": "a"\n}\n')
  8851        },
  8852      )
  8853    }),
  8854  
  8855    // This tests that watch mode writes to stdout correctly
  8856    testWatchStdout([
  8857      {
  8858        input: 'console.log(1+2)',
  8859        stdout: ['console.log(1 + 2);'],
  8860        stderr: ['[watch] build finished, watching for changes...'],
  8861      },
  8862      {
  8863        input: 'console.log(2+3)',
  8864        stdout: ['console.log(2 + 3);'],
  8865        stderr: ['[watch] build started (change: "in.js")', '[watch] build finished'],
  8866      },
  8867      {
  8868        input: 'console.log(3+4)',
  8869        stdout: ['console.log(3 + 4);'],
  8870        stderr: ['[watch] build started (change: "in.js")', '[watch] build finished'],
  8871      },
  8872    ]),
  8873  )
  8874  
  8875  function waitForCondition(what, seconds, mutator, condition) {
  8876    return new Promise(async (resolve, reject) => {
  8877      const start = Date.now()
  8878      let e
  8879      try {
  8880        await mutator()
  8881        while (true) {
  8882          if (Date.now() - start > seconds * 1000) {
  8883            throw new Error(`Timeout of ${seconds} seconds waiting for ${what}` + (e ? `: ${e && e.message || e}` : ''))
  8884          }
  8885          await new Promise(r => setTimeout(r, 50))
  8886          try {
  8887            await condition()
  8888            break
  8889          } catch (err) {
  8890            e = err
  8891          }
  8892        }
  8893        resolve()
  8894      } catch (e) {
  8895        reject(e)
  8896      }
  8897    })
  8898  }
  8899  
  8900  function test(args, files, options) {
  8901    return async () => {
  8902      const hasBundle = args.includes('--bundle')
  8903      const hasIIFE = args.includes('--format=iife')
  8904      const hasCJS = args.includes('--format=cjs')
  8905      const hasESM = args.includes('--format=esm')
  8906      const formats = hasIIFE ? ['iife'] : hasESM ? ['esm'] : hasCJS || !hasBundle ? ['cjs'] : ['cjs', 'esm']
  8907      const expectedStderr = options && options.expectedStderr || '';
  8908  
  8909      // If the test doesn't specify a format, test both formats
  8910      for (const format of formats) {
  8911        const formatArg = `--format=${format}`
  8912        const logLevelArgs = args.some(arg => arg.startsWith('--log-level=')) ? [] : ['--log-level=warning']
  8913        const modifiedArgs = (!hasBundle || args.includes(formatArg) ? args : args.concat(formatArg)).concat(logLevelArgs)
  8914        const thisTestDir = path.join(testDir, '' + testCount++)
  8915        await fs.mkdir(thisTestDir, { recursive: true })
  8916  
  8917        try {
  8918          // Test setup
  8919          for (const file in files) {
  8920            const filePath = path.join(thisTestDir, file)
  8921            const contents = files[file]
  8922            await fs.mkdir(path.dirname(filePath), { recursive: true })
  8923  
  8924            // Optionally symlink the file if the test requests it
  8925            if (contents.symlink) await fs.symlink(contents.symlink.replace('TEST_DIR_ABS_PATH', thisTestDir), filePath)
  8926            else await fs.writeFile(filePath, contents)
  8927          }
  8928  
  8929          // Run esbuild
  8930          let stderr
  8931          if (options && options.cwd) {
  8932            // Use the shell to set the working directory instead of using node's
  8933            // "child_process" module. For some reason it looks like node doesn't
  8934            // handle symlinks correctly and some of these tests check esbuild's
  8935            // behavior in the presence of symlinks. Using the shell is the only
  8936            // way I could find to do this correctly.
  8937            const quote = arg => arg.replace(/([#!"$&'()*,:;<=>?@\[\\\]^`{|}])/g, '\\$1')
  8938            const cwd = path.join(thisTestDir, options.cwd)
  8939            const command = ['cd', quote(cwd), '&&', quote(esbuildPath)].concat(modifiedArgs.map(quote)).join(' ')
  8940            stderr = (await execAsync(command, { stdio: 'pipe' })).stderr
  8941          } else {
  8942            stderr = (await execFileAsync(esbuildPath, modifiedArgs, { cwd: thisTestDir, stdio: 'pipe' })).stderr
  8943          }
  8944          if (Array.isArray(expectedStderr)) {
  8945            // An array of possible outputs (due to log output order non-determinism)
  8946            if (!expectedStderr.includes(stderr))
  8947              assert.strictEqual(stderr, expectedStderr[0]);
  8948          } else {
  8949            assert.strictEqual(stderr, expectedStderr);
  8950          }
  8951  
  8952          // Run the resulting node.js file and make sure it exits cleanly. The
  8953          // use of "pathToFileURL" is a workaround for a problem where node
  8954          // only supports absolute paths on Unix-style systems, not on Windows.
  8955          // See https://github.com/nodejs/node/issues/31710 for more info.
  8956          const nodePath = path.join(thisTestDir, 'node')
  8957          const pjPath = path.join(thisTestDir, 'package.json')
  8958          const pjExists = await fs.stat(pjPath).then(() => true, () => false)
  8959          let testExports
  8960          switch (format) {
  8961            case 'cjs':
  8962            case 'iife':
  8963              if (!pjExists) await fs.writeFile(pjPath, '{"type": "commonjs"}')
  8964              testExports = (await import(url.pathToFileURL(`${nodePath}.js`))).default
  8965              break
  8966  
  8967            case 'esm':
  8968              if (!pjExists) await fs.writeFile(pjPath, '{"type": "module"}')
  8969              testExports = await import(url.pathToFileURL(`${nodePath}.js`))
  8970              break
  8971          }
  8972  
  8973          // If this is an async test, run the async part
  8974          if (options && options.async) {
  8975            if (!(testExports.async instanceof Function))
  8976              throw new Error('Expected async instanceof Function')
  8977            await testExports.async()
  8978          }
  8979  
  8980          // Clean up test output
  8981          removeRecursiveSync(thisTestDir)
  8982        }
  8983  
  8984        catch (e) {
  8985          if (e && e.stderr !== void 0) {
  8986            try {
  8987              if (Array.isArray(expectedStderr)) {
  8988                // An array of possible outputs (due to log output order non-determinism)
  8989                if (!expectedStderr.includes(e.stderr))
  8990                  assert.strictEqual(e.stderr, expectedStderr[0]);
  8991              } else {
  8992                assert.strictEqual(e.stderr, expectedStderr);
  8993              }
  8994  
  8995              // Clean up test output
  8996              removeRecursiveSync(thisTestDir)
  8997              continue;
  8998            } catch (e2) {
  8999              e = e2;
  9000            }
  9001          }
  9002          console.error(`❌ test failed: ${e && e.message || e}
  9003    dir: ${path.relative(dirname, thisTestDir)}
  9004    args: ${modifiedArgs.join(' ')}
  9005    files: ${Object.entries(files).map(([k, v]) => `\n    ${k}: ${JSON.stringify(v)}`).join('')}
  9006  `)
  9007          return false
  9008        }
  9009      }
  9010  
  9011      return true
  9012    }
  9013  }
  9014  
  9015  // There's a feature where bundling without "outfile" or "outdir" writes to stdout instead
  9016  function testStdout(input, args, callback) {
  9017    return async () => {
  9018      const thisTestDir = path.join(testDir, '' + testCount++)
  9019  
  9020      try {
  9021        await fs.mkdir(thisTestDir, { recursive: true })
  9022        const inputFile = path.join(thisTestDir, 'example.js')
  9023        await fs.writeFile(inputFile, input)
  9024  
  9025        // Run whatever check the caller is doing
  9026        await callback(async () => {
  9027          const { stdout } = await execFileAsync(
  9028            esbuildPath, [inputFile, '--log-level=warning'].concat(args), { cwd: thisTestDir, stdio: 'pipe' })
  9029          return stdout
  9030        })
  9031  
  9032        // Clean up test output
  9033        removeRecursiveSync(thisTestDir)
  9034      } catch (e) {
  9035        console.error(`❌ test failed: ${e && e.message || e}
  9036    dir: ${path.relative(dirname, thisTestDir)}`)
  9037        return false
  9038      }
  9039  
  9040      return true
  9041    }
  9042  }
  9043  
  9044  function testWatch(options, callback) {
  9045    return async () => {
  9046      const thisTestDir = path.join(testDir, '' + testCount++)
  9047      const infile = path.join(thisTestDir, 'in.js')
  9048      const outdir = path.join(thisTestDir, 'out')
  9049      const outfile = path.join(outdir, path.basename(infile))
  9050      const args = ['--watch=forever', infile, '--outdir=' + outdir, '--color'].concat(options.args || [])
  9051      let metafile
  9052      let mangleCache
  9053  
  9054      if (options.metafile) {
  9055        metafile = path.join(thisTestDir, 'meta.json')
  9056        args.push('--metafile=' + metafile)
  9057      }
  9058  
  9059      if (options.mangleCache) {
  9060        mangleCache = path.join(thisTestDir, 'mangle.json')
  9061        args.push('--mangle-cache=' + mangleCache)
  9062      }
  9063  
  9064      let stderrPromise
  9065      try {
  9066        await fs.mkdir(thisTestDir, { recursive: true })
  9067        const maxSeconds = 60
  9068  
  9069        // Start the child
  9070        const child = childProcess.spawn(esbuildPath, args, {
  9071          cwd: thisTestDir,
  9072          stdio: ['inherit', 'inherit', 'pipe'],
  9073          timeout: maxSeconds * 1000,
  9074        })
  9075  
  9076        // Make sure the child is always killed
  9077        try {
  9078          // Buffer stderr in case we need it
  9079          const stderr = []
  9080          child.stderr.on('data', data => stderr.push(data))
  9081          const exitPromise = new Promise((_, reject) => {
  9082            child.on('close', code => reject(new Error(`Child "esbuild" process exited with code ${code}`)))
  9083          })
  9084          stderrPromise = new Promise(resolve => {
  9085            child.stderr.on('end', () => resolve(Buffer.concat(stderr).toString()))
  9086          })
  9087  
  9088          // Run whatever check the caller is doing
  9089          let timeout
  9090          await Promise.race([
  9091            new Promise((_, reject) => {
  9092              timeout = setTimeout(() => reject(new Error(`Timeout of ${maxSeconds} seconds exceeded`)), maxSeconds * 1000)
  9093            }),
  9094            exitPromise,
  9095            callback({
  9096              infile,
  9097              outfile,
  9098              metafile,
  9099              mangleCache,
  9100            }),
  9101          ])
  9102          clearTimeout(timeout)
  9103  
  9104          // Clean up test output
  9105          removeRecursiveSync(thisTestDir)
  9106        } finally {
  9107          child.kill()
  9108        }
  9109      } catch (e) {
  9110        let stderr = stderrPromise ? '\n  stderr:' + ('\n' + await stderrPromise).split('\n').join('\n    ') : ''
  9111        console.error(`❌ test failed: ${e && e.message || e}
  9112    dir: ${path.relative(dirname, thisTestDir)}
  9113    args: ${args.join(' ')}` + stderr)
  9114        return false
  9115      }
  9116  
  9117      return true
  9118    }
  9119  }
  9120  
  9121  function testWatchStdout(sequence) {
  9122    return async () => {
  9123      const thisTestDir = path.join(testDir, '' + testCount++)
  9124      const infile = path.join(thisTestDir, 'in.js')
  9125      const args = ['--watch=forever', infile]
  9126  
  9127      try {
  9128        await fs.mkdir(thisTestDir, { recursive: true })
  9129        await fs.writeFile(infile, sequence[0].input)
  9130        const maxSeconds = 60
  9131  
  9132        // Start the child
  9133        const child = childProcess.spawn(esbuildPath, args, {
  9134          cwd: thisTestDir,
  9135          stdio: ['inherit', 'pipe', 'pipe'],
  9136          timeout: maxSeconds * 1000,
  9137        })
  9138  
  9139        // Make sure the child is always killed
  9140        try {
  9141          for (const { input, stdout: expectedStdout, stderr: expectedStderr } of sequence) {
  9142            let totalStdout = ''
  9143            let totalStderr = ''
  9144            let stdoutBuffer = ''
  9145            let stderrBuffer = ''
  9146            const onstdout = data => {
  9147              totalStdout += data
  9148              stdoutBuffer += data
  9149              check()
  9150            }
  9151            const onstderr = data => {
  9152              totalStderr += data
  9153              stderrBuffer += data
  9154              check()
  9155            }
  9156            let check = () => { }
  9157  
  9158            child.stdout.on('data', onstdout)
  9159            child.stderr.on('data', onstderr)
  9160  
  9161            await new Promise((resolve, reject) => {
  9162              const seconds = 30
  9163              const timeout = setTimeout(() => reject(new Error(
  9164                `Watch mode + stdout test failed to match expected output after ${seconds} seconds
  9165    input: ${JSON.stringify(input)}
  9166    stdout: ${JSON.stringify(totalStdout)}
  9167    stderr: ${JSON.stringify(totalStderr)}
  9168  `)), seconds * 1000)
  9169  
  9170              check = () => {
  9171                let index
  9172  
  9173                while ((index = stdoutBuffer.indexOf('\n')) >= 0) {
  9174                  const line = stdoutBuffer.slice(0, index)
  9175                  stdoutBuffer = stdoutBuffer.slice(index + 1)
  9176                  if (line === expectedStdout[0]) expectedStdout.shift()
  9177                }
  9178  
  9179                while ((index = stderrBuffer.indexOf('\n')) >= 0) {
  9180                  const line = stderrBuffer.slice(0, index)
  9181                  stderrBuffer = stderrBuffer.slice(index + 1)
  9182                  if (line === expectedStderr[0]) expectedStderr.shift()
  9183                }
  9184  
  9185                if (!expectedStdout.length && !expectedStderr.length) {
  9186                  clearTimeout(timeout)
  9187                  resolve()
  9188                }
  9189              }
  9190  
  9191              writeFileAtomic(infile, input)
  9192            })
  9193  
  9194            child.stdout.off('data', onstdout)
  9195            child.stderr.off('data', onstderr)
  9196          }
  9197        } finally {
  9198          child.kill()
  9199        }
  9200      } catch (e) {
  9201        console.error(`❌ test failed: ${e && e.message || e}
  9202    dir: ${path.relative(dirname, thisTestDir)}
  9203    args: ${args.join(' ')}`)
  9204        return false
  9205      }
  9206  
  9207      return true
  9208    }
  9209  }
  9210  
  9211  async function main() {
  9212    // Create a fresh test directory
  9213    removeRecursiveSync(testDir)
  9214    await fs.mkdir(testDir, { recursive: true })
  9215  
  9216    // Run tests in batches so they work in CI, which has a limited memory ceiling
  9217    let allTestsPassed = true
  9218    let batch = 32
  9219    for (let i = 0; i < tests.length; i += batch) {
  9220      let promises = []
  9221      for (let test of tests.slice(i, i + batch)) {
  9222        let promise = test()
  9223        promise.then(
  9224          success => { if (!success) allTestsPassed = false },
  9225          () => allTestsPassed = false,
  9226        )
  9227        promises.push(promise)
  9228      }
  9229      await Promise.all(promises)
  9230    }
  9231  
  9232    if (!allTestsPassed) {
  9233      console.error(`❌ end-to-end tests failed`)
  9234      process.exit(1)
  9235    } else {
  9236      console.log(`✅ end-to-end tests passed`)
  9237      removeRecursiveSync(testDir)
  9238    }
  9239  }
  9240  
  9241  main()