github.com/evanw/esbuild@v0.21.4/internal/bundler_tests/bundler_dce_test.go (about)

     1  package bundler_tests
     2  
     3  import (
     4  	"regexp"
     5  	"testing"
     6  
     7  	"github.com/evanw/esbuild/internal/compat"
     8  	"github.com/evanw/esbuild/internal/config"
     9  )
    10  
    11  var dce_suite = suite{
    12  	name: "dce",
    13  }
    14  
    15  func TestPackageJsonSideEffectsFalseKeepNamedImportES6(t *testing.T) {
    16  	dce_suite.expectBundled(t, bundled{
    17  		files: map[string]string{
    18  			"/Users/user/project/src/entry.js": `
    19  				import {foo} from "demo-pkg"
    20  				console.log(foo)
    21  			`,
    22  			"/Users/user/project/node_modules/demo-pkg/index.js": `
    23  				export const foo = 123
    24  				console.log('hello')
    25  			`,
    26  			"/Users/user/project/node_modules/demo-pkg/package.json": `
    27  				{
    28  					"sideEffects": false
    29  				}
    30  			`,
    31  		},
    32  		entryPaths: []string{"/Users/user/project/src/entry.js"},
    33  		options: config.Options{
    34  			Mode:          config.ModeBundle,
    35  			AbsOutputFile: "/out.js",
    36  		},
    37  	})
    38  }
    39  
    40  func TestPackageJsonSideEffectsFalseKeepNamedImportCommonJS(t *testing.T) {
    41  	dce_suite.expectBundled(t, bundled{
    42  		files: map[string]string{
    43  			"/Users/user/project/src/entry.js": `
    44  				import {foo} from "demo-pkg"
    45  				console.log(foo)
    46  			`,
    47  			"/Users/user/project/node_modules/demo-pkg/index.js": `
    48  				exports.foo = 123
    49  				console.log('hello')
    50  			`,
    51  			"/Users/user/project/node_modules/demo-pkg/package.json": `
    52  				{
    53  					"sideEffects": false
    54  				}
    55  			`,
    56  		},
    57  		entryPaths: []string{"/Users/user/project/src/entry.js"},
    58  		options: config.Options{
    59  			Mode:          config.ModeBundle,
    60  			AbsOutputFile: "/out.js",
    61  		},
    62  	})
    63  }
    64  
    65  func TestPackageJsonSideEffectsFalseKeepStarImportES6(t *testing.T) {
    66  	dce_suite.expectBundled(t, bundled{
    67  		files: map[string]string{
    68  			"/Users/user/project/src/entry.js": `
    69  				import * as ns from "demo-pkg"
    70  				console.log(ns)
    71  			`,
    72  			"/Users/user/project/node_modules/demo-pkg/index.js": `
    73  				export const foo = 123
    74  				console.log('hello')
    75  			`,
    76  			"/Users/user/project/node_modules/demo-pkg/package.json": `
    77  				{
    78  					"sideEffects": false
    79  				}
    80  			`,
    81  		},
    82  		entryPaths: []string{"/Users/user/project/src/entry.js"},
    83  		options: config.Options{
    84  			Mode:          config.ModeBundle,
    85  			AbsOutputFile: "/out.js",
    86  		},
    87  	})
    88  }
    89  
    90  func TestPackageJsonSideEffectsFalseKeepStarImportCommonJS(t *testing.T) {
    91  	dce_suite.expectBundled(t, bundled{
    92  		files: map[string]string{
    93  			"/Users/user/project/src/entry.js": `
    94  				import * as ns from "demo-pkg"
    95  				console.log(ns)
    96  			`,
    97  			"/Users/user/project/node_modules/demo-pkg/index.js": `
    98  				exports.foo = 123
    99  				console.log('hello')
   100  			`,
   101  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   102  				{
   103  					"sideEffects": false
   104  				}
   105  			`,
   106  		},
   107  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   108  		options: config.Options{
   109  			Mode:          config.ModeBundle,
   110  			AbsOutputFile: "/out.js",
   111  		},
   112  	})
   113  }
   114  
   115  func TestPackageJsonSideEffectsTrueKeepES6(t *testing.T) {
   116  	dce_suite.expectBundled(t, bundled{
   117  		files: map[string]string{
   118  			"/Users/user/project/src/entry.js": `
   119  				import "demo-pkg"
   120  				console.log('unused import')
   121  			`,
   122  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   123  				export const foo = 123
   124  				console.log('hello')
   125  			`,
   126  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   127  				{
   128  					"sideEffects": true
   129  				}
   130  			`,
   131  		},
   132  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   133  		options: config.Options{
   134  			Mode:          config.ModeBundle,
   135  			AbsOutputFile: "/out.js",
   136  		},
   137  	})
   138  }
   139  
   140  func TestPackageJsonSideEffectsTrueKeepCommonJS(t *testing.T) {
   141  	dce_suite.expectBundled(t, bundled{
   142  		files: map[string]string{
   143  			"/Users/user/project/src/entry.js": `
   144  				import "demo-pkg"
   145  				console.log('unused import')
   146  			`,
   147  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   148  				exports.foo = 123
   149  				console.log('hello')
   150  			`,
   151  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   152  				{
   153  					"sideEffects": true
   154  				}
   155  			`,
   156  		},
   157  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   158  		options: config.Options{
   159  			Mode:          config.ModeBundle,
   160  			AbsOutputFile: "/out.js",
   161  		},
   162  	})
   163  }
   164  
   165  func TestPackageJsonSideEffectsFalseKeepBareImportAndRequireES6(t *testing.T) {
   166  	dce_suite.expectBundled(t, bundled{
   167  		files: map[string]string{
   168  			"/Users/user/project/src/entry.js": `
   169  				import "demo-pkg"
   170  				require('demo-pkg')
   171  				console.log('unused import')
   172  			`,
   173  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   174  				export const foo = 123
   175  				console.log('hello')
   176  			`,
   177  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   178  				{
   179  					"sideEffects": false
   180  				}
   181  			`,
   182  		},
   183  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   184  		options: config.Options{
   185  			Mode:          config.ModeBundle,
   186  			AbsOutputFile: "/out.js",
   187  		},
   188  		expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
   189  Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
   190  `,
   191  	})
   192  }
   193  
   194  func TestPackageJsonSideEffectsFalseKeepBareImportAndRequireCommonJS(t *testing.T) {
   195  	dce_suite.expectBundled(t, bundled{
   196  		files: map[string]string{
   197  			"/Users/user/project/src/entry.js": `
   198  				import "demo-pkg"
   199  				require('demo-pkg')
   200  				console.log('unused import')
   201  			`,
   202  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   203  				exports.foo = 123
   204  				console.log('hello')
   205  			`,
   206  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   207  				{
   208  					"sideEffects": false
   209  				}
   210  			`,
   211  		},
   212  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   213  		options: config.Options{
   214  			Mode:          config.ModeBundle,
   215  			AbsOutputFile: "/out.js",
   216  		},
   217  		expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
   218  Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
   219  `,
   220  	})
   221  }
   222  
   223  func TestPackageJsonSideEffectsFalseRemoveBareImportES6(t *testing.T) {
   224  	dce_suite.expectBundled(t, bundled{
   225  		files: map[string]string{
   226  			"/Users/user/project/src/entry.js": `
   227  				import "demo-pkg"
   228  				console.log('unused import')
   229  			`,
   230  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   231  				export const foo = 123
   232  				console.log('hello')
   233  			`,
   234  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   235  				{
   236  					"sideEffects": false
   237  				}
   238  			`,
   239  		},
   240  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   241  		options: config.Options{
   242  			Mode:          config.ModeBundle,
   243  			AbsOutputFile: "/out.js",
   244  		},
   245  		expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
   246  Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
   247  `,
   248  	})
   249  }
   250  
   251  func TestPackageJsonSideEffectsFalseRemoveBareImportCommonJS(t *testing.T) {
   252  	dce_suite.expectBundled(t, bundled{
   253  		files: map[string]string{
   254  			"/Users/user/project/src/entry.js": `
   255  				import "demo-pkg"
   256  				console.log('unused import')
   257  			`,
   258  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   259  				exports.foo = 123
   260  				console.log('hello')
   261  			`,
   262  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   263  				{
   264  					"sideEffects": false
   265  				}
   266  			`,
   267  		},
   268  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   269  		options: config.Options{
   270  			Mode:          config.ModeBundle,
   271  			AbsOutputFile: "/out.js",
   272  		},
   273  		expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/index.js" was marked as having no side effects
   274  Users/user/project/node_modules/demo-pkg/package.json: NOTE: "sideEffects" is false in the enclosing "package.json" file:
   275  `,
   276  	})
   277  }
   278  
   279  func TestPackageJsonSideEffectsFalseRemoveNamedImportES6(t *testing.T) {
   280  	dce_suite.expectBundled(t, bundled{
   281  		files: map[string]string{
   282  			"/Users/user/project/src/entry.js": `
   283  				import {foo} from "demo-pkg"
   284  				console.log('unused import')
   285  			`,
   286  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   287  				export const foo = 123
   288  				console.log('hello')
   289  			`,
   290  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   291  				{
   292  					"sideEffects": false
   293  				}
   294  			`,
   295  		},
   296  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   297  		options: config.Options{
   298  			Mode:          config.ModeBundle,
   299  			AbsOutputFile: "/out.js",
   300  		},
   301  	})
   302  }
   303  
   304  func TestPackageJsonSideEffectsFalseRemoveNamedImportCommonJS(t *testing.T) {
   305  	dce_suite.expectBundled(t, bundled{
   306  		files: map[string]string{
   307  			"/Users/user/project/src/entry.js": `
   308  				import {foo} from "demo-pkg"
   309  				console.log('unused import')
   310  			`,
   311  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   312  				exports.foo = 123
   313  				console.log('hello')
   314  			`,
   315  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   316  				{
   317  					"sideEffects": false
   318  				}
   319  			`,
   320  		},
   321  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   322  		options: config.Options{
   323  			Mode:          config.ModeBundle,
   324  			AbsOutputFile: "/out.js",
   325  		},
   326  	})
   327  }
   328  
   329  func TestPackageJsonSideEffectsFalseRemoveStarImportES6(t *testing.T) {
   330  	dce_suite.expectBundled(t, bundled{
   331  		files: map[string]string{
   332  			"/Users/user/project/src/entry.js": `
   333  				import * as ns from "demo-pkg"
   334  				console.log('unused import')
   335  			`,
   336  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   337  				export const foo = 123
   338  				console.log('hello')
   339  			`,
   340  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   341  				{
   342  					"sideEffects": false
   343  				}
   344  			`,
   345  		},
   346  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   347  		options: config.Options{
   348  			Mode:          config.ModeBundle,
   349  			AbsOutputFile: "/out.js",
   350  		},
   351  	})
   352  }
   353  
   354  func TestPackageJsonSideEffectsFalseRemoveStarImportCommonJS(t *testing.T) {
   355  	dce_suite.expectBundled(t, bundled{
   356  		files: map[string]string{
   357  			"/Users/user/project/src/entry.js": `
   358  				import * as ns from "demo-pkg"
   359  				console.log('unused import')
   360  			`,
   361  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   362  				exports.foo = 123
   363  				console.log('hello')
   364  			`,
   365  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   366  				{
   367  					"sideEffects": false
   368  				}
   369  			`,
   370  		},
   371  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   372  		options: config.Options{
   373  			Mode:          config.ModeBundle,
   374  			AbsOutputFile: "/out.js",
   375  		},
   376  	})
   377  }
   378  
   379  func TestPackageJsonSideEffectsArrayRemove(t *testing.T) {
   380  	dce_suite.expectBundled(t, bundled{
   381  		files: map[string]string{
   382  			"/Users/user/project/src/entry.js": `
   383  				import {foo} from "demo-pkg"
   384  				console.log('unused import')
   385  			`,
   386  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   387  				export const foo = 123
   388  				console.log('hello')
   389  			`,
   390  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   391  				{
   392  					"sideEffects": []
   393  				}
   394  			`,
   395  		},
   396  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   397  		options: config.Options{
   398  			Mode:          config.ModeBundle,
   399  			AbsOutputFile: "/out.js",
   400  		},
   401  	})
   402  }
   403  
   404  func TestPackageJsonSideEffectsArrayKeep(t *testing.T) {
   405  	dce_suite.expectBundled(t, bundled{
   406  		files: map[string]string{
   407  			"/Users/user/project/src/entry.js": `
   408  				import {foo} from "demo-pkg"
   409  				console.log('unused import')
   410  			`,
   411  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   412  				export const foo = 123
   413  				console.log('hello')
   414  			`,
   415  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   416  				{
   417  					"sideEffects": ["./index.js"]
   418  				}
   419  			`,
   420  		},
   421  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   422  		options: config.Options{
   423  			Mode:          config.ModeBundle,
   424  			AbsOutputFile: "/out.js",
   425  		},
   426  	})
   427  }
   428  
   429  func TestPackageJsonSideEffectsArrayKeepMainUseModule(t *testing.T) {
   430  	dce_suite.expectBundled(t, bundled{
   431  		files: map[string]string{
   432  			"/Users/user/project/src/entry.js": `
   433  				import {foo} from "demo-pkg"
   434  				console.log('unused import')
   435  			`,
   436  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   437  				export const foo = 123
   438  				console.log('TEST FAILED')
   439  			`,
   440  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   441  				export const foo = 123
   442  				console.log('TEST FAILED')
   443  			`,
   444  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   445  				{
   446  					"main": "index-main.js",
   447  					"module": "index-module.js",
   448  					"sideEffects": ["./index-main.js"]
   449  				}
   450  			`,
   451  		},
   452  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   453  		options: config.Options{
   454  			Mode:          config.ModeBundle,
   455  			AbsOutputFile: "/out.js",
   456  			MainFields:    []string{"module"},
   457  		},
   458  	})
   459  }
   460  
   461  func TestPackageJsonSideEffectsArrayKeepMainUseMain(t *testing.T) {
   462  	dce_suite.expectBundled(t, bundled{
   463  		files: map[string]string{
   464  			"/Users/user/project/src/entry.js": `
   465  				import {foo} from "demo-pkg"
   466  				console.log('unused import')
   467  			`,
   468  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   469  				export const foo = 123
   470  				console.log('this should be kept')
   471  			`,
   472  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   473  				export const foo = 123
   474  				console.log('TEST FAILED')
   475  			`,
   476  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   477  				{
   478  					"main": "index-main.js",
   479  					"module": "index-module.js",
   480  					"sideEffects": ["./index-main.js"]
   481  				}
   482  			`,
   483  		},
   484  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   485  		options: config.Options{
   486  			Mode:          config.ModeBundle,
   487  			AbsOutputFile: "/out.js",
   488  			MainFields:    []string{"main"},
   489  		},
   490  	})
   491  }
   492  
   493  func TestPackageJsonSideEffectsArrayKeepMainImplicitModule(t *testing.T) {
   494  	dce_suite.expectBundled(t, bundled{
   495  		files: map[string]string{
   496  			"/Users/user/project/src/entry.js": `
   497  				import {foo} from "demo-pkg"
   498  				console.log('unused import')
   499  			`,
   500  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   501  				export const foo = 123
   502  				console.log('TEST FAILED')
   503  			`,
   504  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   505  				export const foo = 123
   506  				console.log('TEST FAILED')
   507  			`,
   508  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   509  				{
   510  					"main": "index-main.js",
   511  					"module": "index-module.js",
   512  					"sideEffects": ["./index-main.js"]
   513  				}
   514  			`,
   515  		},
   516  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   517  		options: config.Options{
   518  			Mode:          config.ModeBundle,
   519  			AbsOutputFile: "/out.js",
   520  		},
   521  	})
   522  }
   523  
   524  func TestPackageJsonSideEffectsArrayKeepMainImplicitMain(t *testing.T) {
   525  	dce_suite.expectBundled(t, bundled{
   526  		files: map[string]string{
   527  			"/Users/user/project/src/entry.js": `
   528  				import {foo} from "demo-pkg"
   529  				import "./require-demo-pkg"
   530  				console.log('unused import')
   531  			`,
   532  			"/Users/user/project/src/require-demo-pkg.js": `
   533  				// This causes "index-main.js" to be selected
   534  				require('demo-pkg')
   535  			`,
   536  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   537  				export const foo = 123
   538  				console.log('this should be kept')
   539  			`,
   540  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   541  				export const foo = 123
   542  				console.log('TEST FAILED')
   543  			`,
   544  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   545  				{
   546  					"main": "index-main.js",
   547  					"module": "index-module.js",
   548  					"sideEffects": ["./index-main.js"]
   549  				}
   550  			`,
   551  		},
   552  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   553  		options: config.Options{
   554  			Mode:          config.ModeBundle,
   555  			AbsOutputFile: "/out.js",
   556  		},
   557  	})
   558  }
   559  
   560  func TestPackageJsonSideEffectsArrayKeepModuleUseModule(t *testing.T) {
   561  	dce_suite.expectBundled(t, bundled{
   562  		files: map[string]string{
   563  			"/Users/user/project/src/entry.js": `
   564  				import {foo} from "demo-pkg"
   565  				console.log('unused import')
   566  			`,
   567  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   568  				export const foo = 123
   569  				console.log('TEST FAILED')
   570  			`,
   571  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   572  				export const foo = 123
   573  				console.log('this should be kept')
   574  			`,
   575  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   576  				{
   577  					"main": "index-main.js",
   578  					"module": "index-module.js",
   579  					"sideEffects": ["./index-module.js"]
   580  				}
   581  			`,
   582  		},
   583  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   584  		options: config.Options{
   585  			Mode:          config.ModeBundle,
   586  			AbsOutputFile: "/out.js",
   587  			MainFields:    []string{"module"},
   588  		},
   589  	})
   590  }
   591  
   592  func TestPackageJsonSideEffectsArrayKeepModuleUseMain(t *testing.T) {
   593  	dce_suite.expectBundled(t, bundled{
   594  		files: map[string]string{
   595  			"/Users/user/project/src/entry.js": `
   596  				import {foo} from "demo-pkg"
   597  				console.log('unused import')
   598  			`,
   599  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   600  				export const foo = 123
   601  				console.log('TEST FAILED')
   602  			`,
   603  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   604  				export const foo = 123
   605  				console.log('TEST FAILED')
   606  			`,
   607  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   608  				{
   609  					"main": "index-main.js",
   610  					"module": "index-module.js",
   611  					"sideEffects": ["./index-module.js"]
   612  				}
   613  			`,
   614  		},
   615  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   616  		options: config.Options{
   617  			Mode:          config.ModeBundle,
   618  			AbsOutputFile: "/out.js",
   619  			MainFields:    []string{"main"},
   620  		},
   621  	})
   622  }
   623  
   624  func TestPackageJsonSideEffectsArrayKeepModuleImplicitModule(t *testing.T) {
   625  	dce_suite.expectBundled(t, bundled{
   626  		files: map[string]string{
   627  			"/Users/user/project/src/entry.js": `
   628  				import {foo} from "demo-pkg"
   629  				console.log('unused import')
   630  			`,
   631  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   632  				export const foo = 123
   633  				console.log('TEST FAILED')
   634  			`,
   635  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   636  				export const foo = 123
   637  				console.log('this should be kept')
   638  			`,
   639  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   640  				{
   641  					"main": "index-main.js",
   642  					"module": "index-module.js",
   643  					"sideEffects": ["./index-module.js"]
   644  				}
   645  			`,
   646  		},
   647  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   648  		options: config.Options{
   649  			Mode:          config.ModeBundle,
   650  			AbsOutputFile: "/out.js",
   651  		},
   652  	})
   653  }
   654  
   655  func TestPackageJsonSideEffectsArrayKeepModuleImplicitMain(t *testing.T) {
   656  	dce_suite.expectBundled(t, bundled{
   657  		files: map[string]string{
   658  			"/Users/user/project/src/entry.js": `
   659  				import {foo} from "demo-pkg"
   660  				import "./require-demo-pkg"
   661  				console.log('unused import')
   662  			`,
   663  			"/Users/user/project/src/require-demo-pkg.js": `
   664  				// This causes "index-main.js" to be selected
   665  				require('demo-pkg')
   666  			`,
   667  			"/Users/user/project/node_modules/demo-pkg/index-main.js": `
   668  				export const foo = 123
   669  				console.log('this should be kept')
   670  			`,
   671  			"/Users/user/project/node_modules/demo-pkg/index-module.js": `
   672  				export const foo = 123
   673  				console.log('TEST FAILED')
   674  			`,
   675  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   676  				{
   677  					"main": "index-main.js",
   678  					"module": "index-module.js",
   679  					"sideEffects": ["./index-module.js"]
   680  				}
   681  			`,
   682  		},
   683  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   684  		options: config.Options{
   685  			Mode:          config.ModeBundle,
   686  			AbsOutputFile: "/out.js",
   687  		},
   688  	})
   689  }
   690  
   691  func TestPackageJsonSideEffectsArrayGlob(t *testing.T) {
   692  	dce_suite.expectBundled(t, bundled{
   693  		files: map[string]string{
   694  			"/Users/user/project/src/entry.js": `
   695  				import "demo-pkg/keep/this/file"
   696  				import "demo-pkg/remove/this/file"
   697  			`,
   698  			"/Users/user/project/node_modules/demo-pkg/keep/this/file.js": `
   699  				console.log('this should be kept')
   700  			`,
   701  			"/Users/user/project/node_modules/demo-pkg/remove/this/file.js": `
   702  				console.log('TEST FAILED')
   703  			`,
   704  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   705  				{
   706  					"sideEffects": [
   707  						"./ke?p/*/file.js",
   708  						"./remove/this/file.j",
   709  						"./re?ve/this/file.js"
   710  					]
   711  				}
   712  			`,
   713  		},
   714  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   715  		options: config.Options{
   716  			Mode:          config.ModeBundle,
   717  			AbsOutputFile: "/out.js",
   718  		},
   719  		expectedScanLog: `Users/user/project/src/entry.js: WARNING: Ignoring this import because "Users/user/project/node_modules/demo-pkg/remove/this/file.js" was marked as having no side effects
   720  Users/user/project/node_modules/demo-pkg/package.json: NOTE: It was excluded from the "sideEffects" array in the enclosing "package.json" file:
   721  `,
   722  	})
   723  }
   724  
   725  func TestPackageJsonSideEffectsNestedDirectoryRemove(t *testing.T) {
   726  	dce_suite.expectBundled(t, bundled{
   727  		files: map[string]string{
   728  			"/Users/user/project/src/entry.js": `
   729  				import {foo} from "demo-pkg/a/b/c"
   730  				console.log('unused import')
   731  			`,
   732  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   733  				{
   734  					"sideEffects": false
   735  				}
   736  			`,
   737  			"/Users/user/project/node_modules/demo-pkg/a/b/c/index.js": `
   738  				export const foo = 123
   739  				console.log('hello')
   740  			`,
   741  		},
   742  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   743  		options: config.Options{
   744  			Mode:          config.ModeBundle,
   745  			AbsOutputFile: "/out.js",
   746  		},
   747  	})
   748  }
   749  
   750  func TestPackageJsonSideEffectsKeepExportDefaultExpr(t *testing.T) {
   751  	dce_suite.expectBundled(t, bundled{
   752  		files: map[string]string{
   753  			"/Users/user/project/src/entry.js": `
   754  				import foo from "demo-pkg"
   755  				console.log(foo)
   756  			`,
   757  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   758  				export default exprWithSideEffects()
   759  			`,
   760  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   761  				{
   762  					"sideEffects": false
   763  				}
   764  			`,
   765  		},
   766  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   767  		options: config.Options{
   768  			Mode:          config.ModeBundle,
   769  			AbsOutputFile: "/out.js",
   770  		},
   771  	})
   772  }
   773  
   774  func TestPackageJsonSideEffectsFalseNoWarningInNodeModulesIssue999(t *testing.T) {
   775  	dce_suite.expectBundled(t, bundled{
   776  		files: map[string]string{
   777  			"/Users/user/project/src/entry.js": `
   778  				import "demo-pkg"
   779  				console.log('used import')
   780  				`,
   781  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   782  				import "demo-pkg2"
   783  				console.log('unused import')
   784  			`,
   785  			"/Users/user/project/node_modules/demo-pkg2/index.js": `
   786  				export const foo = 123
   787  				console.log('hello')
   788  			`,
   789  			"/Users/user/project/node_modules/demo-pkg2/package.json": `
   790  				{
   791  					"sideEffects": false
   792  				}
   793  			`,
   794  		},
   795  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   796  		options: config.Options{
   797  			Mode:          config.ModeBundle,
   798  			AbsOutputFile: "/out.js",
   799  		},
   800  	})
   801  }
   802  
   803  func TestPackageJsonSideEffectsFalseIntermediateFilesUnused(t *testing.T) {
   804  	dce_suite.expectBundled(t, bundled{
   805  		files: map[string]string{
   806  			"/Users/user/project/src/entry.js": `
   807  				import {foo} from "demo-pkg"
   808  			`,
   809  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   810  				export {foo} from "./foo.js"
   811  				throw 'REMOVE THIS'
   812  			`,
   813  			"/Users/user/project/node_modules/demo-pkg/foo.js": `
   814  				export const foo = 123
   815  			`,
   816  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   817  				{ "sideEffects": false }
   818  			`,
   819  		},
   820  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   821  		options: config.Options{
   822  			Mode:          config.ModeBundle,
   823  			AbsOutputFile: "/out.js",
   824  		},
   825  	})
   826  }
   827  
   828  func TestPackageJsonSideEffectsFalseIntermediateFilesUsed(t *testing.T) {
   829  	dce_suite.expectBundled(t, bundled{
   830  		files: map[string]string{
   831  			"/Users/user/project/src/entry.js": `
   832  				import {foo} from "demo-pkg"
   833  				console.log(foo)
   834  			`,
   835  			"/Users/user/project/node_modules/demo-pkg/index.js": `
   836  				export {foo} from "./foo.js"
   837  				throw 'keep this'
   838  			`,
   839  			"/Users/user/project/node_modules/demo-pkg/foo.js": `
   840  				export const foo = 123
   841  			`,
   842  			"/Users/user/project/node_modules/demo-pkg/package.json": `
   843  				{ "sideEffects": false }
   844  			`,
   845  		},
   846  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   847  		options: config.Options{
   848  			Mode:          config.ModeBundle,
   849  			AbsOutputFile: "/out.js",
   850  		},
   851  	})
   852  }
   853  
   854  func TestPackageJsonSideEffectsFalseIntermediateFilesChainAll(t *testing.T) {
   855  	dce_suite.expectBundled(t, bundled{
   856  		files: map[string]string{
   857  			"/Users/user/project/src/entry.js": `
   858  				import {foo} from "a"
   859  				console.log(foo)
   860  			`,
   861  			"/Users/user/project/node_modules/a/index.js": `
   862  				export {foo} from "b"
   863  			`,
   864  			"/Users/user/project/node_modules/a/package.json": `
   865  				{ "sideEffects": false }
   866  			`,
   867  			"/Users/user/project/node_modules/b/index.js": `
   868  				export {foo} from "c"
   869  				throw 'keep this'
   870  			`,
   871  			"/Users/user/project/node_modules/b/package.json": `
   872  				{ "sideEffects": false }
   873  			`,
   874  			"/Users/user/project/node_modules/c/index.js": `
   875  				export {foo} from "d"
   876  			`,
   877  			"/Users/user/project/node_modules/c/package.json": `
   878  				{ "sideEffects": false }
   879  			`,
   880  			"/Users/user/project/node_modules/d/index.js": `
   881  				export const foo = 123
   882  			`,
   883  			"/Users/user/project/node_modules/d/package.json": `
   884  				{ "sideEffects": false }
   885  			`,
   886  		},
   887  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   888  		options: config.Options{
   889  			Mode:          config.ModeBundle,
   890  			AbsOutputFile: "/out.js",
   891  		},
   892  	})
   893  }
   894  
   895  func TestPackageJsonSideEffectsFalseIntermediateFilesChainOne(t *testing.T) {
   896  	dce_suite.expectBundled(t, bundled{
   897  		files: map[string]string{
   898  			"/Users/user/project/src/entry.js": `
   899  				import {foo} from "a"
   900  				console.log(foo)
   901  			`,
   902  			"/Users/user/project/node_modules/a/index.js": `
   903  				export {foo} from "b"
   904  			`,
   905  			"/Users/user/project/node_modules/b/index.js": `
   906  				export {foo} from "c"
   907  				throw 'keep this'
   908  			`,
   909  			"/Users/user/project/node_modules/b/package.json": `
   910  				{ "sideEffects": false }
   911  			`,
   912  			"/Users/user/project/node_modules/c/index.js": `
   913  				export {foo} from "d"
   914  			`,
   915  			"/Users/user/project/node_modules/d/index.js": `
   916  				export const foo = 123
   917  			`,
   918  		},
   919  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   920  		options: config.Options{
   921  			Mode:          config.ModeBundle,
   922  			AbsOutputFile: "/out.js",
   923  		},
   924  	})
   925  }
   926  
   927  func TestPackageJsonSideEffectsFalseIntermediateFilesDiamond(t *testing.T) {
   928  	dce_suite.expectBundled(t, bundled{
   929  		files: map[string]string{
   930  			"/Users/user/project/src/entry.js": `
   931  				import {foo} from "a"
   932  				console.log(foo)
   933  			`,
   934  			"/Users/user/project/node_modules/a/index.js": `
   935  				export * from "b1"
   936  				export * from "b2"
   937  			`,
   938  			"/Users/user/project/node_modules/b1/index.js": `
   939  				export {foo} from "c"
   940  				throw 'keep this 1'
   941  			`,
   942  			"/Users/user/project/node_modules/b1/package.json": `
   943  				{ "sideEffects": false }
   944  			`,
   945  			"/Users/user/project/node_modules/b2/index.js": `
   946  				export {foo} from "c"
   947  				throw 'keep this 2'
   948  			`,
   949  			"/Users/user/project/node_modules/b2/package.json": `
   950  				{ "sideEffects": false }
   951  			`,
   952  			"/Users/user/project/node_modules/c/index.js": `
   953  				export {foo} from "d"
   954  			`,
   955  			"/Users/user/project/node_modules/d/index.js": `
   956  				export const foo = 123
   957  			`,
   958  		},
   959  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   960  		options: config.Options{
   961  			Mode:          config.ModeBundle,
   962  			AbsOutputFile: "/out.js",
   963  		},
   964  	})
   965  }
   966  
   967  func TestPackageJsonSideEffectsFalseOneFork(t *testing.T) {
   968  	dce_suite.expectBundled(t, bundled{
   969  		files: map[string]string{
   970  			"/Users/user/project/src/entry.js": `
   971  				import("a").then(x => assert(x.foo === "foo"))
   972  			`,
   973  			"/Users/user/project/node_modules/a/index.js": `
   974  				export {foo} from "b"
   975  			`,
   976  			"/Users/user/project/node_modules/b/index.js": `
   977  				export {foo, bar} from "c"
   978  				export {baz} from "d"
   979  			`,
   980  			"/Users/user/project/node_modules/b/package.json": `
   981  				{ "sideEffects": false }
   982  			`,
   983  			"/Users/user/project/node_modules/c/index.js": `
   984  				export let foo = "foo"
   985  				export let bar = "bar"
   986  			`,
   987  			"/Users/user/project/node_modules/d/index.js": `
   988  				export let baz = "baz"
   989  			`,
   990  		},
   991  		entryPaths: []string{"/Users/user/project/src/entry.js"},
   992  		options: config.Options{
   993  			Mode:          config.ModeBundle,
   994  			AbsOutputFile: "/out.js",
   995  		},
   996  	})
   997  }
   998  
   999  func TestPackageJsonSideEffectsFalseAllFork(t *testing.T) {
  1000  	dce_suite.expectBundled(t, bundled{
  1001  		files: map[string]string{
  1002  			"/Users/user/project/src/entry.js": `
  1003  				import("a").then(x => assert(x.foo === "foo"))
  1004  			`,
  1005  			"/Users/user/project/node_modules/a/index.js": `
  1006  				export {foo} from "b"
  1007  			`,
  1008  			"/Users/user/project/node_modules/b/index.js": `
  1009  				export {foo, bar} from "c"
  1010  				export {baz} from "d"
  1011  			`,
  1012  			"/Users/user/project/node_modules/b/package.json": `
  1013  				{ "sideEffects": false }
  1014  			`,
  1015  			"/Users/user/project/node_modules/c/index.js": `
  1016  				export let foo = "foo"
  1017  				export let bar = "bar"
  1018  			`,
  1019  			"/Users/user/project/node_modules/c/package.json": `
  1020  				{ "sideEffects": false }
  1021  			`,
  1022  			"/Users/user/project/node_modules/d/index.js": `
  1023  				export let baz = "baz"
  1024  			`,
  1025  			"/Users/user/project/node_modules/d/package.json": `
  1026  				{ "sideEffects": false }
  1027  			`,
  1028  		},
  1029  		entryPaths: []string{"/Users/user/project/src/entry.js"},
  1030  		options: config.Options{
  1031  			Mode:          config.ModeBundle,
  1032  			AbsOutputFile: "/out.js",
  1033  		},
  1034  	})
  1035  }
  1036  
  1037  func TestJSONLoaderRemoveUnused(t *testing.T) {
  1038  	dce_suite.expectBundled(t, bundled{
  1039  		files: map[string]string{
  1040  			"/entry.js": `
  1041  				import unused from "./example.json"
  1042  				console.log('unused import')
  1043  			`,
  1044  			"/example.json": `{"data": true}`,
  1045  		},
  1046  		entryPaths: []string{"/entry.js"},
  1047  		options: config.Options{
  1048  			Mode:          config.ModeBundle,
  1049  			AbsOutputFile: "/out.js",
  1050  		},
  1051  	})
  1052  }
  1053  
  1054  func TestTextLoaderRemoveUnused(t *testing.T) {
  1055  	dce_suite.expectBundled(t, bundled{
  1056  		files: map[string]string{
  1057  			"/entry.js": `
  1058  				import unused from "./example.txt"
  1059  				console.log('unused import')
  1060  			`,
  1061  			"/example.txt": `some data`,
  1062  		},
  1063  		entryPaths: []string{"/entry.js"},
  1064  		options: config.Options{
  1065  			Mode:          config.ModeBundle,
  1066  			AbsOutputFile: "/out.js",
  1067  		},
  1068  	})
  1069  }
  1070  
  1071  func TestBase64LoaderRemoveUnused(t *testing.T) {
  1072  	dce_suite.expectBundled(t, bundled{
  1073  		files: map[string]string{
  1074  			"/entry.js": `
  1075  				import unused from "./example.data"
  1076  				console.log('unused import')
  1077  			`,
  1078  			"/example.data": `some data`,
  1079  		},
  1080  		entryPaths: []string{"/entry.js"},
  1081  		options: config.Options{
  1082  			Mode:          config.ModeBundle,
  1083  			AbsOutputFile: "/out.js",
  1084  			ExtensionToLoader: map[string]config.Loader{
  1085  				".js":   config.LoaderJS,
  1086  				".data": config.LoaderBase64,
  1087  			},
  1088  		},
  1089  	})
  1090  }
  1091  
  1092  func TestDataURLLoaderRemoveUnused(t *testing.T) {
  1093  	dce_suite.expectBundled(t, bundled{
  1094  		files: map[string]string{
  1095  			"/entry.js": `
  1096  				import unused from "./example.data"
  1097  				console.log('unused import')
  1098  			`,
  1099  			"/example.data": `some data`,
  1100  		},
  1101  		entryPaths: []string{"/entry.js"},
  1102  		options: config.Options{
  1103  			Mode:          config.ModeBundle,
  1104  			AbsOutputFile: "/out.js",
  1105  			ExtensionToLoader: map[string]config.Loader{
  1106  				".js":   config.LoaderJS,
  1107  				".data": config.LoaderDataURL,
  1108  			},
  1109  		},
  1110  	})
  1111  }
  1112  
  1113  func TestFileLoaderRemoveUnused(t *testing.T) {
  1114  	dce_suite.expectBundled(t, bundled{
  1115  		files: map[string]string{
  1116  			"/entry.js": `
  1117  				import unused from "./example.data"
  1118  				console.log('unused import')
  1119  			`,
  1120  			"/example.data": `some data`,
  1121  		},
  1122  		entryPaths: []string{"/entry.js"},
  1123  		options: config.Options{
  1124  			Mode:          config.ModeBundle,
  1125  			AbsOutputFile: "/out.js",
  1126  			ExtensionToLoader: map[string]config.Loader{
  1127  				".js":   config.LoaderJS,
  1128  				".data": config.LoaderFile,
  1129  			},
  1130  		},
  1131  	})
  1132  }
  1133  
  1134  func TestRemoveUnusedImportMeta(t *testing.T) {
  1135  	dce_suite.expectBundled(t, bundled{
  1136  		files: map[string]string{
  1137  			"/entry.js": `
  1138  				function foo() {
  1139  					console.log(import.meta.url, import.meta.path)
  1140  				}
  1141  				console.log('foo is unused')
  1142  			`,
  1143  		},
  1144  		entryPaths: []string{"/entry.js"},
  1145  		options: config.Options{
  1146  			Mode:          config.ModeBundle,
  1147  			AbsOutputFile: "/out.js",
  1148  		},
  1149  	})
  1150  }
  1151  
  1152  func TestRemoveUnusedPureCommentCalls(t *testing.T) {
  1153  	dce_suite.expectBundled(t, bundled{
  1154  		files: map[string]string{
  1155  			"/entry.js": `
  1156  				function bar() {}
  1157  				let bare = foo(bar);
  1158  
  1159  				let at_yes = /* @__PURE__ */ foo(bar);
  1160  				let at_no = /* @__PURE__ */ foo(bar());
  1161  				let new_at_yes = /* @__PURE__ */ new foo(bar);
  1162  				let new_at_no = /* @__PURE__ */ new foo(bar());
  1163  
  1164  				let nospace_at_yes = /*@__PURE__*/ foo(bar);
  1165  				let nospace_at_no = /*@__PURE__*/ foo(bar());
  1166  				let nospace_new_at_yes = /*@__PURE__*/ new foo(bar);
  1167  				let nospace_new_at_no = /*@__PURE__*/ new foo(bar());
  1168  
  1169  				let num_yes = /* #__PURE__ */ foo(bar);
  1170  				let num_no = /* #__PURE__ */ foo(bar());
  1171  				let new_num_yes = /* #__PURE__ */ new foo(bar);
  1172  				let new_num_no = /* #__PURE__ */ new foo(bar());
  1173  
  1174  				let nospace_num_yes = /*#__PURE__*/ foo(bar);
  1175  				let nospace_num_no = /*#__PURE__*/ foo(bar());
  1176  				let nospace_new_num_yes = /*#__PURE__*/ new foo(bar);
  1177  				let nospace_new_num_no = /*#__PURE__*/ new foo(bar());
  1178  
  1179  				let dot_yes = /* @__PURE__ */ foo(sideEffect()).dot(bar);
  1180  				let dot_no = /* @__PURE__ */ foo(sideEffect()).dot(bar());
  1181  				let new_dot_yes = /* @__PURE__ */ new foo(sideEffect()).dot(bar);
  1182  				let new_dot_no = /* @__PURE__ */ new foo(sideEffect()).dot(bar());
  1183  
  1184  				let nested_yes = [1, /* @__PURE__ */ foo(bar), 2];
  1185  				let nested_no = [1, /* @__PURE__ */ foo(bar()), 2];
  1186  				let new_nested_yes = [1, /* @__PURE__ */ new foo(bar), 2];
  1187  				let new_nested_no = [1, /* @__PURE__ */ new foo(bar()), 2];
  1188  
  1189  				let single_at_yes = // @__PURE__
  1190  					foo(bar);
  1191  				let single_at_no = // @__PURE__
  1192  					foo(bar());
  1193  				let new_single_at_yes = // @__PURE__
  1194  					new foo(bar);
  1195  				let new_single_at_no = // @__PURE__
  1196  					new foo(bar());
  1197  
  1198  				let single_num_yes = // #__PURE__
  1199  					foo(bar);
  1200  				let single_num_no = // #__PURE__
  1201  					foo(bar());
  1202  				let new_single_num_yes = // #__PURE__
  1203  					new foo(bar);
  1204  				let new_single_num_no = // #__PURE__
  1205  					new foo(bar());
  1206  
  1207  				let bad_no = /* __PURE__ */ foo(bar);
  1208  				let new_bad_no = /* __PURE__ */ new foo(bar);
  1209  
  1210  				let parens_no = (/* @__PURE__ */ foo)(bar);
  1211  				let new_parens_no = new (/* @__PURE__ */ foo)(bar);
  1212  
  1213  				let exp_no = /* @__PURE__ */ foo() ** foo();
  1214  				let new_exp_no = /* @__PURE__ */ new foo() ** foo();
  1215  			`,
  1216  		},
  1217  		entryPaths: []string{"/entry.js"},
  1218  		options: config.Options{
  1219  			Mode:          config.ModeBundle,
  1220  			AbsOutputFile: "/out.js",
  1221  		},
  1222  	})
  1223  }
  1224  
  1225  func TestRemoveUnusedNoSideEffectsTaggedTemplates(t *testing.T) {
  1226  	dce_suite.expectBundled(t, bundled{
  1227  		files: map[string]string{
  1228  			"/entry.js": `
  1229  				// @__NO_SIDE_EFFECTS__
  1230  				function foo() {}
  1231  
  1232  				foo` + "`remove`" + `;
  1233  				foo` + "`remove${null}`" + `;
  1234  				foo` + "`remove${123}`" + `;
  1235  
  1236  				use(foo` + "`keep`" + `);
  1237  				foo` + "`remove this part ${keep} and this ${alsoKeep}`" + `;
  1238  				` + "`remove this part ${keep} and this ${alsoKeep}`" + `;
  1239  			`,
  1240  		},
  1241  		entryPaths: []string{"/entry.js"},
  1242  		options: config.Options{
  1243  			Mode:          config.ModeBundle,
  1244  			AbsOutputFile: "/out.js",
  1245  			MinifySyntax:  true,
  1246  		},
  1247  	})
  1248  }
  1249  
  1250  func TestTreeShakingReactElements(t *testing.T) {
  1251  	dce_suite.expectBundled(t, bundled{
  1252  		files: map[string]string{
  1253  			"/entry.jsx": `
  1254  				function Foo() {}
  1255  
  1256  				let a = <div/>
  1257  				let b = <Foo>{a}</Foo>
  1258  				let c = <>{b}</>
  1259  
  1260  				let d = <div/>
  1261  				let e = <Foo>{d}</Foo>
  1262  				let f = <>{e}</>
  1263  				console.log(f)
  1264  			`,
  1265  		},
  1266  		entryPaths: []string{"/entry.jsx"},
  1267  		options: config.Options{
  1268  			Mode:          config.ModeBundle,
  1269  			AbsOutputFile: "/out.js",
  1270  		},
  1271  	})
  1272  }
  1273  
  1274  func TestDisableTreeShaking(t *testing.T) {
  1275  	defines := config.ProcessDefines(map[string]config.DefineData{
  1276  		"pure":    {Flags: config.CallCanBeUnwrappedIfUnused},
  1277  		"some.fn": {Flags: config.CallCanBeUnwrappedIfUnused},
  1278  	})
  1279  	dce_suite.expectBundled(t, bundled{
  1280  		files: map[string]string{
  1281  			"/entry.jsx": `
  1282  				import './remove-me'
  1283  				function RemoveMe1() {}
  1284  				let removeMe2 = 0
  1285  				class RemoveMe3 {}
  1286  
  1287  				import './keep-me'
  1288  				function KeepMe1() {}
  1289  				let keepMe2 = <KeepMe1/>
  1290  				function keepMe3() { console.log('side effects') }
  1291  				let keepMe4 = /* @__PURE__ */ keepMe3()
  1292  				let keepMe5 = pure()
  1293  				let keepMe6 = some.fn()
  1294  			`,
  1295  			"/remove-me.js": `
  1296  				export default 'unused'
  1297  			`,
  1298  			"/keep-me/index.js": `
  1299  				console.log('side effects')
  1300  			`,
  1301  			"/keep-me/package.json": `
  1302  				{ "sideEffects": false }
  1303  			`,
  1304  		},
  1305  		entryPaths: []string{"/entry.jsx"},
  1306  		options: config.Options{
  1307  			Mode:                 config.ModeBundle,
  1308  			AbsOutputFile:        "/out.js",
  1309  			IgnoreDCEAnnotations: true,
  1310  			Defines:              &defines,
  1311  		},
  1312  	})
  1313  }
  1314  
  1315  func TestDeadCodeFollowingJump(t *testing.T) {
  1316  	dce_suite.expectBundled(t, bundled{
  1317  		files: map[string]string{
  1318  			"/entry.js": `
  1319  				function testReturn() {
  1320  					if (true) return y + z()
  1321  					if (FAIL) return FAIL
  1322  					if (x) { var y }
  1323  					function z() { KEEP_ME() }
  1324  					return FAIL
  1325  				}
  1326  
  1327  				function testThrow() {
  1328  					if (true) throw y + z()
  1329  					if (FAIL) return FAIL
  1330  					if (x) { var y }
  1331  					function z() { KEEP_ME() }
  1332  					return FAIL
  1333  				}
  1334  
  1335  				function testBreak() {
  1336  					while (true) {
  1337  						if (true) {
  1338  							y + z()
  1339  							break
  1340  						}
  1341  						if (FAIL) return FAIL
  1342  						if (x) { var y }
  1343  						function z() { KEEP_ME() }
  1344  						return FAIL
  1345  					}
  1346  				}
  1347  
  1348  				function testContinue() {
  1349  					while (true) {
  1350  						if (true) {
  1351  							y + z()
  1352  							continue
  1353  						}
  1354  						if (FAIL) return FAIL
  1355  						if (x) { var y }
  1356  						function z() { KEEP_ME() }
  1357  						return FAIL
  1358  					}
  1359  				}
  1360  
  1361  				function testStmts() {
  1362  					return [a, b, c, d, e, f, g, h, i]
  1363  
  1364  					while (x) { var a }
  1365  					while (FAIL) { let FAIL }
  1366  
  1367  					do { var b } while (x)
  1368  					do { let FAIL } while (FAIL)
  1369  
  1370  					for (var c; ;) ;
  1371  					for (let FAIL; ;) ;
  1372  
  1373  					for (var d in x) ;
  1374  					for (let FAIL in FAIL) ;
  1375  
  1376  					for (var e of x) ;
  1377  					for (let FAIL of FAIL) ;
  1378  
  1379  					if (x) { var f }
  1380  					if (FAIL) { let FAIL }
  1381  
  1382  					if (x) ; else { var g }
  1383  					if (FAIL) ; else { let FAIL }
  1384  
  1385  					{ var h }
  1386  					{ let FAIL }
  1387  
  1388  					x: { var i }
  1389  					x: { let FAIL }
  1390  				}
  1391  
  1392  				testReturn()
  1393  				testThrow()
  1394  				testBreak()
  1395  				testContinue()
  1396  				testStmts()
  1397  			`,
  1398  		},
  1399  		entryPaths: []string{"/entry.js"},
  1400  		options: config.Options{
  1401  			Mode:          config.ModeBundle,
  1402  			AbsOutputFile: "/out.js",
  1403  			MinifySyntax:  true,
  1404  		},
  1405  	})
  1406  }
  1407  
  1408  func TestRemoveTrailingReturn(t *testing.T) {
  1409  	dce_suite.expectBundled(t, bundled{
  1410  		files: map[string]string{
  1411  			"/entry.js": `
  1412  				function foo() {
  1413  					if (a) b()
  1414  					return
  1415  				}
  1416  				function bar() {
  1417  					if (a) b()
  1418  					return KEEP_ME
  1419  				}
  1420  				export default [
  1421  					foo,
  1422  					bar,
  1423  					function () {
  1424  						if (a) b()
  1425  						return
  1426  					},
  1427  					function () {
  1428  						if (a) b()
  1429  						return KEEP_ME
  1430  					},
  1431  					() => {
  1432  						if (a) b()
  1433  						return
  1434  					},
  1435  					() => {
  1436  						if (a) b()
  1437  						return KEEP_ME
  1438  					},
  1439  				]
  1440  			`,
  1441  		},
  1442  		entryPaths: []string{"/entry.js"},
  1443  		options: config.Options{
  1444  			Mode:          config.ModeBundle,
  1445  			AbsOutputFile: "/out.js",
  1446  			MinifySyntax:  true,
  1447  			OutputFormat:  config.FormatESModule,
  1448  		},
  1449  	})
  1450  }
  1451  
  1452  func TestImportReExportOfNamespaceImport(t *testing.T) {
  1453  	dce_suite.expectBundled(t, bundled{
  1454  		files: map[string]string{
  1455  			"/Users/user/project/entry.js": `
  1456  				import * as ns from 'pkg'
  1457  				console.log(ns.foo)
  1458  			`,
  1459  			"/Users/user/project/node_modules/pkg/index.js": `
  1460  				export { default as foo } from './foo'
  1461  				export { default as bar } from './bar'
  1462  			`,
  1463  			"/Users/user/project/node_modules/pkg/package.json": `
  1464  				{ "sideEffects": false }
  1465  			`,
  1466  			"/Users/user/project/node_modules/pkg/foo.js": `
  1467  				module.exports = 123
  1468  			`,
  1469  			"/Users/user/project/node_modules/pkg/bar.js": `
  1470  				module.exports = 'abc'
  1471  			`,
  1472  		},
  1473  		entryPaths: []string{"/Users/user/project/entry.js"},
  1474  		options: config.Options{
  1475  			Mode:          config.ModeBundle,
  1476  			AbsOutputFile: "/out.js",
  1477  		},
  1478  	})
  1479  }
  1480  
  1481  func TestTreeShakingImportIdentifier(t *testing.T) {
  1482  	dce_suite.expectBundled(t, bundled{
  1483  		files: map[string]string{
  1484  			"/entry.js": `
  1485  				import * as a from './a'
  1486  				new a.Keep()
  1487  			`,
  1488  			"/a.js": `
  1489  				import * as b from './b'
  1490  				export class Keep extends b.Base {}
  1491  				export class REMOVE extends b.Base {}
  1492  			`,
  1493  			"/b.js": `
  1494  				export class Base {}
  1495  			`,
  1496  		},
  1497  		entryPaths: []string{"/entry.js"},
  1498  		options: config.Options{
  1499  			Mode:          config.ModeBundle,
  1500  			AbsOutputFile: "/out.js",
  1501  		},
  1502  	})
  1503  }
  1504  
  1505  func TestTreeShakingObjectProperty(t *testing.T) {
  1506  	dce_suite.expectBundled(t, bundled{
  1507  		files: map[string]string{
  1508  			"/entry.js": `
  1509  				let remove1 = { x: 'x' }
  1510  				let remove2 = { x() {} }
  1511  				let remove3 = { get x() {} }
  1512  				let remove4 = { set x(_) {} }
  1513  				let remove5 = { async x() {} }
  1514  				let remove6 = { ['x']: 'x' }
  1515  				let remove7 = { ['x']() {} }
  1516  				let remove8 = { get ['x']() {} }
  1517  				let remove9 = { set ['x'](_) {} }
  1518  				let remove10 = { async ['x']() {} }
  1519  				let remove11 = { [0]: 'x' }
  1520  				let remove12 = { [null]: 'x' }
  1521  				let remove13 = { [undefined]: 'x' }
  1522  				let remove14 = { [false]: 'x' }
  1523  				let remove15 = { [0n]: 'x' }
  1524  				let remove16 = { toString() {} }
  1525  
  1526  				let keep1 = { x }
  1527  				let keep2 = { x: x }
  1528  				let keep3 = { ...x }
  1529  				let keep4 = { [x]: 'x' }
  1530  				let keep5 = { [x]() {} }
  1531  				let keep6 = { get [x]() {} }
  1532  				let keep7 = { set [x](_) {} }
  1533  				let keep8 = { async [x]() {} }
  1534  				let keep9 = { [{ toString() {} }]: 'x' }
  1535  			`,
  1536  		},
  1537  		entryPaths: []string{"/entry.js"},
  1538  		options: config.Options{
  1539  			Mode:          config.ModePassThrough,
  1540  			TreeShaking:   true,
  1541  			AbsOutputFile: "/out.js",
  1542  		},
  1543  	})
  1544  }
  1545  
  1546  func TestTreeShakingClassProperty(t *testing.T) {
  1547  	dce_suite.expectBundled(t, bundled{
  1548  		files: map[string]string{
  1549  			"/entry.js": `
  1550  				let remove1 = class { x }
  1551  				let remove2 = class { x = x }
  1552  				let remove3 = class { x() {} }
  1553  				let remove4 = class { get x() {} }
  1554  				let remove5 = class { set x(_) {} }
  1555  				let remove6 = class { async x() {} }
  1556  				let remove7 = class { ['x'] = x }
  1557  				let remove8 = class { ['x']() {} }
  1558  				let remove9 = class { get ['x']() {} }
  1559  				let remove10 = class { set ['x'](_) {} }
  1560  				let remove11 = class { async ['x']() {} }
  1561  				let remove12 = class { [0] = 'x' }
  1562  				let remove13 = class { [null] = 'x' }
  1563  				let remove14 = class { [undefined] = 'x' }
  1564  				let remove15 = class { [false] = 'x' }
  1565  				let remove16 = class { [0n] = 'x' }
  1566  				let remove17 = class { toString() {} }
  1567  
  1568  				let keep1 = class { [x] = 'x' }
  1569  				let keep2 = class { [x]() {} }
  1570  				let keep3 = class { get [x]() {} }
  1571  				let keep4 = class { set [x](_) {} }
  1572  				let keep5 = class { async [x]() {} }
  1573  				let keep6 = class { [{ toString() {} }] = 'x' }
  1574  			`,
  1575  		},
  1576  		entryPaths: []string{"/entry.js"},
  1577  		options: config.Options{
  1578  			Mode:          config.ModePassThrough,
  1579  			TreeShaking:   true,
  1580  			AbsOutputFile: "/out.js",
  1581  		},
  1582  	})
  1583  }
  1584  
  1585  func TestTreeShakingClassStaticProperty(t *testing.T) {
  1586  	dce_suite.expectBundled(t, bundled{
  1587  		files: map[string]string{
  1588  			"/entry.js": `
  1589  				let remove1 = class { static x }
  1590  				let remove3 = class { static x() {} }
  1591  				let remove4 = class { static get x() {} }
  1592  				let remove5 = class { static set x(_) {} }
  1593  				let remove6 = class { static async x() {} }
  1594  				let remove8 = class { static ['x']() {} }
  1595  				let remove9 = class { static get ['x']() {} }
  1596  				let remove10 = class { static set ['x'](_) {} }
  1597  				let remove11 = class { static async ['x']() {} }
  1598  				let remove12 = class { static [0] = 'x' }
  1599  				let remove13 = class { static [null] = 'x' }
  1600  				let remove14 = class { static [undefined] = 'x' }
  1601  				let remove15 = class { static [false] = 'x' }
  1602  				let remove16 = class { static [0n] = 'x' }
  1603  				let remove17 = class { static toString() {} }
  1604  
  1605  				let keep1 = class { static x = x }
  1606  				let keep2 = class { static ['x'] = x }
  1607  				let keep3 = class { static [x] = 'x' }
  1608  				let keep4 = class { static [x]() {} }
  1609  				let keep5 = class { static get [x]() {} }
  1610  				let keep6 = class { static set [x](_) {} }
  1611  				let keep7 = class { static async [x]() {} }
  1612  				let keep8 = class { static [{ toString() {} }] = 'x' }
  1613  			`,
  1614  		},
  1615  		entryPaths: []string{"/entry.js"},
  1616  		options: config.Options{
  1617  			Mode:          config.ModePassThrough,
  1618  			TreeShaking:   true,
  1619  			AbsOutputFile: "/out.js",
  1620  		},
  1621  	})
  1622  }
  1623  
  1624  func TestTreeShakingUnaryOperators(t *testing.T) {
  1625  	dce_suite.expectBundled(t, bundled{
  1626  		files: map[string]string{
  1627  			"/entry.js": `
  1628  				// These operators may have side effects
  1629  				let keep;
  1630  				+keep;
  1631  				-keep;
  1632  				~keep;
  1633  				delete keep;
  1634  				++keep;
  1635  				--keep;
  1636  				keep++;
  1637  				keep--;
  1638  
  1639  				// These operators never have side effects
  1640  				let REMOVE;
  1641  				!REMOVE;
  1642  				void REMOVE;
  1643  			`,
  1644  		},
  1645  		entryPaths: []string{"/entry.js"},
  1646  		options: config.Options{
  1647  			Mode:          config.ModeBundle,
  1648  			AbsOutputFile: "/out.js",
  1649  			OutputFormat:  config.FormatIIFE,
  1650  		},
  1651  	})
  1652  }
  1653  
  1654  func TestTreeShakingBinaryOperators(t *testing.T) {
  1655  	dce_suite.expectBundled(t, bundled{
  1656  		files: map[string]string{
  1657  			"/entry.js": `
  1658  				// These operators may have side effects
  1659  				let keep, keep2;
  1660  				keep + keep2;
  1661  				keep - keep2;
  1662  				keep * keep2;
  1663  				keep / keep2;
  1664  				keep % keep2;
  1665  				keep ** keep2;
  1666  				keep < keep2;
  1667  				keep <= keep2;
  1668  				keep > keep2;
  1669  				keep >= keep2;
  1670  				keep in keep2;
  1671  				keep instanceof keep2;
  1672  				keep << keep2;
  1673  				keep >> keep2;
  1674  				keep >>> keep2;
  1675  				keep == keep2;
  1676  				keep != keep2;
  1677  				keep | keep2;
  1678  				keep & keep2;
  1679  				keep ^ keep2;
  1680  				keep = keep2;
  1681  				keep += keep2;
  1682  				keep -= keep2;
  1683  				keep *= keep2;
  1684  				keep /= keep2;
  1685  				keep %= keep2;
  1686  				keep **= keep2;
  1687  				keep <<= keep2;
  1688  				keep >>= keep2;
  1689  				keep >>>= keep2;
  1690  				keep |= keep2;
  1691  				keep &= keep2;
  1692  				keep ^= keep2;
  1693  				keep ??= keep2;
  1694  				keep ||= keep2;
  1695  				keep &&= keep2;
  1696  
  1697  				// These operators never have side effects
  1698  				let REMOVE, REMOVE2;
  1699  				REMOVE === REMOVE2;
  1700  				REMOVE !== REMOVE2;
  1701  				REMOVE, REMOVE2;
  1702  				REMOVE ?? REMOVE2;
  1703  				REMOVE || REMOVE2;
  1704  				REMOVE && REMOVE2;
  1705  			`,
  1706  		},
  1707  		entryPaths: []string{"/entry.js"},
  1708  		options: config.Options{
  1709  			Mode:          config.ModeBundle,
  1710  			AbsOutputFile: "/out.js",
  1711  		},
  1712  	})
  1713  }
  1714  
  1715  func TestTreeShakingNoBundleESM(t *testing.T) {
  1716  	dce_suite.expectBundled(t, bundled{
  1717  		files: map[string]string{
  1718  			"/entry.js": `
  1719  				function keep() {}
  1720  				function unused() {}
  1721  				keep()
  1722  			`,
  1723  		},
  1724  		entryPaths: []string{"/entry.js"},
  1725  		options: config.Options{
  1726  			Mode:          config.ModeConvertFormat,
  1727  			OutputFormat:  config.FormatESModule,
  1728  			AbsOutputFile: "/out.js",
  1729  		},
  1730  	})
  1731  }
  1732  
  1733  func TestTreeShakingNoBundleCJS(t *testing.T) {
  1734  	dce_suite.expectBundled(t, bundled{
  1735  		files: map[string]string{
  1736  			"/entry.js": `
  1737  				function keep() {}
  1738  				function unused() {}
  1739  				keep()
  1740  			`,
  1741  		},
  1742  		entryPaths: []string{"/entry.js"},
  1743  		options: config.Options{
  1744  			Mode:          config.ModeConvertFormat,
  1745  			OutputFormat:  config.FormatCommonJS,
  1746  			AbsOutputFile: "/out.js",
  1747  		},
  1748  	})
  1749  }
  1750  
  1751  func TestTreeShakingNoBundleIIFE(t *testing.T) {
  1752  	dce_suite.expectBundled(t, bundled{
  1753  		files: map[string]string{
  1754  			"/entry.js": `
  1755  				function keep() {}
  1756  				function REMOVE() {}
  1757  				keep()
  1758  			`,
  1759  		},
  1760  		entryPaths: []string{"/entry.js"},
  1761  		options: config.Options{
  1762  			Mode:          config.ModeConvertFormat,
  1763  			OutputFormat:  config.FormatIIFE,
  1764  			AbsOutputFile: "/out.js",
  1765  		},
  1766  	})
  1767  }
  1768  
  1769  func TestTreeShakingInESMWrapper(t *testing.T) {
  1770  	dce_suite.expectBundled(t, bundled{
  1771  		files: map[string]string{
  1772  			"/entry.js": `
  1773  				import {keep1} from './lib'
  1774  				console.log(keep1(), require('./cjs'))
  1775  			`,
  1776  			"/cjs.js": `
  1777  				import {keep2} from './lib'
  1778  				export default keep2()
  1779  			`,
  1780  			"/lib.js": `
  1781  				export let keep1 = () => 'keep1'
  1782  				export let keep2 = () => 'keep2'
  1783  				export let REMOVE = () => 'REMOVE'
  1784  			`,
  1785  		},
  1786  		entryPaths: []string{"/entry.js"},
  1787  		options: config.Options{
  1788  			Mode:          config.ModeBundle,
  1789  			OutputFormat:  config.FormatESModule,
  1790  			AbsOutputFile: "/out.js",
  1791  		},
  1792  	})
  1793  }
  1794  
  1795  func TestDCETypeOf(t *testing.T) {
  1796  	dce_suite.expectBundled(t, bundled{
  1797  		files: map[string]string{
  1798  			"/entry.js": `
  1799  				// These should be removed because they have no side effects
  1800  				typeof x_REMOVE
  1801  				typeof v_REMOVE
  1802  				typeof f_REMOVE
  1803  				typeof g_REMOVE
  1804  				typeof a_REMOVE
  1805  				var v_REMOVE
  1806  				function f_REMOVE() {}
  1807  				function* g_REMOVE() {}
  1808  				async function a_REMOVE() {}
  1809  
  1810  				// These technically have side effects due to TDZ, but this is not currently handled
  1811  				typeof c_remove
  1812  				typeof l_remove
  1813  				typeof s_remove
  1814  				const c_remove = 0
  1815  				let l_remove
  1816  				class s_remove {}
  1817  			`,
  1818  		},
  1819  		entryPaths: []string{"/entry.js"},
  1820  		options: config.Options{
  1821  			Mode:          config.ModeBundle,
  1822  			OutputFormat:  config.FormatESModule,
  1823  			AbsOutputFile: "/out.js",
  1824  		},
  1825  	})
  1826  }
  1827  
  1828  func TestDCETypeOfEqualsString(t *testing.T) {
  1829  	dce_suite.expectBundled(t, bundled{
  1830  		files: map[string]string{
  1831  			"/entry.js": `
  1832  				var hasBar = typeof bar !== 'undefined'
  1833  				if (false) console.log(hasBar)
  1834  			`,
  1835  		},
  1836  		entryPaths: []string{"/entry.js"},
  1837  		options: config.Options{
  1838  			Mode:          config.ModeBundle,
  1839  			OutputFormat:  config.FormatIIFE,
  1840  			AbsOutputFile: "/out.js",
  1841  		},
  1842  	})
  1843  }
  1844  
  1845  func TestDCETypeOfEqualsStringMangle(t *testing.T) {
  1846  	dce_suite.expectBundled(t, bundled{
  1847  		files: map[string]string{
  1848  			"/entry.js": `
  1849  				// Everything here should be removed as dead code due to tree shaking
  1850  				var hasBar = typeof bar !== 'undefined'
  1851  				if (false) console.log(hasBar)
  1852  			`,
  1853  		},
  1854  		entryPaths: []string{"/entry.js"},
  1855  		options: config.Options{
  1856  			Mode:          config.ModeBundle,
  1857  			OutputFormat:  config.FormatIIFE,
  1858  			MinifySyntax:  true,
  1859  			AbsOutputFile: "/out.js",
  1860  		},
  1861  	})
  1862  }
  1863  
  1864  func TestDCETypeOfEqualsStringGuardCondition(t *testing.T) {
  1865  	dce_suite.expectBundled(t, bundled{
  1866  		files: map[string]string{
  1867  			"/entry.js": `
  1868  				// Everything here should be removed as dead code due to tree shaking
  1869  				var REMOVE_1 = typeof x !== 'undefined' ? x : null
  1870  				var REMOVE_1 = typeof x != 'undefined' ? x : null
  1871  				var REMOVE_1 = typeof x === 'undefined' ? null : x
  1872  				var REMOVE_1 = typeof x == 'undefined' ? null : x
  1873  				var REMOVE_1 = typeof x !== 'undefined' && x
  1874  				var REMOVE_1 = typeof x != 'undefined' && x
  1875  				var REMOVE_1 = typeof x === 'undefined' || x
  1876  				var REMOVE_1 = typeof x == 'undefined' || x
  1877  				var REMOVE_1 = 'undefined' !== typeof x ? x : null
  1878  				var REMOVE_1 = 'undefined' != typeof x ? x : null
  1879  				var REMOVE_1 = 'undefined' === typeof x ? null : x
  1880  				var REMOVE_1 = 'undefined' == typeof x ? null : x
  1881  				var REMOVE_1 = 'undefined' !== typeof x && x
  1882  				var REMOVE_1 = 'undefined' != typeof x && x
  1883  				var REMOVE_1 = 'undefined' === typeof x || x
  1884  				var REMOVE_1 = 'undefined' == typeof x || x
  1885  
  1886  				// Everything here should be removed as dead code due to tree shaking
  1887  				var REMOVE_2 = typeof x === 'object' ? x : null
  1888  				var REMOVE_2 = typeof x == 'object' ? x : null
  1889  				var REMOVE_2 = typeof x !== 'object' ? null : x
  1890  				var REMOVE_2 = typeof x != 'object' ? null : x
  1891  				var REMOVE_2 = typeof x === 'object' && x
  1892  				var REMOVE_2 = typeof x == 'object' && x
  1893  				var REMOVE_2 = typeof x !== 'object' || x
  1894  				var REMOVE_2 = typeof x != 'object' || x
  1895  				var REMOVE_2 = 'object' === typeof x ? x : null
  1896  				var REMOVE_2 = 'object' == typeof x ? x : null
  1897  				var REMOVE_2 = 'object' !== typeof x ? null : x
  1898  				var REMOVE_2 = 'object' != typeof x ? null : x
  1899  				var REMOVE_2 = 'object' === typeof x && x
  1900  				var REMOVE_2 = 'object' == typeof x && x
  1901  				var REMOVE_2 = 'object' !== typeof x || x
  1902  				var REMOVE_2 = 'object' != typeof x || x
  1903  
  1904  				// Everything here should be kept as live code because it has side effects
  1905  				var keep_1 = typeof x !== 'object' ? x : null
  1906  				var keep_1 = typeof x != 'object' ? x : null
  1907  				var keep_1 = typeof x === 'object' ? null : x
  1908  				var keep_1 = typeof x == 'object' ? null : x
  1909  				var keep_1 = typeof x !== 'object' && x
  1910  				var keep_1 = typeof x != 'object' && x
  1911  				var keep_1 = typeof x === 'object' || x
  1912  				var keep_1 = typeof x == 'object' || x
  1913  				var keep_1 = 'object' !== typeof x ? x : null
  1914  				var keep_1 = 'object' != typeof x ? x : null
  1915  				var keep_1 = 'object' === typeof x ? null : x
  1916  				var keep_1 = 'object' == typeof x ? null : x
  1917  				var keep_1 = 'object' !== typeof x && x
  1918  				var keep_1 = 'object' != typeof x && x
  1919  				var keep_1 = 'object' === typeof x || x
  1920  				var keep_1 = 'object' == typeof x || x
  1921  
  1922  				// Everything here should be kept as live code because it has side effects
  1923  				var keep_2 = typeof x !== 'undefined' ? y : null
  1924  				var keep_2 = typeof x != 'undefined' ? y : null
  1925  				var keep_2 = typeof x === 'undefined' ? null : y
  1926  				var keep_2 = typeof x == 'undefined' ? null : y
  1927  				var keep_2 = typeof x !== 'undefined' && y
  1928  				var keep_2 = typeof x != 'undefined' && y
  1929  				var keep_2 = typeof x === 'undefined' || y
  1930  				var keep_2 = typeof x == 'undefined' || y
  1931  				var keep_2 = 'undefined' !== typeof x ? y : null
  1932  				var keep_2 = 'undefined' != typeof x ? y : null
  1933  				var keep_2 = 'undefined' === typeof x ? null : y
  1934  				var keep_2 = 'undefined' == typeof x ? null : y
  1935  				var keep_2 = 'undefined' !== typeof x && y
  1936  				var keep_2 = 'undefined' != typeof x && y
  1937  				var keep_2 = 'undefined' === typeof x || y
  1938  				var keep_2 = 'undefined' == typeof x || y
  1939  
  1940  				// Everything here should be kept as live code because it has side effects
  1941  				var keep_3 = typeof x !== 'undefined' ? null : x
  1942  				var keep_3 = typeof x != 'undefined' ? null : x
  1943  				var keep_3 = typeof x === 'undefined' ? x : null
  1944  				var keep_3 = typeof x == 'undefined' ? x : null
  1945  				var keep_3 = typeof x !== 'undefined' || x
  1946  				var keep_3 = typeof x != 'undefined' || x
  1947  				var keep_3 = typeof x === 'undefined' && x
  1948  				var keep_3 = typeof x == 'undefined' && x
  1949  				var keep_3 = 'undefined' !== typeof x ? null : x
  1950  				var keep_3 = 'undefined' != typeof x ? null : x
  1951  				var keep_3 = 'undefined' === typeof x ? x : null
  1952  				var keep_3 = 'undefined' == typeof x ? x : null
  1953  				var keep_3 = 'undefined' !== typeof x || x
  1954  				var keep_3 = 'undefined' != typeof x || x
  1955  				var keep_3 = 'undefined' === typeof x && x
  1956  				var keep_3 = 'undefined' == typeof x && x
  1957  			`,
  1958  		},
  1959  		entryPaths: []string{"/entry.js"},
  1960  		options: config.Options{
  1961  			Mode:          config.ModeBundle,
  1962  			OutputFormat:  config.FormatIIFE,
  1963  			AbsOutputFile: "/out.js",
  1964  		},
  1965  	})
  1966  }
  1967  
  1968  func TestDCETypeOfCompareStringGuardCondition(t *testing.T) {
  1969  	dce_suite.expectBundled(t, bundled{
  1970  		files: map[string]string{
  1971  			"/entry.js": `
  1972  				// Everything here should be removed as dead code due to tree shaking
  1973  				var REMOVE_1 = typeof x <= 'u' ? x : null
  1974  				var REMOVE_1 = typeof x < 'u' ? x : null
  1975  				var REMOVE_1 = typeof x >= 'u' ? null : x
  1976  				var REMOVE_1 = typeof x > 'u' ? null : x
  1977  				var REMOVE_1 = typeof x <= 'u' && x
  1978  				var REMOVE_1 = typeof x < 'u' && x
  1979  				var REMOVE_1 = typeof x >= 'u' || x
  1980  				var REMOVE_1 = typeof x > 'u' || x
  1981  				var REMOVE_1 = 'u' >= typeof x ? x : null
  1982  				var REMOVE_1 = 'u' > typeof x ? x : null
  1983  				var REMOVE_1 = 'u' <= typeof x ? null : x
  1984  				var REMOVE_1 = 'u' < typeof x ? null : x
  1985  				var REMOVE_1 = 'u' >= typeof x && x
  1986  				var REMOVE_1 = 'u' > typeof x && x
  1987  				var REMOVE_1 = 'u' <= typeof x || x
  1988  				var REMOVE_1 = 'u' < typeof x || x
  1989  
  1990  				// Everything here should be kept as live code because it has side effects
  1991  				var keep_1 = typeof x <= 'u' ? y : null
  1992  				var keep_1 = typeof x < 'u' ? y : null
  1993  				var keep_1 = typeof x >= 'u' ? null : y
  1994  				var keep_1 = typeof x > 'u' ? null : y
  1995  				var keep_1 = typeof x <= 'u' && y
  1996  				var keep_1 = typeof x < 'u' && y
  1997  				var keep_1 = typeof x >= 'u' || y
  1998  				var keep_1 = typeof x > 'u' || y
  1999  				var keep_1 = 'u' >= typeof x ? y : null
  2000  				var keep_1 = 'u' > typeof x ? y : null
  2001  				var keep_1 = 'u' <= typeof x ? null : y
  2002  				var keep_1 = 'u' < typeof x ? null : y
  2003  				var keep_1 = 'u' >= typeof x && y
  2004  				var keep_1 = 'u' > typeof x && y
  2005  				var keep_1 = 'u' <= typeof x || y
  2006  				var keep_1 = 'u' < typeof x || y
  2007  
  2008  				// Everything here should be kept as live code because it has side effects
  2009  				var keep_2 = typeof x <= 'u' ? null : x
  2010  				var keep_2 = typeof x < 'u' ? null : x
  2011  				var keep_2 = typeof x >= 'u' ? x : null
  2012  				var keep_2 = typeof x > 'u' ? x : null
  2013  				var keep_2 = typeof x <= 'u' || x
  2014  				var keep_2 = typeof x < 'u' || x
  2015  				var keep_2 = typeof x >= 'u' && x
  2016  				var keep_2 = typeof x > 'u' && x
  2017  				var keep_2 = 'u' >= typeof x ? null : x
  2018  				var keep_2 = 'u' > typeof x ? null : x
  2019  				var keep_2 = 'u' <= typeof x ? x : null
  2020  				var keep_2 = 'u' < typeof x ? x : null
  2021  				var keep_2 = 'u' >= typeof x || x
  2022  				var keep_2 = 'u' > typeof x || x
  2023  				var keep_2 = 'u' <= typeof x && x
  2024  				var keep_2 = 'u' < typeof x && x
  2025  			`,
  2026  		},
  2027  		entryPaths: []string{"/entry.js"},
  2028  		options: config.Options{
  2029  			Mode:          config.ModeBundle,
  2030  			OutputFormat:  config.FormatIIFE,
  2031  			AbsOutputFile: "/out.js",
  2032  		},
  2033  	})
  2034  }
  2035  
  2036  // These unused imports should be removed since they aren't used, and removing
  2037  // them makes the code shorter.
  2038  func TestRemoveUnusedImports(t *testing.T) {
  2039  	dce_suite.expectBundled(t, bundled{
  2040  		files: map[string]string{
  2041  			"/entry.js": `
  2042  				import a from 'a'
  2043  				import * as b from 'b'
  2044  				import {c} from 'c'
  2045  			`,
  2046  		},
  2047  		entryPaths: []string{"/entry.js"},
  2048  		options: config.Options{
  2049  			Mode:          config.ModePassThrough,
  2050  			MinifySyntax:  true,
  2051  			AbsOutputFile: "/out.js",
  2052  		},
  2053  	})
  2054  }
  2055  
  2056  // These unused imports should be kept since the direct eval could potentially
  2057  // reference them, even though they appear to be unused.
  2058  func TestRemoveUnusedImportsEval(t *testing.T) {
  2059  	dce_suite.expectBundled(t, bundled{
  2060  		files: map[string]string{
  2061  			"/entry.js": `
  2062  				import a from 'a'
  2063  				import * as b from 'b'
  2064  				import {c} from 'c'
  2065  				eval('foo(a, b, c)')
  2066  			`,
  2067  		},
  2068  		entryPaths: []string{"/entry.js"},
  2069  		options: config.Options{
  2070  			Mode:          config.ModePassThrough,
  2071  			MinifySyntax:  true,
  2072  			AbsOutputFile: "/out.js",
  2073  		},
  2074  	})
  2075  }
  2076  
  2077  // These unused imports should be removed even though there is a direct eval
  2078  // because they may be types, not values, so keeping them will likely cause
  2079  // module instantiation failures. It's still true that direct eval could
  2080  // access them of course, but that's very unlikely while module instantiation
  2081  // failure is very likely so we bias towards the likely case here instead.
  2082  func TestRemoveUnusedImportsEvalTS(t *testing.T) {
  2083  	dce_suite.expectBundled(t, bundled{
  2084  		files: map[string]string{
  2085  			"/entry.ts": `
  2086  				import a from 'a'
  2087  				import * as b from 'b'
  2088  				import {c} from 'c'
  2089  				eval('foo(a, b, c)')
  2090  			`,
  2091  		},
  2092  		entryPaths: []string{"/entry.js"},
  2093  		options: config.Options{
  2094  			Mode:          config.ModePassThrough,
  2095  			MinifySyntax:  true,
  2096  			AbsOutputFile: "/out.js",
  2097  		},
  2098  	})
  2099  }
  2100  
  2101  func TestDCEClassStaticBlocks(t *testing.T) {
  2102  	dce_suite.expectBundled(t, bundled{
  2103  		files: map[string]string{
  2104  			"/entry.ts": `
  2105  				class A_REMOVE {
  2106  					static {}
  2107  				}
  2108  				class B_REMOVE {
  2109  					static { 123 }
  2110  				}
  2111  				class C_REMOVE {
  2112  					static { /* @__PURE__*/ foo() }
  2113  				}
  2114  				class D_REMOVE {
  2115  					static { try {} catch {} }
  2116  				}
  2117  				class E_REMOVE {
  2118  					static { try { /* @__PURE__*/ foo() } catch {} }
  2119  				}
  2120  				class F_REMOVE {
  2121  					static { try { 123 } catch { 123 } finally { 123 } }
  2122  				}
  2123  
  2124  				class A_keep {
  2125  					static { foo }
  2126  				}
  2127  				class B_keep {
  2128  					static { this.foo }
  2129  				}
  2130  				class C_keep {
  2131  					static { try { foo } catch {} }
  2132  				}
  2133  				class D_keep {
  2134  					static { try {} finally { foo } }
  2135  				}
  2136  			`,
  2137  		},
  2138  		entryPaths: []string{"/entry.js"},
  2139  		options: config.Options{
  2140  			Mode:          config.ModeBundle,
  2141  			AbsOutputFile: "/out.js",
  2142  		},
  2143  	})
  2144  }
  2145  
  2146  func TestDCEClassStaticBlocksMinifySyntax(t *testing.T) {
  2147  	dce_suite.expectBundled(t, bundled{
  2148  		files: map[string]string{
  2149  			"/entry.ts": `
  2150  				class A_REMOVE {
  2151  					static {}
  2152  				}
  2153  				class B_REMOVE {
  2154  					static { 123 }
  2155  				}
  2156  				class C_REMOVE {
  2157  					static { /* @__PURE__*/ foo() }
  2158  				}
  2159  				class D_REMOVE {
  2160  					static { try {} catch {} }
  2161  				}
  2162  				class E_REMOVE {
  2163  					static { try { /* @__PURE__*/ foo() } catch {} }
  2164  				}
  2165  				class F_REMOVE {
  2166  					static { try { 123 } catch { 123 } finally { 123 } }
  2167  				}
  2168  
  2169  				class A_keep {
  2170  					static { foo }
  2171  				}
  2172  				class B_keep {
  2173  					static { this.foo }
  2174  				}
  2175  				class C_keep {
  2176  					static { try { foo } catch {} }
  2177  				}
  2178  				class D_keep {
  2179  					static { try {} finally { foo } }
  2180  				}
  2181  			`,
  2182  		},
  2183  		entryPaths: []string{"/entry.js"},
  2184  		options: config.Options{
  2185  			Mode:          config.ModeBundle,
  2186  			AbsOutputFile: "/out.js",
  2187  			MinifySyntax:  true,
  2188  		},
  2189  	})
  2190  }
  2191  
  2192  func TestDCEVarExports(t *testing.T) {
  2193  	dce_suite.expectBundled(t, bundled{
  2194  		files: map[string]string{
  2195  			"/a.js": `
  2196  				var foo = { bar: 123 }
  2197  				module.exports = foo
  2198  			`,
  2199  			"/b.js": `
  2200  				var exports = { bar: 123 }
  2201  				module.exports = exports
  2202  			`,
  2203  			"/c.js": `
  2204  				var module = { bar: 123 }
  2205  				exports.foo = module
  2206  			`,
  2207  		},
  2208  		entryPaths: []string{"/a.js", "/b.js", "/c.js"},
  2209  		options: config.Options{
  2210  			Mode:         config.ModeBundle,
  2211  			AbsOutputDir: "/out",
  2212  		},
  2213  	})
  2214  }
  2215  
  2216  func TestDCETemplateLiteral(t *testing.T) {
  2217  	dce_suite.expectBundled(t, bundled{
  2218  		files: map[string]string{
  2219  			"/entry.js": "" +
  2220  				"var remove;\n" +
  2221  				"var alsoKeep;\n" +
  2222  				"let a = `${keep}`\n" +
  2223  				"let b = `${123}`\n" +
  2224  				"let c = `${keep ? 1 : 2n}`\n" +
  2225  				"let d = `${remove ? 1 : 2n}`\n" +
  2226  				"let e = `${alsoKeep}`\n",
  2227  		},
  2228  		entryPaths: []string{"/entry.js"},
  2229  		options: config.Options{
  2230  			Mode:         config.ModeBundle,
  2231  			AbsOutputDir: "/out",
  2232  		},
  2233  	})
  2234  }
  2235  
  2236  // Calls to the runtime "__publicField" function are not considered side effects
  2237  func TestTreeShakingLoweredClassStaticField(t *testing.T) {
  2238  	dce_suite.expectBundled(t, bundled{
  2239  		files: map[string]string{
  2240  			"/entry.js": `
  2241  				class REMOVE_ME {
  2242  					static x = 'x'
  2243  					static y = 'y'
  2244  					static z = 'z'
  2245  				}
  2246  				function REMOVE_ME_TOO() {
  2247  					new REMOVE_ME()
  2248  				}
  2249  				class KeepMe1 {
  2250  					static x = 'x'
  2251  					static y = sideEffects()
  2252  					static z = 'z'
  2253  				}
  2254  				class KeepMe2 {
  2255  					static x = 'x'
  2256  					static y = 'y'
  2257  					static z = 'z'
  2258  				}
  2259  				new KeepMe2()
  2260  			`,
  2261  		},
  2262  		entryPaths: []string{"/entry.js"},
  2263  		options: config.Options{
  2264  			Mode:                  config.ModeBundle,
  2265  			AbsOutputDir:          "/out",
  2266  			UnsupportedJSFeatures: compat.ClassStaticField,
  2267  		},
  2268  	})
  2269  }
  2270  
  2271  func TestTreeShakingLoweredClassStaticFieldMinified(t *testing.T) {
  2272  	dce_suite.expectBundled(t, bundled{
  2273  		files: map[string]string{
  2274  			"/entry.js": `
  2275  				class REMOVE_ME {
  2276  					static x = 'x'
  2277  					static y = 'y'
  2278  					static z = 'z'
  2279  				}
  2280  				function REMOVE_ME_TOO() {
  2281  					new REMOVE_ME()
  2282  				}
  2283  				class KeepMe1 {
  2284  					static x = 'x'
  2285  					static y = sideEffects()
  2286  					static z = 'z'
  2287  				}
  2288  				class KeepMe2 {
  2289  					static x = 'x'
  2290  					static y = 'y'
  2291  					static z = 'z'
  2292  				}
  2293  				new KeepMe2()
  2294  			`,
  2295  		},
  2296  		entryPaths: []string{"/entry.js"},
  2297  		options: config.Options{
  2298  			Mode:                  config.ModeBundle,
  2299  			AbsOutputDir:          "/out",
  2300  			UnsupportedJSFeatures: compat.ClassStaticField,
  2301  			MinifySyntax:          true,
  2302  		},
  2303  	})
  2304  }
  2305  
  2306  // Assignments are considered side effects
  2307  func TestTreeShakingLoweredClassStaticFieldAssignment(t *testing.T) {
  2308  	dce_suite.expectBundled(t, bundled{
  2309  		files: map[string]string{
  2310  			"/entry.ts": `
  2311  				class KeepMe1 {
  2312  					static x = 'x'
  2313  					static y = 'y'
  2314  					static z = 'z'
  2315  				}
  2316  				class KeepMe2 {
  2317  					static x = 'x'
  2318  					static y = sideEffects()
  2319  					static z = 'z'
  2320  				}
  2321  				class KeepMe3 {
  2322  					static x = 'x'
  2323  					static y = 'y'
  2324  					static z = 'z'
  2325  				}
  2326  				new KeepMe3()
  2327  			`,
  2328  		},
  2329  		entryPaths: []string{"/entry.js"},
  2330  		options: config.Options{
  2331  			Mode:                  config.ModeBundle,
  2332  			AbsOutputDir:          "/out",
  2333  			UnsupportedJSFeatures: compat.ClassStaticField,
  2334  			TS: config.TSOptions{Config: config.TSConfig{
  2335  				UseDefineForClassFields: config.False,
  2336  			}},
  2337  		},
  2338  	})
  2339  }
  2340  
  2341  func TestInlineIdentityFunctionCalls(t *testing.T) {
  2342  	dce_suite.expectBundled(t, bundled{
  2343  		files: map[string]string{
  2344  			"/identity.js": `
  2345  				function DROP(x) { return x }
  2346  				console.log(DROP(1))
  2347  				DROP(foo())
  2348  				DROP(1)
  2349  			`,
  2350  
  2351  			"/identity-last.js": `
  2352  				function DROP(x) { return [x] }
  2353  				function DROP(x) { return x }
  2354  				console.log(DROP(1))
  2355  				DROP(foo())
  2356  				DROP(1)
  2357  			`,
  2358  
  2359  			"/identity-cross-module.js": `
  2360  				import { DROP } from './identity-cross-module-def'
  2361  				console.log(DROP(1))
  2362  				DROP(foo())
  2363  				DROP(1)
  2364  			`,
  2365  
  2366  			"/identity-cross-module-def.js": `
  2367  				export function DROP(x) { return x }
  2368  			`,
  2369  
  2370  			"/identity-no-args.js": `
  2371  				function keep(x) { return x }
  2372  				console.log(keep())
  2373  				keep()
  2374  			`,
  2375  
  2376  			"/identity-two-args.js": `
  2377  				function keep(x) { return x }
  2378  				console.log(keep(1, 2))
  2379  				keep(1, 2)
  2380  			`,
  2381  
  2382  			"/identity-first.js": `
  2383  				function keep(x) { return x }
  2384  				function keep(x) { return [x] }
  2385  				console.log(keep(1))
  2386  				keep(foo())
  2387  				keep(1)
  2388  			`,
  2389  
  2390  			"/identity-generator.js": `
  2391  				function* keep(x) { return x }
  2392  				console.log(keep(1))
  2393  				keep(foo())
  2394  				keep(1)
  2395  			`,
  2396  
  2397  			"/identity-async.js": `
  2398  				async function keep(x) { return x }
  2399  				console.log(keep(1))
  2400  				keep(foo())
  2401  				keep(1)
  2402  			`,
  2403  
  2404  			"/reassign.js": `
  2405  				function keep(x) { return x }
  2406  				keep = reassigned
  2407  				console.log(keep(1))
  2408  				keep(foo())
  2409  				keep(1)
  2410  			`,
  2411  
  2412  			"/reassign-inc.js": `
  2413  				function keep(x) { return x }
  2414  				keep++
  2415  				console.log(keep(1))
  2416  				keep(foo())
  2417  				keep(1)
  2418  			`,
  2419  
  2420  			"/reassign-div.js": `
  2421  				function keep(x) { return x }
  2422  				keep /= reassigned
  2423  				console.log(keep(1))
  2424  				keep(foo())
  2425  				keep(1)
  2426  			`,
  2427  
  2428  			"/reassign-array.js": `
  2429  				function keep(x) { return x }
  2430  				[keep] = reassigned
  2431  				console.log(keep(1))
  2432  				keep(foo())
  2433  				keep(1)
  2434  			`,
  2435  
  2436  			"/reassign-object.js": `
  2437  				function keep(x) { return x }
  2438  				({keep} = reassigned)
  2439  				console.log(keep(1))
  2440  				keep(foo())
  2441  				keep(1)
  2442  			`,
  2443  
  2444  			"/not-identity-two-args.js": `
  2445  				function keep(x, y) { return x }
  2446  				console.log(keep(1))
  2447  				keep(foo())
  2448  				keep(1)
  2449  			`,
  2450  
  2451  			"/not-identity-default.js": `
  2452  				function keep(x = foo()) { return x }
  2453  				console.log(keep(1))
  2454  				keep(foo())
  2455  				keep(1)
  2456  			`,
  2457  
  2458  			"/not-identity-array.js": `
  2459  				function keep([x]) { return x }
  2460  				console.log(keep(1))
  2461  				keep(foo())
  2462  				keep(1)
  2463  			`,
  2464  
  2465  			"/not-identity-object.js": `
  2466  				function keep({x}) { return x }
  2467  				console.log(keep(1))
  2468  				keep(foo())
  2469  				keep(1)
  2470  			`,
  2471  
  2472  			"/not-identity-rest.js": `
  2473  				function keep(...x) { return x }
  2474  				console.log(keep(1))
  2475  				keep(foo())
  2476  				keep(1)
  2477  			`,
  2478  
  2479  			"/not-identity-return.js": `
  2480  				function keep(x) { return [x] }
  2481  				console.log(keep(1))
  2482  				keep(foo())
  2483  				keep(1)
  2484  			`,
  2485  		},
  2486  		entryPaths: []string{
  2487  			"/identity.js",
  2488  			"/identity-last.js",
  2489  			"/identity-first.js",
  2490  			"/identity-generator.js",
  2491  			"/identity-async.js",
  2492  			"/identity-cross-module.js",
  2493  			"/identity-no-args.js",
  2494  			"/identity-two-args.js",
  2495  			"/reassign.js",
  2496  			"/reassign-inc.js",
  2497  			"/reassign-div.js",
  2498  			"/reassign-array.js",
  2499  			"/reassign-object.js",
  2500  			"/not-identity-two-args.js",
  2501  			"/not-identity-default.js",
  2502  			"/not-identity-array.js",
  2503  			"/not-identity-object.js",
  2504  			"/not-identity-rest.js",
  2505  			"/not-identity-return.js",
  2506  		},
  2507  		options: config.Options{
  2508  			Mode:         config.ModeBundle,
  2509  			AbsOutputDir: "/out",
  2510  			MinifySyntax: true,
  2511  		},
  2512  	})
  2513  }
  2514  
  2515  func TestInlineEmptyFunctionCalls(t *testing.T) {
  2516  	dce_suite.expectBundled(t, bundled{
  2517  		files: map[string]string{
  2518  			"/empty.js": `
  2519  				function DROP() {}
  2520  				console.log(DROP(foo(), bar()))
  2521  				console.log(DROP(foo(), 1))
  2522  				console.log(DROP(1, foo()))
  2523  				console.log(DROP(1))
  2524  				console.log(DROP())
  2525  				DROP(foo(), bar())
  2526  				DROP(foo(), 1)
  2527  				DROP(1, foo())
  2528  				DROP(1)
  2529  				DROP()
  2530  			`,
  2531  
  2532  			"/empty-comma.js": `
  2533  				function DROP() {}
  2534  				console.log((DROP(), DROP(), foo()))
  2535  				console.log((DROP(), foo(), DROP()))
  2536  				console.log((foo(), DROP(), DROP()))
  2537  				for (DROP(); DROP(); DROP()) DROP();
  2538  				DROP(), DROP(), foo();
  2539  				DROP(), foo(), DROP();
  2540  				foo(), DROP(), DROP();
  2541  			`,
  2542  
  2543  			"/empty-if-else.js": `
  2544  				function DROP() {}
  2545  				if (foo) { let bar = baz(); bar(); bar() } else DROP();
  2546  			`,
  2547  
  2548  			"/empty-last.js": `
  2549  				function DROP() { return x }
  2550  				function DROP() { return }
  2551  				console.log(DROP())
  2552  				DROP()
  2553  			`,
  2554  
  2555  			"/empty-cross-module.js": `
  2556  				import { DROP } from './empty-cross-module-def'
  2557  				console.log(DROP())
  2558  				DROP()
  2559  			`,
  2560  
  2561  			"/empty-cross-module-def.js": `
  2562  				export function DROP() {}
  2563  			`,
  2564  
  2565  			"/empty-first.js": `
  2566  				function keep() { return }
  2567  				function keep() { return x }
  2568  				console.log(keep())
  2569  				keep(foo())
  2570  				keep(1)
  2571  			`,
  2572  
  2573  			"/empty-generator.js": `
  2574  				function* keep() {}
  2575  				console.log(keep())
  2576  				keep(foo())
  2577  				keep(1)
  2578  			`,
  2579  
  2580  			"/empty-async.js": `
  2581  				async function keep() {}
  2582  				console.log(keep())
  2583  				keep(foo())
  2584  				keep(1)
  2585  			`,
  2586  
  2587  			"/reassign.js": `
  2588  				function keep() {}
  2589  				keep = reassigned
  2590  				console.log(keep())
  2591  				keep(foo())
  2592  				keep(1)
  2593  			`,
  2594  
  2595  			"/reassign-inc.js": `
  2596  				function keep() {}
  2597  				keep++
  2598  				console.log(keep(1))
  2599  				keep(foo())
  2600  				keep(1)
  2601  			`,
  2602  
  2603  			"/reassign-div.js": `
  2604  				function keep() {}
  2605  				keep /= reassigned
  2606  				console.log(keep(1))
  2607  				keep(foo())
  2608  				keep(1)
  2609  			`,
  2610  
  2611  			"/reassign-array.js": `
  2612  				function keep() {}
  2613  				[keep] = reassigned
  2614  				console.log(keep(1))
  2615  				keep(foo())
  2616  				keep(1)
  2617  			`,
  2618  
  2619  			"/reassign-object.js": `
  2620  				function keep() {}
  2621  				({keep} = reassigned)
  2622  				console.log(keep(1))
  2623  				keep(foo())
  2624  				keep(1)
  2625  			`,
  2626  		},
  2627  		entryPaths: []string{
  2628  			"/empty.js",
  2629  			"/empty-comma.js",
  2630  			"/empty-if-else.js",
  2631  			"/empty-last.js",
  2632  			"/empty-cross-module.js",
  2633  			"/empty-first.js",
  2634  			"/empty-generator.js",
  2635  			"/empty-async.js",
  2636  			"/reassign.js",
  2637  			"/reassign-inc.js",
  2638  			"/reassign-div.js",
  2639  			"/reassign-array.js",
  2640  			"/reassign-object.js",
  2641  		},
  2642  		options: config.Options{
  2643  			Mode:         config.ModeBundle,
  2644  			AbsOutputDir: "/out",
  2645  			MinifySyntax: true,
  2646  		},
  2647  	})
  2648  }
  2649  
  2650  func TestInlineFunctionCallBehaviorChanges(t *testing.T) {
  2651  	dce_suite.expectBundled(t, bundled{
  2652  		files: map[string]string{
  2653  			"/entry.js": `
  2654  				function empty() {}
  2655  				function id(x) { return x }
  2656  
  2657  				export let shouldBeWrapped = [
  2658  					id(foo.bar)(),
  2659  					id(foo[bar])(),
  2660  					id(foo?.bar)(),
  2661  					id(foo?.[bar])(),
  2662  
  2663  					(empty(), foo.bar)(),
  2664  					(empty(), foo[bar])(),
  2665  					(empty(), foo?.bar)(),
  2666  					(empty(), foo?.[bar])(),
  2667  
  2668  					id(eval)(),
  2669  					id(eval)?.(),
  2670  					(empty(), eval)(),
  2671  					(empty(), eval)?.(),
  2672  
  2673  					id(foo.bar)` + "``" + `,
  2674  					id(foo[bar])` + "``" + `,
  2675  					id(foo?.bar)` + "``" + `,
  2676  					id(foo?.[bar])` + "``" + `,
  2677  
  2678  					(empty(), foo.bar)` + "``" + `,
  2679  					(empty(), foo[bar])` + "``" + `,
  2680  					(empty(), foo?.bar)` + "``" + `,
  2681  					(empty(), foo?.[bar])` + "``" + `,
  2682  
  2683  					delete id(foo),
  2684  					delete id(foo.bar),
  2685  					delete id(foo[bar]),
  2686  					delete id(foo?.bar),
  2687  					delete id(foo?.[bar]),
  2688  
  2689  					delete (empty(), foo),
  2690  					delete (empty(), foo.bar),
  2691  					delete (empty(), foo[bar]),
  2692  					delete (empty(), foo?.bar),
  2693  					delete (empty(), foo?.[bar]),
  2694  
  2695  					delete empty(),
  2696  				]
  2697  
  2698  				export let shouldNotBeWrapped = [
  2699  					id(foo)(),
  2700  					(empty(), foo)(),
  2701  
  2702  					id(foo)` + "``" + `,
  2703  					(empty(), foo)` + "``" + `,
  2704  				]
  2705  
  2706  				export let shouldNotBeDoubleWrapped = [
  2707  					delete (empty(), foo(), bar()),
  2708  					delete id((foo(), bar())),
  2709  				]
  2710  			`,
  2711  		},
  2712  		entryPaths: []string{"/entry.js"},
  2713  		options: config.Options{
  2714  			Mode:         config.ModePassThrough,
  2715  			AbsOutputDir: "/out",
  2716  			MinifySyntax: true,
  2717  		},
  2718  	})
  2719  }
  2720  
  2721  func TestInlineFunctionCallForInitDecl(t *testing.T) {
  2722  	dce_suite.expectBundled(t, bundled{
  2723  		files: map[string]string{
  2724  			"/entry.js": `
  2725  				function empty() {}
  2726  				function id(x) { return x }
  2727  
  2728  				for (var y = empty(); false; ) ;
  2729  				for (var z = id(123); false; ) ;
  2730  			`,
  2731  		},
  2732  		entryPaths: []string{"/entry.js"},
  2733  		options: config.Options{
  2734  			Mode:         config.ModeBundle,
  2735  			AbsOutputDir: "/out",
  2736  			MinifySyntax: true,
  2737  		},
  2738  	})
  2739  }
  2740  
  2741  func TestConstValueInliningNoBundle(t *testing.T) {
  2742  	dce_suite.expectBundled(t, bundled{
  2743  		files: map[string]string{
  2744  			"/top-level.js": `
  2745  				// These should be kept because they are top-level and tree shaking is not enabled
  2746  				const n_keep = null
  2747  				const u_keep = undefined
  2748  				const i_keep = 1234567
  2749  				const f_keep = 123.456
  2750  				const s_keep = ''
  2751  
  2752  				// Values should still be inlined
  2753  				console.log(
  2754  					// These are doubled to avoid the "inline const/let into next statement if used once" optimization
  2755  					n_keep, n_keep,
  2756  					u_keep, u_keep,
  2757  					i_keep, i_keep,
  2758  					f_keep, f_keep,
  2759  					s_keep, s_keep,
  2760  				)
  2761  			`,
  2762  			"/nested-block.js": `
  2763  				{
  2764  					const REMOVE_n = null
  2765  					const REMOVE_u = undefined
  2766  					const REMOVE_i = 1234567
  2767  					const REMOVE_f = 123.456
  2768  					const s_keep = '' // String inlining is intentionally not supported right now
  2769  					console.log(
  2770  						// These are doubled to avoid the "inline const/let into next statement if used once" optimization
  2771  						REMOVE_n, REMOVE_n,
  2772  						REMOVE_u, REMOVE_u,
  2773  						REMOVE_i, REMOVE_i,
  2774  						REMOVE_f, REMOVE_f,
  2775  						s_keep, s_keep,
  2776  					)
  2777  				}
  2778  			`,
  2779  			"/nested-function.js": `
  2780  				function nested() {
  2781  					const REMOVE_n = null
  2782  					const REMOVE_u = undefined
  2783  					const REMOVE_i = 1234567
  2784  					const REMOVE_f = 123.456
  2785  					const s_keep = '' // String inlining is intentionally not supported right now
  2786  					console.log(
  2787  						// These are doubled to avoid the "inline const/let into next statement if used once" optimization
  2788  						REMOVE_n, REMOVE_n,
  2789  						REMOVE_u, REMOVE_u,
  2790  						REMOVE_i, REMOVE_i,
  2791  						REMOVE_f, REMOVE_f,
  2792  						s_keep, s_keep,
  2793  					)
  2794  				}
  2795  			`,
  2796  			"/namespace-export.ts": `
  2797  				namespace ns {
  2798  					const x_REMOVE = 1
  2799  					export const y_keep = 2
  2800  					console.log(
  2801  						x_REMOVE, x_REMOVE,
  2802  						y_keep, y_keep,
  2803  					)
  2804  				}
  2805  			`,
  2806  
  2807  			"/comment-before.js": `
  2808  				{
  2809  					//! comment
  2810  					const REMOVE = 1
  2811  					x = [REMOVE, REMOVE]
  2812  				}
  2813  			`,
  2814  			"/directive-before.js": `
  2815  				function nested() {
  2816  					'directive'
  2817  					const REMOVE = 1
  2818  					x = [REMOVE, REMOVE]
  2819  				}
  2820  			`,
  2821  			"/semicolon-before.js": `
  2822  				{
  2823  					;
  2824  					const REMOVE = 1
  2825  					x = [REMOVE, REMOVE]
  2826  				}
  2827  			`,
  2828  			"/debugger-before.js": `
  2829  				{
  2830  					debugger
  2831  					const REMOVE = 1
  2832  					x = [REMOVE, REMOVE]
  2833  				}
  2834  			`,
  2835  			"/type-before.ts": `
  2836  				{
  2837  					declare let x
  2838  					const REMOVE = 1
  2839  					x = [REMOVE, REMOVE]
  2840  				}
  2841  			`,
  2842  			"/exprs-before.js": `
  2843  				function nested() {
  2844  					const x = [, '', {}, 0n, /./, function() {}, () => {}]
  2845  					const y_REMOVE = 1
  2846  					function foo() {
  2847  						return y_REMOVE
  2848  					}
  2849  				}
  2850  			`,
  2851  
  2852  			"/disabled-tdz.js": `
  2853  				foo()
  2854  				const x_keep = 1
  2855  				function foo() {
  2856  					return x_keep
  2857  				}
  2858  			`,
  2859  			"/backwards-reference-top-level.js": `
  2860  				const x = y
  2861  				const y = 1
  2862  				console.log(
  2863  					x, x,
  2864  					y, y,
  2865  				)
  2866  			`,
  2867  			"/backwards-reference-nested-function.js": `
  2868  				function foo() {
  2869  					const x = y
  2870  					const y = 1
  2871  					console.log(
  2872  						x, x,
  2873  						y, y,
  2874  					)
  2875  				}
  2876  			`,
  2877  			"/issue-3125.js": `
  2878  				function foo() {
  2879  					const f = () => x
  2880  					const x = 0
  2881  					return f()
  2882  				}
  2883  			`,
  2884  		},
  2885  		entryPaths: []string{
  2886  			"/top-level.js",
  2887  			"/nested-block.js",
  2888  			"/nested-function.js",
  2889  			"/namespace-export.ts",
  2890  
  2891  			"/comment-before.js",
  2892  			"/directive-before.js",
  2893  			"/semicolon-before.js",
  2894  			"/debugger-before.js",
  2895  			"/type-before.ts",
  2896  			"/exprs-before.js",
  2897  
  2898  			"/disabled-tdz.js",
  2899  			"/backwards-reference-top-level.js",
  2900  			"/backwards-reference-nested-function.js",
  2901  			"/issue-3125.js",
  2902  		},
  2903  		options: config.Options{
  2904  			Mode:         config.ModePassThrough,
  2905  			AbsOutputDir: "/out",
  2906  			MinifySyntax: true,
  2907  		},
  2908  	})
  2909  }
  2910  
  2911  func TestConstValueInliningBundle(t *testing.T) {
  2912  	dce_suite.expectBundled(t, bundled{
  2913  		files: map[string]string{
  2914  			"/exported-entry.js": `
  2915  				const x_REMOVE = 1
  2916  				export const y_keep = 2
  2917  				console.log(
  2918  					x_REMOVE,
  2919  					y_keep,
  2920  				)
  2921  			`,
  2922  
  2923  			"/re-exported-entry.js": `
  2924  				import { x_REMOVE, y_keep } from './re-exported-constants'
  2925  				console.log(x_REMOVE, y_keep)
  2926  				export { y_keep }
  2927  			`,
  2928  			"/re-exported-constants.js": `
  2929  				export const x_REMOVE = 1
  2930  				export const y_keep = 2
  2931  			`,
  2932  
  2933  			"/re-exported-2-entry.js": `
  2934  				export { y_keep } from './re-exported-2-constants'
  2935  			`,
  2936  			"/re-exported-2-constants.js": `
  2937  				export const x_REMOVE = 1
  2938  				export const y_keep = 2
  2939  			`,
  2940  
  2941  			"/re-exported-star-entry.js": `
  2942  				export * from './re-exported-star-constants'
  2943  			`,
  2944  			"/re-exported-star-constants.js": `
  2945  				export const x_keep = 1
  2946  				export const y_keep = 2
  2947  			`,
  2948  
  2949  			"/cross-module-entry.js": `
  2950  				import { x_REMOVE, y_keep } from './cross-module-constants'
  2951  				console.log(x_REMOVE, y_keep)
  2952  			`,
  2953  			"/cross-module-constants.js": `
  2954  				export const x_REMOVE = 1
  2955  				foo()
  2956  				export const y_keep = 1
  2957  				export function foo() {
  2958  					return [x_REMOVE, y_keep]
  2959  				}
  2960  			`,
  2961  
  2962  			"/print-shorthand-entry.js": `
  2963  				import { foo, _bar } from './print-shorthand-constants'
  2964  				// The inlined constants must still be present in the output! We don't
  2965  				// want the printer to use the shorthand syntax here to refer to the
  2966  				// name of the constant itself because the constant declaration is omitted.
  2967  				console.log({ foo, _bar })
  2968  			`,
  2969  			"/print-shorthand-constants.js": `
  2970  				export const foo = 123
  2971  				export const _bar = -321
  2972  			`,
  2973  
  2974  			"/circular-import-entry.js": `
  2975  				import './circular-import-constants'
  2976  			`,
  2977  			"/circular-import-constants.js": `
  2978  				export const foo = 123 // Inlining should be prevented by the cycle
  2979  				export function bar() {
  2980  					return foo
  2981  				}
  2982  				import './circular-import-cycle'
  2983  			`,
  2984  			"/circular-import-cycle.js": `
  2985  				import { bar } from './circular-import-constants'
  2986  				console.log(bar()) // This accesses "foo" before it's initialized
  2987  			`,
  2988  
  2989  			"/circular-re-export-entry.js": `
  2990  				import { baz } from './circular-re-export-constants'
  2991  				console.log(baz)
  2992  			`,
  2993  			"/circular-re-export-constants.js": `
  2994  				export const foo = 123 // Inlining should be prevented by the cycle
  2995  				export function bar() {
  2996  					return foo
  2997  				}
  2998  				export { baz } from './circular-re-export-cycle'
  2999  			`,
  3000  			"/circular-re-export-cycle.js": `
  3001  				export const baz = 0
  3002  				import { bar } from './circular-re-export-constants'
  3003  				console.log(bar()) // This accesses "foo" before it's initialized
  3004  			`,
  3005  
  3006  			"/circular-re-export-star-entry.js": `
  3007  				import './circular-re-export-star-constants'
  3008  			`,
  3009  			"/circular-re-export-star-constants.js": `
  3010  				export const foo = 123 // Inlining should be prevented by the cycle
  3011  				export function bar() {
  3012  					return foo
  3013  				}
  3014  				export * from './circular-re-export-star-cycle'
  3015  			`,
  3016  			"/circular-re-export-star-cycle.js": `
  3017  				import { bar } from './circular-re-export-star-constants'
  3018  				console.log(bar()) // This accesses "foo" before it's initialized
  3019  			`,
  3020  
  3021  			"/non-circular-export-entry.js": `
  3022  				import { foo, bar } from './non-circular-export-constants'
  3023  				console.log(foo, bar())
  3024  			`,
  3025  			"/non-circular-export-constants.js": `
  3026  				const foo = 123 // Inlining should be prevented by the cycle
  3027  				function bar() {
  3028  					return foo
  3029  				}
  3030  				export { foo, bar }
  3031  			`,
  3032  		},
  3033  		entryPaths: []string{
  3034  			"/exported-entry.js",
  3035  			"/re-exported-entry.js",
  3036  			"/re-exported-2-entry.js",
  3037  			"/re-exported-star-entry.js",
  3038  			"/cross-module-entry.js",
  3039  			"/print-shorthand-entry.js",
  3040  			"/circular-import-entry.js",
  3041  			"/circular-re-export-entry.js",
  3042  			"/circular-re-export-star-entry.js",
  3043  			"/non-circular-export-entry.js",
  3044  		},
  3045  		options: config.Options{
  3046  			Mode:         config.ModeBundle,
  3047  			OutputFormat: config.FormatESModule,
  3048  			AbsOutputDir: "/out",
  3049  			MinifySyntax: true,
  3050  			MangleProps:  regexp.MustCompile("^_"),
  3051  		},
  3052  	})
  3053  }
  3054  
  3055  // Assignment to an inlined constant is not allowed since that would cause a
  3056  // syntax error in the output. We don't just keep the reference there because
  3057  // the declaration may actually have been completely removed already by the
  3058  // time we discover the assignment. I think making these cases an error is
  3059  // fine because it's bad code anyway.
  3060  func TestConstValueInliningAssign(t *testing.T) {
  3061  	dce_suite.expectBundled(t, bundled{
  3062  		files: map[string]string{
  3063  			"/const-assign.js": `
  3064  				const x = 1
  3065  				x = 2
  3066  			`,
  3067  			"/const-update.js": `
  3068  				const x = 1
  3069  				x += 2
  3070  			`,
  3071  		},
  3072  		entryPaths: []string{
  3073  			"/const-assign.js",
  3074  			"/const-update.js",
  3075  		},
  3076  		options: config.Options{
  3077  			Mode:         config.ModePassThrough,
  3078  			AbsOutputDir: "/out",
  3079  			MinifySyntax: true,
  3080  		},
  3081  		expectedScanLog: `const-assign.js: ERROR: Cannot assign to "x" because it is a constant
  3082  const-assign.js: NOTE: The symbol "x" was declared a constant here:
  3083  const-update.js: ERROR: Cannot assign to "x" because it is a constant
  3084  const-update.js: NOTE: The symbol "x" was declared a constant here:
  3085  `,
  3086  	})
  3087  }
  3088  
  3089  func TestConstValueInliningDirectEval(t *testing.T) {
  3090  	dce_suite.expectBundled(t, bundled{
  3091  		files: map[string]string{
  3092  			"/top-level-no-eval.js": `
  3093  				const x = 1
  3094  				console.log(x, evil('x'))
  3095  			`,
  3096  			"/top-level-eval.js": `
  3097  				const x = 1
  3098  				console.log(x, eval('x'))
  3099  			`,
  3100  			"/nested-no-eval.js": `
  3101  				(() => {
  3102  					const x = 1
  3103  					console.log(x, evil('x'))
  3104  				})()
  3105  			`,
  3106  			"/nested-eval.js": `
  3107  				(() => {
  3108  					const x = 1
  3109  					console.log(x, eval('x'))
  3110  				})()
  3111  			`,
  3112  			"/ts-namespace-no-eval.ts": `
  3113  				namespace y {
  3114  					export const x = 1
  3115  					console.log(x, evil('x'))
  3116  				}
  3117  			`,
  3118  			"/ts-namespace-eval.ts": `
  3119  				namespace z {
  3120  					export const x = 1
  3121  					console.log(x, eval('x'))
  3122  				}
  3123  			`,
  3124  		},
  3125  		entryPaths: []string{
  3126  			"/top-level-no-eval.js",
  3127  			"/top-level-eval.js",
  3128  			"/nested-no-eval.js",
  3129  			"/nested-eval.js",
  3130  			"/ts-namespace-no-eval.ts",
  3131  			"/ts-namespace-eval.ts",
  3132  		},
  3133  		options: config.Options{
  3134  			Mode:         config.ModePassThrough,
  3135  			AbsOutputDir: "/out",
  3136  			MinifySyntax: true,
  3137  		},
  3138  	})
  3139  }
  3140  
  3141  func TestCrossModuleConstantFoldingNumber(t *testing.T) {
  3142  	dce_suite.expectBundled(t, bundled{
  3143  		files: map[string]string{
  3144  			"/enum-constants.ts": `
  3145  				export enum x {
  3146  					a = 3,
  3147  					b = 6,
  3148  				}
  3149  			`,
  3150  			"/enum-entry.ts": `
  3151  				import { x } from './enum-constants'
  3152  				console.log([
  3153  					+x.b,
  3154  					-x.b,
  3155  					~x.b,
  3156  					!x.b,
  3157  					typeof x.b,
  3158  				], [
  3159  					x.a + x.b,
  3160  					x.a - x.b,
  3161  					x.a * x.b,
  3162  					x.a / x.b,
  3163  					x.a % x.b,
  3164  					x.a ** x.b,
  3165  				], [
  3166  					x.a < x.b,
  3167  					x.a > x.b,
  3168  					x.a <= x.b,
  3169  					x.a >= x.b,
  3170  					x.a == x.b,
  3171  					x.a != x.b,
  3172  					x.a === x.b,
  3173  					x.a !== x.b,
  3174  				], [
  3175  					x.b << 1,
  3176  					x.b >> 1,
  3177  					x.b >>> 1,
  3178  				], [
  3179  					x.a & x.b,
  3180  					x.a | x.b,
  3181  					x.a ^ x.b,
  3182  				], [
  3183  					x.a && x.b,
  3184  					x.a || x.b,
  3185  					x.a ?? x.b,
  3186  					x.a ? 'y' : 'n',
  3187  					!x.b ? 'y' : 'n',
  3188  				])
  3189  			`,
  3190  
  3191  			"/const-constants.js": `
  3192  				export const a = 3
  3193  				export const b = 6
  3194  			`,
  3195  			"/const-entry.js": `
  3196  				import { a, b } from './const-constants'
  3197  				console.log([
  3198  					+b,
  3199  					-b,
  3200  					~b,
  3201  					!b,
  3202  					typeof b,
  3203  				], [
  3204  					a + b,
  3205  					a - b,
  3206  					a * b,
  3207  					a / b,
  3208  					a % b,
  3209  					a ** b,
  3210  				], [
  3211  					a < b,
  3212  					a > b,
  3213  					a <= b,
  3214  					a >= b,
  3215  					a == b,
  3216  					a != b,
  3217  					a === b,
  3218  					a !== b,
  3219  				], [
  3220  					b << 1,
  3221  					b >> 1,
  3222  					b >>> 1,
  3223  				], [
  3224  					a & b,
  3225  					a | b,
  3226  					a ^ b,
  3227  				], [
  3228  					a && b,
  3229  					a || b,
  3230  					a ?? b,
  3231  					a ? 'y' : 'n',
  3232  					!b ? 'y' : 'n',
  3233  				])
  3234  			`,
  3235  
  3236  			"/nested-constants.ts": `
  3237  				export const a = 2
  3238  				export const b = 4
  3239  				export const c = 8
  3240  				export enum x {
  3241  					a = 16,
  3242  					b = 32,
  3243  					c = 64,
  3244  				}
  3245  			`,
  3246  			"/nested-entry.ts": `
  3247  				import { a, b, c, x } from './nested-constants'
  3248  				console.log({
  3249  					'should be 4': ~(~a & ~b) & (b | c),
  3250  					'should be 32': ~(~x.a & ~x.b) & (x.b | x.c),
  3251  				})
  3252  			`,
  3253  		},
  3254  		entryPaths: []string{
  3255  			"/enum-entry.ts",
  3256  			"/const-entry.js",
  3257  			"/nested-entry.ts",
  3258  		},
  3259  		options: config.Options{
  3260  			Mode:         config.ModeBundle,
  3261  			AbsOutputDir: "/out",
  3262  			MinifySyntax: true,
  3263  		},
  3264  	})
  3265  }
  3266  
  3267  func TestCrossModuleConstantFoldingString(t *testing.T) {
  3268  	dce_suite.expectBundled(t, bundled{
  3269  		files: map[string]string{
  3270  			"/enum-constants.ts": `
  3271  				export enum x {
  3272  					a = 'foo',
  3273  					b = 'bar',
  3274  				}
  3275  			`,
  3276  			"/enum-entry.ts": `
  3277  				import { x } from './enum-constants'
  3278  				console.log([
  3279  					typeof x.b,
  3280  				], [
  3281  					x.a + x.b,
  3282  				], [
  3283  					x.a < x.b,
  3284  					x.a > x.b,
  3285  					x.a <= x.b,
  3286  					x.a >= x.b,
  3287  					x.a == x.b,
  3288  					x.a != x.b,
  3289  					x.a === x.b,
  3290  					x.a !== x.b,
  3291  				], [
  3292  					x.a && x.b,
  3293  					x.a || x.b,
  3294  					x.a ?? x.b,
  3295  					x.a ? 'y' : 'n',
  3296  					!x.b ? 'y' : 'n',
  3297  				])
  3298  			`,
  3299  
  3300  			"/const-constants.js": `
  3301  				export const a = 'foo'
  3302  				export const b = 'bar'
  3303  			`,
  3304  			"/const-entry.js": `
  3305  				import { a, b } from './const-constants'
  3306  				console.log([
  3307  					typeof b,
  3308  				], [
  3309  					a + b,
  3310  				], [
  3311  					a < b,
  3312  					a > b,
  3313  					a <= b,
  3314  					a >= b,
  3315  					a == b,
  3316  					a != b,
  3317  					a === b,
  3318  					a !== b,
  3319  				], [
  3320  					a && b,
  3321  					a || b,
  3322  					a ?? b,
  3323  					a ? 'y' : 'n',
  3324  					!b ? 'y' : 'n',
  3325  				])
  3326  			`,
  3327  
  3328  			"/nested-constants.ts": `
  3329  				export const a = 'foo'
  3330  				export const b = 'bar'
  3331  				export const c = 'baz'
  3332  				export enum x {
  3333  					a = 'FOO',
  3334  					b = 'BAR',
  3335  					c = 'BAZ',
  3336  				}
  3337  			`,
  3338  			"/nested-entry.ts": `
  3339  				import { a, b, c, x } from './nested-constants'
  3340  				console.log({
  3341  					'should be foobarbaz': a + b + c,
  3342  					'should be FOOBARBAZ': x.a + x.b + x.c,
  3343  				})
  3344  			`,
  3345  		},
  3346  		entryPaths: []string{
  3347  			"/enum-entry.ts",
  3348  			"/const-entry.js",
  3349  			"/nested-entry.ts",
  3350  		},
  3351  		options: config.Options{
  3352  			Mode:         config.ModeBundle,
  3353  			AbsOutputDir: "/out",
  3354  			MinifySyntax: true,
  3355  		},
  3356  	})
  3357  }
  3358  
  3359  func TestMultipleDeclarationTreeShaking(t *testing.T) {
  3360  	dce_suite.expectBundled(t, bundled{
  3361  		files: map[string]string{
  3362  			"/var2.js": `
  3363  				var x = 1
  3364  				console.log(x)
  3365  				var x = 2
  3366  			`,
  3367  			"/var3.js": `
  3368  				var x = 1
  3369  				console.log(x)
  3370  				var x = 2
  3371  				console.log(x)
  3372  				var x = 3
  3373  			`,
  3374  			"/function2.js": `
  3375  				function x() { return 1 }
  3376  				console.log(x())
  3377  				function x() { return 2 }
  3378  			`,
  3379  			"/function3.js": `
  3380  				function x() { return 1 }
  3381  				console.log(x())
  3382  				function x() { return 2 }
  3383  				console.log(x())
  3384  				function x() { return 3 }
  3385  			`,
  3386  		},
  3387  		entryPaths: []string{
  3388  			"/var2.js",
  3389  			"/var3.js",
  3390  			"/function2.js",
  3391  			"/function3.js",
  3392  		},
  3393  		options: config.Options{
  3394  			Mode:         config.ModeBundle,
  3395  			AbsOutputDir: "/out",
  3396  			MinifySyntax: false,
  3397  		},
  3398  	})
  3399  }
  3400  
  3401  func TestMultipleDeclarationTreeShakingMinifySyntax(t *testing.T) {
  3402  	dce_suite.expectBundled(t, bundled{
  3403  		files: map[string]string{
  3404  			"/var2.js": `
  3405  				var x = 1
  3406  				console.log(x)
  3407  				var x = 2
  3408  			`,
  3409  			"/var3.js": `
  3410  				var x = 1
  3411  				console.log(x)
  3412  				var x = 2
  3413  				console.log(x)
  3414  				var x = 3
  3415  			`,
  3416  			"/function2.js": `
  3417  				function x() { return 1 }
  3418  				console.log(x())
  3419  				function x() { return 2 }
  3420  			`,
  3421  			"/function3.js": `
  3422  				function x() { return 1 }
  3423  				console.log(x())
  3424  				function x() { return 2 }
  3425  				console.log(x())
  3426  				function x() { return 3 }
  3427  			`,
  3428  		},
  3429  		entryPaths: []string{
  3430  			"/var2.js",
  3431  			"/var3.js",
  3432  			"/function2.js",
  3433  			"/function3.js",
  3434  		},
  3435  		options: config.Options{
  3436  			Mode:         config.ModeBundle,
  3437  			AbsOutputDir: "/out",
  3438  			MinifySyntax: true,
  3439  		},
  3440  	})
  3441  }
  3442  
  3443  // Pure call removal should still run iterators, which can have side effects
  3444  func TestPureCallsWithSpread(t *testing.T) {
  3445  	dce_suite.expectBundled(t, bundled{
  3446  		files: map[string]string{
  3447  			"/entry.js": `
  3448  				/* @__PURE__ */ foo(...args);
  3449  				/* @__PURE__ */ new foo(...args);
  3450  			`,
  3451  		},
  3452  		entryPaths: []string{"/entry.js"},
  3453  		options: config.Options{
  3454  			Mode:          config.ModeBundle,
  3455  			AbsOutputFile: "/out.js",
  3456  			MinifySyntax:  true,
  3457  		},
  3458  	})
  3459  }
  3460  
  3461  func TestTopLevelFunctionInliningWithSpread(t *testing.T) {
  3462  	dce_suite.expectBundled(t, bundled{
  3463  		files: map[string]string{
  3464  			"/entry.js": `
  3465  				function empty1() {}
  3466  				function empty2() {}
  3467  				function empty3() {}
  3468  
  3469  				function identity1(x) { return x }
  3470  				function identity2(x) { return x }
  3471  				function identity3(x) { return x }
  3472  
  3473  				empty1()
  3474  				empty2(args)
  3475  				empty3(...args)
  3476  
  3477  				identity1()
  3478  				identity2(args)
  3479  				identity3(...args)
  3480  			`,
  3481  
  3482  			"/inner.js": `
  3483  				export function empty1() {}
  3484  				export function empty2() {}
  3485  				export function empty3() {}
  3486  
  3487  				export function identity1(x) { return x }
  3488  				export function identity2(x) { return x }
  3489  				export function identity3(x) { return x }
  3490  			`,
  3491  
  3492  			"/entry-outer.js": `
  3493  				import {
  3494  					empty1,
  3495  					empty2,
  3496  					empty3,
  3497  
  3498  					identity1,
  3499  					identity2,
  3500  					identity3,
  3501  				} from './inner.js'
  3502  
  3503  				empty1()
  3504  				empty2(args)
  3505  				empty3(...args)
  3506  
  3507  				identity1()
  3508  				identity2(args)
  3509  				identity3(...args)
  3510  			`,
  3511  		},
  3512  		entryPaths: []string{"/entry.js", "/entry-outer.js"},
  3513  		options: config.Options{
  3514  			Mode:         config.ModeBundle,
  3515  			AbsOutputDir: "/out",
  3516  			MinifySyntax: true,
  3517  		},
  3518  	})
  3519  }
  3520  
  3521  func TestNestedFunctionInliningWithSpread(t *testing.T) {
  3522  	dce_suite.expectBundled(t, bundled{
  3523  		files: map[string]string{
  3524  			"/entry.js": `
  3525  				function empty1() {}
  3526  				function empty2() {}
  3527  				function empty3() {}
  3528  
  3529  				function identity1(x) { return x }
  3530  				function identity2(x) { return x }
  3531  				function identity3(x) { return x }
  3532  
  3533  				check(
  3534  					empty1(),
  3535  					empty2(args),
  3536  					empty3(...args),
  3537  
  3538  					identity1(),
  3539  					identity2(args),
  3540  					identity3(...args),
  3541  				)
  3542  			`,
  3543  
  3544  			"/inner.js": `
  3545  				export function empty1() {}
  3546  				export function empty2() {}
  3547  				export function empty3() {}
  3548  
  3549  				export function identity1(x) { return x }
  3550  				export function identity2(x) { return x }
  3551  				export function identity3(x) { return x }
  3552  			`,
  3553  
  3554  			"/entry-outer.js": `
  3555  				import {
  3556  					empty1,
  3557  					empty2,
  3558  					empty3,
  3559  
  3560  					identity1,
  3561  					identity2,
  3562  					identity3,
  3563  				} from './inner.js'
  3564  
  3565  				check(
  3566  					empty1(),
  3567  					empty2(args),
  3568  					empty3(...args),
  3569  
  3570  					identity1(),
  3571  					identity2(args),
  3572  					identity3(...args),
  3573  				)
  3574  			`,
  3575  		},
  3576  		entryPaths: []string{"/entry.js", "/entry-outer.js"},
  3577  		options: config.Options{
  3578  			Mode:         config.ModeBundle,
  3579  			AbsOutputDir: "/out",
  3580  			MinifySyntax: true,
  3581  		},
  3582  	})
  3583  }
  3584  
  3585  func TestPackageJsonSideEffectsFalseCrossPlatformSlash(t *testing.T) {
  3586  	dce_suite.expectBundled(t, bundled{
  3587  		files: map[string]string{
  3588  			"/Users/user/project/src/entry.js": `
  3589  				import "demo-pkg/foo"
  3590  				import "demo-pkg/bar"
  3591  			`,
  3592  			"/Users/user/project/node_modules/demo-pkg/foo.js": `
  3593  				console.log('foo')
  3594  			`,
  3595  			"/Users/user/project/node_modules/demo-pkg/bar/index.js": `
  3596  				console.log('bar')
  3597  			`,
  3598  			"/Users/user/project/node_modules/demo-pkg/package.json": `
  3599  				{
  3600  					"sideEffects": [
  3601  						"**/foo.js",
  3602  						"bar/index.js"
  3603  					]
  3604  				}
  3605  			`,
  3606  		},
  3607  		entryPaths: []string{"/Users/user/project/src/entry.js"},
  3608  		options: config.Options{
  3609  			Mode:          config.ModeBundle,
  3610  			AbsOutputFile: "/out.js",
  3611  		},
  3612  	})
  3613  }
  3614  
  3615  func TestTreeShakingJSWithAssociatedCSS(t *testing.T) {
  3616  	dce_suite.expectBundled(t, bundled{
  3617  		files: map[string]string{
  3618  			"/project/test.jsx": `
  3619  				import { Button } from 'pkg/button'
  3620  				import { Menu } from 'pkg/menu'
  3621  				render(<Button/>)
  3622  			`,
  3623  			"/project/node_modules/pkg/button.js": `
  3624  				import './button.css'
  3625  				export let Button
  3626  			`,
  3627  			"/project/node_modules/pkg/button.css": `
  3628  				button { color: red }
  3629  			`,
  3630  			"/project/node_modules/pkg/menu.js": `
  3631  				import './menu.css'
  3632  				export let Menu
  3633  			`,
  3634  			"/project/node_modules/pkg/menu.css": `
  3635  				menu { color: red }
  3636  			`,
  3637  		},
  3638  		entryPaths: []string{"/project/test.jsx"},
  3639  		options: config.Options{
  3640  			Mode:         config.ModeBundle,
  3641  			AbsOutputDir: "/out",
  3642  		},
  3643  	})
  3644  }
  3645  
  3646  func TestTreeShakingJSWithAssociatedCSSReExportSideEffectsFalse(t *testing.T) {
  3647  	dce_suite.expectBundled(t, bundled{
  3648  		files: map[string]string{
  3649  			"/project/test.jsx": `
  3650  				import { Button } from 'pkg'
  3651  				render(<Button/>)
  3652  			`,
  3653  			"/project/node_modules/pkg/entry.js": `
  3654  				export { Button } from './components'
  3655  			`,
  3656  			"/project/node_modules/pkg/package.json": `{
  3657  				"main": "./entry.js",
  3658  				"sideEffects": false
  3659  			}`,
  3660  			"/project/node_modules/pkg/components.jsx": `
  3661  				require('./button.css')
  3662  				export const Button = () => <button/>
  3663  			`,
  3664  			"/project/node_modules/pkg/button.css": `
  3665  				button { color: red }
  3666  			`,
  3667  		},
  3668  		entryPaths: []string{"/project/test.jsx"},
  3669  		options: config.Options{
  3670  			Mode:         config.ModeBundle,
  3671  			AbsOutputDir: "/out",
  3672  		},
  3673  	})
  3674  }
  3675  
  3676  func TestTreeShakingJSWithAssociatedCSSReExportSideEffectsFalseOnlyJS(t *testing.T) {
  3677  	dce_suite.expectBundled(t, bundled{
  3678  		files: map[string]string{
  3679  			"/project/test.jsx": `
  3680  				import { Button } from 'pkg'
  3681  				render(<Button/>)
  3682  			`,
  3683  			"/project/node_modules/pkg/entry.js": `
  3684  				export { Button } from './components'
  3685  			`,
  3686  			"/project/node_modules/pkg/package.json": `{
  3687  				"main": "./entry.js",
  3688  				"sideEffects": ["*.css"]
  3689  			}`,
  3690  			"/project/node_modules/pkg/components.jsx": `
  3691  				require('./button.css')
  3692  				export const Button = () => <button/>
  3693  			`,
  3694  			"/project/node_modules/pkg/button.css": `
  3695  				button { color: red }
  3696  			`,
  3697  		},
  3698  		entryPaths: []string{"/project/test.jsx"},
  3699  		options: config.Options{
  3700  			Mode:         config.ModeBundle,
  3701  			AbsOutputDir: "/out",
  3702  		},
  3703  	})
  3704  }
  3705  
  3706  func TestTreeShakingJSWithAssociatedCSSExportStarSideEffectsFalse(t *testing.T) {
  3707  	dce_suite.expectBundled(t, bundled{
  3708  		files: map[string]string{
  3709  			"/project/test.jsx": `
  3710  				import { Button } from 'pkg'
  3711  				render(<Button/>)
  3712  			`,
  3713  			"/project/node_modules/pkg/entry.js": `
  3714  				export * from './components'
  3715  			`,
  3716  			"/project/node_modules/pkg/package.json": `{
  3717  				"main": "./entry.js",
  3718  				"sideEffects": false
  3719  			}`,
  3720  			"/project/node_modules/pkg/components.jsx": `
  3721  				require('./button.css')
  3722  				export const Button = () => <button/>
  3723  			`,
  3724  			"/project/node_modules/pkg/button.css": `
  3725  				button { color: red }
  3726  			`,
  3727  		},
  3728  		entryPaths: []string{"/project/test.jsx"},
  3729  		options: config.Options{
  3730  			Mode:         config.ModeBundle,
  3731  			AbsOutputDir: "/out",
  3732  		},
  3733  	})
  3734  }
  3735  
  3736  func TestTreeShakingJSWithAssociatedCSSExportStarSideEffectsFalseOnlyJS(t *testing.T) {
  3737  	dce_suite.expectBundled(t, bundled{
  3738  		files: map[string]string{
  3739  			"/project/test.jsx": `
  3740  				import { Button } from 'pkg'
  3741  				render(<Button/>)
  3742  			`,
  3743  			"/project/node_modules/pkg/entry.js": `
  3744  				export * from './components'
  3745  			`,
  3746  			"/project/node_modules/pkg/package.json": `{
  3747  				"main": "./entry.js",
  3748  				"sideEffects": ["*.css"]
  3749  			}`,
  3750  			"/project/node_modules/pkg/components.jsx": `
  3751  				require('./button.css')
  3752  				export const Button = () => <button/>
  3753  			`,
  3754  			"/project/node_modules/pkg/button.css": `
  3755  				button { color: red }
  3756  			`,
  3757  		},
  3758  		entryPaths: []string{"/project/test.jsx"},
  3759  		options: config.Options{
  3760  			Mode:         config.ModeBundle,
  3761  			AbsOutputDir: "/out",
  3762  		},
  3763  	})
  3764  }
  3765  
  3766  func TestTreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalse(t *testing.T) {
  3767  	dce_suite.expectBundled(t, bundled{
  3768  		files: map[string]string{
  3769  			"/project/test.jsx": `
  3770  				import { Button } from 'pkg/button'
  3771  				render(<Button/>)
  3772  			`,
  3773  			"/project/node_modules/pkg/package.json": `{
  3774  				"sideEffects": false
  3775  			}`,
  3776  			"/project/node_modules/pkg/button.jsx": `
  3777  				import styles from './styles'
  3778  				export const Button = () => <button/>
  3779  			`,
  3780  			"/project/node_modules/pkg/styles.js": `
  3781  				import './styles.css'
  3782  				export default {}
  3783  			`,
  3784  			"/project/node_modules/pkg/styles.css": `
  3785  				button { color: red }
  3786  			`,
  3787  		},
  3788  		entryPaths: []string{"/project/test.jsx"},
  3789  		options: config.Options{
  3790  			Mode:         config.ModeBundle,
  3791  			AbsOutputDir: "/out",
  3792  		},
  3793  	})
  3794  }
  3795  
  3796  func TestTreeShakingJSWithAssociatedCSSUnusedNestedImportSideEffectsFalseOnlyJS(t *testing.T) {
  3797  	dce_suite.expectBundled(t, bundled{
  3798  		files: map[string]string{
  3799  			"/project/test.jsx": `
  3800  				import { Button } from 'pkg/button'
  3801  				render(<Button/>)
  3802  			`,
  3803  			"/project/node_modules/pkg/package.json": `{
  3804  				"sideEffects": ["*.css"]
  3805  			}`,
  3806  			"/project/node_modules/pkg/button.jsx": `
  3807  				import styles from './styles'
  3808  				export const Button = () => <button/>
  3809  			`,
  3810  			"/project/node_modules/pkg/styles.js": `
  3811  				import './styles.css'
  3812  				export default {}
  3813  			`,
  3814  			"/project/node_modules/pkg/styles.css": `
  3815  				button { color: red }
  3816  			`,
  3817  		},
  3818  		entryPaths: []string{"/project/test.jsx"},
  3819  		options: config.Options{
  3820  			Mode:         config.ModeBundle,
  3821  			AbsOutputDir: "/out",
  3822  		},
  3823  	})
  3824  }
  3825  
  3826  func TestPreserveDirectivesMinifyPassThrough(t *testing.T) {
  3827  	dce_suite.expectBundled(t, bundled{
  3828  		files: map[string]string{
  3829  			"/entry.js": `
  3830  				//! 1
  3831  				'use 1'
  3832  				//! 2
  3833  				'use 2'
  3834  				//! 3
  3835  				'use 3'
  3836  				entry()
  3837  				//! 4
  3838  				'use 4'
  3839  				//! 5
  3840  				'use 5'
  3841  				//! 6
  3842  				'use 6'
  3843  			`,
  3844  		},
  3845  		entryPaths: []string{"/entry.js"},
  3846  		options: config.Options{
  3847  			Mode:          config.ModePassThrough,
  3848  			AbsOutputFile: "/out.js",
  3849  			MinifySyntax:  true,
  3850  		},
  3851  	})
  3852  }
  3853  
  3854  func TestPreserveDirectivesMinifyIIFE(t *testing.T) {
  3855  	dce_suite.expectBundled(t, bundled{
  3856  		files: map[string]string{
  3857  			"/entry.js": `
  3858  				//! 1
  3859  				'use 1'
  3860  				//! 2
  3861  				'use 2'
  3862  				//! 3
  3863  				'use 3'
  3864  				entry()
  3865  				//! 4
  3866  				'use 4'
  3867  				//! 5
  3868  				'use 5'
  3869  				//! 6
  3870  				'use 6'
  3871  			`,
  3872  		},
  3873  		entryPaths: []string{"/entry.js"},
  3874  		options: config.Options{
  3875  			Mode:          config.ModeConvertFormat,
  3876  			OutputFormat:  config.FormatIIFE,
  3877  			AbsOutputFile: "/out.js",
  3878  			MinifySyntax:  true,
  3879  		},
  3880  	})
  3881  }
  3882  
  3883  func TestPreserveDirectivesMinifyBundle(t *testing.T) {
  3884  	dce_suite.expectBundled(t, bundled{
  3885  		files: map[string]string{
  3886  			"/entry.js": `
  3887  				//! 1
  3888  				'use 1'
  3889  				//! 2
  3890  				'use 2'
  3891  				//! 3
  3892  				'use 3'
  3893  				entry()
  3894  				//! 4
  3895  				'use 4'
  3896  				//! 5
  3897  				'use 5'
  3898  				//! 6
  3899  				'use 6'
  3900  				import "./nested.js"
  3901  			`,
  3902  			"/nested.js": `
  3903  				//! A
  3904  				'use A'
  3905  				//! B
  3906  				'use B'
  3907  				//! C
  3908  				'use C'
  3909  				nested()
  3910  				//! D
  3911  				'use D'
  3912  				//! E
  3913  				'use E'
  3914  				//! F
  3915  				'use F'
  3916  			`,
  3917  		},
  3918  		entryPaths: []string{"/entry.js"},
  3919  		options: config.Options{
  3920  			Mode:          config.ModeBundle,
  3921  			OutputFormat:  config.FormatIIFE,
  3922  			AbsOutputFile: "/out.js",
  3923  			MinifySyntax:  true,
  3924  		},
  3925  	})
  3926  }
  3927  
  3928  // See: https://github.com/rollup/rollup/pull/5024
  3929  func TestNoSideEffectsComment(t *testing.T) {
  3930  	dce_suite.expectBundled(t, bundled{
  3931  		files: map[string]string{
  3932  			"/expr-fn.js": `
  3933  				//! These should all have "no side effects"
  3934  				x([
  3935  					/* #__NO_SIDE_EFFECTS__ */ function() {},
  3936  					/* #__NO_SIDE_EFFECTS__ */ function y() {},
  3937  					/* #__NO_SIDE_EFFECTS__ */ function*() {},
  3938  					/* #__NO_SIDE_EFFECTS__ */ function* y() {},
  3939  					/* #__NO_SIDE_EFFECTS__ */ async function() {},
  3940  					/* #__NO_SIDE_EFFECTS__ */ async function y() {},
  3941  					/* #__NO_SIDE_EFFECTS__ */ async function*() {},
  3942  					/* #__NO_SIDE_EFFECTS__ */ async function* y() {},
  3943  				])
  3944  			`,
  3945  			"/expr-arrow.js": `
  3946  				//! These should all have "no side effects"
  3947  				x([
  3948  					/* #__NO_SIDE_EFFECTS__ */ y => y,
  3949  					/* #__NO_SIDE_EFFECTS__ */ () => {},
  3950  					/* #__NO_SIDE_EFFECTS__ */ (y) => (y),
  3951  					/* #__NO_SIDE_EFFECTS__ */ async y => y,
  3952  					/* #__NO_SIDE_EFFECTS__ */ async () => {},
  3953  					/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
  3954  				])
  3955  			`,
  3956  
  3957  			"/stmt-fn.js": `
  3958  				//! These should all have "no side effects"
  3959  				// #__NO_SIDE_EFFECTS__
  3960  				function a() {}
  3961  				// #__NO_SIDE_EFFECTS__
  3962  				function* b() {}
  3963  				// #__NO_SIDE_EFFECTS__
  3964  				async function c() {}
  3965  				// #__NO_SIDE_EFFECTS__
  3966  				async function* d() {}
  3967  			`,
  3968  			"/stmt-export-fn.js": `
  3969  				//! These should all have "no side effects"
  3970  				/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  3971  				/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  3972  				/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  3973  				/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  3974  			`,
  3975  			"/stmt-local.js": `
  3976  				//! Only "c0" and "c2" should have "no side effects" (Rollup only respects "const" and only for the first one)
  3977  				/* #__NO_SIDE_EFFECTS__ */ var v0 = function() {}, v1 = function() {}
  3978  				/* #__NO_SIDE_EFFECTS__ */ let l0 = function() {}, l1 = function() {}
  3979  				/* #__NO_SIDE_EFFECTS__ */ const c0 = function() {}, c1 = function() {}
  3980  				/* #__NO_SIDE_EFFECTS__ */ var v2 = () => {}, v3 = () => {}
  3981  				/* #__NO_SIDE_EFFECTS__ */ let l2 = () => {}, l3 = () => {}
  3982  				/* #__NO_SIDE_EFFECTS__ */ const c2 = () => {}, c3 = () => {}
  3983  			`,
  3984  			"/stmt-export-local.js": `
  3985  				//! Only "c0" and "c2" should have "no side effects" (Rollup only respects "const" and only for the first one)
  3986  				/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  3987  				/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  3988  				/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  3989  				/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  3990  				/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  3991  				/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  3992  			`,
  3993  
  3994  			"/ns-export-fn.ts": `
  3995  				namespace ns {
  3996  					//! These should all have "no side effects"
  3997  					/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  3998  					/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  3999  					/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  4000  					/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  4001  				}
  4002  			`,
  4003  			"/ns-export-local.ts": `
  4004  				namespace ns {
  4005  					//! Only "c0" and "c2" should have "no side effects" (Rollup only respects "const" and only for the first one)
  4006  					/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  4007  					/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  4008  					/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  4009  					/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  4010  					/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  4011  					/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  4012  				}
  4013  			`,
  4014  
  4015  			"/stmt-export-default-before-fn-anon.js":           `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default function() {}`,
  4016  			"/stmt-export-default-before-fn-name.js":           `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default function f() {}`,
  4017  			"/stmt-export-default-before-gen-fn-anon.js":       `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default function*() {}`,
  4018  			"/stmt-export-default-before-gen-fn-name.js":       `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default function* f() {}`,
  4019  			"/stmt-export-default-before-async-fn-anon.js":     `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default async function() {}`,
  4020  			"/stmt-export-default-before-async-fn-name.js":     `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default async function f() {}`,
  4021  			"/stmt-export-default-before-async-gen-fn-anon.js": `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default async function*() {}`,
  4022  			"/stmt-export-default-before-async-gen-fn-name.js": `/*! This should have "no side effects" */ /* #__NO_SIDE_EFFECTS__ */ export default async function* f() {}`,
  4023  
  4024  			"/stmt-export-default-after-fn-anon.js":           `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ function() {}`,
  4025  			"/stmt-export-default-after-fn-name.js":           `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ function f() {}`,
  4026  			"/stmt-export-default-after-gen-fn-anon.js":       `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ function*() {}`,
  4027  			"/stmt-export-default-after-gen-fn-name.js":       `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ function* f() {}`,
  4028  			"/stmt-export-default-after-async-fn-anon.js":     `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ async function() {}`,
  4029  			"/stmt-export-default-after-async-fn-name.js":     `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ async function f() {}`,
  4030  			"/stmt-export-default-after-async-gen-fn-anon.js": `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ async function*() {}`,
  4031  			"/stmt-export-default-after-async-gen-fn-name.js": `/*! This should have "no side effects" */ export default /* @__NO_SIDE_EFFECTS__ */ async function* f() {}`,
  4032  		},
  4033  		entryPaths: []string{
  4034  			"/expr-fn.js",
  4035  			"/expr-arrow.js",
  4036  
  4037  			"/stmt-fn.js",
  4038  			"/stmt-export-fn.js",
  4039  			"/stmt-local.js",
  4040  			"/stmt-export-local.js",
  4041  
  4042  			"/ns-export-fn.ts",
  4043  			"/ns-export-local.ts",
  4044  
  4045  			"/stmt-export-default-before-fn-anon.js",
  4046  			"/stmt-export-default-before-fn-name.js",
  4047  			"/stmt-export-default-before-gen-fn-anon.js",
  4048  			"/stmt-export-default-before-gen-fn-name.js",
  4049  			"/stmt-export-default-before-async-fn-anon.js",
  4050  			"/stmt-export-default-before-async-fn-name.js",
  4051  			"/stmt-export-default-before-async-gen-fn-anon.js",
  4052  			"/stmt-export-default-before-async-gen-fn-name.js",
  4053  
  4054  			"/stmt-export-default-after-fn-anon.js",
  4055  			"/stmt-export-default-after-fn-name.js",
  4056  			"/stmt-export-default-after-gen-fn-anon.js",
  4057  			"/stmt-export-default-after-gen-fn-name.js",
  4058  			"/stmt-export-default-after-async-fn-anon.js",
  4059  			"/stmt-export-default-after-async-fn-name.js",
  4060  			"/stmt-export-default-after-async-gen-fn-anon.js",
  4061  			"/stmt-export-default-after-async-gen-fn-name.js",
  4062  		},
  4063  		options: config.Options{
  4064  			AbsOutputDir: "/out",
  4065  		},
  4066  	})
  4067  }
  4068  
  4069  func TestNoSideEffectsCommentIgnoreAnnotations(t *testing.T) {
  4070  	dce_suite.expectBundled(t, bundled{
  4071  		files: map[string]string{
  4072  			"/expr-fn.js": `
  4073  				x([
  4074  					/* #__NO_SIDE_EFFECTS__ */ function() {},
  4075  					/* #__NO_SIDE_EFFECTS__ */ function y() {},
  4076  					/* #__NO_SIDE_EFFECTS__ */ function*() {},
  4077  					/* #__NO_SIDE_EFFECTS__ */ function* y() {},
  4078  					/* #__NO_SIDE_EFFECTS__ */ async function() {},
  4079  					/* #__NO_SIDE_EFFECTS__ */ async function y() {},
  4080  					/* #__NO_SIDE_EFFECTS__ */ async function*() {},
  4081  					/* #__NO_SIDE_EFFECTS__ */ async function* y() {},
  4082  				])
  4083  			`,
  4084  			"/expr-arrow.js": `
  4085  				x([
  4086  					/* #__NO_SIDE_EFFECTS__ */ y => y,
  4087  					/* #__NO_SIDE_EFFECTS__ */ () => {},
  4088  					/* #__NO_SIDE_EFFECTS__ */ (y) => (y),
  4089  					/* #__NO_SIDE_EFFECTS__ */ async y => y,
  4090  					/* #__NO_SIDE_EFFECTS__ */ async () => {},
  4091  					/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
  4092  				])
  4093  			`,
  4094  
  4095  			"/stmt-fn.js": `
  4096  				// #__NO_SIDE_EFFECTS__
  4097  				function a() {}
  4098  				// #__NO_SIDE_EFFECTS__
  4099  				function* b() {}
  4100  				// #__NO_SIDE_EFFECTS__
  4101  				async function c() {}
  4102  				// #__NO_SIDE_EFFECTS__
  4103  				async function* d() {}
  4104  			`,
  4105  			"/stmt-export-fn.js": `
  4106  				/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  4107  				/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  4108  				/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  4109  				/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  4110  			`,
  4111  			"/stmt-local.js": `
  4112  				/* #__NO_SIDE_EFFECTS__ */ var v0 = function() {}, v1 = function() {}
  4113  				/* #__NO_SIDE_EFFECTS__ */ let l0 = function() {}, l1 = function() {}
  4114  				/* #__NO_SIDE_EFFECTS__ */ const c0 = function() {}, c1 = function() {}
  4115  				/* #__NO_SIDE_EFFECTS__ */ var v2 = () => {}, v3 = () => {}
  4116  				/* #__NO_SIDE_EFFECTS__ */ let l2 = () => {}, l3 = () => {}
  4117  				/* #__NO_SIDE_EFFECTS__ */ const c2 = () => {}, c3 = () => {}
  4118  			`,
  4119  			"/stmt-export-local.js": `
  4120  				/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  4121  				/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  4122  				/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  4123  				/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  4124  				/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  4125  				/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  4126  			`,
  4127  
  4128  			"/ns-export-fn.ts": `
  4129  				namespace ns {
  4130  					/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  4131  					/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  4132  					/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  4133  					/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  4134  				}
  4135  			`,
  4136  			"/ns-export-local.ts": `
  4137  				namespace ns {
  4138  					/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  4139  					/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  4140  					/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  4141  					/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  4142  					/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  4143  					/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  4144  				}
  4145  			`,
  4146  
  4147  			"/stmt-export-default-before-fn-anon.js":           `/* #__NO_SIDE_EFFECTS__ */ export default function() {}`,
  4148  			"/stmt-export-default-before-fn-name.js":           `/* #__NO_SIDE_EFFECTS__ */ export default function f() {}`,
  4149  			"/stmt-export-default-before-gen-fn-anon.js":       `/* #__NO_SIDE_EFFECTS__ */ export default function*() {}`,
  4150  			"/stmt-export-default-before-gen-fn-name.js":       `/* #__NO_SIDE_EFFECTS__ */ export default function* f() {}`,
  4151  			"/stmt-export-default-before-async-fn-anon.js":     `/* #__NO_SIDE_EFFECTS__ */ export default async function() {}`,
  4152  			"/stmt-export-default-before-async-fn-name.js":     `/* #__NO_SIDE_EFFECTS__ */ export default async function f() {}`,
  4153  			"/stmt-export-default-before-async-gen-fn-anon.js": `/* #__NO_SIDE_EFFECTS__ */ export default async function*() {}`,
  4154  			"/stmt-export-default-before-async-gen-fn-name.js": `/* #__NO_SIDE_EFFECTS__ */ export default async function* f() {}`,
  4155  
  4156  			"/stmt-export-default-after-fn-anon.js":           `export default /* @__NO_SIDE_EFFECTS__ */ function() {}`,
  4157  			"/stmt-export-default-after-fn-name.js":           `export default /* @__NO_SIDE_EFFECTS__ */ function f() {}`,
  4158  			"/stmt-export-default-after-gen-fn-anon.js":       `export default /* @__NO_SIDE_EFFECTS__ */ function*() {}`,
  4159  			"/stmt-export-default-after-gen-fn-name.js":       `export default /* @__NO_SIDE_EFFECTS__ */ function* f() {}`,
  4160  			"/stmt-export-default-after-async-fn-anon.js":     `export default /* @__NO_SIDE_EFFECTS__ */ async function() {}`,
  4161  			"/stmt-export-default-after-async-fn-name.js":     `export default /* @__NO_SIDE_EFFECTS__ */ async function f() {}`,
  4162  			"/stmt-export-default-after-async-gen-fn-anon.js": `export default /* @__NO_SIDE_EFFECTS__ */ async function*() {}`,
  4163  			"/stmt-export-default-after-async-gen-fn-name.js": `export default /* @__NO_SIDE_EFFECTS__ */ async function* f() {}`,
  4164  		},
  4165  		entryPaths: []string{
  4166  			"/expr-fn.js",
  4167  			"/expr-arrow.js",
  4168  
  4169  			"/stmt-fn.js",
  4170  			"/stmt-export-fn.js",
  4171  			"/stmt-local.js",
  4172  			"/stmt-export-local.js",
  4173  
  4174  			"/ns-export-fn.ts",
  4175  			"/ns-export-local.ts",
  4176  
  4177  			"/stmt-export-default-before-fn-anon.js",
  4178  			"/stmt-export-default-before-fn-name.js",
  4179  			"/stmt-export-default-before-gen-fn-anon.js",
  4180  			"/stmt-export-default-before-gen-fn-name.js",
  4181  			"/stmt-export-default-before-async-fn-anon.js",
  4182  			"/stmt-export-default-before-async-fn-name.js",
  4183  			"/stmt-export-default-before-async-gen-fn-anon.js",
  4184  			"/stmt-export-default-before-async-gen-fn-name.js",
  4185  
  4186  			"/stmt-export-default-after-fn-anon.js",
  4187  			"/stmt-export-default-after-fn-name.js",
  4188  			"/stmt-export-default-after-gen-fn-anon.js",
  4189  			"/stmt-export-default-after-gen-fn-name.js",
  4190  			"/stmt-export-default-after-async-fn-anon.js",
  4191  			"/stmt-export-default-after-async-fn-name.js",
  4192  			"/stmt-export-default-after-async-gen-fn-anon.js",
  4193  			"/stmt-export-default-after-async-gen-fn-name.js",
  4194  		},
  4195  		options: config.Options{
  4196  			AbsOutputDir:         "/out",
  4197  			IgnoreDCEAnnotations: true,
  4198  		},
  4199  	})
  4200  }
  4201  
  4202  func TestNoSideEffectsCommentMinifyWhitespace(t *testing.T) {
  4203  	dce_suite.expectBundled(t, bundled{
  4204  		files: map[string]string{
  4205  			"/expr-fn.js": `
  4206  				x([
  4207  					/* #__NO_SIDE_EFFECTS__ */ function() {},
  4208  					/* #__NO_SIDE_EFFECTS__ */ function y() {},
  4209  					/* #__NO_SIDE_EFFECTS__ */ function*() {},
  4210  					/* #__NO_SIDE_EFFECTS__ */ function* y() {},
  4211  					/* #__NO_SIDE_EFFECTS__ */ async function() {},
  4212  					/* #__NO_SIDE_EFFECTS__ */ async function y() {},
  4213  					/* #__NO_SIDE_EFFECTS__ */ async function*() {},
  4214  					/* #__NO_SIDE_EFFECTS__ */ async function* y() {},
  4215  				])
  4216  			`,
  4217  			"/expr-arrow.js": `
  4218  				x([
  4219  					/* #__NO_SIDE_EFFECTS__ */ y => y,
  4220  					/* #__NO_SIDE_EFFECTS__ */ () => {},
  4221  					/* #__NO_SIDE_EFFECTS__ */ (y) => (y),
  4222  					/* #__NO_SIDE_EFFECTS__ */ async y => y,
  4223  					/* #__NO_SIDE_EFFECTS__ */ async () => {},
  4224  					/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
  4225  				])
  4226  			`,
  4227  
  4228  			"/stmt-fn.js": `
  4229  				// #__NO_SIDE_EFFECTS__
  4230  				function a() {}
  4231  				// #__NO_SIDE_EFFECTS__
  4232  				function* b() {}
  4233  				// #__NO_SIDE_EFFECTS__
  4234  				async function c() {}
  4235  				// #__NO_SIDE_EFFECTS__
  4236  				async function* d() {}
  4237  			`,
  4238  			"/stmt-export-fn.js": `
  4239  				/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  4240  				/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  4241  				/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  4242  				/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  4243  			`,
  4244  			"/stmt-local.js": `
  4245  				/* #__NO_SIDE_EFFECTS__ */ var v0 = function() {}, v1 = function() {}
  4246  				/* #__NO_SIDE_EFFECTS__ */ let l0 = function() {}, l1 = function() {}
  4247  				/* #__NO_SIDE_EFFECTS__ */ const c0 = function() {}, c1 = function() {}
  4248  				/* #__NO_SIDE_EFFECTS__ */ var v2 = () => {}, v3 = () => {}
  4249  				/* #__NO_SIDE_EFFECTS__ */ let l2 = () => {}, l3 = () => {}
  4250  				/* #__NO_SIDE_EFFECTS__ */ const c2 = () => {}, c3 = () => {}
  4251  			`,
  4252  			"/stmt-export-local.js": `
  4253  				/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  4254  				/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  4255  				/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  4256  				/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  4257  				/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  4258  				/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  4259  			`,
  4260  
  4261  			"/ns-export-fn.ts": `
  4262  				namespace ns {
  4263  					/* @__NO_SIDE_EFFECTS__ */ export function a() {}
  4264  					/* @__NO_SIDE_EFFECTS__ */ export function* b() {}
  4265  					/* @__NO_SIDE_EFFECTS__ */ export async function c() {}
  4266  					/* @__NO_SIDE_EFFECTS__ */ export async function* d() {}
  4267  				}
  4268  			`,
  4269  			"/ns-export-local.ts": `
  4270  				namespace ns {
  4271  					/* #__NO_SIDE_EFFECTS__ */ export var v0 = function() {}, v1 = function() {}
  4272  					/* #__NO_SIDE_EFFECTS__ */ export let l0 = function() {}, l1 = function() {}
  4273  					/* #__NO_SIDE_EFFECTS__ */ export const c0 = function() {}, c1 = function() {}
  4274  					/* #__NO_SIDE_EFFECTS__ */ export var v2 = () => {}, v3 = () => {}
  4275  					/* #__NO_SIDE_EFFECTS__ */ export let l2 = () => {}, l3 = () => {}
  4276  					/* #__NO_SIDE_EFFECTS__ */ export const c2 = () => {}, c3 = () => {}
  4277  				}
  4278  			`,
  4279  
  4280  			"/stmt-export-default-before-fn-anon.js":           `/* #__NO_SIDE_EFFECTS__ */ export default function() {}`,
  4281  			"/stmt-export-default-before-fn-name.js":           `/* #__NO_SIDE_EFFECTS__ */ export default function f() {}`,
  4282  			"/stmt-export-default-before-gen-fn-anon.js":       `/* #__NO_SIDE_EFFECTS__ */ export default function*() {}`,
  4283  			"/stmt-export-default-before-gen-fn-name.js":       `/* #__NO_SIDE_EFFECTS__ */ export default function* f() {}`,
  4284  			"/stmt-export-default-before-async-fn-anon.js":     `/* #__NO_SIDE_EFFECTS__ */ export default async function() {}`,
  4285  			"/stmt-export-default-before-async-fn-name.js":     `/* #__NO_SIDE_EFFECTS__ */ export default async function f() {}`,
  4286  			"/stmt-export-default-before-async-gen-fn-anon.js": `/* #__NO_SIDE_EFFECTS__ */ export default async function*() {}`,
  4287  			"/stmt-export-default-before-async-gen-fn-name.js": `/* #__NO_SIDE_EFFECTS__ */ export default async function* f() {}`,
  4288  
  4289  			"/stmt-export-default-after-fn-anon.js":           `export default /* @__NO_SIDE_EFFECTS__ */ function() {}`,
  4290  			"/stmt-export-default-after-fn-name.js":           `export default /* @__NO_SIDE_EFFECTS__ */ function f() {}`,
  4291  			"/stmt-export-default-after-gen-fn-anon.js":       `export default /* @__NO_SIDE_EFFECTS__ */ function*() {}`,
  4292  			"/stmt-export-default-after-gen-fn-name.js":       `export default /* @__NO_SIDE_EFFECTS__ */ function* f() {}`,
  4293  			"/stmt-export-default-after-async-fn-anon.js":     `export default /* @__NO_SIDE_EFFECTS__ */ async function() {}`,
  4294  			"/stmt-export-default-after-async-fn-name.js":     `export default /* @__NO_SIDE_EFFECTS__ */ async function f() {}`,
  4295  			"/stmt-export-default-after-async-gen-fn-anon.js": `export default /* @__NO_SIDE_EFFECTS__ */ async function*() {}`,
  4296  			"/stmt-export-default-after-async-gen-fn-name.js": `export default /* @__NO_SIDE_EFFECTS__ */ async function* f() {}`,
  4297  		},
  4298  		entryPaths: []string{
  4299  			"/expr-fn.js",
  4300  			"/expr-arrow.js",
  4301  
  4302  			"/stmt-fn.js",
  4303  			"/stmt-export-fn.js",
  4304  			"/stmt-local.js",
  4305  			"/stmt-export-local.js",
  4306  
  4307  			"/ns-export-fn.ts",
  4308  			"/ns-export-local.ts",
  4309  
  4310  			"/stmt-export-default-before-fn-anon.js",
  4311  			"/stmt-export-default-before-fn-name.js",
  4312  			"/stmt-export-default-before-gen-fn-anon.js",
  4313  			"/stmt-export-default-before-gen-fn-name.js",
  4314  			"/stmt-export-default-before-async-fn-anon.js",
  4315  			"/stmt-export-default-before-async-fn-name.js",
  4316  			"/stmt-export-default-before-async-gen-fn-anon.js",
  4317  			"/stmt-export-default-before-async-gen-fn-name.js",
  4318  
  4319  			"/stmt-export-default-after-fn-anon.js",
  4320  			"/stmt-export-default-after-fn-name.js",
  4321  			"/stmt-export-default-after-gen-fn-anon.js",
  4322  			"/stmt-export-default-after-gen-fn-name.js",
  4323  			"/stmt-export-default-after-async-fn-anon.js",
  4324  			"/stmt-export-default-after-async-fn-name.js",
  4325  			"/stmt-export-default-after-async-gen-fn-anon.js",
  4326  			"/stmt-export-default-after-async-gen-fn-name.js",
  4327  		},
  4328  		options: config.Options{
  4329  			AbsOutputDir:     "/out",
  4330  			MinifyWhitespace: true,
  4331  		},
  4332  	})
  4333  }
  4334  
  4335  func TestNoSideEffectsCommentUnusedCalls(t *testing.T) {
  4336  	dce_suite.expectBundled(t, bundled{
  4337  		files: map[string]string{
  4338  			"/stmt-fn.js": `
  4339  				/* @__NO_SIDE_EFFECTS__ */ function f(y) { sideEffect(y) }
  4340  				/* @__NO_SIDE_EFFECTS__ */ function* g(y) { sideEffect(y) }
  4341  				f('removeThisCall')
  4342  				g('removeThisCall')
  4343  				f(onlyKeepThisIdentifier)
  4344  				g(onlyKeepThisIdentifier)
  4345  				x(f('keepThisCall'))
  4346  				x(g('keepThisCall'))
  4347  			`,
  4348  			"/stmt-local.js": `
  4349  				/* @__NO_SIDE_EFFECTS__ */ const f = function (y) { sideEffect(y) }
  4350  				/* @__NO_SIDE_EFFECTS__ */ const g = function* (y) { sideEffect(y) }
  4351  				f('removeThisCall')
  4352  				g('removeThisCall')
  4353  				f(onlyKeepThisIdentifier)
  4354  				g(onlyKeepThisIdentifier)
  4355  				x(f('keepThisCall'))
  4356  				x(g('keepThisCall'))
  4357  			`,
  4358  			"/expr-fn.js": `
  4359  				const f = /* @__NO_SIDE_EFFECTS__ */ function (y) { sideEffect(y) }
  4360  				const g = /* @__NO_SIDE_EFFECTS__ */ function* (y) { sideEffect(y) }
  4361  				f('removeThisCall')
  4362  				g('removeThisCall')
  4363  				f(onlyKeepThisIdentifier)
  4364  				g(onlyKeepThisIdentifier)
  4365  				x(f('keepThisCall'))
  4366  				x(g('keepThisCall'))
  4367  			`,
  4368  			"/stmt-export-default-fn.js": `
  4369  				/* @__NO_SIDE_EFFECTS__ */ export default function f(y) { sideEffect(y) }
  4370  				f('removeThisCall')
  4371  				f(onlyKeepThisIdentifier)
  4372  				x(f('keepThisCall'))
  4373  			`,
  4374  		},
  4375  		entryPaths: []string{
  4376  			"/stmt-fn.js",
  4377  			"/stmt-local.js",
  4378  			"/expr-fn.js",
  4379  			"/stmt-export-default-fn.js",
  4380  		},
  4381  		options: config.Options{
  4382  			AbsOutputDir: "/out",
  4383  			TreeShaking:  true,
  4384  			MinifySyntax: true,
  4385  		},
  4386  	})
  4387  }
  4388  
  4389  func TestNoSideEffectsCommentTypeScriptDeclare(t *testing.T) {
  4390  	dce_suite.expectBundled(t, bundled{
  4391  		files: map[string]string{
  4392  			"/entry.ts": `
  4393  				// These should not cause us to crash
  4394  				/* @__NO_SIDE_EFFECTS__ */ declare function f1(y) { sideEffect(y) }
  4395  				/* @__NO_SIDE_EFFECTS__ */ declare const f2 = function (y) { sideEffect(y) }
  4396  				/* @__NO_SIDE_EFFECTS__ */ declare const f3 = (y) => { sideEffect(y) }
  4397  				declare const f4 = /* @__NO_SIDE_EFFECTS__ */ function (y) { sideEffect(y) }
  4398  				declare const f5 = /* @__NO_SIDE_EFFECTS__ */ (y) => { sideEffect(y) }
  4399  				namespace ns {
  4400  					/* @__NO_SIDE_EFFECTS__ */ export declare function f1(y) { sideEffect(y) }
  4401  					/* @__NO_SIDE_EFFECTS__ */ export declare const f2 = function (y) { sideEffect(y) }
  4402  					/* @__NO_SIDE_EFFECTS__ */ export declare const f3 = (y) => { sideEffect(y) }
  4403  					export declare const f4 = /* @__NO_SIDE_EFFECTS__ */ function (y) { sideEffect(y) }
  4404  					export declare const f5 = /* @__NO_SIDE_EFFECTS__ */ (y) => { sideEffect(y) }
  4405  				}
  4406  			`,
  4407  		},
  4408  		entryPaths: []string{
  4409  			"/entry.ts",
  4410  		},
  4411  		options: config.Options{
  4412  			AbsOutputDir: "/out",
  4413  		},
  4414  	})
  4415  }
  4416  
  4417  func TestDCEOfIIFE(t *testing.T) {
  4418  	dce_suite.expectBundled(t, bundled{
  4419  		files: map[string]string{
  4420  			"/remove-these.js": `
  4421  				(() => {})();
  4422  				(() => {})(keepThisButRemoveTheIIFE);
  4423  				(() => { /* @__PURE__ */ removeMe() })();
  4424  				var someVar;
  4425  				(x => {})(someVar);
  4426  				var removeThis = /* @__PURE__ */ (() => stuff())();
  4427  				var removeThis2 = (() => 123)();
  4428  			`,
  4429  			"/keep-these.js": `
  4430  				undef = (() => {})();
  4431  				(() => { keepMe() })();
  4432  				((x = keepMe()) => {})();
  4433  				var someVar;
  4434  				(([y]) => {})(someVar);
  4435  				(({z}) => {})(someVar);
  4436  				var keepThis = /* @__PURE__ */ (() => stuff())();
  4437  				keepThis();
  4438  				((_ = keepMe()) => {})();
  4439  				var isPure = ((x, y) => 123)();
  4440  				use(isPure);
  4441  				var isNotPure = ((x = foo, y = bar) => 123)();
  4442  				use(isNotPure);
  4443  				(async () => ({ get then() { notPure() } }))();
  4444  				(async function() { return { get then() { notPure() } }; })();
  4445  			`,
  4446  		},
  4447  		entryPaths: []string{
  4448  			"/remove-these.js",
  4449  			"/keep-these.js",
  4450  		},
  4451  		options: config.Options{
  4452  			AbsOutputDir: "/out",
  4453  			MinifySyntax: true,
  4454  			TreeShaking:  true,
  4455  		},
  4456  	})
  4457  }
  4458  
  4459  func TestDCEOfDestructuring(t *testing.T) {
  4460  	dce_suite.expectBundled(t, bundled{
  4461  		files: map[string]string{
  4462  			"/entry.js": `
  4463  				// Identifier bindings
  4464  				var remove1
  4465  				var remove2 = null
  4466  				var KEEP1 = x
  4467  
  4468  				// Array patterns
  4469  				var [remove3] = []
  4470  				var [remove4, ...remove5] = [...[1, 2], 3]
  4471  				var [, , remove6] = [, , 3]
  4472  				var [KEEP2] = [x]
  4473  				var [KEEP3] = [...{}]
  4474  
  4475  				// Object patterns (not handled right now)
  4476  				var { KEEP4 } = {}
  4477  			`,
  4478  		},
  4479  		entryPaths: []string{"/entry.js"},
  4480  		options: config.Options{
  4481  			Mode:         config.ModeBundle,
  4482  			AbsOutputDir: "/out",
  4483  		},
  4484  	})
  4485  }
  4486  
  4487  func TestDCEOfDecorators(t *testing.T) {
  4488  	dce_suite.expectBundled(t, bundled{
  4489  		files: map[string]string{
  4490  			"/keep-these.js": `
  4491  				import { fn } from './decorator'
  4492  				@fn class Class {}
  4493  				class Field { @fn field }
  4494  				class Method { @fn method() {} }
  4495  				class Accessor { @fn accessor accessor }
  4496  				class StaticField { @fn static field }
  4497  				class StaticMethod { @fn static method() {} }
  4498  				class StaticAccessor { @fn static accessor accessor }
  4499  			`,
  4500  			"/decorator.js": `
  4501  				export const fn = () => {
  4502  					console.log('side effect')
  4503  				}
  4504  			`,
  4505  		},
  4506  		entryPaths: []string{"/keep-these.js"},
  4507  		options: config.Options{
  4508  			Mode:         config.ModeBundle,
  4509  			AbsOutputDir: "/out",
  4510  		},
  4511  	})
  4512  }
  4513  
  4514  func TestDCEOfExperimentalDecorators(t *testing.T) {
  4515  	dce_suite.expectBundled(t, bundled{
  4516  		files: map[string]string{
  4517  			"/keep-these.ts": `
  4518  				import { fn } from './decorator'
  4519  				@fn class Class {}
  4520  				class Field { @fn field }
  4521  				class Method { @fn method() {} }
  4522  				class Accessor { @fn accessor accessor }
  4523  				class Parameter { foo(@fn bar) {} }
  4524  				class StaticField { @fn static field }
  4525  				class StaticMethod { @fn static method() {} }
  4526  				class StaticAccessor { @fn static accessor accessor }
  4527  				class StaticParameter { static foo(@fn bar) {} }
  4528  			`,
  4529  			"/decorator.ts": `
  4530  				export const fn = () => {
  4531  					console.log('side effect')
  4532  				}
  4533  			`,
  4534  			"/tsconfig.json": `{
  4535  				"compilerOptions": {
  4536  					"experimentalDecorators": true
  4537  				}
  4538  			}`,
  4539  		},
  4540  		entryPaths: []string{"/keep-these.ts"},
  4541  		options: config.Options{
  4542  			Mode:         config.ModeBundle,
  4543  			AbsOutputDir: "/out",
  4544  		},
  4545  	})
  4546  }
  4547  
  4548  func TestDCEOfUsingDeclarations(t *testing.T) {
  4549  	dce_suite.expectBundled(t, bundled{
  4550  		files: map[string]string{
  4551  			"/entry.js": `
  4552  				// Note: Only remove "using" if it's null or undefined and not awaited
  4553  
  4554  				using null_remove = null
  4555  				using null_keep = null
  4556  				await using await_null_keep = null
  4557  
  4558  				// This has a side effect: throwing an error
  4559  				using throw_keep = {}
  4560  
  4561  				using dispose_keep = { [Symbol.dispose]() { console.log('side effect') } }
  4562  				await using await_asyncDispose_keep = { [Symbol.asyncDispose]() { console.log('side effect') } }
  4563  
  4564  				using undef_remove = undefined
  4565  				using undef_keep = undefined
  4566  				await using await_undef_keep = undefined
  4567  
  4568  				// Assume these have no side effects
  4569  				const Symbol_dispose_remove = Symbol.dispose
  4570  				const Symbol_asyncDispose_remove = Symbol.asyncDispose
  4571  
  4572  				console.log(
  4573  					null_keep,
  4574  					undef_keep,
  4575  				)
  4576  			`,
  4577  		},
  4578  		entryPaths: []string{"/entry.js"},
  4579  		options: config.Options{
  4580  			Mode:         config.ModeBundle,
  4581  			TreeShaking:  true,
  4582  			AbsOutputDir: "/out",
  4583  		},
  4584  	})
  4585  }
  4586  
  4587  func TestDCEOfExprAfterKeepNamesIssue3195(t *testing.T) {
  4588  	dce_suite.expectBundled(t, bundled{
  4589  		files: map[string]string{
  4590  			"/entry.js": `
  4591  				(() => {
  4592  					function f() {}
  4593  					firstImportantSideEffect(f());
  4594  				})();
  4595  				(() => {
  4596  					function g() {}
  4597  					debugger;
  4598  					secondImportantSideEffect(g());
  4599  				})();
  4600  			`,
  4601  		},
  4602  		entryPaths: []string{"/entry.js"},
  4603  		options: config.Options{
  4604  			MinifySyntax:  true,
  4605  			KeepNames:     true,
  4606  			AbsOutputFile: "/out.js",
  4607  		},
  4608  	})
  4609  }
  4610  
  4611  func TestDropLabels(t *testing.T) {
  4612  	dce_suite.expectBundled(t, bundled{
  4613  		files: map[string]string{
  4614  			"/entry.js": `
  4615  				keep_1: require('foo1')
  4616  				DROP_1: require('bar1')
  4617  				exports.bar = function() {
  4618  					if (x) DROP_2: require('foo2')
  4619  					if (y) keep_2: require('bar2')
  4620  				}
  4621  			`,
  4622  		},
  4623  		entryPaths: []string{"/entry.js"},
  4624  		options: config.Options{
  4625  			Mode: config.ModeBundle,
  4626  			DropLabels: []string{
  4627  				"DROP_1",
  4628  				"DROP_2",
  4629  			},
  4630  			ExternalSettings: config.ExternalSettings{
  4631  				PreResolve: config.ExternalMatchers{
  4632  					Exact: map[string]bool{
  4633  						"foo1": true,
  4634  						"bar2": true,
  4635  					},
  4636  				},
  4637  			},
  4638  			AbsOutputFile: "/out.js",
  4639  			OutputFormat:  config.FormatCommonJS,
  4640  		},
  4641  	})
  4642  }
  4643  
  4644  func TestRemoveCodeAfterLabelWithReturn(t *testing.T) {
  4645  	dce_suite.expectBundled(t, bundled{
  4646  		files: map[string]string{
  4647  			"/entry.js": `
  4648  				function earlyReturn() {
  4649  					// This comes up when doing conditional compilation with "DropLabels"
  4650  					keep: {
  4651  						onlyWithKeep()
  4652  						return
  4653  					}
  4654  					onlyWithoutKeep()
  4655  				}
  4656  				function loop() {
  4657  					if (foo()) {
  4658  						keep: {
  4659  							bar()
  4660  							return;
  4661  						}
  4662  					}
  4663  				}
  4664  			`,
  4665  		},
  4666  		entryPaths: []string{"/entry.js"},
  4667  		options: config.Options{
  4668  			AbsOutputFile: "/out.js",
  4669  			MinifySyntax:  true,
  4670  		},
  4671  	})
  4672  }
  4673  
  4674  func TestDropLabelTreeShakingBugIssue3311(t *testing.T) {
  4675  	dce_suite.expectBundled(t, bundled{
  4676  		files: map[string]string{
  4677  			"/entry.js": `
  4678  				const myFunc = ()=> {
  4679  					DROP: {console.log("drop")}
  4680  					console.log("keep")
  4681  				}
  4682  				export default myFunc
  4683  			`,
  4684  		},
  4685  		entryPaths: []string{"/entry.js"},
  4686  		options: config.Options{
  4687  			Mode:          config.ModeBundle,
  4688  			AbsOutputFile: "/out.js",
  4689  			DropLabels:    []string{"DROP"},
  4690  			OutputFormat:  config.FormatESModule,
  4691  		},
  4692  	})
  4693  }
  4694  
  4695  func TestDCEOfSymbolInstances(t *testing.T) {
  4696  	dce_suite.expectBundled(t, bundled{
  4697  		files: map[string]string{
  4698  			"/class.js": `
  4699  				class Remove1 {}
  4700  				class Remove2 { *[Symbol.iterator]() {} }
  4701  				class Remove3 { *[Symbol['iterator']]() {} }
  4702  
  4703  				class Keep1 { *[Symbol.iterator]() {} [keep] }
  4704  				class Keep2 { [keep]; *[Symbol.iterator]() {} }
  4705  				class Keep3 { *[Symbol.wtf]() {} }
  4706  			`,
  4707  			"/object.js": `
  4708  				let remove1 = {}
  4709  				let remove2 = { *[Symbol.iterator]() {} }
  4710  				let remove3 = { *[Symbol['iterator']]() {} }
  4711  
  4712  				let keep1 = { *[Symbol.iterator]() {}, [keep]: null }
  4713  				let keep2 = { [keep]: null, *[Symbol.iterator]() {} }
  4714  				let keep3 = { *[Symbol.wtf]() {} }
  4715  			`,
  4716  		},
  4717  		entryPaths: []string{
  4718  			"/class.js",
  4719  			"/object.js",
  4720  		},
  4721  		options: config.Options{
  4722  			Mode:         config.ModeBundle,
  4723  			AbsOutputDir: "/out",
  4724  		},
  4725  	})
  4726  }