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 ╵ " 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 ╵ ' 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()