github.com/evanw/esbuild@v0.21.4/internal/js_parser/js_parser.go (about) 1 package js_parser 2 3 import ( 4 "fmt" 5 "math" 6 "regexp" 7 "sort" 8 "strings" 9 "unicode/utf8" 10 11 "github.com/evanw/esbuild/internal/ast" 12 "github.com/evanw/esbuild/internal/compat" 13 "github.com/evanw/esbuild/internal/config" 14 "github.com/evanw/esbuild/internal/helpers" 15 "github.com/evanw/esbuild/internal/js_ast" 16 "github.com/evanw/esbuild/internal/js_lexer" 17 "github.com/evanw/esbuild/internal/logger" 18 "github.com/evanw/esbuild/internal/renamer" 19 "github.com/evanw/esbuild/internal/runtime" 20 ) 21 22 // This parser does two passes: 23 // 24 // 1. Parse the source into an AST, create the scope tree, and declare symbols. 25 // 26 // 2. Visit each node in the AST, bind identifiers to declared symbols, do 27 // constant folding, substitute compile-time variable definitions, and 28 // lower certain syntactic constructs as appropriate given the language 29 // target. 30 // 31 // So many things have been put in so few passes because we want to minimize 32 // the number of full-tree passes to improve performance. However, we need 33 // to have at least two separate passes to handle variable hoisting. See the 34 // comment about scopesInOrder below for more information. 35 type parser struct { 36 options Options 37 log logger.Log 38 source logger.Source 39 tracker logger.LineColumnTracker 40 fnOrArrowDataParse fnOrArrowDataParse 41 fnOnlyDataVisit fnOnlyDataVisit 42 allocatedNames []string 43 currentScope *js_ast.Scope 44 scopesForCurrentPart []*js_ast.Scope 45 symbols []ast.Symbol 46 astHelpers js_ast.HelperContext 47 tsUseCounts []uint32 48 injectedDefineSymbols []ast.Ref 49 injectedSymbolSources map[ast.Ref]injectedSymbolSource 50 injectedDotNames map[string][]injectedDotName 51 dropLabelsMap map[string]struct{} 52 exprComments map[logger.Loc][]string 53 mangledProps map[string]ast.Ref 54 reservedProps map[string]bool 55 symbolUses map[ast.Ref]js_ast.SymbolUse 56 importSymbolPropertyUses map[ast.Ref]map[string]js_ast.SymbolUse 57 symbolCallUses map[ast.Ref]js_ast.SymbolCallUse 58 declaredSymbols []js_ast.DeclaredSymbol 59 globPatternImports []globPatternImport 60 runtimeImports map[string]ast.LocRef 61 duplicateCaseChecker duplicateCaseChecker 62 unrepresentableIdentifiers map[string]bool 63 legacyOctalLiterals map[js_ast.E]logger.Range 64 scopesInOrderForEnum map[logger.Loc][]scopeOrder 65 binaryExprStack []binaryExprVisitor 66 67 // For strict mode handling 68 hoistedRefForSloppyModeBlockFn map[ast.Ref]ast.Ref 69 70 // For lowering private methods 71 privateGetters map[ast.Ref]ast.Ref 72 privateSetters map[ast.Ref]ast.Ref 73 74 // These are for TypeScript 75 // 76 // We build up enough information about the TypeScript namespace hierarchy to 77 // be able to resolve scope lookups and property accesses for TypeScript enum 78 // and namespace features. Each JavaScript scope object inside a namespace 79 // has a reference to a map of exported namespace members from sibling scopes. 80 // 81 // In addition, there is a map from each relevant symbol reference to the data 82 // associated with that namespace or namespace member: "refToTSNamespaceMemberData". 83 // This gives enough info to be able to resolve queries into the namespace. 84 // 85 // When visiting expressions, namespace metadata is associated with the most 86 // recently visited node. If namespace metadata is present, "tsNamespaceTarget" 87 // will be set to the most recently visited node (as a way to mark that this 88 // node has metadata) and "tsNamespaceMemberData" will be set to the metadata. 89 refToTSNamespaceMemberData map[ast.Ref]js_ast.TSNamespaceMemberData 90 tsNamespaceTarget js_ast.E 91 tsNamespaceMemberData js_ast.TSNamespaceMemberData 92 emittedNamespaceVars map[ast.Ref]bool 93 isExportedInsideNamespace map[ast.Ref]ast.Ref 94 localTypeNames map[string]bool 95 tsEnums map[ast.Ref]map[string]js_ast.TSEnumValue 96 constValues map[ast.Ref]js_ast.ConstValue 97 propDerivedCtorValue js_ast.E 98 propMethodDecoratorScope *js_ast.Scope 99 100 // This is the reference to the generated function argument for the namespace, 101 // which is different than the reference to the namespace itself: 102 // 103 // namespace ns { 104 // } 105 // 106 // The code above is transformed into something like this: 107 // 108 // var ns1; 109 // (function(ns2) { 110 // })(ns1 || (ns1 = {})); 111 // 112 // This variable is "ns2" not "ns1". It is only used during the second 113 // "visit" pass. 114 enclosingNamespaceArgRef *ast.Ref 115 116 // Imports (both ES6 and CommonJS) are tracked at the top level 117 importRecords []ast.ImportRecord 118 importRecordsForCurrentPart []uint32 119 exportStarImportRecords []uint32 120 121 // These are for handling ES6 imports and exports 122 importItemsForNamespace map[ast.Ref]namespaceImportItems 123 isImportItem map[ast.Ref]bool 124 namedImports map[ast.Ref]js_ast.NamedImport 125 namedExports map[string]js_ast.NamedExport 126 topLevelSymbolToParts map[ast.Ref][]uint32 127 importNamespaceCCMap map[importNamespaceCall]bool 128 129 // The parser does two passes and we need to pass the scope tree information 130 // from the first pass to the second pass. That's done by tracking the calls 131 // to pushScopeForParsePass() and popScope() during the first pass in 132 // scopesInOrder. 133 // 134 // Then, when the second pass calls pushScopeForVisitPass() and popScope(), 135 // we consume entries from scopesInOrder and make sure they are in the same 136 // order. This way the second pass can efficiently use the same scope tree 137 // as the first pass without having to attach the scope tree to the AST. 138 // 139 // We need to split this into two passes because the pass that declares the 140 // symbols must be separate from the pass that binds identifiers to declared 141 // symbols to handle declaring a hoisted "var" symbol in a nested scope and 142 // binding a name to it in a parent or sibling scope. 143 scopesInOrder []scopeOrder 144 145 // These propagate the name from the parent context into an anonymous child 146 // expression. For example: 147 // 148 // let foo = function() {} 149 // assert.strictEqual(foo.name, 'foo') 150 // 151 nameToKeep string 152 nameToKeepIsFor js_ast.E 153 154 // These properties are for the visit pass, which runs after the parse pass. 155 // The visit pass binds identifiers to declared symbols, does constant 156 // folding, substitutes compile-time variable definitions, and lowers certain 157 // syntactic constructs as appropriate. 158 stmtExprValue js_ast.E 159 callTarget js_ast.E 160 dotOrIndexTarget js_ast.E 161 templateTag js_ast.E 162 deleteTarget js_ast.E 163 loopBody js_ast.S 164 suspiciousLogicalOperatorInsideArrow js_ast.E 165 moduleScope *js_ast.Scope 166 167 // This is internal-only data used for the implementation of Yarn PnP 168 manifestForYarnPnP js_ast.Expr 169 stringLocalsForYarnPnP map[ast.Ref]stringLocalForYarnPnP 170 171 // This helps recognize the "await import()" pattern. When this is present, 172 // warnings about non-string import paths will be omitted inside try blocks. 173 awaitTarget js_ast.E 174 175 // This helps recognize the "import().catch()" pattern. We also try to avoid 176 // warning about this just like the "try { await import() }" pattern. 177 thenCatchChain thenCatchChain 178 179 // When bundling, hoisted top-level local variables declared with "var" in 180 // nested scopes are moved up to be declared in the top-level scope instead. 181 // The old "var" statements are turned into regular assignments instead. This 182 // makes it easier to quickly scan the top-level statements for "var" locals 183 // with the guarantee that all will be found. 184 relocatedTopLevelVars []ast.LocRef 185 186 // We need to lower private names such as "#foo" if they are used in a brand 187 // check such as "#foo in x" even if the private name syntax would otherwise 188 // be supported. This is because private names are a newly-added feature. 189 // 190 // However, this parser operates in only two passes for speed. The first pass 191 // parses things and declares variables, and the second pass lowers things and 192 // resolves references to declared variables. So the existence of a "#foo in x" 193 // expression for a specific "#foo" cannot be used to decide to lower "#foo" 194 // because it's too late by that point. There may be another expression such 195 // as "x.#foo" before that point and that must be lowered as well even though 196 // it has already been visited. 197 // 198 // Instead what we do is track just the names of fields used in private brand 199 // checks during the first pass. This tracks the names themselves, not symbol 200 // references. Then, during the second pass when we are about to enter into 201 // a class, we conservatively decide to lower all private names in that class 202 // which are used in a brand check anywhere in the file. 203 lowerAllOfThesePrivateNames map[string]bool 204 205 // Temporary variables used for lowering 206 tempLetsToDeclare []ast.Ref 207 tempRefsToDeclare []tempRef 208 topLevelTempRefsToDeclare []tempRef 209 210 lexer js_lexer.Lexer 211 212 // Private field access in a decorator lowers all private fields in that class 213 parseExperimentalDecoratorNesting int 214 215 // Temporary variables used for lowering 216 tempRefCount int 217 topLevelTempRefCount int 218 219 // We need to scan over the source contents to recover the line and column offsets 220 jsxSourceLoc int 221 jsxSourceLine int 222 jsxSourceColumn int 223 224 exportsRef ast.Ref 225 requireRef ast.Ref 226 moduleRef ast.Ref 227 importMetaRef ast.Ref 228 promiseRef ast.Ref 229 regExpRef ast.Ref 230 superCtorRef ast.Ref 231 232 // Imports from "react/jsx-runtime" and "react", respectively. 233 // (Or whatever was specified in the "importSource" option) 234 jsxRuntimeImports map[string]ast.LocRef 235 jsxLegacyImports map[string]ast.LocRef 236 237 // For lowering private methods 238 weakMapRef ast.Ref 239 weakSetRef ast.Ref 240 241 esmImportStatementKeyword logger.Range 242 esmImportMeta logger.Range 243 esmExportKeyword logger.Range 244 enclosingClassKeyword logger.Range 245 topLevelAwaitKeyword logger.Range 246 liveTopLevelAwaitKeyword logger.Range 247 248 latestArrowArgLoc logger.Loc 249 forbidSuffixAfterAsLoc logger.Loc 250 firstJSXElementLoc logger.Loc 251 252 fnOrArrowDataVisit fnOrArrowDataVisit 253 254 // ArrowFunction is a special case in the grammar. Although it appears to be 255 // a PrimaryExpression, it's actually an AssignmentExpression. This means if 256 // a AssignmentExpression ends up producing an ArrowFunction then nothing can 257 // come after it other than the comma operator, since the comma operator is 258 // the only thing above AssignmentExpression under the Expression rule: 259 // 260 // AssignmentExpression: 261 // ArrowFunction 262 // ConditionalExpression 263 // LeftHandSideExpression = AssignmentExpression 264 // LeftHandSideExpression AssignmentOperator AssignmentExpression 265 // 266 // Expression: 267 // AssignmentExpression 268 // Expression , AssignmentExpression 269 // 270 afterArrowBodyLoc logger.Loc 271 272 // Setting this to true disables warnings about code that is very likely to 273 // be a bug. This is used to ignore issues inside "node_modules" directories. 274 // This has caught real issues in the past. However, it's not esbuild's job 275 // to find bugs in other libraries, and these warnings are problematic for 276 // people using these libraries with esbuild. The only fix is to either 277 // disable all esbuild warnings and not get warnings about your own code, or 278 // to try to get the warning fixed in the affected library. This is 279 // especially annoying if the warning is a false positive as was the case in 280 // https://github.com/firebase/firebase-js-sdk/issues/3814. So these warnings 281 // are now disabled for code inside "node_modules" directories. 282 suppressWarningsAboutWeirdCode bool 283 284 // A file is considered to be an ECMAScript module if it has any of the 285 // features of one (e.g. the "export" keyword), otherwise it's considered 286 // a CommonJS module. 287 // 288 // However, we have a single exception: a file where the only ESM feature 289 // is the "import" keyword is allowed to have CommonJS exports. This feature 290 // is necessary to be able to synchronously import ESM code into CommonJS, 291 // which we need to enable in a few important cases. Some examples are: 292 // our runtime code, injected files (the "inject" feature is ESM-only), 293 // and certain automatically-generated virtual modules from plugins. 294 isFileConsideredToHaveESMExports bool // Use only for export-related stuff 295 isFileConsideredESM bool // Use for all other stuff 296 297 // Inside a TypeScript namespace, an "export declare" statement can be used 298 // to cause a namespace to be emitted even though it has no other observable 299 // effect. This flag is used to implement this feature. 300 // 301 // Specifically, namespaces should be generated for all of the following 302 // namespaces below except for "f", which should not be generated: 303 // 304 // namespace a { export declare const a } 305 // namespace b { export declare let [[b]] } 306 // namespace c { export declare function c() } 307 // namespace d { export declare class d {} } 308 // namespace e { export declare enum e {} } 309 // namespace f { export declare namespace f {} } 310 // 311 // The TypeScript compiler compiles this into the following code (notice "f" 312 // is missing): 313 // 314 // var a; (function (a_1) {})(a || (a = {})); 315 // var b; (function (b_1) {})(b || (b = {})); 316 // var c; (function (c_1) {})(c || (c = {})); 317 // var d; (function (d_1) {})(d || (d = {})); 318 // var e; (function (e_1) {})(e || (e = {})); 319 // 320 // Note that this should not be implemented by declaring symbols for "export 321 // declare" statements because the TypeScript compiler doesn't generate any 322 // code for these statements, so these statements are actually references to 323 // global variables. There is one exception, which is that local variables 324 // *should* be declared as symbols because they are replaced with. This seems 325 // like very arbitrary behavior but it's what the TypeScript compiler does, 326 // so we try to match it. 327 // 328 // Specifically, in the following code below "a" and "b" should be declared 329 // and should be substituted with "ns.a" and "ns.b" but the other symbols 330 // shouldn't. References to the other symbols actually refer to global 331 // variables instead of to symbols that are exported from the namespace. 332 // This is the case as of TypeScript 4.3. I assume this is a TypeScript bug: 333 // 334 // namespace ns { 335 // export declare const a 336 // export declare let [[b]] 337 // export declare function c() 338 // export declare class d { } 339 // export declare enum e { } 340 // console.log(a, b, c, d, e) 341 // } 342 // 343 // The TypeScript compiler compiles this into the following code: 344 // 345 // var ns; 346 // (function (ns) { 347 // console.log(ns.a, ns.b, c, d, e); 348 // })(ns || (ns = {})); 349 // 350 // Relevant issue: https://github.com/evanw/esbuild/issues/1158 351 hasNonLocalExportDeclareInsideNamespace bool 352 353 // When this flag is enabled, we attempt to fold all expressions that 354 // TypeScript would consider to be "constant expressions". This flag is 355 // enabled inside each enum body block since TypeScript requires numeric 356 // constant folding in enum definitions. 357 // 358 // We also enable this flag in certain cases in JavaScript files such as when 359 // parsing "const" declarations at the top of a non-ESM file, but we still 360 // reuse TypeScript's notion of "constant expressions" for our own convenience. 361 // 362 // As of TypeScript 5.0, a "constant expression" is defined as follows: 363 // 364 // An expression is considered a constant expression if it is 365 // 366 // * a number or string literal, 367 // * a unary +, -, or ~ applied to a numeric constant expression, 368 // * a binary +, -, *, /, %, **, <<, >>, >>>, |, &, ^ applied to two numeric constant expressions, 369 // * a binary + applied to two constant expressions whereof at least one is a string, 370 // * a template expression where each substitution expression is a constant expression, 371 // * a parenthesized constant expression, 372 // * a dotted name (e.g. x.y.z) that references a const variable with a constant expression initializer and no type annotation, 373 // * a dotted name that references an enum member with an enum literal type, or 374 // * a dotted name indexed by a string literal (e.g. x.y["z"]) that references an enum member with an enum literal type. 375 // 376 // More detail: https://github.com/microsoft/TypeScript/pull/50528. Note that 377 // we don't implement certain items in this list. For example, we don't do all 378 // number-to-string conversions since ours might differ from how JavaScript 379 // would do it, which would be a correctness issue. 380 shouldFoldTypeScriptConstantExpressions bool 381 382 allowIn bool 383 allowPrivateIdentifiers bool 384 hasTopLevelReturn bool 385 latestReturnHadSemicolon bool 386 messageAboutThisIsUndefined bool 387 isControlFlowDead bool 388 389 // If this is true, then all top-level statements are wrapped in a try/catch 390 willWrapModuleInTryCatchForUsing bool 391 } 392 393 type globPatternImport struct { 394 assertOrWith *ast.ImportAssertOrWith 395 parts []helpers.GlobPart 396 name string 397 approximateRange logger.Range 398 ref ast.Ref 399 kind ast.ImportKind 400 } 401 402 type namespaceImportItems struct { 403 entries map[string]ast.LocRef 404 importRecordIndex uint32 405 } 406 407 type stringLocalForYarnPnP struct { 408 value []uint16 409 loc logger.Loc 410 } 411 412 type injectedSymbolSource struct { 413 source logger.Source 414 loc logger.Loc 415 } 416 417 type injectedDotName struct { 418 parts []string 419 injectedDefineIndex uint32 420 } 421 422 type importNamespaceCallKind uint8 423 424 const ( 425 exprKindCall importNamespaceCallKind = iota 426 exprKindNew 427 exprKindJSXTag 428 ) 429 430 type importNamespaceCall struct { 431 ref ast.Ref 432 kind importNamespaceCallKind 433 } 434 435 type thenCatchChain struct { 436 nextTarget js_ast.E 437 catchLoc logger.Loc 438 hasMultipleArgs bool 439 hasCatch bool 440 } 441 442 // This is used as part of an incremental build cache key. Some of these values 443 // can potentially change between builds if they are derived from nearby 444 // "package.json" or "tsconfig.json" files that were changed since the last 445 // build. 446 type Options struct { 447 injectedFiles []config.InjectedFile 448 jsx config.JSXOptions 449 tsAlwaysStrict *config.TSAlwaysStrict 450 mangleProps *regexp.Regexp 451 reserveProps *regexp.Regexp 452 dropLabels []string 453 454 // This pointer will always be different for each build but the contents 455 // shouldn't ever behave different semantically. We ignore this field for the 456 // equality comparison. 457 defines *config.ProcessedDefines 458 459 // This is an embedded struct. Always access these directly instead of off 460 // the name "optionsThatSupportStructuralEquality". This is only grouped like 461 // this to make the equality comparison easier and safer (and hopefully faster). 462 optionsThatSupportStructuralEquality 463 } 464 465 type optionsThatSupportStructuralEquality struct { 466 originalTargetEnv string 467 moduleTypeData js_ast.ModuleTypeData 468 unsupportedJSFeatures compat.JSFeature 469 unsupportedJSFeatureOverrides compat.JSFeature 470 unsupportedJSFeatureOverridesMask compat.JSFeature 471 472 // Byte-sized values go here (gathered together here to keep this object compact) 473 ts config.TSOptions 474 mode config.Mode 475 platform config.Platform 476 outputFormat config.Format 477 asciiOnly bool 478 keepNames bool 479 minifySyntax bool 480 minifyIdentifiers bool 481 minifyWhitespace bool 482 omitRuntimeForTests bool 483 omitJSXRuntimeForTests bool 484 ignoreDCEAnnotations bool 485 treeShaking bool 486 dropDebugger bool 487 mangleQuoted bool 488 489 // This is an internal-only option used for the implementation of Yarn PnP 490 decodeHydrateRuntimeStateYarnPnP bool 491 } 492 493 func OptionsForYarnPnP() Options { 494 return Options{ 495 optionsThatSupportStructuralEquality: optionsThatSupportStructuralEquality{ 496 decodeHydrateRuntimeStateYarnPnP: true, 497 }, 498 } 499 } 500 501 func OptionsFromConfig(options *config.Options) Options { 502 return Options{ 503 injectedFiles: options.InjectedFiles, 504 jsx: options.JSX, 505 defines: options.Defines, 506 tsAlwaysStrict: options.TSAlwaysStrict, 507 mangleProps: options.MangleProps, 508 reserveProps: options.ReserveProps, 509 dropLabels: options.DropLabels, 510 511 optionsThatSupportStructuralEquality: optionsThatSupportStructuralEquality{ 512 unsupportedJSFeatures: options.UnsupportedJSFeatures, 513 unsupportedJSFeatureOverrides: options.UnsupportedJSFeatureOverrides, 514 unsupportedJSFeatureOverridesMask: options.UnsupportedJSFeatureOverridesMask, 515 originalTargetEnv: options.OriginalTargetEnv, 516 ts: options.TS, 517 mode: options.Mode, 518 platform: options.Platform, 519 outputFormat: options.OutputFormat, 520 moduleTypeData: options.ModuleTypeData, 521 asciiOnly: options.ASCIIOnly, 522 keepNames: options.KeepNames, 523 minifySyntax: options.MinifySyntax, 524 minifyIdentifiers: options.MinifyIdentifiers, 525 minifyWhitespace: options.MinifyWhitespace, 526 omitRuntimeForTests: options.OmitRuntimeForTests, 527 omitJSXRuntimeForTests: options.OmitJSXRuntimeForTests, 528 ignoreDCEAnnotations: options.IgnoreDCEAnnotations, 529 treeShaking: options.TreeShaking, 530 dropDebugger: options.DropDebugger, 531 mangleQuoted: options.MangleQuoted, 532 }, 533 } 534 } 535 536 func (a *Options) Equal(b *Options) bool { 537 // Compare "optionsThatSupportStructuralEquality" 538 if a.optionsThatSupportStructuralEquality != b.optionsThatSupportStructuralEquality { 539 return false 540 } 541 542 // Compare "tsAlwaysStrict" 543 if (a.tsAlwaysStrict == nil && b.tsAlwaysStrict != nil) || (a.tsAlwaysStrict != nil && b.tsAlwaysStrict == nil) || 544 (a.tsAlwaysStrict != nil && b.tsAlwaysStrict != nil && *a.tsAlwaysStrict != *b.tsAlwaysStrict) { 545 return false 546 } 547 548 // Compare "mangleProps" and "reserveProps" 549 if !isSameRegexp(a.mangleProps, b.mangleProps) || !isSameRegexp(a.reserveProps, b.reserveProps) { 550 return false 551 } 552 553 // Compare "dropLabels" 554 if !helpers.StringArraysEqual(a.dropLabels, b.dropLabels) { 555 return false 556 } 557 558 // Compare "injectedFiles" 559 if len(a.injectedFiles) != len(b.injectedFiles) { 560 return false 561 } 562 for i, x := range a.injectedFiles { 563 y := b.injectedFiles[i] 564 if x.Source != y.Source || x.DefineName != y.DefineName || len(x.Exports) != len(y.Exports) { 565 return false 566 } 567 for j := range x.Exports { 568 if x.Exports[j] != y.Exports[j] { 569 return false 570 } 571 } 572 } 573 574 // Compare "jsx" 575 if a.jsx.Parse != b.jsx.Parse || !jsxExprsEqual(a.jsx.Factory, b.jsx.Factory) || !jsxExprsEqual(a.jsx.Fragment, b.jsx.Fragment) { 576 return false 577 } 578 579 // Do a cheap assert that the defines object hasn't changed 580 if (a.defines != nil || b.defines != nil) && (a.defines == nil || b.defines == nil || 581 len(a.defines.IdentifierDefines) != len(b.defines.IdentifierDefines) || 582 len(a.defines.DotDefines) != len(b.defines.DotDefines)) { 583 panic("Internal error") 584 } 585 586 return true 587 } 588 589 func isSameRegexp(a *regexp.Regexp, b *regexp.Regexp) bool { 590 if a == nil { 591 return b == nil 592 } else { 593 return b != nil && a.String() == b.String() 594 } 595 } 596 597 func jsxExprsEqual(a config.DefineExpr, b config.DefineExpr) bool { 598 if !helpers.StringArraysEqual(a.Parts, b.Parts) { 599 return false 600 } 601 602 if a.Constant != nil { 603 if b.Constant == nil || !js_ast.ValuesLookTheSame(a.Constant, b.Constant) { 604 return false 605 } 606 } else if b.Constant != nil { 607 return false 608 } 609 610 return true 611 } 612 613 type tempRef struct { 614 valueOrNil js_ast.Expr 615 ref ast.Ref 616 } 617 618 const ( 619 locModuleScope = -1 620 ) 621 622 type scopeOrder struct { 623 scope *js_ast.Scope 624 loc logger.Loc 625 } 626 627 type awaitOrYield uint8 628 629 const ( 630 // The keyword is used as an identifier, not a special expression 631 allowIdent awaitOrYield = iota 632 633 // Declaring the identifier is forbidden, and the keyword is used as a special expression 634 allowExpr 635 636 // Declaring the identifier is forbidden, and using the identifier is also forbidden 637 forbidAll 638 ) 639 640 // This is function-specific information used during parsing. It is saved and 641 // restored on the call stack around code that parses nested functions and 642 // arrow expressions. 643 type fnOrArrowDataParse struct { 644 arrowArgErrors *deferredArrowArgErrors 645 decoratorScope *js_ast.Scope 646 asyncRange logger.Range 647 needsAsyncLoc logger.Loc 648 await awaitOrYield 649 yield awaitOrYield 650 allowSuperCall bool 651 allowSuperProperty bool 652 isTopLevel bool 653 isConstructor bool 654 isTypeScriptDeclare bool 655 isThisDisallowed bool 656 isReturnDisallowed bool 657 658 // In TypeScript, forward declarations of functions have no bodies 659 allowMissingBodyForTypeScript bool 660 } 661 662 // This is function-specific information used during visiting. It is saved and 663 // restored on the call stack around code that parses nested functions and 664 // arrow expressions. 665 type fnOrArrowDataVisit struct { 666 // This is used to silence unresolvable imports due to "require" calls inside 667 // a try/catch statement. The assumption is that the try/catch statement is 668 // there to handle the case where the reference to "require" crashes. 669 tryBodyCount int32 670 tryCatchLoc logger.Loc 671 672 isArrow bool 673 isAsync bool 674 isGenerator bool 675 isInsideLoop bool 676 isInsideSwitch bool 677 isDerivedClassCtor bool 678 isOutsideFnOrArrow bool 679 shouldLowerSuperPropertyAccess bool 680 } 681 682 // This is function-specific information used during visiting. It is saved and 683 // restored on the call stack around code that parses nested functions (but not 684 // nested arrow functions). 685 type fnOnlyDataVisit struct { 686 // This is a reference to the magic "arguments" variable that exists inside 687 // functions in JavaScript. It will be non-nil inside functions and nil 688 // otherwise. 689 argumentsRef *ast.Ref 690 691 // Arrow functions don't capture the value of "this" and "arguments". Instead, 692 // the values are inherited from the surrounding context. If arrow functions 693 // are turned into regular functions due to lowering, we will need to generate 694 // local variables to capture these values so they are preserved correctly. 695 thisCaptureRef *ast.Ref 696 argumentsCaptureRef *ast.Ref 697 698 // If true, we're inside a static class context where "this" expressions 699 // should be replaced with the class name. 700 shouldReplaceThisWithInnerClassNameRef bool 701 702 // This is true if "this" is equal to the class name. It's true if we're in a 703 // static class field initializer, a static class method, or a static class 704 // block. 705 isInStaticClassContext bool 706 707 // This is a reference to the enclosing class name if there is one. It's used 708 // to implement "this" and "super" references. A name is automatically generated 709 // if one is missing so this will always be present inside a class body. 710 innerClassNameRef *ast.Ref 711 712 // If we're inside an async arrow function and async functions are not 713 // supported, then we will have to convert that arrow function to a generator 714 // function. That means references to "arguments" inside the arrow function 715 // will have to reference a captured variable instead of the real variable. 716 isInsideAsyncArrowFn bool 717 718 // If false, disallow "new.target" expressions. We disallow all "new.target" 719 // expressions at the top-level of the file (i.e. not inside a function or 720 // a class field). Technically since CommonJS files are wrapped in a function 721 // you can use "new.target" in node as an alias for "undefined" but we don't 722 // support that. 723 isNewTargetAllowed bool 724 725 // If false, the value for "this" is the top-level module scope "this" value. 726 // That means it's "undefined" for ECMAScript modules and "exports" for 727 // CommonJS modules. We track this information so that we can substitute the 728 // correct value for these top-level "this" references at compile time instead 729 // of passing the "this" expression through to the output and leaving the 730 // interpretation up to the run-time behavior of the generated code. 731 // 732 // If true, the value for "this" is nested inside something (either a function 733 // or a class declaration). That means the top-level module scope "this" value 734 // has been shadowed and is now inaccessible. 735 isThisNested bool 736 737 // Do not warn about "this" being undefined for code that the TypeScript 738 // compiler generates that looks like this: 739 // 740 // var __rest = (this && this.__rest) || function (s, e) { 741 // ... 742 // }; 743 // 744 silenceMessageAboutThisBeingUndefined bool 745 } 746 747 const bloomFilterSize = 251 748 749 type duplicateCaseValue struct { 750 value js_ast.Expr 751 hash uint32 752 } 753 754 type duplicateCaseChecker struct { 755 cases []duplicateCaseValue 756 bloomFilter [(bloomFilterSize + 7) / 8]byte 757 } 758 759 func (dc *duplicateCaseChecker) reset() { 760 // Preserve capacity 761 dc.cases = dc.cases[:0] 762 763 // This should be optimized by the compiler. See this for more information: 764 // https://github.com/golang/go/issues/5373 765 bytes := dc.bloomFilter 766 for i := range bytes { 767 bytes[i] = 0 768 } 769 } 770 771 func (dc *duplicateCaseChecker) check(p *parser, expr js_ast.Expr) { 772 if hash, ok := duplicateCaseHash(expr); ok { 773 bucket := hash % bloomFilterSize 774 entry := &dc.bloomFilter[bucket/8] 775 mask := byte(1) << (bucket % 8) 776 777 // Check for collisions 778 if (*entry & mask) != 0 { 779 for _, c := range dc.cases { 780 if c.hash == hash { 781 if equals, couldBeIncorrect := duplicateCaseEquals(c.value, expr); equals { 782 var laterRange logger.Range 783 var earlierRange logger.Range 784 if _, ok := expr.Data.(*js_ast.EString); ok { 785 laterRange = p.source.RangeOfString(expr.Loc) 786 } else { 787 laterRange = p.source.RangeOfOperatorBefore(expr.Loc, "case") 788 } 789 if _, ok := c.value.Data.(*js_ast.EString); ok { 790 earlierRange = p.source.RangeOfString(c.value.Loc) 791 } else { 792 earlierRange = p.source.RangeOfOperatorBefore(c.value.Loc, "case") 793 } 794 text := "This case clause will never be evaluated because it duplicates an earlier case clause" 795 if couldBeIncorrect { 796 text = "This case clause may never be evaluated because it likely duplicates an earlier case clause" 797 } 798 kind := logger.Warning 799 if p.suppressWarningsAboutWeirdCode { 800 kind = logger.Debug 801 } 802 p.log.AddIDWithNotes(logger.MsgID_JS_DuplicateCase, kind, &p.tracker, laterRange, text, 803 []logger.MsgData{p.tracker.MsgData(earlierRange, "The earlier case clause is here:")}) 804 } 805 return 806 } 807 } 808 } 809 810 *entry |= mask 811 dc.cases = append(dc.cases, duplicateCaseValue{hash: hash, value: expr}) 812 } 813 } 814 815 func duplicateCaseHash(expr js_ast.Expr) (uint32, bool) { 816 switch e := expr.Data.(type) { 817 case *js_ast.EInlinedEnum: 818 return duplicateCaseHash(e.Value) 819 820 case *js_ast.ENull: 821 return 0, true 822 823 case *js_ast.EUndefined: 824 return 1, true 825 826 case *js_ast.EBoolean: 827 if e.Value { 828 return helpers.HashCombine(2, 1), true 829 } 830 return helpers.HashCombine(2, 0), true 831 832 case *js_ast.ENumber: 833 bits := math.Float64bits(e.Value) 834 return helpers.HashCombine(helpers.HashCombine(3, uint32(bits)), uint32(bits>>32)), true 835 836 case *js_ast.EString: 837 hash := uint32(4) 838 for _, c := range e.Value { 839 hash = helpers.HashCombine(hash, uint32(c)) 840 } 841 return hash, true 842 843 case *js_ast.EBigInt: 844 hash := uint32(5) 845 for _, c := range e.Value { 846 hash = helpers.HashCombine(hash, uint32(c)) 847 } 848 return hash, true 849 850 case *js_ast.EIdentifier: 851 return helpers.HashCombine(6, e.Ref.InnerIndex), true 852 853 case *js_ast.EDot: 854 if target, ok := duplicateCaseHash(e.Target); ok { 855 return helpers.HashCombineString(helpers.HashCombine(7, target), e.Name), true 856 } 857 858 case *js_ast.EIndex: 859 if target, ok := duplicateCaseHash(e.Target); ok { 860 if index, ok := duplicateCaseHash(e.Index); ok { 861 return helpers.HashCombine(helpers.HashCombine(8, target), index), true 862 } 863 } 864 } 865 866 return 0, false 867 } 868 869 func duplicateCaseEquals(left js_ast.Expr, right js_ast.Expr) (equals bool, couldBeIncorrect bool) { 870 if b, ok := right.Data.(*js_ast.EInlinedEnum); ok { 871 return duplicateCaseEquals(left, b.Value) 872 } 873 874 switch a := left.Data.(type) { 875 case *js_ast.EInlinedEnum: 876 return duplicateCaseEquals(a.Value, right) 877 878 case *js_ast.ENull: 879 _, ok := right.Data.(*js_ast.ENull) 880 return ok, false 881 882 case *js_ast.EUndefined: 883 _, ok := right.Data.(*js_ast.EUndefined) 884 return ok, false 885 886 case *js_ast.EBoolean: 887 b, ok := right.Data.(*js_ast.EBoolean) 888 return ok && a.Value == b.Value, false 889 890 case *js_ast.ENumber: 891 b, ok := right.Data.(*js_ast.ENumber) 892 return ok && a.Value == b.Value, false 893 894 case *js_ast.EString: 895 b, ok := right.Data.(*js_ast.EString) 896 return ok && helpers.UTF16EqualsUTF16(a.Value, b.Value), false 897 898 case *js_ast.EBigInt: 899 if b, ok := right.Data.(*js_ast.EBigInt); ok { 900 equal, ok := js_ast.CheckEqualityBigInt(a.Value, b.Value) 901 return ok && equal, false 902 } 903 904 case *js_ast.EIdentifier: 905 b, ok := right.Data.(*js_ast.EIdentifier) 906 return ok && a.Ref == b.Ref, false 907 908 case *js_ast.EDot: 909 if b, ok := right.Data.(*js_ast.EDot); ok && a.OptionalChain == b.OptionalChain && a.Name == b.Name { 910 equals, _ := duplicateCaseEquals(a.Target, b.Target) 911 return equals, true 912 } 913 914 case *js_ast.EIndex: 915 if b, ok := right.Data.(*js_ast.EIndex); ok && a.OptionalChain == b.OptionalChain { 916 if equals, _ := duplicateCaseEquals(a.Index, b.Index); equals { 917 equals, _ := duplicateCaseEquals(a.Target, b.Target) 918 return equals, true 919 } 920 } 921 } 922 923 return false, false 924 } 925 926 type duplicatePropertiesIn uint8 927 928 const ( 929 duplicatePropertiesInObject duplicatePropertiesIn = iota 930 duplicatePropertiesInClass 931 ) 932 933 func (p *parser) warnAboutDuplicateProperties(properties []js_ast.Property, in duplicatePropertiesIn) { 934 if len(properties) < 2 { 935 return 936 } 937 938 type keyKind uint8 939 type existingKey struct { 940 loc logger.Loc 941 kind keyKind 942 } 943 const ( 944 keyMissing keyKind = iota 945 keyNormal 946 keyGet 947 keySet 948 keyGetAndSet 949 ) 950 instanceKeys := make(map[string]existingKey) 951 staticKeys := make(map[string]existingKey) 952 953 for _, property := range properties { 954 if property.Kind != js_ast.PropertySpread { 955 if str, ok := property.Key.Data.(*js_ast.EString); ok { 956 var keys map[string]existingKey 957 if property.Flags.Has(js_ast.PropertyIsStatic) { 958 keys = staticKeys 959 } else { 960 keys = instanceKeys 961 } 962 key := helpers.UTF16ToString(str.Value) 963 prevKey := keys[key] 964 nextKey := existingKey{kind: keyNormal, loc: property.Key.Loc} 965 966 if property.Kind == js_ast.PropertyGetter { 967 nextKey.kind = keyGet 968 } else if property.Kind == js_ast.PropertySetter { 969 nextKey.kind = keySet 970 } 971 972 if prevKey.kind != keyMissing && (in != duplicatePropertiesInObject || key != "__proto__") && (in != duplicatePropertiesInClass || key != "constructor") { 973 if (prevKey.kind == keyGet && nextKey.kind == keySet) || (prevKey.kind == keySet && nextKey.kind == keyGet) { 974 nextKey.kind = keyGetAndSet 975 } else { 976 var id logger.MsgID 977 var what string 978 var where string 979 switch in { 980 case duplicatePropertiesInObject: 981 id = logger.MsgID_JS_DuplicateObjectKey 982 what = "key" 983 where = "object literal" 984 case duplicatePropertiesInClass: 985 id = logger.MsgID_JS_DuplicateClassMember 986 what = "member" 987 where = "class body" 988 } 989 r := js_lexer.RangeOfIdentifier(p.source, property.Key.Loc) 990 p.log.AddIDWithNotes(id, logger.Warning, &p.tracker, r, 991 fmt.Sprintf("Duplicate %s %q in %s", what, key, where), 992 []logger.MsgData{p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, prevKey.loc), 993 fmt.Sprintf("The original %s %q is here:", what, key))}) 994 } 995 } 996 997 keys[key] = nextKey 998 } 999 } 1000 } 1001 } 1002 1003 func isJumpStatement(data js_ast.S) bool { 1004 switch data.(type) { 1005 case *js_ast.SBreak, *js_ast.SContinue, *js_ast.SReturn, *js_ast.SThrow: 1006 return true 1007 } 1008 1009 return false 1010 } 1011 1012 func jumpStmtsLookTheSame(left js_ast.S, right js_ast.S) bool { 1013 switch a := left.(type) { 1014 case *js_ast.SBreak: 1015 b, ok := right.(*js_ast.SBreak) 1016 return ok && (a.Label == nil) == (b.Label == nil) && (a.Label == nil || a.Label.Ref == b.Label.Ref) 1017 1018 case *js_ast.SContinue: 1019 b, ok := right.(*js_ast.SContinue) 1020 return ok && (a.Label == nil) == (b.Label == nil) && (a.Label == nil || a.Label.Ref == b.Label.Ref) 1021 1022 case *js_ast.SReturn: 1023 b, ok := right.(*js_ast.SReturn) 1024 return ok && (a.ValueOrNil.Data == nil) == (b.ValueOrNil.Data == nil) && 1025 (a.ValueOrNil.Data == nil || js_ast.ValuesLookTheSame(a.ValueOrNil.Data, b.ValueOrNil.Data)) 1026 1027 case *js_ast.SThrow: 1028 b, ok := right.(*js_ast.SThrow) 1029 return ok && js_ast.ValuesLookTheSame(a.Value.Data, b.Value.Data) 1030 } 1031 1032 return false 1033 } 1034 1035 func (p *parser) selectLocalKind(kind js_ast.LocalKind) js_ast.LocalKind { 1036 // Use "var" instead of "let" and "const" if the variable declaration may 1037 // need to be separated from the initializer. This allows us to safely move 1038 // this declaration into a nested scope. 1039 if p.currentScope.Parent == nil && (kind == js_ast.LocalLet || kind == js_ast.LocalConst) && 1040 (p.options.mode == config.ModeBundle || p.willWrapModuleInTryCatchForUsing) { 1041 return js_ast.LocalVar 1042 } 1043 1044 // Optimization: use "let" instead of "const" because it's shorter. This is 1045 // only done when bundling because assigning to "const" is only an error when 1046 // bundling. 1047 if p.options.mode == config.ModeBundle && kind == js_ast.LocalConst && p.options.minifySyntax { 1048 return js_ast.LocalLet 1049 } 1050 1051 return kind 1052 } 1053 1054 func (p *parser) pushScopeForParsePass(kind js_ast.ScopeKind, loc logger.Loc) int { 1055 parent := p.currentScope 1056 scope := &js_ast.Scope{ 1057 Kind: kind, 1058 Parent: parent, 1059 Members: make(map[string]js_ast.ScopeMember), 1060 Label: ast.LocRef{Ref: ast.InvalidRef}, 1061 } 1062 if parent != nil { 1063 parent.Children = append(parent.Children, scope) 1064 scope.StrictMode = parent.StrictMode 1065 scope.UseStrictLoc = parent.UseStrictLoc 1066 } 1067 p.currentScope = scope 1068 1069 // Enforce that scope locations are strictly increasing to help catch bugs 1070 // where the pushed scopes are mismatched between the first and second passes 1071 if len(p.scopesInOrder) > 0 { 1072 prevStart := p.scopesInOrder[len(p.scopesInOrder)-1].loc.Start 1073 if prevStart >= loc.Start { 1074 panic(fmt.Sprintf("Scope location %d must be greater than %d", loc.Start, prevStart)) 1075 } 1076 } 1077 1078 // Copy down function arguments into the function body scope. That way we get 1079 // errors if a statement in the function body tries to re-declare any of the 1080 // arguments. 1081 if kind == js_ast.ScopeFunctionBody { 1082 if scope.Parent.Kind != js_ast.ScopeFunctionArgs { 1083 panic("Internal error") 1084 } 1085 for name, member := range scope.Parent.Members { 1086 // Don't copy down the optional function expression name. Re-declaring 1087 // the name of a function expression is allowed. 1088 kind := p.symbols[member.Ref.InnerIndex].Kind 1089 if kind != ast.SymbolHoistedFunction { 1090 scope.Members[name] = member 1091 } 1092 } 1093 } 1094 1095 // Remember the length in case we call popAndDiscardScope() later 1096 scopeIndex := len(p.scopesInOrder) 1097 p.scopesInOrder = append(p.scopesInOrder, scopeOrder{loc: loc, scope: scope}) 1098 return scopeIndex 1099 } 1100 1101 func (p *parser) popScope() { 1102 // We cannot rename anything inside a scope containing a direct eval() call 1103 if p.currentScope.ContainsDirectEval { 1104 for _, member := range p.currentScope.Members { 1105 // Using direct eval when bundling is not a good idea in general because 1106 // esbuild must assume that it can potentially reach anything in any of 1107 // the containing scopes. We try to make it work but this isn't possible 1108 // in some cases. 1109 // 1110 // For example, symbols imported using an ESM import are a live binding 1111 // to the underlying symbol in another file. This is emulated during 1112 // scope hoisting by erasing the ESM import and just referencing the 1113 // underlying symbol in the flattened bundle directly. However, that 1114 // symbol may have a different name which could break uses of direct 1115 // eval: 1116 // 1117 // // Before bundling 1118 // import { foo as bar } from './foo.js' 1119 // console.log(eval('bar')) 1120 // 1121 // // After bundling 1122 // let foo = 123 // The contents of "foo.js" 1123 // console.log(eval('bar')) 1124 // 1125 // There really isn't any way to fix this. You can't just rename "foo" to 1126 // "bar" in the example above because there may be a third bundled file 1127 // that also contains direct eval and imports the same symbol with a 1128 // different conflicting import alias. And there is no way to store a 1129 // live binding to the underlying symbol in a variable with the import's 1130 // name so that direct eval can access it: 1131 // 1132 // // After bundling 1133 // let foo = 123 // The contents of "foo.js" 1134 // const bar = /* cannot express a live binding to "foo" here */ 1135 // console.log(eval('bar')) 1136 // 1137 // Technically a "with" statement could potentially make this work (with 1138 // a big hit to performance), but they are deprecated and are unavailable 1139 // in strict mode. This is a non-starter since all ESM code is strict mode. 1140 // 1141 // So while we still try to obey the requirement that all symbol names are 1142 // pinned when direct eval is present, we make an exception for top-level 1143 // symbols in an ESM file when bundling is enabled. We make no guarantee 1144 // that "eval" will be able to reach these symbols and we allow them to be 1145 // renamed or removed by tree shaking. 1146 if p.options.mode == config.ModeBundle && p.currentScope.Parent == nil && p.isFileConsideredESM { 1147 continue 1148 } 1149 1150 p.symbols[member.Ref.InnerIndex].Flags |= ast.MustNotBeRenamed 1151 } 1152 } 1153 1154 p.currentScope = p.currentScope.Parent 1155 } 1156 1157 func (p *parser) popAndDiscardScope(scopeIndex int) { 1158 // Unwind any newly-added scopes in reverse order 1159 for i := len(p.scopesInOrder) - 1; i >= scopeIndex; i-- { 1160 scope := p.scopesInOrder[i].scope 1161 parent := scope.Parent 1162 last := len(parent.Children) - 1 1163 if parent.Children[last] != scope { 1164 panic("Internal error") 1165 } 1166 parent.Children = parent.Children[:last] 1167 } 1168 1169 // Move up to the parent scope 1170 p.currentScope = p.currentScope.Parent 1171 1172 // Truncate the scope order where we started to pretend we never saw this scope 1173 p.scopesInOrder = p.scopesInOrder[:scopeIndex] 1174 } 1175 1176 func (p *parser) popAndFlattenScope(scopeIndex int) { 1177 // Move up to the parent scope 1178 toFlatten := p.currentScope 1179 parent := toFlatten.Parent 1180 p.currentScope = parent 1181 1182 // Erase this scope from the order. This will shift over the indices of all 1183 // the scopes that were created after us. However, we shouldn't have to 1184 // worry about other code with outstanding scope indices for these scopes. 1185 // These scopes were all created in between this scope's push and pop 1186 // operations, so they should all be child scopes and should all be popped 1187 // by the time we get here. 1188 copy(p.scopesInOrder[scopeIndex:], p.scopesInOrder[scopeIndex+1:]) 1189 p.scopesInOrder = p.scopesInOrder[:len(p.scopesInOrder)-1] 1190 1191 // Remove the last child from the parent scope 1192 last := len(parent.Children) - 1 1193 if parent.Children[last] != toFlatten { 1194 panic("Internal error") 1195 } 1196 parent.Children = parent.Children[:last] 1197 1198 // Reparent our child scopes into our parent 1199 for _, scope := range toFlatten.Children { 1200 scope.Parent = parent 1201 parent.Children = append(parent.Children, scope) 1202 } 1203 } 1204 1205 // Undo all scopes pushed and popped after this scope index. This assumes that 1206 // the scope stack is at the same level now as it was at the given scope index. 1207 func (p *parser) discardScopesUpTo(scopeIndex int) { 1208 // Remove any direct children from their parent 1209 children := p.currentScope.Children 1210 for _, child := range p.scopesInOrder[scopeIndex:] { 1211 if child.scope.Parent == p.currentScope { 1212 for i := len(children) - 1; i >= 0; i-- { 1213 if children[i] == child.scope { 1214 children = append(children[:i], children[i+1:]...) 1215 break 1216 } 1217 } 1218 } 1219 } 1220 p.currentScope.Children = children 1221 1222 // Truncate the scope order where we started to pretend we never saw this scope 1223 p.scopesInOrder = p.scopesInOrder[:scopeIndex] 1224 } 1225 1226 func (p *parser) newSymbol(kind ast.SymbolKind, name string) ast.Ref { 1227 ref := ast.Ref{SourceIndex: p.source.Index, InnerIndex: uint32(len(p.symbols))} 1228 p.symbols = append(p.symbols, ast.Symbol{ 1229 Kind: kind, 1230 OriginalName: name, 1231 Link: ast.InvalidRef, 1232 }) 1233 if p.options.ts.Parse { 1234 p.tsUseCounts = append(p.tsUseCounts, 0) 1235 } 1236 return ref 1237 } 1238 1239 // This is similar to "ast.MergeSymbols" but it works with this parser's 1240 // one-level symbol map instead of the linker's two-level symbol map. It also 1241 // doesn't handle cycles since they shouldn't come up due to the way this 1242 // function is used. 1243 func (p *parser) mergeSymbols(old ast.Ref, new ast.Ref) ast.Ref { 1244 if old == new { 1245 return new 1246 } 1247 1248 oldSymbol := &p.symbols[old.InnerIndex] 1249 if oldSymbol.Link != ast.InvalidRef { 1250 oldSymbol.Link = p.mergeSymbols(oldSymbol.Link, new) 1251 return oldSymbol.Link 1252 } 1253 1254 newSymbol := &p.symbols[new.InnerIndex] 1255 if newSymbol.Link != ast.InvalidRef { 1256 newSymbol.Link = p.mergeSymbols(old, newSymbol.Link) 1257 return newSymbol.Link 1258 } 1259 1260 oldSymbol.Link = new 1261 newSymbol.MergeContentsWith(oldSymbol) 1262 return new 1263 } 1264 1265 type mergeResult int 1266 1267 const ( 1268 mergeForbidden = iota 1269 mergeReplaceWithNew 1270 mergeOverwriteWithNew 1271 mergeKeepExisting 1272 mergeBecomePrivateGetSetPair 1273 mergeBecomePrivateStaticGetSetPair 1274 ) 1275 1276 func (p *parser) canMergeSymbols(scope *js_ast.Scope, existing ast.SymbolKind, new ast.SymbolKind) mergeResult { 1277 if existing == ast.SymbolUnbound { 1278 return mergeReplaceWithNew 1279 } 1280 1281 // In TypeScript, imports are allowed to silently collide with symbols within 1282 // the module. Presumably this is because the imports may be type-only: 1283 // 1284 // import {Foo} from 'bar' 1285 // class Foo {} 1286 // 1287 if p.options.ts.Parse && existing == ast.SymbolImport { 1288 return mergeReplaceWithNew 1289 } 1290 1291 // "enum Foo {} enum Foo {}" 1292 if new == ast.SymbolTSEnum && existing == ast.SymbolTSEnum { 1293 return mergeKeepExisting 1294 } 1295 1296 // "namespace Foo { ... } enum Foo {}" 1297 if new == ast.SymbolTSEnum && existing == ast.SymbolTSNamespace { 1298 return mergeReplaceWithNew 1299 } 1300 1301 // "namespace Foo { ... } namespace Foo { ... }" 1302 // "function Foo() {} namespace Foo { ... }" 1303 // "enum Foo {} namespace Foo { ... }" 1304 if new == ast.SymbolTSNamespace { 1305 switch existing { 1306 case ast.SymbolTSNamespace, ast.SymbolHoistedFunction, ast.SymbolGeneratorOrAsyncFunction, ast.SymbolTSEnum, ast.SymbolClass: 1307 return mergeKeepExisting 1308 } 1309 } 1310 1311 // "var foo; var foo;" 1312 // "var foo; function foo() {}" 1313 // "function foo() {} var foo;" 1314 // "function *foo() {} function *foo() {}" but not "{ function *foo() {} function *foo() {} }" 1315 if new.IsHoistedOrFunction() && existing.IsHoistedOrFunction() && 1316 (scope.Kind == js_ast.ScopeEntry || 1317 scope.Kind == js_ast.ScopeFunctionBody || 1318 scope.Kind == js_ast.ScopeFunctionArgs || 1319 (new == existing && new.IsHoisted())) { 1320 return mergeReplaceWithNew 1321 } 1322 1323 // "get #foo() {} set #foo() {}" 1324 // "set #foo() {} get #foo() {}" 1325 if (existing == ast.SymbolPrivateGet && new == ast.SymbolPrivateSet) || 1326 (existing == ast.SymbolPrivateSet && new == ast.SymbolPrivateGet) { 1327 return mergeBecomePrivateGetSetPair 1328 } 1329 if (existing == ast.SymbolPrivateStaticGet && new == ast.SymbolPrivateStaticSet) || 1330 (existing == ast.SymbolPrivateStaticSet && new == ast.SymbolPrivateStaticGet) { 1331 return mergeBecomePrivateStaticGetSetPair 1332 } 1333 1334 // "try {} catch (e) { var e }" 1335 if existing == ast.SymbolCatchIdentifier && new == ast.SymbolHoisted { 1336 return mergeReplaceWithNew 1337 } 1338 1339 // "function() { var arguments }" 1340 if existing == ast.SymbolArguments && new == ast.SymbolHoisted { 1341 return mergeKeepExisting 1342 } 1343 1344 // "function() { let arguments }" 1345 if existing == ast.SymbolArguments && new != ast.SymbolHoisted { 1346 return mergeOverwriteWithNew 1347 } 1348 1349 return mergeForbidden 1350 } 1351 1352 func (p *parser) addSymbolAlreadyDeclaredError(name string, newLoc logger.Loc, oldLoc logger.Loc) { 1353 p.log.AddErrorWithNotes(&p.tracker, 1354 js_lexer.RangeOfIdentifier(p.source, newLoc), 1355 fmt.Sprintf("The symbol %q has already been declared", name), 1356 1357 []logger.MsgData{p.tracker.MsgData( 1358 js_lexer.RangeOfIdentifier(p.source, oldLoc), 1359 fmt.Sprintf("The symbol %q was originally declared here:", name), 1360 )}, 1361 ) 1362 } 1363 1364 func (p *parser) declareSymbol(kind ast.SymbolKind, loc logger.Loc, name string) ast.Ref { 1365 p.checkForUnrepresentableIdentifier(loc, name) 1366 1367 // Allocate a new symbol 1368 ref := p.newSymbol(kind, name) 1369 1370 // Check for a collision in the declaring scope 1371 if existing, ok := p.currentScope.Members[name]; ok { 1372 symbol := &p.symbols[existing.Ref.InnerIndex] 1373 1374 switch p.canMergeSymbols(p.currentScope, symbol.Kind, kind) { 1375 case mergeForbidden: 1376 p.addSymbolAlreadyDeclaredError(name, loc, existing.Loc) 1377 return existing.Ref 1378 1379 case mergeKeepExisting: 1380 ref = existing.Ref 1381 1382 case mergeReplaceWithNew: 1383 symbol.Link = ref 1384 p.currentScope.Replaced = append(p.currentScope.Replaced, existing) 1385 1386 // If these are both functions, remove the overwritten declaration 1387 if p.options.minifySyntax && kind.IsFunction() && symbol.Kind.IsFunction() { 1388 symbol.Flags |= ast.RemoveOverwrittenFunctionDeclaration 1389 } 1390 1391 case mergeBecomePrivateGetSetPair: 1392 ref = existing.Ref 1393 symbol.Kind = ast.SymbolPrivateGetSetPair 1394 1395 case mergeBecomePrivateStaticGetSetPair: 1396 ref = existing.Ref 1397 symbol.Kind = ast.SymbolPrivateStaticGetSetPair 1398 1399 case mergeOverwriteWithNew: 1400 } 1401 } 1402 1403 // Overwrite this name in the declaring scope 1404 p.currentScope.Members[name] = js_ast.ScopeMember{Ref: ref, Loc: loc} 1405 return ref 1406 1407 } 1408 1409 // This type is just so we can use Go's native sort function 1410 type scopeMemberArray []js_ast.ScopeMember 1411 1412 func (a scopeMemberArray) Len() int { return len(a) } 1413 func (a scopeMemberArray) Swap(i int, j int) { a[i], a[j] = a[j], a[i] } 1414 1415 func (a scopeMemberArray) Less(i int, j int) bool { 1416 ai := a[i].Ref 1417 bj := a[j].Ref 1418 return ai.InnerIndex < bj.InnerIndex || (ai.InnerIndex == bj.InnerIndex && ai.SourceIndex < bj.SourceIndex) 1419 } 1420 1421 func (p *parser) hoistSymbols(scope *js_ast.Scope) { 1422 // Duplicate function declarations are forbidden in nested blocks in strict 1423 // mode. Separately, they are also forbidden at the top-level of modules. 1424 // This check needs to be delayed until now instead of being done when the 1425 // functions are declared because we potentially need to scan the whole file 1426 // to know if the file is considered to be in strict mode (or is considered 1427 // to be a module). We might only encounter an "export {}" clause at the end 1428 // of the file. 1429 if (scope.StrictMode != js_ast.SloppyMode && scope.Kind == js_ast.ScopeBlock) || (scope.Parent == nil && p.isFileConsideredESM) { 1430 for _, replaced := range scope.Replaced { 1431 symbol := &p.symbols[replaced.Ref.InnerIndex] 1432 if symbol.Kind.IsFunction() { 1433 if member, ok := scope.Members[symbol.OriginalName]; ok && p.symbols[member.Ref.InnerIndex].Kind.IsFunction() { 1434 var notes []logger.MsgData 1435 if scope.Parent == nil && p.isFileConsideredESM { 1436 _, notes = p.whyESModule() 1437 notes[0].Text = fmt.Sprintf("Duplicate top-level function declarations are not allowed in an ECMAScript module. %s", notes[0].Text) 1438 } else { 1439 var where string 1440 where, notes = p.whyStrictMode(scope) 1441 notes[0].Text = fmt.Sprintf("Duplicate function declarations are not allowed in nested blocks %s. %s", where, notes[0].Text) 1442 } 1443 1444 p.log.AddErrorWithNotes(&p.tracker, 1445 js_lexer.RangeOfIdentifier(p.source, member.Loc), 1446 fmt.Sprintf("The symbol %q has already been declared", symbol.OriginalName), 1447 1448 append([]logger.MsgData{p.tracker.MsgData( 1449 js_lexer.RangeOfIdentifier(p.source, replaced.Loc), 1450 fmt.Sprintf("The symbol %q was originally declared here:", symbol.OriginalName), 1451 )}, notes...), 1452 ) 1453 } 1454 } 1455 } 1456 } 1457 1458 if !scope.Kind.StopsHoisting() { 1459 // We create new symbols in the loop below, so the iteration order of the 1460 // loop must be deterministic to avoid generating different minified names 1461 sortedMembers := make(scopeMemberArray, 0, len(scope.Members)) 1462 for _, member := range scope.Members { 1463 sortedMembers = append(sortedMembers, member) 1464 } 1465 sort.Sort(sortedMembers) 1466 1467 nextMember: 1468 for _, member := range sortedMembers { 1469 symbol := &p.symbols[member.Ref.InnerIndex] 1470 1471 // Handle non-hoisted collisions between catch bindings and the catch body. 1472 // This implements "B.3.4 VariableStatements in Catch Blocks" from Annex B 1473 // of the ECMAScript standard version 6+ (except for the hoisted case, which 1474 // is handled later on below): 1475 // 1476 // * It is a Syntax Error if any element of the BoundNames of CatchParameter 1477 // also occurs in the LexicallyDeclaredNames of Block. 1478 // 1479 // * It is a Syntax Error if any element of the BoundNames of CatchParameter 1480 // also occurs in the VarDeclaredNames of Block unless CatchParameter is 1481 // CatchParameter : BindingIdentifier . 1482 // 1483 if scope.Parent.Kind == js_ast.ScopeCatchBinding && symbol.Kind != ast.SymbolHoisted { 1484 if existingMember, ok := scope.Parent.Members[symbol.OriginalName]; ok { 1485 p.addSymbolAlreadyDeclaredError(symbol.OriginalName, member.Loc, existingMember.Loc) 1486 continue 1487 } 1488 } 1489 1490 if !symbol.Kind.IsHoisted() { 1491 continue 1492 } 1493 1494 // Implement "Block-Level Function Declarations Web Legacy Compatibility 1495 // Semantics" from Annex B of the ECMAScript standard version 6+ 1496 isSloppyModeBlockLevelFnStmt := false 1497 originalMemberRef := member.Ref 1498 if symbol.Kind == ast.SymbolHoistedFunction { 1499 // Block-level function declarations behave like "let" in strict mode 1500 if scope.StrictMode != js_ast.SloppyMode { 1501 continue 1502 } 1503 1504 // In sloppy mode, block level functions behave like "let" except with 1505 // an assignment to "var", sort of. This code: 1506 // 1507 // if (x) { 1508 // f(); 1509 // function f() {} 1510 // } 1511 // f(); 1512 // 1513 // behaves like this code: 1514 // 1515 // if (x) { 1516 // let f2 = function() {} 1517 // var f = f2; 1518 // f2(); 1519 // } 1520 // f(); 1521 // 1522 hoistedRef := p.newSymbol(ast.SymbolHoisted, symbol.OriginalName) 1523 scope.Generated = append(scope.Generated, hoistedRef) 1524 if p.hoistedRefForSloppyModeBlockFn == nil { 1525 p.hoistedRefForSloppyModeBlockFn = make(map[ast.Ref]ast.Ref) 1526 } 1527 p.hoistedRefForSloppyModeBlockFn[member.Ref] = hoistedRef 1528 symbol = &p.symbols[hoistedRef.InnerIndex] 1529 member.Ref = hoistedRef 1530 isSloppyModeBlockLevelFnStmt = true 1531 } 1532 1533 // Check for collisions that would prevent to hoisting "var" symbols up to the enclosing function scope 1534 s := scope.Parent 1535 for { 1536 // Variable declarations hoisted past a "with" statement may actually end 1537 // up overwriting a property on the target of the "with" statement instead 1538 // of initializing the variable. We must not rename them or we risk 1539 // causing a behavior change. 1540 // 1541 // var obj = { foo: 1 } 1542 // with (obj) { var foo = 2 } 1543 // assert(foo === undefined) 1544 // assert(obj.foo === 2) 1545 // 1546 if s.Kind == js_ast.ScopeWith { 1547 symbol.Flags |= ast.MustNotBeRenamed 1548 } 1549 1550 if existingMember, ok := s.Members[symbol.OriginalName]; ok { 1551 existingSymbol := &p.symbols[existingMember.Ref.InnerIndex] 1552 1553 // We can hoist the symbol from the child scope into the symbol in 1554 // this scope if: 1555 // 1556 // - The symbol is unbound (i.e. a global variable access) 1557 // - The symbol is also another hoisted variable 1558 // - The symbol is a function of any kind and we're in a function or module scope 1559 // 1560 // Is this unbound (i.e. a global access) or also hoisted? 1561 if existingSymbol.Kind == ast.SymbolUnbound || existingSymbol.Kind == ast.SymbolHoisted || 1562 (existingSymbol.Kind.IsFunction() && (s.Kind == js_ast.ScopeEntry || s.Kind == js_ast.ScopeFunctionBody)) { 1563 // Silently merge this symbol into the existing symbol 1564 symbol.Link = existingMember.Ref 1565 s.Members[symbol.OriginalName] = existingMember 1566 continue nextMember 1567 } 1568 1569 // Otherwise if this isn't a catch identifier or "arguments", it's a collision 1570 if existingSymbol.Kind != ast.SymbolCatchIdentifier && existingSymbol.Kind != ast.SymbolArguments { 1571 // An identifier binding from a catch statement and a function 1572 // declaration can both silently shadow another hoisted symbol 1573 if symbol.Kind != ast.SymbolCatchIdentifier && symbol.Kind != ast.SymbolHoistedFunction { 1574 if !isSloppyModeBlockLevelFnStmt { 1575 p.addSymbolAlreadyDeclaredError(symbol.OriginalName, member.Loc, existingMember.Loc) 1576 } else if s == scope.Parent { 1577 // Never mind about this, turns out it's not needed after all 1578 delete(p.hoistedRefForSloppyModeBlockFn, originalMemberRef) 1579 } 1580 } 1581 continue nextMember 1582 } 1583 1584 // If this is a catch identifier, silently merge the existing symbol 1585 // into this symbol but continue hoisting past this catch scope 1586 existingSymbol.Link = member.Ref 1587 s.Members[symbol.OriginalName] = member 1588 } 1589 1590 if s.Kind.StopsHoisting() { 1591 // Declare the member in the scope that stopped the hoisting 1592 s.Members[symbol.OriginalName] = member 1593 break 1594 } 1595 s = s.Parent 1596 } 1597 } 1598 } 1599 1600 for _, child := range scope.Children { 1601 p.hoistSymbols(child) 1602 } 1603 } 1604 1605 func (p *parser) declareBinding(kind ast.SymbolKind, binding js_ast.Binding, opts parseStmtOpts) { 1606 js_ast.ForEachIdentifierBinding(binding, func(loc logger.Loc, b *js_ast.BIdentifier) { 1607 if !opts.isTypeScriptDeclare || (opts.isNamespaceScope && opts.isExport) { 1608 b.Ref = p.declareSymbol(kind, binding.Loc, p.loadNameFromRef(b.Ref)) 1609 } 1610 }) 1611 } 1612 1613 func (p *parser) recordUsage(ref ast.Ref) { 1614 // The use count stored in the symbol is used for generating symbol names 1615 // during minification. These counts shouldn't include references inside dead 1616 // code regions since those will be culled. 1617 if !p.isControlFlowDead { 1618 p.symbols[ref.InnerIndex].UseCountEstimate++ 1619 use := p.symbolUses[ref] 1620 use.CountEstimate++ 1621 p.symbolUses[ref] = use 1622 } 1623 1624 // The correctness of TypeScript-to-JavaScript conversion relies on accurate 1625 // symbol use counts for the whole file, including dead code regions. This is 1626 // tracked separately in a parser-only data structure. 1627 if p.options.ts.Parse { 1628 p.tsUseCounts[ref.InnerIndex]++ 1629 } 1630 } 1631 1632 func (p *parser) ignoreUsage(ref ast.Ref) { 1633 // Roll back the use count increment in recordUsage() 1634 if !p.isControlFlowDead { 1635 p.symbols[ref.InnerIndex].UseCountEstimate-- 1636 use := p.symbolUses[ref] 1637 use.CountEstimate-- 1638 if use.CountEstimate == 0 { 1639 delete(p.symbolUses, ref) 1640 } else { 1641 p.symbolUses[ref] = use 1642 } 1643 } 1644 1645 // Don't roll back the "tsUseCounts" increment. This must be counted even if 1646 // the value is ignored because that's what the TypeScript compiler does. 1647 } 1648 1649 func (p *parser) ignoreUsageOfIdentifierInDotChain(expr js_ast.Expr) { 1650 for { 1651 switch e := expr.Data.(type) { 1652 case *js_ast.EIdentifier: 1653 p.ignoreUsage(e.Ref) 1654 1655 case *js_ast.EDot: 1656 expr = e.Target 1657 continue 1658 1659 case *js_ast.EIndex: 1660 if _, ok := e.Index.Data.(*js_ast.EString); ok { 1661 expr = e.Target 1662 continue 1663 } 1664 } 1665 1666 return 1667 } 1668 } 1669 1670 func (p *parser) importFromRuntime(loc logger.Loc, name string) js_ast.Expr { 1671 it, ok := p.runtimeImports[name] 1672 if !ok { 1673 it.Loc = loc 1674 it.Ref = p.newSymbol(ast.SymbolOther, name) 1675 p.moduleScope.Generated = append(p.moduleScope.Generated, it.Ref) 1676 p.runtimeImports[name] = it 1677 } 1678 p.recordUsage(it.Ref) 1679 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: it.Ref}} 1680 } 1681 1682 func (p *parser) callRuntime(loc logger.Loc, name string, args []js_ast.Expr) js_ast.Expr { 1683 return js_ast.Expr{Loc: loc, Data: &js_ast.ECall{ 1684 Target: p.importFromRuntime(loc, name), 1685 Args: args, 1686 }} 1687 } 1688 1689 type JSXImport uint8 1690 1691 const ( 1692 JSXImportJSX JSXImport = iota 1693 JSXImportJSXS 1694 JSXImportFragment 1695 JSXImportCreateElement 1696 ) 1697 1698 func (p *parser) importJSXSymbol(loc logger.Loc, jsx JSXImport) js_ast.Expr { 1699 var symbols map[string]ast.LocRef 1700 var name string 1701 1702 switch jsx { 1703 case JSXImportJSX: 1704 symbols = p.jsxRuntimeImports 1705 if p.options.jsx.Development { 1706 name = "jsxDEV" 1707 } else { 1708 name = "jsx" 1709 } 1710 1711 case JSXImportJSXS: 1712 symbols = p.jsxRuntimeImports 1713 if p.options.jsx.Development { 1714 name = "jsxDEV" 1715 } else { 1716 name = "jsxs" 1717 } 1718 1719 case JSXImportFragment: 1720 symbols = p.jsxRuntimeImports 1721 name = "Fragment" 1722 1723 case JSXImportCreateElement: 1724 symbols = p.jsxLegacyImports 1725 name = "createElement" 1726 } 1727 1728 it, ok := symbols[name] 1729 if !ok { 1730 it.Loc = loc 1731 it.Ref = p.newSymbol(ast.SymbolOther, name) 1732 p.moduleScope.Generated = append(p.moduleScope.Generated, it.Ref) 1733 p.isImportItem[it.Ref] = true 1734 symbols[name] = it 1735 } 1736 1737 p.recordUsage(it.Ref) 1738 return p.handleIdentifier(loc, &js_ast.EIdentifier{Ref: it.Ref}, identifierOpts{ 1739 wasOriginallyIdentifier: true, 1740 }) 1741 } 1742 1743 func (p *parser) valueToSubstituteForRequire(loc logger.Loc) js_ast.Expr { 1744 if p.source.Index != runtime.SourceIndex && 1745 config.ShouldCallRuntimeRequire(p.options.mode, p.options.outputFormat) { 1746 return p.importFromRuntime(loc, "__require") 1747 } 1748 1749 p.recordUsage(p.requireRef) 1750 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.requireRef}} 1751 } 1752 1753 func (p *parser) makePromiseRef() ast.Ref { 1754 if p.promiseRef == ast.InvalidRef { 1755 p.promiseRef = p.newSymbol(ast.SymbolUnbound, "Promise") 1756 } 1757 return p.promiseRef 1758 } 1759 1760 func (p *parser) makeRegExpRef() ast.Ref { 1761 if p.regExpRef == ast.InvalidRef { 1762 p.regExpRef = p.newSymbol(ast.SymbolUnbound, "RegExp") 1763 p.moduleScope.Generated = append(p.moduleScope.Generated, p.regExpRef) 1764 } 1765 return p.regExpRef 1766 } 1767 1768 // The name is temporarily stored in the ref until the scope traversal pass 1769 // happens, at which point a symbol will be generated and the ref will point 1770 // to the symbol instead. 1771 // 1772 // The scope traversal pass will reconstruct the name using one of two methods. 1773 // In the common case, the name is a slice of the file itself. In that case we 1774 // can just store the slice and not need to allocate any extra memory. In the 1775 // rare case, the name is an externally-allocated string. In that case we store 1776 // an index to the string and use that index during the scope traversal pass. 1777 func (p *parser) storeNameInRef(name js_lexer.MaybeSubstring) ast.Ref { 1778 // Is the data in "name" a subset of the data in "p.source.Contents"? 1779 if name.Start.IsValid() { 1780 // The name is a slice of the file contents, so we can just reference it by 1781 // length and don't have to allocate anything. This is the common case. 1782 // 1783 // It's stored as a negative value so we'll crash if we try to use it. That 1784 // way we'll catch cases where we've forgotten to call loadNameFromRef(). 1785 // The length is the negative part because we know it's non-zero. 1786 return ast.Ref{SourceIndex: -uint32(len(name.String)), InnerIndex: uint32(name.Start.GetIndex())} 1787 } else { 1788 // The name is some memory allocated elsewhere. This is either an inline 1789 // string constant in the parser or an identifier with escape sequences 1790 // in the source code, which is very unusual. Stash it away for later. 1791 // This uses allocations but it should hopefully be very uncommon. 1792 ref := ast.Ref{SourceIndex: 0x80000000, InnerIndex: uint32(len(p.allocatedNames))} 1793 p.allocatedNames = append(p.allocatedNames, name.String) 1794 return ref 1795 } 1796 } 1797 1798 // This is the inverse of storeNameInRef() above 1799 func (p *parser) loadNameFromRef(ref ast.Ref) string { 1800 if ref.SourceIndex == 0x80000000 { 1801 return p.allocatedNames[ref.InnerIndex] 1802 } else { 1803 if (ref.SourceIndex & 0x80000000) == 0 { 1804 panic("Internal error: invalid symbol reference") 1805 } 1806 return p.source.Contents[ref.InnerIndex : int32(ref.InnerIndex)-int32(ref.SourceIndex)] 1807 } 1808 } 1809 1810 // Due to ES6 destructuring patterns, there are many cases where it's 1811 // impossible to distinguish between an array or object literal and a 1812 // destructuring assignment until we hit the "=" operator later on. 1813 // This object defers errors about being in one state or the other 1814 // until we discover which state we're in. 1815 type deferredErrors struct { 1816 // These are errors for expressions 1817 invalidExprDefaultValue logger.Range 1818 invalidExprAfterQuestion logger.Range 1819 arraySpreadFeature logger.Range 1820 1821 // These errors are for arrow functions 1822 invalidParens []logger.Range 1823 } 1824 1825 func (from *deferredErrors) mergeInto(to *deferredErrors) { 1826 if from.invalidExprDefaultValue.Len > 0 { 1827 to.invalidExprDefaultValue = from.invalidExprDefaultValue 1828 } 1829 if from.invalidExprAfterQuestion.Len > 0 { 1830 to.invalidExprAfterQuestion = from.invalidExprAfterQuestion 1831 } 1832 if from.arraySpreadFeature.Len > 0 { 1833 to.arraySpreadFeature = from.arraySpreadFeature 1834 } 1835 if len(from.invalidParens) > 0 { 1836 if len(to.invalidParens) > 0 { 1837 to.invalidParens = append(to.invalidParens, from.invalidParens...) 1838 } else { 1839 to.invalidParens = from.invalidParens 1840 } 1841 } 1842 } 1843 1844 func (p *parser) logExprErrors(errors *deferredErrors) { 1845 if errors.invalidExprDefaultValue.Len > 0 { 1846 p.log.AddError(&p.tracker, errors.invalidExprDefaultValue, "Unexpected \"=\"") 1847 } 1848 1849 if errors.invalidExprAfterQuestion.Len > 0 { 1850 r := errors.invalidExprAfterQuestion 1851 p.log.AddError(&p.tracker, r, fmt.Sprintf("Unexpected %q", p.source.Contents[r.Loc.Start:r.Loc.Start+r.Len])) 1852 } 1853 1854 if errors.arraySpreadFeature.Len > 0 { 1855 p.markSyntaxFeature(compat.ArraySpread, errors.arraySpreadFeature) 1856 } 1857 } 1858 1859 func (p *parser) logDeferredArrowArgErrors(errors *deferredErrors) { 1860 for _, paren := range errors.invalidParens { 1861 p.log.AddError(&p.tracker, paren, "Invalid binding pattern") 1862 } 1863 } 1864 1865 func (p *parser) logNullishCoalescingErrorPrecedenceError(op string) { 1866 prevOp := "??" 1867 if p.lexer.Token == js_lexer.TQuestionQuestion { 1868 op, prevOp = prevOp, op 1869 } 1870 // p.log.AddError(&p.tracker, p.lexer.Range(), fmt.Sprintf("The %q operator requires parentheses")) 1871 p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), fmt.Sprintf("Cannot use %q with %q without parentheses", op, prevOp), 1872 []logger.MsgData{{Text: fmt.Sprintf("Expressions of the form \"x %s y %s z\" are not allowed in JavaScript. "+ 1873 "You must disambiguate between \"(x %s y) %s z\" and \"x %s (y %s z)\" by adding parentheses.", prevOp, op, prevOp, op, prevOp, op)}}) 1874 } 1875 1876 func defineValueCanBeUsedInAssignTarget(data js_ast.E) bool { 1877 switch data.(type) { 1878 case *js_ast.EIdentifier, *js_ast.EDot: 1879 return true 1880 } 1881 1882 // Substituting a constant into an assignment target (e.g. "x = 1" becomes 1883 // "0 = 1") will cause a syntax error, so we avoid doing this. The caller 1884 // will log a warning instead. 1885 return false 1886 } 1887 1888 func (p *parser) logAssignToDefine(r logger.Range, name string, expr js_ast.Expr) { 1889 // If this is a compound expression, pretty-print it for the error message. 1890 // We don't use a literal slice of the source text in case it contains 1891 // problematic things (e.g. spans multiple lines, has embedded comments). 1892 if expr.Data != nil { 1893 var parts []string 1894 for { 1895 if id, ok := expr.Data.(*js_ast.EIdentifier); ok { 1896 parts = append(parts, p.loadNameFromRef(id.Ref)) 1897 break 1898 } else if dot, ok := expr.Data.(*js_ast.EDot); ok { 1899 parts = append(parts, dot.Name) 1900 parts = append(parts, ".") 1901 expr = dot.Target 1902 } else if index, ok := expr.Data.(*js_ast.EIndex); ok { 1903 if str, ok := index.Index.Data.(*js_ast.EString); ok { 1904 parts = append(parts, "]") 1905 parts = append(parts, string(helpers.QuoteSingle(helpers.UTF16ToString(str.Value), false))) 1906 parts = append(parts, "[") 1907 expr = index.Target 1908 } else { 1909 return 1910 } 1911 } else { 1912 return 1913 } 1914 } 1915 for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 { 1916 parts[i], parts[j] = parts[j], parts[i] 1917 } 1918 name = strings.Join(parts, "") 1919 } 1920 1921 kind := logger.Warning 1922 if p.suppressWarningsAboutWeirdCode { 1923 kind = logger.Debug 1924 } 1925 1926 p.log.AddIDWithNotes(logger.MsgID_JS_AssignToDefine, kind, &p.tracker, r, 1927 fmt.Sprintf("Suspicious assignment to defined constant %q", name), 1928 []logger.MsgData{{Text: fmt.Sprintf( 1929 "The expression %q has been configured to be replaced with a constant using the \"define\" feature. "+ 1930 "If this expression is supposed to be a compile-time constant, then it doesn't make sense to assign to it here. "+ 1931 "Or if this expression is supposed to change at run-time, this \"define\" substitution should be removed.", name)}}) 1932 } 1933 1934 // The "await" and "yield" expressions are never allowed in argument lists but 1935 // may or may not be allowed otherwise depending on the details of the enclosing 1936 // function or module. This needs to be handled when parsing an arrow function 1937 // argument list because we don't know if these expressions are not allowed until 1938 // we reach the "=>" token (or discover the absence of one). 1939 // 1940 // Specifically, for await: 1941 // 1942 // // This is ok 1943 // async function foo() { (x = await y) } 1944 // 1945 // // This is an error 1946 // async function foo() { (x = await y) => {} } 1947 // 1948 // And for yield: 1949 // 1950 // // This is ok 1951 // function* foo() { (x = yield y) } 1952 // 1953 // // This is an error 1954 // function* foo() { (x = yield y) => {} } 1955 type deferredArrowArgErrors struct { 1956 invalidExprAwait logger.Range 1957 invalidExprYield logger.Range 1958 } 1959 1960 func (p *parser) logArrowArgErrors(errors *deferredArrowArgErrors) { 1961 if errors.invalidExprAwait.Len > 0 { 1962 p.log.AddError(&p.tracker, errors.invalidExprAwait, "Cannot use an \"await\" expression here:") 1963 } 1964 1965 if errors.invalidExprYield.Len > 0 { 1966 p.log.AddError(&p.tracker, errors.invalidExprYield, "Cannot use a \"yield\" expression here:") 1967 } 1968 } 1969 1970 func (p *parser) keyNameForError(key js_ast.Expr) string { 1971 switch k := key.Data.(type) { 1972 case *js_ast.EString: 1973 return fmt.Sprintf("%q", helpers.UTF16ToString(k.Value)) 1974 case *js_ast.EPrivateIdentifier: 1975 return fmt.Sprintf("%q", p.loadNameFromRef(k.Ref)) 1976 } 1977 return "property" 1978 } 1979 1980 func (p *parser) checkForLegacyOctalLiteral(e js_ast.E) { 1981 if p.lexer.IsLegacyOctalLiteral { 1982 if p.legacyOctalLiterals == nil { 1983 p.legacyOctalLiterals = make(map[js_ast.E]logger.Range) 1984 } 1985 p.legacyOctalLiterals[e] = p.lexer.Range() 1986 } 1987 } 1988 1989 func (p *parser) notesForAssertTypeJSON(record *ast.ImportRecord, alias string) []logger.MsgData { 1990 return []logger.MsgData{p.tracker.MsgData( 1991 js_lexer.RangeOfImportAssertOrWith(p.source, *ast.FindAssertOrWithEntry(record.AssertOrWith.Entries, "type"), js_lexer.KeyAndValueRange), 1992 "The JSON import assertion is here:"), 1993 {Text: fmt.Sprintf("You can either keep the import assertion and only use the \"default\" import, "+ 1994 "or you can remove the import assertion and use the %q import.", alias)}} 1995 } 1996 1997 // This assumes the caller has already checked for TStringLiteral or TNoSubstitutionTemplateLiteral 1998 func (p *parser) parseStringLiteral() js_ast.Expr { 1999 var legacyOctalLoc logger.Loc 2000 loc := p.lexer.Loc() 2001 text := p.lexer.StringLiteral() 2002 2003 // Enable using a "/* @__KEY__ */" comment to turn a string into a key 2004 hasPropertyKeyComment := (p.lexer.HasCommentBefore & js_lexer.KeyCommentBefore) != 0 2005 if hasPropertyKeyComment { 2006 if name := helpers.UTF16ToString(text); p.isMangledProp(name) { 2007 value := js_ast.Expr{Loc: loc, Data: &js_ast.ENameOfSymbol{ 2008 Ref: p.storeNameInRef(js_lexer.MaybeSubstring{String: name}), 2009 HasPropertyKeyComment: true, 2010 }} 2011 p.lexer.Next() 2012 return value 2013 } 2014 } 2015 2016 if p.lexer.LegacyOctalLoc.Start > loc.Start { 2017 legacyOctalLoc = p.lexer.LegacyOctalLoc 2018 } 2019 value := js_ast.Expr{Loc: loc, Data: &js_ast.EString{ 2020 Value: text, 2021 LegacyOctalLoc: legacyOctalLoc, 2022 PreferTemplate: p.lexer.Token == js_lexer.TNoSubstitutionTemplateLiteral, 2023 HasPropertyKeyComment: hasPropertyKeyComment, 2024 }} 2025 p.lexer.Next() 2026 return value 2027 } 2028 2029 type propertyOpts struct { 2030 decorators []js_ast.Decorator 2031 decoratorScope *js_ast.Scope 2032 decoratorContext decoratorContextFlags 2033 2034 asyncRange logger.Range 2035 generatorRange logger.Range 2036 tsDeclareRange logger.Range 2037 classKeyword logger.Range 2038 isAsync bool 2039 isGenerator bool 2040 2041 // Class-related options 2042 isStatic bool 2043 isTSAbstract bool 2044 isClass bool 2045 classHasExtends bool 2046 } 2047 2048 func (p *parser) parseProperty(startLoc logger.Loc, kind js_ast.PropertyKind, opts propertyOpts, errors *deferredErrors) (js_ast.Property, bool) { 2049 var flags js_ast.PropertyFlags 2050 var key js_ast.Expr 2051 var closeBracketLoc logger.Loc 2052 keyRange := p.lexer.Range() 2053 2054 switch p.lexer.Token { 2055 case js_lexer.TNumericLiteral: 2056 key = js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.ENumber{Value: p.lexer.Number}} 2057 p.checkForLegacyOctalLiteral(key.Data) 2058 p.lexer.Next() 2059 2060 case js_lexer.TStringLiteral: 2061 key = p.parseStringLiteral() 2062 if !p.options.minifySyntax { 2063 flags |= js_ast.PropertyPreferQuotedKey 2064 } 2065 2066 case js_lexer.TBigIntegerLiteral: 2067 key = js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EBigInt{Value: p.lexer.Identifier.String}} 2068 p.markSyntaxFeature(compat.Bigint, p.lexer.Range()) 2069 p.lexer.Next() 2070 2071 case js_lexer.TPrivateIdentifier: 2072 if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0 { 2073 p.log.AddError(&p.tracker, p.lexer.Range(), "TypeScript experimental decorators cannot be used on private identifiers") 2074 } else if !opts.isClass { 2075 p.lexer.Expected(js_lexer.TIdentifier) 2076 } else if opts.tsDeclareRange.Len != 0 { 2077 p.log.AddError(&p.tracker, opts.tsDeclareRange, "\"declare\" cannot be used with a private identifier") 2078 } 2079 name := p.lexer.Identifier 2080 key = js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EPrivateIdentifier{Ref: p.storeNameInRef(name)}} 2081 p.reportPrivateNameUsage(name.String) 2082 p.lexer.Next() 2083 2084 case js_lexer.TOpenBracket: 2085 flags |= js_ast.PropertyIsComputed 2086 p.markSyntaxFeature(compat.ObjectExtensions, p.lexer.Range()) 2087 p.lexer.Next() 2088 wasIdentifier := p.lexer.Token == js_lexer.TIdentifier 2089 expr := p.parseExpr(js_ast.LComma) 2090 2091 // Handle index signatures 2092 if p.options.ts.Parse && p.lexer.Token == js_lexer.TColon && wasIdentifier && opts.isClass { 2093 if _, ok := expr.Data.(*js_ast.EIdentifier); ok { 2094 if opts.tsDeclareRange.Len != 0 { 2095 p.log.AddError(&p.tracker, opts.tsDeclareRange, "\"declare\" cannot be used with an index signature") 2096 } 2097 2098 // "[key: string]: any;" 2099 p.lexer.Next() 2100 p.skipTypeScriptType(js_ast.LLowest) 2101 p.lexer.Expect(js_lexer.TCloseBracket) 2102 p.lexer.Expect(js_lexer.TColon) 2103 p.skipTypeScriptType(js_ast.LLowest) 2104 p.lexer.ExpectOrInsertSemicolon() 2105 2106 // Skip this property entirely 2107 return js_ast.Property{}, false 2108 } 2109 } 2110 2111 closeBracketLoc = p.saveExprCommentsHere() 2112 p.lexer.Expect(js_lexer.TCloseBracket) 2113 key = expr 2114 2115 case js_lexer.TAsterisk: 2116 if kind != js_ast.PropertyField && (kind != js_ast.PropertyMethod || opts.isGenerator) { 2117 p.lexer.Unexpected() 2118 } 2119 opts.isGenerator = true 2120 opts.generatorRange = p.lexer.Range() 2121 p.lexer.Next() 2122 return p.parseProperty(startLoc, js_ast.PropertyMethod, opts, errors) 2123 2124 default: 2125 name := p.lexer.Identifier 2126 raw := p.lexer.Raw() 2127 nameRange := p.lexer.Range() 2128 if !p.lexer.IsIdentifierOrKeyword() { 2129 p.lexer.Expect(js_lexer.TIdentifier) 2130 } 2131 p.lexer.Next() 2132 2133 // Support contextual keywords 2134 if kind == js_ast.PropertyField { 2135 // Does the following token look like a key? 2136 couldBeModifierKeyword := p.lexer.IsIdentifierOrKeyword() 2137 if !couldBeModifierKeyword { 2138 switch p.lexer.Token { 2139 case js_lexer.TOpenBracket, js_lexer.TNumericLiteral, js_lexer.TStringLiteral, 2140 js_lexer.TAsterisk, js_lexer.TPrivateIdentifier: 2141 couldBeModifierKeyword = true 2142 } 2143 } 2144 2145 // If so, check for a modifier keyword 2146 if couldBeModifierKeyword { 2147 switch name.String { 2148 case "get": 2149 if !opts.isAsync && raw == name.String { 2150 p.markSyntaxFeature(compat.ObjectAccessors, nameRange) 2151 return p.parseProperty(startLoc, js_ast.PropertyGetter, opts, nil) 2152 } 2153 2154 case "set": 2155 if !opts.isAsync && raw == name.String { 2156 p.markSyntaxFeature(compat.ObjectAccessors, nameRange) 2157 return p.parseProperty(startLoc, js_ast.PropertySetter, opts, nil) 2158 } 2159 2160 case "accessor": 2161 if !p.lexer.HasNewlineBefore && !opts.isAsync && opts.isClass && raw == name.String { 2162 return p.parseProperty(startLoc, js_ast.PropertyAutoAccessor, opts, nil) 2163 } 2164 2165 case "async": 2166 if !p.lexer.HasNewlineBefore && !opts.isAsync && raw == name.String { 2167 opts.isAsync = true 2168 opts.asyncRange = nameRange 2169 return p.parseProperty(startLoc, js_ast.PropertyMethod, opts, nil) 2170 } 2171 2172 case "static": 2173 if !opts.isStatic && !opts.isAsync && opts.isClass && raw == name.String { 2174 opts.isStatic = true 2175 return p.parseProperty(startLoc, kind, opts, nil) 2176 } 2177 2178 case "declare": 2179 if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && opts.tsDeclareRange.Len == 0 && raw == name.String { 2180 opts.tsDeclareRange = nameRange 2181 scopeIndex := len(p.scopesInOrder) 2182 2183 if prop, ok := p.parseProperty(startLoc, kind, opts, nil); ok && 2184 prop.Kind == js_ast.PropertyField && prop.ValueOrNil.Data == nil && 2185 (p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0) { 2186 // If this is a well-formed class field with the "declare" keyword, 2187 // only keep the declaration to preserve its side-effects when 2188 // there are TypeScript experimental decorators present: 2189 // 2190 // class Foo { 2191 // // Remove this 2192 // declare [(console.log('side effect 1'), 'foo')] 2193 // 2194 // // Keep this 2195 // @decorator(console.log('side effect 2')) declare bar 2196 // } 2197 // 2198 // This behavior is surprisingly somehow valid with TypeScript 2199 // experimental decorators, which was possibly by accident. 2200 // TypeScript does not allow this with JavaScript decorators. 2201 // 2202 // References: 2203 // 2204 // https://github.com/evanw/esbuild/issues/1675 2205 // https://github.com/microsoft/TypeScript/issues/46345 2206 // 2207 prop.Kind = js_ast.PropertyDeclareOrAbstract 2208 return prop, true 2209 } 2210 2211 p.discardScopesUpTo(scopeIndex) 2212 return js_ast.Property{}, false 2213 } 2214 2215 case "abstract": 2216 if !p.lexer.HasNewlineBefore && opts.isClass && p.options.ts.Parse && !opts.isTSAbstract && raw == name.String { 2217 opts.isTSAbstract = true 2218 scopeIndex := len(p.scopesInOrder) 2219 2220 if prop, ok := p.parseProperty(startLoc, kind, opts, nil); ok && 2221 prop.Kind == js_ast.PropertyField && prop.ValueOrNil.Data == nil && 2222 (p.options.ts.Config.ExperimentalDecorators == config.True && len(opts.decorators) > 0) { 2223 // If this is a well-formed class field with the "abstract" keyword, 2224 // only keep the declaration to preserve its side-effects when 2225 // there are TypeScript experimental decorators present: 2226 // 2227 // abstract class Foo { 2228 // // Remove this 2229 // abstract [(console.log('side effect 1'), 'foo')] 2230 // 2231 // // Keep this 2232 // @decorator(console.log('side effect 2')) abstract bar 2233 // } 2234 // 2235 // This behavior is valid with TypeScript experimental decorators. 2236 // TypeScript does not allow this with JavaScript decorators. 2237 // 2238 // References: 2239 // 2240 // https://github.com/evanw/esbuild/issues/3684 2241 // 2242 prop.Kind = js_ast.PropertyDeclareOrAbstract 2243 return prop, true 2244 } 2245 2246 p.discardScopesUpTo(scopeIndex) 2247 return js_ast.Property{}, false 2248 } 2249 2250 case "private", "protected", "public", "readonly", "override": 2251 // Skip over TypeScript keywords 2252 if opts.isClass && p.options.ts.Parse && raw == name.String { 2253 return p.parseProperty(startLoc, kind, opts, nil) 2254 } 2255 } 2256 } else if p.lexer.Token == js_lexer.TOpenBrace && name.String == "static" && len(opts.decorators) == 0 { 2257 loc := p.lexer.Loc() 2258 p.lexer.Next() 2259 2260 oldFnOrArrowDataParse := p.fnOrArrowDataParse 2261 p.fnOrArrowDataParse = fnOrArrowDataParse{ 2262 isReturnDisallowed: true, 2263 allowSuperProperty: true, 2264 await: forbidAll, 2265 } 2266 2267 p.pushScopeForParsePass(js_ast.ScopeClassStaticInit, loc) 2268 stmts := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{}) 2269 p.popScope() 2270 2271 p.fnOrArrowDataParse = oldFnOrArrowDataParse 2272 2273 closeBraceLoc := p.lexer.Loc() 2274 p.lexer.Expect(js_lexer.TCloseBrace) 2275 return js_ast.Property{ 2276 Kind: js_ast.PropertyClassStaticBlock, 2277 Loc: startLoc, 2278 ClassStaticBlock: &js_ast.ClassStaticBlock{ 2279 Loc: loc, 2280 Block: js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}, 2281 }, 2282 }, true 2283 } 2284 } 2285 2286 if p.isMangledProp(name.String) { 2287 key = js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.ENameOfSymbol{Ref: p.storeNameInRef(name)}} 2288 } else { 2289 key = js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name.String)}} 2290 } 2291 2292 // Parse a shorthand property 2293 if !opts.isClass && kind == js_ast.PropertyField && p.lexer.Token != js_lexer.TColon && 2294 p.lexer.Token != js_lexer.TOpenParen && p.lexer.Token != js_lexer.TLessThan && 2295 js_lexer.Keywords[name.String] == js_lexer.T(0) { 2296 2297 // Forbid invalid identifiers 2298 if (p.fnOrArrowDataParse.await != allowIdent && name.String == "await") || 2299 (p.fnOrArrowDataParse.yield != allowIdent && name.String == "yield") { 2300 p.log.AddError(&p.tracker, nameRange, fmt.Sprintf("Cannot use %q as an identifier here:", name.String)) 2301 } 2302 2303 ref := p.storeNameInRef(name) 2304 value := js_ast.Expr{Loc: key.Loc, Data: &js_ast.EIdentifier{Ref: ref}} 2305 2306 // Destructuring patterns have an optional default value 2307 var initializerOrNil js_ast.Expr 2308 if errors != nil && p.lexer.Token == js_lexer.TEquals { 2309 errors.invalidExprDefaultValue = p.lexer.Range() 2310 p.lexer.Next() 2311 initializerOrNil = p.parseExpr(js_ast.LComma) 2312 } 2313 2314 return js_ast.Property{ 2315 Kind: kind, 2316 Loc: startLoc, 2317 Key: key, 2318 ValueOrNil: value, 2319 InitializerOrNil: initializerOrNil, 2320 Flags: js_ast.PropertyWasShorthand, 2321 }, true 2322 } 2323 } 2324 2325 hasTypeParameters := false 2326 hasDefiniteAssignmentAssertionOperator := false 2327 2328 if p.options.ts.Parse { 2329 if opts.isClass { 2330 if p.lexer.Token == js_lexer.TQuestion { 2331 // "class X { foo?: number }" 2332 // "class X { foo?(): number }" 2333 p.lexer.Next() 2334 } else if p.lexer.Token == js_lexer.TExclamation && !p.lexer.HasNewlineBefore && 2335 (kind == js_ast.PropertyField || kind == js_ast.PropertyAutoAccessor) { 2336 // "class X { foo!: number }" 2337 p.lexer.Next() 2338 hasDefiniteAssignmentAssertionOperator = true 2339 } 2340 } 2341 2342 // "class X { foo?<T>(): T }" 2343 // "const x = { foo<T>(): T {} }" 2344 if !hasDefiniteAssignmentAssertionOperator && kind != js_ast.PropertyAutoAccessor { 2345 hasTypeParameters = p.skipTypeScriptTypeParameters(allowConstModifier) != didNotSkipAnything 2346 } 2347 } 2348 2349 // Parse a class field with an optional initial value 2350 if kind == js_ast.PropertyAutoAccessor || (opts.isClass && kind == js_ast.PropertyField && 2351 !hasTypeParameters && (p.lexer.Token != js_lexer.TOpenParen || hasDefiniteAssignmentAssertionOperator)) { 2352 var initializerOrNil js_ast.Expr 2353 2354 // Forbid the names "constructor" and "prototype" in some cases 2355 if !flags.Has(js_ast.PropertyIsComputed) { 2356 if str, ok := key.Data.(*js_ast.EString); ok && (helpers.UTF16EqualsString(str.Value, "constructor") || 2357 (opts.isStatic && helpers.UTF16EqualsString(str.Value, "prototype"))) { 2358 p.log.AddError(&p.tracker, keyRange, fmt.Sprintf("Invalid field name %q", helpers.UTF16ToString(str.Value))) 2359 } 2360 } 2361 2362 // Skip over types 2363 if p.options.ts.Parse && p.lexer.Token == js_lexer.TColon { 2364 p.lexer.Next() 2365 p.skipTypeScriptType(js_ast.LLowest) 2366 } 2367 2368 if p.lexer.Token == js_lexer.TEquals { 2369 p.lexer.Next() 2370 2371 // "this" and "super" property access is allowed in field initializers 2372 oldIsThisDisallowed := p.fnOrArrowDataParse.isThisDisallowed 2373 oldAllowSuperProperty := p.fnOrArrowDataParse.allowSuperProperty 2374 p.fnOrArrowDataParse.isThisDisallowed = false 2375 p.fnOrArrowDataParse.allowSuperProperty = true 2376 2377 initializerOrNil = p.parseExpr(js_ast.LComma) 2378 2379 p.fnOrArrowDataParse.isThisDisallowed = oldIsThisDisallowed 2380 p.fnOrArrowDataParse.allowSuperProperty = oldAllowSuperProperty 2381 } 2382 2383 // Special-case private identifiers 2384 if private, ok := key.Data.(*js_ast.EPrivateIdentifier); ok { 2385 name := p.loadNameFromRef(private.Ref) 2386 if name == "#constructor" { 2387 p.log.AddError(&p.tracker, keyRange, fmt.Sprintf("Invalid field name %q", name)) 2388 } 2389 var declare ast.SymbolKind 2390 if kind == js_ast.PropertyAutoAccessor { 2391 if opts.isStatic { 2392 declare = ast.SymbolPrivateStaticGetSetPair 2393 } else { 2394 declare = ast.SymbolPrivateGetSetPair 2395 } 2396 private.Ref = p.declareSymbol(declare, key.Loc, name) 2397 p.privateGetters[private.Ref] = p.newSymbol(ast.SymbolOther, name[1:]+"_get") 2398 p.privateSetters[private.Ref] = p.newSymbol(ast.SymbolOther, name[1:]+"_set") 2399 } else { 2400 if opts.isStatic { 2401 declare = ast.SymbolPrivateStaticField 2402 } else { 2403 declare = ast.SymbolPrivateField 2404 } 2405 private.Ref = p.declareSymbol(declare, key.Loc, name) 2406 } 2407 } 2408 2409 p.lexer.ExpectOrInsertSemicolon() 2410 if opts.isStatic { 2411 flags |= js_ast.PropertyIsStatic 2412 } 2413 return js_ast.Property{ 2414 Decorators: opts.decorators, 2415 Loc: startLoc, 2416 Kind: kind, 2417 Flags: flags, 2418 Key: key, 2419 InitializerOrNil: initializerOrNil, 2420 CloseBracketLoc: closeBracketLoc, 2421 }, true 2422 } 2423 2424 // Parse a method expression 2425 if p.lexer.Token == js_lexer.TOpenParen || kind.IsMethodDefinition() || opts.isClass { 2426 hasError := false 2427 2428 if !hasError && opts.tsDeclareRange.Len != 0 { 2429 what := "method" 2430 if kind == js_ast.PropertyGetter { 2431 what = "getter" 2432 } else if kind == js_ast.PropertySetter { 2433 what = "setter" 2434 } 2435 p.log.AddError(&p.tracker, opts.tsDeclareRange, "\"declare\" cannot be used with a "+what) 2436 hasError = true 2437 } 2438 2439 if opts.isAsync && p.markAsyncFn(opts.asyncRange, opts.isGenerator) { 2440 hasError = true 2441 } 2442 2443 if !hasError && opts.isGenerator && p.markSyntaxFeature(compat.Generator, opts.generatorRange) { 2444 hasError = true 2445 } 2446 2447 if !hasError && p.lexer.Token == js_lexer.TOpenParen && kind != js_ast.PropertyGetter && kind != js_ast.PropertySetter && p.markSyntaxFeature(compat.ObjectExtensions, p.lexer.Range()) { 2448 hasError = true 2449 } 2450 2451 loc := p.lexer.Loc() 2452 scopeIndex := p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, loc) 2453 isConstructor := false 2454 2455 // Forbid the names "constructor" and "prototype" in some cases 2456 if opts.isClass && !flags.Has(js_ast.PropertyIsComputed) { 2457 if str, ok := key.Data.(*js_ast.EString); ok { 2458 if !opts.isStatic && helpers.UTF16EqualsString(str.Value, "constructor") { 2459 switch { 2460 case kind == js_ast.PropertyGetter: 2461 p.log.AddError(&p.tracker, keyRange, "Class constructor cannot be a getter") 2462 case kind == js_ast.PropertySetter: 2463 p.log.AddError(&p.tracker, keyRange, "Class constructor cannot be a setter") 2464 case opts.isAsync: 2465 p.log.AddError(&p.tracker, keyRange, "Class constructor cannot be an async function") 2466 case opts.isGenerator: 2467 p.log.AddError(&p.tracker, keyRange, "Class constructor cannot be a generator") 2468 default: 2469 isConstructor = true 2470 } 2471 } else if opts.isStatic && helpers.UTF16EqualsString(str.Value, "prototype") { 2472 p.log.AddError(&p.tracker, keyRange, "Invalid static method name \"prototype\"") 2473 } 2474 } 2475 } 2476 2477 await := allowIdent 2478 yield := allowIdent 2479 if opts.isAsync { 2480 await = allowExpr 2481 } 2482 if opts.isGenerator { 2483 yield = allowExpr 2484 } 2485 2486 fn, hadBody := p.parseFn(nil, opts.classKeyword, opts.decoratorContext, fnOrArrowDataParse{ 2487 needsAsyncLoc: key.Loc, 2488 asyncRange: opts.asyncRange, 2489 await: await, 2490 yield: yield, 2491 allowSuperCall: opts.classHasExtends && isConstructor, 2492 allowSuperProperty: true, 2493 decoratorScope: opts.decoratorScope, 2494 isConstructor: isConstructor, 2495 2496 // Only allow omitting the body if we're parsing TypeScript class 2497 allowMissingBodyForTypeScript: p.options.ts.Parse && opts.isClass, 2498 }) 2499 2500 // "class Foo { foo(): void; foo(): void {} }" 2501 if !hadBody { 2502 // Skip this property entirely 2503 p.popAndDiscardScope(scopeIndex) 2504 return js_ast.Property{}, false 2505 } 2506 2507 p.popScope() 2508 fn.IsUniqueFormalParameters = true 2509 value := js_ast.Expr{Loc: loc, Data: &js_ast.EFunction{Fn: fn}} 2510 2511 // Enforce argument rules for accessors 2512 switch kind { 2513 case js_ast.PropertyGetter: 2514 if len(fn.Args) > 0 { 2515 r := js_lexer.RangeOfIdentifier(p.source, fn.Args[0].Binding.Loc) 2516 p.log.AddError(&p.tracker, r, fmt.Sprintf("Getter %s must have zero arguments", p.keyNameForError(key))) 2517 } 2518 2519 case js_ast.PropertySetter: 2520 if len(fn.Args) != 1 { 2521 r := js_lexer.RangeOfIdentifier(p.source, key.Loc) 2522 if len(fn.Args) > 1 { 2523 r = js_lexer.RangeOfIdentifier(p.source, fn.Args[1].Binding.Loc) 2524 } 2525 p.log.AddError(&p.tracker, r, fmt.Sprintf("Setter %s must have exactly one argument", p.keyNameForError(key))) 2526 } 2527 2528 default: 2529 kind = js_ast.PropertyMethod 2530 } 2531 2532 // Special-case private identifiers 2533 if private, ok := key.Data.(*js_ast.EPrivateIdentifier); ok { 2534 var declare ast.SymbolKind 2535 var suffix string 2536 switch kind { 2537 case js_ast.PropertyGetter: 2538 if opts.isStatic { 2539 declare = ast.SymbolPrivateStaticGet 2540 } else { 2541 declare = ast.SymbolPrivateGet 2542 } 2543 suffix = "_get" 2544 case js_ast.PropertySetter: 2545 if opts.isStatic { 2546 declare = ast.SymbolPrivateStaticSet 2547 } else { 2548 declare = ast.SymbolPrivateSet 2549 } 2550 suffix = "_set" 2551 default: 2552 if opts.isStatic { 2553 declare = ast.SymbolPrivateStaticMethod 2554 } else { 2555 declare = ast.SymbolPrivateMethod 2556 } 2557 suffix = "_fn" 2558 } 2559 name := p.loadNameFromRef(private.Ref) 2560 if name == "#constructor" { 2561 p.log.AddError(&p.tracker, keyRange, fmt.Sprintf("Invalid method name %q", name)) 2562 } 2563 private.Ref = p.declareSymbol(declare, key.Loc, name) 2564 methodRef := p.newSymbol(ast.SymbolOther, name[1:]+suffix) 2565 if kind == js_ast.PropertySetter { 2566 p.privateSetters[private.Ref] = methodRef 2567 } else { 2568 p.privateGetters[private.Ref] = methodRef 2569 } 2570 } 2571 2572 if opts.isStatic { 2573 flags |= js_ast.PropertyIsStatic 2574 } 2575 return js_ast.Property{ 2576 Decorators: opts.decorators, 2577 Loc: startLoc, 2578 Kind: kind, 2579 Flags: flags, 2580 Key: key, 2581 ValueOrNil: value, 2582 CloseBracketLoc: closeBracketLoc, 2583 }, true 2584 } 2585 2586 // Parse an object key/value pair 2587 p.lexer.Expect(js_lexer.TColon) 2588 value := p.parseExprOrBindings(js_ast.LComma, errors) 2589 return js_ast.Property{ 2590 Loc: startLoc, 2591 Kind: kind, 2592 Flags: flags, 2593 Key: key, 2594 ValueOrNil: value, 2595 CloseBracketLoc: closeBracketLoc, 2596 }, true 2597 } 2598 2599 func (p *parser) parsePropertyBinding() js_ast.PropertyBinding { 2600 var key js_ast.Expr 2601 var closeBracketLoc logger.Loc 2602 isComputed := false 2603 preferQuotedKey := false 2604 loc := p.lexer.Loc() 2605 2606 switch p.lexer.Token { 2607 case js_lexer.TDotDotDot: 2608 p.lexer.Next() 2609 value := js_ast.Binding{Loc: p.saveExprCommentsHere(), Data: &js_ast.BIdentifier{Ref: p.storeNameInRef(p.lexer.Identifier)}} 2610 p.lexer.Expect(js_lexer.TIdentifier) 2611 return js_ast.PropertyBinding{ 2612 Loc: loc, 2613 IsSpread: true, 2614 Value: value, 2615 } 2616 2617 case js_lexer.TNumericLiteral: 2618 key = js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.ENumber{Value: p.lexer.Number}} 2619 p.checkForLegacyOctalLiteral(key.Data) 2620 p.lexer.Next() 2621 2622 case js_lexer.TStringLiteral: 2623 key = p.parseStringLiteral() 2624 preferQuotedKey = !p.options.minifySyntax 2625 2626 case js_lexer.TBigIntegerLiteral: 2627 key = js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EBigInt{Value: p.lexer.Identifier.String}} 2628 p.markSyntaxFeature(compat.Bigint, p.lexer.Range()) 2629 p.lexer.Next() 2630 2631 case js_lexer.TOpenBracket: 2632 isComputed = true 2633 p.lexer.Next() 2634 key = p.parseExpr(js_ast.LComma) 2635 closeBracketLoc = p.saveExprCommentsHere() 2636 p.lexer.Expect(js_lexer.TCloseBracket) 2637 2638 default: 2639 name := p.lexer.Identifier 2640 nameRange := p.lexer.Range() 2641 if !p.lexer.IsIdentifierOrKeyword() { 2642 p.lexer.Expect(js_lexer.TIdentifier) 2643 } 2644 p.lexer.Next() 2645 if p.isMangledProp(name.String) { 2646 key = js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.ENameOfSymbol{Ref: p.storeNameInRef(name)}} 2647 } else { 2648 key = js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name.String)}} 2649 } 2650 2651 if p.lexer.Token != js_lexer.TColon && p.lexer.Token != js_lexer.TOpenParen { 2652 // Forbid invalid identifiers 2653 if (p.fnOrArrowDataParse.await != allowIdent && name.String == "await") || 2654 (p.fnOrArrowDataParse.yield != allowIdent && name.String == "yield") { 2655 p.log.AddError(&p.tracker, nameRange, fmt.Sprintf("Cannot use %q as an identifier here:", name.String)) 2656 } 2657 2658 ref := p.storeNameInRef(name) 2659 value := js_ast.Binding{Loc: nameRange.Loc, Data: &js_ast.BIdentifier{Ref: ref}} 2660 2661 var defaultValueOrNil js_ast.Expr 2662 if p.lexer.Token == js_lexer.TEquals { 2663 p.lexer.Next() 2664 defaultValueOrNil = p.parseExpr(js_ast.LComma) 2665 } 2666 2667 return js_ast.PropertyBinding{ 2668 Loc: loc, 2669 Key: key, 2670 Value: value, 2671 DefaultValueOrNil: defaultValueOrNil, 2672 } 2673 } 2674 } 2675 2676 p.lexer.Expect(js_lexer.TColon) 2677 value := p.parseBinding(parseBindingOpts{}) 2678 2679 var defaultValueOrNil js_ast.Expr 2680 if p.lexer.Token == js_lexer.TEquals { 2681 p.lexer.Next() 2682 defaultValueOrNil = p.parseExpr(js_ast.LComma) 2683 } 2684 2685 return js_ast.PropertyBinding{ 2686 Loc: loc, 2687 IsComputed: isComputed, 2688 PreferQuotedKey: preferQuotedKey, 2689 Key: key, 2690 Value: value, 2691 DefaultValueOrNil: defaultValueOrNil, 2692 CloseBracketLoc: closeBracketLoc, 2693 } 2694 } 2695 2696 // These properties have special semantics in JavaScript. They must not be 2697 // mangled or we could potentially fail to parse valid JavaScript syntax or 2698 // generate invalid JavaScript syntax as output. 2699 // 2700 // This list is only intended to contain properties specific to the JavaScript 2701 // language itself to avoid syntax errors in the generated output. It's not 2702 // intended to contain properties for JavaScript APIs. Those must be provided 2703 // by the user. 2704 var permanentReservedProps = map[string]bool{ 2705 "__proto__": true, 2706 "constructor": true, 2707 "prototype": true, 2708 } 2709 2710 func (p *parser) isMangledProp(name string) bool { 2711 if p.options.mangleProps == nil { 2712 return false 2713 } 2714 if p.options.mangleProps.MatchString(name) && !permanentReservedProps[name] && (p.options.reserveProps == nil || !p.options.reserveProps.MatchString(name)) { 2715 return true 2716 } 2717 reservedProps := p.reservedProps 2718 if reservedProps == nil { 2719 reservedProps = make(map[string]bool) 2720 p.reservedProps = reservedProps 2721 } 2722 reservedProps[name] = true 2723 return false 2724 } 2725 2726 func (p *parser) symbolForMangledProp(name string) ast.Ref { 2727 mangledProps := p.mangledProps 2728 if mangledProps == nil { 2729 mangledProps = make(map[string]ast.Ref) 2730 p.mangledProps = mangledProps 2731 } 2732 ref, ok := mangledProps[name] 2733 if !ok { 2734 ref = p.newSymbol(ast.SymbolMangledProp, name) 2735 mangledProps[name] = ref 2736 } 2737 if !p.isControlFlowDead { 2738 p.symbols[ref.InnerIndex].UseCountEstimate++ 2739 } 2740 return ref 2741 } 2742 2743 type wasOriginallyDotOrIndex uint8 2744 2745 const ( 2746 wasOriginallyDot wasOriginallyDotOrIndex = iota 2747 wasOriginallyIndex 2748 ) 2749 2750 func (p *parser) dotOrMangledPropParse( 2751 target js_ast.Expr, 2752 name js_lexer.MaybeSubstring, 2753 nameLoc logger.Loc, 2754 optionalChain js_ast.OptionalChain, 2755 original wasOriginallyDotOrIndex, 2756 ) js_ast.E { 2757 if (original != wasOriginallyIndex || p.options.mangleQuoted) && p.isMangledProp(name.String) { 2758 return &js_ast.EIndex{ 2759 Target: target, 2760 Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.ENameOfSymbol{Ref: p.storeNameInRef(name)}}, 2761 OptionalChain: optionalChain, 2762 } 2763 } 2764 2765 return &js_ast.EDot{ 2766 Target: target, 2767 Name: name.String, 2768 NameLoc: nameLoc, 2769 OptionalChain: optionalChain, 2770 } 2771 } 2772 2773 func (p *parser) dotOrMangledPropVisit(target js_ast.Expr, name string, nameLoc logger.Loc) js_ast.E { 2774 if p.isMangledProp(name) { 2775 return &js_ast.EIndex{ 2776 Target: target, 2777 Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.ENameOfSymbol{Ref: p.symbolForMangledProp(name)}}, 2778 } 2779 } 2780 2781 return &js_ast.EDot{ 2782 Target: target, 2783 Name: name, 2784 NameLoc: nameLoc, 2785 } 2786 } 2787 2788 func (p *parser) parseArrowBody(args []js_ast.Arg, data fnOrArrowDataParse) *js_ast.EArrow { 2789 arrowLoc := p.lexer.Loc() 2790 2791 // Newlines are not allowed before "=>" 2792 if p.lexer.HasNewlineBefore { 2793 p.log.AddError(&p.tracker, p.lexer.Range(), "Unexpected newline before \"=>\"") 2794 panic(js_lexer.LexerPanic{}) 2795 } 2796 2797 p.lexer.Expect(js_lexer.TEqualsGreaterThan) 2798 2799 for _, arg := range args { 2800 p.declareBinding(ast.SymbolHoisted, arg.Binding, parseStmtOpts{}) 2801 } 2802 2803 // The ability to use "this" and "super" is inherited by arrow functions 2804 data.isThisDisallowed = p.fnOrArrowDataParse.isThisDisallowed 2805 data.allowSuperCall = p.fnOrArrowDataParse.allowSuperCall 2806 data.allowSuperProperty = p.fnOrArrowDataParse.allowSuperProperty 2807 2808 if p.lexer.Token == js_lexer.TOpenBrace { 2809 body := p.parseFnBody(data) 2810 p.afterArrowBodyLoc = p.lexer.Loc() 2811 return &js_ast.EArrow{Args: args, Body: body} 2812 } 2813 2814 p.pushScopeForParsePass(js_ast.ScopeFunctionBody, arrowLoc) 2815 defer p.popScope() 2816 2817 oldFnOrArrowData := p.fnOrArrowDataParse 2818 p.fnOrArrowDataParse = data 2819 expr := p.parseExpr(js_ast.LComma) 2820 p.fnOrArrowDataParse = oldFnOrArrowData 2821 return &js_ast.EArrow{ 2822 Args: args, 2823 PreferExpr: true, 2824 Body: js_ast.FnBody{Loc: arrowLoc, Block: js_ast.SBlock{Stmts: []js_ast.Stmt{{Loc: expr.Loc, Data: &js_ast.SReturn{ValueOrNil: expr}}}}}, 2825 } 2826 } 2827 2828 func (p *parser) checkForArrowAfterTheCurrentToken() bool { 2829 oldLexer := p.lexer 2830 p.lexer.IsLogDisabled = true 2831 2832 // Implement backtracking by restoring the lexer's memory to its original state 2833 defer func() { 2834 r := recover() 2835 if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic { 2836 p.lexer = oldLexer 2837 } else if r != nil { 2838 panic(r) 2839 } 2840 }() 2841 2842 p.lexer.Next() 2843 isArrowAfterThisToken := p.lexer.Token == js_lexer.TEqualsGreaterThan 2844 2845 p.lexer = oldLexer 2846 return isArrowAfterThisToken 2847 } 2848 2849 // This parses an expression. This assumes we've already parsed the "async" 2850 // keyword and are currently looking at the following token. 2851 func (p *parser) parseAsyncPrefixExpr(asyncRange logger.Range, level js_ast.L, flags exprFlag) js_ast.Expr { 2852 // "async function() {}" 2853 if !p.lexer.HasNewlineBefore && p.lexer.Token == js_lexer.TFunction { 2854 return p.parseFnExpr(asyncRange.Loc, true /* isAsync */, asyncRange) 2855 } 2856 2857 // Check the precedence level to avoid parsing an arrow function in 2858 // "new async () => {}". This also avoids parsing "new async()" as 2859 // "new (async())()" instead. 2860 if !p.lexer.HasNewlineBefore && level < js_ast.LMember { 2861 switch p.lexer.Token { 2862 // "async => {}" 2863 case js_lexer.TEqualsGreaterThan: 2864 if level <= js_ast.LAssign { 2865 arg := js_ast.Arg{Binding: js_ast.Binding{Loc: asyncRange.Loc, Data: &js_ast.BIdentifier{ 2866 Ref: p.storeNameInRef(js_lexer.MaybeSubstring{String: "async"})}}} 2867 2868 p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, asyncRange.Loc) 2869 defer p.popScope() 2870 2871 return js_ast.Expr{Loc: asyncRange.Loc, Data: p.parseArrowBody([]js_ast.Arg{arg}, fnOrArrowDataParse{ 2872 needsAsyncLoc: asyncRange.Loc, 2873 })} 2874 } 2875 2876 // "async x => {}" 2877 case js_lexer.TIdentifier: 2878 if level <= js_ast.LAssign { 2879 // See https://github.com/tc39/ecma262/issues/2034 for details 2880 isArrowFn := true 2881 if (flags&exprFlagForLoopInit) != 0 && p.lexer.Identifier.String == "of" { 2882 // "for (async of" is only an arrow function if the next token is "=>" 2883 isArrowFn = p.checkForArrowAfterTheCurrentToken() 2884 2885 // Do not allow "for (async of []) ;" but do allow "for await (async of []) ;" 2886 if !isArrowFn && (flags&exprFlagForAwaitLoopInit) == 0 && p.lexer.Raw() == "of" { 2887 r := logger.Range{Loc: asyncRange.Loc, Len: p.lexer.Range().End() - asyncRange.Loc.Start} 2888 p.log.AddError(&p.tracker, r, "For loop initializers cannot start with \"async of\"") 2889 panic(js_lexer.LexerPanic{}) 2890 } 2891 } 2892 2893 if isArrowFn { 2894 p.markAsyncFn(asyncRange, false) 2895 ref := p.storeNameInRef(p.lexer.Identifier) 2896 arg := js_ast.Arg{Binding: js_ast.Binding{Loc: p.lexer.Loc(), Data: &js_ast.BIdentifier{Ref: ref}}} 2897 p.lexer.Next() 2898 2899 p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, asyncRange.Loc) 2900 defer p.popScope() 2901 2902 arrow := p.parseArrowBody([]js_ast.Arg{arg}, fnOrArrowDataParse{ 2903 needsAsyncLoc: arg.Binding.Loc, 2904 await: allowExpr, 2905 }) 2906 arrow.IsAsync = true 2907 return js_ast.Expr{Loc: asyncRange.Loc, Data: arrow} 2908 } 2909 } 2910 2911 // "async()" 2912 // "async () => {}" 2913 case js_lexer.TOpenParen: 2914 p.lexer.Next() 2915 return p.parseParenExpr(asyncRange.Loc, level, parenExprOpts{asyncRange: asyncRange}) 2916 2917 // "async<T>()" 2918 // "async <T>() => {}" 2919 case js_lexer.TLessThan: 2920 if p.options.ts.Parse && (!p.options.jsx.Parse || p.isTSArrowFnJSX()) { 2921 if result := p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking(); result != didNotSkipAnything { 2922 p.lexer.Next() 2923 return p.parseParenExpr(asyncRange.Loc, level, parenExprOpts{ 2924 asyncRange: asyncRange, 2925 forceArrowFn: result == definitelyTypeParameters, 2926 }) 2927 } 2928 } 2929 } 2930 } 2931 2932 // "async" 2933 // "async + 1" 2934 return js_ast.Expr{Loc: asyncRange.Loc, Data: &js_ast.EIdentifier{ 2935 Ref: p.storeNameInRef(js_lexer.MaybeSubstring{String: "async"})}} 2936 } 2937 2938 func (p *parser) parseFnExpr(loc logger.Loc, isAsync bool, asyncRange logger.Range) js_ast.Expr { 2939 p.lexer.Next() 2940 isGenerator := p.lexer.Token == js_lexer.TAsterisk 2941 hasError := false 2942 if isAsync { 2943 hasError = p.markAsyncFn(asyncRange, isGenerator) 2944 } 2945 if isGenerator { 2946 if !hasError { 2947 p.markSyntaxFeature(compat.Generator, p.lexer.Range()) 2948 } 2949 p.lexer.Next() 2950 } 2951 var name *ast.LocRef 2952 2953 p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, loc) 2954 defer p.popScope() 2955 2956 // The name is optional 2957 if p.lexer.Token == js_lexer.TIdentifier { 2958 // Don't declare the name "arguments" since it's shadowed and inaccessible 2959 name = &ast.LocRef{Loc: p.lexer.Loc()} 2960 if text := p.lexer.Identifier.String; text != "arguments" { 2961 name.Ref = p.declareSymbol(ast.SymbolHoistedFunction, name.Loc, text) 2962 } else { 2963 name.Ref = p.newSymbol(ast.SymbolHoistedFunction, text) 2964 } 2965 p.lexer.Next() 2966 } 2967 2968 // Even anonymous functions can have TypeScript type parameters 2969 if p.options.ts.Parse { 2970 p.skipTypeScriptTypeParameters(allowConstModifier) 2971 } 2972 2973 await := allowIdent 2974 yield := allowIdent 2975 if isAsync { 2976 await = allowExpr 2977 } 2978 if isGenerator { 2979 yield = allowExpr 2980 } 2981 2982 fn, _ := p.parseFn(name, logger.Range{}, 0, fnOrArrowDataParse{ 2983 needsAsyncLoc: loc, 2984 asyncRange: asyncRange, 2985 await: await, 2986 yield: yield, 2987 }) 2988 p.validateFunctionName(fn, fnExpr) 2989 return js_ast.Expr{Loc: loc, Data: &js_ast.EFunction{Fn: fn}} 2990 } 2991 2992 type parenExprOpts struct { 2993 asyncRange logger.Range 2994 forceArrowFn bool 2995 } 2996 2997 // This assumes that the open parenthesis has already been parsed by the caller 2998 func (p *parser) parseParenExpr(loc logger.Loc, level js_ast.L, opts parenExprOpts) js_ast.Expr { 2999 items := []js_ast.Expr{} 3000 errors := deferredErrors{} 3001 arrowArgErrors := deferredArrowArgErrors{} 3002 spreadRange := logger.Range{} 3003 typeColonRange := logger.Range{} 3004 commaAfterSpread := logger.Loc{} 3005 isAsync := opts.asyncRange.Len > 0 3006 3007 // Push a scope assuming this is an arrow function. It may not be, in which 3008 // case we'll need to roll this change back. This has to be done ahead of 3009 // parsing the arguments instead of later on when we hit the "=>" token and 3010 // we know it's an arrow function because the arguments may have default 3011 // values that introduce new scopes and declare new symbols. If this is an 3012 // arrow function, then those new scopes will need to be parented under the 3013 // scope of the arrow function itself. 3014 scopeIndex := p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, loc) 3015 3016 // Allow "in" inside parentheses 3017 oldAllowIn := p.allowIn 3018 p.allowIn = true 3019 3020 // Forbid "await" and "yield", but only for arrow functions 3021 oldFnOrArrowData := p.fnOrArrowDataParse 3022 p.fnOrArrowDataParse.arrowArgErrors = &arrowArgErrors 3023 3024 // Scan over the comma-separated arguments or expressions 3025 for p.lexer.Token != js_lexer.TCloseParen { 3026 itemLoc := p.lexer.Loc() 3027 isSpread := p.lexer.Token == js_lexer.TDotDotDot 3028 3029 if isSpread { 3030 spreadRange = p.lexer.Range() 3031 p.markSyntaxFeature(compat.RestArgument, spreadRange) 3032 p.lexer.Next() 3033 } 3034 3035 // We don't know yet whether these are arguments or expressions, so parse 3036 // a superset of the expression syntax. Errors about things that are valid 3037 // in one but not in the other are deferred. 3038 p.latestArrowArgLoc = p.lexer.Loc() 3039 item := p.parseExprOrBindings(js_ast.LComma, &errors) 3040 3041 if isSpread { 3042 item = js_ast.Expr{Loc: itemLoc, Data: &js_ast.ESpread{Value: item}} 3043 } 3044 3045 // Skip over types 3046 if p.options.ts.Parse && p.lexer.Token == js_lexer.TColon { 3047 typeColonRange = p.lexer.Range() 3048 p.lexer.Next() 3049 p.skipTypeScriptType(js_ast.LLowest) 3050 } 3051 3052 // There may be a "=" after the type (but not after an "as" cast) 3053 if p.options.ts.Parse && p.lexer.Token == js_lexer.TEquals && p.lexer.Loc() != p.forbidSuffixAfterAsLoc { 3054 p.lexer.Next() 3055 item = js_ast.Assign(item, p.parseExpr(js_ast.LComma)) 3056 } 3057 3058 items = append(items, item) 3059 if p.lexer.Token != js_lexer.TComma { 3060 break 3061 } 3062 3063 // Spread arguments must come last. If there's a spread argument followed 3064 // by a comma, throw an error if we use these expressions as bindings. 3065 if isSpread { 3066 commaAfterSpread = p.lexer.Loc() 3067 } 3068 3069 // Eat the comma token 3070 p.lexer.Next() 3071 } 3072 3073 // The parenthetical construct must end with a close parenthesis 3074 p.lexer.Expect(js_lexer.TCloseParen) 3075 3076 // Restore "in" operator status before we parse the arrow function body 3077 p.allowIn = oldAllowIn 3078 3079 // Also restore "await" and "yield" expression errors 3080 p.fnOrArrowDataParse = oldFnOrArrowData 3081 3082 // Are these arguments to an arrow function? 3083 if p.lexer.Token == js_lexer.TEqualsGreaterThan || opts.forceArrowFn || (p.options.ts.Parse && p.lexer.Token == js_lexer.TColon) { 3084 // Arrow functions are not allowed inside certain expressions 3085 if level > js_ast.LAssign { 3086 p.lexer.Unexpected() 3087 } 3088 3089 var invalidLog invalidLog 3090 args := []js_ast.Arg{} 3091 3092 if isAsync { 3093 p.markAsyncFn(opts.asyncRange, false) 3094 } 3095 3096 // First, try converting the expressions to bindings 3097 for _, item := range items { 3098 isSpread := false 3099 if spread, ok := item.Data.(*js_ast.ESpread); ok { 3100 item = spread.Value 3101 isSpread = true 3102 } 3103 binding, initializerOrNil, log := p.convertExprToBindingAndInitializer(item, invalidLog, isSpread) 3104 invalidLog = log 3105 args = append(args, js_ast.Arg{Binding: binding, DefaultOrNil: initializerOrNil}) 3106 } 3107 3108 // Avoid parsing TypeScript code like "a ? (1 + 2) : (3 + 4)" as an arrow 3109 // function. The ":" after the ")" may be a return type annotation, so we 3110 // attempt to convert the expressions to bindings first before deciding 3111 // whether this is an arrow function, and only pick an arrow function if 3112 // there were no conversion errors. 3113 if p.lexer.Token == js_lexer.TEqualsGreaterThan || (len(invalidLog.invalidTokens) == 0 && 3114 p.trySkipTypeScriptArrowReturnTypeWithBacktracking()) || opts.forceArrowFn { 3115 if commaAfterSpread.Start != 0 { 3116 p.log.AddError(&p.tracker, logger.Range{Loc: commaAfterSpread, Len: 1}, "Unexpected \",\" after rest pattern") 3117 } 3118 p.logArrowArgErrors(&arrowArgErrors) 3119 p.logDeferredArrowArgErrors(&errors) 3120 3121 // Now that we've decided we're an arrow function, report binding pattern 3122 // conversion errors 3123 if len(invalidLog.invalidTokens) > 0 { 3124 for _, token := range invalidLog.invalidTokens { 3125 p.log.AddError(&p.tracker, token, "Invalid binding pattern") 3126 } 3127 panic(js_lexer.LexerPanic{}) 3128 } 3129 3130 // Also report syntax features used in bindings 3131 for _, entry := range invalidLog.syntaxFeatures { 3132 p.markSyntaxFeature(entry.feature, entry.token) 3133 } 3134 3135 await := allowIdent 3136 if isAsync { 3137 await = allowExpr 3138 } 3139 3140 arrow := p.parseArrowBody(args, fnOrArrowDataParse{ 3141 needsAsyncLoc: loc, 3142 await: await, 3143 }) 3144 arrow.IsAsync = isAsync 3145 arrow.HasRestArg = spreadRange.Len > 0 3146 p.popScope() 3147 return js_ast.Expr{Loc: loc, Data: arrow} 3148 } 3149 } 3150 3151 // If we get here, it's not an arrow function so undo the pushing of the 3152 // scope we did earlier. This needs to flatten any child scopes into the 3153 // parent scope as if the scope was never pushed in the first place. 3154 p.popAndFlattenScope(scopeIndex) 3155 3156 // If this isn't an arrow function, then types aren't allowed 3157 if typeColonRange.Len > 0 { 3158 p.log.AddError(&p.tracker, typeColonRange, "Unexpected \":\"") 3159 panic(js_lexer.LexerPanic{}) 3160 } 3161 3162 // Are these arguments for a call to a function named "async"? 3163 if isAsync { 3164 p.logExprErrors(&errors) 3165 async := js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{ 3166 Ref: p.storeNameInRef(js_lexer.MaybeSubstring{String: "async"})}} 3167 return js_ast.Expr{Loc: loc, Data: &js_ast.ECall{ 3168 Target: async, 3169 Args: items, 3170 }} 3171 } 3172 3173 // Is this a chain of expressions and comma operators? 3174 if len(items) > 0 { 3175 p.logExprErrors(&errors) 3176 if spreadRange.Len > 0 { 3177 p.log.AddError(&p.tracker, spreadRange, "Unexpected \"...\"") 3178 panic(js_lexer.LexerPanic{}) 3179 } 3180 value := js_ast.JoinAllWithComma(items) 3181 p.markExprAsParenthesized(value, loc, isAsync) 3182 return value 3183 } 3184 3185 // Indicate that we expected an arrow function 3186 p.lexer.Expected(js_lexer.TEqualsGreaterThan) 3187 return js_ast.Expr{} 3188 } 3189 3190 type invalidLog struct { 3191 invalidTokens []logger.Range 3192 syntaxFeatures []syntaxFeature 3193 } 3194 3195 type syntaxFeature struct { 3196 feature compat.JSFeature 3197 token logger.Range 3198 } 3199 3200 func (p *parser) convertExprToBindingAndInitializer( 3201 expr js_ast.Expr, invalidLog invalidLog, isSpread bool, 3202 ) (js_ast.Binding, js_ast.Expr, invalidLog) { 3203 var initializerOrNil js_ast.Expr 3204 if assign, ok := expr.Data.(*js_ast.EBinary); ok && assign.Op == js_ast.BinOpAssign { 3205 initializerOrNil = assign.Right 3206 expr = assign.Left 3207 } 3208 binding, invalidLog := p.convertExprToBinding(expr, invalidLog) 3209 if initializerOrNil.Data != nil { 3210 equalsRange := p.source.RangeOfOperatorBefore(initializerOrNil.Loc, "=") 3211 if isSpread { 3212 p.log.AddError(&p.tracker, equalsRange, "A rest argument cannot have a default initializer") 3213 } else { 3214 invalidLog.syntaxFeatures = append(invalidLog.syntaxFeatures, syntaxFeature{ 3215 feature: compat.DefaultArgument, 3216 token: equalsRange, 3217 }) 3218 } 3219 } 3220 return binding, initializerOrNil, invalidLog 3221 } 3222 3223 // Note: do not write to "p.log" in this function. Any errors due to conversion 3224 // from expression to binding should be written to "invalidLog" instead. That 3225 // way we can potentially keep this as an expression if it turns out it's not 3226 // needed as a binding after all. 3227 func (p *parser) convertExprToBinding(expr js_ast.Expr, invalidLog invalidLog) (js_ast.Binding, invalidLog) { 3228 switch e := expr.Data.(type) { 3229 case *js_ast.EMissing: 3230 return js_ast.Binding{Loc: expr.Loc, Data: js_ast.BMissingShared}, invalidLog 3231 3232 case *js_ast.EIdentifier: 3233 return js_ast.Binding{Loc: expr.Loc, Data: &js_ast.BIdentifier{Ref: e.Ref}}, invalidLog 3234 3235 case *js_ast.EArray: 3236 if e.CommaAfterSpread.Start != 0 { 3237 invalidLog.invalidTokens = append(invalidLog.invalidTokens, logger.Range{Loc: e.CommaAfterSpread, Len: 1}) 3238 } 3239 invalidLog.syntaxFeatures = append(invalidLog.syntaxFeatures, 3240 syntaxFeature{feature: compat.Destructuring, token: p.source.RangeOfOperatorAfter(expr.Loc, "[")}) 3241 items := []js_ast.ArrayBinding{} 3242 isSpread := false 3243 for _, item := range e.Items { 3244 if i, ok := item.Data.(*js_ast.ESpread); ok { 3245 isSpread = true 3246 item = i.Value 3247 if _, ok := item.Data.(*js_ast.EIdentifier); !ok { 3248 p.markSyntaxFeature(compat.NestedRestBinding, p.source.RangeOfOperatorAfter(item.Loc, "[")) 3249 } 3250 } 3251 binding, initializerOrNil, log := p.convertExprToBindingAndInitializer(item, invalidLog, isSpread) 3252 invalidLog = log 3253 items = append(items, js_ast.ArrayBinding{ 3254 Binding: binding, 3255 DefaultValueOrNil: initializerOrNil, 3256 Loc: item.Loc, 3257 }) 3258 } 3259 return js_ast.Binding{Loc: expr.Loc, Data: &js_ast.BArray{ 3260 Items: items, 3261 HasSpread: isSpread, 3262 IsSingleLine: e.IsSingleLine, 3263 CloseBracketLoc: e.CloseBracketLoc, 3264 }}, invalidLog 3265 3266 case *js_ast.EObject: 3267 if e.CommaAfterSpread.Start != 0 { 3268 invalidLog.invalidTokens = append(invalidLog.invalidTokens, logger.Range{Loc: e.CommaAfterSpread, Len: 1}) 3269 } 3270 invalidLog.syntaxFeatures = append(invalidLog.syntaxFeatures, 3271 syntaxFeature{feature: compat.Destructuring, token: p.source.RangeOfOperatorAfter(expr.Loc, "{")}) 3272 properties := []js_ast.PropertyBinding{} 3273 for _, property := range e.Properties { 3274 if property.Kind.IsMethodDefinition() { 3275 invalidLog.invalidTokens = append(invalidLog.invalidTokens, js_lexer.RangeOfIdentifier(p.source, property.Key.Loc)) 3276 continue 3277 } 3278 binding, initializerOrNil, log := p.convertExprToBindingAndInitializer(property.ValueOrNil, invalidLog, false) 3279 invalidLog = log 3280 if initializerOrNil.Data == nil { 3281 initializerOrNil = property.InitializerOrNil 3282 } 3283 properties = append(properties, js_ast.PropertyBinding{ 3284 Loc: property.Loc, 3285 IsSpread: property.Kind == js_ast.PropertySpread, 3286 IsComputed: property.Flags.Has(js_ast.PropertyIsComputed), 3287 Key: property.Key, 3288 Value: binding, 3289 DefaultValueOrNil: initializerOrNil, 3290 }) 3291 } 3292 return js_ast.Binding{Loc: expr.Loc, Data: &js_ast.BObject{ 3293 Properties: properties, 3294 IsSingleLine: e.IsSingleLine, 3295 CloseBraceLoc: e.CloseBraceLoc, 3296 }}, invalidLog 3297 3298 default: 3299 invalidLog.invalidTokens = append(invalidLog.invalidTokens, logger.Range{Loc: expr.Loc}) 3300 return js_ast.Binding{}, invalidLog 3301 } 3302 } 3303 3304 func (p *parser) saveExprCommentsHere() logger.Loc { 3305 loc := p.lexer.Loc() 3306 if p.exprComments != nil && len(p.lexer.CommentsBeforeToken) > 0 { 3307 comments := make([]string, len(p.lexer.CommentsBeforeToken)) 3308 for i, comment := range p.lexer.CommentsBeforeToken { 3309 comments[i] = p.source.CommentTextWithoutIndent(comment) 3310 } 3311 p.exprComments[loc] = comments 3312 p.lexer.CommentsBeforeToken = p.lexer.CommentsBeforeToken[0:] 3313 } 3314 return loc 3315 } 3316 3317 type exprFlag uint8 3318 3319 const ( 3320 exprFlagDecorator exprFlag = 1 << iota 3321 exprFlagForLoopInit 3322 exprFlagForAwaitLoopInit 3323 ) 3324 3325 func (p *parser) parsePrefix(level js_ast.L, errors *deferredErrors, flags exprFlag) js_ast.Expr { 3326 loc := p.saveExprCommentsHere() 3327 3328 switch p.lexer.Token { 3329 case js_lexer.TSuper: 3330 superRange := p.lexer.Range() 3331 p.lexer.Next() 3332 3333 switch p.lexer.Token { 3334 case js_lexer.TOpenParen: 3335 if level < js_ast.LCall && p.fnOrArrowDataParse.allowSuperCall { 3336 return js_ast.Expr{Loc: loc, Data: js_ast.ESuperShared} 3337 } 3338 3339 case js_lexer.TDot, js_lexer.TOpenBracket: 3340 if p.fnOrArrowDataParse.allowSuperProperty { 3341 return js_ast.Expr{Loc: loc, Data: js_ast.ESuperShared} 3342 } 3343 } 3344 3345 p.log.AddError(&p.tracker, superRange, "Unexpected \"super\"") 3346 return js_ast.Expr{Loc: loc, Data: js_ast.ESuperShared} 3347 3348 case js_lexer.TOpenParen: 3349 if errors != nil { 3350 errors.invalidParens = append(errors.invalidParens, p.lexer.Range()) 3351 } 3352 3353 p.lexer.Next() 3354 3355 // Arrow functions aren't allowed in the middle of expressions 3356 if level > js_ast.LAssign { 3357 // Allow "in" inside parentheses 3358 oldAllowIn := p.allowIn 3359 p.allowIn = true 3360 3361 value := p.parseExpr(js_ast.LLowest) 3362 p.markExprAsParenthesized(value, loc, false) 3363 p.lexer.Expect(js_lexer.TCloseParen) 3364 3365 p.allowIn = oldAllowIn 3366 return value 3367 } 3368 3369 value := p.parseParenExpr(loc, level, parenExprOpts{}) 3370 return value 3371 3372 case js_lexer.TFalse: 3373 p.lexer.Next() 3374 return js_ast.Expr{Loc: loc, Data: &js_ast.EBoolean{Value: false}} 3375 3376 case js_lexer.TTrue: 3377 p.lexer.Next() 3378 return js_ast.Expr{Loc: loc, Data: &js_ast.EBoolean{Value: true}} 3379 3380 case js_lexer.TNull: 3381 p.lexer.Next() 3382 return js_ast.Expr{Loc: loc, Data: js_ast.ENullShared} 3383 3384 case js_lexer.TThis: 3385 if p.fnOrArrowDataParse.isThisDisallowed { 3386 p.log.AddError(&p.tracker, p.lexer.Range(), "Cannot use \"this\" here:") 3387 } 3388 p.lexer.Next() 3389 return js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} 3390 3391 case js_lexer.TPrivateIdentifier: 3392 if !p.allowPrivateIdentifiers || !p.allowIn || level >= js_ast.LCompare { 3393 p.lexer.Unexpected() 3394 } 3395 3396 name := p.lexer.Identifier 3397 p.lexer.Next() 3398 3399 // Check for "#foo in bar" 3400 if p.lexer.Token != js_lexer.TIn { 3401 p.lexer.Expected(js_lexer.TIn) 3402 } 3403 3404 // Make sure to lower all matching private names 3405 if p.options.unsupportedJSFeatures.Has(compat.ClassPrivateBrandCheck) { 3406 if p.lowerAllOfThesePrivateNames == nil { 3407 p.lowerAllOfThesePrivateNames = make(map[string]bool) 3408 } 3409 p.lowerAllOfThesePrivateNames[name.String] = true 3410 } 3411 3412 return js_ast.Expr{Loc: loc, Data: &js_ast.EPrivateIdentifier{Ref: p.storeNameInRef(name)}} 3413 3414 case js_lexer.TIdentifier: 3415 name := p.lexer.Identifier 3416 nameRange := p.lexer.Range() 3417 raw := p.lexer.Raw() 3418 p.lexer.Next() 3419 3420 // Handle async and await expressions 3421 switch name.String { 3422 case "async": 3423 if raw == "async" { 3424 return p.parseAsyncPrefixExpr(nameRange, level, flags) 3425 } 3426 3427 case "await": 3428 switch p.fnOrArrowDataParse.await { 3429 case forbidAll: 3430 p.log.AddError(&p.tracker, nameRange, "The keyword \"await\" cannot be used here:") 3431 3432 case allowExpr: 3433 if raw != "await" { 3434 p.log.AddError(&p.tracker, nameRange, "The keyword \"await\" cannot be escaped") 3435 } else { 3436 if p.fnOrArrowDataParse.isTopLevel { 3437 p.topLevelAwaitKeyword = nameRange 3438 } 3439 if p.fnOrArrowDataParse.arrowArgErrors != nil { 3440 p.fnOrArrowDataParse.arrowArgErrors.invalidExprAwait = nameRange 3441 } 3442 value := p.parseExpr(js_ast.LPrefix) 3443 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3444 p.lexer.Unexpected() 3445 } 3446 return js_ast.Expr{Loc: loc, Data: &js_ast.EAwait{Value: value}} 3447 } 3448 3449 case allowIdent: 3450 p.lexer.PrevTokenWasAwaitKeyword = true 3451 p.lexer.AwaitKeywordLoc = loc 3452 p.lexer.FnOrArrowStartLoc = p.fnOrArrowDataParse.needsAsyncLoc 3453 } 3454 3455 case "yield": 3456 switch p.fnOrArrowDataParse.yield { 3457 case forbidAll: 3458 p.log.AddError(&p.tracker, nameRange, "The keyword \"yield\" cannot be used here:") 3459 3460 case allowExpr: 3461 if raw != "yield" { 3462 p.log.AddError(&p.tracker, nameRange, "The keyword \"yield\" cannot be escaped") 3463 } else { 3464 if level > js_ast.LAssign { 3465 p.log.AddError(&p.tracker, nameRange, "Cannot use a \"yield\" expression here without parentheses:") 3466 } 3467 if p.fnOrArrowDataParse.arrowArgErrors != nil { 3468 p.fnOrArrowDataParse.arrowArgErrors.invalidExprYield = nameRange 3469 } 3470 return p.parseYieldExpr(loc) 3471 } 3472 3473 case allowIdent: 3474 if !p.lexer.HasNewlineBefore { 3475 // Try to gracefully recover if "yield" is used in the wrong place 3476 switch p.lexer.Token { 3477 case js_lexer.TNull, js_lexer.TIdentifier, js_lexer.TFalse, js_lexer.TTrue, 3478 js_lexer.TNumericLiteral, js_lexer.TBigIntegerLiteral, js_lexer.TStringLiteral: 3479 p.log.AddError(&p.tracker, nameRange, "Cannot use \"yield\" outside a generator function") 3480 return p.parseYieldExpr(loc) 3481 } 3482 } 3483 } 3484 } 3485 3486 // Handle the start of an arrow expression 3487 if p.lexer.Token == js_lexer.TEqualsGreaterThan && level <= js_ast.LAssign { 3488 ref := p.storeNameInRef(name) 3489 arg := js_ast.Arg{Binding: js_ast.Binding{Loc: loc, Data: &js_ast.BIdentifier{Ref: ref}}} 3490 3491 p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, loc) 3492 defer p.popScope() 3493 3494 return js_ast.Expr{Loc: loc, Data: p.parseArrowBody([]js_ast.Arg{arg}, fnOrArrowDataParse{ 3495 needsAsyncLoc: loc, 3496 })} 3497 } 3498 3499 ref := p.storeNameInRef(name) 3500 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}} 3501 3502 case js_lexer.TStringLiteral, js_lexer.TNoSubstitutionTemplateLiteral: 3503 return p.parseStringLiteral() 3504 3505 case js_lexer.TTemplateHead: 3506 var legacyOctalLoc logger.Loc 3507 headLoc := p.lexer.Loc() 3508 head := p.lexer.StringLiteral() 3509 if p.lexer.LegacyOctalLoc.Start > loc.Start { 3510 legacyOctalLoc = p.lexer.LegacyOctalLoc 3511 } 3512 parts, tailLegacyOctalLoc := p.parseTemplateParts(false /* includeRaw */) 3513 if tailLegacyOctalLoc.Start > 0 { 3514 legacyOctalLoc = tailLegacyOctalLoc 3515 } 3516 return js_ast.Expr{Loc: loc, Data: &js_ast.ETemplate{ 3517 HeadLoc: headLoc, 3518 HeadCooked: head, 3519 Parts: parts, 3520 LegacyOctalLoc: legacyOctalLoc, 3521 }} 3522 3523 case js_lexer.TNumericLiteral: 3524 value := js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: p.lexer.Number}} 3525 p.checkForLegacyOctalLiteral(value.Data) 3526 p.lexer.Next() 3527 return value 3528 3529 case js_lexer.TBigIntegerLiteral: 3530 value := p.lexer.Identifier 3531 p.markSyntaxFeature(compat.Bigint, p.lexer.Range()) 3532 p.lexer.Next() 3533 return js_ast.Expr{Loc: loc, Data: &js_ast.EBigInt{Value: value.String}} 3534 3535 case js_lexer.TSlash, js_lexer.TSlashEquals: 3536 p.lexer.ScanRegExp() 3537 value := p.lexer.Raw() 3538 p.lexer.Next() 3539 return js_ast.Expr{Loc: loc, Data: &js_ast.ERegExp{Value: value}} 3540 3541 case js_lexer.TVoid: 3542 p.lexer.Next() 3543 value := p.parseExpr(js_ast.LPrefix) 3544 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3545 p.lexer.Unexpected() 3546 } 3547 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpVoid, Value: value}} 3548 3549 case js_lexer.TTypeof: 3550 p.lexer.Next() 3551 value := p.parseExpr(js_ast.LPrefix) 3552 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3553 p.lexer.Unexpected() 3554 } 3555 _, valueIsIdentifier := value.Data.(*js_ast.EIdentifier) 3556 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{ 3557 Op: js_ast.UnOpTypeof, 3558 Value: value, 3559 WasOriginallyTypeofIdentifier: valueIsIdentifier, 3560 }} 3561 3562 case js_lexer.TDelete: 3563 p.lexer.Next() 3564 value := p.parseExpr(js_ast.LPrefix) 3565 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3566 p.lexer.Unexpected() 3567 } 3568 if index, ok := value.Data.(*js_ast.EIndex); ok { 3569 if private, ok := index.Index.Data.(*js_ast.EPrivateIdentifier); ok { 3570 name := p.loadNameFromRef(private.Ref) 3571 r := logger.Range{Loc: index.Index.Loc, Len: int32(len(name))} 3572 p.log.AddError(&p.tracker, r, fmt.Sprintf("Deleting the private name %q is forbidden", name)) 3573 } 3574 } 3575 _, valueIsIdentifier := value.Data.(*js_ast.EIdentifier) 3576 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{ 3577 Op: js_ast.UnOpDelete, 3578 Value: value, 3579 WasOriginallyDeleteOfIdentifierOrPropertyAccess: valueIsIdentifier || js_ast.IsPropertyAccess(value), 3580 }} 3581 3582 case js_lexer.TPlus: 3583 p.lexer.Next() 3584 value := p.parseExpr(js_ast.LPrefix) 3585 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3586 p.lexer.Unexpected() 3587 } 3588 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpPos, Value: value}} 3589 3590 case js_lexer.TMinus: 3591 p.lexer.Next() 3592 value := p.parseExpr(js_ast.LPrefix) 3593 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3594 p.lexer.Unexpected() 3595 } 3596 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpNeg, Value: value}} 3597 3598 case js_lexer.TTilde: 3599 p.lexer.Next() 3600 value := p.parseExpr(js_ast.LPrefix) 3601 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3602 p.lexer.Unexpected() 3603 } 3604 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpCpl, Value: value}} 3605 3606 case js_lexer.TExclamation: 3607 p.lexer.Next() 3608 value := p.parseExpr(js_ast.LPrefix) 3609 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 3610 p.lexer.Unexpected() 3611 } 3612 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpNot, Value: value}} 3613 3614 case js_lexer.TMinusMinus: 3615 p.lexer.Next() 3616 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpPreDec, Value: p.parseExpr(js_ast.LPrefix)}} 3617 3618 case js_lexer.TPlusPlus: 3619 p.lexer.Next() 3620 return js_ast.Expr{Loc: loc, Data: &js_ast.EUnary{Op: js_ast.UnOpPreInc, Value: p.parseExpr(js_ast.LPrefix)}} 3621 3622 case js_lexer.TFunction: 3623 return p.parseFnExpr(loc, false /* isAsync */, logger.Range{}) 3624 3625 case js_lexer.TClass: 3626 return p.parseClassExpr(nil) 3627 3628 case js_lexer.TAt: 3629 // Parse decorators before class expressions 3630 decorators := p.parseDecorators(p.currentScope, logger.Range{}, decoratorBeforeClassExpr) 3631 return p.parseClassExpr(decorators) 3632 3633 case js_lexer.TNew: 3634 p.lexer.Next() 3635 3636 // Special-case the weird "new.target" expression here 3637 if p.lexer.Token == js_lexer.TDot { 3638 p.lexer.Next() 3639 if p.lexer.Token != js_lexer.TIdentifier || p.lexer.Raw() != "target" { 3640 p.lexer.Unexpected() 3641 } 3642 r := logger.Range{Loc: loc, Len: p.lexer.Range().End() - loc.Start} 3643 p.markSyntaxFeature(compat.NewTarget, r) 3644 p.lexer.Next() 3645 return js_ast.Expr{Loc: loc, Data: &js_ast.ENewTarget{Range: r}} 3646 } 3647 3648 target := p.parseExprWithFlags(js_ast.LMember, flags) 3649 args := []js_ast.Expr{} 3650 var closeParenLoc logger.Loc 3651 var isMultiLine bool 3652 3653 if p.lexer.Token == js_lexer.TOpenParen { 3654 args, closeParenLoc, isMultiLine = p.parseCallArgs() 3655 } 3656 3657 return js_ast.Expr{Loc: loc, Data: &js_ast.ENew{ 3658 Target: target, 3659 Args: args, 3660 CloseParenLoc: closeParenLoc, 3661 IsMultiLine: isMultiLine, 3662 }} 3663 3664 case js_lexer.TOpenBracket: 3665 p.lexer.Next() 3666 isSingleLine := !p.lexer.HasNewlineBefore 3667 items := []js_ast.Expr{} 3668 selfErrors := deferredErrors{} 3669 commaAfterSpread := logger.Loc{} 3670 3671 // Allow "in" inside arrays 3672 oldAllowIn := p.allowIn 3673 p.allowIn = true 3674 3675 for p.lexer.Token != js_lexer.TCloseBracket { 3676 switch p.lexer.Token { 3677 case js_lexer.TComma: 3678 items = append(items, js_ast.Expr{Loc: p.lexer.Loc(), Data: js_ast.EMissingShared}) 3679 3680 case js_lexer.TDotDotDot: 3681 if errors != nil { 3682 errors.arraySpreadFeature = p.lexer.Range() 3683 } else { 3684 p.markSyntaxFeature(compat.ArraySpread, p.lexer.Range()) 3685 } 3686 dotsLoc := p.saveExprCommentsHere() 3687 p.lexer.Next() 3688 item := p.parseExprOrBindings(js_ast.LComma, &selfErrors) 3689 items = append(items, js_ast.Expr{Loc: dotsLoc, Data: &js_ast.ESpread{Value: item}}) 3690 3691 // Commas are not allowed here when destructuring 3692 if p.lexer.Token == js_lexer.TComma { 3693 commaAfterSpread = p.lexer.Loc() 3694 } 3695 3696 default: 3697 item := p.parseExprOrBindings(js_ast.LComma, &selfErrors) 3698 items = append(items, item) 3699 } 3700 3701 if p.lexer.Token != js_lexer.TComma { 3702 break 3703 } 3704 if p.lexer.HasNewlineBefore { 3705 isSingleLine = false 3706 } 3707 p.lexer.Next() 3708 if p.lexer.HasNewlineBefore { 3709 isSingleLine = false 3710 } 3711 } 3712 3713 if p.lexer.HasNewlineBefore { 3714 isSingleLine = false 3715 } 3716 closeBracketLoc := p.saveExprCommentsHere() 3717 p.lexer.Expect(js_lexer.TCloseBracket) 3718 p.allowIn = oldAllowIn 3719 3720 if p.willNeedBindingPattern() { 3721 // Is this a binding pattern? 3722 } else if errors == nil { 3723 // Is this an expression? 3724 p.logExprErrors(&selfErrors) 3725 } else { 3726 // In this case, we can't distinguish between the two yet 3727 selfErrors.mergeInto(errors) 3728 } 3729 3730 return js_ast.Expr{Loc: loc, Data: &js_ast.EArray{ 3731 Items: items, 3732 CommaAfterSpread: commaAfterSpread, 3733 IsSingleLine: isSingleLine, 3734 CloseBracketLoc: closeBracketLoc, 3735 }} 3736 3737 case js_lexer.TOpenBrace: 3738 p.lexer.Next() 3739 isSingleLine := !p.lexer.HasNewlineBefore 3740 properties := []js_ast.Property{} 3741 selfErrors := deferredErrors{} 3742 commaAfterSpread := logger.Loc{} 3743 3744 // Allow "in" inside object literals 3745 oldAllowIn := p.allowIn 3746 p.allowIn = true 3747 3748 for p.lexer.Token != js_lexer.TCloseBrace { 3749 if p.lexer.Token == js_lexer.TDotDotDot { 3750 dotLoc := p.saveExprCommentsHere() 3751 p.lexer.Next() 3752 value := p.parseExprOrBindings(js_ast.LComma, &selfErrors) 3753 properties = append(properties, js_ast.Property{ 3754 Kind: js_ast.PropertySpread, 3755 Loc: dotLoc, 3756 ValueOrNil: value, 3757 }) 3758 3759 // Commas are not allowed here when destructuring 3760 if p.lexer.Token == js_lexer.TComma { 3761 commaAfterSpread = p.lexer.Loc() 3762 } 3763 } else { 3764 // This property may turn out to be a type in TypeScript, which should be ignored 3765 if property, ok := p.parseProperty(p.saveExprCommentsHere(), js_ast.PropertyField, propertyOpts{}, &selfErrors); ok { 3766 properties = append(properties, property) 3767 } 3768 } 3769 3770 if p.lexer.Token != js_lexer.TComma { 3771 break 3772 } 3773 if p.lexer.HasNewlineBefore { 3774 isSingleLine = false 3775 } 3776 p.lexer.Next() 3777 if p.lexer.HasNewlineBefore { 3778 isSingleLine = false 3779 } 3780 } 3781 3782 if p.lexer.HasNewlineBefore { 3783 isSingleLine = false 3784 } 3785 closeBraceLoc := p.saveExprCommentsHere() 3786 p.lexer.Expect(js_lexer.TCloseBrace) 3787 p.allowIn = oldAllowIn 3788 3789 if p.willNeedBindingPattern() { 3790 // Is this a binding pattern? 3791 } else if errors == nil { 3792 // Is this an expression? 3793 p.logExprErrors(&selfErrors) 3794 } else { 3795 // In this case, we can't distinguish between the two yet 3796 selfErrors.mergeInto(errors) 3797 } 3798 3799 return js_ast.Expr{Loc: loc, Data: &js_ast.EObject{ 3800 Properties: properties, 3801 CommaAfterSpread: commaAfterSpread, 3802 IsSingleLine: isSingleLine, 3803 CloseBraceLoc: closeBraceLoc, 3804 }} 3805 3806 case js_lexer.TLessThan: 3807 // This is a very complicated and highly ambiguous area of TypeScript 3808 // syntax. Many similar-looking things are overloaded. 3809 // 3810 // TS: 3811 // 3812 // A type cast: 3813 // <A>(x) 3814 // <[]>(x) 3815 // <A[]>(x) 3816 // <const>(x) 3817 // 3818 // An arrow function with type parameters: 3819 // <A>(x) => {} 3820 // <A, B>(x) => {} 3821 // <A = B>(x) => {} 3822 // <A extends B>(x) => {} 3823 // <const A>(x) => {} 3824 // <const A extends B>(x) => {} 3825 // 3826 // A syntax error: 3827 // <>() => {} 3828 // 3829 // TSX: 3830 // 3831 // A JSX element: 3832 // <>() => {}</> 3833 // <A>(x) => {}</A> 3834 // <A extends/> 3835 // <A extends>(x) => {}</A> 3836 // <A extends={false}>(x) => {}</A> 3837 // <const A extends/> 3838 // <const A extends>(x) => {}</const> 3839 // 3840 // An arrow function with type parameters: 3841 // <A,>(x) => {} 3842 // <A, B>(x) => {} 3843 // <A = B>(x) => {} 3844 // <A extends B>(x) => {} 3845 // <const>(x)</const> 3846 // <const A extends B>(x) => {} 3847 // 3848 // A syntax error: 3849 // <[]>(x) 3850 // <A[]>(x) 3851 // <>() => {} 3852 // <A>(x) => {} 3853 3854 if p.options.ts.Parse && p.options.jsx.Parse && p.isTSArrowFnJSX() { 3855 p.skipTypeScriptTypeParameters(allowConstModifier) 3856 p.lexer.Expect(js_lexer.TOpenParen) 3857 return p.parseParenExpr(loc, level, parenExprOpts{forceArrowFn: true}) 3858 } 3859 3860 // Print a friendly error message when parsing JSX as JavaScript 3861 if !p.options.jsx.Parse && !p.options.ts.Parse { 3862 var how string 3863 switch logger.API { 3864 case logger.CLIAPI: 3865 how = " You can use \"--loader:.js=jsx\" to do that." 3866 case logger.JSAPI: 3867 how = " You can use \"loader: { '.js': 'jsx' }\" to do that." 3868 case logger.GoAPI: 3869 how = " You can use 'Loader: map[string]api.Loader{\".js\": api.LoaderJSX}' to do that." 3870 } 3871 p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), "The JSX syntax extension is not currently enabled", []logger.MsgData{{ 3872 Text: "The esbuild loader for this file is currently set to \"js\" but it must be set to \"jsx\" to be able to parse JSX syntax." + how}}) 3873 p.options.jsx.Parse = true 3874 } 3875 3876 if p.options.jsx.Parse { 3877 // Use NextInsideJSXElement() instead of Next() so we parse "<<" as "<" 3878 p.lexer.NextInsideJSXElement() 3879 element := p.parseJSXElement(loc) 3880 3881 // The call to parseJSXElement() above doesn't consume the last 3882 // TGreaterThan because the caller knows what Next() function to call. 3883 // Use Next() instead of NextInsideJSXElement() here since the next 3884 // token is an expression. 3885 p.lexer.Next() 3886 return element 3887 } 3888 3889 if p.options.ts.Parse { 3890 // This is either an old-style type cast or a generic lambda function 3891 3892 // TypeScript 4.5 introduced the ".mts" and ".cts" extensions that forbid 3893 // the use of an expression starting with "<" that would be ambiguous 3894 // when the file is in JSX mode. 3895 if p.options.ts.NoAmbiguousLessThan && !p.isTSArrowFnJSX() { 3896 p.log.AddError(&p.tracker, p.lexer.Range(), 3897 "This syntax is not allowed in files with the \".mts\" or \".cts\" extension") 3898 } 3899 3900 // "<T>(x)" 3901 // "<T>(x) => {}" 3902 if result := p.trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking(); result != didNotSkipAnything { 3903 p.lexer.Expect(js_lexer.TOpenParen) 3904 return p.parseParenExpr(loc, level, parenExprOpts{ 3905 forceArrowFn: result == definitelyTypeParameters, 3906 }) 3907 } 3908 3909 // "<T>x" 3910 p.lexer.Next() 3911 p.skipTypeScriptType(js_ast.LLowest) 3912 p.lexer.ExpectGreaterThan(false /* isInsideJSXElement */) 3913 value := p.parsePrefix(level, errors, flags) 3914 return value 3915 } 3916 3917 p.lexer.Unexpected() 3918 return js_ast.Expr{} 3919 3920 case js_lexer.TImport: 3921 p.lexer.Next() 3922 return p.parseImportExpr(loc, level) 3923 3924 default: 3925 p.lexer.Unexpected() 3926 return js_ast.Expr{} 3927 } 3928 } 3929 3930 func (p *parser) parseYieldExpr(loc logger.Loc) js_ast.Expr { 3931 // Parse a yield-from expression, which yields from an iterator 3932 isStar := p.lexer.Token == js_lexer.TAsterisk 3933 if isStar && !p.lexer.HasNewlineBefore { 3934 p.lexer.Next() 3935 } 3936 3937 var valueOrNil js_ast.Expr 3938 3939 // The yield expression only has a value in certain cases 3940 if isStar { 3941 valueOrNil = p.parseExpr(js_ast.LYield) 3942 } else { 3943 switch p.lexer.Token { 3944 case js_lexer.TCloseBrace, js_lexer.TCloseBracket, js_lexer.TCloseParen, 3945 js_lexer.TColon, js_lexer.TComma, js_lexer.TSemicolon: 3946 3947 default: 3948 if !p.lexer.HasNewlineBefore { 3949 valueOrNil = p.parseExpr(js_ast.LYield) 3950 } 3951 } 3952 } 3953 3954 return js_ast.Expr{Loc: loc, Data: &js_ast.EYield{ValueOrNil: valueOrNil, IsStar: isStar}} 3955 } 3956 3957 func (p *parser) willNeedBindingPattern() bool { 3958 switch p.lexer.Token { 3959 case js_lexer.TEquals: 3960 // "[a] = b;" 3961 return true 3962 3963 case js_lexer.TIn: 3964 // "for ([a] in b) {}" 3965 return !p.allowIn 3966 3967 case js_lexer.TIdentifier: 3968 // "for ([a] of b) {}" 3969 return !p.allowIn && p.lexer.IsContextualKeyword("of") 3970 3971 default: 3972 return false 3973 } 3974 } 3975 3976 // Note: The caller has already parsed the "import" keyword 3977 func (p *parser) parseImportExpr(loc logger.Loc, level js_ast.L) js_ast.Expr { 3978 // Parse an "import.meta" expression 3979 if p.lexer.Token == js_lexer.TDot { 3980 p.lexer.Next() 3981 if !p.lexer.IsContextualKeyword("meta") { 3982 p.lexer.ExpectedString("\"meta\"") 3983 } 3984 p.esmImportMeta = logger.Range{Loc: loc, Len: p.lexer.Range().End() - loc.Start} 3985 p.lexer.Next() 3986 return js_ast.Expr{Loc: loc, Data: &js_ast.EImportMeta{RangeLen: p.esmImportMeta.Len}} 3987 } 3988 3989 if level > js_ast.LCall { 3990 r := js_lexer.RangeOfIdentifier(p.source, loc) 3991 p.log.AddError(&p.tracker, r, "Cannot use an \"import\" expression here without parentheses:") 3992 } 3993 3994 // Allow "in" inside call arguments 3995 oldAllowIn := p.allowIn 3996 p.allowIn = true 3997 3998 p.lexer.Expect(js_lexer.TOpenParen) 3999 4000 value := p.parseExpr(js_ast.LComma) 4001 var optionsOrNil js_ast.Expr 4002 4003 if p.lexer.Token == js_lexer.TComma { 4004 // "import('./foo.json', )" 4005 p.lexer.Next() 4006 4007 if p.lexer.Token != js_lexer.TCloseParen { 4008 // "import('./foo.json', { assert: { type: 'json' } })" 4009 optionsOrNil = p.parseExpr(js_ast.LComma) 4010 4011 if p.lexer.Token == js_lexer.TComma { 4012 // "import('./foo.json', { assert: { type: 'json' } }, )" 4013 p.lexer.Next() 4014 } 4015 } 4016 } 4017 4018 closeParenLoc := p.saveExprCommentsHere() 4019 p.lexer.Expect(js_lexer.TCloseParen) 4020 4021 p.allowIn = oldAllowIn 4022 return js_ast.Expr{Loc: loc, Data: &js_ast.EImportCall{ 4023 Expr: value, 4024 OptionsOrNil: optionsOrNil, 4025 CloseParenLoc: closeParenLoc, 4026 }} 4027 } 4028 4029 func (p *parser) parseExprOrBindings(level js_ast.L, errors *deferredErrors) js_ast.Expr { 4030 return p.parseExprCommon(level, errors, 0) 4031 } 4032 4033 func (p *parser) parseExpr(level js_ast.L) js_ast.Expr { 4034 return p.parseExprCommon(level, nil, 0) 4035 } 4036 4037 func (p *parser) parseExprWithFlags(level js_ast.L, flags exprFlag) js_ast.Expr { 4038 return p.parseExprCommon(level, nil, flags) 4039 } 4040 4041 func (p *parser) parseExprCommon(level js_ast.L, errors *deferredErrors, flags exprFlag) js_ast.Expr { 4042 lexerCommentFlags := p.lexer.HasCommentBefore 4043 expr := p.parsePrefix(level, errors, flags) 4044 4045 if (lexerCommentFlags&(js_lexer.PureCommentBefore|js_lexer.NoSideEffectsCommentBefore)) != 0 && !p.options.ignoreDCEAnnotations { 4046 if (lexerCommentFlags & js_lexer.NoSideEffectsCommentBefore) != 0 { 4047 switch e := expr.Data.(type) { 4048 case *js_ast.EArrow: 4049 e.HasNoSideEffectsComment = true 4050 case *js_ast.EFunction: 4051 e.Fn.HasNoSideEffectsComment = true 4052 } 4053 } 4054 4055 // There is no formal spec for "__PURE__" comments but from reverse- 4056 // engineering, it looks like they apply to the next CallExpression or 4057 // NewExpression. So in "/* @__PURE__ */ a().b() + c()" the comment applies 4058 // to the expression "a().b()". 4059 if (lexerCommentFlags&js_lexer.PureCommentBefore) != 0 && level < js_ast.LCall { 4060 expr = p.parseSuffix(expr, js_ast.LCall-1, errors, flags) 4061 switch e := expr.Data.(type) { 4062 case *js_ast.ECall: 4063 e.CanBeUnwrappedIfUnused = true 4064 case *js_ast.ENew: 4065 e.CanBeUnwrappedIfUnused = true 4066 } 4067 } 4068 } 4069 4070 return p.parseSuffix(expr, level, errors, flags) 4071 } 4072 4073 func (p *parser) parseSuffix(left js_ast.Expr, level js_ast.L, errors *deferredErrors, flags exprFlag) js_ast.Expr { 4074 optionalChain := js_ast.OptionalChainNone 4075 4076 for { 4077 if p.lexer.Loc() == p.afterArrowBodyLoc { 4078 for { 4079 switch p.lexer.Token { 4080 case js_lexer.TComma: 4081 if level >= js_ast.LComma { 4082 return left 4083 } 4084 p.lexer.Next() 4085 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpComma, Left: left, Right: p.parseExpr(js_ast.LComma)}} 4086 4087 default: 4088 return left 4089 } 4090 } 4091 } 4092 4093 // Stop now if this token is forbidden to follow a TypeScript "as" cast 4094 if p.lexer.Loc() == p.forbidSuffixAfterAsLoc { 4095 return left 4096 } 4097 4098 // Reset the optional chain flag by default. That way we won't accidentally 4099 // treat "c.d" as OptionalChainContinue in "a?.b + c.d". 4100 oldOptionalChain := optionalChain 4101 optionalChain = js_ast.OptionalChainNone 4102 4103 switch p.lexer.Token { 4104 case js_lexer.TDot: 4105 p.lexer.Next() 4106 4107 if p.lexer.Token == js_lexer.TPrivateIdentifier && p.allowPrivateIdentifiers { 4108 // "a.#b" 4109 // "a?.b.#c" 4110 if _, ok := left.Data.(*js_ast.ESuper); ok { 4111 p.lexer.Expected(js_lexer.TIdentifier) 4112 } 4113 name := p.lexer.Identifier 4114 nameLoc := p.lexer.Loc() 4115 p.reportPrivateNameUsage(name.String) 4116 p.lexer.Next() 4117 ref := p.storeNameInRef(name) 4118 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EIndex{ 4119 Target: left, 4120 Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EPrivateIdentifier{Ref: ref}}, 4121 OptionalChain: oldOptionalChain, 4122 }} 4123 } else { 4124 // "a.b" 4125 // "a?.b.c" 4126 if !p.lexer.IsIdentifierOrKeyword() { 4127 p.lexer.Expect(js_lexer.TIdentifier) 4128 } 4129 name := p.lexer.Identifier 4130 nameLoc := p.lexer.Loc() 4131 p.lexer.Next() 4132 left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, oldOptionalChain, wasOriginallyDot)} 4133 } 4134 4135 optionalChain = oldOptionalChain 4136 4137 case js_lexer.TQuestionDot: 4138 p.lexer.Next() 4139 optionalStart := js_ast.OptionalChainStart 4140 4141 // Remove unnecessary optional chains 4142 if p.options.minifySyntax { 4143 if isNullOrUndefined, _, ok := js_ast.ToNullOrUndefinedWithSideEffects(left.Data); ok && !isNullOrUndefined { 4144 optionalStart = js_ast.OptionalChainNone 4145 } 4146 } 4147 4148 switch p.lexer.Token { 4149 case js_lexer.TOpenBracket: 4150 // "a?.[b]" 4151 p.lexer.Next() 4152 4153 // Allow "in" inside the brackets 4154 oldAllowIn := p.allowIn 4155 p.allowIn = true 4156 4157 index := p.parseExpr(js_ast.LLowest) 4158 4159 p.allowIn = oldAllowIn 4160 4161 closeBracketLoc := p.saveExprCommentsHere() 4162 p.lexer.Expect(js_lexer.TCloseBracket) 4163 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EIndex{ 4164 Target: left, 4165 Index: index, 4166 OptionalChain: optionalStart, 4167 CloseBracketLoc: closeBracketLoc, 4168 }} 4169 4170 case js_lexer.TOpenParen: 4171 // "a?.()" 4172 if level >= js_ast.LCall { 4173 return left 4174 } 4175 kind := js_ast.NormalCall 4176 if js_ast.IsPropertyAccess(left) { 4177 kind = js_ast.TargetWasOriginallyPropertyAccess 4178 } 4179 args, closeParenLoc, isMultiLine := p.parseCallArgs() 4180 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.ECall{ 4181 Target: left, 4182 Args: args, 4183 CloseParenLoc: closeParenLoc, 4184 OptionalChain: optionalStart, 4185 IsMultiLine: isMultiLine, 4186 Kind: kind, 4187 }} 4188 4189 case js_lexer.TLessThan, js_lexer.TLessThanLessThan: 4190 // "a?.<T>()" 4191 // "a?.<<T>() => T>()" 4192 if !p.options.ts.Parse { 4193 p.lexer.Expected(js_lexer.TIdentifier) 4194 } 4195 p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{}) 4196 if p.lexer.Token != js_lexer.TOpenParen { 4197 p.lexer.Expected(js_lexer.TOpenParen) 4198 } 4199 if level >= js_ast.LCall { 4200 return left 4201 } 4202 kind := js_ast.NormalCall 4203 if js_ast.IsPropertyAccess(left) { 4204 kind = js_ast.TargetWasOriginallyPropertyAccess 4205 } 4206 args, closeParenLoc, isMultiLine := p.parseCallArgs() 4207 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.ECall{ 4208 Target: left, 4209 Args: args, 4210 CloseParenLoc: closeParenLoc, 4211 OptionalChain: optionalStart, 4212 IsMultiLine: isMultiLine, 4213 Kind: kind, 4214 }} 4215 4216 default: 4217 if p.lexer.Token == js_lexer.TPrivateIdentifier && p.allowPrivateIdentifiers { 4218 // "a?.#b" 4219 name := p.lexer.Identifier 4220 nameLoc := p.lexer.Loc() 4221 p.reportPrivateNameUsage(name.String) 4222 p.lexer.Next() 4223 ref := p.storeNameInRef(name) 4224 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EIndex{ 4225 Target: left, 4226 Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EPrivateIdentifier{Ref: ref}}, 4227 OptionalChain: optionalStart, 4228 }} 4229 } else { 4230 // "a?.b" 4231 if !p.lexer.IsIdentifierOrKeyword() { 4232 p.lexer.Expect(js_lexer.TIdentifier) 4233 } 4234 name := p.lexer.Identifier 4235 nameLoc := p.lexer.Loc() 4236 p.lexer.Next() 4237 left = js_ast.Expr{Loc: left.Loc, Data: p.dotOrMangledPropParse(left, name, nameLoc, optionalStart, wasOriginallyDot)} 4238 } 4239 } 4240 4241 // Only continue if we have started 4242 if optionalStart == js_ast.OptionalChainStart { 4243 optionalChain = js_ast.OptionalChainContinue 4244 } 4245 4246 case js_lexer.TNoSubstitutionTemplateLiteral: 4247 if oldOptionalChain != js_ast.OptionalChainNone { 4248 p.log.AddError(&p.tracker, p.lexer.Range(), "Template literals cannot have an optional chain as a tag") 4249 } 4250 headLoc := p.lexer.Loc() 4251 headCooked, headRaw := p.lexer.CookedAndRawTemplateContents() 4252 p.lexer.Next() 4253 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.ETemplate{ 4254 TagOrNil: left, 4255 HeadLoc: headLoc, 4256 HeadCooked: headCooked, 4257 HeadRaw: headRaw, 4258 TagWasOriginallyPropertyAccess: js_ast.IsPropertyAccess(left), 4259 }} 4260 4261 case js_lexer.TTemplateHead: 4262 if oldOptionalChain != js_ast.OptionalChainNone { 4263 p.log.AddError(&p.tracker, p.lexer.Range(), "Template literals cannot have an optional chain as a tag") 4264 } 4265 headLoc := p.lexer.Loc() 4266 headCooked, headRaw := p.lexer.CookedAndRawTemplateContents() 4267 parts, _ := p.parseTemplateParts(true /* includeRaw */) 4268 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.ETemplate{ 4269 TagOrNil: left, 4270 HeadLoc: headLoc, 4271 HeadCooked: headCooked, 4272 HeadRaw: headRaw, 4273 Parts: parts, 4274 TagWasOriginallyPropertyAccess: js_ast.IsPropertyAccess(left), 4275 }} 4276 4277 case js_lexer.TOpenBracket: 4278 // When parsing a decorator, ignore EIndex expressions since they may be 4279 // part of a computed property: 4280 // 4281 // class Foo { 4282 // @foo ['computed']() {} 4283 // } 4284 // 4285 // This matches the behavior of the TypeScript compiler. 4286 if (flags & exprFlagDecorator) != 0 { 4287 return left 4288 } 4289 4290 p.lexer.Next() 4291 4292 // Allow "in" inside the brackets 4293 oldAllowIn := p.allowIn 4294 p.allowIn = true 4295 4296 index := p.parseExpr(js_ast.LLowest) 4297 4298 p.allowIn = oldAllowIn 4299 4300 closeBracketLoc := p.saveExprCommentsHere() 4301 p.lexer.Expect(js_lexer.TCloseBracket) 4302 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EIndex{ 4303 Target: left, 4304 Index: index, 4305 OptionalChain: oldOptionalChain, 4306 CloseBracketLoc: closeBracketLoc, 4307 }} 4308 optionalChain = oldOptionalChain 4309 4310 case js_lexer.TOpenParen: 4311 if level >= js_ast.LCall { 4312 return left 4313 } 4314 kind := js_ast.NormalCall 4315 if js_ast.IsPropertyAccess(left) { 4316 kind = js_ast.TargetWasOriginallyPropertyAccess 4317 } 4318 args, closeParenLoc, isMultiLine := p.parseCallArgs() 4319 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.ECall{ 4320 Target: left, 4321 Args: args, 4322 CloseParenLoc: closeParenLoc, 4323 OptionalChain: oldOptionalChain, 4324 IsMultiLine: isMultiLine, 4325 Kind: kind, 4326 }} 4327 optionalChain = oldOptionalChain 4328 4329 case js_lexer.TQuestion: 4330 if level >= js_ast.LConditional { 4331 return left 4332 } 4333 p.lexer.Next() 4334 4335 // Stop now if we're parsing one of these: 4336 // "(a?) => {}" 4337 // "(a?: b) => {}" 4338 // "(a?, b?) => {}" 4339 if p.options.ts.Parse && left.Loc == p.latestArrowArgLoc && (p.lexer.Token == js_lexer.TColon || 4340 p.lexer.Token == js_lexer.TCloseParen || p.lexer.Token == js_lexer.TComma) { 4341 if errors == nil { 4342 p.lexer.Unexpected() 4343 } 4344 errors.invalidExprAfterQuestion = p.lexer.Range() 4345 return left 4346 } 4347 4348 // Allow "in" in between "?" and ":" 4349 oldAllowIn := p.allowIn 4350 p.allowIn = true 4351 4352 yes := p.parseExpr(js_ast.LComma) 4353 4354 p.allowIn = oldAllowIn 4355 4356 p.lexer.Expect(js_lexer.TColon) 4357 no := p.parseExpr(js_ast.LComma) 4358 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EIf{Test: left, Yes: yes, No: no}} 4359 4360 case js_lexer.TExclamation: 4361 // Skip over TypeScript non-null assertions 4362 if p.lexer.HasNewlineBefore { 4363 return left 4364 } 4365 if !p.options.ts.Parse { 4366 p.lexer.Unexpected() 4367 } 4368 p.lexer.Next() 4369 optionalChain = oldOptionalChain 4370 4371 case js_lexer.TMinusMinus: 4372 if p.lexer.HasNewlineBefore || level >= js_ast.LPostfix { 4373 return left 4374 } 4375 p.lexer.Next() 4376 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EUnary{Op: js_ast.UnOpPostDec, Value: left}} 4377 4378 case js_lexer.TPlusPlus: 4379 if p.lexer.HasNewlineBefore || level >= js_ast.LPostfix { 4380 return left 4381 } 4382 p.lexer.Next() 4383 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EUnary{Op: js_ast.UnOpPostInc, Value: left}} 4384 4385 case js_lexer.TComma: 4386 if level >= js_ast.LComma { 4387 return left 4388 } 4389 p.lexer.Next() 4390 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpComma, Left: left, Right: p.parseExpr(js_ast.LComma)}} 4391 4392 case js_lexer.TPlus: 4393 if level >= js_ast.LAdd { 4394 return left 4395 } 4396 p.lexer.Next() 4397 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpAdd, Left: left, Right: p.parseExpr(js_ast.LAdd)}} 4398 4399 case js_lexer.TPlusEquals: 4400 if level >= js_ast.LAssign { 4401 return left 4402 } 4403 p.lexer.Next() 4404 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpAddAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4405 4406 case js_lexer.TMinus: 4407 if level >= js_ast.LAdd { 4408 return left 4409 } 4410 p.lexer.Next() 4411 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpSub, Left: left, Right: p.parseExpr(js_ast.LAdd)}} 4412 4413 case js_lexer.TMinusEquals: 4414 if level >= js_ast.LAssign { 4415 return left 4416 } 4417 p.lexer.Next() 4418 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpSubAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4419 4420 case js_lexer.TAsterisk: 4421 if level >= js_ast.LMultiply { 4422 return left 4423 } 4424 p.lexer.Next() 4425 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpMul, Left: left, Right: p.parseExpr(js_ast.LMultiply)}} 4426 4427 case js_lexer.TAsteriskAsterisk: 4428 if level >= js_ast.LExponentiation { 4429 return left 4430 } 4431 p.lexer.Next() 4432 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpPow, Left: left, Right: p.parseExpr(js_ast.LExponentiation - 1)}} 4433 4434 case js_lexer.TAsteriskAsteriskEquals: 4435 if level >= js_ast.LAssign { 4436 return left 4437 } 4438 p.lexer.Next() 4439 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpPowAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4440 4441 case js_lexer.TAsteriskEquals: 4442 if level >= js_ast.LAssign { 4443 return left 4444 } 4445 p.lexer.Next() 4446 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpMulAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4447 4448 case js_lexer.TPercent: 4449 if level >= js_ast.LMultiply { 4450 return left 4451 } 4452 p.lexer.Next() 4453 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpRem, Left: left, Right: p.parseExpr(js_ast.LMultiply)}} 4454 4455 case js_lexer.TPercentEquals: 4456 if level >= js_ast.LAssign { 4457 return left 4458 } 4459 p.lexer.Next() 4460 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpRemAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4461 4462 case js_lexer.TSlash: 4463 if level >= js_ast.LMultiply { 4464 return left 4465 } 4466 p.lexer.Next() 4467 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpDiv, Left: left, Right: p.parseExpr(js_ast.LMultiply)}} 4468 4469 case js_lexer.TSlashEquals: 4470 if level >= js_ast.LAssign { 4471 return left 4472 } 4473 p.lexer.Next() 4474 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpDivAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4475 4476 case js_lexer.TEqualsEquals: 4477 if level >= js_ast.LEquals { 4478 return left 4479 } 4480 p.lexer.Next() 4481 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLooseEq, Left: left, Right: p.parseExpr(js_ast.LEquals)}} 4482 4483 case js_lexer.TExclamationEquals: 4484 if level >= js_ast.LEquals { 4485 return left 4486 } 4487 p.lexer.Next() 4488 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLooseNe, Left: left, Right: p.parseExpr(js_ast.LEquals)}} 4489 4490 case js_lexer.TEqualsEqualsEquals: 4491 if level >= js_ast.LEquals { 4492 return left 4493 } 4494 p.lexer.Next() 4495 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpStrictEq, Left: left, Right: p.parseExpr(js_ast.LEquals)}} 4496 4497 case js_lexer.TExclamationEqualsEquals: 4498 if level >= js_ast.LEquals { 4499 return left 4500 } 4501 p.lexer.Next() 4502 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpStrictNe, Left: left, Right: p.parseExpr(js_ast.LEquals)}} 4503 4504 case js_lexer.TLessThan: 4505 // TypeScript allows type arguments to be specified with angle brackets 4506 // inside an expression. Unlike in other languages, this unfortunately 4507 // appears to require backtracking to parse. 4508 if p.options.ts.Parse && p.trySkipTypeArgumentsInExpressionWithBacktracking() { 4509 optionalChain = oldOptionalChain 4510 continue 4511 } 4512 4513 if level >= js_ast.LCompare { 4514 return left 4515 } 4516 p.lexer.Next() 4517 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLt, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4518 4519 case js_lexer.TLessThanEquals: 4520 if level >= js_ast.LCompare { 4521 return left 4522 } 4523 p.lexer.Next() 4524 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLe, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4525 4526 case js_lexer.TGreaterThan: 4527 if level >= js_ast.LCompare { 4528 return left 4529 } 4530 p.lexer.Next() 4531 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpGt, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4532 4533 case js_lexer.TGreaterThanEquals: 4534 if level >= js_ast.LCompare { 4535 return left 4536 } 4537 p.lexer.Next() 4538 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpGe, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4539 4540 case js_lexer.TLessThanLessThan: 4541 // TypeScript allows type arguments to be specified with angle brackets 4542 // inside an expression. Unlike in other languages, this unfortunately 4543 // appears to require backtracking to parse. 4544 if p.options.ts.Parse && p.trySkipTypeArgumentsInExpressionWithBacktracking() { 4545 optionalChain = oldOptionalChain 4546 continue 4547 } 4548 4549 if level >= js_ast.LShift { 4550 return left 4551 } 4552 p.lexer.Next() 4553 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpShl, Left: left, Right: p.parseExpr(js_ast.LShift)}} 4554 4555 case js_lexer.TLessThanLessThanEquals: 4556 if level >= js_ast.LAssign { 4557 return left 4558 } 4559 p.lexer.Next() 4560 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpShlAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4561 4562 case js_lexer.TGreaterThanGreaterThan: 4563 if level >= js_ast.LShift { 4564 return left 4565 } 4566 p.lexer.Next() 4567 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpShr, Left: left, Right: p.parseExpr(js_ast.LShift)}} 4568 4569 case js_lexer.TGreaterThanGreaterThanEquals: 4570 if level >= js_ast.LAssign { 4571 return left 4572 } 4573 p.lexer.Next() 4574 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpShrAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4575 4576 case js_lexer.TGreaterThanGreaterThanGreaterThan: 4577 if level >= js_ast.LShift { 4578 return left 4579 } 4580 p.lexer.Next() 4581 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpUShr, Left: left, Right: p.parseExpr(js_ast.LShift)}} 4582 4583 case js_lexer.TGreaterThanGreaterThanGreaterThanEquals: 4584 if level >= js_ast.LAssign { 4585 return left 4586 } 4587 p.lexer.Next() 4588 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpUShrAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4589 4590 case js_lexer.TQuestionQuestion: 4591 if level >= js_ast.LNullishCoalescing { 4592 return left 4593 } 4594 p.lexer.Next() 4595 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpNullishCoalescing, Left: left, Right: p.parseExpr(js_ast.LNullishCoalescing)}} 4596 4597 case js_lexer.TQuestionQuestionEquals: 4598 if level >= js_ast.LAssign { 4599 return left 4600 } 4601 p.lexer.Next() 4602 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpNullishCoalescingAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4603 4604 case js_lexer.TBarBar: 4605 if level >= js_ast.LLogicalOr { 4606 return left 4607 } 4608 4609 // Prevent "||" inside "??" from the right 4610 if level == js_ast.LNullishCoalescing { 4611 p.logNullishCoalescingErrorPrecedenceError("||") 4612 } 4613 4614 p.lexer.Next() 4615 right := p.parseExpr(js_ast.LLogicalOr) 4616 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLogicalOr, Left: left, Right: right}} 4617 4618 // Prevent "||" inside "??" from the left 4619 if level < js_ast.LNullishCoalescing { 4620 left = p.parseSuffix(left, js_ast.LNullishCoalescing+1, nil, flags) 4621 if p.lexer.Token == js_lexer.TQuestionQuestion { 4622 p.logNullishCoalescingErrorPrecedenceError("||") 4623 } 4624 } 4625 4626 case js_lexer.TBarBarEquals: 4627 if level >= js_ast.LAssign { 4628 return left 4629 } 4630 p.lexer.Next() 4631 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLogicalOrAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4632 4633 case js_lexer.TAmpersandAmpersand: 4634 if level >= js_ast.LLogicalAnd { 4635 return left 4636 } 4637 4638 // Prevent "&&" inside "??" from the right 4639 if level == js_ast.LNullishCoalescing { 4640 p.logNullishCoalescingErrorPrecedenceError("&&") 4641 } 4642 4643 p.lexer.Next() 4644 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLogicalAnd, Left: left, Right: p.parseExpr(js_ast.LLogicalAnd)}} 4645 4646 // Prevent "&&" inside "??" from the left 4647 if level < js_ast.LNullishCoalescing { 4648 left = p.parseSuffix(left, js_ast.LNullishCoalescing+1, nil, flags) 4649 if p.lexer.Token == js_lexer.TQuestionQuestion { 4650 p.logNullishCoalescingErrorPrecedenceError("&&") 4651 } 4652 } 4653 4654 case js_lexer.TAmpersandAmpersandEquals: 4655 if level >= js_ast.LAssign { 4656 return left 4657 } 4658 p.lexer.Next() 4659 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpLogicalAndAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4660 4661 case js_lexer.TBar: 4662 if level >= js_ast.LBitwiseOr { 4663 return left 4664 } 4665 p.lexer.Next() 4666 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseOr, Left: left, Right: p.parseExpr(js_ast.LBitwiseOr)}} 4667 4668 case js_lexer.TBarEquals: 4669 if level >= js_ast.LAssign { 4670 return left 4671 } 4672 p.lexer.Next() 4673 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseOrAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4674 4675 case js_lexer.TAmpersand: 4676 if level >= js_ast.LBitwiseAnd { 4677 return left 4678 } 4679 p.lexer.Next() 4680 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseAnd, Left: left, Right: p.parseExpr(js_ast.LBitwiseAnd)}} 4681 4682 case js_lexer.TAmpersandEquals: 4683 if level >= js_ast.LAssign { 4684 return left 4685 } 4686 p.lexer.Next() 4687 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseAndAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4688 4689 case js_lexer.TCaret: 4690 if level >= js_ast.LBitwiseXor { 4691 return left 4692 } 4693 p.lexer.Next() 4694 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseXor, Left: left, Right: p.parseExpr(js_ast.LBitwiseXor)}} 4695 4696 case js_lexer.TCaretEquals: 4697 if level >= js_ast.LAssign { 4698 return left 4699 } 4700 p.lexer.Next() 4701 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpBitwiseXorAssign, Left: left, Right: p.parseExpr(js_ast.LAssign - 1)}} 4702 4703 case js_lexer.TEquals: 4704 if level >= js_ast.LAssign { 4705 return left 4706 } 4707 p.lexer.Next() 4708 left = js_ast.Assign(left, p.parseExpr(js_ast.LAssign-1)) 4709 4710 case js_lexer.TIn: 4711 if level >= js_ast.LCompare || !p.allowIn { 4712 return left 4713 } 4714 4715 // Warn about "!a in b" instead of "!(a in b)" 4716 kind := logger.Warning 4717 if p.suppressWarningsAboutWeirdCode { 4718 kind = logger.Debug 4719 } 4720 if e, ok := left.Data.(*js_ast.EUnary); ok && e.Op == js_ast.UnOpNot { 4721 r := logger.Range{Loc: left.Loc, Len: p.source.LocBeforeWhitespace(p.lexer.Loc()).Start - left.Loc.Start} 4722 data := p.tracker.MsgData(r, "Suspicious use of the \"!\" operator inside the \"in\" operator") 4723 data.Location.Suggestion = fmt.Sprintf("(%s)", p.source.TextForRange(r)) 4724 p.log.AddMsgID(logger.MsgID_JS_SuspiciousBooleanNot, logger.Msg{ 4725 Kind: kind, 4726 Data: data, 4727 Notes: []logger.MsgData{{Text: "The code \"!x in y\" is parsed as \"(!x) in y\". " + 4728 "You need to insert parentheses to get \"!(x in y)\" instead."}}, 4729 }) 4730 } 4731 4732 p.lexer.Next() 4733 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpIn, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4734 4735 case js_lexer.TInstanceof: 4736 if level >= js_ast.LCompare { 4737 return left 4738 } 4739 4740 // Warn about "!a instanceof b" instead of "!(a instanceof b)". Here's an 4741 // example of code with this problem: https://github.com/mrdoob/three.js/pull/11182. 4742 kind := logger.Warning 4743 if p.suppressWarningsAboutWeirdCode { 4744 kind = logger.Debug 4745 } 4746 if e, ok := left.Data.(*js_ast.EUnary); ok && e.Op == js_ast.UnOpNot { 4747 r := logger.Range{Loc: left.Loc, Len: p.source.LocBeforeWhitespace(p.lexer.Loc()).Start - left.Loc.Start} 4748 data := p.tracker.MsgData(r, "Suspicious use of the \"!\" operator inside the \"instanceof\" operator") 4749 data.Location.Suggestion = fmt.Sprintf("(%s)", p.source.TextForRange(r)) 4750 p.log.AddMsgID(logger.MsgID_JS_SuspiciousBooleanNot, logger.Msg{ 4751 Kind: kind, 4752 Data: data, 4753 Notes: []logger.MsgData{{Text: "The code \"!x instanceof y\" is parsed as \"(!x) instanceof y\". " + 4754 "You need to insert parentheses to get \"!(x instanceof y)\" instead."}}, 4755 }) 4756 } 4757 4758 p.lexer.Next() 4759 left = js_ast.Expr{Loc: left.Loc, Data: &js_ast.EBinary{Op: js_ast.BinOpInstanceof, Left: left, Right: p.parseExpr(js_ast.LCompare)}} 4760 4761 default: 4762 // Handle the TypeScript "as"/"satisfies" operator 4763 if p.options.ts.Parse && level < js_ast.LCompare && !p.lexer.HasNewlineBefore && (p.lexer.IsContextualKeyword("as") || p.lexer.IsContextualKeyword("satisfies")) { 4764 p.lexer.Next() 4765 p.skipTypeScriptType(js_ast.LLowest) 4766 4767 // These tokens are not allowed to follow a cast expression. This isn't 4768 // an outright error because it may be on a new line, in which case it's 4769 // the start of a new expression when it's after a cast: 4770 // 4771 // x = y as z 4772 // (something); 4773 // 4774 switch p.lexer.Token { 4775 case js_lexer.TPlusPlus, js_lexer.TMinusMinus, js_lexer.TNoSubstitutionTemplateLiteral, 4776 js_lexer.TTemplateHead, js_lexer.TOpenParen, js_lexer.TOpenBracket, js_lexer.TQuestionDot: 4777 p.forbidSuffixAfterAsLoc = p.lexer.Loc() 4778 return left 4779 } 4780 if p.lexer.Token.IsAssign() { 4781 p.forbidSuffixAfterAsLoc = p.lexer.Loc() 4782 return left 4783 } 4784 continue 4785 } 4786 4787 return left 4788 } 4789 } 4790 } 4791 4792 func (p *parser) parseExprOrLetOrUsingStmt(opts parseStmtOpts) (js_ast.Expr, js_ast.Stmt, []js_ast.Decl) { 4793 couldBeLet := false 4794 couldBeUsing := false 4795 couldBeAwaitUsing := false 4796 tokenRange := p.lexer.Range() 4797 4798 if p.lexer.Token == js_lexer.TIdentifier { 4799 raw := p.lexer.Raw() 4800 couldBeLet = raw == "let" 4801 couldBeUsing = raw == "using" 4802 couldBeAwaitUsing = raw == "await" && p.fnOrArrowDataParse.await == allowExpr 4803 } 4804 4805 if !couldBeLet && !couldBeUsing && !couldBeAwaitUsing { 4806 var flags exprFlag 4807 if opts.isForLoopInit { 4808 flags |= exprFlagForLoopInit 4809 } 4810 if opts.isForAwaitLoopInit { 4811 flags |= exprFlagForAwaitLoopInit 4812 } 4813 return p.parseExprCommon(js_ast.LLowest, nil, flags), js_ast.Stmt{}, nil 4814 } 4815 4816 name := p.lexer.Identifier 4817 p.lexer.Next() 4818 4819 if couldBeLet { 4820 isLet := opts.isExport 4821 switch p.lexer.Token { 4822 case js_lexer.TIdentifier, js_lexer.TOpenBracket, js_lexer.TOpenBrace: 4823 if opts.lexicalDecl == lexicalDeclAllowAll || !p.lexer.HasNewlineBefore || p.lexer.Token == js_lexer.TOpenBracket { 4824 isLet = true 4825 } 4826 } 4827 if isLet { 4828 // Handle a "let" declaration 4829 if opts.lexicalDecl != lexicalDeclAllowAll { 4830 p.forbidLexicalDecl(tokenRange.Loc) 4831 } 4832 p.markSyntaxFeature(compat.ConstAndLet, tokenRange) 4833 decls := p.parseAndDeclareDecls(ast.SymbolOther, opts) 4834 return js_ast.Expr{}, js_ast.Stmt{Loc: tokenRange.Loc, Data: &js_ast.SLocal{ 4835 Kind: js_ast.LocalLet, 4836 Decls: decls, 4837 IsExport: opts.isExport, 4838 }}, decls 4839 } 4840 } else if couldBeUsing && p.lexer.Token == js_lexer.TIdentifier && !p.lexer.HasNewlineBefore && (!opts.isForLoopInit || p.lexer.Raw() != "of") { 4841 // Handle a "using" declaration 4842 if opts.lexicalDecl != lexicalDeclAllowAll { 4843 p.forbidLexicalDecl(tokenRange.Loc) 4844 } 4845 opts.isUsingStmt = true 4846 decls := p.parseAndDeclareDecls(ast.SymbolConst, opts) 4847 if !opts.isForLoopInit { 4848 p.requireInitializers(js_ast.LocalUsing, decls) 4849 } 4850 return js_ast.Expr{}, js_ast.Stmt{Loc: tokenRange.Loc, Data: &js_ast.SLocal{ 4851 Kind: js_ast.LocalUsing, 4852 Decls: decls, 4853 IsExport: opts.isExport, 4854 }}, decls 4855 } else if couldBeAwaitUsing { 4856 // Handle an "await using" declaration 4857 if p.fnOrArrowDataParse.isTopLevel { 4858 p.topLevelAwaitKeyword = tokenRange 4859 } 4860 var value js_ast.Expr 4861 if p.lexer.Token == js_lexer.TIdentifier && p.lexer.Raw() == "using" { 4862 usingLoc := p.saveExprCommentsHere() 4863 usingRange := p.lexer.Range() 4864 p.lexer.Next() 4865 if p.lexer.Token == js_lexer.TIdentifier && !p.lexer.HasNewlineBefore { 4866 // It's an "await using" declaration if we get here 4867 if opts.lexicalDecl != lexicalDeclAllowAll { 4868 p.forbidLexicalDecl(usingRange.Loc) 4869 } 4870 opts.isUsingStmt = true 4871 decls := p.parseAndDeclareDecls(ast.SymbolConst, opts) 4872 if !opts.isForLoopInit { 4873 p.requireInitializers(js_ast.LocalAwaitUsing, decls) 4874 } 4875 return js_ast.Expr{}, js_ast.Stmt{Loc: tokenRange.Loc, Data: &js_ast.SLocal{ 4876 Kind: js_ast.LocalAwaitUsing, 4877 Decls: decls, 4878 IsExport: opts.isExport, 4879 }}, decls 4880 } 4881 value = js_ast.Expr{Loc: usingLoc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(js_lexer.MaybeSubstring{String: "using"})}} 4882 } else { 4883 value = p.parseExpr(js_ast.LPrefix) 4884 } 4885 if p.lexer.Token == js_lexer.TAsteriskAsterisk { 4886 p.lexer.Unexpected() 4887 } 4888 value = p.parseSuffix(value, js_ast.LPrefix, nil, 0) 4889 expr := js_ast.Expr{Loc: tokenRange.Loc, Data: &js_ast.EAwait{Value: value}} 4890 return p.parseSuffix(expr, js_ast.LLowest, nil, 0), js_ast.Stmt{}, nil 4891 } 4892 4893 // Parse the remainder of this expression that starts with an identifier 4894 expr := js_ast.Expr{Loc: tokenRange.Loc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(name)}} 4895 return p.parseSuffix(expr, js_ast.LLowest, nil, 0), js_ast.Stmt{}, nil 4896 } 4897 4898 func (p *parser) parseCallArgs() (args []js_ast.Expr, closeParenLoc logger.Loc, isMultiLine bool) { 4899 // Allow "in" inside call arguments 4900 oldAllowIn := p.allowIn 4901 p.allowIn = true 4902 4903 p.lexer.Expect(js_lexer.TOpenParen) 4904 4905 for p.lexer.Token != js_lexer.TCloseParen { 4906 if p.lexer.HasNewlineBefore { 4907 isMultiLine = true 4908 } 4909 loc := p.lexer.Loc() 4910 isSpread := p.lexer.Token == js_lexer.TDotDotDot 4911 if isSpread { 4912 p.markSyntaxFeature(compat.RestArgument, p.lexer.Range()) 4913 p.lexer.Next() 4914 } 4915 arg := p.parseExpr(js_ast.LComma) 4916 if isSpread { 4917 arg = js_ast.Expr{Loc: loc, Data: &js_ast.ESpread{Value: arg}} 4918 } 4919 args = append(args, arg) 4920 if p.lexer.Token != js_lexer.TComma { 4921 break 4922 } 4923 if p.lexer.HasNewlineBefore { 4924 isMultiLine = true 4925 } 4926 p.lexer.Next() 4927 } 4928 4929 if p.lexer.HasNewlineBefore { 4930 isMultiLine = true 4931 } 4932 closeParenLoc = p.saveExprCommentsHere() 4933 p.lexer.Expect(js_lexer.TCloseParen) 4934 p.allowIn = oldAllowIn 4935 return 4936 } 4937 4938 func (p *parser) parseJSXNamespacedName() (logger.Range, js_lexer.MaybeSubstring) { 4939 nameRange := p.lexer.Range() 4940 name := p.lexer.Identifier 4941 p.lexer.ExpectInsideJSXElement(js_lexer.TIdentifier) 4942 4943 // Parse JSX namespaces. These are not supported by React or TypeScript 4944 // but someone using JSX syntax in more obscure ways may find a use for 4945 // them. A namespaced name is just always turned into a string so you 4946 // can't use this feature to reference JavaScript identifiers. 4947 if p.lexer.Token == js_lexer.TColon { 4948 // Parse the colon 4949 nameRange.Len = p.lexer.Range().End() - nameRange.Loc.Start 4950 ns := name.String + ":" 4951 p.lexer.NextInsideJSXElement() 4952 4953 // Parse the second identifier 4954 if p.lexer.Token == js_lexer.TIdentifier { 4955 nameRange.Len = p.lexer.Range().End() - nameRange.Loc.Start 4956 ns += p.lexer.Identifier.String 4957 p.lexer.NextInsideJSXElement() 4958 } else { 4959 p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: nameRange.End()}}, 4960 fmt.Sprintf("Expected identifier after %q in namespaced JSX name", ns)) 4961 panic(js_lexer.LexerPanic{}) 4962 } 4963 return nameRange, js_lexer.MaybeSubstring{String: ns} 4964 } 4965 4966 return nameRange, name 4967 } 4968 4969 func tagOrFragmentHelpText(tag string) string { 4970 if tag == "" { 4971 return "fragment tag" 4972 } 4973 return fmt.Sprintf("%q tag", tag) 4974 } 4975 4976 func (p *parser) parseJSXTag() (logger.Range, string, js_ast.Expr) { 4977 loc := p.lexer.Loc() 4978 4979 // A missing tag is a fragment 4980 if p.lexer.Token == js_lexer.TGreaterThan { 4981 return logger.Range{Loc: loc, Len: 0}, "", js_ast.Expr{} 4982 } 4983 4984 // The tag is an identifier 4985 tagRange, tagName := p.parseJSXNamespacedName() 4986 4987 // Certain identifiers are strings 4988 if strings.ContainsAny(tagName.String, "-:") || (p.lexer.Token != js_lexer.TDot && tagName.String[0] >= 'a' && tagName.String[0] <= 'z') { 4989 return tagRange, tagName.String, js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(tagName.String)}} 4990 } 4991 4992 // Otherwise, this is an identifier 4993 tag := js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(tagName)}} 4994 4995 // Parse a member expression chain 4996 chain := tagName.String 4997 for p.lexer.Token == js_lexer.TDot { 4998 p.lexer.NextInsideJSXElement() 4999 memberRange := p.lexer.Range() 5000 member := p.lexer.Identifier 5001 p.lexer.ExpectInsideJSXElement(js_lexer.TIdentifier) 5002 5003 // Dashes are not allowed in member expression chains 5004 index := strings.IndexByte(member.String, '-') 5005 if index >= 0 { 5006 p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: memberRange.Loc.Start + int32(index)}}, 5007 "Unexpected \"-\"") 5008 panic(js_lexer.LexerPanic{}) 5009 } 5010 5011 chain += "." + member.String 5012 tag = js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropParse(tag, member, memberRange.Loc, js_ast.OptionalChainNone, wasOriginallyDot)} 5013 tagRange.Len = memberRange.Loc.Start + memberRange.Len - tagRange.Loc.Start 5014 } 5015 5016 return tagRange, chain, tag 5017 } 5018 5019 func (p *parser) parseJSXElement(loc logger.Loc) js_ast.Expr { 5020 // Keep track of the location of the first JSX element for error messages 5021 if p.firstJSXElementLoc.Start == -1 { 5022 p.firstJSXElementLoc = loc 5023 } 5024 5025 // Parse the tag 5026 startRange, startText, startTagOrNil := p.parseJSXTag() 5027 5028 // The tag may have TypeScript type arguments: "<Foo<T>/>" 5029 if p.options.ts.Parse { 5030 // Pass a flag to the type argument skipper because we need to call 5031 // js_lexer.NextInsideJSXElement() after we hit the closing ">". The next 5032 // token after the ">" might be an attribute name with a dash in it 5033 // like this: "<Foo<T> data-disabled/>" 5034 p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{isInsideJSXElement: true}) 5035 } 5036 5037 // Parse attributes 5038 var previousStringWithBackslashLoc logger.Loc 5039 properties := []js_ast.Property{} 5040 isSingleLine := true 5041 if startTagOrNil.Data != nil { 5042 parseAttributes: 5043 for { 5044 if p.lexer.HasNewlineBefore { 5045 isSingleLine = false 5046 } 5047 5048 switch p.lexer.Token { 5049 case js_lexer.TIdentifier: 5050 // Parse the key 5051 keyRange, keyName := p.parseJSXNamespacedName() 5052 var key js_ast.Expr 5053 if p.isMangledProp(keyName.String) && !strings.ContainsRune(keyName.String, ':') { 5054 key = js_ast.Expr{Loc: keyRange.Loc, Data: &js_ast.ENameOfSymbol{Ref: p.storeNameInRef(keyName)}} 5055 } else { 5056 key = js_ast.Expr{Loc: keyRange.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(keyName.String)}} 5057 } 5058 5059 // Parse the value 5060 var value js_ast.Expr 5061 var flags js_ast.PropertyFlags 5062 if p.lexer.Token != js_lexer.TEquals { 5063 // Implicitly true value 5064 flags |= js_ast.PropertyWasShorthand 5065 value = js_ast.Expr{Loc: logger.Loc{Start: keyRange.Loc.Start + keyRange.Len}, Data: &js_ast.EBoolean{Value: true}} 5066 } else { 5067 // Use NextInsideJSXElement() not Next() so we can parse a JSX-style string literal 5068 p.lexer.NextInsideJSXElement() 5069 if p.lexer.Token == js_lexer.TStringLiteral { 5070 stringLoc := p.lexer.Loc() 5071 if p.lexer.PreviousBackslashQuoteInJSX.Loc.Start > stringLoc.Start { 5072 previousStringWithBackslashLoc = stringLoc 5073 } 5074 if p.options.jsx.Preserve { 5075 value = js_ast.Expr{Loc: stringLoc, Data: &js_ast.EJSXText{Raw: p.lexer.Raw()}} 5076 } else { 5077 value = js_ast.Expr{Loc: stringLoc, Data: &js_ast.EString{Value: p.lexer.StringLiteral()}} 5078 } 5079 p.lexer.NextInsideJSXElement() 5080 } else if p.lexer.Token == js_lexer.TLessThan { 5081 // This may be removed in the future: https://github.com/facebook/jsx/issues/53 5082 loc := p.lexer.Loc() 5083 p.lexer.NextInsideJSXElement() 5084 flags |= js_ast.PropertyWasShorthand 5085 value = p.parseJSXElement(loc) 5086 5087 // The call to parseJSXElement() above doesn't consume the last 5088 // TGreaterThan because the caller knows what Next() function to call. 5089 // Use NextJSXElementChild() here since the next token is inside a JSX 5090 // element. 5091 p.lexer.NextInsideJSXElement() 5092 } else { 5093 // Use Expect() not ExpectInsideJSXElement() so we can parse expression tokens 5094 p.lexer.Expect(js_lexer.TOpenBrace) 5095 value = p.parseExpr(js_ast.LLowest) 5096 p.lexer.ExpectInsideJSXElement(js_lexer.TCloseBrace) 5097 } 5098 } 5099 5100 // Add a property 5101 properties = append(properties, js_ast.Property{ 5102 Loc: keyRange.Loc, 5103 Key: key, 5104 ValueOrNil: value, 5105 Flags: flags, 5106 }) 5107 5108 case js_lexer.TOpenBrace: 5109 // Use Next() not ExpectInsideJSXElement() so we can parse "..." 5110 p.lexer.Next() 5111 dotLoc := p.saveExprCommentsHere() 5112 p.lexer.Expect(js_lexer.TDotDotDot) 5113 value := p.parseExpr(js_ast.LComma) 5114 properties = append(properties, js_ast.Property{ 5115 Kind: js_ast.PropertySpread, 5116 Loc: dotLoc, 5117 ValueOrNil: value, 5118 }) 5119 5120 // Use NextInsideJSXElement() not Next() so we can parse ">>" as ">" 5121 p.lexer.NextInsideJSXElement() 5122 5123 default: 5124 break parseAttributes 5125 } 5126 } 5127 5128 // Check for and warn about duplicate attributes 5129 if len(properties) > 1 && !p.suppressWarningsAboutWeirdCode { 5130 keys := make(map[string]logger.Loc) 5131 for _, property := range properties { 5132 if property.Kind != js_ast.PropertySpread { 5133 if str, ok := property.Key.Data.(*js_ast.EString); ok { 5134 key := helpers.UTF16ToString(str.Value) 5135 if prevLoc, ok := keys[key]; ok { 5136 r := js_lexer.RangeOfIdentifier(p.source, property.Key.Loc) 5137 p.log.AddIDWithNotes(logger.MsgID_JS_DuplicateObjectKey, logger.Warning, &p.tracker, r, 5138 fmt.Sprintf("Duplicate %q attribute in JSX element", key), 5139 []logger.MsgData{p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, prevLoc), 5140 fmt.Sprintf("The original %q attribute is here:", key))}) 5141 } 5142 keys[key] = property.Key.Loc 5143 } 5144 } 5145 } 5146 } 5147 } 5148 5149 // People sometimes try to use the output of "JSON.stringify()" as a JSX 5150 // attribute when automatically-generating JSX code. Doing so is incorrect 5151 // because JSX strings work like XML instead of like JS (since JSX is XML-in- 5152 // JS). Specifically, using a backslash before a quote does not cause it to 5153 // be escaped: 5154 // 5155 // JSX ends the "content" attribute here and sets "content" to 'some so-called \\' 5156 // v 5157 // <Button content="some so-called \"button text\"" /> 5158 // ^ 5159 // There is no "=" after the JSX attribute "text", so we expect a ">" 5160 // 5161 // This code special-cases this error to provide a less obscure error message. 5162 if p.lexer.Token == js_lexer.TSyntaxError && p.lexer.Raw() == "\\" && previousStringWithBackslashLoc.Start > 0 { 5163 msg := logger.Msg{Kind: logger.Error, Data: p.tracker.MsgData(p.lexer.Range(), 5164 "Unexpected backslash in JSX element")} 5165 5166 // Option 1: Suggest using an XML escape 5167 jsEscape := p.source.TextForRange(p.lexer.PreviousBackslashQuoteInJSX) 5168 xmlEscape := "" 5169 if jsEscape == "\\\"" { 5170 xmlEscape = """ 5171 } else if jsEscape == "\\'" { 5172 xmlEscape = "'" 5173 } 5174 if xmlEscape != "" { 5175 data := p.tracker.MsgData(p.lexer.PreviousBackslashQuoteInJSX, 5176 "Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes:") 5177 data.Location.Suggestion = xmlEscape 5178 msg.Notes = append(msg.Notes, data) 5179 } 5180 5181 // Option 2: Suggest using a JavaScript string 5182 if stringRange := p.source.RangeOfString(previousStringWithBackslashLoc); stringRange.Len > 0 { 5183 data := p.tracker.MsgData(stringRange, 5184 "Consider using a JavaScript string inside {...} instead of a quoted JSX attribute:") 5185 data.Location.Suggestion = fmt.Sprintf("{%s}", p.source.TextForRange(stringRange)) 5186 msg.Notes = append(msg.Notes, data) 5187 } 5188 5189 p.log.AddMsg(msg) 5190 panic(js_lexer.LexerPanic{}) 5191 } 5192 5193 // A slash here is a self-closing element 5194 if p.lexer.Token == js_lexer.TSlash { 5195 // Use NextInsideJSXElement() not Next() so we can parse ">>" as ">" 5196 closeLoc := p.lexer.Loc() 5197 p.lexer.NextInsideJSXElement() 5198 if p.lexer.Token != js_lexer.TGreaterThan { 5199 p.lexer.Expected(js_lexer.TGreaterThan) 5200 } 5201 return js_ast.Expr{Loc: loc, Data: &js_ast.EJSXElement{ 5202 TagOrNil: startTagOrNil, 5203 Properties: properties, 5204 CloseLoc: closeLoc, 5205 IsTagSingleLine: isSingleLine, 5206 }} 5207 } 5208 5209 // Attempt to provide a better error message for people incorrectly trying to 5210 // use arrow functions in TSX (which doesn't work because they are JSX elements) 5211 if p.options.ts.Parse && len(properties) == 0 && startText != "" && p.lexer.Token == js_lexer.TGreaterThan && 5212 strings.HasPrefix(p.source.Contents[p.lexer.Loc().Start:], ">(") { 5213 badArrowInTSXRange := p.lexer.BadArrowInTSXRange 5214 badArrowInTSXSuggestion := p.lexer.BadArrowInTSXSuggestion 5215 5216 p.lexer.CouldBeBadArrowInTSX++ 5217 p.lexer.BadArrowInTSXRange = logger.Range{Loc: loc, Len: p.lexer.Range().End() - loc.Start} 5218 p.lexer.BadArrowInTSXSuggestion = fmt.Sprintf("<%s,>", startText) 5219 5220 defer func() { 5221 p.lexer.CouldBeBadArrowInTSX-- 5222 p.lexer.BadArrowInTSXRange = badArrowInTSXRange 5223 p.lexer.BadArrowInTSXSuggestion = badArrowInTSXSuggestion 5224 }() 5225 } 5226 5227 // Use ExpectJSXElementChild() so we parse child strings 5228 p.lexer.ExpectJSXElementChild(js_lexer.TGreaterThan) 5229 5230 // Parse the children of this element 5231 nullableChildren := []js_ast.Expr{} 5232 for { 5233 switch p.lexer.Token { 5234 case js_lexer.TStringLiteral: 5235 if p.options.jsx.Preserve { 5236 nullableChildren = append(nullableChildren, js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EJSXText{Raw: p.lexer.Raw()}}) 5237 } else { 5238 nullableChildren = append(nullableChildren, js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EString{Value: p.lexer.StringLiteral()}}) 5239 } 5240 p.lexer.NextJSXElementChild() 5241 5242 case js_lexer.TOpenBrace: 5243 // Use Next() instead of NextJSXElementChild() here since the next token is an expression 5244 p.lexer.Next() 5245 5246 // The expression is optional, and may be absent 5247 if p.lexer.Token == js_lexer.TCloseBrace { 5248 // Save comments even for absent expressions 5249 nullableChildren = append(nullableChildren, js_ast.Expr{Loc: p.saveExprCommentsHere(), Data: nil}) 5250 } else { 5251 if p.lexer.Token == js_lexer.TDotDotDot { 5252 // TypeScript preserves "..." before JSX child expressions here. 5253 // Babel gives the error "Spread children are not supported in React" 5254 // instead, so it should be safe to support this TypeScript-specific 5255 // behavior. Note that TypeScript's behavior changed in TypeScript 4.5. 5256 // Before that, the "..." was omitted instead of being preserved. 5257 itemLoc := p.lexer.Loc() 5258 p.markSyntaxFeature(compat.RestArgument, p.lexer.Range()) 5259 p.lexer.Next() 5260 nullableChildren = append(nullableChildren, js_ast.Expr{Loc: itemLoc, Data: &js_ast.ESpread{Value: p.parseExpr(js_ast.LLowest)}}) 5261 } else { 5262 nullableChildren = append(nullableChildren, p.parseExpr(js_ast.LLowest)) 5263 } 5264 } 5265 5266 // Use ExpectJSXElementChild() so we parse child strings 5267 p.lexer.ExpectJSXElementChild(js_lexer.TCloseBrace) 5268 5269 case js_lexer.TLessThan: 5270 lessThanLoc := p.lexer.Loc() 5271 p.lexer.NextInsideJSXElement() 5272 5273 if p.lexer.Token != js_lexer.TSlash { 5274 // This is a child element 5275 nullableChildren = append(nullableChildren, p.parseJSXElement(lessThanLoc)) 5276 5277 // The call to parseJSXElement() above doesn't consume the last 5278 // TGreaterThan because the caller knows what Next() function to call. 5279 // Use NextJSXElementChild() here since the next token is an element 5280 // child. 5281 p.lexer.NextJSXElementChild() 5282 continue 5283 } 5284 5285 // This is the closing element 5286 p.lexer.NextInsideJSXElement() 5287 endRange, endText, _ := p.parseJSXTag() 5288 if startText != endText { 5289 startTag := tagOrFragmentHelpText(startText) 5290 endTag := tagOrFragmentHelpText(endText) 5291 msg := logger.Msg{ 5292 Kind: logger.Error, 5293 Data: p.tracker.MsgData(endRange, fmt.Sprintf("Unexpected closing %s does not match opening %s", endTag, startTag)), 5294 Notes: []logger.MsgData{p.tracker.MsgData(startRange, fmt.Sprintf("The opening %s is here:", startTag))}, 5295 } 5296 msg.Data.Location.Suggestion = startText 5297 p.log.AddMsg(msg) 5298 } 5299 if p.lexer.Token != js_lexer.TGreaterThan { 5300 p.lexer.Expected(js_lexer.TGreaterThan) 5301 } 5302 5303 return js_ast.Expr{Loc: loc, Data: &js_ast.EJSXElement{ 5304 TagOrNil: startTagOrNil, 5305 Properties: properties, 5306 NullableChildren: nullableChildren, 5307 CloseLoc: lessThanLoc, 5308 IsTagSingleLine: isSingleLine, 5309 }} 5310 5311 case js_lexer.TEndOfFile: 5312 startTag := tagOrFragmentHelpText(startText) 5313 msg := logger.Msg{ 5314 Kind: logger.Error, 5315 Data: p.tracker.MsgData(p.lexer.Range(), fmt.Sprintf("Unexpected end of file before a closing %s", startTag)), 5316 Notes: []logger.MsgData{p.tracker.MsgData(startRange, fmt.Sprintf("The opening %s is here:", startTag))}, 5317 } 5318 msg.Data.Location.Suggestion = fmt.Sprintf("</%s>", startText) 5319 p.log.AddMsg(msg) 5320 panic(js_lexer.LexerPanic{}) 5321 5322 default: 5323 p.lexer.Unexpected() 5324 } 5325 } 5326 } 5327 5328 func (p *parser) parseTemplateParts(includeRaw bool) (parts []js_ast.TemplatePart, legacyOctalLoc logger.Loc) { 5329 // Allow "in" inside template literals 5330 oldAllowIn := p.allowIn 5331 p.allowIn = true 5332 5333 for { 5334 p.lexer.Next() 5335 value := p.parseExpr(js_ast.LLowest) 5336 tailLoc := p.lexer.Loc() 5337 p.lexer.RescanCloseBraceAsTemplateToken() 5338 if includeRaw { 5339 tailCooked, tailRaw := p.lexer.CookedAndRawTemplateContents() 5340 parts = append(parts, js_ast.TemplatePart{ 5341 Value: value, 5342 TailLoc: tailLoc, 5343 TailCooked: tailCooked, 5344 TailRaw: tailRaw, 5345 }) 5346 } else { 5347 parts = append(parts, js_ast.TemplatePart{ 5348 Value: value, 5349 TailLoc: tailLoc, 5350 TailCooked: p.lexer.StringLiteral(), 5351 }) 5352 if p.lexer.LegacyOctalLoc.Start > tailLoc.Start { 5353 legacyOctalLoc = p.lexer.LegacyOctalLoc 5354 } 5355 } 5356 if p.lexer.Token == js_lexer.TTemplateTail { 5357 p.lexer.Next() 5358 break 5359 } 5360 } 5361 5362 p.allowIn = oldAllowIn 5363 5364 return parts, legacyOctalLoc 5365 } 5366 5367 func (p *parser) parseAndDeclareDecls(kind ast.SymbolKind, opts parseStmtOpts) []js_ast.Decl { 5368 decls := []js_ast.Decl{} 5369 5370 for { 5371 // Forbid "let let" and "const let" but not "var let" 5372 if (kind == ast.SymbolOther || kind == ast.SymbolConst) && p.lexer.IsContextualKeyword("let") { 5373 p.log.AddError(&p.tracker, p.lexer.Range(), "Cannot use \"let\" as an identifier here:") 5374 } 5375 5376 var valueOrNil js_ast.Expr 5377 local := p.parseBinding(parseBindingOpts{isUsingStmt: opts.isUsingStmt}) 5378 p.declareBinding(kind, local, opts) 5379 5380 // Skip over types 5381 if p.options.ts.Parse { 5382 // "let foo!" 5383 isDefiniteAssignmentAssertion := p.lexer.Token == js_lexer.TExclamation && !p.lexer.HasNewlineBefore 5384 if isDefiniteAssignmentAssertion { 5385 p.lexer.Next() 5386 } 5387 5388 // "let foo: number" 5389 if isDefiniteAssignmentAssertion || p.lexer.Token == js_lexer.TColon { 5390 p.lexer.Expect(js_lexer.TColon) 5391 p.skipTypeScriptType(js_ast.LLowest) 5392 } 5393 } 5394 5395 if p.lexer.Token == js_lexer.TEquals { 5396 p.lexer.Next() 5397 valueOrNil = p.parseExpr(js_ast.LComma) 5398 5399 // Rollup (the tool that invented the "@__NO_SIDE_EFFECTS__" comment) only 5400 // applies this to the first declaration, and only when it's a "const". 5401 // For more info see: https://github.com/rollup/rollup/pull/5024/files 5402 if !p.options.ignoreDCEAnnotations && kind == ast.SymbolConst { 5403 switch e := valueOrNil.Data.(type) { 5404 case *js_ast.EArrow: 5405 if opts.hasNoSideEffectsComment { 5406 e.HasNoSideEffectsComment = true 5407 } 5408 if e.HasNoSideEffectsComment && !opts.isTypeScriptDeclare { 5409 if b, ok := local.Data.(*js_ast.BIdentifier); ok { 5410 p.symbols[b.Ref.InnerIndex].Flags |= ast.CallCanBeUnwrappedIfUnused 5411 } 5412 } 5413 5414 case *js_ast.EFunction: 5415 if opts.hasNoSideEffectsComment { 5416 e.Fn.HasNoSideEffectsComment = true 5417 } 5418 if e.Fn.HasNoSideEffectsComment && !opts.isTypeScriptDeclare { 5419 if b, ok := local.Data.(*js_ast.BIdentifier); ok { 5420 p.symbols[b.Ref.InnerIndex].Flags |= ast.CallCanBeUnwrappedIfUnused 5421 } 5422 } 5423 } 5424 5425 // Only apply this to the first declaration 5426 opts.hasNoSideEffectsComment = false 5427 } 5428 } 5429 5430 decls = append(decls, js_ast.Decl{Binding: local, ValueOrNil: valueOrNil}) 5431 5432 if p.lexer.Token != js_lexer.TComma { 5433 break 5434 } 5435 p.lexer.Next() 5436 } 5437 5438 return decls 5439 } 5440 5441 func (p *parser) requireInitializers(kind js_ast.LocalKind, decls []js_ast.Decl) { 5442 for _, d := range decls { 5443 if d.ValueOrNil.Data == nil { 5444 what := "constant" 5445 if kind == js_ast.LocalUsing { 5446 what = "declaration" 5447 } 5448 if id, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 5449 r := js_lexer.RangeOfIdentifier(p.source, d.Binding.Loc) 5450 p.log.AddError(&p.tracker, r, 5451 fmt.Sprintf("The %s %q must be initialized", what, p.symbols[id.Ref.InnerIndex].OriginalName)) 5452 } else { 5453 p.log.AddError(&p.tracker, logger.Range{Loc: d.Binding.Loc}, 5454 fmt.Sprintf("This %s must be initialized", what)) 5455 } 5456 } 5457 } 5458 } 5459 5460 func (p *parser) forbidInitializers(decls []js_ast.Decl, loopType string, isVar bool) { 5461 if len(decls) > 1 { 5462 p.log.AddError(&p.tracker, logger.Range{Loc: decls[0].Binding.Loc}, 5463 fmt.Sprintf("for-%s loops must have a single declaration", loopType)) 5464 } else if len(decls) == 1 && decls[0].ValueOrNil.Data != nil { 5465 if isVar { 5466 if _, ok := decls[0].Binding.Data.(*js_ast.BIdentifier); ok { 5467 // This is a weird special case. Initializers are allowed in "var" 5468 // statements with identifier bindings. 5469 return 5470 } 5471 } 5472 p.log.AddError(&p.tracker, logger.Range{Loc: decls[0].ValueOrNil.Loc}, 5473 fmt.Sprintf("for-%s loop variables cannot have an initializer", loopType)) 5474 } 5475 } 5476 5477 func (p *parser) parseClauseAlias(kind string) js_lexer.MaybeSubstring { 5478 loc := p.lexer.Loc() 5479 5480 // The alias may now be a string (see https://github.com/tc39/ecma262/pull/2154) 5481 if p.lexer.Token == js_lexer.TStringLiteral { 5482 r := p.source.RangeOfString(loc) 5483 alias, problem, ok := helpers.UTF16ToStringWithValidation(p.lexer.StringLiteral()) 5484 if !ok { 5485 p.log.AddError(&p.tracker, r, 5486 fmt.Sprintf("This %s alias is invalid because it contains the unpaired Unicode surrogate U+%X", kind, problem)) 5487 } 5488 return js_lexer.MaybeSubstring{String: alias} 5489 } 5490 5491 // The alias may be a keyword 5492 if !p.lexer.IsIdentifierOrKeyword() { 5493 p.lexer.Expect(js_lexer.TIdentifier) 5494 } 5495 5496 alias := p.lexer.Identifier 5497 p.checkForUnrepresentableIdentifier(loc, alias.String) 5498 return alias 5499 } 5500 5501 func (p *parser) parseImportClause() ([]js_ast.ClauseItem, bool) { 5502 items := []js_ast.ClauseItem{} 5503 p.lexer.Expect(js_lexer.TOpenBrace) 5504 isSingleLine := !p.lexer.HasNewlineBefore 5505 5506 for p.lexer.Token != js_lexer.TCloseBrace { 5507 isIdentifier := p.lexer.Token == js_lexer.TIdentifier 5508 aliasLoc := p.lexer.Loc() 5509 alias := p.parseClauseAlias("import") 5510 name := ast.LocRef{Loc: aliasLoc, Ref: p.storeNameInRef(alias)} 5511 originalName := alias 5512 p.lexer.Next() 5513 5514 // "import { type xx } from 'mod'" 5515 // "import { type xx as yy } from 'mod'" 5516 // "import { type 'xx' as yy } from 'mod'" 5517 // "import { type as } from 'mod'" 5518 // "import { type as as } from 'mod'" 5519 // "import { type as as as } from 'mod'" 5520 if p.options.ts.Parse && alias.String == "type" && p.lexer.Token != js_lexer.TComma && p.lexer.Token != js_lexer.TCloseBrace { 5521 if p.lexer.IsContextualKeyword("as") { 5522 p.lexer.Next() 5523 if p.lexer.IsContextualKeyword("as") { 5524 originalName = p.lexer.Identifier 5525 name = ast.LocRef{Loc: p.lexer.Loc(), Ref: p.storeNameInRef(originalName)} 5526 p.lexer.Next() 5527 5528 if p.lexer.Token == js_lexer.TIdentifier { 5529 // "import { type as as as } from 'mod'" 5530 // "import { type as as foo } from 'mod'" 5531 p.lexer.Next() 5532 } else { 5533 // "import { type as as } from 'mod'" 5534 items = append(items, js_ast.ClauseItem{ 5535 Alias: alias.String, 5536 AliasLoc: aliasLoc, 5537 Name: name, 5538 OriginalName: originalName.String, 5539 }) 5540 } 5541 } else if p.lexer.Token == js_lexer.TIdentifier { 5542 // "import { type as xxx } from 'mod'" 5543 originalName = p.lexer.Identifier 5544 name = ast.LocRef{Loc: p.lexer.Loc(), Ref: p.storeNameInRef(originalName)} 5545 p.lexer.Expect(js_lexer.TIdentifier) 5546 5547 // Reject forbidden names 5548 if isEvalOrArguments(originalName.String) { 5549 r := js_lexer.RangeOfIdentifier(p.source, name.Loc) 5550 p.log.AddError(&p.tracker, r, fmt.Sprintf("Cannot use %q as an identifier here:", originalName.String)) 5551 } 5552 5553 items = append(items, js_ast.ClauseItem{ 5554 Alias: alias.String, 5555 AliasLoc: aliasLoc, 5556 Name: name, 5557 OriginalName: originalName.String, 5558 }) 5559 } 5560 } else { 5561 isIdentifier := p.lexer.Token == js_lexer.TIdentifier 5562 5563 // "import { type xx } from 'mod'" 5564 // "import { type xx as yy } from 'mod'" 5565 // "import { type if as yy } from 'mod'" 5566 // "import { type 'xx' as yy } from 'mod'" 5567 p.parseClauseAlias("import") 5568 p.lexer.Next() 5569 5570 if p.lexer.IsContextualKeyword("as") { 5571 p.lexer.Next() 5572 p.lexer.Expect(js_lexer.TIdentifier) 5573 } else if !isIdentifier { 5574 // An import where the name is a keyword must have an alias 5575 p.lexer.ExpectedString("\"as\"") 5576 } 5577 } 5578 } else { 5579 if p.lexer.IsContextualKeyword("as") { 5580 p.lexer.Next() 5581 originalName = p.lexer.Identifier 5582 name = ast.LocRef{Loc: p.lexer.Loc(), Ref: p.storeNameInRef(originalName)} 5583 p.lexer.Expect(js_lexer.TIdentifier) 5584 } else if !isIdentifier { 5585 // An import where the name is a keyword must have an alias 5586 p.lexer.ExpectedString("\"as\"") 5587 } 5588 5589 // Reject forbidden names 5590 if isEvalOrArguments(originalName.String) { 5591 r := js_lexer.RangeOfIdentifier(p.source, name.Loc) 5592 p.log.AddError(&p.tracker, r, fmt.Sprintf("Cannot use %q as an identifier here:", originalName.String)) 5593 } 5594 5595 items = append(items, js_ast.ClauseItem{ 5596 Alias: alias.String, 5597 AliasLoc: aliasLoc, 5598 Name: name, 5599 OriginalName: originalName.String, 5600 }) 5601 } 5602 5603 if p.lexer.Token != js_lexer.TComma { 5604 break 5605 } 5606 if p.lexer.HasNewlineBefore { 5607 isSingleLine = false 5608 } 5609 p.lexer.Next() 5610 if p.lexer.HasNewlineBefore { 5611 isSingleLine = false 5612 } 5613 } 5614 5615 if p.lexer.HasNewlineBefore { 5616 isSingleLine = false 5617 } 5618 p.lexer.Expect(js_lexer.TCloseBrace) 5619 return items, isSingleLine 5620 } 5621 5622 func (p *parser) parseExportClause() ([]js_ast.ClauseItem, bool) { 5623 items := []js_ast.ClauseItem{} 5624 firstNonIdentifierLoc := logger.Loc{} 5625 p.lexer.Expect(js_lexer.TOpenBrace) 5626 isSingleLine := !p.lexer.HasNewlineBefore 5627 5628 for p.lexer.Token != js_lexer.TCloseBrace { 5629 alias := p.parseClauseAlias("export") 5630 aliasLoc := p.lexer.Loc() 5631 name := ast.LocRef{Loc: aliasLoc, Ref: p.storeNameInRef(alias)} 5632 originalName := alias 5633 5634 // The name can actually be a keyword if we're really an "export from" 5635 // statement. However, we won't know until later. Allow keywords as 5636 // identifiers for now and throw an error later if there's no "from". 5637 // 5638 // // This is fine 5639 // export { default } from 'path' 5640 // 5641 // // This is a syntax error 5642 // export { default } 5643 // 5644 if p.lexer.Token != js_lexer.TIdentifier && firstNonIdentifierLoc.Start == 0 { 5645 firstNonIdentifierLoc = p.lexer.Loc() 5646 } 5647 p.lexer.Next() 5648 5649 if p.options.ts.Parse && alias.String == "type" && p.lexer.Token != js_lexer.TComma && p.lexer.Token != js_lexer.TCloseBrace { 5650 if p.lexer.IsContextualKeyword("as") { 5651 p.lexer.Next() 5652 if p.lexer.IsContextualKeyword("as") { 5653 alias = p.parseClauseAlias("export") 5654 aliasLoc = p.lexer.Loc() 5655 p.lexer.Next() 5656 5657 if p.lexer.Token != js_lexer.TComma && p.lexer.Token != js_lexer.TCloseBrace { 5658 // "export { type as as as }" 5659 // "export { type as as foo }" 5660 // "export { type as as 'foo' }" 5661 p.parseClauseAlias("export") 5662 p.lexer.Next() 5663 } else { 5664 // "export { type as as }" 5665 items = append(items, js_ast.ClauseItem{ 5666 Alias: alias.String, 5667 AliasLoc: aliasLoc, 5668 Name: name, 5669 OriginalName: originalName.String, 5670 }) 5671 } 5672 } else if p.lexer.Token != js_lexer.TComma && p.lexer.Token != js_lexer.TCloseBrace { 5673 // "export { type as xxx }" 5674 // "export { type as 'xxx' }" 5675 alias = p.parseClauseAlias("export") 5676 aliasLoc = p.lexer.Loc() 5677 p.lexer.Next() 5678 5679 items = append(items, js_ast.ClauseItem{ 5680 Alias: alias.String, 5681 AliasLoc: aliasLoc, 5682 Name: name, 5683 OriginalName: originalName.String, 5684 }) 5685 } 5686 } else { 5687 // The name can actually be a keyword if we're really an "export from" 5688 // statement. However, we won't know until later. Allow keywords as 5689 // identifiers for now and throw an error later if there's no "from". 5690 // 5691 // // This is fine 5692 // export { type default } from 'path' 5693 // 5694 // // This is a syntax error 5695 // export { type default } 5696 // 5697 if p.lexer.Token != js_lexer.TIdentifier && firstNonIdentifierLoc.Start == 0 { 5698 firstNonIdentifierLoc = p.lexer.Loc() 5699 } 5700 5701 // "export { type xx }" 5702 // "export { type xx as yy }" 5703 // "export { type xx as if }" 5704 // "export { type default } from 'path'" 5705 // "export { type default as if } from 'path'" 5706 // "export { type xx as 'yy' }" 5707 // "export { type 'xx' } from 'mod'" 5708 p.parseClauseAlias("export") 5709 p.lexer.Next() 5710 5711 if p.lexer.IsContextualKeyword("as") { 5712 p.lexer.Next() 5713 p.parseClauseAlias("export") 5714 p.lexer.Next() 5715 } 5716 } 5717 } else { 5718 if p.lexer.IsContextualKeyword("as") { 5719 p.lexer.Next() 5720 alias = p.parseClauseAlias("export") 5721 aliasLoc = p.lexer.Loc() 5722 p.lexer.Next() 5723 } 5724 5725 items = append(items, js_ast.ClauseItem{ 5726 Alias: alias.String, 5727 AliasLoc: aliasLoc, 5728 Name: name, 5729 OriginalName: originalName.String, 5730 }) 5731 } 5732 5733 if p.lexer.Token != js_lexer.TComma { 5734 break 5735 } 5736 if p.lexer.HasNewlineBefore { 5737 isSingleLine = false 5738 } 5739 p.lexer.Next() 5740 if p.lexer.HasNewlineBefore { 5741 isSingleLine = false 5742 } 5743 } 5744 5745 if p.lexer.HasNewlineBefore { 5746 isSingleLine = false 5747 } 5748 p.lexer.Expect(js_lexer.TCloseBrace) 5749 5750 // Throw an error here if we found a keyword earlier and this isn't an 5751 // "export from" statement after all 5752 if firstNonIdentifierLoc.Start != 0 && !p.lexer.IsContextualKeyword("from") { 5753 r := js_lexer.RangeOfIdentifier(p.source, firstNonIdentifierLoc) 5754 p.log.AddError(&p.tracker, r, fmt.Sprintf("Expected identifier but found %q", p.source.TextForRange(r))) 5755 panic(js_lexer.LexerPanic{}) 5756 } 5757 5758 return items, isSingleLine 5759 } 5760 5761 type parseBindingOpts struct { 5762 isUsingStmt bool 5763 } 5764 5765 func (p *parser) parseBinding(opts parseBindingOpts) js_ast.Binding { 5766 loc := p.lexer.Loc() 5767 5768 switch p.lexer.Token { 5769 case js_lexer.TIdentifier: 5770 name := p.lexer.Identifier 5771 5772 // Forbid invalid identifiers 5773 if (p.fnOrArrowDataParse.await != allowIdent && name.String == "await") || 5774 (p.fnOrArrowDataParse.yield != allowIdent && name.String == "yield") { 5775 p.log.AddError(&p.tracker, p.lexer.Range(), fmt.Sprintf("Cannot use %q as an identifier here:", name.String)) 5776 } 5777 5778 ref := p.storeNameInRef(name) 5779 p.lexer.Next() 5780 return js_ast.Binding{Loc: loc, Data: &js_ast.BIdentifier{Ref: ref}} 5781 5782 case js_lexer.TOpenBracket: 5783 if opts.isUsingStmt { 5784 break 5785 } 5786 p.markSyntaxFeature(compat.Destructuring, p.lexer.Range()) 5787 p.lexer.Next() 5788 isSingleLine := !p.lexer.HasNewlineBefore 5789 items := []js_ast.ArrayBinding{} 5790 hasSpread := false 5791 5792 // "in" expressions are allowed 5793 oldAllowIn := p.allowIn 5794 p.allowIn = true 5795 5796 for p.lexer.Token != js_lexer.TCloseBracket { 5797 itemLoc := p.saveExprCommentsHere() 5798 5799 if p.lexer.Token == js_lexer.TComma { 5800 binding := js_ast.Binding{Loc: itemLoc, Data: js_ast.BMissingShared} 5801 items = append(items, js_ast.ArrayBinding{ 5802 Binding: binding, 5803 Loc: itemLoc, 5804 }) 5805 } else { 5806 if p.lexer.Token == js_lexer.TDotDotDot { 5807 p.lexer.Next() 5808 hasSpread = true 5809 5810 // This was a bug in the ES2015 spec that was fixed in ES2016 5811 if p.lexer.Token != js_lexer.TIdentifier { 5812 p.markSyntaxFeature(compat.NestedRestBinding, p.lexer.Range()) 5813 } 5814 } 5815 5816 p.saveExprCommentsHere() 5817 binding := p.parseBinding(parseBindingOpts{}) 5818 5819 var defaultValueOrNil js_ast.Expr 5820 if !hasSpread && p.lexer.Token == js_lexer.TEquals { 5821 p.lexer.Next() 5822 defaultValueOrNil = p.parseExpr(js_ast.LComma) 5823 } 5824 5825 items = append(items, js_ast.ArrayBinding{ 5826 Binding: binding, 5827 DefaultValueOrNil: defaultValueOrNil, 5828 Loc: itemLoc, 5829 }) 5830 5831 // Commas after spread elements are not allowed 5832 if hasSpread && p.lexer.Token == js_lexer.TComma { 5833 p.log.AddError(&p.tracker, p.lexer.Range(), "Unexpected \",\" after rest pattern") 5834 panic(js_lexer.LexerPanic{}) 5835 } 5836 } 5837 5838 if p.lexer.Token != js_lexer.TComma { 5839 break 5840 } 5841 if p.lexer.HasNewlineBefore { 5842 isSingleLine = false 5843 } 5844 p.lexer.Next() 5845 if p.lexer.HasNewlineBefore { 5846 isSingleLine = false 5847 } 5848 } 5849 5850 p.allowIn = oldAllowIn 5851 5852 if p.lexer.HasNewlineBefore { 5853 isSingleLine = false 5854 } 5855 closeBracketLoc := p.saveExprCommentsHere() 5856 p.lexer.Expect(js_lexer.TCloseBracket) 5857 return js_ast.Binding{Loc: loc, Data: &js_ast.BArray{ 5858 Items: items, 5859 HasSpread: hasSpread, 5860 IsSingleLine: isSingleLine, 5861 CloseBracketLoc: closeBracketLoc, 5862 }} 5863 5864 case js_lexer.TOpenBrace: 5865 if opts.isUsingStmt { 5866 break 5867 } 5868 p.markSyntaxFeature(compat.Destructuring, p.lexer.Range()) 5869 p.lexer.Next() 5870 isSingleLine := !p.lexer.HasNewlineBefore 5871 properties := []js_ast.PropertyBinding{} 5872 5873 // "in" expressions are allowed 5874 oldAllowIn := p.allowIn 5875 p.allowIn = true 5876 5877 for p.lexer.Token != js_lexer.TCloseBrace { 5878 p.saveExprCommentsHere() 5879 property := p.parsePropertyBinding() 5880 properties = append(properties, property) 5881 5882 // Commas after spread elements are not allowed 5883 if property.IsSpread && p.lexer.Token == js_lexer.TComma { 5884 p.log.AddError(&p.tracker, p.lexer.Range(), "Unexpected \",\" after rest pattern") 5885 panic(js_lexer.LexerPanic{}) 5886 } 5887 5888 if p.lexer.Token != js_lexer.TComma { 5889 break 5890 } 5891 if p.lexer.HasNewlineBefore { 5892 isSingleLine = false 5893 } 5894 p.lexer.Next() 5895 if p.lexer.HasNewlineBefore { 5896 isSingleLine = false 5897 } 5898 } 5899 5900 p.allowIn = oldAllowIn 5901 5902 if p.lexer.HasNewlineBefore { 5903 isSingleLine = false 5904 } 5905 closeBraceLoc := p.saveExprCommentsHere() 5906 p.lexer.Expect(js_lexer.TCloseBrace) 5907 return js_ast.Binding{Loc: loc, Data: &js_ast.BObject{ 5908 Properties: properties, 5909 IsSingleLine: isSingleLine, 5910 CloseBraceLoc: closeBraceLoc, 5911 }} 5912 } 5913 5914 p.lexer.Expect(js_lexer.TIdentifier) 5915 return js_ast.Binding{} 5916 } 5917 5918 func (p *parser) parseFn( 5919 name *ast.LocRef, 5920 classKeyword logger.Range, 5921 decoratorContext decoratorContextFlags, 5922 data fnOrArrowDataParse, 5923 ) (fn js_ast.Fn, hadBody bool) { 5924 fn.Name = name 5925 fn.HasRestArg = false 5926 fn.IsAsync = data.await == allowExpr 5927 fn.IsGenerator = data.yield == allowExpr 5928 fn.ArgumentsRef = ast.InvalidRef 5929 fn.OpenParenLoc = p.lexer.Loc() 5930 p.lexer.Expect(js_lexer.TOpenParen) 5931 5932 // Await and yield are not allowed in function arguments 5933 oldFnOrArrowData := p.fnOrArrowDataParse 5934 if data.await == allowExpr { 5935 p.fnOrArrowDataParse.await = forbidAll 5936 } else { 5937 p.fnOrArrowDataParse.await = allowIdent 5938 } 5939 if data.yield == allowExpr { 5940 p.fnOrArrowDataParse.yield = forbidAll 5941 } else { 5942 p.fnOrArrowDataParse.yield = allowIdent 5943 } 5944 5945 // Don't suggest inserting "async" before anything if "await" is found 5946 p.fnOrArrowDataParse.needsAsyncLoc.Start = -1 5947 5948 // If "super" is allowed in the body, it's allowed in the arguments 5949 p.fnOrArrowDataParse.allowSuperCall = data.allowSuperCall 5950 p.fnOrArrowDataParse.allowSuperProperty = data.allowSuperProperty 5951 5952 for p.lexer.Token != js_lexer.TCloseParen { 5953 // Skip over "this" type annotations 5954 if p.options.ts.Parse && p.lexer.Token == js_lexer.TThis { 5955 p.lexer.Next() 5956 if p.lexer.Token == js_lexer.TColon { 5957 p.lexer.Next() 5958 p.skipTypeScriptType(js_ast.LLowest) 5959 } 5960 if p.lexer.Token != js_lexer.TComma { 5961 break 5962 } 5963 p.lexer.Next() 5964 continue 5965 } 5966 5967 var decorators []js_ast.Decorator 5968 if data.decoratorScope != nil { 5969 oldAwait := p.fnOrArrowDataParse.await 5970 oldNeedsAsyncLoc := p.fnOrArrowDataParse.needsAsyncLoc 5971 5972 // While TypeScript parameter decorators are expressions, they are not 5973 // evaluated where they exist in the code. They are moved to after the 5974 // class declaration and evaluated there instead. Specifically this 5975 // TypeScript code: 5976 // 5977 // class Foo { 5978 // foo(@bar() baz) {} 5979 // } 5980 // 5981 // becomes this JavaScript code: 5982 // 5983 // class Foo { 5984 // foo(baz) {} 5985 // } 5986 // __decorate([ 5987 // __param(0, bar()) 5988 // ], Foo.prototype, "foo", null); 5989 // 5990 // One consequence of this is that whether "await" is allowed or not 5991 // depends on whether the class declaration itself is inside an "async" 5992 // function or not. The TypeScript compiler allows code that does this: 5993 // 5994 // async function fn(foo) { 5995 // class Foo { 5996 // foo(@bar(await foo) baz) {} 5997 // } 5998 // return Foo 5999 // } 6000 // 6001 // because that becomes the following valid JavaScript: 6002 // 6003 // async function fn(foo) { 6004 // class Foo { 6005 // foo(baz) {} 6006 // } 6007 // __decorate([ 6008 // __param(0, bar(await foo)) 6009 // ], Foo.prototype, "foo", null); 6010 // return Foo; 6011 // } 6012 // 6013 if oldFnOrArrowData.await == allowExpr { 6014 p.fnOrArrowDataParse.await = allowExpr 6015 } else { 6016 p.fnOrArrowDataParse.needsAsyncLoc = oldFnOrArrowData.needsAsyncLoc 6017 } 6018 6019 decorators = p.parseDecorators(data.decoratorScope, classKeyword, decoratorContext|decoratorInFnArgs) 6020 6021 p.fnOrArrowDataParse.await = oldAwait 6022 p.fnOrArrowDataParse.needsAsyncLoc = oldNeedsAsyncLoc 6023 } 6024 6025 if !fn.HasRestArg && p.lexer.Token == js_lexer.TDotDotDot { 6026 p.markSyntaxFeature(compat.RestArgument, p.lexer.Range()) 6027 p.lexer.Next() 6028 fn.HasRestArg = true 6029 } 6030 6031 isTypeScriptCtorField := false 6032 isIdentifier := p.lexer.Token == js_lexer.TIdentifier 6033 text := p.lexer.Identifier.String 6034 arg := p.parseBinding(parseBindingOpts{}) 6035 6036 if p.options.ts.Parse { 6037 // Skip over TypeScript accessibility modifiers, which turn this argument 6038 // into a class field when used inside a class constructor. This is known 6039 // as a "parameter property" in TypeScript. 6040 if isIdentifier && data.isConstructor { 6041 for p.lexer.Token == js_lexer.TIdentifier || p.lexer.Token == js_lexer.TOpenBrace || p.lexer.Token == js_lexer.TOpenBracket { 6042 if text != "public" && text != "private" && text != "protected" && text != "readonly" && text != "override" { 6043 break 6044 } 6045 isTypeScriptCtorField = true 6046 6047 // TypeScript requires an identifier binding 6048 if p.lexer.Token != js_lexer.TIdentifier { 6049 p.lexer.Expect(js_lexer.TIdentifier) 6050 } 6051 text = p.lexer.Identifier.String 6052 6053 // Re-parse the binding (the current binding is the TypeScript keyword) 6054 arg = p.parseBinding(parseBindingOpts{}) 6055 } 6056 } 6057 6058 // "function foo(a?) {}" 6059 if p.lexer.Token == js_lexer.TQuestion { 6060 p.lexer.Next() 6061 } 6062 6063 // "function foo(a: any) {}" 6064 if p.lexer.Token == js_lexer.TColon { 6065 p.lexer.Next() 6066 p.skipTypeScriptType(js_ast.LLowest) 6067 } 6068 } 6069 6070 p.declareBinding(ast.SymbolHoisted, arg, parseStmtOpts{}) 6071 6072 var defaultValueOrNil js_ast.Expr 6073 if !fn.HasRestArg && p.lexer.Token == js_lexer.TEquals { 6074 p.markSyntaxFeature(compat.DefaultArgument, p.lexer.Range()) 6075 p.lexer.Next() 6076 defaultValueOrNil = p.parseExpr(js_ast.LComma) 6077 } 6078 6079 fn.Args = append(fn.Args, js_ast.Arg{ 6080 Decorators: decorators, 6081 Binding: arg, 6082 DefaultOrNil: defaultValueOrNil, 6083 6084 // We need to track this because it affects code generation 6085 IsTypeScriptCtorField: isTypeScriptCtorField, 6086 }) 6087 6088 if p.lexer.Token != js_lexer.TComma { 6089 break 6090 } 6091 if fn.HasRestArg { 6092 // JavaScript does not allow a comma after a rest argument 6093 if data.isTypeScriptDeclare { 6094 // TypeScript does allow a comma after a rest argument in a "declare" context 6095 p.lexer.Next() 6096 } else { 6097 p.lexer.Expect(js_lexer.TCloseParen) 6098 } 6099 break 6100 } 6101 p.lexer.Next() 6102 } 6103 6104 // Reserve the special name "arguments" in this scope. This ensures that it 6105 // shadows any variable called "arguments" in any parent scopes. But only do 6106 // this if it wasn't already declared above because arguments are allowed to 6107 // be called "arguments", in which case the real "arguments" is inaccessible. 6108 if _, ok := p.currentScope.Members["arguments"]; !ok { 6109 fn.ArgumentsRef = p.declareSymbol(ast.SymbolArguments, fn.OpenParenLoc, "arguments") 6110 p.symbols[fn.ArgumentsRef.InnerIndex].Flags |= ast.MustNotBeRenamed 6111 } 6112 6113 p.lexer.Expect(js_lexer.TCloseParen) 6114 p.fnOrArrowDataParse = oldFnOrArrowData 6115 6116 // "function foo(): any {}" 6117 if p.options.ts.Parse && p.lexer.Token == js_lexer.TColon { 6118 p.lexer.Next() 6119 p.skipTypeScriptReturnType() 6120 } 6121 6122 // "function foo(): any;" 6123 if data.allowMissingBodyForTypeScript && p.lexer.Token != js_lexer.TOpenBrace { 6124 p.lexer.ExpectOrInsertSemicolon() 6125 return 6126 } 6127 6128 fn.Body = p.parseFnBody(data) 6129 hadBody = true 6130 return 6131 } 6132 6133 type fnKind uint8 6134 6135 const ( 6136 fnStmt fnKind = iota 6137 fnExpr 6138 ) 6139 6140 func (p *parser) validateFunctionName(fn js_ast.Fn, kind fnKind) { 6141 // Prevent the function name from being the same as a function-specific keyword 6142 if fn.Name != nil { 6143 if fn.IsAsync && p.symbols[fn.Name.Ref.InnerIndex].OriginalName == "await" { 6144 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, fn.Name.Loc), 6145 "An async function cannot be named \"await\"") 6146 } else if fn.IsGenerator && p.symbols[fn.Name.Ref.InnerIndex].OriginalName == "yield" && kind == fnExpr { 6147 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, fn.Name.Loc), 6148 "A generator function expression cannot be named \"yield\"") 6149 } 6150 } 6151 } 6152 6153 func (p *parser) validateDeclaredSymbolName(loc logger.Loc, name string) { 6154 if js_lexer.StrictModeReservedWords[name] { 6155 p.markStrictModeFeature(reservedWord, js_lexer.RangeOfIdentifier(p.source, loc), name) 6156 } else if isEvalOrArguments(name) { 6157 p.markStrictModeFeature(evalOrArguments, js_lexer.RangeOfIdentifier(p.source, loc), name) 6158 } 6159 } 6160 6161 func (p *parser) parseClassStmt(loc logger.Loc, opts parseStmtOpts) js_ast.Stmt { 6162 var name *ast.LocRef 6163 classKeyword := p.lexer.Range() 6164 if p.lexer.Token == js_lexer.TClass { 6165 p.markSyntaxFeature(compat.Class, classKeyword) 6166 p.lexer.Next() 6167 } else { 6168 p.lexer.Expected(js_lexer.TClass) 6169 } 6170 6171 if !opts.isNameOptional || (p.lexer.Token == js_lexer.TIdentifier && (!p.options.ts.Parse || p.lexer.Identifier.String != "implements")) { 6172 nameLoc := p.lexer.Loc() 6173 nameText := p.lexer.Identifier.String 6174 p.lexer.Expect(js_lexer.TIdentifier) 6175 if p.fnOrArrowDataParse.await != allowIdent && nameText == "await" { 6176 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, nameLoc), "Cannot use \"await\" as an identifier here:") 6177 } 6178 name = &ast.LocRef{Loc: nameLoc, Ref: ast.InvalidRef} 6179 if !opts.isTypeScriptDeclare { 6180 name.Ref = p.declareSymbol(ast.SymbolClass, nameLoc, nameText) 6181 } 6182 } 6183 6184 // Even anonymous classes can have TypeScript type parameters 6185 if p.options.ts.Parse { 6186 p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowConstModifier) 6187 } 6188 6189 classOpts := parseClassOpts{ 6190 isTypeScriptDeclare: opts.isTypeScriptDeclare, 6191 } 6192 if opts.deferredDecorators != nil { 6193 classOpts.decorators = opts.deferredDecorators.decorators 6194 } 6195 scopeIndex := p.pushScopeForParsePass(js_ast.ScopeClassName, loc) 6196 class := p.parseClass(classKeyword, name, classOpts) 6197 6198 if opts.isTypeScriptDeclare { 6199 p.popAndDiscardScope(scopeIndex) 6200 6201 if opts.isNamespaceScope && opts.isExport { 6202 p.hasNonLocalExportDeclareInsideNamespace = true 6203 } 6204 6205 // Remember that this was a "declare class" so we can allow decorators on it 6206 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptSharedWasDeclareClass} 6207 } 6208 6209 p.popScope() 6210 return js_ast.Stmt{Loc: loc, Data: &js_ast.SClass{Class: class, IsExport: opts.isExport}} 6211 } 6212 6213 func (p *parser) parseClassExpr(decorators []js_ast.Decorator) js_ast.Expr { 6214 classKeyword := p.lexer.Range() 6215 p.markSyntaxFeature(compat.Class, classKeyword) 6216 p.lexer.Expect(js_lexer.TClass) 6217 var name *ast.LocRef 6218 6219 opts := parseClassOpts{ 6220 decorators: decorators, 6221 decoratorContext: decoratorInClassExpr, 6222 } 6223 p.pushScopeForParsePass(js_ast.ScopeClassName, classKeyword.Loc) 6224 6225 // Parse an optional class name 6226 if p.lexer.Token == js_lexer.TIdentifier { 6227 if nameText := p.lexer.Identifier.String; !p.options.ts.Parse || nameText != "implements" { 6228 if p.fnOrArrowDataParse.await != allowIdent && nameText == "await" { 6229 p.log.AddError(&p.tracker, p.lexer.Range(), "Cannot use \"await\" as an identifier here:") 6230 } 6231 name = &ast.LocRef{Loc: p.lexer.Loc(), Ref: p.newSymbol(ast.SymbolOther, nameText)} 6232 p.lexer.Next() 6233 } 6234 } 6235 6236 // Even anonymous classes can have TypeScript type parameters 6237 if p.options.ts.Parse { 6238 p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowConstModifier) 6239 } 6240 6241 class := p.parseClass(classKeyword, name, opts) 6242 6243 p.popScope() 6244 return js_ast.Expr{Loc: classKeyword.Loc, Data: &js_ast.EClass{Class: class}} 6245 } 6246 6247 type parseClassOpts struct { 6248 decorators []js_ast.Decorator 6249 decoratorContext decoratorContextFlags 6250 isTypeScriptDeclare bool 6251 } 6252 6253 // By the time we call this, the identifier and type parameters have already 6254 // been parsed. We need to start parsing from the "extends" clause. 6255 func (p *parser) parseClass(classKeyword logger.Range, name *ast.LocRef, classOpts parseClassOpts) js_ast.Class { 6256 var extendsOrNil js_ast.Expr 6257 6258 if p.lexer.Token == js_lexer.TExtends { 6259 p.lexer.Next() 6260 extendsOrNil = p.parseExpr(js_ast.LNew) 6261 6262 // TypeScript's type argument parser inside expressions backtracks if the 6263 // first token after the end of the type parameter list is "{", so the 6264 // parsed expression above will have backtracked if there are any type 6265 // arguments. This means we have to re-parse for any type arguments here. 6266 // This seems kind of wasteful to me but it's what the official compiler 6267 // does and it probably doesn't have that high of a performance overhead 6268 // because "extends" clauses aren't that frequent, so it should be ok. 6269 if p.options.ts.Parse { 6270 p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{}) 6271 } 6272 } 6273 6274 if p.options.ts.Parse && p.lexer.IsContextualKeyword("implements") { 6275 p.lexer.Next() 6276 for { 6277 p.skipTypeScriptType(js_ast.LLowest) 6278 if p.lexer.Token != js_lexer.TComma { 6279 break 6280 } 6281 p.lexer.Next() 6282 } 6283 } 6284 6285 bodyLoc := p.lexer.Loc() 6286 p.lexer.Expect(js_lexer.TOpenBrace) 6287 properties := []js_ast.Property{} 6288 6289 // Allow "in" and private fields inside class bodies 6290 oldAllowIn := p.allowIn 6291 oldAllowPrivateIdentifiers := p.allowPrivateIdentifiers 6292 p.allowIn = true 6293 p.allowPrivateIdentifiers = true 6294 6295 // A scope is needed for private identifiers 6296 scopeIndex := p.pushScopeForParsePass(js_ast.ScopeClassBody, bodyLoc) 6297 6298 opts := propertyOpts{ 6299 isClass: true, 6300 decoratorScope: p.currentScope, 6301 decoratorContext: classOpts.decoratorContext, 6302 classHasExtends: extendsOrNil.Data != nil, 6303 classKeyword: classKeyword, 6304 } 6305 hasConstructor := false 6306 6307 for p.lexer.Token != js_lexer.TCloseBrace { 6308 if p.lexer.Token == js_lexer.TSemicolon { 6309 p.lexer.Next() 6310 continue 6311 } 6312 6313 // Parse decorators for this property 6314 firstDecoratorLoc := p.lexer.Loc() 6315 scopeIndex := len(p.scopesInOrder) 6316 opts.decorators = p.parseDecorators(p.currentScope, classKeyword, opts.decoratorContext) 6317 6318 // This property may turn out to be a type in TypeScript, which should be ignored 6319 if property, ok := p.parseProperty(p.saveExprCommentsHere(), js_ast.PropertyField, opts, nil); ok { 6320 properties = append(properties, property) 6321 6322 // Forbid decorators on class constructors 6323 if key, ok := property.Key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(key.Value, "constructor") { 6324 if len(opts.decorators) > 0 { 6325 p.log.AddError(&p.tracker, logger.Range{Loc: firstDecoratorLoc}, 6326 "Decorators are not allowed on class constructors") 6327 } 6328 if property.Kind.IsMethodDefinition() && !property.Flags.Has(js_ast.PropertyIsStatic) && !property.Flags.Has(js_ast.PropertyIsComputed) { 6329 if hasConstructor { 6330 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, property.Key.Loc), 6331 "Classes cannot contain more than one constructor") 6332 } 6333 hasConstructor = true 6334 } 6335 } 6336 } else if !classOpts.isTypeScriptDeclare && len(opts.decorators) > 0 { 6337 p.log.AddError(&p.tracker, logger.Range{Loc: firstDecoratorLoc, Len: 1}, "Decorators are not valid here") 6338 p.discardScopesUpTo(scopeIndex) 6339 } 6340 } 6341 6342 // Discard the private identifier scope inside a TypeScript "declare class" 6343 if classOpts.isTypeScriptDeclare { 6344 p.popAndDiscardScope(scopeIndex) 6345 } else { 6346 p.popScope() 6347 } 6348 6349 p.allowIn = oldAllowIn 6350 p.allowPrivateIdentifiers = oldAllowPrivateIdentifiers 6351 6352 closeBraceLoc := p.saveExprCommentsHere() 6353 p.lexer.Expect(js_lexer.TCloseBrace) 6354 return js_ast.Class{ 6355 ClassKeyword: classKeyword, 6356 Decorators: classOpts.decorators, 6357 Name: name, 6358 ExtendsOrNil: extendsOrNil, 6359 BodyLoc: bodyLoc, 6360 Properties: properties, 6361 CloseBraceLoc: closeBraceLoc, 6362 6363 // TypeScript has legacy behavior that uses assignment semantics instead of 6364 // define semantics for class fields when "useDefineForClassFields" is enabled 6365 // (in which case TypeScript behaves differently than JavaScript, which is 6366 // arguably "wrong"). 6367 // 6368 // This legacy behavior exists because TypeScript added class fields to 6369 // TypeScript before they were added to JavaScript. They decided to go with 6370 // assignment semantics for whatever reason. Later on TC39 decided to go with 6371 // define semantics for class fields instead. This behaves differently if the 6372 // base class has a setter with the same name. 6373 // 6374 // The value of "useDefineForClassFields" defaults to false when it's not 6375 // specified and the target is earlier than "ES2022" since the class field 6376 // language feature was added in ES2022. However, TypeScript's "target" 6377 // setting currently defaults to "ES3" which unfortunately means that the 6378 // "useDefineForClassFields" setting defaults to false (i.e. to "wrong"). 6379 // 6380 // We default "useDefineForClassFields" to true (i.e. to "correct") instead. 6381 // This is partially because our target defaults to "esnext", and partially 6382 // because this is a legacy behavior that no one should be using anymore. 6383 // Users that want the wrong behavior can either set "useDefineForClassFields" 6384 // to false in "tsconfig.json" explicitly, or set TypeScript's "target" to 6385 // "ES2021" or earlier in their in "tsconfig.json" file. 6386 UseDefineForClassFields: !p.options.ts.Parse || p.options.ts.Config.UseDefineForClassFields == config.True || 6387 (p.options.ts.Config.UseDefineForClassFields == config.Unspecified && p.options.ts.Config.Target != config.TSTargetBelowES2022), 6388 } 6389 } 6390 6391 func (p *parser) parseLabelName() *ast.LocRef { 6392 if p.lexer.Token != js_lexer.TIdentifier || p.lexer.HasNewlineBefore { 6393 return nil 6394 } 6395 6396 name := ast.LocRef{Loc: p.lexer.Loc(), Ref: p.storeNameInRef(p.lexer.Identifier)} 6397 p.lexer.Next() 6398 return &name 6399 } 6400 6401 func (p *parser) parsePath() (logger.Range, string, *ast.ImportAssertOrWith, ast.ImportRecordFlags) { 6402 var flags ast.ImportRecordFlags 6403 pathRange := p.lexer.Range() 6404 pathText := helpers.UTF16ToString(p.lexer.StringLiteral()) 6405 if p.lexer.Token == js_lexer.TNoSubstitutionTemplateLiteral { 6406 p.lexer.Next() 6407 } else { 6408 p.lexer.Expect(js_lexer.TStringLiteral) 6409 } 6410 6411 // See https://github.com/tc39/proposal-import-attributes for more info 6412 var assertOrWith *ast.ImportAssertOrWith 6413 if p.lexer.Token == js_lexer.TWith || (!p.lexer.HasNewlineBefore && p.lexer.IsContextualKeyword("assert")) { 6414 // "import './foo.json' assert { type: 'json' }" 6415 // "import './foo.json' with { type: 'json' }" 6416 var entries []ast.AssertOrWithEntry 6417 duplicates := make(map[string]logger.Range) 6418 keyword := ast.WithKeyword 6419 if p.lexer.Token != js_lexer.TWith { 6420 keyword = ast.AssertKeyword 6421 } 6422 keywordLoc := p.saveExprCommentsHere() 6423 p.lexer.Next() 6424 openBraceLoc := p.saveExprCommentsHere() 6425 p.lexer.Expect(js_lexer.TOpenBrace) 6426 6427 for p.lexer.Token != js_lexer.TCloseBrace { 6428 // Parse the key 6429 keyLoc := p.saveExprCommentsHere() 6430 preferQuotedKey := false 6431 var key []uint16 6432 var keyText string 6433 if p.lexer.IsIdentifierOrKeyword() { 6434 keyText = p.lexer.Identifier.String 6435 key = helpers.StringToUTF16(keyText) 6436 } else if p.lexer.Token == js_lexer.TStringLiteral { 6437 key = p.lexer.StringLiteral() 6438 keyText = helpers.UTF16ToString(key) 6439 preferQuotedKey = !p.options.minifySyntax 6440 } else { 6441 p.lexer.Expect(js_lexer.TIdentifier) 6442 } 6443 if prevRange, ok := duplicates[keyText]; ok { 6444 what := "attribute" 6445 if keyword == ast.AssertKeyword { 6446 what = "assertion" 6447 } 6448 p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), fmt.Sprintf("Duplicate import %s %q", what, keyText), 6449 []logger.MsgData{p.tracker.MsgData(prevRange, fmt.Sprintf("The first %q was here:", keyText))}) 6450 } 6451 duplicates[keyText] = p.lexer.Range() 6452 p.lexer.Next() 6453 p.lexer.Expect(js_lexer.TColon) 6454 6455 // Parse the value 6456 valueLoc := p.saveExprCommentsHere() 6457 value := p.lexer.StringLiteral() 6458 p.lexer.Expect(js_lexer.TStringLiteral) 6459 6460 entries = append(entries, ast.AssertOrWithEntry{ 6461 Key: key, 6462 KeyLoc: keyLoc, 6463 Value: value, 6464 ValueLoc: valueLoc, 6465 PreferQuotedKey: preferQuotedKey, 6466 }) 6467 6468 // Using "assert: { type: 'json' }" triggers special behavior 6469 if keyword == ast.AssertKeyword && helpers.UTF16EqualsString(key, "type") && helpers.UTF16EqualsString(value, "json") { 6470 flags |= ast.AssertTypeJSON 6471 } 6472 6473 if p.lexer.Token != js_lexer.TComma { 6474 break 6475 } 6476 p.lexer.Next() 6477 } 6478 6479 closeBraceLoc := p.saveExprCommentsHere() 6480 p.lexer.Expect(js_lexer.TCloseBrace) 6481 if keyword == ast.AssertKeyword { 6482 p.maybeWarnAboutAssertKeyword(keywordLoc) 6483 } 6484 assertOrWith = &ast.ImportAssertOrWith{ 6485 Entries: entries, 6486 Keyword: keyword, 6487 KeywordLoc: keywordLoc, 6488 InnerOpenBraceLoc: openBraceLoc, 6489 InnerCloseBraceLoc: closeBraceLoc, 6490 } 6491 } 6492 6493 return pathRange, pathText, assertOrWith, flags 6494 } 6495 6496 // Let people know if they probably should be using "with" instead of "assert" 6497 func (p *parser) maybeWarnAboutAssertKeyword(loc logger.Loc) { 6498 if p.options.unsupportedJSFeatures.Has(compat.ImportAssertions) && !p.options.unsupportedJSFeatures.Has(compat.ImportAttributes) { 6499 where := config.PrettyPrintTargetEnvironment(p.options.originalTargetEnv, p.options.unsupportedJSFeatureOverridesMask) 6500 msg := logger.Msg{ 6501 Kind: logger.Warning, 6502 Data: p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, loc), "The \"assert\" keyword is not supported in "+where), 6503 Notes: []logger.MsgData{{Text: "Did you mean to use \"with\" instead of \"assert\"?"}}, 6504 } 6505 msg.Data.Location.Suggestion = "with" 6506 p.log.AddMsgID(logger.MsgID_JS_AssertToWith, msg) 6507 } 6508 } 6509 6510 // This assumes the "function" token has already been parsed 6511 func (p *parser) parseFnStmt(loc logger.Loc, opts parseStmtOpts, isAsync bool, asyncRange logger.Range) js_ast.Stmt { 6512 isGenerator := p.lexer.Token == js_lexer.TAsterisk 6513 hasError := false 6514 if isAsync { 6515 hasError = p.markAsyncFn(asyncRange, isGenerator) 6516 } 6517 if isGenerator { 6518 if !hasError { 6519 p.markSyntaxFeature(compat.Generator, p.lexer.Range()) 6520 } 6521 p.lexer.Next() 6522 } 6523 6524 switch opts.lexicalDecl { 6525 case lexicalDeclForbid: 6526 p.forbidLexicalDecl(loc) 6527 6528 // Allow certain function statements in certain single-statement contexts 6529 case lexicalDeclAllowFnInsideIf, lexicalDeclAllowFnInsideLabel: 6530 if opts.isTypeScriptDeclare || isGenerator || isAsync { 6531 p.forbidLexicalDecl(loc) 6532 } 6533 } 6534 6535 var name *ast.LocRef 6536 var nameText string 6537 6538 // The name is optional for "export default function() {}" pseudo-statements 6539 if !opts.isNameOptional || p.lexer.Token == js_lexer.TIdentifier { 6540 nameLoc := p.lexer.Loc() 6541 nameText = p.lexer.Identifier.String 6542 if !isAsync && p.fnOrArrowDataParse.await != allowIdent && nameText == "await" { 6543 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, nameLoc), "Cannot use \"await\" as an identifier here:") 6544 } 6545 p.lexer.Expect(js_lexer.TIdentifier) 6546 name = &ast.LocRef{Loc: nameLoc, Ref: ast.InvalidRef} 6547 } 6548 6549 // Even anonymous functions can have TypeScript type parameters 6550 if p.options.ts.Parse { 6551 p.skipTypeScriptTypeParameters(allowConstModifier) 6552 } 6553 6554 // Introduce a fake block scope for function declarations inside if statements 6555 var ifStmtScopeIndex int 6556 hasIfScope := opts.lexicalDecl == lexicalDeclAllowFnInsideIf 6557 if hasIfScope { 6558 ifStmtScopeIndex = p.pushScopeForParsePass(js_ast.ScopeBlock, loc) 6559 } 6560 6561 scopeIndex := p.pushScopeForParsePass(js_ast.ScopeFunctionArgs, p.lexer.Loc()) 6562 6563 await := allowIdent 6564 yield := allowIdent 6565 if isAsync { 6566 await = allowExpr 6567 } 6568 if isGenerator { 6569 yield = allowExpr 6570 } 6571 6572 fn, hadBody := p.parseFn(name, logger.Range{}, 0, fnOrArrowDataParse{ 6573 needsAsyncLoc: loc, 6574 asyncRange: asyncRange, 6575 await: await, 6576 yield: yield, 6577 isTypeScriptDeclare: opts.isTypeScriptDeclare, 6578 6579 // Only allow omitting the body if we're parsing TypeScript 6580 allowMissingBodyForTypeScript: p.options.ts.Parse, 6581 }) 6582 6583 // Don't output anything if it's just a forward declaration of a function 6584 if opts.isTypeScriptDeclare || !hadBody { 6585 p.popAndDiscardScope(scopeIndex) 6586 6587 // Balance the fake block scope introduced above 6588 if hasIfScope { 6589 p.popAndDiscardScope(ifStmtScopeIndex) 6590 } 6591 6592 if opts.isTypeScriptDeclare && opts.isNamespaceScope && opts.isExport { 6593 p.hasNonLocalExportDeclareInsideNamespace = true 6594 } 6595 6596 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 6597 } 6598 6599 p.popScope() 6600 6601 // Only declare the function after we know if it had a body or not. Otherwise 6602 // TypeScript code such as this will double-declare the symbol: 6603 // 6604 // function foo(): void; 6605 // function foo(): void {} 6606 // 6607 if name != nil { 6608 kind := ast.SymbolHoistedFunction 6609 if isGenerator || isAsync { 6610 kind = ast.SymbolGeneratorOrAsyncFunction 6611 } 6612 name.Ref = p.declareSymbol(kind, name.Loc, nameText) 6613 } 6614 6615 // Balance the fake block scope introduced above 6616 if hasIfScope { 6617 p.popScope() 6618 } 6619 6620 fn.HasIfScope = hasIfScope 6621 p.validateFunctionName(fn, fnStmt) 6622 if opts.hasNoSideEffectsComment && !p.options.ignoreDCEAnnotations { 6623 fn.HasNoSideEffectsComment = true 6624 if name != nil && !opts.isTypeScriptDeclare { 6625 p.symbols[name.Ref.InnerIndex].Flags |= ast.CallCanBeUnwrappedIfUnused 6626 } 6627 } 6628 return js_ast.Stmt{Loc: loc, Data: &js_ast.SFunction{Fn: fn, IsExport: opts.isExport}} 6629 } 6630 6631 type deferredDecorators struct { 6632 decorators []js_ast.Decorator 6633 } 6634 6635 type decoratorContextFlags uint8 6636 6637 const ( 6638 decoratorBeforeClassExpr = 1 << iota 6639 decoratorInClassExpr 6640 decoratorInFnArgs 6641 ) 6642 6643 func (p *parser) parseDecorators(decoratorScope *js_ast.Scope, classKeyword logger.Range, context decoratorContextFlags) (decorators []js_ast.Decorator) { 6644 if p.lexer.Token == js_lexer.TAt { 6645 if p.options.ts.Parse { 6646 if p.options.ts.Config.ExperimentalDecorators == config.True { 6647 if (context & decoratorInClassExpr) != 0 { 6648 p.lexer.AddRangeErrorWithNotes(p.lexer.Range(), "TypeScript experimental decorators can only be used with class declarations", 6649 []logger.MsgData{p.tracker.MsgData(classKeyword, "This is a class expression, not a class declaration:")}) 6650 } else if (context & decoratorBeforeClassExpr) != 0 { 6651 p.log.AddError(&p.tracker, p.lexer.Range(), "TypeScript experimental decorators cannot be used in expression position") 6652 } 6653 } else { 6654 if (context&decoratorInFnArgs) != 0 && p.options.ts.Config.ExperimentalDecorators != config.True { 6655 p.log.AddErrorWithNotes(&p.tracker, p.lexer.Range(), "Parameter decorators only work when experimental decorators are enabled", []logger.MsgData{{ 6656 Text: "You can enable experimental decorators by adding \"experimentalDecorators\": true to your \"tsconfig.json\" file.", 6657 }}) 6658 } 6659 } 6660 } else { 6661 if (context & decoratorInFnArgs) != 0 { 6662 p.log.AddError(&p.tracker, p.lexer.Range(), "Parameter decorators are not allowed in JavaScript") 6663 } 6664 } 6665 } 6666 6667 // TypeScript decorators cause us to temporarily revert to the scope that 6668 // encloses the class declaration, since that's where the generated code 6669 // for TypeScript decorators will be inserted. 6670 oldScope := p.currentScope 6671 p.currentScope = decoratorScope 6672 6673 for p.lexer.Token == js_lexer.TAt { 6674 atLoc := p.lexer.Loc() 6675 p.lexer.Next() 6676 6677 var value js_ast.Expr 6678 if p.options.ts.Parse && p.options.ts.Config.ExperimentalDecorators == config.True { 6679 // TypeScript's experimental decorator syntax is more permissive than 6680 // JavaScript. Parse a new/call expression with "exprFlagDecorator" so 6681 // we ignore EIndex expressions, since they may be part of a computed 6682 // property: 6683 // 6684 // class Foo { 6685 // @foo ['computed']() {} 6686 // } 6687 // 6688 // This matches the behavior of the TypeScript compiler. 6689 p.parseExperimentalDecoratorNesting++ 6690 value = p.parseExprWithFlags(js_ast.LNew, exprFlagDecorator) 6691 p.parseExperimentalDecoratorNesting-- 6692 } else { 6693 // JavaScript's decorator syntax is more restrictive. Parse it using a 6694 // special parser that doesn't allow normal expressions (e.g. "?."). 6695 value = p.parseDecorator() 6696 } 6697 decorators = append(decorators, js_ast.Decorator{ 6698 Value: value, 6699 AtLoc: atLoc, 6700 OmitNewlineAfter: !p.lexer.HasNewlineBefore, 6701 }) 6702 } 6703 6704 // Avoid "popScope" because this decorator scope is not hierarchical 6705 p.currentScope = oldScope 6706 return decorators 6707 } 6708 6709 func (p *parser) parseDecorator() js_ast.Expr { 6710 if p.lexer.Token == js_lexer.TOpenParen { 6711 p.lexer.Next() 6712 value := p.parseExpr(js_ast.LLowest) 6713 p.lexer.Expect(js_lexer.TCloseParen) 6714 return value 6715 } 6716 6717 name := p.lexer.Identifier 6718 nameRange := p.lexer.Range() 6719 p.lexer.Expect(js_lexer.TIdentifier) 6720 6721 // Forbid invalid identifiers 6722 if (p.fnOrArrowDataParse.await != allowIdent && name.String == "await") || 6723 (p.fnOrArrowDataParse.yield != allowIdent && name.String == "yield") { 6724 p.log.AddError(&p.tracker, nameRange, fmt.Sprintf("Cannot use %q as an identifier here:", name.String)) 6725 } 6726 6727 memberExpr := js_ast.Expr{Loc: nameRange.Loc, Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(name)}} 6728 6729 // Custom error reporting for error recovery 6730 var syntaxError logger.MsgData 6731 wrapRange := nameRange 6732 6733 loop: 6734 for { 6735 switch p.lexer.Token { 6736 case js_lexer.TExclamation: 6737 // Skip over TypeScript non-null assertions 6738 if p.lexer.HasNewlineBefore { 6739 break loop 6740 } 6741 if !p.options.ts.Parse { 6742 p.lexer.Unexpected() 6743 } 6744 wrapRange.Len = p.lexer.Range().End() - wrapRange.Loc.Start 6745 p.lexer.Next() 6746 6747 case js_lexer.TDot, js_lexer.TQuestionDot: 6748 // The grammar for "DecoratorMemberExpression" currently forbids "?." 6749 if p.lexer.Token == js_lexer.TQuestionDot && syntaxError.Location == nil { 6750 syntaxError = p.tracker.MsgData(p.lexer.Range(), "JavaScript decorator syntax does not allow \"?.\" here") 6751 } 6752 6753 p.lexer.Next() 6754 wrapRange.Len = p.lexer.Range().End() - wrapRange.Loc.Start 6755 6756 if p.lexer.Token == js_lexer.TPrivateIdentifier { 6757 name := p.lexer.Identifier 6758 memberExpr.Data = &js_ast.EIndex{ 6759 Target: memberExpr, 6760 Index: js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EPrivateIdentifier{Ref: p.storeNameInRef(name)}}, 6761 } 6762 p.reportPrivateNameUsage(name.String) 6763 p.lexer.Next() 6764 } else { 6765 memberExpr.Data = &js_ast.EDot{ 6766 Target: memberExpr, 6767 Name: p.lexer.Identifier.String, 6768 NameLoc: p.lexer.Loc(), 6769 } 6770 p.lexer.Expect(js_lexer.TIdentifier) 6771 } 6772 6773 case js_lexer.TOpenParen: 6774 args, closeParenLoc, isMultiLine := p.parseCallArgs() 6775 memberExpr.Data = &js_ast.ECall{ 6776 Target: memberExpr, 6777 Args: args, 6778 CloseParenLoc: closeParenLoc, 6779 IsMultiLine: isMultiLine, 6780 Kind: js_ast.TargetWasOriginallyPropertyAccess, 6781 } 6782 wrapRange.Len = closeParenLoc.Start + 1 - wrapRange.Loc.Start 6783 6784 // The grammar for "DecoratorCallExpression" currently forbids anything after it 6785 if p.lexer.Token == js_lexer.TDot { 6786 if syntaxError.Location == nil { 6787 syntaxError = p.tracker.MsgData(p.lexer.Range(), "JavaScript decorator syntax does not allow \".\" after a call expression") 6788 } 6789 continue 6790 } 6791 break loop 6792 6793 default: 6794 // "@x<y>" 6795 // "@x.y<z>" 6796 if !p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{}) { 6797 break loop 6798 } 6799 } 6800 } 6801 6802 // Suggest that non-decorator expressions be wrapped in parentheses 6803 if syntaxError.Location != nil { 6804 var notes []logger.MsgData 6805 if text := p.source.TextForRange(wrapRange); !strings.ContainsRune(text, '\n') { 6806 note := p.tracker.MsgData(wrapRange, "Wrap this decorator in parentheses to allow arbitrary expressions:") 6807 note.Location.Suggestion = fmt.Sprintf("(%s)", text) 6808 notes = []logger.MsgData{note} 6809 } 6810 p.log.AddMsg(logger.Msg{ 6811 Kind: logger.Error, 6812 Data: syntaxError, 6813 Notes: notes, 6814 }) 6815 } 6816 6817 return memberExpr 6818 } 6819 6820 type lexicalDecl uint8 6821 6822 const ( 6823 lexicalDeclForbid lexicalDecl = iota 6824 lexicalDeclAllowAll 6825 lexicalDeclAllowFnInsideIf 6826 lexicalDeclAllowFnInsideLabel 6827 ) 6828 6829 type parseStmtOpts struct { 6830 deferredDecorators *deferredDecorators 6831 lexicalDecl lexicalDecl 6832 isModuleScope bool 6833 isNamespaceScope bool 6834 isExport bool 6835 isExportDefault bool 6836 isNameOptional bool // For "export default" pseudo-statements 6837 isTypeScriptDeclare bool 6838 isForLoopInit bool 6839 isForAwaitLoopInit bool 6840 allowDirectivePrologue bool 6841 hasNoSideEffectsComment bool 6842 isUsingStmt bool 6843 } 6844 6845 func (p *parser) parseStmt(opts parseStmtOpts) js_ast.Stmt { 6846 loc := p.lexer.Loc() 6847 6848 if (p.lexer.HasCommentBefore & js_lexer.NoSideEffectsCommentBefore) != 0 { 6849 opts.hasNoSideEffectsComment = true 6850 } 6851 6852 // Do not attach any leading comments to the next expression 6853 p.lexer.CommentsBeforeToken = p.lexer.CommentsBeforeToken[:0] 6854 6855 switch p.lexer.Token { 6856 case js_lexer.TSemicolon: 6857 p.lexer.Next() 6858 return js_ast.Stmt{Loc: loc, Data: js_ast.SEmptyShared} 6859 6860 case js_lexer.TExport: 6861 previousExportKeyword := p.esmExportKeyword 6862 if opts.isModuleScope { 6863 p.esmExportKeyword = p.lexer.Range() 6864 } else if !opts.isNamespaceScope { 6865 p.lexer.Unexpected() 6866 } 6867 p.lexer.Next() 6868 6869 switch p.lexer.Token { 6870 case js_lexer.TClass, js_lexer.TConst, js_lexer.TFunction, js_lexer.TVar, js_lexer.TAt: 6871 opts.isExport = true 6872 return p.parseStmt(opts) 6873 6874 case js_lexer.TImport: 6875 // "export import foo = bar" 6876 if p.options.ts.Parse && (opts.isModuleScope || opts.isNamespaceScope) { 6877 opts.isExport = true 6878 return p.parseStmt(opts) 6879 } 6880 6881 p.lexer.Unexpected() 6882 return js_ast.Stmt{} 6883 6884 case js_lexer.TEnum: 6885 if !p.options.ts.Parse { 6886 p.lexer.Unexpected() 6887 } 6888 opts.isExport = true 6889 return p.parseStmt(opts) 6890 6891 case js_lexer.TIdentifier: 6892 if p.lexer.IsContextualKeyword("let") { 6893 opts.isExport = true 6894 return p.parseStmt(opts) 6895 } 6896 6897 if p.lexer.IsContextualKeyword("as") { 6898 // "export as namespace ns;" 6899 p.lexer.Next() 6900 p.lexer.ExpectContextualKeyword("namespace") 6901 p.lexer.Expect(js_lexer.TIdentifier) 6902 p.lexer.ExpectOrInsertSemicolon() 6903 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 6904 } 6905 6906 if p.lexer.IsContextualKeyword("async") { 6907 // "export async function foo() {}" 6908 asyncRange := p.lexer.Range() 6909 p.lexer.Next() 6910 if p.lexer.HasNewlineBefore { 6911 p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: asyncRange.End()}}, 6912 "Unexpected newline after \"async\"") 6913 panic(js_lexer.LexerPanic{}) 6914 } 6915 p.lexer.Expect(js_lexer.TFunction) 6916 opts.isExport = true 6917 return p.parseFnStmt(loc, opts, true /* isAsync */, asyncRange) 6918 } 6919 6920 if p.options.ts.Parse { 6921 switch p.lexer.Identifier.String { 6922 case "type": 6923 // "export type foo = ..." 6924 typeRange := p.lexer.Range() 6925 p.lexer.Next() 6926 if p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace && p.lexer.Token != js_lexer.TAsterisk { 6927 p.log.AddError(&p.tracker, logger.Range{Loc: logger.Loc{Start: typeRange.End()}}, 6928 "Unexpected newline after \"type\"") 6929 panic(js_lexer.LexerPanic{}) 6930 } 6931 p.skipTypeScriptTypeStmt(parseStmtOpts{isModuleScope: opts.isModuleScope, isExport: true}) 6932 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 6933 6934 case "namespace", "abstract", "module", "interface": 6935 // "export namespace Foo {}" 6936 // "export abstract class Foo {}" 6937 // "export module Foo {}" 6938 // "export interface Foo {}" 6939 opts.isExport = true 6940 return p.parseStmt(opts) 6941 6942 case "declare": 6943 // "export declare class Foo {}" 6944 opts.isExport = true 6945 opts.lexicalDecl = lexicalDeclAllowAll 6946 opts.isTypeScriptDeclare = true 6947 return p.parseStmt(opts) 6948 } 6949 } 6950 6951 p.lexer.Unexpected() 6952 return js_ast.Stmt{} 6953 6954 case js_lexer.TDefault: 6955 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 6956 p.lexer.Unexpected() 6957 } 6958 6959 defaultLoc := p.lexer.Loc() 6960 p.lexer.Next() 6961 6962 // Also pick up comments after the "default" keyword 6963 if (p.lexer.HasCommentBefore & js_lexer.NoSideEffectsCommentBefore) != 0 { 6964 opts.hasNoSideEffectsComment = true 6965 } 6966 6967 // The default name is lazily generated only if no other name is present 6968 createDefaultName := func() ast.LocRef { 6969 // This must be named "default" for when "--keep-names" is active 6970 defaultName := ast.LocRef{Loc: defaultLoc, Ref: p.newSymbol(ast.SymbolOther, "default")} 6971 p.currentScope.Generated = append(p.currentScope.Generated, defaultName.Ref) 6972 return defaultName 6973 } 6974 6975 // "export default async function() {}" 6976 // "export default async function foo() {}" 6977 if p.lexer.IsContextualKeyword("async") { 6978 asyncRange := p.lexer.Range() 6979 p.lexer.Next() 6980 6981 if p.lexer.Token == js_lexer.TFunction && !p.lexer.HasNewlineBefore { 6982 p.lexer.Next() 6983 stmt := p.parseFnStmt(loc, parseStmtOpts{ 6984 isNameOptional: true, 6985 lexicalDecl: lexicalDeclAllowAll, 6986 hasNoSideEffectsComment: opts.hasNoSideEffectsComment, 6987 }, true /* isAsync */, asyncRange) 6988 if _, ok := stmt.Data.(*js_ast.STypeScript); ok { 6989 return stmt // This was just a type annotation 6990 } 6991 6992 // Use the statement name if present, since it's a better name 6993 var defaultName ast.LocRef 6994 if s, ok := stmt.Data.(*js_ast.SFunction); ok && s.Fn.Name != nil { 6995 defaultName = ast.LocRef{Loc: defaultLoc, Ref: s.Fn.Name.Ref} 6996 } else { 6997 defaultName = createDefaultName() 6998 } 6999 7000 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportDefault{DefaultName: defaultName, Value: stmt}} 7001 } 7002 7003 defaultName := createDefaultName() 7004 expr := p.parseSuffix(p.parseAsyncPrefixExpr(asyncRange, js_ast.LComma, 0), js_ast.LComma, nil, 0) 7005 p.lexer.ExpectOrInsertSemicolon() 7006 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportDefault{ 7007 DefaultName: defaultName, Value: js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}}}} 7008 } 7009 7010 // "export default class {}" 7011 // "export default class Foo {}" 7012 // "export default @x class {}" 7013 // "export default @x class Foo {}" 7014 // "export default function() {}" 7015 // "export default function foo() {}" 7016 // "export default interface Foo {}" 7017 // "export default interface + 1" 7018 if p.lexer.Token == js_lexer.TFunction || p.lexer.Token == js_lexer.TClass || p.lexer.Token == js_lexer.TAt || 7019 (p.options.ts.Parse && p.lexer.IsContextualKeyword("interface")) { 7020 stmt := p.parseStmt(parseStmtOpts{ 7021 deferredDecorators: opts.deferredDecorators, 7022 isNameOptional: true, 7023 isExportDefault: true, 7024 lexicalDecl: lexicalDeclAllowAll, 7025 hasNoSideEffectsComment: opts.hasNoSideEffectsComment, 7026 }) 7027 7028 // Use the statement name if present, since it's a better name 7029 var defaultName ast.LocRef 7030 switch s := stmt.Data.(type) { 7031 case *js_ast.STypeScript, *js_ast.SExpr: 7032 return stmt // Handle the "interface" case above 7033 case *js_ast.SFunction: 7034 if s.Fn.Name != nil { 7035 defaultName = ast.LocRef{Loc: defaultLoc, Ref: s.Fn.Name.Ref} 7036 } else { 7037 defaultName = createDefaultName() 7038 } 7039 case *js_ast.SClass: 7040 if s.Class.Name != nil { 7041 defaultName = ast.LocRef{Loc: defaultLoc, Ref: s.Class.Name.Ref} 7042 } else { 7043 defaultName = createDefaultName() 7044 } 7045 default: 7046 panic("Internal error") 7047 } 7048 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportDefault{DefaultName: defaultName, Value: stmt}} 7049 } 7050 7051 isIdentifier := p.lexer.Token == js_lexer.TIdentifier 7052 name := p.lexer.Identifier.String 7053 expr := p.parseExpr(js_ast.LComma) 7054 7055 // "export default abstract class {}" 7056 // "export default abstract class Foo {}" 7057 if p.options.ts.Parse && isIdentifier && name == "abstract" && !p.lexer.HasNewlineBefore { 7058 if _, ok := expr.Data.(*js_ast.EIdentifier); ok && p.lexer.Token == js_lexer.TClass { 7059 stmt := p.parseClassStmt(loc, parseStmtOpts{ 7060 deferredDecorators: opts.deferredDecorators, 7061 isNameOptional: true, 7062 }) 7063 7064 // Use the statement name if present, since it's a better name 7065 var defaultName ast.LocRef 7066 if s, ok := stmt.Data.(*js_ast.SClass); ok && s.Class.Name != nil { 7067 defaultName = ast.LocRef{Loc: defaultLoc, Ref: s.Class.Name.Ref} 7068 } else { 7069 defaultName = createDefaultName() 7070 } 7071 7072 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportDefault{DefaultName: defaultName, Value: stmt}} 7073 } 7074 } 7075 7076 p.lexer.ExpectOrInsertSemicolon() 7077 defaultName := createDefaultName() 7078 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportDefault{ 7079 DefaultName: defaultName, Value: js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}}}} 7080 7081 case js_lexer.TAsterisk: 7082 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 7083 p.lexer.Unexpected() 7084 } 7085 7086 p.lexer.Next() 7087 var namespaceRef ast.Ref 7088 var alias *js_ast.ExportStarAlias 7089 var pathRange logger.Range 7090 var pathText string 7091 var assertOrWith *ast.ImportAssertOrWith 7092 var flags ast.ImportRecordFlags 7093 7094 if p.lexer.IsContextualKeyword("as") { 7095 // "export * as ns from 'path'" 7096 p.lexer.Next() 7097 name := p.parseClauseAlias("export") 7098 namespaceRef = p.storeNameInRef(name) 7099 alias = &js_ast.ExportStarAlias{Loc: p.lexer.Loc(), OriginalName: name.String} 7100 p.lexer.Next() 7101 p.lexer.ExpectContextualKeyword("from") 7102 pathRange, pathText, assertOrWith, flags = p.parsePath() 7103 } else { 7104 // "export * from 'path'" 7105 p.lexer.ExpectContextualKeyword("from") 7106 pathRange, pathText, assertOrWith, flags = p.parsePath() 7107 name := js_ast.GenerateNonUniqueNameFromPath(pathText) + "_star" 7108 namespaceRef = p.storeNameInRef(js_lexer.MaybeSubstring{String: name}) 7109 } 7110 importRecordIndex := p.addImportRecord(ast.ImportStmt, pathRange, pathText, assertOrWith, flags) 7111 7112 // Export-star statements anywhere in the file disable top-level const 7113 // local prefix because import cycles can be used to trigger TDZ 7114 p.currentScope.IsAfterConstLocalPrefix = true 7115 7116 p.lexer.ExpectOrInsertSemicolon() 7117 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportStar{ 7118 NamespaceRef: namespaceRef, 7119 Alias: alias, 7120 ImportRecordIndex: importRecordIndex, 7121 }} 7122 7123 case js_lexer.TOpenBrace: 7124 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 7125 p.lexer.Unexpected() 7126 } 7127 7128 items, isSingleLine := p.parseExportClause() 7129 if p.lexer.IsContextualKeyword("from") { 7130 // "export {} from 'path'" 7131 p.lexer.Next() 7132 pathLoc, pathText, assertOrWith, flags := p.parsePath() 7133 importRecordIndex := p.addImportRecord(ast.ImportStmt, pathLoc, pathText, assertOrWith, flags) 7134 name := "import_" + js_ast.GenerateNonUniqueNameFromPath(pathText) 7135 namespaceRef := p.storeNameInRef(js_lexer.MaybeSubstring{String: name}) 7136 7137 // Export clause statements anywhere in the file disable top-level const 7138 // local prefix because import cycles can be used to trigger TDZ 7139 p.currentScope.IsAfterConstLocalPrefix = true 7140 7141 p.lexer.ExpectOrInsertSemicolon() 7142 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportFrom{ 7143 Items: items, 7144 NamespaceRef: namespaceRef, 7145 ImportRecordIndex: importRecordIndex, 7146 IsSingleLine: isSingleLine, 7147 }} 7148 } 7149 7150 p.lexer.ExpectOrInsertSemicolon() 7151 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportClause{Items: items, IsSingleLine: isSingleLine}} 7152 7153 case js_lexer.TEquals: 7154 // "export = value;" 7155 p.esmExportKeyword = previousExportKeyword // This wasn't an ESM export statement after all 7156 if p.options.ts.Parse { 7157 p.lexer.Next() 7158 value := p.parseExpr(js_ast.LLowest) 7159 p.lexer.ExpectOrInsertSemicolon() 7160 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExportEquals{Value: value}} 7161 } 7162 p.lexer.Unexpected() 7163 return js_ast.Stmt{} 7164 7165 default: 7166 p.lexer.Unexpected() 7167 return js_ast.Stmt{} 7168 } 7169 7170 case js_lexer.TFunction: 7171 p.lexer.Next() 7172 return p.parseFnStmt(loc, opts, false /* isAsync */, logger.Range{}) 7173 7174 case js_lexer.TEnum: 7175 if !p.options.ts.Parse { 7176 p.lexer.Unexpected() 7177 } 7178 return p.parseTypeScriptEnumStmt(loc, opts) 7179 7180 case js_lexer.TAt: 7181 // Parse decorators before class statements, which are potentially exported 7182 scopeIndex := len(p.scopesInOrder) 7183 decorators := p.parseDecorators(p.currentScope, logger.Range{}, 0) 7184 7185 // "@x export @y class Foo {}" 7186 if opts.deferredDecorators != nil { 7187 p.log.AddError(&p.tracker, logger.Range{Loc: loc, Len: 1}, "Decorators are not valid here") 7188 p.discardScopesUpTo(scopeIndex) 7189 return p.parseStmt(opts) 7190 } 7191 7192 // If this turns out to be a "declare class" statement, we need to undo the 7193 // scopes that were potentially pushed while parsing the decorator arguments. 7194 // That can look like any one of the following: 7195 // 7196 // "@decorator declare class Foo {}" 7197 // "@decorator declare abstract class Foo {}" 7198 // "@decorator export declare class Foo {}" 7199 // "@decorator export declare abstract class Foo {}" 7200 // 7201 opts.deferredDecorators = &deferredDecorators{ 7202 decorators: decorators, 7203 } 7204 7205 stmt := p.parseStmt(opts) 7206 7207 // Check for valid decorator targets 7208 switch s := stmt.Data.(type) { 7209 case *js_ast.SClass: 7210 return stmt 7211 7212 case *js_ast.SExportDefault: 7213 switch s.Value.Data.(type) { 7214 case *js_ast.SClass: 7215 return stmt 7216 } 7217 7218 case *js_ast.STypeScript: 7219 if s.WasDeclareClass { 7220 // If this is a type declaration, discard any scopes that were pushed 7221 // while parsing decorators. Unlike with the class statements above, 7222 // these scopes won't end up being visited during the upcoming visit 7223 // pass because type declarations aren't visited at all. 7224 p.discardScopesUpTo(scopeIndex) 7225 return stmt 7226 } 7227 } 7228 7229 // Forbid decorators on anything other than a class statement 7230 p.log.AddError(&p.tracker, logger.Range{Loc: loc, Len: 1}, "Decorators are not valid here") 7231 stmt.Data = js_ast.STypeScriptShared 7232 p.discardScopesUpTo(scopeIndex) 7233 return stmt 7234 7235 case js_lexer.TClass: 7236 if opts.lexicalDecl != lexicalDeclAllowAll { 7237 p.forbidLexicalDecl(loc) 7238 } 7239 return p.parseClassStmt(loc, opts) 7240 7241 case js_lexer.TVar: 7242 p.lexer.Next() 7243 decls := p.parseAndDeclareDecls(ast.SymbolHoisted, opts) 7244 p.lexer.ExpectOrInsertSemicolon() 7245 return js_ast.Stmt{Loc: loc, Data: &js_ast.SLocal{ 7246 Kind: js_ast.LocalVar, 7247 Decls: decls, 7248 IsExport: opts.isExport, 7249 }} 7250 7251 case js_lexer.TConst: 7252 if opts.lexicalDecl != lexicalDeclAllowAll { 7253 p.forbidLexicalDecl(loc) 7254 } 7255 p.markSyntaxFeature(compat.ConstAndLet, p.lexer.Range()) 7256 p.lexer.Next() 7257 7258 if p.options.ts.Parse && p.lexer.Token == js_lexer.TEnum { 7259 return p.parseTypeScriptEnumStmt(loc, opts) 7260 } 7261 7262 decls := p.parseAndDeclareDecls(ast.SymbolConst, opts) 7263 p.lexer.ExpectOrInsertSemicolon() 7264 if !opts.isTypeScriptDeclare { 7265 p.requireInitializers(js_ast.LocalConst, decls) 7266 } 7267 return js_ast.Stmt{Loc: loc, Data: &js_ast.SLocal{ 7268 Kind: js_ast.LocalConst, 7269 Decls: decls, 7270 IsExport: opts.isExport, 7271 }} 7272 7273 case js_lexer.TIf: 7274 p.lexer.Next() 7275 p.lexer.Expect(js_lexer.TOpenParen) 7276 test := p.parseExpr(js_ast.LLowest) 7277 p.lexer.Expect(js_lexer.TCloseParen) 7278 isSingleLineYes := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7279 yes := p.parseStmt(parseStmtOpts{lexicalDecl: lexicalDeclAllowFnInsideIf}) 7280 var noOrNil js_ast.Stmt 7281 var isSingleLineNo bool 7282 if p.lexer.Token == js_lexer.TElse { 7283 p.lexer.Next() 7284 isSingleLineNo = !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7285 noOrNil = p.parseStmt(parseStmtOpts{lexicalDecl: lexicalDeclAllowFnInsideIf}) 7286 } 7287 return js_ast.Stmt{Loc: loc, Data: &js_ast.SIf{Test: test, Yes: yes, NoOrNil: noOrNil, IsSingleLineYes: isSingleLineYes, IsSingleLineNo: isSingleLineNo}} 7288 7289 case js_lexer.TDo: 7290 p.lexer.Next() 7291 body := p.parseStmt(parseStmtOpts{}) 7292 p.lexer.Expect(js_lexer.TWhile) 7293 p.lexer.Expect(js_lexer.TOpenParen) 7294 test := p.parseExpr(js_ast.LLowest) 7295 p.lexer.Expect(js_lexer.TCloseParen) 7296 7297 // This is a weird corner case where automatic semicolon insertion applies 7298 // even without a newline present 7299 if p.lexer.Token == js_lexer.TSemicolon { 7300 p.lexer.Next() 7301 } 7302 return js_ast.Stmt{Loc: loc, Data: &js_ast.SDoWhile{Body: body, Test: test}} 7303 7304 case js_lexer.TWhile: 7305 p.lexer.Next() 7306 p.lexer.Expect(js_lexer.TOpenParen) 7307 test := p.parseExpr(js_ast.LLowest) 7308 p.lexer.Expect(js_lexer.TCloseParen) 7309 isSingleLineBody := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7310 body := p.parseStmt(parseStmtOpts{}) 7311 return js_ast.Stmt{Loc: loc, Data: &js_ast.SWhile{Test: test, Body: body, IsSingleLineBody: isSingleLineBody}} 7312 7313 case js_lexer.TWith: 7314 p.lexer.Next() 7315 p.lexer.Expect(js_lexer.TOpenParen) 7316 test := p.parseExpr(js_ast.LLowest) 7317 bodyLoc := p.lexer.Loc() 7318 p.lexer.Expect(js_lexer.TCloseParen) 7319 7320 // Push a scope so we make sure to prevent any bare identifiers referenced 7321 // within the body from being renamed. Renaming them might change the 7322 // semantics of the code. 7323 p.pushScopeForParsePass(js_ast.ScopeWith, bodyLoc) 7324 isSingleLineBody := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7325 body := p.parseStmt(parseStmtOpts{}) 7326 p.popScope() 7327 7328 return js_ast.Stmt{Loc: loc, Data: &js_ast.SWith{Value: test, BodyLoc: bodyLoc, Body: body, IsSingleLineBody: isSingleLineBody}} 7329 7330 case js_lexer.TSwitch: 7331 p.lexer.Next() 7332 p.lexer.Expect(js_lexer.TOpenParen) 7333 test := p.parseExpr(js_ast.LLowest) 7334 p.lexer.Expect(js_lexer.TCloseParen) 7335 7336 bodyLoc := p.lexer.Loc() 7337 p.pushScopeForParsePass(js_ast.ScopeBlock, bodyLoc) 7338 defer p.popScope() 7339 7340 p.lexer.Expect(js_lexer.TOpenBrace) 7341 cases := []js_ast.Case{} 7342 foundDefault := false 7343 7344 for p.lexer.Token != js_lexer.TCloseBrace { 7345 var value js_ast.Expr 7346 body := []js_ast.Stmt{} 7347 caseLoc := p.lexer.Loc() 7348 7349 if p.lexer.Token == js_lexer.TDefault { 7350 if foundDefault { 7351 p.log.AddError(&p.tracker, p.lexer.Range(), "Multiple default clauses are not allowed") 7352 panic(js_lexer.LexerPanic{}) 7353 } 7354 foundDefault = true 7355 p.lexer.Next() 7356 p.lexer.Expect(js_lexer.TColon) 7357 } else { 7358 p.lexer.Expect(js_lexer.TCase) 7359 value = p.parseExpr(js_ast.LLowest) 7360 p.lexer.Expect(js_lexer.TColon) 7361 } 7362 7363 caseBody: 7364 for { 7365 switch p.lexer.Token { 7366 case js_lexer.TCloseBrace, js_lexer.TCase, js_lexer.TDefault: 7367 break caseBody 7368 7369 default: 7370 body = append(body, p.parseStmt(parseStmtOpts{lexicalDecl: lexicalDeclAllowAll})) 7371 } 7372 } 7373 7374 cases = append(cases, js_ast.Case{ValueOrNil: value, Body: body, Loc: caseLoc}) 7375 } 7376 7377 closeBraceLoc := p.lexer.Loc() 7378 p.lexer.Expect(js_lexer.TCloseBrace) 7379 return js_ast.Stmt{Loc: loc, Data: &js_ast.SSwitch{ 7380 Test: test, 7381 Cases: cases, 7382 BodyLoc: bodyLoc, 7383 CloseBraceLoc: closeBraceLoc, 7384 }} 7385 7386 case js_lexer.TTry: 7387 p.lexer.Next() 7388 blockLoc := p.lexer.Loc() 7389 p.lexer.Expect(js_lexer.TOpenBrace) 7390 p.pushScopeForParsePass(js_ast.ScopeBlock, loc) 7391 body := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{}) 7392 p.popScope() 7393 closeBraceLoc := p.lexer.Loc() 7394 p.lexer.Next() 7395 7396 var catch *js_ast.Catch = nil 7397 var finally *js_ast.Finally = nil 7398 7399 if p.lexer.Token == js_lexer.TCatch { 7400 catchLoc := p.lexer.Loc() 7401 p.pushScopeForParsePass(js_ast.ScopeCatchBinding, catchLoc) 7402 p.lexer.Next() 7403 var bindingOrNil js_ast.Binding 7404 7405 // The catch binding is optional, and can be omitted 7406 if p.lexer.Token == js_lexer.TOpenBrace { 7407 if p.options.unsupportedJSFeatures.Has(compat.OptionalCatchBinding) { 7408 // Generate a new symbol for the catch binding for older browsers 7409 ref := p.newSymbol(ast.SymbolOther, "e") 7410 p.currentScope.Generated = append(p.currentScope.Generated, ref) 7411 bindingOrNil = js_ast.Binding{Loc: p.lexer.Loc(), Data: &js_ast.BIdentifier{Ref: ref}} 7412 } 7413 } else { 7414 p.lexer.Expect(js_lexer.TOpenParen) 7415 bindingOrNil = p.parseBinding(parseBindingOpts{}) 7416 7417 // Skip over types 7418 if p.options.ts.Parse && p.lexer.Token == js_lexer.TColon { 7419 p.lexer.Expect(js_lexer.TColon) 7420 p.skipTypeScriptType(js_ast.LLowest) 7421 } 7422 7423 p.lexer.Expect(js_lexer.TCloseParen) 7424 7425 // Bare identifiers are a special case 7426 kind := ast.SymbolOther 7427 if _, ok := bindingOrNil.Data.(*js_ast.BIdentifier); ok { 7428 kind = ast.SymbolCatchIdentifier 7429 } 7430 p.declareBinding(kind, bindingOrNil, parseStmtOpts{}) 7431 } 7432 7433 blockLoc := p.lexer.Loc() 7434 p.lexer.Expect(js_lexer.TOpenBrace) 7435 7436 p.pushScopeForParsePass(js_ast.ScopeBlock, blockLoc) 7437 stmts := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{}) 7438 p.popScope() 7439 7440 closeBraceLoc := p.lexer.Loc() 7441 p.lexer.Next() 7442 catch = &js_ast.Catch{Loc: catchLoc, BindingOrNil: bindingOrNil, BlockLoc: blockLoc, Block: js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}} 7443 p.popScope() 7444 } 7445 7446 if p.lexer.Token == js_lexer.TFinally || catch == nil { 7447 finallyLoc := p.lexer.Loc() 7448 p.pushScopeForParsePass(js_ast.ScopeBlock, finallyLoc) 7449 p.lexer.Expect(js_lexer.TFinally) 7450 p.lexer.Expect(js_lexer.TOpenBrace) 7451 stmts := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{}) 7452 closeBraceLoc := p.lexer.Loc() 7453 p.lexer.Next() 7454 finally = &js_ast.Finally{Loc: finallyLoc, Block: js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}} 7455 p.popScope() 7456 } 7457 7458 return js_ast.Stmt{Loc: loc, Data: &js_ast.STry{ 7459 BlockLoc: blockLoc, 7460 Block: js_ast.SBlock{Stmts: body, CloseBraceLoc: closeBraceLoc}, 7461 Catch: catch, 7462 Finally: finally, 7463 }} 7464 7465 case js_lexer.TFor: 7466 p.pushScopeForParsePass(js_ast.ScopeBlock, loc) 7467 defer p.popScope() 7468 7469 p.lexer.Next() 7470 7471 // "for await (let x of y) {}" 7472 var awaitRange logger.Range 7473 if p.lexer.IsContextualKeyword("await") { 7474 awaitRange = p.lexer.Range() 7475 if p.fnOrArrowDataParse.await != allowExpr { 7476 p.log.AddError(&p.tracker, awaitRange, "Cannot use \"await\" outside an async function") 7477 awaitRange = logger.Range{} 7478 } else { 7479 didGenerateError := false 7480 if p.fnOrArrowDataParse.isTopLevel { 7481 p.topLevelAwaitKeyword = awaitRange 7482 } 7483 if !didGenerateError && p.options.unsupportedJSFeatures.Has(compat.AsyncAwait) && p.options.unsupportedJSFeatures.Has(compat.Generator) { 7484 // If for-await loops aren't supported, then we only support lowering 7485 // if either async/await or generators is supported. Otherwise we 7486 // cannot lower for-await loops. 7487 p.markSyntaxFeature(compat.ForAwait, awaitRange) 7488 } 7489 } 7490 p.lexer.Next() 7491 } 7492 7493 p.lexer.Expect(js_lexer.TOpenParen) 7494 7495 var initOrNil js_ast.Stmt 7496 var testOrNil js_ast.Expr 7497 var updateOrNil js_ast.Expr 7498 7499 // "in" expressions aren't allowed here 7500 p.allowIn = false 7501 7502 var badLetRange logger.Range 7503 if p.lexer.IsContextualKeyword("let") { 7504 badLetRange = p.lexer.Range() 7505 } 7506 decls := []js_ast.Decl{} 7507 initLoc := p.lexer.Loc() 7508 isVar := false 7509 switch p.lexer.Token { 7510 case js_lexer.TVar: 7511 isVar = true 7512 p.lexer.Next() 7513 decls = p.parseAndDeclareDecls(ast.SymbolHoisted, parseStmtOpts{}) 7514 initOrNil = js_ast.Stmt{Loc: initLoc, Data: &js_ast.SLocal{Kind: js_ast.LocalVar, Decls: decls}} 7515 7516 case js_lexer.TConst: 7517 p.markSyntaxFeature(compat.ConstAndLet, p.lexer.Range()) 7518 p.lexer.Next() 7519 decls = p.parseAndDeclareDecls(ast.SymbolConst, parseStmtOpts{}) 7520 initOrNil = js_ast.Stmt{Loc: initLoc, Data: &js_ast.SLocal{Kind: js_ast.LocalConst, Decls: decls}} 7521 7522 case js_lexer.TSemicolon: 7523 7524 default: 7525 var expr js_ast.Expr 7526 var stmt js_ast.Stmt 7527 expr, stmt, decls = p.parseExprOrLetOrUsingStmt(parseStmtOpts{ 7528 lexicalDecl: lexicalDeclAllowAll, 7529 isForLoopInit: true, 7530 isForAwaitLoopInit: awaitRange.Len > 0, 7531 }) 7532 if stmt.Data != nil { 7533 badLetRange = logger.Range{} 7534 initOrNil = stmt 7535 } else { 7536 initOrNil = js_ast.Stmt{Loc: expr.Loc, Data: &js_ast.SExpr{Value: expr}} 7537 } 7538 } 7539 7540 // "in" expressions are allowed again 7541 p.allowIn = true 7542 7543 // Detect for-of loops 7544 if p.lexer.IsContextualKeyword("of") || awaitRange.Len > 0 { 7545 if badLetRange.Len > 0 { 7546 p.log.AddError(&p.tracker, badLetRange, "\"let\" must be wrapped in parentheses to be used as an expression here:") 7547 } 7548 if awaitRange.Len > 0 && !p.lexer.IsContextualKeyword("of") { 7549 if initOrNil.Data != nil { 7550 p.lexer.ExpectedString("\"of\"") 7551 } else { 7552 p.lexer.Unexpected() 7553 } 7554 } 7555 p.forbidInitializers(decls, "of", false) 7556 p.markSyntaxFeature(compat.ForOf, p.lexer.Range()) 7557 p.lexer.Next() 7558 value := p.parseExpr(js_ast.LComma) 7559 p.lexer.Expect(js_lexer.TCloseParen) 7560 isSingleLineBody := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7561 body := p.parseStmt(parseStmtOpts{}) 7562 return js_ast.Stmt{Loc: loc, Data: &js_ast.SForOf{Await: awaitRange, Init: initOrNil, Value: value, Body: body, IsSingleLineBody: isSingleLineBody}} 7563 } 7564 7565 // Detect for-in loops 7566 if p.lexer.Token == js_lexer.TIn { 7567 p.forbidInitializers(decls, "in", isVar) 7568 if len(decls) == 1 { 7569 if local, ok := initOrNil.Data.(*js_ast.SLocal); ok { 7570 if local.Kind == js_ast.LocalUsing { 7571 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, initOrNil.Loc), "\"using\" declarations are not allowed here") 7572 } else if local.Kind == js_ast.LocalAwaitUsing { 7573 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, initOrNil.Loc), "\"await using\" declarations are not allowed here") 7574 } 7575 } 7576 } 7577 p.lexer.Next() 7578 value := p.parseExpr(js_ast.LLowest) 7579 p.lexer.Expect(js_lexer.TCloseParen) 7580 isSingleLineBody := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7581 body := p.parseStmt(parseStmtOpts{}) 7582 return js_ast.Stmt{Loc: loc, Data: &js_ast.SForIn{Init: initOrNil, Value: value, Body: body, IsSingleLineBody: isSingleLineBody}} 7583 } 7584 7585 p.lexer.Expect(js_lexer.TSemicolon) 7586 7587 // "await using" declarations are only allowed in for-of loops 7588 if local, ok := initOrNil.Data.(*js_ast.SLocal); ok && local.Kind == js_ast.LocalAwaitUsing { 7589 p.log.AddError(&p.tracker, js_lexer.RangeOfIdentifier(p.source, initOrNil.Loc), "\"await using\" declarations are not allowed here") 7590 } 7591 7592 // Only require "const" statement initializers when we know we're a normal for loop 7593 if local, ok := initOrNil.Data.(*js_ast.SLocal); ok && (local.Kind == js_ast.LocalConst || local.Kind == js_ast.LocalUsing) { 7594 p.requireInitializers(local.Kind, decls) 7595 } 7596 7597 if p.lexer.Token != js_lexer.TSemicolon { 7598 testOrNil = p.parseExpr(js_ast.LLowest) 7599 } 7600 7601 p.lexer.Expect(js_lexer.TSemicolon) 7602 7603 if p.lexer.Token != js_lexer.TCloseParen { 7604 updateOrNil = p.parseExpr(js_ast.LLowest) 7605 } 7606 7607 p.lexer.Expect(js_lexer.TCloseParen) 7608 isSingleLineBody := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7609 body := p.parseStmt(parseStmtOpts{}) 7610 return js_ast.Stmt{Loc: loc, Data: &js_ast.SFor{ 7611 InitOrNil: initOrNil, 7612 TestOrNil: testOrNil, 7613 UpdateOrNil: updateOrNil, 7614 Body: body, 7615 IsSingleLineBody: isSingleLineBody, 7616 }} 7617 7618 case js_lexer.TImport: 7619 previousImportStatementKeyword := p.esmImportStatementKeyword 7620 p.esmImportStatementKeyword = p.lexer.Range() 7621 p.lexer.Next() 7622 stmt := js_ast.SImport{} 7623 wasOriginallyBareImport := false 7624 7625 // "export import foo = bar" 7626 // "import foo = bar" in a namespace 7627 if (opts.isExport || (opts.isNamespaceScope && !opts.isTypeScriptDeclare)) && p.lexer.Token != js_lexer.TIdentifier { 7628 p.lexer.Expected(js_lexer.TIdentifier) 7629 } 7630 7631 syntaxBeforePath: 7632 switch p.lexer.Token { 7633 case js_lexer.TOpenParen, js_lexer.TDot: 7634 // "import('path')" 7635 // "import.meta" 7636 p.esmImportStatementKeyword = previousImportStatementKeyword // This wasn't an ESM import statement after all 7637 expr := p.parseSuffix(p.parseImportExpr(loc, js_ast.LLowest), js_ast.LLowest, nil, 0) 7638 p.lexer.ExpectOrInsertSemicolon() 7639 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}} 7640 7641 case js_lexer.TStringLiteral, js_lexer.TNoSubstitutionTemplateLiteral: 7642 // "import 'path'" 7643 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 7644 p.lexer.Unexpected() 7645 return js_ast.Stmt{} 7646 } 7647 7648 wasOriginallyBareImport = true 7649 7650 case js_lexer.TAsterisk: 7651 // "import * as ns from 'path'" 7652 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 7653 p.lexer.Unexpected() 7654 return js_ast.Stmt{} 7655 } 7656 7657 p.lexer.Next() 7658 p.lexer.ExpectContextualKeyword("as") 7659 stmt.NamespaceRef = p.storeNameInRef(p.lexer.Identifier) 7660 starLoc := p.lexer.Loc() 7661 stmt.StarNameLoc = &starLoc 7662 p.lexer.Expect(js_lexer.TIdentifier) 7663 p.lexer.ExpectContextualKeyword("from") 7664 7665 case js_lexer.TOpenBrace: 7666 // "import {item1, item2} from 'path'" 7667 if !opts.isModuleScope && (!opts.isNamespaceScope || !opts.isTypeScriptDeclare) { 7668 p.lexer.Unexpected() 7669 return js_ast.Stmt{} 7670 } 7671 7672 items, isSingleLine := p.parseImportClause() 7673 stmt.Items = &items 7674 stmt.IsSingleLine = isSingleLine 7675 p.lexer.ExpectContextualKeyword("from") 7676 7677 case js_lexer.TIdentifier: 7678 // "import defaultItem from 'path'" 7679 // "import foo = bar" 7680 if !opts.isModuleScope && !opts.isNamespaceScope { 7681 p.lexer.Unexpected() 7682 return js_ast.Stmt{} 7683 } 7684 7685 defaultName := p.lexer.Identifier 7686 stmt.DefaultName = &ast.LocRef{Loc: p.lexer.Loc(), Ref: p.storeNameInRef(defaultName)} 7687 p.lexer.Next() 7688 7689 if p.options.ts.Parse { 7690 // Skip over type-only imports 7691 if defaultName.String == "type" { 7692 switch p.lexer.Token { 7693 case js_lexer.TIdentifier: 7694 nameSubstring := p.lexer.Identifier 7695 nameLoc := p.lexer.Loc() 7696 p.lexer.Next() 7697 if p.lexer.Token == js_lexer.TEquals { 7698 // "import type foo = require('bar');" 7699 // "import type foo = bar.baz;" 7700 opts.isTypeScriptDeclare = true 7701 return p.parseTypeScriptImportEqualsStmt(loc, opts, nameLoc, nameSubstring.String) 7702 } else if p.lexer.Token == js_lexer.TStringLiteral && nameSubstring.String == "from" { 7703 // "import type from 'bar';" 7704 break syntaxBeforePath 7705 } else { 7706 // "import type foo from 'bar';" 7707 p.lexer.ExpectContextualKeyword("from") 7708 p.parsePath() 7709 p.lexer.ExpectOrInsertSemicolon() 7710 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7711 } 7712 7713 case js_lexer.TAsterisk: 7714 // "import type * as foo from 'bar';" 7715 p.lexer.Next() 7716 p.lexer.ExpectContextualKeyword("as") 7717 p.lexer.Expect(js_lexer.TIdentifier) 7718 p.lexer.ExpectContextualKeyword("from") 7719 p.parsePath() 7720 p.lexer.ExpectOrInsertSemicolon() 7721 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7722 7723 case js_lexer.TOpenBrace: 7724 // "import type {foo} from 'bar';" 7725 p.parseImportClause() 7726 p.lexer.ExpectContextualKeyword("from") 7727 p.parsePath() 7728 p.lexer.ExpectOrInsertSemicolon() 7729 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7730 } 7731 } 7732 7733 // Parse TypeScript import assignment statements 7734 if p.lexer.Token == js_lexer.TEquals || opts.isExport || (opts.isNamespaceScope && !opts.isTypeScriptDeclare) { 7735 p.esmImportStatementKeyword = previousImportStatementKeyword // This wasn't an ESM import statement after all 7736 return p.parseTypeScriptImportEqualsStmt(loc, opts, stmt.DefaultName.Loc, defaultName.String) 7737 } 7738 } 7739 7740 if p.lexer.Token == js_lexer.TComma { 7741 p.lexer.Next() 7742 switch p.lexer.Token { 7743 case js_lexer.TAsterisk: 7744 // "import defaultItem, * as ns from 'path'" 7745 p.lexer.Next() 7746 p.lexer.ExpectContextualKeyword("as") 7747 stmt.NamespaceRef = p.storeNameInRef(p.lexer.Identifier) 7748 starLoc := p.lexer.Loc() 7749 stmt.StarNameLoc = &starLoc 7750 p.lexer.Expect(js_lexer.TIdentifier) 7751 7752 case js_lexer.TOpenBrace: 7753 // "import defaultItem, {item1, item2} from 'path'" 7754 items, isSingleLine := p.parseImportClause() 7755 stmt.Items = &items 7756 stmt.IsSingleLine = isSingleLine 7757 7758 default: 7759 p.lexer.Unexpected() 7760 } 7761 } 7762 7763 p.lexer.ExpectContextualKeyword("from") 7764 7765 default: 7766 p.lexer.Unexpected() 7767 return js_ast.Stmt{} 7768 } 7769 7770 pathLoc, pathText, assertOrWith, flags := p.parsePath() 7771 p.lexer.ExpectOrInsertSemicolon() 7772 7773 // If TypeScript's "preserveValueImports": true setting is active, TypeScript's 7774 // "importsNotUsedAsValues": "preserve" setting is NOT active, and the import 7775 // clause is present and empty (or is non-empty but filled with type-only 7776 // items), then the import statement should still be removed entirely to match 7777 // the behavior of the TypeScript compiler: 7778 // 7779 // // Keep these 7780 // import 'x' 7781 // import { y } from 'x' 7782 // import { y, type z } from 'x' 7783 // 7784 // // Remove these 7785 // import {} from 'x' 7786 // import { type y } from 'x' 7787 // 7788 // // Remove the items from these 7789 // import d, {} from 'x' 7790 // import d, { type y } from 'x' 7791 // 7792 if p.options.ts.Parse && p.options.ts.Config.UnusedImportFlags() == config.TSUnusedImport_KeepValues && stmt.Items != nil && len(*stmt.Items) == 0 { 7793 if stmt.DefaultName == nil { 7794 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7795 } 7796 stmt.Items = nil 7797 } 7798 7799 if wasOriginallyBareImport { 7800 flags |= ast.WasOriginallyBareImport 7801 } 7802 stmt.ImportRecordIndex = p.addImportRecord(ast.ImportStmt, pathLoc, pathText, assertOrWith, flags) 7803 7804 if stmt.StarNameLoc != nil { 7805 name := p.loadNameFromRef(stmt.NamespaceRef) 7806 stmt.NamespaceRef = p.declareSymbol(ast.SymbolImport, *stmt.StarNameLoc, name) 7807 } else { 7808 // Generate a symbol for the namespace 7809 name := "import_" + js_ast.GenerateNonUniqueNameFromPath(pathText) 7810 stmt.NamespaceRef = p.newSymbol(ast.SymbolOther, name) 7811 p.currentScope.Generated = append(p.currentScope.Generated, stmt.NamespaceRef) 7812 } 7813 itemRefs := make(map[string]ast.LocRef) 7814 7815 // Link the default item to the namespace 7816 if stmt.DefaultName != nil { 7817 name := p.loadNameFromRef(stmt.DefaultName.Ref) 7818 ref := p.declareSymbol(ast.SymbolImport, stmt.DefaultName.Loc, name) 7819 p.isImportItem[ref] = true 7820 stmt.DefaultName.Ref = ref 7821 } 7822 7823 // Link each import item to the namespace 7824 if stmt.Items != nil { 7825 for i, item := range *stmt.Items { 7826 name := p.loadNameFromRef(item.Name.Ref) 7827 ref := p.declareSymbol(ast.SymbolImport, item.Name.Loc, name) 7828 p.checkForUnrepresentableIdentifier(item.AliasLoc, item.Alias) 7829 p.isImportItem[ref] = true 7830 (*stmt.Items)[i].Name.Ref = ref 7831 itemRefs[item.Alias] = ast.LocRef{Loc: item.Name.Loc, Ref: ref} 7832 } 7833 } 7834 7835 // Track the items for this namespace 7836 p.importItemsForNamespace[stmt.NamespaceRef] = namespaceImportItems{ 7837 entries: itemRefs, 7838 importRecordIndex: stmt.ImportRecordIndex, 7839 } 7840 7841 // Import statements anywhere in the file disable top-level const 7842 // local prefix because import cycles can be used to trigger TDZ 7843 p.currentScope.IsAfterConstLocalPrefix = true 7844 return js_ast.Stmt{Loc: loc, Data: &stmt} 7845 7846 case js_lexer.TBreak: 7847 p.lexer.Next() 7848 name := p.parseLabelName() 7849 p.lexer.ExpectOrInsertSemicolon() 7850 return js_ast.Stmt{Loc: loc, Data: &js_ast.SBreak{Label: name}} 7851 7852 case js_lexer.TContinue: 7853 p.lexer.Next() 7854 name := p.parseLabelName() 7855 p.lexer.ExpectOrInsertSemicolon() 7856 return js_ast.Stmt{Loc: loc, Data: &js_ast.SContinue{Label: name}} 7857 7858 case js_lexer.TReturn: 7859 if p.fnOrArrowDataParse.isReturnDisallowed { 7860 p.log.AddError(&p.tracker, p.lexer.Range(), "A return statement cannot be used here:") 7861 } 7862 p.lexer.Next() 7863 var value js_ast.Expr 7864 if p.lexer.Token != js_lexer.TSemicolon && 7865 !p.lexer.HasNewlineBefore && 7866 p.lexer.Token != js_lexer.TCloseBrace && 7867 p.lexer.Token != js_lexer.TEndOfFile { 7868 value = p.parseExpr(js_ast.LLowest) 7869 } 7870 p.latestReturnHadSemicolon = p.lexer.Token == js_lexer.TSemicolon 7871 p.lexer.ExpectOrInsertSemicolon() 7872 return js_ast.Stmt{Loc: loc, Data: &js_ast.SReturn{ValueOrNil: value}} 7873 7874 case js_lexer.TThrow: 7875 p.lexer.Next() 7876 if p.lexer.HasNewlineBefore { 7877 endLoc := logger.Loc{Start: loc.Start + 5} 7878 p.log.AddError(&p.tracker, logger.Range{Loc: endLoc}, 7879 "Unexpected newline after \"throw\"") 7880 return js_ast.Stmt{Loc: loc, Data: &js_ast.SThrow{Value: js_ast.Expr{Loc: endLoc, Data: js_ast.ENullShared}}} 7881 } 7882 expr := p.parseExpr(js_ast.LLowest) 7883 p.lexer.ExpectOrInsertSemicolon() 7884 return js_ast.Stmt{Loc: loc, Data: &js_ast.SThrow{Value: expr}} 7885 7886 case js_lexer.TDebugger: 7887 p.lexer.Next() 7888 p.lexer.ExpectOrInsertSemicolon() 7889 return js_ast.Stmt{Loc: loc, Data: js_ast.SDebuggerShared} 7890 7891 case js_lexer.TOpenBrace: 7892 p.pushScopeForParsePass(js_ast.ScopeBlock, loc) 7893 defer p.popScope() 7894 7895 p.lexer.Next() 7896 stmts := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{}) 7897 closeBraceLoc := p.lexer.Loc() 7898 p.lexer.Next() 7899 return js_ast.Stmt{Loc: loc, Data: &js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}} 7900 7901 default: 7902 isIdentifier := p.lexer.Token == js_lexer.TIdentifier 7903 nameRange := p.lexer.Range() 7904 name := p.lexer.Identifier.String 7905 7906 // Parse either an async function, an async expression, or a normal expression 7907 var expr js_ast.Expr 7908 if isIdentifier && p.lexer.Raw() == "async" { 7909 p.lexer.Next() 7910 if p.lexer.Token == js_lexer.TFunction && !p.lexer.HasNewlineBefore { 7911 p.lexer.Next() 7912 return p.parseFnStmt(nameRange.Loc, opts, true /* isAsync */, nameRange) 7913 } 7914 expr = p.parseSuffix(p.parseAsyncPrefixExpr(nameRange, js_ast.LLowest, 0), js_ast.LLowest, nil, 0) 7915 } else { 7916 var stmt js_ast.Stmt 7917 expr, stmt, _ = p.parseExprOrLetOrUsingStmt(opts) 7918 if stmt.Data != nil { 7919 p.lexer.ExpectOrInsertSemicolon() 7920 return stmt 7921 } 7922 } 7923 7924 if isIdentifier { 7925 if ident, ok := expr.Data.(*js_ast.EIdentifier); ok { 7926 if p.lexer.Token == js_lexer.TColon && opts.deferredDecorators == nil { 7927 p.pushScopeForParsePass(js_ast.ScopeLabel, loc) 7928 defer p.popScope() 7929 7930 // Parse a labeled statement 7931 p.lexer.Next() 7932 name := ast.LocRef{Loc: expr.Loc, Ref: ident.Ref} 7933 nestedOpts := parseStmtOpts{} 7934 if opts.lexicalDecl == lexicalDeclAllowAll || opts.lexicalDecl == lexicalDeclAllowFnInsideLabel { 7935 nestedOpts.lexicalDecl = lexicalDeclAllowFnInsideLabel 7936 } 7937 isSingleLineStmt := !p.lexer.HasNewlineBefore && p.lexer.Token != js_lexer.TOpenBrace 7938 stmt := p.parseStmt(nestedOpts) 7939 return js_ast.Stmt{Loc: loc, Data: &js_ast.SLabel{Name: name, Stmt: stmt, IsSingleLineStmt: isSingleLineStmt}} 7940 } 7941 7942 if p.options.ts.Parse { 7943 switch name { 7944 case "type": 7945 if !p.lexer.HasNewlineBefore && p.lexer.Token == js_lexer.TIdentifier { 7946 // "type Foo = any" 7947 p.skipTypeScriptTypeStmt(parseStmtOpts{isModuleScope: opts.isModuleScope}) 7948 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7949 } 7950 7951 case "namespace", "module": 7952 // "namespace Foo {}" 7953 // "module Foo {}" 7954 // "declare module 'fs' {}" 7955 // "declare module 'fs';" 7956 if !p.lexer.HasNewlineBefore && (opts.isModuleScope || opts.isNamespaceScope) && (p.lexer.Token == js_lexer.TIdentifier || 7957 (p.lexer.Token == js_lexer.TStringLiteral && opts.isTypeScriptDeclare)) { 7958 return p.parseTypeScriptNamespaceStmt(loc, opts) 7959 } 7960 7961 case "interface": 7962 // "interface Foo {}" 7963 // "export default interface Foo {}" 7964 // "export default interface \n Foo {}" 7965 if !p.lexer.HasNewlineBefore || opts.isExportDefault { 7966 p.skipTypeScriptInterfaceStmt(parseStmtOpts{isModuleScope: opts.isModuleScope}) 7967 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7968 } 7969 7970 // "interface \n Foo {}" 7971 // "export interface \n Foo {}" 7972 if opts.isExport { 7973 p.log.AddError(&p.tracker, nameRange, "Unexpected \"interface\"") 7974 panic(js_lexer.LexerPanic{}) 7975 } 7976 7977 case "abstract": 7978 if !p.lexer.HasNewlineBefore && p.lexer.Token == js_lexer.TClass { 7979 return p.parseClassStmt(loc, opts) 7980 } 7981 7982 case "global": 7983 // "declare module 'fs' { global { namespace NodeJS {} } }" 7984 if opts.isNamespaceScope && opts.isTypeScriptDeclare && p.lexer.Token == js_lexer.TOpenBrace { 7985 p.lexer.Next() 7986 p.parseStmtsUpTo(js_lexer.TCloseBrace, opts) 7987 p.lexer.Next() 7988 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 7989 } 7990 7991 case "declare": 7992 if !p.lexer.HasNewlineBefore { 7993 opts.lexicalDecl = lexicalDeclAllowAll 7994 opts.isTypeScriptDeclare = true 7995 7996 // "declare global { ... }" 7997 if p.lexer.IsContextualKeyword("global") { 7998 p.lexer.Next() 7999 p.lexer.Expect(js_lexer.TOpenBrace) 8000 p.parseStmtsUpTo(js_lexer.TCloseBrace, opts) 8001 p.lexer.Next() 8002 return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} 8003 } 8004 8005 // "declare const x: any" 8006 scopeIndex := len(p.scopesInOrder) 8007 oldLexer := p.lexer 8008 stmt := p.parseStmt(opts) 8009 typeDeclarationData := js_ast.STypeScriptShared 8010 switch s := stmt.Data.(type) { 8011 case *js_ast.SEmpty: 8012 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}} 8013 8014 case *js_ast.STypeScript: 8015 // Type declarations are expected. Propagate the "declare class" 8016 // status in case our caller is a decorator that needs to know 8017 // this was a "declare class" statement. 8018 typeDeclarationData = s 8019 8020 case *js_ast.SLocal: 8021 // This is also a type declaration (but doesn't use "STypeScript" 8022 // because we need to be able to handle namespace exports below) 8023 8024 default: 8025 // Anything that we don't expect is a syntax error. For example, 8026 // we consider this a syntax error: 8027 // 8028 // declare let declare: any, foo: any 8029 // declare foo 8030 // 8031 // Strangely TypeScript allows this code starting with version 8032 // 4.4, but I assume this is a bug. This bug was reported here: 8033 // https://github.com/microsoft/TypeScript/issues/54602 8034 p.lexer = oldLexer 8035 p.lexer.Unexpected() 8036 } 8037 p.discardScopesUpTo(scopeIndex) 8038 8039 // Unlike almost all uses of "declare", statements that use 8040 // "export declare" with "var/let/const" inside a namespace affect 8041 // code generation. They cause any declared bindings to be 8042 // considered exports of the namespace. Identifier references to 8043 // those names must be converted into property accesses off the 8044 // namespace object: 8045 // 8046 // namespace ns { 8047 // export declare const x 8048 // export function y() { return x } 8049 // } 8050 // 8051 // (ns as any).x = 1 8052 // console.log(ns.y()) 8053 // 8054 // In this example, "return x" must be replaced with "return ns.x". 8055 // This is handled by replacing each "export declare" statement 8056 // inside a namespace with an "export var" statement containing all 8057 // of the declared bindings. That "export var" statement will later 8058 // cause identifiers to be transformed into property accesses. 8059 if opts.isNamespaceScope && opts.isExport { 8060 var decls []js_ast.Decl 8061 if s, ok := stmt.Data.(*js_ast.SLocal); ok { 8062 js_ast.ForEachIdentifierBindingInDecls(s.Decls, func(loc logger.Loc, b *js_ast.BIdentifier) { 8063 decls = append(decls, js_ast.Decl{Binding: js_ast.Binding{Loc: loc, Data: b}}) 8064 }) 8065 } 8066 if len(decls) > 0 { 8067 return js_ast.Stmt{Loc: loc, Data: &js_ast.SLocal{ 8068 Kind: js_ast.LocalVar, 8069 IsExport: true, 8070 Decls: decls, 8071 }} 8072 } 8073 } 8074 8075 return js_ast.Stmt{Loc: loc, Data: typeDeclarationData} 8076 } 8077 } 8078 } 8079 } 8080 } 8081 8082 p.lexer.ExpectOrInsertSemicolon() 8083 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}} 8084 } 8085 } 8086 8087 func (p *parser) addImportRecord(kind ast.ImportKind, pathRange logger.Range, text string, assertOrWith *ast.ImportAssertOrWith, flags ast.ImportRecordFlags) uint32 { 8088 index := uint32(len(p.importRecords)) 8089 p.importRecords = append(p.importRecords, ast.ImportRecord{ 8090 Kind: kind, 8091 Range: pathRange, 8092 Path: logger.Path{Text: text}, 8093 AssertOrWith: assertOrWith, 8094 Flags: flags, 8095 }) 8096 return index 8097 } 8098 8099 func (p *parser) parseFnBody(data fnOrArrowDataParse) js_ast.FnBody { 8100 oldFnOrArrowData := p.fnOrArrowDataParse 8101 oldAllowIn := p.allowIn 8102 p.fnOrArrowDataParse = data 8103 p.allowIn = true 8104 8105 loc := p.lexer.Loc() 8106 p.pushScopeForParsePass(js_ast.ScopeFunctionBody, loc) 8107 defer p.popScope() 8108 8109 p.lexer.Expect(js_lexer.TOpenBrace) 8110 stmts := p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{ 8111 allowDirectivePrologue: true, 8112 }) 8113 closeBraceLoc := p.lexer.Loc() 8114 p.lexer.Next() 8115 8116 p.allowIn = oldAllowIn 8117 p.fnOrArrowDataParse = oldFnOrArrowData 8118 return js_ast.FnBody{Loc: loc, Block: js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}} 8119 } 8120 8121 func (p *parser) forbidLexicalDecl(loc logger.Loc) { 8122 r := js_lexer.RangeOfIdentifier(p.source, loc) 8123 p.log.AddError(&p.tracker, r, "Cannot use a declaration in a single-statement context") 8124 } 8125 8126 func (p *parser) parseStmtsUpTo(end js_lexer.T, opts parseStmtOpts) []js_ast.Stmt { 8127 stmts := []js_ast.Stmt{} 8128 returnWithoutSemicolonStart := int32(-1) 8129 opts.lexicalDecl = lexicalDeclAllowAll 8130 isDirectivePrologue := opts.allowDirectivePrologue 8131 8132 for { 8133 // Preserve some statement-level comments 8134 comments := p.lexer.LegalCommentsBeforeToken 8135 if len(comments) > 0 { 8136 for _, comment := range comments { 8137 stmts = append(stmts, js_ast.Stmt{ 8138 Loc: comment.Loc, 8139 Data: &js_ast.SComment{ 8140 Text: p.source.CommentTextWithoutIndent(comment), 8141 IsLegalComment: true, 8142 }, 8143 }) 8144 } 8145 } 8146 8147 if p.lexer.Token == end { 8148 break 8149 } 8150 8151 stmt := p.parseStmt(opts) 8152 8153 // Skip TypeScript types entirely 8154 if p.options.ts.Parse { 8155 if _, ok := stmt.Data.(*js_ast.STypeScript); ok { 8156 continue 8157 } 8158 } 8159 8160 // Parse one or more directives at the beginning 8161 if isDirectivePrologue { 8162 isDirectivePrologue = false 8163 if expr, ok := stmt.Data.(*js_ast.SExpr); ok { 8164 if str, ok := expr.Value.Data.(*js_ast.EString); ok && !str.PreferTemplate { 8165 stmt.Data = &js_ast.SDirective{Value: str.Value, LegacyOctalLoc: str.LegacyOctalLoc} 8166 isDirectivePrologue = true 8167 8168 if helpers.UTF16EqualsString(str.Value, "use strict") { 8169 // Track "use strict" directives 8170 p.currentScope.StrictMode = js_ast.ExplicitStrictMode 8171 p.currentScope.UseStrictLoc = expr.Value.Loc 8172 8173 // Inside a function, strict mode actually propagates from the child 8174 // scope to the parent scope: 8175 // 8176 // // This is a syntax error 8177 // function fn(arguments) { 8178 // "use strict"; 8179 // } 8180 // 8181 if p.currentScope.Kind == js_ast.ScopeFunctionBody && 8182 p.currentScope.Parent.Kind == js_ast.ScopeFunctionArgs && 8183 p.currentScope.Parent.StrictMode == js_ast.SloppyMode { 8184 p.currentScope.Parent.StrictMode = js_ast.ExplicitStrictMode 8185 p.currentScope.Parent.UseStrictLoc = expr.Value.Loc 8186 } 8187 } else if helpers.UTF16EqualsString(str.Value, "use asm") { 8188 // Deliberately remove "use asm" directives. The asm.js subset of 8189 // JavaScript has complicated validation rules that are triggered 8190 // by this directive. This parser is not designed with asm.js in 8191 // mind and round-tripping asm.js code through esbuild will very 8192 // likely cause it to no longer validate as asm.js. When this 8193 // happens, V8 prints a warning and people don't like seeing the 8194 // warning. 8195 // 8196 // We deliberately do not attempt to preserve the validity of 8197 // asm.js code because it's a complicated legacy format and it's 8198 // obsolete now that WebAssembly exists. By removing this directive 8199 // it will just become normal JavaScript, which will work fine and 8200 // won't generate a warning (but will run slower). We don't generate 8201 // a warning ourselves in this case because there isn't necessarily 8202 // anything easy and actionable that the user can do to fix this. 8203 stmt.Data = &js_ast.SEmpty{} 8204 } 8205 } 8206 } 8207 } 8208 8209 stmts = append(stmts, stmt) 8210 8211 // Warn about ASI and return statements. Here's an example of code with 8212 // this problem: https://github.com/rollup/rollup/issues/3729 8213 if !p.suppressWarningsAboutWeirdCode { 8214 if s, ok := stmt.Data.(*js_ast.SReturn); ok && s.ValueOrNil.Data == nil && !p.latestReturnHadSemicolon { 8215 returnWithoutSemicolonStart = stmt.Loc.Start 8216 } else { 8217 if returnWithoutSemicolonStart != -1 { 8218 if _, ok := stmt.Data.(*js_ast.SExpr); ok { 8219 p.log.AddID(logger.MsgID_JS_SemicolonAfterReturn, logger.Warning, &p.tracker, logger.Range{Loc: logger.Loc{Start: returnWithoutSemicolonStart + 6}}, 8220 "The following expression is not returned because of an automatically-inserted semicolon") 8221 } 8222 } 8223 returnWithoutSemicolonStart = -1 8224 } 8225 } 8226 } 8227 8228 return stmts 8229 } 8230 8231 type generateTempRefArg uint8 8232 8233 const ( 8234 tempRefNeedsDeclare generateTempRefArg = iota 8235 tempRefNoDeclare 8236 8237 // This is used when the generated temporary may a) be used inside of a loop 8238 // body and b) may be used inside of a closure. In that case we can't use 8239 // "var" for the temporary and we can't declare the temporary at the top of 8240 // the enclosing function. Instead, we need to use "let" and we need to 8241 // declare the temporary in the enclosing block (so it's inside of the loop 8242 // body). 8243 tempRefNeedsDeclareMayBeCapturedInsideLoop 8244 ) 8245 8246 func (p *parser) generateTempRef(declare generateTempRefArg, optionalName string) ast.Ref { 8247 scope := p.currentScope 8248 8249 if declare != tempRefNeedsDeclareMayBeCapturedInsideLoop { 8250 for !scope.Kind.StopsHoisting() { 8251 scope = scope.Parent 8252 } 8253 } 8254 8255 if optionalName == "" { 8256 optionalName = "_" + ast.DefaultNameMinifierJS.NumberToMinifiedName(p.tempRefCount) 8257 p.tempRefCount++ 8258 } 8259 ref := p.newSymbol(ast.SymbolOther, optionalName) 8260 8261 if declare == tempRefNeedsDeclareMayBeCapturedInsideLoop && !scope.Kind.StopsHoisting() { 8262 p.tempLetsToDeclare = append(p.tempLetsToDeclare, ref) 8263 } else if declare != tempRefNoDeclare { 8264 p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ref: ref}) 8265 } 8266 8267 scope.Generated = append(scope.Generated, ref) 8268 return ref 8269 } 8270 8271 func (p *parser) generateTopLevelTempRef() ast.Ref { 8272 ref := p.newSymbol(ast.SymbolOther, "_"+ast.DefaultNameMinifierJS.NumberToMinifiedName(p.topLevelTempRefCount)) 8273 p.topLevelTempRefsToDeclare = append(p.topLevelTempRefsToDeclare, tempRef{ref: ref}) 8274 p.moduleScope.Generated = append(p.moduleScope.Generated, ref) 8275 p.topLevelTempRefCount++ 8276 return ref 8277 } 8278 8279 func (p *parser) pushScopeForVisitPass(kind js_ast.ScopeKind, loc logger.Loc) { 8280 order := p.scopesInOrder[0] 8281 8282 // Sanity-check that the scopes generated by the first and second passes match 8283 if order.loc != loc || order.scope.Kind != kind { 8284 panic(fmt.Sprintf("Expected scope (%d, %d) in %s, found scope (%d, %d)", 8285 kind, loc.Start, 8286 p.source.PrettyPath, 8287 order.scope.Kind, order.loc.Start)) 8288 } 8289 8290 p.scopesInOrder = p.scopesInOrder[1:] 8291 p.currentScope = order.scope 8292 p.scopesForCurrentPart = append(p.scopesForCurrentPart, order.scope) 8293 } 8294 8295 type findSymbolResult struct { 8296 ref ast.Ref 8297 declareLoc logger.Loc 8298 isInsideWithScope bool 8299 } 8300 8301 func (p *parser) findSymbol(loc logger.Loc, name string) findSymbolResult { 8302 var ref ast.Ref 8303 var declareLoc logger.Loc 8304 isInsideWithScope := false 8305 didForbidArguments := false 8306 s := p.currentScope 8307 8308 for { 8309 // Track if we're inside a "with" statement body 8310 if s.Kind == js_ast.ScopeWith { 8311 isInsideWithScope = true 8312 } 8313 8314 // Forbid referencing "arguments" inside class bodies 8315 if s.ForbidArguments && name == "arguments" && !didForbidArguments { 8316 r := js_lexer.RangeOfIdentifier(p.source, loc) 8317 p.log.AddError(&p.tracker, r, fmt.Sprintf("Cannot access %q here:", name)) 8318 didForbidArguments = true 8319 } 8320 8321 // Is the symbol a member of this scope? 8322 if member, ok := s.Members[name]; ok { 8323 ref = member.Ref 8324 declareLoc = member.Loc 8325 break 8326 } 8327 8328 // Is the symbol a member of this scope's TypeScript namespace? 8329 if tsNamespace := s.TSNamespace; tsNamespace != nil { 8330 if member, ok := tsNamespace.ExportedMembers[name]; ok && tsNamespace.IsEnumScope == member.IsEnumValue { 8331 // If this is an identifier from a sibling TypeScript namespace, then we're 8332 // going to have to generate a property access instead of a simple reference. 8333 // Lazily-generate an identifier that represents this property access. 8334 cache := tsNamespace.LazilyGeneratedProperyAccesses 8335 if cache == nil { 8336 cache = make(map[string]ast.Ref) 8337 tsNamespace.LazilyGeneratedProperyAccesses = cache 8338 } 8339 ref, ok = cache[name] 8340 if !ok { 8341 ref = p.newSymbol(ast.SymbolOther, name) 8342 p.symbols[ref.InnerIndex].NamespaceAlias = &ast.NamespaceAlias{ 8343 NamespaceRef: tsNamespace.ArgRef, 8344 Alias: name, 8345 } 8346 cache[name] = ref 8347 } 8348 declareLoc = member.Loc 8349 break 8350 } 8351 } 8352 8353 s = s.Parent 8354 if s == nil { 8355 // Allocate an "unbound" symbol 8356 p.checkForUnrepresentableIdentifier(loc, name) 8357 ref = p.newSymbol(ast.SymbolUnbound, name) 8358 declareLoc = loc 8359 p.moduleScope.Members[name] = js_ast.ScopeMember{Ref: ref, Loc: logger.Loc{Start: -1}} 8360 break 8361 } 8362 } 8363 8364 // If we had to pass through a "with" statement body to get to the symbol 8365 // declaration, then this reference could potentially also refer to a 8366 // property on the target object of the "with" statement. We must not rename 8367 // it or we risk changing the behavior of the code. 8368 if isInsideWithScope { 8369 p.symbols[ref.InnerIndex].Flags |= ast.MustNotBeRenamed 8370 } 8371 8372 // Track how many times we've referenced this symbol 8373 p.recordUsage(ref) 8374 return findSymbolResult{ref, declareLoc, isInsideWithScope} 8375 } 8376 8377 func (p *parser) findLabelSymbol(loc logger.Loc, name string) (ref ast.Ref, isLoop bool, ok bool) { 8378 for s := p.currentScope; s != nil && !s.Kind.StopsHoisting(); s = s.Parent { 8379 if s.Kind == js_ast.ScopeLabel && name == p.symbols[s.Label.Ref.InnerIndex].OriginalName { 8380 // Track how many times we've referenced this symbol 8381 p.recordUsage(s.Label.Ref) 8382 ref = s.Label.Ref 8383 isLoop = s.LabelStmtIsLoop 8384 ok = true 8385 return 8386 } 8387 } 8388 8389 r := js_lexer.RangeOfIdentifier(p.source, loc) 8390 p.log.AddError(&p.tracker, r, fmt.Sprintf("There is no containing label named %q", name)) 8391 8392 // Allocate an "unbound" symbol 8393 ref = p.newSymbol(ast.SymbolUnbound, name) 8394 8395 // Track how many times we've referenced this symbol 8396 p.recordUsage(ref) 8397 return 8398 } 8399 8400 func findIdentifiers(binding js_ast.Binding, identifiers []js_ast.Decl) []js_ast.Decl { 8401 switch b := binding.Data.(type) { 8402 case *js_ast.BIdentifier: 8403 identifiers = append(identifiers, js_ast.Decl{Binding: binding}) 8404 8405 case *js_ast.BArray: 8406 for _, item := range b.Items { 8407 identifiers = findIdentifiers(item.Binding, identifiers) 8408 } 8409 8410 case *js_ast.BObject: 8411 for _, property := range b.Properties { 8412 identifiers = findIdentifiers(property.Value, identifiers) 8413 } 8414 } 8415 8416 return identifiers 8417 } 8418 8419 // If this is in a dead branch, then we want to trim as much dead code as we 8420 // can. Everything can be trimmed except for hoisted declarations ("var" and 8421 // "function"), which affect the parent scope. For example: 8422 // 8423 // function foo() { 8424 // if (false) { var x; } 8425 // x = 1; 8426 // } 8427 // 8428 // We can't trim the entire branch as dead or calling foo() will incorrectly 8429 // assign to a global variable instead. 8430 func shouldKeepStmtInDeadControlFlow(stmt js_ast.Stmt) bool { 8431 switch s := stmt.Data.(type) { 8432 case *js_ast.SEmpty, *js_ast.SExpr, *js_ast.SThrow, *js_ast.SReturn, 8433 *js_ast.SBreak, *js_ast.SContinue, *js_ast.SClass, *js_ast.SDebugger: 8434 // Omit these statements entirely 8435 return false 8436 8437 case *js_ast.SLocal: 8438 if s.Kind != js_ast.LocalVar { 8439 // Omit these statements entirely 8440 return false 8441 } 8442 8443 // Omit everything except the identifiers 8444 identifiers := []js_ast.Decl{} 8445 for _, decl := range s.Decls { 8446 identifiers = findIdentifiers(decl.Binding, identifiers) 8447 } 8448 if len(identifiers) == 0 { 8449 return false 8450 } 8451 s.Decls = identifiers 8452 return true 8453 8454 case *js_ast.SBlock: 8455 for _, child := range s.Stmts { 8456 if shouldKeepStmtInDeadControlFlow(child) { 8457 return true 8458 } 8459 } 8460 return false 8461 8462 case *js_ast.SIf: 8463 return shouldKeepStmtInDeadControlFlow(s.Yes) || (s.NoOrNil.Data != nil && shouldKeepStmtInDeadControlFlow(s.NoOrNil)) 8464 8465 case *js_ast.SWhile: 8466 return shouldKeepStmtInDeadControlFlow(s.Body) 8467 8468 case *js_ast.SDoWhile: 8469 return shouldKeepStmtInDeadControlFlow(s.Body) 8470 8471 case *js_ast.SFor: 8472 return (s.InitOrNil.Data != nil && shouldKeepStmtInDeadControlFlow(s.InitOrNil)) || shouldKeepStmtInDeadControlFlow(s.Body) 8473 8474 case *js_ast.SForIn: 8475 return shouldKeepStmtInDeadControlFlow(s.Init) || shouldKeepStmtInDeadControlFlow(s.Body) 8476 8477 case *js_ast.SForOf: 8478 return shouldKeepStmtInDeadControlFlow(s.Init) || shouldKeepStmtInDeadControlFlow(s.Body) 8479 8480 case *js_ast.SLabel: 8481 return shouldKeepStmtInDeadControlFlow(s.Stmt) 8482 8483 default: 8484 // Everything else must be kept 8485 return true 8486 } 8487 } 8488 8489 type prependTempRefsOpts struct { 8490 fnBodyLoc *logger.Loc 8491 kind stmtsKind 8492 } 8493 8494 func (p *parser) visitStmtsAndPrependTempRefs(stmts []js_ast.Stmt, opts prependTempRefsOpts) []js_ast.Stmt { 8495 oldTempRefs := p.tempRefsToDeclare 8496 oldTempRefCount := p.tempRefCount 8497 p.tempRefsToDeclare = nil 8498 p.tempRefCount = 0 8499 8500 stmts = p.visitStmts(stmts, opts.kind) 8501 8502 // Prepend values for "this" and "arguments" 8503 if opts.fnBodyLoc != nil { 8504 // Capture "this" 8505 if ref := p.fnOnlyDataVisit.thisCaptureRef; ref != nil { 8506 p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ 8507 ref: *ref, 8508 valueOrNil: js_ast.Expr{Loc: *opts.fnBodyLoc, Data: js_ast.EThisShared}, 8509 }) 8510 p.currentScope.Generated = append(p.currentScope.Generated, *ref) 8511 } 8512 8513 // Capture "arguments" 8514 if ref := p.fnOnlyDataVisit.argumentsCaptureRef; ref != nil { 8515 p.tempRefsToDeclare = append(p.tempRefsToDeclare, tempRef{ 8516 ref: *ref, 8517 valueOrNil: js_ast.Expr{Loc: *opts.fnBodyLoc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.argumentsRef}}, 8518 }) 8519 p.currentScope.Generated = append(p.currentScope.Generated, *ref) 8520 } 8521 } 8522 8523 // There may also be special top-level-only temporaries to declare 8524 if p.currentScope == p.moduleScope && p.topLevelTempRefsToDeclare != nil { 8525 p.tempRefsToDeclare = append(p.tempRefsToDeclare, p.topLevelTempRefsToDeclare...) 8526 p.topLevelTempRefsToDeclare = nil 8527 } 8528 8529 // Prepend the generated temporary variables to the beginning of the statement list 8530 decls := []js_ast.Decl{} 8531 for _, temp := range p.tempRefsToDeclare { 8532 if p.symbols[temp.ref.InnerIndex].UseCountEstimate > 0 { 8533 decls = append(decls, js_ast.Decl{Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: temp.ref}}, ValueOrNil: temp.valueOrNil}) 8534 p.recordDeclaredSymbol(temp.ref) 8535 } 8536 } 8537 if len(decls) > 0 { 8538 // Skip past leading directives and comments 8539 split := 0 8540 for split < len(stmts) { 8541 switch stmts[split].Data.(type) { 8542 case *js_ast.SComment, *js_ast.SDirective: 8543 split++ 8544 continue 8545 } 8546 break 8547 } 8548 stmts = append( 8549 append( 8550 append( 8551 []js_ast.Stmt{}, 8552 stmts[:split]...), 8553 js_ast.Stmt{Data: &js_ast.SLocal{Kind: js_ast.LocalVar, Decls: decls}}), 8554 stmts[split:]...) 8555 } 8556 8557 p.tempRefsToDeclare = oldTempRefs 8558 p.tempRefCount = oldTempRefCount 8559 return stmts 8560 } 8561 8562 type stmtsKind uint8 8563 8564 const ( 8565 stmtsNormal stmtsKind = iota 8566 stmtsSwitch 8567 stmtsLoopBody 8568 stmtsFnBody 8569 ) 8570 8571 func (p *parser) visitStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt { 8572 // Save the current control-flow liveness. This represents if we are 8573 // currently inside an "if (false) { ... }" block. 8574 oldIsControlFlowDead := p.isControlFlowDead 8575 8576 oldTempLetsToDeclare := p.tempLetsToDeclare 8577 p.tempLetsToDeclare = nil 8578 8579 // Visit all statements first 8580 visited := make([]js_ast.Stmt, 0, len(stmts)) 8581 var before []js_ast.Stmt 8582 var after []js_ast.Stmt 8583 var preprocessedEnums map[int][]js_ast.Stmt 8584 if p.scopesInOrderForEnum != nil { 8585 // Preprocess TypeScript enums to improve code generation. Otherwise 8586 // uses of an enum before that enum has been declared won't be inlined: 8587 // 8588 // console.log(Foo.FOO) // We want "FOO" to be inlined here 8589 // const enum Foo { FOO = 0 } 8590 // 8591 // The TypeScript compiler itself contains code with this pattern, so 8592 // it's important to implement this optimization. 8593 for i, stmt := range stmts { 8594 if _, ok := stmt.Data.(*js_ast.SEnum); ok { 8595 if preprocessedEnums == nil { 8596 preprocessedEnums = make(map[int][]js_ast.Stmt) 8597 } 8598 oldScopesInOrder := p.scopesInOrder 8599 p.scopesInOrder = p.scopesInOrderForEnum[stmt.Loc] 8600 preprocessedEnums[i] = p.visitAndAppendStmt(nil, stmt) 8601 p.scopesInOrder = oldScopesInOrder 8602 } 8603 } 8604 } 8605 for i, stmt := range stmts { 8606 switch s := stmt.Data.(type) { 8607 case *js_ast.SExportEquals: 8608 // TypeScript "export = value;" becomes "module.exports = value;". This 8609 // must happen at the end after everything is parsed because TypeScript 8610 // moves this statement to the end when it generates code. 8611 after = p.visitAndAppendStmt(after, stmt) 8612 continue 8613 8614 case *js_ast.SFunction: 8615 // Manually hoist block-level function declarations to preserve semantics. 8616 // This is only done for function declarations that are not generators 8617 // or async functions, since this is a backwards-compatibility hack from 8618 // Annex B of the JavaScript standard. 8619 if !p.currentScope.Kind.StopsHoisting() && p.symbols[int(s.Fn.Name.Ref.InnerIndex)].Kind == ast.SymbolHoistedFunction { 8620 before = p.visitAndAppendStmt(before, stmt) 8621 continue 8622 } 8623 8624 case *js_ast.SEnum: 8625 visited = append(visited, preprocessedEnums[i]...) 8626 p.scopesInOrder = p.scopesInOrder[len(p.scopesInOrderForEnum[stmt.Loc]):] 8627 continue 8628 } 8629 visited = p.visitAndAppendStmt(visited, stmt) 8630 } 8631 8632 // This is used for temporary variables that could be captured in a closure, 8633 // and therefore need to be generated inside the nearest enclosing block in 8634 // case they are generated inside a loop. 8635 if len(p.tempLetsToDeclare) > 0 { 8636 decls := make([]js_ast.Decl, 0, len(p.tempLetsToDeclare)) 8637 for _, ref := range p.tempLetsToDeclare { 8638 decls = append(decls, js_ast.Decl{Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: ref}}}) 8639 } 8640 before = append(before, js_ast.Stmt{Data: &js_ast.SLocal{Kind: js_ast.LocalLet, Decls: decls}}) 8641 } 8642 p.tempLetsToDeclare = oldTempLetsToDeclare 8643 8644 // Transform block-level function declarations into variable declarations 8645 if len(before) > 0 { 8646 var letDecls []js_ast.Decl 8647 var varDecls []js_ast.Decl 8648 var nonFnStmts []js_ast.Stmt 8649 fnStmts := make(map[ast.Ref]int) 8650 for _, stmt := range before { 8651 s, ok := stmt.Data.(*js_ast.SFunction) 8652 if !ok { 8653 // We may get non-function statements here in certain scenarios such as when "KeepNames" is enabled 8654 nonFnStmts = append(nonFnStmts, stmt) 8655 continue 8656 } 8657 8658 // This transformation of function declarations in nested scopes is 8659 // intended to preserve the hoisting semantics of the original code. In 8660 // JavaScript, function hoisting works differently in strict mode vs. 8661 // sloppy mode code. We want the code we generate to use the semantics of 8662 // the original environment, not the generated environment. However, if 8663 // direct "eval" is present then it's not possible to preserve the 8664 // semantics because we need two identifiers to do that and direct "eval" 8665 // means neither identifier can be renamed to something else. So in that 8666 // case we give up and do not preserve the semantics of the original code. 8667 if p.currentScope.ContainsDirectEval { 8668 if hoistedRef, ok := p.hoistedRefForSloppyModeBlockFn[s.Fn.Name.Ref]; ok { 8669 // Merge the two identifiers back into a single one 8670 p.symbols[hoistedRef.InnerIndex].Link = s.Fn.Name.Ref 8671 } 8672 nonFnStmts = append(nonFnStmts, stmt) 8673 continue 8674 } 8675 8676 index, ok := fnStmts[s.Fn.Name.Ref] 8677 if !ok { 8678 index = len(letDecls) 8679 fnStmts[s.Fn.Name.Ref] = index 8680 letDecls = append(letDecls, js_ast.Decl{Binding: js_ast.Binding{ 8681 Loc: s.Fn.Name.Loc, Data: &js_ast.BIdentifier{Ref: s.Fn.Name.Ref}}}) 8682 8683 // Also write the function to the hoisted sibling symbol if applicable 8684 if hoistedRef, ok := p.hoistedRefForSloppyModeBlockFn[s.Fn.Name.Ref]; ok { 8685 p.recordDeclaredSymbol(hoistedRef) 8686 p.recordUsage(s.Fn.Name.Ref) 8687 varDecls = append(varDecls, js_ast.Decl{ 8688 Binding: js_ast.Binding{Loc: s.Fn.Name.Loc, Data: &js_ast.BIdentifier{Ref: hoistedRef}}, 8689 ValueOrNil: js_ast.Expr{Loc: s.Fn.Name.Loc, Data: &js_ast.EIdentifier{Ref: s.Fn.Name.Ref}}, 8690 }) 8691 } 8692 } 8693 8694 // The last function statement for a given symbol wins 8695 s.Fn.Name = nil 8696 letDecls[index].ValueOrNil = js_ast.Expr{Loc: stmt.Loc, Data: &js_ast.EFunction{Fn: s.Fn}} 8697 } 8698 8699 // Reuse memory from "before" 8700 before = before[:0] 8701 kind := js_ast.LocalLet 8702 if p.options.unsupportedJSFeatures.Has(compat.ConstAndLet) { 8703 kind = js_ast.LocalVar 8704 } 8705 if len(letDecls) > 0 { 8706 before = append(before, js_ast.Stmt{Loc: letDecls[0].ValueOrNil.Loc, Data: &js_ast.SLocal{Kind: kind, Decls: letDecls}}) 8707 } 8708 if len(varDecls) > 0 { 8709 // Potentially relocate "var" declarations to the top level 8710 if assign, ok := p.maybeRelocateVarsToTopLevel(varDecls, relocateVarsNormal); ok { 8711 if assign.Data != nil { 8712 before = append(before, assign) 8713 } 8714 } else { 8715 before = append(before, js_ast.Stmt{Loc: varDecls[0].ValueOrNil.Loc, Data: &js_ast.SLocal{Kind: js_ast.LocalVar, Decls: varDecls}}) 8716 } 8717 } 8718 before = append(before, nonFnStmts...) 8719 visited = append(before, visited...) 8720 } 8721 8722 // Move TypeScript "export =" statements to the end 8723 visited = append(visited, after...) 8724 8725 // Restore the current control-flow liveness if it was changed inside the 8726 // loop above. This is important because the caller will not restore it. 8727 p.isControlFlowDead = oldIsControlFlowDead 8728 8729 // Lower using declarations 8730 if kind != stmtsSwitch && p.shouldLowerUsingDeclarations(visited) { 8731 ctx := p.lowerUsingDeclarationContext() 8732 ctx.scanStmts(p, visited) 8733 visited = ctx.finalize(p, visited, p.currentScope.Parent == nil) 8734 } 8735 8736 // Stop now if we're not mangling 8737 if !p.options.minifySyntax { 8738 return visited 8739 } 8740 8741 // If this is in a dead branch, trim as much dead code as we can 8742 if p.isControlFlowDead { 8743 end := 0 8744 for _, stmt := range visited { 8745 if !shouldKeepStmtInDeadControlFlow(stmt) { 8746 continue 8747 } 8748 8749 // Merge adjacent var statements 8750 if s, ok := stmt.Data.(*js_ast.SLocal); ok && s.Kind == js_ast.LocalVar && end > 0 { 8751 prevStmt := visited[end-1] 8752 if prevS, ok := prevStmt.Data.(*js_ast.SLocal); ok && prevS.Kind == js_ast.LocalVar && s.IsExport == prevS.IsExport { 8753 prevS.Decls = append(prevS.Decls, s.Decls...) 8754 continue 8755 } 8756 } 8757 8758 visited[end] = stmt 8759 end++ 8760 } 8761 return visited[:end] 8762 } 8763 8764 return p.mangleStmts(visited, kind) 8765 } 8766 8767 func (p *parser) mangleStmts(stmts []js_ast.Stmt, kind stmtsKind) []js_ast.Stmt { 8768 // Remove inlined constants now that we know whether any of these statements 8769 // contained a direct eval() or not. This can't be done earlier when we 8770 // encounter the constant because we haven't encountered the eval() yet. 8771 // Inlined constants are not removed if they are in a top-level scope or 8772 // if they are exported (which could be in a nested TypeScript namespace). 8773 if p.currentScope.Parent != nil && !p.currentScope.ContainsDirectEval { 8774 for i, stmt := range stmts { 8775 switch s := stmt.Data.(type) { 8776 case *js_ast.SEmpty, *js_ast.SComment, *js_ast.SDirective, *js_ast.SDebugger, *js_ast.STypeScript: 8777 continue 8778 8779 case *js_ast.SLocal: 8780 if !s.IsExport { 8781 end := 0 8782 for _, d := range s.Decls { 8783 if id, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 8784 if _, ok := p.constValues[id.Ref]; ok && p.symbols[id.Ref.InnerIndex].UseCountEstimate == 0 { 8785 continue 8786 } 8787 } 8788 s.Decls[end] = d 8789 end++ 8790 } 8791 if end == 0 { 8792 stmts[i].Data = js_ast.SEmptyShared 8793 } else { 8794 s.Decls = s.Decls[:end] 8795 } 8796 } 8797 continue 8798 } 8799 break 8800 } 8801 } 8802 8803 // Merge adjacent statements during mangling 8804 result := make([]js_ast.Stmt, 0, len(stmts)) 8805 isControlFlowDead := false 8806 for i, stmt := range stmts { 8807 if isControlFlowDead && !shouldKeepStmtInDeadControlFlow(stmt) { 8808 // Strip unnecessary statements if the control flow is dead here 8809 continue 8810 } 8811 8812 // Inline single-use variable declarations where possible: 8813 // 8814 // // Before 8815 // let x = fn(); 8816 // return x.y(); 8817 // 8818 // // After 8819 // return fn().y(); 8820 // 8821 // The declaration must not be exported. We can't just check for the 8822 // "export" keyword because something might do "export {id};" later on. 8823 // Instead we just ignore all top-level declarations for now. That means 8824 // this optimization currently only applies in nested scopes. 8825 // 8826 // Ignore declarations if the scope is shadowed by a direct "eval" call. 8827 // The eval'd code may indirectly reference this symbol and the actual 8828 // use count may be greater than 1. 8829 if p.currentScope != p.moduleScope && !p.currentScope.ContainsDirectEval { 8830 // Keep inlining variables until a failure or until there are none left. 8831 // That handles cases like this: 8832 // 8833 // // Before 8834 // let x = fn(); 8835 // let y = x.prop; 8836 // return y; 8837 // 8838 // // After 8839 // return fn().prop; 8840 // 8841 for len(result) > 0 { 8842 // Ignore "var" declarations since those have function-level scope and 8843 // we may not have visited all of their uses yet by this point. We 8844 // should have visited all the uses of "let" and "const" declarations 8845 // by now since they are scoped to this block which we just finished 8846 // visiting. 8847 if prevS, ok := result[len(result)-1].Data.(*js_ast.SLocal); ok && prevS.Kind != js_ast.LocalVar { 8848 last := prevS.Decls[len(prevS.Decls)-1] 8849 8850 // The binding must be an identifier that is only used once. 8851 // Ignore destructuring bindings since that's not the simple case. 8852 // Destructuring bindings could potentially execute side-effecting 8853 // code which would invalidate reordering. 8854 if id, ok := last.Binding.Data.(*js_ast.BIdentifier); ok { 8855 // Don't do this if "__name" was called on this symbol. In that 8856 // case there is actually more than one use even though it says 8857 // there is only one. The "__name" use isn't counted so that 8858 // tree shaking still works when names are kept. 8859 if symbol := p.symbols[id.Ref.InnerIndex]; symbol.UseCountEstimate == 1 && !symbol.Flags.Has(ast.DidKeepName) { 8860 replacement := last.ValueOrNil 8861 8862 // The variable must be initialized, since we will be substituting 8863 // the value into the usage. 8864 if replacement.Data == nil { 8865 replacement = js_ast.Expr{Loc: last.Binding.Loc, Data: js_ast.EUndefinedShared} 8866 } 8867 8868 // Try to substitute the identifier with the initializer. This will 8869 // fail if something with side effects is in between the declaration 8870 // and the usage. 8871 if p.substituteSingleUseSymbolInStmt(stmt, id.Ref, replacement) { 8872 // Remove the previous declaration, since the substitution was 8873 // successful. 8874 if len(prevS.Decls) == 1 { 8875 result = result[:len(result)-1] 8876 } else { 8877 prevS.Decls = prevS.Decls[:len(prevS.Decls)-1] 8878 } 8879 8880 // Loop back to try again 8881 continue 8882 } 8883 } 8884 } 8885 } 8886 8887 // Substitution failed so stop trying 8888 break 8889 } 8890 } 8891 8892 switch s := stmt.Data.(type) { 8893 case *js_ast.SEmpty: 8894 // Strip empty statements 8895 continue 8896 8897 case *js_ast.SLocal: 8898 // Merge adjacent local statements 8899 if len(result) > 0 { 8900 prevStmt := result[len(result)-1] 8901 if prevS, ok := prevStmt.Data.(*js_ast.SLocal); ok && s.Kind == prevS.Kind && s.IsExport == prevS.IsExport { 8902 prevS.Decls = append(prevS.Decls, s.Decls...) 8903 continue 8904 } 8905 } 8906 8907 case *js_ast.SExpr: 8908 // Merge adjacent expression statements 8909 if len(result) > 0 { 8910 prevStmt := result[len(result)-1] 8911 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 8912 if !s.IsFromClassOrFnThatCanBeRemovedIfUnused { 8913 prevS.IsFromClassOrFnThatCanBeRemovedIfUnused = false 8914 } 8915 prevS.Value = js_ast.JoinWithComma(prevS.Value, s.Value) 8916 continue 8917 } 8918 } 8919 8920 case *js_ast.SSwitch: 8921 // Absorb a previous expression statement 8922 if len(result) > 0 { 8923 prevStmt := result[len(result)-1] 8924 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 8925 s.Test = js_ast.JoinWithComma(prevS.Value, s.Test) 8926 result = result[:len(result)-1] 8927 } 8928 } 8929 8930 case *js_ast.SIf: 8931 // Absorb a previous expression statement 8932 if len(result) > 0 { 8933 prevStmt := result[len(result)-1] 8934 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 8935 s.Test = js_ast.JoinWithComma(prevS.Value, s.Test) 8936 result = result[:len(result)-1] 8937 } 8938 } 8939 8940 if isJumpStatement(s.Yes.Data) { 8941 optimizeImplicitJump := false 8942 8943 // Absorb a previous if statement 8944 if len(result) > 0 { 8945 prevStmt := result[len(result)-1] 8946 if prevS, ok := prevStmt.Data.(*js_ast.SIf); ok && prevS.NoOrNil.Data == nil && jumpStmtsLookTheSame(prevS.Yes.Data, s.Yes.Data) { 8947 // "if (a) break c; if (b) break c;" => "if (a || b) break c;" 8948 // "if (a) continue c; if (b) continue c;" => "if (a || b) continue c;" 8949 // "if (a) return c; if (b) return c;" => "if (a || b) return c;" 8950 // "if (a) throw c; if (b) throw c;" => "if (a || b) throw c;" 8951 s.Test = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalOr, prevS.Test, s.Test) 8952 result = result[:len(result)-1] 8953 } 8954 } 8955 8956 // "while (x) { if (y) continue; z(); }" => "while (x) { if (!y) z(); }" 8957 // "while (x) { if (y) continue; else z(); w(); }" => "while (x) { if (!y) { z(); w(); } }" => "for (; x;) !y && (z(), w());" 8958 if kind == stmtsLoopBody { 8959 if continueS, ok := s.Yes.Data.(*js_ast.SContinue); ok && continueS.Label == nil { 8960 optimizeImplicitJump = true 8961 } 8962 } 8963 8964 // "let x = () => { if (y) return; z(); };" => "let x = () => { if (!y) z(); };" 8965 // "let x = () => { if (y) return; else z(); w(); };" => "let x = () => { if (!y) { z(); w(); } };" => "let x = () => { !y && (z(), w()); };" 8966 if kind == stmtsFnBody { 8967 if returnS, ok := s.Yes.Data.(*js_ast.SReturn); ok && returnS.ValueOrNil.Data == nil { 8968 optimizeImplicitJump = true 8969 } 8970 } 8971 8972 if optimizeImplicitJump { 8973 var body []js_ast.Stmt 8974 if s.NoOrNil.Data != nil { 8975 body = append(body, s.NoOrNil) 8976 } 8977 body = append(body, stmts[i+1:]...) 8978 8979 // Don't do this transformation if the branch condition could 8980 // potentially access symbols declared later on on this scope below. 8981 // If so, inverting the branch condition and nesting statements after 8982 // this in a block would break that access which is a behavior change. 8983 // 8984 // // This transformation is incorrect 8985 // if (a()) return; function a() {} 8986 // if (!a()) { function a() {} } 8987 // 8988 // // This transformation is incorrect 8989 // if (a(() => b)) return; let b; 8990 // if (a(() => b)) { let b; } 8991 // 8992 canMoveBranchConditionOutsideScope := true 8993 for _, stmt := range body { 8994 if statementCaresAboutScope(stmt) { 8995 canMoveBranchConditionOutsideScope = false 8996 break 8997 } 8998 } 8999 9000 if canMoveBranchConditionOutsideScope { 9001 body = p.mangleStmts(body, kind) 9002 bodyLoc := s.Yes.Loc 9003 if len(body) > 0 { 9004 bodyLoc = body[0].Loc 9005 } 9006 return p.mangleIf(result, stmt.Loc, &js_ast.SIf{ 9007 Test: p.astHelpers.SimplifyBooleanExpr(js_ast.Not(s.Test)), 9008 Yes: stmtsToSingleStmt(bodyLoc, body, logger.Loc{}), 9009 }) 9010 } 9011 } 9012 9013 if s.NoOrNil.Data != nil { 9014 // "if (a) return b; else if (c) return d; else return e;" => "if (a) return b; if (c) return d; return e;" 9015 for { 9016 result = append(result, stmt) 9017 stmt = s.NoOrNil 9018 s.NoOrNil = js_ast.Stmt{} 9019 var ok bool 9020 s, ok = stmt.Data.(*js_ast.SIf) 9021 if !ok || !isJumpStatement(s.Yes.Data) || s.NoOrNil.Data == nil { 9022 break 9023 } 9024 } 9025 result = appendIfOrLabelBodyPreservingScope(result, stmt) 9026 if isJumpStatement(stmt.Data) { 9027 isControlFlowDead = true 9028 } 9029 continue 9030 } 9031 } 9032 9033 case *js_ast.SReturn: 9034 // Merge return statements with the previous expression statement 9035 if len(result) > 0 && s.ValueOrNil.Data != nil { 9036 prevStmt := result[len(result)-1] 9037 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 9038 result[len(result)-1] = js_ast.Stmt{Loc: prevStmt.Loc, 9039 Data: &js_ast.SReturn{ValueOrNil: js_ast.JoinWithComma(prevS.Value, s.ValueOrNil)}} 9040 continue 9041 } 9042 } 9043 9044 isControlFlowDead = true 9045 9046 case *js_ast.SThrow: 9047 // Merge throw statements with the previous expression statement 9048 if len(result) > 0 { 9049 prevStmt := result[len(result)-1] 9050 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 9051 result[len(result)-1] = js_ast.Stmt{Loc: prevStmt.Loc, Data: &js_ast.SThrow{Value: js_ast.JoinWithComma(prevS.Value, s.Value)}} 9052 continue 9053 } 9054 } 9055 9056 isControlFlowDead = true 9057 9058 case *js_ast.SBreak, *js_ast.SContinue: 9059 isControlFlowDead = true 9060 9061 case *js_ast.SFor: 9062 if len(result) > 0 { 9063 prevStmt := result[len(result)-1] 9064 if prevS, ok := prevStmt.Data.(*js_ast.SExpr); ok { 9065 // Insert the previous expression into the for loop initializer 9066 if s.InitOrNil.Data == nil { 9067 result[len(result)-1] = stmt 9068 s.InitOrNil = js_ast.Stmt{Loc: prevStmt.Loc, Data: &js_ast.SExpr{Value: prevS.Value}} 9069 continue 9070 } else if s2, ok := s.InitOrNil.Data.(*js_ast.SExpr); ok { 9071 result[len(result)-1] = stmt 9072 s.InitOrNil = js_ast.Stmt{Loc: prevStmt.Loc, Data: &js_ast.SExpr{Value: js_ast.JoinWithComma(prevS.Value, s2.Value)}} 9073 continue 9074 } 9075 } else { 9076 // Insert the previous variable declaration into the for loop 9077 // initializer if it's a "var" declaration, since the scope 9078 // doesn't matter due to scope hoisting 9079 if s.InitOrNil.Data == nil { 9080 if s2, ok := prevStmt.Data.(*js_ast.SLocal); ok && s2.Kind == js_ast.LocalVar && !s2.IsExport { 9081 result[len(result)-1] = stmt 9082 s.InitOrNil = prevStmt 9083 continue 9084 } 9085 } else { 9086 if s2, ok := prevStmt.Data.(*js_ast.SLocal); ok && s2.Kind == js_ast.LocalVar && !s2.IsExport { 9087 if s3, ok := s.InitOrNil.Data.(*js_ast.SLocal); ok && s3.Kind == js_ast.LocalVar { 9088 result[len(result)-1] = stmt 9089 s.InitOrNil.Data = &js_ast.SLocal{Kind: js_ast.LocalVar, Decls: append(s2.Decls, s3.Decls...)} 9090 continue 9091 } 9092 } 9093 } 9094 } 9095 } 9096 9097 case *js_ast.STry: 9098 // Drop an unused identifier binding if the optional catch binding feature is supported 9099 if !p.options.unsupportedJSFeatures.Has(compat.OptionalCatchBinding) && s.Catch != nil { 9100 if id, ok := s.Catch.BindingOrNil.Data.(*js_ast.BIdentifier); ok { 9101 if symbol := p.symbols[id.Ref.InnerIndex]; symbol.UseCountEstimate == 0 { 9102 if symbol.Link != ast.InvalidRef { 9103 // We cannot transform "try { x() } catch (y) { var y = 1 }" into 9104 // "try { x() } catch { var y = 1 }" even though "y" is never used 9105 // because the hoisted variable "y" would have different values 9106 // after the statement ends due to a strange JavaScript quirk: 9107 // 9108 // try { x() } catch (y) { var y = 1 } 9109 // console.log(y) // undefined 9110 // 9111 // try { x() } catch { var y = 1 } 9112 // console.log(y) // 1 9113 // 9114 } else if p.currentScope.ContainsDirectEval { 9115 // We cannot transform "try { x() } catch (y) { eval('z = y') }" 9116 // into "try { x() } catch { eval('z = y') }" because the variable 9117 // "y" is actually still used. 9118 } else { 9119 // "try { x() } catch (y) {}" => "try { x() } catch {}" 9120 s.Catch.BindingOrNil.Data = nil 9121 } 9122 } 9123 } 9124 } 9125 } 9126 9127 result = append(result, stmt) 9128 } 9129 9130 // Drop a trailing unconditional jump statement if applicable 9131 if len(result) > 0 { 9132 switch kind { 9133 case stmtsLoopBody: 9134 // "while (x) { y(); continue; }" => "while (x) { y(); }" 9135 if continueS, ok := result[len(result)-1].Data.(*js_ast.SContinue); ok && continueS.Label == nil { 9136 result = result[:len(result)-1] 9137 } 9138 9139 case stmtsFnBody: 9140 // "function f() { x(); return; }" => "function f() { x(); }" 9141 if returnS, ok := result[len(result)-1].Data.(*js_ast.SReturn); ok && returnS.ValueOrNil.Data == nil { 9142 result = result[:len(result)-1] 9143 } 9144 } 9145 } 9146 9147 // Merge certain statements in reverse order 9148 if len(result) >= 2 { 9149 lastStmt := result[len(result)-1] 9150 9151 if lastReturn, ok := lastStmt.Data.(*js_ast.SReturn); ok { 9152 // "if (a) return b; if (c) return d; return e;" => "return a ? b : c ? d : e;" 9153 returnLoop: 9154 for len(result) >= 2 { 9155 prevIndex := len(result) - 2 9156 prevStmt := result[prevIndex] 9157 9158 switch prevS := prevStmt.Data.(type) { 9159 case *js_ast.SExpr: 9160 // This return statement must have a value 9161 if lastReturn.ValueOrNil.Data == nil { 9162 break returnLoop 9163 } 9164 9165 // "a(); return b;" => "return a(), b;" 9166 lastReturn = &js_ast.SReturn{ValueOrNil: js_ast.JoinWithComma(prevS.Value, lastReturn.ValueOrNil)} 9167 9168 // Merge the last two statements 9169 lastStmt = js_ast.Stmt{Loc: prevStmt.Loc, Data: lastReturn} 9170 result[prevIndex] = lastStmt 9171 result = result[:len(result)-1] 9172 9173 case *js_ast.SIf: 9174 // The previous statement must be an if statement with no else clause 9175 if prevS.NoOrNil.Data != nil { 9176 break returnLoop 9177 } 9178 9179 // The then clause must be a return 9180 prevReturn, ok := prevS.Yes.Data.(*js_ast.SReturn) 9181 if !ok { 9182 break returnLoop 9183 } 9184 9185 // Handle some or all of the values being undefined 9186 left := prevReturn.ValueOrNil 9187 right := lastReturn.ValueOrNil 9188 if left.Data == nil { 9189 // "if (a) return; return b;" => "return a ? void 0 : b;" 9190 left = js_ast.Expr{Loc: prevS.Yes.Loc, Data: js_ast.EUndefinedShared} 9191 } 9192 if right.Data == nil { 9193 // "if (a) return a; return;" => "return a ? b : void 0;" 9194 right = js_ast.Expr{Loc: lastStmt.Loc, Data: js_ast.EUndefinedShared} 9195 } 9196 9197 // "if (!a) return b; return c;" => "return a ? c : b;" 9198 if not, ok := prevS.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9199 prevS.Test = not.Value 9200 left, right = right, left 9201 } 9202 9203 if comma, ok := prevS.Test.Data.(*js_ast.EBinary); ok && comma.Op == js_ast.BinOpComma { 9204 // "if (a, b) return c; return d;" => "return a, b ? c : d;" 9205 lastReturn = &js_ast.SReturn{ValueOrNil: js_ast.JoinWithComma(comma.Left, 9206 p.astHelpers.MangleIfExpr(comma.Right.Loc, &js_ast.EIf{Test: comma.Right, Yes: left, No: right}, p.options.unsupportedJSFeatures))} 9207 } else { 9208 // "if (a) return b; return c;" => "return a ? b : c;" 9209 lastReturn = &js_ast.SReturn{ValueOrNil: p.astHelpers.MangleIfExpr( 9210 prevS.Test.Loc, &js_ast.EIf{Test: prevS.Test, Yes: left, No: right}, p.options.unsupportedJSFeatures)} 9211 } 9212 9213 // Merge the last two statements 9214 lastStmt = js_ast.Stmt{Loc: prevStmt.Loc, Data: lastReturn} 9215 result[prevIndex] = lastStmt 9216 result = result[:len(result)-1] 9217 9218 default: 9219 break returnLoop 9220 } 9221 } 9222 } else if lastThrow, ok := lastStmt.Data.(*js_ast.SThrow); ok { 9223 // "if (a) throw b; if (c) throw d; throw e;" => "throw a ? b : c ? d : e;" 9224 throwLoop: 9225 for len(result) >= 2 { 9226 prevIndex := len(result) - 2 9227 prevStmt := result[prevIndex] 9228 9229 switch prevS := prevStmt.Data.(type) { 9230 case *js_ast.SExpr: 9231 // "a(); throw b;" => "throw a(), b;" 9232 lastThrow = &js_ast.SThrow{Value: js_ast.JoinWithComma(prevS.Value, lastThrow.Value)} 9233 9234 // Merge the last two statements 9235 lastStmt = js_ast.Stmt{Loc: prevStmt.Loc, Data: lastThrow} 9236 result[prevIndex] = lastStmt 9237 result = result[:len(result)-1] 9238 9239 case *js_ast.SIf: 9240 // The previous statement must be an if statement with no else clause 9241 if prevS.NoOrNil.Data != nil { 9242 break throwLoop 9243 } 9244 9245 // The then clause must be a throw 9246 prevThrow, ok := prevS.Yes.Data.(*js_ast.SThrow) 9247 if !ok { 9248 break throwLoop 9249 } 9250 9251 left := prevThrow.Value 9252 right := lastThrow.Value 9253 9254 // "if (!a) throw b; throw c;" => "throw a ? c : b;" 9255 if not, ok := prevS.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9256 prevS.Test = not.Value 9257 left, right = right, left 9258 } 9259 9260 // Merge the last two statements 9261 if comma, ok := prevS.Test.Data.(*js_ast.EBinary); ok && comma.Op == js_ast.BinOpComma { 9262 // "if (a, b) return c; return d;" => "return a, b ? c : d;" 9263 lastThrow = &js_ast.SThrow{Value: js_ast.JoinWithComma(comma.Left, 9264 p.astHelpers.MangleIfExpr(comma.Right.Loc, &js_ast.EIf{Test: comma.Right, Yes: left, No: right}, p.options.unsupportedJSFeatures))} 9265 } else { 9266 // "if (a) return b; return c;" => "return a ? b : c;" 9267 lastThrow = &js_ast.SThrow{ 9268 Value: p.astHelpers.MangleIfExpr(prevS.Test.Loc, &js_ast.EIf{Test: prevS.Test, Yes: left, No: right}, p.options.unsupportedJSFeatures)} 9269 } 9270 lastStmt = js_ast.Stmt{Loc: prevStmt.Loc, Data: lastThrow} 9271 result[prevIndex] = lastStmt 9272 result = result[:len(result)-1] 9273 9274 default: 9275 break throwLoop 9276 } 9277 } 9278 } 9279 } 9280 9281 return result 9282 } 9283 9284 func (p *parser) substituteSingleUseSymbolInStmt(stmt js_ast.Stmt, ref ast.Ref, replacement js_ast.Expr) bool { 9285 var expr *js_ast.Expr 9286 9287 switch s := stmt.Data.(type) { 9288 case *js_ast.SExpr: 9289 expr = &s.Value 9290 case *js_ast.SThrow: 9291 expr = &s.Value 9292 case *js_ast.SReturn: 9293 expr = &s.ValueOrNil 9294 case *js_ast.SIf: 9295 expr = &s.Test 9296 case *js_ast.SSwitch: 9297 expr = &s.Test 9298 case *js_ast.SLocal: 9299 // Only try substituting into the initializer for the first declaration 9300 if first := &s.Decls[0]; first.ValueOrNil.Data != nil { 9301 // Make sure there isn't destructuring, which could evaluate code 9302 if _, ok := first.Binding.Data.(*js_ast.BIdentifier); ok { 9303 expr = &first.ValueOrNil 9304 } 9305 } 9306 } 9307 9308 if expr != nil { 9309 // Only continue trying to insert this replacement into sub-expressions 9310 // after the first one if the replacement has no side effects: 9311 // 9312 // // Substitution is ok 9313 // let replacement = 123; 9314 // return x + replacement; 9315 // 9316 // // Substitution is not ok because "fn()" may change "x" 9317 // let replacement = fn(); 9318 // return x + replacement; 9319 // 9320 // // Substitution is not ok because "x == x" may change "x" due to "valueOf()" evaluation 9321 // let replacement = [x]; 9322 // return (x == x) + replacement; 9323 // 9324 replacementCanBeRemoved := p.astHelpers.ExprCanBeRemovedIfUnused(replacement) 9325 9326 if new, status := p.substituteSingleUseSymbolInExpr(*expr, ref, replacement, replacementCanBeRemoved); status == substituteSuccess { 9327 *expr = new 9328 return true 9329 } 9330 } 9331 9332 return false 9333 } 9334 9335 type substituteStatus uint8 9336 9337 const ( 9338 substituteContinue substituteStatus = iota 9339 substituteSuccess 9340 substituteFailure 9341 ) 9342 9343 func (p *parser) substituteSingleUseSymbolInExpr( 9344 expr js_ast.Expr, 9345 ref ast.Ref, 9346 replacement js_ast.Expr, 9347 replacementCanBeRemoved bool, 9348 ) (js_ast.Expr, substituteStatus) { 9349 switch e := expr.Data.(type) { 9350 case *js_ast.EIdentifier: 9351 if e.Ref == ref { 9352 p.ignoreUsage(ref) 9353 return replacement, substituteSuccess 9354 } 9355 9356 case *js_ast.ESpread: 9357 if value, status := p.substituteSingleUseSymbolInExpr(e.Value, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9358 e.Value = value 9359 return expr, status 9360 } 9361 9362 case *js_ast.EAwait: 9363 if value, status := p.substituteSingleUseSymbolInExpr(e.Value, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9364 e.Value = value 9365 return expr, status 9366 } 9367 9368 case *js_ast.EYield: 9369 if e.ValueOrNil.Data != nil { 9370 if value, status := p.substituteSingleUseSymbolInExpr(e.ValueOrNil, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9371 e.ValueOrNil = value 9372 return expr, status 9373 } 9374 } 9375 9376 case *js_ast.EImportCall: 9377 if value, status := p.substituteSingleUseSymbolInExpr(e.Expr, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9378 e.Expr = value 9379 return expr, status 9380 } 9381 9382 // The "import()" expression has side effects but the side effects are 9383 // always asynchronous so there is no way for the side effects to modify 9384 // the replacement value. So it's ok to reorder the replacement value 9385 // past the "import()" expression assuming everything else checks out. 9386 if replacementCanBeRemoved && p.astHelpers.ExprCanBeRemovedIfUnused(e.Expr) { 9387 return expr, substituteContinue 9388 } 9389 9390 case *js_ast.EUnary: 9391 switch e.Op { 9392 case js_ast.UnOpPreInc, js_ast.UnOpPostInc, js_ast.UnOpPreDec, js_ast.UnOpPostDec, js_ast.UnOpDelete: 9393 // Do not substitute into an assignment position 9394 9395 default: 9396 if value, status := p.substituteSingleUseSymbolInExpr(e.Value, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9397 e.Value = value 9398 return expr, status 9399 } 9400 } 9401 9402 case *js_ast.EDot: 9403 if value, status := p.substituteSingleUseSymbolInExpr(e.Target, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9404 e.Target = value 9405 return expr, status 9406 } 9407 9408 case *js_ast.EBinary: 9409 // Do not substitute into an assignment position 9410 if e.Op.BinaryAssignTarget() == js_ast.AssignTargetNone { 9411 if value, status := p.substituteSingleUseSymbolInExpr(e.Left, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9412 e.Left = value 9413 return expr, status 9414 } 9415 } else if !p.astHelpers.ExprCanBeRemovedIfUnused(e.Left) { 9416 // Do not reorder past a side effect in an assignment target, as that may 9417 // change the replacement value. For example, "fn()" may change "a" here: 9418 // 9419 // let a = 1; 9420 // foo[fn()] = a; 9421 // 9422 return expr, substituteFailure 9423 } else if e.Op.BinaryAssignTarget() == js_ast.AssignTargetUpdate && !replacementCanBeRemoved { 9424 // If this is a read-modify-write assignment and the replacement has side 9425 // effects, don't reorder it past the assignment target. The assignment 9426 // target is being read so it may be changed by the side effect. For 9427 // example, "fn()" may change "foo" here: 9428 // 9429 // let a = fn(); 9430 // foo += a; 9431 // 9432 return expr, substituteFailure 9433 } 9434 9435 // If we get here then it should be safe to attempt to substitute the 9436 // replacement past the left operand into the right operand. 9437 if value, status := p.substituteSingleUseSymbolInExpr(e.Right, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9438 e.Right = value 9439 return expr, status 9440 } 9441 9442 case *js_ast.EIf: 9443 if value, status := p.substituteSingleUseSymbolInExpr(e.Test, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9444 e.Test = value 9445 return expr, status 9446 } 9447 9448 // Do not substitute our unconditionally-executed value into a branch 9449 // unless the value itself has no side effects 9450 if replacementCanBeRemoved { 9451 // Unlike other branches in this function such as "a && b" or "a?.[b]", 9452 // the "a ? b : c" form has potential code evaluation along both control 9453 // flow paths. Handle this by allowing substitution into either branch. 9454 // Side effects in one branch should not prevent the substitution into 9455 // the other branch. 9456 9457 yesValue, yesStatus := p.substituteSingleUseSymbolInExpr(e.Yes, ref, replacement, replacementCanBeRemoved) 9458 if yesStatus == substituteSuccess { 9459 e.Yes = yesValue 9460 return expr, yesStatus 9461 } 9462 9463 noValue, noStatus := p.substituteSingleUseSymbolInExpr(e.No, ref, replacement, replacementCanBeRemoved) 9464 if noStatus == substituteSuccess { 9465 e.No = noValue 9466 return expr, noStatus 9467 } 9468 9469 // Side effects in either branch should stop us from continuing to try to 9470 // substitute the replacement after the control flow branches merge again. 9471 if yesStatus != substituteContinue || noStatus != substituteContinue { 9472 return expr, substituteFailure 9473 } 9474 } 9475 9476 case *js_ast.EIndex: 9477 if value, status := p.substituteSingleUseSymbolInExpr(e.Target, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9478 e.Target = value 9479 return expr, status 9480 } 9481 9482 // Do not substitute our unconditionally-executed value into a branch 9483 // unless the value itself has no side effects 9484 if replacementCanBeRemoved || e.OptionalChain == js_ast.OptionalChainNone { 9485 if value, status := p.substituteSingleUseSymbolInExpr(e.Index, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9486 e.Index = value 9487 return expr, status 9488 } 9489 } 9490 9491 case *js_ast.ECall: 9492 // Don't substitute something into a call target that could change "this" 9493 _, isDot := replacement.Data.(*js_ast.EDot) 9494 _, isIndex := replacement.Data.(*js_ast.EIndex) 9495 if isDot || isIndex { 9496 if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok && id.Ref == ref { 9497 break 9498 } 9499 } 9500 9501 if value, status := p.substituteSingleUseSymbolInExpr(e.Target, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9502 e.Target = value 9503 return expr, status 9504 } 9505 9506 // Do not substitute our unconditionally-executed value into a branch 9507 // unless the value itself has no side effects 9508 if replacementCanBeRemoved || e.OptionalChain == js_ast.OptionalChainNone { 9509 for i, arg := range e.Args { 9510 if value, status := p.substituteSingleUseSymbolInExpr(arg, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9511 e.Args[i] = value 9512 return expr, status 9513 } 9514 } 9515 } 9516 9517 case *js_ast.EArray: 9518 for i, item := range e.Items { 9519 if value, status := p.substituteSingleUseSymbolInExpr(item, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9520 e.Items[i] = value 9521 return expr, status 9522 } 9523 } 9524 9525 case *js_ast.EObject: 9526 for i, property := range e.Properties { 9527 // Check the key 9528 if property.Flags.Has(js_ast.PropertyIsComputed) { 9529 if value, status := p.substituteSingleUseSymbolInExpr(property.Key, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9530 e.Properties[i].Key = value 9531 return expr, status 9532 } 9533 9534 // Stop now because both computed keys and property spread have side effects 9535 return expr, substituteFailure 9536 } 9537 9538 // Check the value 9539 if property.ValueOrNil.Data != nil { 9540 if value, status := p.substituteSingleUseSymbolInExpr(property.ValueOrNil, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9541 e.Properties[i].ValueOrNil = value 9542 return expr, status 9543 } 9544 } 9545 } 9546 9547 case *js_ast.ETemplate: 9548 if e.TagOrNil.Data != nil { 9549 if value, status := p.substituteSingleUseSymbolInExpr(e.TagOrNil, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9550 e.TagOrNil = value 9551 return expr, status 9552 } 9553 } 9554 9555 for i, part := range e.Parts { 9556 if value, status := p.substituteSingleUseSymbolInExpr(part.Value, ref, replacement, replacementCanBeRemoved); status != substituteContinue { 9557 e.Parts[i].Value = value 9558 9559 // If we substituted a primitive, merge it into the template 9560 if js_ast.IsPrimitiveLiteral(value.Data) { 9561 expr = js_ast.InlinePrimitivesIntoTemplate(expr.Loc, e) 9562 } 9563 return expr, status 9564 } 9565 } 9566 } 9567 9568 // If both the replacement and this expression have no observable side 9569 // effects, then we can reorder the replacement past this expression 9570 if replacementCanBeRemoved && p.astHelpers.ExprCanBeRemovedIfUnused(expr) { 9571 return expr, substituteContinue 9572 } 9573 9574 // We can always reorder past primitive values 9575 if js_ast.IsPrimitiveLiteral(expr.Data) || js_ast.IsPrimitiveLiteral(replacement.Data) { 9576 return expr, substituteContinue 9577 } 9578 9579 // Otherwise we should stop trying to substitute past this point 9580 return expr, substituteFailure 9581 } 9582 9583 func (p *parser) visitLoopBody(stmt js_ast.Stmt) js_ast.Stmt { 9584 oldIsInsideLoop := p.fnOrArrowDataVisit.isInsideLoop 9585 p.fnOrArrowDataVisit.isInsideLoop = true 9586 p.loopBody = stmt.Data 9587 stmt = p.visitSingleStmt(stmt, stmtsLoopBody) 9588 p.fnOrArrowDataVisit.isInsideLoop = oldIsInsideLoop 9589 return stmt 9590 } 9591 9592 func (p *parser) visitSingleStmt(stmt js_ast.Stmt, kind stmtsKind) js_ast.Stmt { 9593 // To reduce stack depth, special-case blocks and process their children directly 9594 if block, ok := stmt.Data.(*js_ast.SBlock); ok { 9595 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 9596 block.Stmts = p.visitStmts(block.Stmts, kind) 9597 p.popScope() 9598 if p.options.minifySyntax { 9599 stmt = stmtsToSingleStmt(stmt.Loc, block.Stmts, block.CloseBraceLoc) 9600 } 9601 return stmt 9602 } 9603 9604 // Introduce a fake block scope for function declarations inside if statements 9605 fn, ok := stmt.Data.(*js_ast.SFunction) 9606 hasIfScope := ok && fn.Fn.HasIfScope 9607 if hasIfScope { 9608 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 9609 if p.isStrictMode() { 9610 p.markStrictModeFeature(ifElseFunctionStmt, js_lexer.RangeOfIdentifier(p.source, stmt.Loc), "") 9611 } 9612 } 9613 9614 stmts := p.visitStmts([]js_ast.Stmt{stmt}, kind) 9615 9616 // Balance the fake block scope introduced above 9617 if hasIfScope { 9618 p.popScope() 9619 } 9620 9621 return stmtsToSingleStmt(stmt.Loc, stmts, logger.Loc{}) 9622 } 9623 9624 // One statement could potentially expand to several statements 9625 func stmtsToSingleStmt(loc logger.Loc, stmts []js_ast.Stmt, closeBraceLoc logger.Loc) js_ast.Stmt { 9626 if len(stmts) == 0 { 9627 return js_ast.Stmt{Loc: loc, Data: js_ast.SEmptyShared} 9628 } 9629 if len(stmts) == 1 && !statementCaresAboutScope(stmts[0]) { 9630 return stmts[0] 9631 } 9632 return js_ast.Stmt{Loc: loc, Data: &js_ast.SBlock{Stmts: stmts, CloseBraceLoc: closeBraceLoc}} 9633 } 9634 9635 func (p *parser) visitForLoopInit(stmt js_ast.Stmt, isInOrOf bool) js_ast.Stmt { 9636 switch s := stmt.Data.(type) { 9637 case *js_ast.SExpr: 9638 assignTarget := js_ast.AssignTargetNone 9639 if isInOrOf { 9640 assignTarget = js_ast.AssignTargetReplace 9641 } 9642 p.stmtExprValue = s.Value.Data 9643 s.Value, _ = p.visitExprInOut(s.Value, exprIn{assignTarget: assignTarget}) 9644 9645 case *js_ast.SLocal: 9646 for i := range s.Decls { 9647 d := &s.Decls[i] 9648 p.visitBinding(d.Binding, bindingOpts{}) 9649 if d.ValueOrNil.Data != nil { 9650 d.ValueOrNil = p.visitExpr(d.ValueOrNil) 9651 } 9652 } 9653 s.Decls = p.lowerObjectRestInDecls(s.Decls) 9654 s.Kind = p.selectLocalKind(s.Kind) 9655 9656 default: 9657 panic("Internal error") 9658 } 9659 9660 return stmt 9661 } 9662 9663 func (p *parser) recordDeclaredSymbol(ref ast.Ref) { 9664 p.declaredSymbols = append(p.declaredSymbols, js_ast.DeclaredSymbol{ 9665 Ref: ref, 9666 IsTopLevel: p.currentScope == p.moduleScope, 9667 }) 9668 } 9669 9670 type bindingOpts struct { 9671 duplicateArgCheck map[string]logger.Range 9672 } 9673 9674 func (p *parser) visitBinding(binding js_ast.Binding, opts bindingOpts) { 9675 switch b := binding.Data.(type) { 9676 case *js_ast.BMissing: 9677 9678 case *js_ast.BIdentifier: 9679 p.recordDeclaredSymbol(b.Ref) 9680 name := p.symbols[b.Ref.InnerIndex].OriginalName 9681 p.validateDeclaredSymbolName(binding.Loc, name) 9682 if opts.duplicateArgCheck != nil { 9683 r := js_lexer.RangeOfIdentifier(p.source, binding.Loc) 9684 if firstRange := opts.duplicateArgCheck[name]; firstRange.Len > 0 { 9685 p.log.AddErrorWithNotes(&p.tracker, r, 9686 fmt.Sprintf("%q cannot be bound multiple times in the same parameter list", name), 9687 []logger.MsgData{p.tracker.MsgData(firstRange, fmt.Sprintf("The name %q was originally bound here:", name))}) 9688 } else { 9689 opts.duplicateArgCheck[name] = r 9690 } 9691 } 9692 9693 case *js_ast.BArray: 9694 for i := range b.Items { 9695 item := &b.Items[i] 9696 p.visitBinding(item.Binding, opts) 9697 if item.DefaultValueOrNil.Data != nil { 9698 // Propagate the name to keep from the binding into the initializer 9699 if id, ok := item.Binding.Data.(*js_ast.BIdentifier); ok { 9700 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 9701 p.nameToKeepIsFor = item.DefaultValueOrNil.Data 9702 } 9703 9704 item.DefaultValueOrNil = p.visitExpr(item.DefaultValueOrNil) 9705 } 9706 } 9707 9708 case *js_ast.BObject: 9709 for i, property := range b.Properties { 9710 if !property.IsSpread { 9711 property.Key, _ = p.visitExprInOut(property.Key, exprIn{ 9712 shouldMangleStringsAsProps: true, 9713 }) 9714 } 9715 p.visitBinding(property.Value, opts) 9716 if property.DefaultValueOrNil.Data != nil { 9717 // Propagate the name to keep from the binding into the initializer 9718 if id, ok := property.Value.Data.(*js_ast.BIdentifier); ok { 9719 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 9720 p.nameToKeepIsFor = property.DefaultValueOrNil.Data 9721 } 9722 9723 property.DefaultValueOrNil = p.visitExpr(property.DefaultValueOrNil) 9724 } 9725 b.Properties[i] = property 9726 } 9727 9728 default: 9729 panic("Internal error") 9730 } 9731 } 9732 9733 func statementCaresAboutScope(stmt js_ast.Stmt) bool { 9734 switch s := stmt.Data.(type) { 9735 case *js_ast.SBlock, *js_ast.SEmpty, *js_ast.SDebugger, *js_ast.SExpr, *js_ast.SIf, 9736 *js_ast.SFor, *js_ast.SForIn, *js_ast.SForOf, *js_ast.SDoWhile, *js_ast.SWhile, 9737 *js_ast.SWith, *js_ast.STry, *js_ast.SSwitch, *js_ast.SReturn, *js_ast.SThrow, 9738 *js_ast.SBreak, *js_ast.SContinue, *js_ast.SDirective, *js_ast.SLabel: 9739 return false 9740 9741 case *js_ast.SLocal: 9742 return s.Kind != js_ast.LocalVar 9743 9744 default: 9745 return true 9746 } 9747 } 9748 9749 func dropFirstStatement(body js_ast.Stmt, replaceOrNil js_ast.Stmt) js_ast.Stmt { 9750 if block, ok := body.Data.(*js_ast.SBlock); ok && len(block.Stmts) > 0 { 9751 if replaceOrNil.Data != nil { 9752 block.Stmts[0] = replaceOrNil 9753 } else if len(block.Stmts) == 2 && !statementCaresAboutScope(block.Stmts[1]) { 9754 return block.Stmts[1] 9755 } else { 9756 block.Stmts = block.Stmts[1:] 9757 } 9758 return body 9759 } 9760 if replaceOrNil.Data != nil { 9761 return replaceOrNil 9762 } 9763 return js_ast.Stmt{Loc: body.Loc, Data: js_ast.SEmptyShared} 9764 } 9765 9766 func mangleFor(s *js_ast.SFor) { 9767 // Get the first statement in the loop 9768 first := s.Body 9769 if block, ok := first.Data.(*js_ast.SBlock); ok && len(block.Stmts) > 0 { 9770 first = block.Stmts[0] 9771 } 9772 9773 if ifS, ok := first.Data.(*js_ast.SIf); ok { 9774 // "for (;;) if (x) break;" => "for (; !x;) ;" 9775 // "for (; a;) if (x) break;" => "for (; a && !x;) ;" 9776 // "for (;;) if (x) break; else y();" => "for (; !x;) y();" 9777 // "for (; a;) if (x) break; else y();" => "for (; a && !x;) y();" 9778 if breakS, ok := ifS.Yes.Data.(*js_ast.SBreak); ok && breakS.Label == nil { 9779 var not js_ast.Expr 9780 if unary, ok := ifS.Test.Data.(*js_ast.EUnary); ok && unary.Op == js_ast.UnOpNot { 9781 not = unary.Value 9782 } else { 9783 not = js_ast.Not(ifS.Test) 9784 } 9785 if s.TestOrNil.Data != nil { 9786 s.TestOrNil = js_ast.Expr{Loc: s.TestOrNil.Loc, Data: &js_ast.EBinary{ 9787 Op: js_ast.BinOpLogicalAnd, 9788 Left: s.TestOrNil, 9789 Right: not, 9790 }} 9791 } else { 9792 s.TestOrNil = not 9793 } 9794 s.Body = dropFirstStatement(s.Body, ifS.NoOrNil) 9795 return 9796 } 9797 9798 // "for (;;) if (x) y(); else break;" => "for (; x;) y();" 9799 // "for (; a;) if (x) y(); else break;" => "for (; a && x;) y();" 9800 if ifS.NoOrNil.Data != nil { 9801 if breakS, ok := ifS.NoOrNil.Data.(*js_ast.SBreak); ok && breakS.Label == nil { 9802 if s.TestOrNil.Data != nil { 9803 s.TestOrNil = js_ast.Expr{Loc: s.TestOrNil.Loc, Data: &js_ast.EBinary{ 9804 Op: js_ast.BinOpLogicalAnd, 9805 Left: s.TestOrNil, 9806 Right: ifS.Test, 9807 }} 9808 } else { 9809 s.TestOrNil = ifS.Test 9810 } 9811 s.Body = dropFirstStatement(s.Body, ifS.Yes) 9812 return 9813 } 9814 } 9815 } 9816 } 9817 9818 func appendIfOrLabelBodyPreservingScope(stmts []js_ast.Stmt, body js_ast.Stmt) []js_ast.Stmt { 9819 if block, ok := body.Data.(*js_ast.SBlock); ok { 9820 keepBlock := false 9821 for _, stmt := range block.Stmts { 9822 if statementCaresAboutScope(stmt) { 9823 keepBlock = true 9824 break 9825 } 9826 } 9827 if !keepBlock { 9828 return append(stmts, block.Stmts...) 9829 } 9830 } 9831 9832 if statementCaresAboutScope(body) { 9833 return append(stmts, js_ast.Stmt{Loc: body.Loc, Data: &js_ast.SBlock{Stmts: []js_ast.Stmt{body}}}) 9834 } 9835 9836 return append(stmts, body) 9837 } 9838 9839 func (p *parser) mangleIf(stmts []js_ast.Stmt, loc logger.Loc, s *js_ast.SIf) []js_ast.Stmt { 9840 // Constant folding using the test expression 9841 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(s.Test.Data); ok { 9842 if boolean { 9843 // The test is truthy 9844 if s.NoOrNil.Data == nil || !shouldKeepStmtInDeadControlFlow(s.NoOrNil) { 9845 // We can drop the "no" branch 9846 if sideEffects == js_ast.CouldHaveSideEffects { 9847 // Keep the condition if it could have side effects (but is still known to be truthy) 9848 if test := p.astHelpers.SimplifyUnusedExpr(s.Test, p.options.unsupportedJSFeatures); test.Data != nil { 9849 stmts = append(stmts, js_ast.Stmt{Loc: s.Test.Loc, Data: &js_ast.SExpr{Value: test}}) 9850 } 9851 } 9852 return appendIfOrLabelBodyPreservingScope(stmts, s.Yes) 9853 } else { 9854 // We have to keep the "no" branch 9855 } 9856 } else { 9857 // The test is falsy 9858 if !shouldKeepStmtInDeadControlFlow(s.Yes) { 9859 // We can drop the "yes" branch 9860 if sideEffects == js_ast.CouldHaveSideEffects { 9861 // Keep the condition if it could have side effects (but is still known to be falsy) 9862 if test := p.astHelpers.SimplifyUnusedExpr(s.Test, p.options.unsupportedJSFeatures); test.Data != nil { 9863 stmts = append(stmts, js_ast.Stmt{Loc: s.Test.Loc, Data: &js_ast.SExpr{Value: test}}) 9864 } 9865 } 9866 if s.NoOrNil.Data == nil { 9867 return stmts 9868 } 9869 return appendIfOrLabelBodyPreservingScope(stmts, s.NoOrNil) 9870 } else { 9871 // We have to keep the "yes" branch 9872 } 9873 } 9874 9875 // Use "1" and "0" instead of "true" and "false" to be shorter 9876 if sideEffects == js_ast.NoSideEffects { 9877 if boolean { 9878 s.Test.Data = &js_ast.ENumber{Value: 1} 9879 } else { 9880 s.Test.Data = &js_ast.ENumber{Value: 0} 9881 } 9882 } 9883 } 9884 9885 var expr js_ast.Expr 9886 9887 if yes, ok := s.Yes.Data.(*js_ast.SExpr); ok { 9888 // "yes" is an expression 9889 if s.NoOrNil.Data == nil { 9890 if not, ok := s.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9891 // "if (!a) b();" => "a || b();" 9892 expr = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalOr, not.Value, yes.Value) 9893 } else { 9894 // "if (a) b();" => "a && b();" 9895 expr = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalAnd, s.Test, yes.Value) 9896 } 9897 } else if no, ok := s.NoOrNil.Data.(*js_ast.SExpr); ok { 9898 // "if (a) b(); else c();" => "a ? b() : c();" 9899 expr = p.astHelpers.MangleIfExpr(loc, &js_ast.EIf{ 9900 Test: s.Test, 9901 Yes: yes.Value, 9902 No: no.Value, 9903 }, p.options.unsupportedJSFeatures) 9904 } 9905 } else if _, ok := s.Yes.Data.(*js_ast.SEmpty); ok { 9906 // "yes" is missing 9907 if s.NoOrNil.Data == nil { 9908 // "yes" and "no" are both missing 9909 if p.astHelpers.ExprCanBeRemovedIfUnused(s.Test) { 9910 // "if (1) {}" => "" 9911 return stmts 9912 } else { 9913 // "if (a) {}" => "a;" 9914 expr = s.Test 9915 } 9916 } else if no, ok := s.NoOrNil.Data.(*js_ast.SExpr); ok { 9917 if not, ok := s.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9918 // "if (!a) {} else b();" => "a && b();" 9919 expr = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalAnd, not.Value, no.Value) 9920 } else { 9921 // "if (a) {} else b();" => "a || b();" 9922 expr = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalOr, s.Test, no.Value) 9923 } 9924 } else { 9925 // "yes" is missing and "no" is not missing (and is not an expression) 9926 if not, ok := s.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9927 // "if (!a) {} else throw b;" => "if (a) throw b;" 9928 s.Test = not.Value 9929 s.Yes = s.NoOrNil 9930 s.NoOrNil = js_ast.Stmt{} 9931 } else { 9932 // "if (a) {} else throw b;" => "if (!a) throw b;" 9933 s.Test = js_ast.Not(s.Test) 9934 s.Yes = s.NoOrNil 9935 s.NoOrNil = js_ast.Stmt{} 9936 } 9937 } 9938 } else { 9939 // "yes" is not missing (and is not an expression) 9940 if s.NoOrNil.Data != nil { 9941 // "yes" is not missing (and is not an expression) and "no" is not missing 9942 if not, ok := s.Test.Data.(*js_ast.EUnary); ok && not.Op == js_ast.UnOpNot { 9943 // "if (!a) return b; else return c;" => "if (a) return c; else return b;" 9944 s.Test = not.Value 9945 s.Yes, s.NoOrNil = s.NoOrNil, s.Yes 9946 } 9947 } else { 9948 // "no" is missing 9949 if s2, ok := s.Yes.Data.(*js_ast.SIf); ok && s2.NoOrNil.Data == nil { 9950 // "if (a) if (b) return c;" => "if (a && b) return c;" 9951 s.Test = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalAnd, s.Test, s2.Test) 9952 s.Yes = s2.Yes 9953 } 9954 } 9955 } 9956 9957 // Return an expression if we replaced the if statement with an expression above 9958 if expr.Data != nil { 9959 expr = p.astHelpers.SimplifyUnusedExpr(expr, p.options.unsupportedJSFeatures) 9960 return append(stmts, js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{Value: expr}}) 9961 } 9962 9963 return append(stmts, js_ast.Stmt{Loc: loc, Data: s}) 9964 } 9965 9966 func (p *parser) keepExprSymbolName(value js_ast.Expr, name string) js_ast.Expr { 9967 value = p.callRuntime(value.Loc, "__name", []js_ast.Expr{value, 9968 {Loc: value.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}}, 9969 }) 9970 9971 // Make sure tree shaking removes this if the function is never used 9972 value.Data.(*js_ast.ECall).CanBeUnwrappedIfUnused = true 9973 return value 9974 } 9975 9976 func (p *parser) keepClassOrFnSymbolName(loc logger.Loc, expr js_ast.Expr, name string) js_ast.Stmt { 9977 return js_ast.Stmt{Loc: loc, Data: &js_ast.SExpr{ 9978 Value: p.callRuntime(loc, "__name", []js_ast.Expr{ 9979 expr, 9980 {Loc: loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}}, 9981 }), 9982 IsFromClassOrFnThatCanBeRemovedIfUnused: true, 9983 }} 9984 } 9985 9986 func (p *parser) visitAndAppendStmt(stmts []js_ast.Stmt, stmt js_ast.Stmt) []js_ast.Stmt { 9987 // By default any statement ends the const local prefix 9988 wasAfterAfterConstLocalPrefix := p.currentScope.IsAfterConstLocalPrefix 9989 p.currentScope.IsAfterConstLocalPrefix = true 9990 9991 switch s := stmt.Data.(type) { 9992 case *js_ast.SEmpty, *js_ast.SComment: 9993 // Comments do not end the const local prefix 9994 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 9995 9996 case *js_ast.SDebugger: 9997 // Debugger statements do not end the const local prefix 9998 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 9999 10000 if p.options.dropDebugger { 10001 return stmts 10002 } 10003 10004 case *js_ast.STypeScript: 10005 // Type annotations do not end the const local prefix 10006 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 10007 10008 // Erase TypeScript constructs from the output completely 10009 return stmts 10010 10011 case *js_ast.SDirective: 10012 // Directives do not end the const local prefix 10013 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 10014 10015 if p.isStrictMode() && s.LegacyOctalLoc.Start > 0 { 10016 p.markStrictModeFeature(legacyOctalEscape, p.source.RangeOfLegacyOctalEscape(s.LegacyOctalLoc), "") 10017 } 10018 10019 case *js_ast.SImport: 10020 p.recordDeclaredSymbol(s.NamespaceRef) 10021 10022 if s.DefaultName != nil { 10023 p.recordDeclaredSymbol(s.DefaultName.Ref) 10024 } 10025 10026 if s.Items != nil { 10027 for _, item := range *s.Items { 10028 p.recordDeclaredSymbol(item.Name.Ref) 10029 } 10030 } 10031 10032 case *js_ast.SExportClause: 10033 // "export {foo}" 10034 end := 0 10035 for _, item := range s.Items { 10036 name := p.loadNameFromRef(item.Name.Ref) 10037 ref := p.findSymbol(item.AliasLoc, name).ref 10038 10039 if p.symbols[ref.InnerIndex].Kind == ast.SymbolUnbound { 10040 // Silently strip exports of non-local symbols in TypeScript, since 10041 // those likely correspond to type-only exports. But report exports of 10042 // non-local symbols as errors in JavaScript. 10043 if !p.options.ts.Parse { 10044 r := js_lexer.RangeOfIdentifier(p.source, item.Name.Loc) 10045 p.log.AddError(&p.tracker, r, fmt.Sprintf("%q is not declared in this file", name)) 10046 } 10047 continue 10048 } 10049 10050 item.Name.Ref = ref 10051 s.Items[end] = item 10052 end++ 10053 } 10054 10055 // Note: do not remove empty export statements since TypeScript uses them as module markers 10056 s.Items = s.Items[:end] 10057 10058 case *js_ast.SExportFrom: 10059 // "export {foo} from 'path'" 10060 name := p.loadNameFromRef(s.NamespaceRef) 10061 s.NamespaceRef = p.newSymbol(ast.SymbolOther, name) 10062 p.currentScope.Generated = append(p.currentScope.Generated, s.NamespaceRef) 10063 p.recordDeclaredSymbol(s.NamespaceRef) 10064 10065 // This is a re-export and the symbols created here are used to reference 10066 // names in another file. This means the symbols are really aliases. 10067 for i, item := range s.Items { 10068 name := p.loadNameFromRef(item.Name.Ref) 10069 ref := p.newSymbol(ast.SymbolOther, name) 10070 p.currentScope.Generated = append(p.currentScope.Generated, ref) 10071 p.recordDeclaredSymbol(ref) 10072 s.Items[i].Name.Ref = ref 10073 } 10074 10075 case *js_ast.SExportStar: 10076 // "export * from 'path'" 10077 // "export * as ns from 'path'" 10078 name := p.loadNameFromRef(s.NamespaceRef) 10079 s.NamespaceRef = p.newSymbol(ast.SymbolOther, name) 10080 p.currentScope.Generated = append(p.currentScope.Generated, s.NamespaceRef) 10081 p.recordDeclaredSymbol(s.NamespaceRef) 10082 10083 // "export * as ns from 'path'" 10084 if s.Alias != nil { 10085 // "import * as ns from 'path'" 10086 // "export {ns}" 10087 if p.options.unsupportedJSFeatures.Has(compat.ExportStarAs) { 10088 p.recordUsage(s.NamespaceRef) 10089 return append(stmts, 10090 js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SImport{ 10091 NamespaceRef: s.NamespaceRef, 10092 StarNameLoc: &s.Alias.Loc, 10093 ImportRecordIndex: s.ImportRecordIndex, 10094 }}, 10095 js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SExportClause{ 10096 Items: []js_ast.ClauseItem{{ 10097 Alias: s.Alias.OriginalName, 10098 OriginalName: s.Alias.OriginalName, 10099 AliasLoc: s.Alias.Loc, 10100 Name: ast.LocRef{Loc: s.Alias.Loc, Ref: s.NamespaceRef}, 10101 }}, 10102 IsSingleLine: true, 10103 }}, 10104 ) 10105 } 10106 } 10107 10108 case *js_ast.SExportDefault: 10109 p.recordDeclaredSymbol(s.DefaultName.Ref) 10110 10111 switch s2 := s.Value.Data.(type) { 10112 case *js_ast.SExpr: 10113 // Propagate the name to keep from the export into the value 10114 p.nameToKeep = "default" 10115 p.nameToKeepIsFor = s2.Value.Data 10116 10117 s2.Value = p.visitExpr(s2.Value) 10118 10119 // Discard type-only export default statements 10120 if p.options.ts.Parse { 10121 if id, ok := s2.Value.Data.(*js_ast.EIdentifier); ok { 10122 symbol := p.symbols[id.Ref.InnerIndex] 10123 if symbol.Kind == ast.SymbolUnbound && p.localTypeNames[symbol.OriginalName] { 10124 return stmts 10125 } 10126 } 10127 } 10128 10129 // If there are lowered "using" declarations, change this into a "var" 10130 if p.currentScope.Parent == nil && p.willWrapModuleInTryCatchForUsing { 10131 stmts = append(stmts, 10132 js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SLocal{ 10133 Decls: []js_ast.Decl{{ 10134 Binding: js_ast.Binding{Loc: s.DefaultName.Loc, Data: &js_ast.BIdentifier{Ref: s.DefaultName.Ref}}, 10135 ValueOrNil: s2.Value, 10136 }}, 10137 }}, 10138 js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SExportClause{Items: []js_ast.ClauseItem{{ 10139 Alias: "default", 10140 AliasLoc: s.DefaultName.Loc, 10141 Name: s.DefaultName, 10142 }}}}, 10143 ) 10144 break 10145 } 10146 10147 stmts = append(stmts, stmt) 10148 10149 case *js_ast.SFunction: 10150 // If we need to preserve the name but there is no name, generate a name 10151 var name string 10152 if p.options.keepNames { 10153 if s2.Fn.Name == nil { 10154 clone := s.DefaultName 10155 s2.Fn.Name = &clone 10156 name = "default" 10157 } else { 10158 name = p.symbols[s2.Fn.Name.Ref.InnerIndex].OriginalName 10159 } 10160 } 10161 10162 p.visitFn(&s2.Fn, s2.Fn.OpenParenLoc, visitFnOpts{}) 10163 stmts = append(stmts, stmt) 10164 10165 // Optionally preserve the name 10166 if p.options.keepNames { 10167 p.symbols[s2.Fn.Name.Ref.InnerIndex].Flags |= ast.DidKeepName 10168 fn := js_ast.Expr{Loc: s2.Fn.Name.Loc, Data: &js_ast.EIdentifier{Ref: s2.Fn.Name.Ref}} 10169 stmts = append(stmts, p.keepClassOrFnSymbolName(s2.Fn.Name.Loc, fn, name)) 10170 } 10171 10172 case *js_ast.SClass: 10173 result := p.visitClass(s.Value.Loc, &s2.Class, s.DefaultName.Ref, "default") 10174 10175 // Lower class field syntax for browsers that don't support it 10176 classStmts, _ := p.lowerClass(stmt, js_ast.Expr{}, result, "") 10177 10178 // Remember if the class was side-effect free before lowering 10179 if result.canBeRemovedIfUnused { 10180 for _, classStmt := range classStmts { 10181 if s2, ok := classStmt.Data.(*js_ast.SExpr); ok { 10182 s2.IsFromClassOrFnThatCanBeRemovedIfUnused = true 10183 } 10184 } 10185 } 10186 10187 stmts = append(stmts, classStmts...) 10188 10189 default: 10190 panic("Internal error") 10191 } 10192 10193 // Use a more friendly name than "default" now that "--keep-names" has 10194 // been applied and has made sure to enforce the name "default" 10195 if p.symbols[s.DefaultName.Ref.InnerIndex].OriginalName == "default" { 10196 p.symbols[s.DefaultName.Ref.InnerIndex].OriginalName = p.source.IdentifierName + "_default" 10197 } 10198 10199 return stmts 10200 10201 case *js_ast.SExportEquals: 10202 // "module.exports = value" 10203 stmts = append(stmts, js_ast.AssignStmt( 10204 js_ast.Expr{Loc: stmt.Loc, Data: &js_ast.EDot{ 10205 Target: js_ast.Expr{Loc: stmt.Loc, Data: &js_ast.EIdentifier{Ref: p.moduleRef}}, 10206 Name: "exports", 10207 NameLoc: stmt.Loc, 10208 }}, 10209 p.visitExpr(s.Value), 10210 )) 10211 p.recordUsage(p.moduleRef) 10212 return stmts 10213 10214 case *js_ast.SBreak: 10215 if s.Label != nil { 10216 name := p.loadNameFromRef(s.Label.Ref) 10217 s.Label.Ref, _, _ = p.findLabelSymbol(s.Label.Loc, name) 10218 } else if !p.fnOrArrowDataVisit.isInsideLoop && !p.fnOrArrowDataVisit.isInsideSwitch { 10219 r := js_lexer.RangeOfIdentifier(p.source, stmt.Loc) 10220 p.log.AddError(&p.tracker, r, "Cannot use \"break\" here:") 10221 } 10222 10223 case *js_ast.SContinue: 10224 if s.Label != nil { 10225 name := p.loadNameFromRef(s.Label.Ref) 10226 var isLoop, ok bool 10227 s.Label.Ref, isLoop, ok = p.findLabelSymbol(s.Label.Loc, name) 10228 if ok && !isLoop { 10229 r := js_lexer.RangeOfIdentifier(p.source, s.Label.Loc) 10230 p.log.AddError(&p.tracker, r, fmt.Sprintf("Cannot continue to label \"%s\"", name)) 10231 } 10232 } else if !p.fnOrArrowDataVisit.isInsideLoop { 10233 r := js_lexer.RangeOfIdentifier(p.source, stmt.Loc) 10234 p.log.AddError(&p.tracker, r, "Cannot use \"continue\" here:") 10235 } 10236 10237 case *js_ast.SLabel: 10238 // Forbid functions inside labels in strict mode 10239 if p.isStrictMode() { 10240 if _, ok := s.Stmt.Data.(*js_ast.SFunction); ok { 10241 p.markStrictModeFeature(labelFunctionStmt, js_lexer.RangeOfIdentifier(p.source, s.Stmt.Loc), "") 10242 } 10243 } 10244 10245 p.pushScopeForVisitPass(js_ast.ScopeLabel, stmt.Loc) 10246 name := p.loadNameFromRef(s.Name.Ref) 10247 if js_lexer.StrictModeReservedWords[name] { 10248 p.markStrictModeFeature(reservedWord, js_lexer.RangeOfIdentifier(p.source, s.Name.Loc), name) 10249 } 10250 ref := p.newSymbol(ast.SymbolLabel, name) 10251 s.Name.Ref = ref 10252 10253 // Duplicate labels are an error 10254 for scope := p.currentScope.Parent; scope != nil; scope = scope.Parent { 10255 if scope.Label.Ref != ast.InvalidRef && name == p.symbols[scope.Label.Ref.InnerIndex].OriginalName { 10256 p.log.AddErrorWithNotes(&p.tracker, js_lexer.RangeOfIdentifier(p.source, s.Name.Loc), 10257 fmt.Sprintf("Duplicate label %q", name), 10258 []logger.MsgData{p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, scope.Label.Loc), 10259 fmt.Sprintf("The original label %q is here:", name))}) 10260 break 10261 } 10262 if scope.Kind == js_ast.ScopeFunctionBody { 10263 // Labels are only visible within the function they are defined in. 10264 break 10265 } 10266 } 10267 10268 p.currentScope.Label = ast.LocRef{Loc: s.Name.Loc, Ref: ref} 10269 switch s.Stmt.Data.(type) { 10270 case *js_ast.SFor, *js_ast.SForIn, *js_ast.SForOf, *js_ast.SWhile, *js_ast.SDoWhile: 10271 p.currentScope.LabelStmtIsLoop = true 10272 } 10273 10274 // If we're dropping this statement, consider control flow to be dead 10275 _, shouldDropLabel := p.dropLabelsMap[name] 10276 old := p.isControlFlowDead 10277 if shouldDropLabel { 10278 p.isControlFlowDead = true 10279 } 10280 10281 s.Stmt = p.visitSingleStmt(s.Stmt, stmtsNormal) 10282 p.popScope() 10283 10284 // Drop this entire statement if requested 10285 if shouldDropLabel { 10286 p.isControlFlowDead = old 10287 return stmts 10288 } 10289 10290 if p.options.minifySyntax { 10291 // Optimize "x: break x" which some people apparently write by hand 10292 if child, ok := s.Stmt.Data.(*js_ast.SBreak); ok && child.Label != nil && child.Label.Ref == s.Name.Ref { 10293 return stmts 10294 } 10295 10296 // Remove the label if it's not necessary 10297 if p.symbols[ref.InnerIndex].UseCountEstimate == 0 { 10298 return appendIfOrLabelBodyPreservingScope(stmts, s.Stmt) 10299 } 10300 } 10301 10302 // Handle "for await" that has been lowered by moving this label inside the "try" 10303 if try, ok := s.Stmt.Data.(*js_ast.STry); ok && len(try.Block.Stmts) > 0 { 10304 if _, ok := try.Block.Stmts[0].Data.(*js_ast.SFor); ok { 10305 try.Block.Stmts[0] = js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SLabel{ 10306 Stmt: try.Block.Stmts[0], 10307 Name: s.Name, 10308 IsSingleLineStmt: s.IsSingleLineStmt, 10309 }} 10310 return append(stmts, s.Stmt) 10311 } 10312 } 10313 10314 case *js_ast.SLocal: 10315 // Silently remove unsupported top-level "await" in dead code branches 10316 if s.Kind == js_ast.LocalAwaitUsing && p.fnOrArrowDataVisit.isOutsideFnOrArrow { 10317 if p.isControlFlowDead && (p.options.unsupportedJSFeatures.Has(compat.TopLevelAwait) || !p.options.outputFormat.KeepESMImportExportSyntax()) { 10318 s.Kind = js_ast.LocalUsing 10319 } else { 10320 p.liveTopLevelAwaitKeyword = logger.Range{Loc: stmt.Loc, Len: 5} 10321 p.markSyntaxFeature(compat.TopLevelAwait, logger.Range{Loc: stmt.Loc, Len: 5}) 10322 } 10323 } 10324 10325 // Local statements do not end the const local prefix 10326 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 10327 10328 for i := range s.Decls { 10329 d := &s.Decls[i] 10330 p.visitBinding(d.Binding, bindingOpts{}) 10331 10332 // Visit the initializer 10333 if d.ValueOrNil.Data != nil { 10334 // Fold numeric constants in the initializer 10335 oldShouldFoldTypeScriptConstantExpressions := p.shouldFoldTypeScriptConstantExpressions 10336 p.shouldFoldTypeScriptConstantExpressions = p.options.minifySyntax && !p.currentScope.IsAfterConstLocalPrefix 10337 10338 // Propagate the name to keep from the binding into the initializer 10339 if id, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 10340 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 10341 p.nameToKeepIsFor = d.ValueOrNil.Data 10342 } 10343 10344 d.ValueOrNil = p.visitExpr(d.ValueOrNil) 10345 10346 p.shouldFoldTypeScriptConstantExpressions = oldShouldFoldTypeScriptConstantExpressions 10347 10348 // Initializing to undefined is implicit, but be careful to not 10349 // accidentally cause a syntax error or behavior change by removing 10350 // the value 10351 // 10352 // Good: 10353 // "let a = undefined;" => "let a;" 10354 // 10355 // Bad (a syntax error): 10356 // "let {} = undefined;" => "let {};" 10357 // 10358 // Bad (a behavior change): 10359 // "a = 123; var a = undefined;" => "a = 123; var a;" 10360 // 10361 if p.options.minifySyntax && s.Kind == js_ast.LocalLet { 10362 if _, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 10363 if _, ok := d.ValueOrNil.Data.(*js_ast.EUndefined); ok { 10364 d.ValueOrNil = js_ast.Expr{} 10365 } 10366 } 10367 } 10368 10369 // Yarn's PnP data may be stored in a variable: https://github.com/yarnpkg/berry/pull/4320 10370 if p.options.decodeHydrateRuntimeStateYarnPnP { 10371 if str, ok := d.ValueOrNil.Data.(*js_ast.EString); ok { 10372 if id, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 10373 if p.stringLocalsForYarnPnP == nil { 10374 p.stringLocalsForYarnPnP = make(map[ast.Ref]stringLocalForYarnPnP) 10375 } 10376 p.stringLocalsForYarnPnP[id.Ref] = stringLocalForYarnPnP{value: str.Value, loc: d.ValueOrNil.Loc} 10377 } 10378 } 10379 } 10380 } 10381 10382 // Attempt to continue the const local prefix 10383 if p.options.minifySyntax && !p.currentScope.IsAfterConstLocalPrefix { 10384 if id, ok := d.Binding.Data.(*js_ast.BIdentifier); ok { 10385 if s.Kind == js_ast.LocalConst && d.ValueOrNil.Data != nil { 10386 if value := js_ast.ExprToConstValue(d.ValueOrNil); value.Kind != js_ast.ConstValueNone { 10387 if p.constValues == nil { 10388 p.constValues = make(map[ast.Ref]js_ast.ConstValue) 10389 } 10390 p.constValues[id.Ref] = value 10391 continue 10392 } 10393 } 10394 10395 if d.ValueOrNil.Data != nil && !isSafeForConstLocalPrefix(d.ValueOrNil) { 10396 p.currentScope.IsAfterConstLocalPrefix = true 10397 } 10398 } else { 10399 // A non-identifier binding ends the const local prefix 10400 p.currentScope.IsAfterConstLocalPrefix = true 10401 } 10402 } 10403 } 10404 10405 // Handle being exported inside a namespace 10406 if s.IsExport && p.enclosingNamespaceArgRef != nil { 10407 wrapIdentifier := func(loc logger.Loc, ref ast.Ref) js_ast.Expr { 10408 p.recordUsage(*p.enclosingNamespaceArgRef) 10409 return js_ast.Expr{Loc: loc, Data: p.dotOrMangledPropVisit( 10410 js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}}, 10411 p.symbols[ref.InnerIndex].OriginalName, 10412 loc, 10413 )} 10414 } 10415 for _, decl := range s.Decls { 10416 if decl.ValueOrNil.Data != nil { 10417 target := js_ast.ConvertBindingToExpr(decl.Binding, wrapIdentifier) 10418 if result, ok := p.lowerAssign(target, decl.ValueOrNil, objRestReturnValueIsUnused); ok { 10419 target = result 10420 } else { 10421 target = js_ast.Assign(target, decl.ValueOrNil) 10422 } 10423 stmts = append(stmts, js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SExpr{Value: target}}) 10424 } 10425 } 10426 return stmts 10427 } 10428 10429 s.Decls = p.lowerObjectRestInDecls(s.Decls) 10430 10431 // Optimization: Avoid unnecessary "using" machinery by changing ones 10432 // initialized to "null" or "undefined" into a normal variable. Note that 10433 // "await using" still needs the "await", so we can't do it for those. 10434 if p.options.minifySyntax && s.Kind == js_ast.LocalUsing { 10435 s.Kind = js_ast.LocalConst 10436 for _, decl := range s.Decls { 10437 if t := js_ast.KnownPrimitiveType(decl.ValueOrNil.Data); t != js_ast.PrimitiveNull && t != js_ast.PrimitiveUndefined { 10438 s.Kind = js_ast.LocalUsing 10439 break 10440 } 10441 } 10442 } 10443 10444 s.Kind = p.selectLocalKind(s.Kind) 10445 10446 // Potentially relocate "var" declarations to the top level 10447 if s.Kind == js_ast.LocalVar { 10448 if assign, ok := p.maybeRelocateVarsToTopLevel(s.Decls, relocateVarsNormal); ok { 10449 if assign.Data != nil { 10450 stmts = append(stmts, assign) 10451 } 10452 return stmts 10453 } 10454 } 10455 10456 case *js_ast.SExpr: 10457 shouldTrimUnsightlyPrimitives := !p.options.minifySyntax && !isUnsightlyPrimitive(s.Value.Data) 10458 p.stmtExprValue = s.Value.Data 10459 s.Value = p.visitExpr(s.Value) 10460 10461 // Expressions that have been simplified down to a single primitive don't 10462 // have any effect, and are automatically removed during minification. 10463 // However, some people are really bothered by seeing them. Remove them 10464 // so we don't bother these people. 10465 if shouldTrimUnsightlyPrimitives && isUnsightlyPrimitive(s.Value.Data) { 10466 return stmts 10467 } 10468 10469 // Trim expressions without side effects 10470 if p.options.minifySyntax { 10471 s.Value = p.astHelpers.SimplifyUnusedExpr(s.Value, p.options.unsupportedJSFeatures) 10472 if s.Value.Data == nil { 10473 return stmts 10474 } 10475 } 10476 10477 case *js_ast.SThrow: 10478 s.Value = p.visitExpr(s.Value) 10479 10480 case *js_ast.SReturn: 10481 // Forbid top-level return inside modules with ECMAScript syntax 10482 if p.fnOrArrowDataVisit.isOutsideFnOrArrow { 10483 if p.isFileConsideredESM { 10484 _, notes := p.whyESModule() 10485 p.log.AddErrorWithNotes(&p.tracker, js_lexer.RangeOfIdentifier(p.source, stmt.Loc), 10486 "Top-level return cannot be used inside an ECMAScript module", notes) 10487 } else { 10488 p.hasTopLevelReturn = true 10489 } 10490 } 10491 10492 if s.ValueOrNil.Data != nil { 10493 s.ValueOrNil = p.visitExpr(s.ValueOrNil) 10494 10495 // Returning undefined is implicit except when inside an async generator 10496 // function, where "return undefined" behaves like "return await undefined" 10497 // but just "return" has no "await". 10498 if p.options.minifySyntax && (!p.fnOrArrowDataVisit.isAsync || !p.fnOrArrowDataVisit.isGenerator) { 10499 if _, ok := s.ValueOrNil.Data.(*js_ast.EUndefined); ok { 10500 s.ValueOrNil = js_ast.Expr{} 10501 } 10502 } 10503 } 10504 10505 case *js_ast.SBlock: 10506 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 10507 10508 // Pass the "is loop body" status on to the direct children of a block used 10509 // as a loop body. This is used to enable optimizations specific to the 10510 // topmost scope in a loop body block. 10511 if p.loopBody == s { 10512 s.Stmts = p.visitStmts(s.Stmts, stmtsLoopBody) 10513 } else { 10514 s.Stmts = p.visitStmts(s.Stmts, stmtsNormal) 10515 } 10516 10517 p.popScope() 10518 10519 if p.options.minifySyntax { 10520 if len(s.Stmts) == 1 && !statementCaresAboutScope(s.Stmts[0]) { 10521 // Unwrap blocks containing a single statement 10522 stmt = s.Stmts[0] 10523 } else if len(s.Stmts) == 0 { 10524 // Trim empty blocks 10525 stmt = js_ast.Stmt{Loc: stmt.Loc, Data: js_ast.SEmptyShared} 10526 } 10527 } 10528 10529 case *js_ast.SWith: 10530 p.markStrictModeFeature(withStatement, js_lexer.RangeOfIdentifier(p.source, stmt.Loc), "") 10531 s.Value = p.visitExpr(s.Value) 10532 p.pushScopeForVisitPass(js_ast.ScopeWith, s.BodyLoc) 10533 s.Body = p.visitSingleStmt(s.Body, stmtsNormal) 10534 p.popScope() 10535 10536 case *js_ast.SWhile: 10537 s.Test = p.visitExpr(s.Test) 10538 s.Body = p.visitLoopBody(s.Body) 10539 10540 if p.options.minifySyntax { 10541 s.Test = p.astHelpers.SimplifyBooleanExpr(s.Test) 10542 10543 // A true value is implied 10544 testOrNil := s.Test 10545 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(s.Test.Data); ok && boolean && sideEffects == js_ast.NoSideEffects { 10546 testOrNil = js_ast.Expr{} 10547 } 10548 10549 // "while (a) {}" => "for (;a;) {}" 10550 forS := &js_ast.SFor{TestOrNil: testOrNil, Body: s.Body, IsSingleLineBody: s.IsSingleLineBody} 10551 mangleFor(forS) 10552 stmt = js_ast.Stmt{Loc: stmt.Loc, Data: forS} 10553 } 10554 10555 case *js_ast.SDoWhile: 10556 s.Body = p.visitLoopBody(s.Body) 10557 s.Test = p.visitExpr(s.Test) 10558 10559 if p.options.minifySyntax { 10560 s.Test = p.astHelpers.SimplifyBooleanExpr(s.Test) 10561 } 10562 10563 case *js_ast.SIf: 10564 s.Test = p.visitExpr(s.Test) 10565 10566 if p.options.minifySyntax { 10567 s.Test = p.astHelpers.SimplifyBooleanExpr(s.Test) 10568 } 10569 10570 // Fold constants 10571 boolean, _, ok := js_ast.ToBooleanWithSideEffects(s.Test.Data) 10572 10573 // Mark the control flow as dead if the branch is never taken 10574 if ok && !boolean { 10575 old := p.isControlFlowDead 10576 p.isControlFlowDead = true 10577 s.Yes = p.visitSingleStmt(s.Yes, stmtsNormal) 10578 p.isControlFlowDead = old 10579 } else { 10580 s.Yes = p.visitSingleStmt(s.Yes, stmtsNormal) 10581 } 10582 10583 // The "else" clause is optional 10584 if s.NoOrNil.Data != nil { 10585 // Mark the control flow as dead if the branch is never taken 10586 if ok && boolean { 10587 old := p.isControlFlowDead 10588 p.isControlFlowDead = true 10589 s.NoOrNil = p.visitSingleStmt(s.NoOrNil, stmtsNormal) 10590 p.isControlFlowDead = old 10591 } else { 10592 s.NoOrNil = p.visitSingleStmt(s.NoOrNil, stmtsNormal) 10593 } 10594 10595 // Trim unnecessary "else" clauses 10596 if p.options.minifySyntax { 10597 if _, ok := s.NoOrNil.Data.(*js_ast.SEmpty); ok { 10598 s.NoOrNil = js_ast.Stmt{} 10599 } 10600 } 10601 } 10602 10603 if p.options.minifySyntax { 10604 return p.mangleIf(stmts, stmt.Loc, s) 10605 } 10606 10607 case *js_ast.SFor: 10608 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 10609 if s.InitOrNil.Data != nil { 10610 p.visitForLoopInit(s.InitOrNil, false) 10611 } 10612 10613 if s.TestOrNil.Data != nil { 10614 s.TestOrNil = p.visitExpr(s.TestOrNil) 10615 10616 if p.options.minifySyntax { 10617 s.TestOrNil = p.astHelpers.SimplifyBooleanExpr(s.TestOrNil) 10618 10619 // A true value is implied 10620 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(s.TestOrNil.Data); ok && boolean && sideEffects == js_ast.NoSideEffects { 10621 s.TestOrNil = js_ast.Expr{} 10622 } 10623 } 10624 } 10625 10626 if s.UpdateOrNil.Data != nil { 10627 s.UpdateOrNil = p.visitExpr(s.UpdateOrNil) 10628 } 10629 s.Body = p.visitLoopBody(s.Body) 10630 10631 // Potentially relocate "var" declarations to the top level. Note that this 10632 // must be done inside the scope of the for loop or they won't be relocated. 10633 if s.InitOrNil.Data != nil { 10634 if init, ok := s.InitOrNil.Data.(*js_ast.SLocal); ok && init.Kind == js_ast.LocalVar { 10635 if assign, ok := p.maybeRelocateVarsToTopLevel(init.Decls, relocateVarsNormal); ok { 10636 if assign.Data != nil { 10637 s.InitOrNil = assign 10638 } else { 10639 s.InitOrNil = js_ast.Stmt{} 10640 } 10641 } 10642 } 10643 } 10644 10645 p.popScope() 10646 10647 if p.options.minifySyntax { 10648 mangleFor(s) 10649 } 10650 10651 case *js_ast.SForIn: 10652 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 10653 p.visitForLoopInit(s.Init, true) 10654 s.Value = p.visitExpr(s.Value) 10655 s.Body = p.visitLoopBody(s.Body) 10656 10657 // Check for a variable initializer 10658 if local, ok := s.Init.Data.(*js_ast.SLocal); ok && local.Kind == js_ast.LocalVar && len(local.Decls) == 1 { 10659 decl := &local.Decls[0] 10660 if id, ok := decl.Binding.Data.(*js_ast.BIdentifier); ok && decl.ValueOrNil.Data != nil { 10661 p.markStrictModeFeature(forInVarInit, p.source.RangeOfOperatorBefore(decl.ValueOrNil.Loc, "="), "") 10662 10663 // Lower for-in variable initializers in case the output is used in strict mode 10664 stmts = append(stmts, js_ast.Stmt{Loc: stmt.Loc, Data: &js_ast.SExpr{Value: js_ast.Assign( 10665 js_ast.Expr{Loc: decl.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}}, 10666 decl.ValueOrNil, 10667 )}}) 10668 decl.ValueOrNil = js_ast.Expr{} 10669 } 10670 } 10671 10672 // Potentially relocate "var" declarations to the top level. Note that this 10673 // must be done inside the scope of the for loop or they won't be relocated. 10674 if init, ok := s.Init.Data.(*js_ast.SLocal); ok && init.Kind == js_ast.LocalVar { 10675 if replacement, ok := p.maybeRelocateVarsToTopLevel(init.Decls, relocateVarsForInOrForOf); ok { 10676 s.Init = replacement 10677 } 10678 } 10679 10680 p.popScope() 10681 10682 p.lowerObjectRestInForLoopInit(s.Init, &s.Body) 10683 10684 case *js_ast.SForOf: 10685 // Silently remove unsupported top-level "await" in dead code branches 10686 if s.Await.Len > 0 && p.fnOrArrowDataVisit.isOutsideFnOrArrow { 10687 if p.isControlFlowDead && (p.options.unsupportedJSFeatures.Has(compat.TopLevelAwait) || !p.options.outputFormat.KeepESMImportExportSyntax()) { 10688 s.Await = logger.Range{} 10689 } else { 10690 p.liveTopLevelAwaitKeyword = s.Await 10691 p.markSyntaxFeature(compat.TopLevelAwait, s.Await) 10692 } 10693 } 10694 10695 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 10696 p.visitForLoopInit(s.Init, true) 10697 s.Value = p.visitExpr(s.Value) 10698 s.Body = p.visitLoopBody(s.Body) 10699 10700 // Potentially relocate "var" declarations to the top level. Note that this 10701 // must be done inside the scope of the for loop or they won't be relocated. 10702 if init, ok := s.Init.Data.(*js_ast.SLocal); ok && init.Kind == js_ast.LocalVar { 10703 if replacement, ok := p.maybeRelocateVarsToTopLevel(init.Decls, relocateVarsForInOrForOf); ok { 10704 s.Init = replacement 10705 } 10706 } 10707 10708 // Handle "for (using x of y)" and "for (await using x of y)" 10709 if local, ok := s.Init.Data.(*js_ast.SLocal); ok { 10710 if local.Kind == js_ast.LocalUsing && p.options.unsupportedJSFeatures.Has(compat.Using) { 10711 p.lowerUsingDeclarationInForOf(s.Init.Loc, local, &s.Body) 10712 } else if local.Kind == js_ast.LocalAwaitUsing { 10713 if p.fnOrArrowDataVisit.isOutsideFnOrArrow { 10714 if p.isControlFlowDead && (p.options.unsupportedJSFeatures.Has(compat.TopLevelAwait) || !p.options.outputFormat.KeepESMImportExportSyntax()) { 10715 // Silently remove unsupported top-level "await" in dead code branches 10716 local.Kind = js_ast.LocalUsing 10717 } else { 10718 p.liveTopLevelAwaitKeyword = logger.Range{Loc: s.Init.Loc, Len: 5} 10719 p.markSyntaxFeature(compat.TopLevelAwait, p.liveTopLevelAwaitKeyword) 10720 } 10721 if p.options.unsupportedJSFeatures.Has(compat.Using) { 10722 p.lowerUsingDeclarationInForOf(s.Init.Loc, local, &s.Body) 10723 } 10724 } else if p.options.unsupportedJSFeatures.Has(compat.Using) || p.options.unsupportedJSFeatures.Has(compat.AsyncAwait) || 10725 (p.options.unsupportedJSFeatures.Has(compat.AsyncGenerator) && p.fnOrArrowDataVisit.isGenerator) { 10726 p.lowerUsingDeclarationInForOf(s.Init.Loc, local, &s.Body) 10727 } 10728 } 10729 } 10730 10731 p.popScope() 10732 10733 p.lowerObjectRestInForLoopInit(s.Init, &s.Body) 10734 10735 // Lower "for await" if it's unsupported if it's in a lowered async generator 10736 if s.Await.Len > 0 && (p.options.unsupportedJSFeatures.Has(compat.ForAwait) || 10737 (p.options.unsupportedJSFeatures.Has(compat.AsyncGenerator) && p.fnOrArrowDataVisit.isGenerator)) { 10738 return p.lowerForAwaitLoop(stmt.Loc, s, stmts) 10739 } 10740 10741 case *js_ast.STry: 10742 p.pushScopeForVisitPass(js_ast.ScopeBlock, stmt.Loc) 10743 if p.fnOrArrowDataVisit.tryBodyCount == 0 { 10744 if s.Catch != nil { 10745 p.fnOrArrowDataVisit.tryCatchLoc = s.Catch.Loc 10746 } else { 10747 p.fnOrArrowDataVisit.tryCatchLoc = stmt.Loc 10748 } 10749 } 10750 p.fnOrArrowDataVisit.tryBodyCount++ 10751 s.Block.Stmts = p.visitStmts(s.Block.Stmts, stmtsNormal) 10752 p.fnOrArrowDataVisit.tryBodyCount-- 10753 p.popScope() 10754 10755 if s.Catch != nil { 10756 p.pushScopeForVisitPass(js_ast.ScopeCatchBinding, s.Catch.Loc) 10757 if s.Catch.BindingOrNil.Data != nil { 10758 p.visitBinding(s.Catch.BindingOrNil, bindingOpts{}) 10759 } 10760 10761 p.pushScopeForVisitPass(js_ast.ScopeBlock, s.Catch.BlockLoc) 10762 s.Catch.Block.Stmts = p.visitStmts(s.Catch.Block.Stmts, stmtsNormal) 10763 p.popScope() 10764 10765 p.lowerObjectRestInCatchBinding(s.Catch) 10766 p.popScope() 10767 } 10768 10769 if s.Finally != nil { 10770 p.pushScopeForVisitPass(js_ast.ScopeBlock, s.Finally.Loc) 10771 s.Finally.Block.Stmts = p.visitStmts(s.Finally.Block.Stmts, stmtsNormal) 10772 p.popScope() 10773 } 10774 10775 case *js_ast.SSwitch: 10776 s.Test = p.visitExpr(s.Test) 10777 p.pushScopeForVisitPass(js_ast.ScopeBlock, s.BodyLoc) 10778 oldIsInsideSwitch := p.fnOrArrowDataVisit.isInsideSwitch 10779 p.fnOrArrowDataVisit.isInsideSwitch = true 10780 for i, c := range s.Cases { 10781 if c.ValueOrNil.Data != nil { 10782 c.ValueOrNil = p.visitExpr(c.ValueOrNil) 10783 p.warnAboutEqualityCheck("case", c.ValueOrNil, c.ValueOrNil.Loc) 10784 p.warnAboutTypeofAndString(s.Test, c.ValueOrNil, onlyCheckOriginalOrder) 10785 } 10786 c.Body = p.visitStmts(c.Body, stmtsSwitch) 10787 10788 // Make sure the assignment to the body above is preserved 10789 s.Cases[i] = c 10790 } 10791 p.fnOrArrowDataVisit.isInsideSwitch = oldIsInsideSwitch 10792 p.popScope() 10793 10794 // Check for duplicate case values 10795 p.duplicateCaseChecker.reset() 10796 for _, c := range s.Cases { 10797 if c.ValueOrNil.Data != nil { 10798 p.duplicateCaseChecker.check(p, c.ValueOrNil) 10799 } 10800 } 10801 10802 // Unwrap switch statements in dead code 10803 if p.options.minifySyntax && p.isControlFlowDead { 10804 for _, c := range s.Cases { 10805 stmts = append(stmts, c.Body...) 10806 } 10807 return stmts 10808 } 10809 10810 // "using" declarations inside switch statements must be special-cased 10811 if lowered := p.maybeLowerUsingDeclarationsInSwitch(stmt.Loc, s); lowered != nil { 10812 return append(stmts, lowered...) 10813 } 10814 10815 case *js_ast.SFunction: 10816 p.visitFn(&s.Fn, s.Fn.OpenParenLoc, visitFnOpts{}) 10817 10818 // Strip this function declaration if it was overwritten 10819 if p.symbols[s.Fn.Name.Ref.InnerIndex].Flags.Has(ast.RemoveOverwrittenFunctionDeclaration) && !s.IsExport { 10820 return stmts 10821 } 10822 10823 if p.options.minifySyntax && !s.Fn.IsGenerator && !s.Fn.IsAsync && !s.Fn.HasRestArg && s.Fn.Name != nil { 10824 if len(s.Fn.Body.Block.Stmts) == 0 { 10825 // Mark if this function is an empty function 10826 hasSideEffectFreeArguments := true 10827 for _, arg := range s.Fn.Args { 10828 if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok { 10829 hasSideEffectFreeArguments = false 10830 break 10831 } 10832 } 10833 if hasSideEffectFreeArguments { 10834 p.symbols[s.Fn.Name.Ref.InnerIndex].Flags |= ast.IsEmptyFunction 10835 } 10836 } else if len(s.Fn.Args) == 1 && len(s.Fn.Body.Block.Stmts) == 1 { 10837 // Mark if this function is an identity function 10838 if arg := s.Fn.Args[0]; arg.DefaultOrNil.Data == nil { 10839 if id, ok := arg.Binding.Data.(*js_ast.BIdentifier); ok { 10840 if ret, ok := s.Fn.Body.Block.Stmts[0].Data.(*js_ast.SReturn); ok { 10841 if retID, ok := ret.ValueOrNil.Data.(*js_ast.EIdentifier); ok && id.Ref == retID.Ref { 10842 p.symbols[s.Fn.Name.Ref.InnerIndex].Flags |= ast.IsIdentityFunction 10843 } 10844 } 10845 } 10846 } 10847 } 10848 } 10849 10850 // Handle exporting this function from a namespace 10851 if s.IsExport && p.enclosingNamespaceArgRef != nil { 10852 s.IsExport = false 10853 stmts = append(stmts, stmt, js_ast.AssignStmt( 10854 js_ast.Expr{Loc: stmt.Loc, Data: p.dotOrMangledPropVisit( 10855 js_ast.Expr{Loc: stmt.Loc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}}, 10856 p.symbols[s.Fn.Name.Ref.InnerIndex].OriginalName, 10857 s.Fn.Name.Loc, 10858 )}, 10859 js_ast.Expr{Loc: s.Fn.Name.Loc, Data: &js_ast.EIdentifier{Ref: s.Fn.Name.Ref}}, 10860 )) 10861 } else { 10862 stmts = append(stmts, stmt) 10863 } 10864 10865 // Optionally preserve the name 10866 if p.options.keepNames { 10867 symbol := &p.symbols[s.Fn.Name.Ref.InnerIndex] 10868 symbol.Flags |= ast.DidKeepName 10869 fn := js_ast.Expr{Loc: s.Fn.Name.Loc, Data: &js_ast.EIdentifier{Ref: s.Fn.Name.Ref}} 10870 stmts = append(stmts, p.keepClassOrFnSymbolName(s.Fn.Name.Loc, fn, symbol.OriginalName)) 10871 } 10872 return stmts 10873 10874 case *js_ast.SClass: 10875 result := p.visitClass(stmt.Loc, &s.Class, ast.InvalidRef, "") 10876 10877 // Remove the export flag inside a namespace 10878 var nameToExport string 10879 wasExportInsideNamespace := s.IsExport && p.enclosingNamespaceArgRef != nil 10880 if wasExportInsideNamespace { 10881 nameToExport = p.symbols[s.Class.Name.Ref.InnerIndex].OriginalName 10882 s.IsExport = false 10883 } 10884 10885 // Lower class field syntax for browsers that don't support it 10886 classStmts, _ := p.lowerClass(stmt, js_ast.Expr{}, result, "") 10887 10888 // Remember if the class was side-effect free before lowering 10889 if result.canBeRemovedIfUnused { 10890 for _, classStmt := range classStmts { 10891 if s2, ok := classStmt.Data.(*js_ast.SExpr); ok { 10892 s2.IsFromClassOrFnThatCanBeRemovedIfUnused = true 10893 } 10894 } 10895 } 10896 10897 stmts = append(stmts, classStmts...) 10898 10899 // Handle exporting this class from a namespace 10900 if wasExportInsideNamespace { 10901 stmts = append(stmts, js_ast.AssignStmt( 10902 js_ast.Expr{Loc: stmt.Loc, Data: p.dotOrMangledPropVisit( 10903 js_ast.Expr{Loc: stmt.Loc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}}, 10904 nameToExport, 10905 s.Class.Name.Loc, 10906 )}, 10907 js_ast.Expr{Loc: s.Class.Name.Loc, Data: &js_ast.EIdentifier{Ref: s.Class.Name.Ref}}, 10908 )) 10909 } 10910 10911 return stmts 10912 10913 case *js_ast.SEnum: 10914 // Do not end the const local prefix after TypeScript enums. We process 10915 // them first within their scope so that they are inlined into all code in 10916 // that scope. We don't want that to cause the const local prefix to end. 10917 p.currentScope.IsAfterConstLocalPrefix = wasAfterAfterConstLocalPrefix 10918 10919 // Track cross-module enum constants during bundling 10920 var tsTopLevelEnumValues map[string]js_ast.TSEnumValue 10921 if p.currentScope == p.moduleScope && p.options.mode == config.ModeBundle { 10922 tsTopLevelEnumValues = make(map[string]js_ast.TSEnumValue) 10923 } 10924 10925 p.recordDeclaredSymbol(s.Name.Ref) 10926 p.pushScopeForVisitPass(js_ast.ScopeEntry, stmt.Loc) 10927 p.recordDeclaredSymbol(s.Arg) 10928 10929 // Scan ahead for any variables inside this namespace. This must be done 10930 // ahead of time before visiting any statements inside the namespace 10931 // because we may end up visiting the uses before the declarations. 10932 // We need to convert the uses into property accesses on the namespace. 10933 for _, value := range s.Values { 10934 if value.Ref != ast.InvalidRef { 10935 p.isExportedInsideNamespace[value.Ref] = s.Arg 10936 } 10937 } 10938 10939 // Values without initializers are initialized to one more than the 10940 // previous value if the previous value is numeric. Otherwise values 10941 // without initializers are initialized to undefined. 10942 nextNumericValue := float64(0) 10943 hasNumericValue := true 10944 valueExprs := []js_ast.Expr{} 10945 allValuesArePure := true 10946 10947 // Update the exported members of this enum as we constant fold each one 10948 exportedMembers := p.currentScope.TSNamespace.ExportedMembers 10949 10950 // We normally don't fold numeric constants because they might increase code 10951 // size, but it's important to fold numeric constants inside enums since 10952 // that's what the TypeScript compiler does. 10953 oldShouldFoldTypeScriptConstantExpressions := p.shouldFoldTypeScriptConstantExpressions 10954 p.shouldFoldTypeScriptConstantExpressions = true 10955 10956 // Create an assignment for each enum value 10957 for _, value := range s.Values { 10958 name := helpers.UTF16ToString(value.Name) 10959 var assignTarget js_ast.Expr 10960 hasStringValue := false 10961 10962 if value.ValueOrNil.Data != nil { 10963 value.ValueOrNil = p.visitExpr(value.ValueOrNil) 10964 hasNumericValue = false 10965 10966 // "See through" any wrapped comments 10967 underlyingValue := value.ValueOrNil 10968 if inlined, ok := value.ValueOrNil.Data.(*js_ast.EInlinedEnum); ok { 10969 underlyingValue = inlined.Value 10970 } 10971 10972 switch e := underlyingValue.Data.(type) { 10973 case *js_ast.ENumber: 10974 if tsTopLevelEnumValues != nil { 10975 tsTopLevelEnumValues[name] = js_ast.TSEnumValue{Number: e.Value} 10976 } 10977 member := exportedMembers[name] 10978 member.Data = &js_ast.TSNamespaceMemberEnumNumber{Value: e.Value} 10979 exportedMembers[name] = member 10980 p.refToTSNamespaceMemberData[value.Ref] = member.Data 10981 hasNumericValue = true 10982 nextNumericValue = e.Value + 1 10983 10984 case *js_ast.EString: 10985 if tsTopLevelEnumValues != nil { 10986 tsTopLevelEnumValues[name] = js_ast.TSEnumValue{String: e.Value} 10987 } 10988 member := exportedMembers[name] 10989 member.Data = &js_ast.TSNamespaceMemberEnumString{Value: e.Value} 10990 exportedMembers[name] = member 10991 p.refToTSNamespaceMemberData[value.Ref] = member.Data 10992 hasStringValue = true 10993 10994 default: 10995 if js_ast.KnownPrimitiveType(underlyingValue.Data) == js_ast.PrimitiveString { 10996 hasStringValue = true 10997 } 10998 if !p.astHelpers.ExprCanBeRemovedIfUnused(underlyingValue) { 10999 allValuesArePure = false 11000 } 11001 } 11002 } else if hasNumericValue { 11003 if tsTopLevelEnumValues != nil { 11004 tsTopLevelEnumValues[name] = js_ast.TSEnumValue{Number: nextNumericValue} 11005 } 11006 member := exportedMembers[name] 11007 member.Data = &js_ast.TSNamespaceMemberEnumNumber{Value: nextNumericValue} 11008 exportedMembers[name] = member 11009 p.refToTSNamespaceMemberData[value.Ref] = member.Data 11010 value.ValueOrNil = js_ast.Expr{Loc: value.Loc, Data: &js_ast.ENumber{Value: nextNumericValue}} 11011 nextNumericValue++ 11012 } else { 11013 value.ValueOrNil = js_ast.Expr{Loc: value.Loc, Data: js_ast.EUndefinedShared} 11014 } 11015 11016 if p.options.minifySyntax && js_ast.IsIdentifier(name) { 11017 // "Enum.Name = value" 11018 assignTarget = js_ast.Assign( 11019 js_ast.Expr{Loc: value.Loc, Data: &js_ast.EDot{ 11020 Target: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EIdentifier{Ref: s.Arg}}, 11021 Name: name, 11022 NameLoc: value.Loc, 11023 }}, 11024 value.ValueOrNil, 11025 ) 11026 } else { 11027 // "Enum['Name'] = value" 11028 assignTarget = js_ast.Assign( 11029 js_ast.Expr{Loc: value.Loc, Data: &js_ast.EIndex{ 11030 Target: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EIdentifier{Ref: s.Arg}}, 11031 Index: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EString{Value: value.Name}}, 11032 }}, 11033 value.ValueOrNil, 11034 ) 11035 } 11036 p.recordUsage(s.Arg) 11037 11038 // String-valued enums do not form a two-way map 11039 if hasStringValue { 11040 valueExprs = append(valueExprs, assignTarget) 11041 } else { 11042 // "Enum[assignTarget] = 'Name'" 11043 valueExprs = append(valueExprs, js_ast.Assign( 11044 js_ast.Expr{Loc: value.Loc, Data: &js_ast.EIndex{ 11045 Target: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EIdentifier{Ref: s.Arg}}, 11046 Index: assignTarget, 11047 }}, 11048 js_ast.Expr{Loc: value.Loc, Data: &js_ast.EString{Value: value.Name}}, 11049 )) 11050 p.recordUsage(s.Arg) 11051 } 11052 } 11053 11054 p.popScope() 11055 p.shouldFoldTypeScriptConstantExpressions = oldShouldFoldTypeScriptConstantExpressions 11056 11057 // Track all exported top-level enums for cross-module inlining 11058 if tsTopLevelEnumValues != nil { 11059 if p.tsEnums == nil { 11060 p.tsEnums = make(map[ast.Ref]map[string]js_ast.TSEnumValue) 11061 } 11062 p.tsEnums[s.Name.Ref] = tsTopLevelEnumValues 11063 } 11064 11065 // Wrap this enum definition in a closure 11066 stmts = p.generateClosureForTypeScriptEnum( 11067 stmts, stmt.Loc, s.IsExport, s.Name.Loc, s.Name.Ref, s.Arg, valueExprs, allValuesArePure) 11068 return stmts 11069 11070 case *js_ast.SNamespace: 11071 p.recordDeclaredSymbol(s.Name.Ref) 11072 11073 // Scan ahead for any variables inside this namespace. This must be done 11074 // ahead of time before visiting any statements inside the namespace 11075 // because we may end up visiting the uses before the declarations. 11076 // We need to convert the uses into property accesses on the namespace. 11077 for _, childStmt := range s.Stmts { 11078 if local, ok := childStmt.Data.(*js_ast.SLocal); ok { 11079 if local.IsExport { 11080 js_ast.ForEachIdentifierBindingInDecls(local.Decls, func(loc logger.Loc, b *js_ast.BIdentifier) { 11081 p.isExportedInsideNamespace[b.Ref] = s.Arg 11082 }) 11083 } 11084 } 11085 } 11086 11087 oldEnclosingNamespaceArgRef := p.enclosingNamespaceArgRef 11088 p.enclosingNamespaceArgRef = &s.Arg 11089 p.pushScopeForVisitPass(js_ast.ScopeEntry, stmt.Loc) 11090 p.recordDeclaredSymbol(s.Arg) 11091 stmtsInsideNamespace := p.visitStmtsAndPrependTempRefs(s.Stmts, prependTempRefsOpts{kind: stmtsFnBody}) 11092 p.popScope() 11093 p.enclosingNamespaceArgRef = oldEnclosingNamespaceArgRef 11094 11095 // Generate a closure for this namespace 11096 stmts = p.generateClosureForTypeScriptNamespaceOrEnum( 11097 stmts, stmt.Loc, s.IsExport, s.Name.Loc, s.Name.Ref, s.Arg, stmtsInsideNamespace) 11098 return stmts 11099 11100 default: 11101 panic("Internal error") 11102 } 11103 11104 stmts = append(stmts, stmt) 11105 return stmts 11106 } 11107 11108 func isUnsightlyPrimitive(data js_ast.E) bool { 11109 switch data.(type) { 11110 case *js_ast.EBoolean, *js_ast.ENull, *js_ast.EUndefined, *js_ast.ENumber, *js_ast.EBigInt, *js_ast.EString: 11111 return true 11112 } 11113 return false 11114 } 11115 11116 // If we encounter a variable initializer that could possibly trigger access to 11117 // a constant declared later on, then we need to end the const local prefix. 11118 // We want to avoid situations like this: 11119 // 11120 // const x = y; // This is supposed to throw due to TDZ 11121 // const y = 1; 11122 // 11123 // or this: 11124 // 11125 // const x = 1; 11126 // const y = foo(); // This is supposed to throw due to TDZ 11127 // const z = 2; 11128 // const foo = () => z; 11129 // 11130 // But a situation like this is ok: 11131 // 11132 // const x = 1; 11133 // const y = [() => x + z]; 11134 // const z = 2; 11135 func isSafeForConstLocalPrefix(expr js_ast.Expr) bool { 11136 switch e := expr.Data.(type) { 11137 case *js_ast.EMissing, *js_ast.EString, *js_ast.ERegExp, *js_ast.EBigInt, *js_ast.EFunction, *js_ast.EArrow: 11138 return true 11139 11140 case *js_ast.EArray: 11141 for _, item := range e.Items { 11142 if !isSafeForConstLocalPrefix(item) { 11143 return false 11144 } 11145 } 11146 return true 11147 11148 case *js_ast.EObject: 11149 // For now just allow "{}" and forbid everything else 11150 return len(e.Properties) == 0 11151 } 11152 11153 return false 11154 } 11155 11156 type relocateVarsMode uint8 11157 11158 const ( 11159 relocateVarsNormal relocateVarsMode = iota 11160 relocateVarsForInOrForOf 11161 ) 11162 11163 // If we are currently in a hoisted child of the module scope, relocate these 11164 // declarations to the top level and return an equivalent assignment statement. 11165 // Make sure to check that the declaration kind is "var" before calling this. 11166 // And make sure to check that the returned statement is not the zero value. 11167 // 11168 // This is done to make it easier to traverse top-level declarations in the linker 11169 // during bundling. Now it is sufficient to just scan the top-level statements 11170 // instead of having to traverse recursively into the statement tree. 11171 func (p *parser) maybeRelocateVarsToTopLevel(decls []js_ast.Decl, mode relocateVarsMode) (js_ast.Stmt, bool) { 11172 // Only do this when bundling, and not when the scope is already top-level 11173 if p.options.mode != config.ModeBundle || p.currentScope == p.moduleScope { 11174 return js_ast.Stmt{}, false 11175 } 11176 11177 // Only do this if we're not inside a function 11178 scope := p.currentScope 11179 for !scope.Kind.StopsHoisting() { 11180 scope = scope.Parent 11181 } 11182 if scope != p.moduleScope { 11183 return js_ast.Stmt{}, false 11184 } 11185 11186 // Convert the declarations to assignments 11187 wrapIdentifier := func(loc logger.Loc, ref ast.Ref) js_ast.Expr { 11188 p.relocatedTopLevelVars = append(p.relocatedTopLevelVars, ast.LocRef{Loc: loc, Ref: ref}) 11189 p.recordUsage(ref) 11190 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}} 11191 } 11192 var value js_ast.Expr 11193 for _, decl := range decls { 11194 binding := js_ast.ConvertBindingToExpr(decl.Binding, wrapIdentifier) 11195 if decl.ValueOrNil.Data != nil { 11196 value = js_ast.JoinWithComma(value, js_ast.Assign(binding, decl.ValueOrNil)) 11197 } else if mode == relocateVarsForInOrForOf { 11198 value = js_ast.JoinWithComma(value, binding) 11199 } 11200 } 11201 if value.Data == nil { 11202 // If none of the variables had any initializers, just remove the declarations 11203 return js_ast.Stmt{}, true 11204 } 11205 return js_ast.Stmt{Loc: value.Loc, Data: &js_ast.SExpr{Value: value}}, true 11206 } 11207 11208 func (p *parser) markExprAsParenthesized(value js_ast.Expr, openParenLoc logger.Loc, isAsync bool) { 11209 // Don't lose comments due to parentheses. For example, we don't want to lose 11210 // the comment here: 11211 // 11212 // ( /* comment */ (foo) ); 11213 // 11214 if !isAsync { 11215 if comments, ok := p.exprComments[openParenLoc]; ok { 11216 delete(p.exprComments, openParenLoc) 11217 p.exprComments[value.Loc] = append(comments, p.exprComments[value.Loc]...) 11218 } 11219 } 11220 11221 switch e := value.Data.(type) { 11222 case *js_ast.EArray: 11223 e.IsParenthesized = true 11224 case *js_ast.EObject: 11225 e.IsParenthesized = true 11226 } 11227 } 11228 11229 func (p *parser) maybeTransposeIfExprChain(expr js_ast.Expr, visit func(js_ast.Expr) js_ast.Expr) js_ast.Expr { 11230 if e, ok := expr.Data.(*js_ast.EIf); ok { 11231 e.Yes = p.maybeTransposeIfExprChain(e.Yes, visit) 11232 e.No = p.maybeTransposeIfExprChain(e.No, visit) 11233 return expr 11234 } 11235 return visit(expr) 11236 } 11237 11238 func (p *parser) iifeCanBeRemovedIfUnused(args []js_ast.Arg, body js_ast.FnBody) bool { 11239 for _, arg := range args { 11240 if arg.DefaultOrNil.Data != nil && !p.astHelpers.ExprCanBeRemovedIfUnused(arg.DefaultOrNil) { 11241 // The default value has a side effect 11242 return false 11243 } 11244 11245 if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok { 11246 // Destructuring is a side effect (due to property access) 11247 return false 11248 } 11249 } 11250 11251 // Check whether any statements have side effects or not. Consider return 11252 // statements as not having side effects because if the IIFE can be removed 11253 // then we know the return value is unused, so we know that returning the 11254 // value has no side effects. 11255 return p.astHelpers.StmtsCanBeRemovedIfUnused(body.Block.Stmts, js_ast.ReturnCanBeRemovedIfUnused) 11256 } 11257 11258 type captureValueMode uint8 11259 11260 const ( 11261 valueDefinitelyNotMutated captureValueMode = iota 11262 valueCouldBeMutated 11263 ) 11264 11265 // This is a helper function to use when you need to capture a value that may 11266 // have side effects so you can use it multiple times. It guarantees that the 11267 // side effects take place exactly once. 11268 // 11269 // Example usage: 11270 // 11271 // // "value" => "value + value" 11272 // // "value()" => "(_a = value(), _a + _a)" 11273 // valueFunc, wrapFunc := p.captureValueWithPossibleSideEffects(loc, 2, value) 11274 // return wrapFunc(js_ast.Expr{Loc: loc, Data: &js_ast.EBinary{ 11275 // Op: js_ast.BinOpAdd, 11276 // Left: valueFunc(), 11277 // Right: valueFunc(), 11278 // }}) 11279 // 11280 // This returns a function for generating references instead of a raw reference 11281 // because AST nodes are supposed to be unique in memory, not aliases of other 11282 // AST nodes. That way you can mutate one during lowering without having to 11283 // worry about messing up other nodes. 11284 func (p *parser) captureValueWithPossibleSideEffects( 11285 loc logger.Loc, // The location to use for the generated references 11286 count int, // The expected number of references to generate 11287 value js_ast.Expr, // The value that might have side effects 11288 mode captureValueMode, // Say if "value" might be mutated and must be captured 11289 ) ( 11290 func() js_ast.Expr, // Generates reference expressions "_a" 11291 func(js_ast.Expr) js_ast.Expr, // Call this on the final expression 11292 ) { 11293 wrapFunc := func(expr js_ast.Expr) js_ast.Expr { 11294 // Make sure side effects still happen if no expression was generated 11295 if expr.Data == nil { 11296 return value 11297 } 11298 return expr 11299 } 11300 11301 // Referencing certain expressions more than once has no side effects, so we 11302 // can just create them inline without capturing them in a temporary variable 11303 var valueFunc func() js_ast.Expr 11304 switch e := value.Data.(type) { 11305 case *js_ast.ENull: 11306 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: js_ast.ENullShared} } 11307 case *js_ast.EUndefined: 11308 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared} } 11309 case *js_ast.EThis: 11310 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} } 11311 case *js_ast.EBoolean: 11312 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: &js_ast.EBoolean{Value: e.Value}} } 11313 case *js_ast.ENumber: 11314 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: e.Value}} } 11315 case *js_ast.EBigInt: 11316 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: &js_ast.EBigInt{Value: e.Value}} } 11317 case *js_ast.EString: 11318 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: e.Value}} } 11319 case *js_ast.EPrivateIdentifier: 11320 valueFunc = func() js_ast.Expr { return js_ast.Expr{Loc: loc, Data: &js_ast.EPrivateIdentifier{Ref: e.Ref}} } 11321 case *js_ast.EIdentifier: 11322 if mode == valueDefinitelyNotMutated { 11323 valueFunc = func() js_ast.Expr { 11324 // Make sure we record this usage in the usage count so that duplicating 11325 // a single-use reference means it's no longer considered a single-use 11326 // reference. Otherwise the single-use reference inlining code may 11327 // incorrectly inline the initializer into the first reference, leaving 11328 // the second reference without a definition. 11329 p.recordUsage(e.Ref) 11330 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: e.Ref}} 11331 } 11332 } 11333 } 11334 if valueFunc != nil { 11335 return valueFunc, wrapFunc 11336 } 11337 11338 // We don't need to worry about side effects if the value won't be used 11339 // multiple times. This special case lets us avoid generating a temporary 11340 // reference. 11341 if count < 2 { 11342 return func() js_ast.Expr { 11343 return value 11344 }, wrapFunc 11345 } 11346 11347 // Otherwise, fall back to generating a temporary reference 11348 tempRef := ast.InvalidRef 11349 11350 // If we're in a function argument scope, then we won't be able to generate 11351 // symbols in this scope to store stuff, since there's nowhere to put the 11352 // variable declaration. We don't want to put the variable declaration 11353 // outside the function since some code in the argument list may cause the 11354 // function to be reentrant, and we can't put the variable declaration in 11355 // the function body since that's not accessible by the argument list. 11356 // 11357 // Instead, we use an immediately-invoked arrow function to create a new 11358 // symbol inline by introducing a new scope. Make sure to only use it for 11359 // symbol declaration and still initialize the variable inline to preserve 11360 // side effect order. 11361 if p.currentScope.Kind == js_ast.ScopeFunctionArgs { 11362 return func() js_ast.Expr { 11363 if tempRef == ast.InvalidRef { 11364 tempRef = p.generateTempRef(tempRefNoDeclare, "") 11365 11366 // Assign inline so the order of side effects remains the same 11367 p.recordUsage(tempRef) 11368 return js_ast.Assign(js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: tempRef}}, value) 11369 } 11370 p.recordUsage(tempRef) 11371 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: tempRef}} 11372 }, func(expr js_ast.Expr) js_ast.Expr { 11373 // Make sure side effects still happen if no expression was generated 11374 if expr.Data == nil { 11375 return value 11376 } 11377 11378 // Generate a new variable using an arrow function to avoid messing with "this" 11379 return js_ast.Expr{Loc: loc, Data: &js_ast.ECall{ 11380 Target: js_ast.Expr{Loc: loc, Data: &js_ast.EArrow{ 11381 Args: []js_ast.Arg{{Binding: js_ast.Binding{Loc: loc, Data: &js_ast.BIdentifier{Ref: tempRef}}}}, 11382 PreferExpr: true, 11383 Body: js_ast.FnBody{Loc: loc, Block: js_ast.SBlock{Stmts: []js_ast.Stmt{{Loc: loc, Data: &js_ast.SReturn{ValueOrNil: expr}}}}}, 11384 }}, 11385 }} 11386 } 11387 } 11388 11389 return func() js_ast.Expr { 11390 if tempRef == ast.InvalidRef { 11391 tempRef = p.generateTempRef(tempRefNeedsDeclare, "") 11392 p.recordUsage(tempRef) 11393 return js_ast.Assign(js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: tempRef}}, value) 11394 } 11395 p.recordUsage(tempRef) 11396 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: tempRef}} 11397 }, wrapFunc 11398 } 11399 11400 func (p *parser) visitDecorators(decorators []js_ast.Decorator, decoratorScope *js_ast.Scope) []js_ast.Decorator { 11401 if decorators != nil { 11402 // Decorators cause us to temporarily revert to the scope that encloses the 11403 // class declaration, since that's where the generated code for decorators 11404 // will be inserted. I believe this currently only matters for parameter 11405 // decorators, where the scope should not be within the argument list. 11406 oldScope := p.currentScope 11407 p.currentScope = decoratorScope 11408 11409 for i, decorator := range decorators { 11410 decorators[i].Value = p.visitExpr(decorator.Value) 11411 } 11412 11413 // Avoid "popScope" because this decorator scope is not hierarchical 11414 p.currentScope = oldScope 11415 } 11416 11417 return decorators 11418 } 11419 11420 type visitClassResult struct { 11421 bodyScope *js_ast.Scope 11422 innerClassNameRef ast.Ref 11423 superCtorRef ast.Ref 11424 11425 // If true, the class was determined to be safe to remove if the class is 11426 // never used (i.e. the class definition is side-effect free). This is 11427 // determined after visiting but before lowering since lowering may generate 11428 // class mutations that cannot be automatically analyzed as side-effect free. 11429 canBeRemovedIfUnused bool 11430 } 11431 11432 func (p *parser) visitClass(nameScopeLoc logger.Loc, class *js_ast.Class, defaultNameRef ast.Ref, nameToKeep string) (result visitClassResult) { 11433 class.Decorators = p.visitDecorators(class.Decorators, p.currentScope) 11434 11435 if class.Name != nil { 11436 p.recordDeclaredSymbol(class.Name.Ref) 11437 if p.options.keepNames { 11438 nameToKeep = p.symbols[class.Name.Ref.InnerIndex].OriginalName 11439 } 11440 } 11441 11442 // Replace "this" with a reference to the class inside static field 11443 // initializers if static fields are being lowered, since that relocates the 11444 // field initializers outside of the class body and "this" will no longer 11445 // reference the same thing. 11446 classLoweringInfo := p.computeClassLoweringInfo(class) 11447 recomputeClassLoweringInfo := false 11448 11449 // Sometimes we need to lower private members even though they are supported. 11450 // This flags them for lowering so that we lower references to them as we 11451 // traverse the class body. 11452 // 11453 // We don't need to worry about possible references to the class shadowing 11454 // symbol inside the class body changing our decision to lower private members 11455 // later on because that shouldn't be possible. 11456 if classLoweringInfo.lowerAllStaticFields { 11457 for _, prop := range class.Properties { 11458 // We need to lower all private members if fields of that type are lowered, 11459 // not just private fields (methods and accessors too): 11460 // 11461 // class Foo { 11462 // get #foo() {} 11463 // static bar = new Foo().#foo 11464 // } 11465 // 11466 // We can't transform that to this: 11467 // 11468 // class Foo { 11469 // get #foo() {} 11470 // } 11471 // Foo.bar = new Foo().#foo; 11472 // 11473 // The private getter must be lowered too. 11474 if private, ok := prop.Key.Data.(*js_ast.EPrivateIdentifier); ok { 11475 p.symbols[private.Ref.InnerIndex].Flags |= ast.PrivateSymbolMustBeLowered 11476 recomputeClassLoweringInfo = true 11477 } 11478 } 11479 } 11480 11481 // Conservatively lower all private names that have been used in a private 11482 // brand check anywhere in the file. See the comment on this map for details. 11483 if p.lowerAllOfThesePrivateNames != nil { 11484 for _, prop := range class.Properties { 11485 if private, ok := prop.Key.Data.(*js_ast.EPrivateIdentifier); ok { 11486 if symbol := &p.symbols[private.Ref.InnerIndex]; p.lowerAllOfThesePrivateNames[symbol.OriginalName] { 11487 symbol.Flags |= ast.PrivateSymbolMustBeLowered 11488 recomputeClassLoweringInfo = true 11489 } 11490 } 11491 } 11492 } 11493 11494 // If we changed private symbol lowering decisions, then recompute class 11495 // lowering info because that may have changed other decisions too 11496 if recomputeClassLoweringInfo { 11497 classLoweringInfo = p.computeClassLoweringInfo(class) 11498 } 11499 11500 p.pushScopeForVisitPass(js_ast.ScopeClassName, nameScopeLoc) 11501 oldEnclosingClassKeyword := p.enclosingClassKeyword 11502 p.enclosingClassKeyword = class.ClassKeyword 11503 p.currentScope.RecursiveSetStrictMode(js_ast.ImplicitStrictModeClass) 11504 if class.Name != nil { 11505 p.validateDeclaredSymbolName(class.Name.Loc, p.symbols[class.Name.Ref.InnerIndex].OriginalName) 11506 } 11507 11508 // Create the "__super" symbol if necessary. This will cause us to replace 11509 // all "super()" call expressions with a call to this symbol, which will 11510 // then be inserted into the "constructor" method. 11511 result.superCtorRef = ast.InvalidRef 11512 if classLoweringInfo.shimSuperCtorCalls { 11513 result.superCtorRef = p.newSymbol(ast.SymbolOther, "__super") 11514 p.currentScope.Generated = append(p.currentScope.Generated, result.superCtorRef) 11515 p.recordDeclaredSymbol(result.superCtorRef) 11516 } 11517 oldSuperCtorRef := p.superCtorRef 11518 p.superCtorRef = result.superCtorRef 11519 11520 // Insert an immutable inner name that spans the whole class to match 11521 // JavaScript's semantics specifically the "CreateImmutableBinding" here: 11522 // https://262.ecma-international.org/6.0/#sec-runtime-semantics-classdefinitionevaluation 11523 // The class body (and extends clause) "captures" the original value of the 11524 // class name. This matters for class statements because the symbol can be 11525 // re-assigned to something else later. The captured values must be the 11526 // original value of the name, not the re-assigned value. Use "const" for 11527 // this symbol to match JavaScript run-time semantics. You are not allowed 11528 // to assign to this symbol (it throws a TypeError). 11529 if class.Name != nil { 11530 name := p.symbols[class.Name.Ref.InnerIndex].OriginalName 11531 result.innerClassNameRef = p.newSymbol(ast.SymbolConst, "_"+name) 11532 p.currentScope.Members[name] = js_ast.ScopeMember{Loc: class.Name.Loc, Ref: result.innerClassNameRef} 11533 } else { 11534 name := "_this" 11535 if defaultNameRef != ast.InvalidRef { 11536 name = "_" + p.source.IdentifierName + "_default" 11537 } 11538 result.innerClassNameRef = p.newSymbol(ast.SymbolConst, name) 11539 } 11540 p.recordDeclaredSymbol(result.innerClassNameRef) 11541 11542 if class.ExtendsOrNil.Data != nil { 11543 class.ExtendsOrNil = p.visitExpr(class.ExtendsOrNil) 11544 } 11545 11546 // A scope is needed for private identifiers 11547 p.pushScopeForVisitPass(js_ast.ScopeClassBody, class.BodyLoc) 11548 result.bodyScope = p.currentScope 11549 11550 for i := range class.Properties { 11551 property := &class.Properties[i] 11552 11553 if property.Kind == js_ast.PropertyClassStaticBlock { 11554 oldFnOrArrowData := p.fnOrArrowDataVisit 11555 oldFnOnlyDataVisit := p.fnOnlyDataVisit 11556 11557 p.fnOrArrowDataVisit = fnOrArrowDataVisit{} 11558 p.fnOnlyDataVisit = fnOnlyDataVisit{ 11559 isThisNested: true, 11560 isNewTargetAllowed: true, 11561 isInStaticClassContext: true, 11562 innerClassNameRef: &result.innerClassNameRef, 11563 } 11564 11565 if classLoweringInfo.lowerAllStaticFields { 11566 // Need to lower "this" and "super" since they won't be valid outside the class body 11567 p.fnOnlyDataVisit.shouldReplaceThisWithInnerClassNameRef = true 11568 p.fnOrArrowDataVisit.shouldLowerSuperPropertyAccess = true 11569 } 11570 11571 p.pushScopeForVisitPass(js_ast.ScopeClassStaticInit, property.ClassStaticBlock.Loc) 11572 11573 // Make it an error to use "arguments" in a static class block 11574 p.currentScope.ForbidArguments = true 11575 11576 property.ClassStaticBlock.Block.Stmts = p.visitStmts(property.ClassStaticBlock.Block.Stmts, stmtsFnBody) 11577 p.popScope() 11578 11579 p.fnOrArrowDataVisit = oldFnOrArrowData 11580 p.fnOnlyDataVisit = oldFnOnlyDataVisit 11581 continue 11582 } 11583 11584 property.Decorators = p.visitDecorators(property.Decorators, result.bodyScope) 11585 11586 // Visit the property key 11587 if private, ok := property.Key.Data.(*js_ast.EPrivateIdentifier); ok { 11588 // Special-case private identifiers here 11589 p.recordDeclaredSymbol(private.Ref) 11590 } else { 11591 // It's forbidden to reference the class name in a computed key 11592 if property.Flags.Has(js_ast.PropertyIsComputed) && class.Name != nil { 11593 p.symbols[result.innerClassNameRef.InnerIndex].Kind = ast.SymbolClassInComputedPropertyKey 11594 } 11595 11596 key, _ := p.visitExprInOut(property.Key, exprIn{ 11597 shouldMangleStringsAsProps: true, 11598 }) 11599 property.Key = key 11600 11601 // Re-allow using the class name after visiting a computed key 11602 if property.Flags.Has(js_ast.PropertyIsComputed) && class.Name != nil { 11603 p.symbols[result.innerClassNameRef.InnerIndex].Kind = ast.SymbolConst 11604 } 11605 11606 if p.options.minifySyntax { 11607 if inlined, ok := key.Data.(*js_ast.EInlinedEnum); ok { 11608 switch inlined.Value.Data.(type) { 11609 case *js_ast.EString, *js_ast.ENumber: 11610 key.Data = inlined.Value.Data 11611 property.Key.Data = key.Data 11612 } 11613 } 11614 switch k := key.Data.(type) { 11615 case *js_ast.ENumber, *js_ast.ENameOfSymbol: 11616 // "class { [123] }" => "class { 123 }" 11617 property.Flags &= ^js_ast.PropertyIsComputed 11618 case *js_ast.EString: 11619 if numberValue, ok := js_ast.StringToEquivalentNumberValue(k.Value); ok && numberValue >= 0 { 11620 // "class { '123' }" => "class { 123 }" 11621 property.Key.Data = &js_ast.ENumber{Value: numberValue} 11622 property.Flags &= ^js_ast.PropertyIsComputed 11623 } else if property.Flags.Has(js_ast.PropertyIsComputed) { 11624 // "class {['x'] = y}" => "class {'x' = y}" 11625 isInvalidConstructor := false 11626 if helpers.UTF16EqualsString(k.Value, "constructor") { 11627 if !property.Kind.IsMethodDefinition() { 11628 // "constructor" is an invalid name for both instance and static fields 11629 isInvalidConstructor = true 11630 } else if !property.Flags.Has(js_ast.PropertyIsStatic) { 11631 // Calling an instance method "constructor" is problematic so avoid that too 11632 isInvalidConstructor = true 11633 } 11634 } 11635 11636 // A static property must not be called "prototype" 11637 isInvalidPrototype := property.Flags.Has(js_ast.PropertyIsStatic) && helpers.UTF16EqualsString(k.Value, "prototype") 11638 11639 if !isInvalidConstructor && !isInvalidPrototype { 11640 property.Flags &= ^js_ast.PropertyIsComputed 11641 } 11642 } 11643 } 11644 } 11645 } 11646 11647 // Make it an error to use "arguments" in a class body 11648 p.currentScope.ForbidArguments = true 11649 11650 // The value of "this" and "super" is shadowed inside property values 11651 oldFnOnlyDataVisit := p.fnOnlyDataVisit 11652 oldShouldLowerSuperPropertyAccess := p.fnOrArrowDataVisit.shouldLowerSuperPropertyAccess 11653 p.fnOrArrowDataVisit.shouldLowerSuperPropertyAccess = false 11654 p.fnOnlyDataVisit.shouldReplaceThisWithInnerClassNameRef = false 11655 p.fnOnlyDataVisit.isThisNested = true 11656 p.fnOnlyDataVisit.isNewTargetAllowed = true 11657 p.fnOnlyDataVisit.isInStaticClassContext = property.Flags.Has(js_ast.PropertyIsStatic) 11658 p.fnOnlyDataVisit.innerClassNameRef = &result.innerClassNameRef 11659 11660 // We need to explicitly assign the name to the property initializer if it 11661 // will be transformed such that it is no longer an inline initializer. 11662 nameToKeep := "" 11663 isLoweredPrivateMethod := false 11664 if private, ok := property.Key.Data.(*js_ast.EPrivateIdentifier); ok { 11665 if !property.Kind.IsMethodDefinition() || p.privateSymbolNeedsToBeLowered(private) { 11666 nameToKeep = p.symbols[private.Ref.InnerIndex].OriginalName 11667 } 11668 11669 // Lowered private methods (both instance and static) are initialized 11670 // outside of the class body, so we must rewrite "super" property 11671 // accesses inside them. Lowered private instance fields are initialized 11672 // inside the constructor where "super" is valid, so those don't need to 11673 // be rewritten. 11674 if property.Kind.IsMethodDefinition() && p.privateSymbolNeedsToBeLowered(private) { 11675 isLoweredPrivateMethod = true 11676 } 11677 } else if !property.Kind.IsMethodDefinition() && !property.Flags.Has(js_ast.PropertyIsComputed) { 11678 if str, ok := property.Key.Data.(*js_ast.EString); ok { 11679 nameToKeep = helpers.UTF16ToString(str.Value) 11680 } 11681 } 11682 11683 // Handle methods 11684 if property.ValueOrNil.Data != nil { 11685 p.propMethodDecoratorScope = result.bodyScope 11686 11687 // Propagate the name to keep from the method into the initializer 11688 if nameToKeep != "" { 11689 p.nameToKeep = nameToKeep 11690 p.nameToKeepIsFor = property.ValueOrNil.Data 11691 } 11692 11693 // Propagate whether we're in a derived class constructor 11694 if class.ExtendsOrNil.Data != nil && !property.Flags.Has(js_ast.PropertyIsComputed) { 11695 if str, ok := property.Key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "constructor") { 11696 p.propDerivedCtorValue = property.ValueOrNil.Data 11697 } 11698 } 11699 11700 property.ValueOrNil, _ = p.visitExprInOut(property.ValueOrNil, exprIn{ 11701 isMethod: true, 11702 isLoweredPrivateMethod: isLoweredPrivateMethod, 11703 }) 11704 } 11705 11706 // Handle initialized fields 11707 if property.InitializerOrNil.Data != nil { 11708 if property.Flags.Has(js_ast.PropertyIsStatic) && classLoweringInfo.lowerAllStaticFields { 11709 // Need to lower "this" and "super" since they won't be valid outside the class body 11710 p.fnOnlyDataVisit.shouldReplaceThisWithInnerClassNameRef = true 11711 p.fnOrArrowDataVisit.shouldLowerSuperPropertyAccess = true 11712 } 11713 11714 // Propagate the name to keep from the field into the initializer 11715 if nameToKeep != "" { 11716 p.nameToKeep = nameToKeep 11717 p.nameToKeepIsFor = property.InitializerOrNil.Data 11718 } 11719 11720 property.InitializerOrNil = p.visitExpr(property.InitializerOrNil) 11721 } 11722 11723 // Restore "this" so it will take the inherited value in property keys 11724 p.fnOnlyDataVisit = oldFnOnlyDataVisit 11725 p.fnOrArrowDataVisit.shouldLowerSuperPropertyAccess = oldShouldLowerSuperPropertyAccess 11726 11727 // Restore the ability to use "arguments" in decorators and computed properties 11728 p.currentScope.ForbidArguments = false 11729 } 11730 11731 // Check for and warn about duplicate keys in class bodies 11732 if !p.suppressWarningsAboutWeirdCode { 11733 p.warnAboutDuplicateProperties(class.Properties, duplicatePropertiesInClass) 11734 } 11735 11736 // Analyze side effects before adding the name keeping call 11737 result.canBeRemovedIfUnused = p.astHelpers.ClassCanBeRemovedIfUnused(*class) 11738 11739 // Implement name keeping using a static block at the start of the class body 11740 if p.options.keepNames && nameToKeep != "" { 11741 propertyPreventsKeepNames := false 11742 for _, prop := range class.Properties { 11743 // A static property called "name" shadows the automatically-generated name 11744 if prop.Flags.Has(js_ast.PropertyIsStatic) { 11745 if str, ok := prop.Key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "name") { 11746 propertyPreventsKeepNames = true 11747 break 11748 } 11749 } 11750 } 11751 if !propertyPreventsKeepNames { 11752 var this js_ast.Expr 11753 if classLoweringInfo.lowerAllStaticFields { 11754 p.recordUsage(result.innerClassNameRef) 11755 this = js_ast.Expr{Loc: class.BodyLoc, Data: &js_ast.EIdentifier{Ref: result.innerClassNameRef}} 11756 } else { 11757 this = js_ast.Expr{Loc: class.BodyLoc, Data: js_ast.EThisShared} 11758 } 11759 properties := make([]js_ast.Property, 0, 1+len(class.Properties)) 11760 properties = append(properties, js_ast.Property{ 11761 Kind: js_ast.PropertyClassStaticBlock, 11762 ClassStaticBlock: &js_ast.ClassStaticBlock{Loc: class.BodyLoc, Block: js_ast.SBlock{Stmts: []js_ast.Stmt{ 11763 p.keepClassOrFnSymbolName(class.BodyLoc, this, nameToKeep), 11764 }}}, 11765 }) 11766 class.Properties = append(properties, class.Properties...) 11767 } 11768 } 11769 11770 p.enclosingClassKeyword = oldEnclosingClassKeyword 11771 p.superCtorRef = oldSuperCtorRef 11772 p.popScope() 11773 11774 if p.symbols[result.innerClassNameRef.InnerIndex].UseCountEstimate == 0 { 11775 // Don't generate a shadowing name if one isn't needed 11776 result.innerClassNameRef = ast.InvalidRef 11777 } else if class.Name == nil { 11778 // If there was originally no class name but something inside needed one 11779 // (e.g. there was a static property initializer that referenced "this"), 11780 // populate the class name. If this is an "export default class" statement, 11781 // use the existing default name so that things will work as expected if 11782 // this is turned into a regular class statement later on. 11783 classNameRef := defaultNameRef 11784 if classNameRef == ast.InvalidRef { 11785 classNameRef = p.newSymbol(ast.SymbolOther, "_this") 11786 p.currentScope.Generated = append(p.currentScope.Generated, classNameRef) 11787 p.recordDeclaredSymbol(classNameRef) 11788 } 11789 class.Name = &ast.LocRef{Loc: nameScopeLoc, Ref: classNameRef} 11790 } 11791 11792 p.popScope() 11793 11794 // Sanity check that the class lowering info hasn't changed before and after 11795 // visiting. The class transform relies on this because lowering assumes that 11796 // must be able to expect that visiting has done certain things. 11797 if classLoweringInfo != p.computeClassLoweringInfo(class) { 11798 panic("Internal error") 11799 } 11800 11801 return 11802 } 11803 11804 func isSimpleParameterList(args []js_ast.Arg, hasRestArg bool) bool { 11805 if hasRestArg { 11806 return false 11807 } 11808 for _, arg := range args { 11809 if _, ok := arg.Binding.Data.(*js_ast.BIdentifier); !ok || arg.DefaultOrNil.Data != nil { 11810 return false 11811 } 11812 } 11813 return true 11814 } 11815 11816 func fnBodyContainsUseStrict(body []js_ast.Stmt) (logger.Loc, bool) { 11817 for _, stmt := range body { 11818 switch s := stmt.Data.(type) { 11819 case *js_ast.SComment: 11820 continue 11821 case *js_ast.SDirective: 11822 if helpers.UTF16EqualsString(s.Value, "use strict") { 11823 return stmt.Loc, true 11824 } 11825 default: 11826 return logger.Loc{}, false 11827 } 11828 } 11829 return logger.Loc{}, false 11830 } 11831 11832 type visitArgsOpts struct { 11833 body []js_ast.Stmt 11834 decoratorScope *js_ast.Scope 11835 hasRestArg bool 11836 11837 // This is true if the function is an arrow function or a method 11838 isUniqueFormalParameters bool 11839 } 11840 11841 func (p *parser) visitArgs(args []js_ast.Arg, opts visitArgsOpts) { 11842 var duplicateArgCheck map[string]logger.Range 11843 useStrictLoc, hasUseStrict := fnBodyContainsUseStrict(opts.body) 11844 hasSimpleArgs := isSimpleParameterList(args, opts.hasRestArg) 11845 11846 // Section 15.2.1 Static Semantics: Early Errors: "It is a Syntax Error if 11847 // FunctionBodyContainsUseStrict of FunctionBody is true and 11848 // IsSimpleParameterList of FormalParameters is false." 11849 if hasUseStrict && !hasSimpleArgs { 11850 p.log.AddError(&p.tracker, p.source.RangeOfString(useStrictLoc), 11851 "Cannot use a \"use strict\" directive in a function with a non-simple parameter list") 11852 } 11853 11854 // Section 15.1.1 Static Semantics: Early Errors: "Multiple occurrences of 11855 // the same BindingIdentifier in a FormalParameterList is only allowed for 11856 // functions which have simple parameter lists and which are not defined in 11857 // strict mode code." 11858 if opts.isUniqueFormalParameters || hasUseStrict || !hasSimpleArgs || p.isStrictMode() { 11859 duplicateArgCheck = make(map[string]logger.Range) 11860 } 11861 11862 for i := range args { 11863 arg := &args[i] 11864 arg.Decorators = p.visitDecorators(arg.Decorators, opts.decoratorScope) 11865 p.visitBinding(arg.Binding, bindingOpts{ 11866 duplicateArgCheck: duplicateArgCheck, 11867 }) 11868 if arg.DefaultOrNil.Data != nil { 11869 arg.DefaultOrNil = p.visitExpr(arg.DefaultOrNil) 11870 } 11871 } 11872 } 11873 11874 func (p *parser) isDotOrIndexDefineMatch(expr js_ast.Expr, parts []string) bool { 11875 switch e := expr.Data.(type) { 11876 case *js_ast.EDot: 11877 if len(parts) > 1 { 11878 // Intermediates must be dot expressions 11879 last := len(parts) - 1 11880 return parts[last] == e.Name && p.isDotOrIndexDefineMatch(e.Target, parts[:last]) 11881 } 11882 11883 case *js_ast.EIndex: 11884 if len(parts) > 1 { 11885 if str, ok := e.Index.Data.(*js_ast.EString); ok { 11886 // Intermediates must be dot expressions 11887 last := len(parts) - 1 11888 return parts[last] == helpers.UTF16ToString(str.Value) && p.isDotOrIndexDefineMatch(e.Target, parts[:last]) 11889 } 11890 } 11891 11892 case *js_ast.EThis: 11893 // Allow matching on top-level "this" 11894 if !p.fnOnlyDataVisit.isThisNested { 11895 return len(parts) == 1 && parts[0] == "this" 11896 } 11897 11898 case *js_ast.EImportMeta: 11899 // Allow matching on "import.meta" 11900 return len(parts) == 2 && parts[0] == "import" && parts[1] == "meta" 11901 11902 case *js_ast.EIdentifier: 11903 // The last expression must be an identifier 11904 if len(parts) == 1 { 11905 // The name must match 11906 name := p.loadNameFromRef(e.Ref) 11907 if name != parts[0] { 11908 return false 11909 } 11910 11911 result := p.findSymbol(expr.Loc, name) 11912 11913 // The "findSymbol" function also marks this symbol as used. But that's 11914 // never what we want here because we're just peeking to see what kind of 11915 // symbol it is to see if it's a match. If it's not a match, it will be 11916 // re-resolved again later and marked as used there. So we don't want to 11917 // mark it as used twice. 11918 p.ignoreUsage(result.ref) 11919 11920 // We must not be in a "with" statement scope 11921 if result.isInsideWithScope { 11922 return false 11923 } 11924 11925 // The last symbol must be unbound or injected 11926 return p.symbols[result.ref.InnerIndex].Kind.IsUnboundOrInjected() 11927 } 11928 } 11929 11930 return false 11931 } 11932 11933 func (p *parser) instantiateDefineExpr(loc logger.Loc, expr config.DefineExpr, opts identifierOpts) js_ast.Expr { 11934 if expr.Constant != nil { 11935 return js_ast.Expr{Loc: loc, Data: expr.Constant} 11936 } 11937 11938 if expr.InjectedDefineIndex.IsValid() { 11939 ref := p.injectedDefineSymbols[expr.InjectedDefineIndex.GetIndex()] 11940 p.recordUsage(ref) 11941 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}} 11942 } 11943 11944 parts := expr.Parts 11945 if len(parts) == 0 { 11946 return js_ast.Expr{} 11947 } 11948 11949 // Check both user-specified defines and known globals 11950 if opts.matchAgainstDefines { 11951 // Make sure define resolution is not recursive 11952 opts.matchAgainstDefines = false 11953 11954 // Substitute user-specified defines 11955 if defines, ok := p.options.defines.DotDefines[parts[len(parts)-1]]; ok { 11956 for _, define := range defines { 11957 if define.Data.DefineExpr != nil && helpers.StringArraysEqual(define.Parts, parts) { 11958 return p.instantiateDefineExpr(loc, *define.Data.DefineExpr, opts) 11959 } 11960 } 11961 } 11962 } 11963 11964 // Check injected dot names 11965 if names, ok := p.injectedDotNames[parts[len(parts)-1]]; ok { 11966 for _, name := range names { 11967 if helpers.StringArraysEqual(name.parts, parts) { 11968 return p.instantiateInjectDotName(loc, name, opts.assignTarget) 11969 } 11970 } 11971 } 11972 11973 // Generate an identifier for the first part 11974 var value js_ast.Expr 11975 firstPart := parts[0] 11976 parts = parts[1:] 11977 switch firstPart { 11978 case "NaN": 11979 value = js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: math.NaN()}} 11980 11981 case "Infinity": 11982 value = js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: math.Inf(1)}} 11983 11984 case "null": 11985 value = js_ast.Expr{Loc: loc, Data: js_ast.ENullShared} 11986 11987 case "undefined": 11988 value = js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared} 11989 11990 case "this": 11991 if thisValue, ok := p.valueForThis(loc, false /* shouldLog */, js_ast.AssignTargetNone, false, false); ok { 11992 value = thisValue 11993 } else { 11994 value = js_ast.Expr{Loc: loc, Data: js_ast.EThisShared} 11995 } 11996 11997 default: 11998 if firstPart == "import" && len(parts) > 0 && parts[0] == "meta" { 11999 if importMeta, ok := p.valueForImportMeta(loc); ok { 12000 value = importMeta 12001 } else { 12002 value = js_ast.Expr{Loc: loc, Data: &js_ast.EImportMeta{}} 12003 } 12004 parts = parts[1:] 12005 break 12006 } 12007 12008 result := p.findSymbol(loc, firstPart) 12009 value = p.handleIdentifier(loc, &js_ast.EIdentifier{ 12010 Ref: result.ref, 12011 MustKeepDueToWithStmt: result.isInsideWithScope, 12012 12013 // Enable tree shaking 12014 CanBeRemovedIfUnused: true, 12015 }, opts) 12016 } 12017 12018 // Build up a chain of property access expressions for subsequent parts 12019 for _, part := range parts { 12020 if expr, ok := p.maybeRewritePropertyAccess(loc, js_ast.AssignTargetNone, false, value, part, loc, false, false, false); ok { 12021 value = expr 12022 } else if p.isMangledProp(part) { 12023 value = js_ast.Expr{Loc: loc, Data: &js_ast.EIndex{ 12024 Target: value, 12025 Index: js_ast.Expr{Loc: loc, Data: &js_ast.ENameOfSymbol{Ref: p.symbolForMangledProp(part)}}, 12026 }} 12027 } else { 12028 value = js_ast.Expr{Loc: loc, Data: &js_ast.EDot{ 12029 Target: value, 12030 Name: part, 12031 NameLoc: loc, 12032 12033 // Enable tree shaking 12034 CanBeRemovedIfUnused: true, 12035 }} 12036 } 12037 } 12038 12039 return value 12040 } 12041 12042 func (p *parser) instantiateInjectDotName(loc logger.Loc, name injectedDotName, assignTarget js_ast.AssignTarget) js_ast.Expr { 12043 // Note: We don't need to "ignoreRef" on the underlying identifier 12044 // because we have only parsed it but not visited it yet 12045 ref := p.injectedDefineSymbols[name.injectedDefineIndex] 12046 p.recordUsage(ref) 12047 12048 if assignTarget != js_ast.AssignTargetNone { 12049 if where, ok := p.injectedSymbolSources[ref]; ok { 12050 r := js_lexer.RangeOfIdentifier(p.source, loc) 12051 tracker := logger.MakeLineColumnTracker(&where.source) 12052 joined := strings.Join(name.parts, ".") 12053 p.log.AddErrorWithNotes(&p.tracker, r, 12054 fmt.Sprintf("Cannot assign to %q because it's an import from an injected file", joined), 12055 []logger.MsgData{tracker.MsgData(js_lexer.RangeOfIdentifier(where.source, where.loc), 12056 fmt.Sprintf("The symbol %q was exported from %q here:", joined, where.source.PrettyPath))}) 12057 } 12058 } 12059 12060 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: ref}} 12061 } 12062 12063 func (p *parser) checkForUnrepresentableIdentifier(loc logger.Loc, name string) { 12064 if p.options.asciiOnly && p.options.unsupportedJSFeatures.Has(compat.UnicodeEscapes) && 12065 helpers.ContainsNonBMPCodePoint(name) { 12066 if p.unrepresentableIdentifiers == nil { 12067 p.unrepresentableIdentifiers = make(map[string]bool) 12068 } 12069 if !p.unrepresentableIdentifiers[name] { 12070 p.unrepresentableIdentifiers[name] = true 12071 where := config.PrettyPrintTargetEnvironment(p.options.originalTargetEnv, p.options.unsupportedJSFeatureOverridesMask) 12072 r := js_lexer.RangeOfIdentifier(p.source, loc) 12073 p.log.AddError(&p.tracker, r, fmt.Sprintf("%q cannot be escaped in %s but you "+ 12074 "can set the charset to \"utf8\" to allow unescaped Unicode characters", name, where)) 12075 } 12076 } 12077 } 12078 12079 type typeofStringOrder uint8 12080 12081 const ( 12082 onlyCheckOriginalOrder typeofStringOrder = iota 12083 checkBothOrders 12084 ) 12085 12086 func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr, order typeofStringOrder) { 12087 if order == checkBothOrders { 12088 if _, ok := a.Data.(*js_ast.EString); ok { 12089 a, b = b, a 12090 } 12091 } 12092 12093 if typeof, ok := a.Data.(*js_ast.EUnary); ok && typeof.Op == js_ast.UnOpTypeof { 12094 if str, ok := b.Data.(*js_ast.EString); ok { 12095 value := helpers.UTF16ToString(str.Value) 12096 switch value { 12097 case "undefined", "object", "boolean", "number", "bigint", "string", "symbol", "function", "unknown": 12098 default: 12099 // Warn about typeof comparisons with values that will never be 12100 // returned. Here's an example of code with this problem: 12101 // https://github.com/olifolkerd/tabulator/issues/2962 12102 r := p.source.RangeOfString(b.Loc) 12103 text := fmt.Sprintf("The \"typeof\" operator will never evaluate to %q", value) 12104 kind := logger.Warning 12105 if p.suppressWarningsAboutWeirdCode { 12106 kind = logger.Debug 12107 } 12108 var notes []logger.MsgData 12109 if value == "null" { 12110 notes = append(notes, logger.MsgData{ 12111 Text: "The expression \"typeof x\" actually evaluates to \"object\" in JavaScript, not \"null\". " + 12112 "You need to use \"x === null\" to test for null.", 12113 }) 12114 } 12115 p.log.AddIDWithNotes(logger.MsgID_JS_ImpossibleTypeof, kind, &p.tracker, r, text, notes) 12116 } 12117 } 12118 } 12119 } 12120 12121 func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc logger.Loc) bool { 12122 switch e := value.Data.(type) { 12123 case *js_ast.ENumber: 12124 // "0 === -0" is true in JavaScript. Here's an example of code with this 12125 // problem: https://github.com/mrdoob/three.js/pull/11183 12126 if e.Value == 0 && math.Signbit(e.Value) { 12127 r := logger.Range{Loc: value.Loc, Len: 0} 12128 if int(r.Loc.Start) < len(p.source.Contents) && p.source.Contents[r.Loc.Start] == '-' { 12129 zeroRange := p.source.RangeOfNumber(logger.Loc{Start: r.Loc.Start + 1}) 12130 r.Len = zeroRange.Len + 1 12131 } 12132 text := fmt.Sprintf("Comparison with -0 using the %q operator will also match 0", op) 12133 if op == "case" { 12134 text = "Comparison with -0 using a case clause will also match 0" 12135 } 12136 kind := logger.Warning 12137 if p.suppressWarningsAboutWeirdCode { 12138 kind = logger.Debug 12139 } 12140 p.log.AddIDWithNotes(logger.MsgID_JS_EqualsNegativeZero, kind, &p.tracker, r, text, 12141 []logger.MsgData{{Text: "Floating-point equality is defined such that 0 and -0 are equal, so \"x === -0\" returns true for both 0 and -0. " + 12142 "You need to use \"Object.is(x, -0)\" instead to test for -0."}}) 12143 return true 12144 } 12145 12146 // "NaN === NaN" is false in JavaScript 12147 if math.IsNaN(e.Value) { 12148 text := fmt.Sprintf("Comparison with NaN using the %q operator here is always %v", op, op[0] == '!') 12149 if op == "case" { 12150 text = "This case clause will never be evaluated because equality with NaN is always false" 12151 } 12152 r := p.source.RangeOfOperatorBefore(afterOpLoc, op) 12153 kind := logger.Warning 12154 if p.suppressWarningsAboutWeirdCode { 12155 kind = logger.Debug 12156 } 12157 p.log.AddIDWithNotes(logger.MsgID_JS_EqualsNaN, kind, &p.tracker, r, text, 12158 []logger.MsgData{{Text: "Floating-point equality is defined such that NaN is never equal to anything, so \"x === NaN\" always returns false. " + 12159 "You need to use \"Number.isNaN(x)\" instead to test for NaN."}}) 12160 return true 12161 } 12162 12163 case *js_ast.EArray, *js_ast.EArrow, *js_ast.EClass, 12164 *js_ast.EFunction, *js_ast.EObject, *js_ast.ERegExp: 12165 // This warning only applies to strict equality because loose equality can 12166 // cause string conversions. For example, "x == []" is true if x is the 12167 // empty string. Here's an example of code with this problem: 12168 // https://github.com/aws/aws-sdk-js/issues/3325 12169 if len(op) > 2 { 12170 text := fmt.Sprintf("Comparison using the %q operator here is always %v", op, op[0] == '!') 12171 if op == "case" { 12172 text = "This case clause will never be evaluated because the comparison is always false" 12173 } 12174 r := p.source.RangeOfOperatorBefore(afterOpLoc, op) 12175 kind := logger.Warning 12176 if p.suppressWarningsAboutWeirdCode { 12177 kind = logger.Debug 12178 } 12179 p.log.AddIDWithNotes(logger.MsgID_JS_EqualsNewObject, kind, &p.tracker, r, text, 12180 []logger.MsgData{{Text: "Equality with a new object is always false in JavaScript because the equality operator tests object identity. " + 12181 "You need to write code to compare the contents of the object instead. " + 12182 "For example, use \"Array.isArray(x) && x.length === 0\" instead of \"x === []\" to test for an empty array."}}) 12183 return true 12184 } 12185 } 12186 12187 return false 12188 } 12189 12190 // EDot nodes represent a property access. This function may return an 12191 // expression to replace the property access with. It assumes that the 12192 // target of the EDot expression has already been visited. 12193 func (p *parser) maybeRewritePropertyAccess( 12194 loc logger.Loc, 12195 assignTarget js_ast.AssignTarget, 12196 isDeleteTarget bool, 12197 target js_ast.Expr, 12198 name string, 12199 nameLoc logger.Loc, 12200 isCallTarget bool, 12201 isTemplateTag bool, 12202 preferQuotedKey bool, 12203 ) (js_ast.Expr, bool) { 12204 if id, ok := target.Data.(*js_ast.EIdentifier); ok { 12205 // Rewrite property accesses on explicit namespace imports as an identifier. 12206 // This lets us replace them easily in the printer to rebind them to 12207 // something else without paying the cost of a whole-tree traversal during 12208 // module linking just to rewrite these EDot expressions. 12209 if p.options.mode == config.ModeBundle { 12210 if importItems, ok := p.importItemsForNamespace[id.Ref]; ok { 12211 // Cache translation so each property access resolves to the same import 12212 item, ok := importItems.entries[name] 12213 if !ok { 12214 // Replace non-default imports with "undefined" for JSON import assertions 12215 if record := &p.importRecords[importItems.importRecordIndex]; (record.Flags&ast.AssertTypeJSON) != 0 && name != "default" { 12216 kind := logger.Warning 12217 if p.suppressWarningsAboutWeirdCode { 12218 kind = logger.Debug 12219 } 12220 p.log.AddIDWithNotes(logger.MsgID_JS_AssertTypeJSON, kind, &p.tracker, js_lexer.RangeOfIdentifier(p.source, nameLoc), 12221 fmt.Sprintf("Non-default import %q is undefined with a JSON import assertion", name), 12222 p.notesForAssertTypeJSON(record, name)) 12223 p.ignoreUsage(id.Ref) 12224 return js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared}, true 12225 } 12226 12227 // Generate a new import item symbol in the module scope 12228 item = ast.LocRef{Loc: nameLoc, Ref: p.newSymbol(ast.SymbolImport, name)} 12229 p.moduleScope.Generated = append(p.moduleScope.Generated, item.Ref) 12230 12231 // Link the namespace import and the import item together 12232 importItems.entries[name] = item 12233 p.isImportItem[item.Ref] = true 12234 12235 symbol := &p.symbols[item.Ref.InnerIndex] 12236 if p.options.mode == config.ModePassThrough { 12237 // Make sure the printer prints this as a property access 12238 symbol.NamespaceAlias = &ast.NamespaceAlias{ 12239 NamespaceRef: id.Ref, 12240 Alias: name, 12241 } 12242 } else { 12243 // Mark this as generated in case it's missing. We don't want to 12244 // generate errors for missing import items that are automatically 12245 // generated. 12246 symbol.ImportItemStatus = ast.ImportItemGenerated 12247 } 12248 } 12249 12250 // Undo the usage count for the namespace itself. This is used later 12251 // to detect whether the namespace symbol has ever been "captured" 12252 // or whether it has just been used to read properties off of. 12253 // 12254 // The benefit of doing this is that if both this module and the 12255 // imported module end up in the same module group and the namespace 12256 // symbol has never been captured, then we don't need to generate 12257 // any code for the namespace at all. 12258 p.ignoreUsage(id.Ref) 12259 12260 // Track how many times we've referenced this symbol 12261 p.recordUsage(item.Ref) 12262 return p.handleIdentifier(nameLoc, &js_ast.EIdentifier{Ref: item.Ref}, identifierOpts{ 12263 assignTarget: assignTarget, 12264 isCallTarget: isCallTarget, 12265 isDeleteTarget: isDeleteTarget, 12266 preferQuotedKey: preferQuotedKey, 12267 12268 // If this expression is used as the target of a call expression, make 12269 // sure the value of "this" is preserved. 12270 wasOriginallyIdentifier: false, 12271 }), true 12272 } 12273 12274 // Rewrite "module.require()" to "require()" for Webpack compatibility. 12275 // See https://github.com/webpack/webpack/pull/7750 for more info. 12276 if isCallTarget && id.Ref == p.moduleRef && name == "require" { 12277 p.ignoreUsage(p.moduleRef) 12278 12279 // This uses "require" instead of a reference to our "__require" 12280 // function so that the code coming up that detects calls to 12281 // "require" will recognize it. 12282 p.recordUsage(p.requireRef) 12283 return js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: p.requireRef}}, true 12284 } 12285 } 12286 } 12287 12288 // Attempt to simplify statically-determined object literal property accesses 12289 if !isCallTarget && !isTemplateTag && p.options.minifySyntax && assignTarget == js_ast.AssignTargetNone { 12290 if object, ok := target.Data.(*js_ast.EObject); ok { 12291 var replace js_ast.Expr 12292 hasProtoNull := false 12293 isUnsafe := false 12294 12295 // Check that doing this is safe 12296 for _, prop := range object.Properties { 12297 // "{ ...a }.a" must be preserved 12298 // "new ({ a() {} }.a)" must throw 12299 // "{ get a() {} }.a" must be preserved 12300 // "{ set a(b) {} }.a = 1" must be preserved 12301 // "{ a: 1, [String.fromCharCode(97)]: 2 }.a" must be 2 12302 if prop.Kind == js_ast.PropertySpread || prop.Flags.Has(js_ast.PropertyIsComputed) || prop.Kind.IsMethodDefinition() { 12303 isUnsafe = true 12304 break 12305 } 12306 12307 // Do not attempt to compare against numeric keys 12308 key, ok := prop.Key.Data.(*js_ast.EString) 12309 if !ok { 12310 isUnsafe = true 12311 break 12312 } 12313 12314 // The "__proto__" key has special behavior 12315 if helpers.UTF16EqualsString(key.Value, "__proto__") { 12316 if _, ok := prop.ValueOrNil.Data.(*js_ast.ENull); ok { 12317 // Replacing "{__proto__: null}.a" with undefined should be safe 12318 hasProtoNull = true 12319 } 12320 } 12321 12322 // This entire object literal must have no side effects 12323 if !p.astHelpers.ExprCanBeRemovedIfUnused(prop.ValueOrNil) { 12324 isUnsafe = true 12325 break 12326 } 12327 12328 // Note that we need to take the last value if there are duplicate keys 12329 if helpers.UTF16EqualsString(key.Value, name) { 12330 replace = prop.ValueOrNil 12331 } 12332 } 12333 12334 if !isUnsafe { 12335 // If the key was found, return the value for that key. Note 12336 // that "{__proto__: null}.__proto__" is undefined, not null. 12337 if replace.Data != nil && name != "__proto__" { 12338 return replace, true 12339 } 12340 12341 // We can only return "undefined" when a key is missing if the prototype is null 12342 if hasProtoNull { 12343 return js_ast.Expr{Loc: target.Loc, Data: js_ast.EUndefinedShared}, true 12344 } 12345 } 12346 } 12347 } 12348 12349 // Handle references to namespaces or namespace members 12350 if target.Data == p.tsNamespaceTarget && assignTarget == js_ast.AssignTargetNone && !isDeleteTarget { 12351 if ns, ok := p.tsNamespaceMemberData.(*js_ast.TSNamespaceMemberNamespace); ok { 12352 if member, ok := ns.ExportedMembers[name]; ok { 12353 switch m := member.Data.(type) { 12354 case *js_ast.TSNamespaceMemberEnumNumber: 12355 p.ignoreUsageOfIdentifierInDotChain(target) 12356 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: m.Value}}, name), true 12357 12358 case *js_ast.TSNamespaceMemberEnumString: 12359 p.ignoreUsageOfIdentifierInDotChain(target) 12360 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: m.Value}}, name), true 12361 12362 case *js_ast.TSNamespaceMemberNamespace: 12363 // If this isn't a constant, return a clone of this property access 12364 // but with the namespace member data associated with it so that 12365 // more property accesses off of this property access are recognized. 12366 if preferQuotedKey || !js_ast.IsIdentifier(name) { 12367 p.tsNamespaceTarget = &js_ast.EIndex{ 12368 Target: target, 12369 Index: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}}, 12370 } 12371 } else { 12372 p.tsNamespaceTarget = p.dotOrMangledPropVisit(target, name, nameLoc) 12373 } 12374 p.tsNamespaceMemberData = member.Data 12375 return js_ast.Expr{Loc: loc, Data: p.tsNamespaceTarget}, true 12376 } 12377 } 12378 } 12379 } 12380 12381 // Symbol uses due to a property access off of an imported symbol are tracked 12382 // specially. This lets us do tree shaking for cross-file TypeScript enums. 12383 if p.options.mode == config.ModeBundle && !p.isControlFlowDead { 12384 if id, ok := target.Data.(*js_ast.EImportIdentifier); ok { 12385 // Remove the normal symbol use 12386 use := p.symbolUses[id.Ref] 12387 use.CountEstimate-- 12388 if use.CountEstimate == 0 { 12389 delete(p.symbolUses, id.Ref) 12390 } else { 12391 p.symbolUses[id.Ref] = use 12392 } 12393 12394 // Add a special symbol use instead 12395 if p.importSymbolPropertyUses == nil { 12396 p.importSymbolPropertyUses = make(map[ast.Ref]map[string]js_ast.SymbolUse) 12397 } 12398 properties := p.importSymbolPropertyUses[id.Ref] 12399 if properties == nil { 12400 properties = make(map[string]js_ast.SymbolUse) 12401 p.importSymbolPropertyUses[id.Ref] = properties 12402 } 12403 use = properties[name] 12404 use.CountEstimate++ 12405 properties[name] = use 12406 } 12407 } 12408 12409 // Minify "foo".length 12410 if p.options.minifySyntax && assignTarget == js_ast.AssignTargetNone { 12411 switch t := target.Data.(type) { 12412 case *js_ast.EString: 12413 if name == "length" { 12414 return js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: float64(len(t.Value))}}, true 12415 } 12416 case *js_ast.EInlinedEnum: 12417 if s, ok := t.Value.Data.(*js_ast.EString); ok && name == "length" { 12418 return js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: float64(len(s.Value))}}, true 12419 } 12420 } 12421 } 12422 12423 return js_ast.Expr{}, false 12424 } 12425 12426 type exprIn struct { 12427 isMethod bool 12428 isLoweredPrivateMethod bool 12429 12430 // This tells us if there are optional chain expressions (EDot, EIndex, or 12431 // ECall) that are chained on to this expression. Because of the way the AST 12432 // works, chaining expressions on to this expression means they are our 12433 // parent expressions. 12434 // 12435 // Some examples: 12436 // 12437 // a?.b.c // EDot 12438 // a?.b[c] // EIndex 12439 // a?.b() // ECall 12440 // 12441 // Note that this is false if our parent is a node with a OptionalChain 12442 // value of OptionalChainStart. That means it's the start of a new chain, so 12443 // it's not considered part of this one. 12444 // 12445 // Some examples: 12446 // 12447 // a?.b?.c // EDot 12448 // a?.b?.[c] // EIndex 12449 // a?.b?.() // ECall 12450 // 12451 // Also note that this is false if our parent is a node with a OptionalChain 12452 // value of OptionalChainNone. That means it's outside parentheses, which 12453 // means it's no longer part of the chain. 12454 // 12455 // Some examples: 12456 // 12457 // (a?.b).c // EDot 12458 // (a?.b)[c] // EIndex 12459 // (a?.b)() // ECall 12460 // 12461 hasChainParent bool 12462 12463 // If our parent is an ECall node with an OptionalChain value of 12464 // OptionalChainStart, then we will need to store the value for the "this" of 12465 // that call somewhere if the current expression is an optional chain that 12466 // ends in a property access. That's because the value for "this" will be 12467 // used twice: once for the inner optional chain and once for the outer 12468 // optional chain. 12469 // 12470 // Example: 12471 // 12472 // // Original 12473 // a?.b?.(); 12474 // 12475 // // Lowered 12476 // var _a; 12477 // (_a = a == null ? void 0 : a.b) == null ? void 0 : _a.call(a); 12478 // 12479 // In the example above we need to store "a" as the value for "this" so we 12480 // can substitute it back in when we call "_a" if "_a" is indeed present. 12481 // See also "thisArgFunc" and "thisArgWrapFunc" in "exprOut". 12482 storeThisArgForParentOptionalChain bool 12483 12484 // If true, string literals that match the current property mangling pattern 12485 // should be turned into ENameOfSymbol expressions, which will cause us to 12486 // rename them in the linker. 12487 shouldMangleStringsAsProps bool 12488 12489 // Certain substitutions of identifiers are disallowed for assignment targets. 12490 // For example, we shouldn't transform "undefined = 1" into "void 0 = 1". This 12491 // isn't something real-world code would do but it matters for conformance 12492 // tests. 12493 assignTarget js_ast.AssignTarget 12494 } 12495 12496 type exprOut struct { 12497 // If our parent is an ECall node with an OptionalChain value of 12498 // OptionalChainContinue, then we may need to return the value for "this" 12499 // from this node or one of this node's children so that the parent that is 12500 // the end of the optional chain can use it. 12501 // 12502 // Example: 12503 // 12504 // // Original 12505 // a?.b?.().c(); 12506 // 12507 // // Lowered 12508 // var _a; 12509 // (_a = a == null ? void 0 : a.b) == null ? void 0 : _a.call(a).c(); 12510 // 12511 // The value "_a" for "this" must be passed all the way up to the call to 12512 // ".c()" which is where the optional chain is lowered. From there it must 12513 // be substituted as the value for "this" in the call to ".b?.()". See also 12514 // "storeThisArgForParentOptionalChain" in "exprIn". 12515 thisArgFunc func() js_ast.Expr 12516 thisArgWrapFunc func(js_ast.Expr) js_ast.Expr 12517 12518 // True if the child node is an optional chain node (EDot, EIndex, or ECall 12519 // with an IsOptionalChain value of true) 12520 childContainsOptionalChain bool 12521 12522 // If true and this is used as a call target, the whole call expression 12523 // must be replaced with undefined. 12524 methodCallMustBeReplacedWithUndefined bool 12525 } 12526 12527 func (p *parser) visitExpr(expr js_ast.Expr) js_ast.Expr { 12528 expr, _ = p.visitExprInOut(expr, exprIn{}) 12529 return expr 12530 } 12531 12532 func (p *parser) valueForThis( 12533 loc logger.Loc, 12534 shouldLog bool, 12535 assignTarget js_ast.AssignTarget, 12536 isCallTarget bool, 12537 isDeleteTarget bool, 12538 ) (js_ast.Expr, bool) { 12539 // Substitute "this" if we're inside a static class context 12540 if p.fnOnlyDataVisit.shouldReplaceThisWithInnerClassNameRef { 12541 p.recordUsage(*p.fnOnlyDataVisit.innerClassNameRef) 12542 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.innerClassNameRef}}, true 12543 } 12544 12545 // Is this a top-level use of "this"? 12546 if !p.fnOnlyDataVisit.isThisNested { 12547 // Substitute user-specified defines 12548 if data, ok := p.options.defines.IdentifierDefines["this"]; ok { 12549 if data.DefineExpr != nil { 12550 return p.instantiateDefineExpr(loc, *data.DefineExpr, identifierOpts{ 12551 assignTarget: assignTarget, 12552 isCallTarget: isCallTarget, 12553 isDeleteTarget: isDeleteTarget, 12554 }), true 12555 } 12556 } 12557 12558 // Otherwise, replace top-level "this" with either "undefined" or "exports" 12559 if p.isFileConsideredToHaveESMExports { 12560 // Warn about "this" becoming undefined, but only once per file 12561 if shouldLog && !p.messageAboutThisIsUndefined && !p.fnOnlyDataVisit.silenceMessageAboutThisBeingUndefined { 12562 p.messageAboutThisIsUndefined = true 12563 kind := logger.Debug 12564 data := p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, loc), 12565 "Top-level \"this\" will be replaced with undefined since this file is an ECMAScript module") 12566 data.Location.Suggestion = "undefined" 12567 _, notes := p.whyESModule() 12568 p.log.AddMsgID(logger.MsgID_JS_ThisIsUndefinedInESM, logger.Msg{Kind: kind, Data: data, Notes: notes}) 12569 } 12570 12571 // In an ES6 module, "this" is supposed to be undefined. Instead of 12572 // doing this at runtime using "fn.call(undefined)", we do it at 12573 // compile time using expression substitution here. 12574 return js_ast.Expr{Loc: loc, Data: js_ast.EUndefinedShared}, true 12575 } else if p.options.mode != config.ModePassThrough { 12576 // In a CommonJS module, "this" is supposed to be the same as "exports". 12577 // Instead of doing this at runtime using "fn.call(module.exports)", we 12578 // do it at compile time using expression substitution here. 12579 p.recordUsage(p.exportsRef) 12580 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.exportsRef}}, true 12581 } 12582 } 12583 12584 return js_ast.Expr{}, false 12585 } 12586 12587 func (p *parser) valueForImportMeta(loc logger.Loc) (js_ast.Expr, bool) { 12588 if p.options.unsupportedJSFeatures.Has(compat.ImportMeta) || 12589 (p.options.mode != config.ModePassThrough && !p.options.outputFormat.KeepESMImportExportSyntax()) { 12590 // Generate the variable if it doesn't exist yet 12591 if p.importMetaRef == ast.InvalidRef { 12592 p.importMetaRef = p.newSymbol(ast.SymbolOther, "import_meta") 12593 p.moduleScope.Generated = append(p.moduleScope.Generated, p.importMetaRef) 12594 } 12595 12596 // Replace "import.meta" with a reference to the symbol 12597 p.recordUsage(p.importMetaRef) 12598 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.importMetaRef}}, true 12599 } 12600 12601 return js_ast.Expr{}, false 12602 } 12603 12604 func locAfterOp(e *js_ast.EBinary) logger.Loc { 12605 if e.Left.Loc.Start < e.Right.Loc.Start { 12606 return e.Right.Loc 12607 } else { 12608 // Handle the case when we have transposed the operands 12609 return e.Left.Loc 12610 } 12611 } 12612 12613 // This function exists to tie all of these checks together in one place 12614 func isEvalOrArguments(name string) bool { 12615 return name == "eval" || name == "arguments" 12616 } 12617 12618 func (p *parser) reportPrivateNameUsage(name string) { 12619 if p.parseExperimentalDecoratorNesting > 0 { 12620 if p.lowerAllOfThesePrivateNames == nil { 12621 p.lowerAllOfThesePrivateNames = make(map[string]bool) 12622 } 12623 p.lowerAllOfThesePrivateNames[name] = true 12624 } 12625 } 12626 12627 func (p *parser) isValidAssignmentTarget(expr js_ast.Expr) bool { 12628 switch e := expr.Data.(type) { 12629 case *js_ast.EIdentifier: 12630 if p.isStrictMode() { 12631 if name := p.loadNameFromRef(e.Ref); isEvalOrArguments(name) { 12632 return false 12633 } 12634 } 12635 return true 12636 case *js_ast.EDot: 12637 return e.OptionalChain == js_ast.OptionalChainNone 12638 case *js_ast.EIndex: 12639 return e.OptionalChain == js_ast.OptionalChainNone 12640 12641 // Don't worry about recursive checking for objects and arrays. This will 12642 // already be handled naturally by passing down the assign target flag. 12643 case *js_ast.EObject: 12644 return !e.IsParenthesized 12645 case *js_ast.EArray: 12646 return !e.IsParenthesized 12647 } 12648 return false 12649 } 12650 12651 func containsClosingScriptTag(text string) bool { 12652 for { 12653 i := strings.Index(text, "</") 12654 if i < 0 { 12655 break 12656 } 12657 text = text[i+2:] 12658 if len(text) >= 6 && strings.EqualFold(text[:6], "script") { 12659 return true 12660 } 12661 } 12662 return false 12663 } 12664 12665 func (p *parser) isUnsupportedRegularExpression(loc logger.Loc, value string) (pattern string, flags string, isUnsupported bool) { 12666 var what string 12667 var r logger.Range 12668 12669 end := strings.LastIndexByte(value, '/') 12670 pattern = value[1:end] 12671 flags = value[end+1:] 12672 isUnicode := strings.IndexByte(flags, 'u') >= 0 12673 parenDepth := 0 12674 i := 0 12675 12676 // Do a simple scan for unsupported features assuming the regular expression 12677 // is valid. This doesn't do a full validation of the regular expression 12678 // because regular expression grammar is complicated. If it contains a syntax 12679 // error that we don't catch, then we will just generate output code with a 12680 // syntax error. Garbage in, garbage out. 12681 pattern: 12682 for i < len(pattern) { 12683 c := pattern[i] 12684 i++ 12685 12686 switch c { 12687 case '[': 12688 class: 12689 for i < len(pattern) { 12690 c := pattern[i] 12691 i++ 12692 12693 switch c { 12694 case ']': 12695 break class 12696 12697 case '\\': 12698 i++ // Skip the escaped character 12699 } 12700 } 12701 12702 case '(': 12703 tail := pattern[i:] 12704 12705 if strings.HasPrefix(tail, "?<=") || strings.HasPrefix(tail, "?<!") { 12706 if p.options.unsupportedJSFeatures.Has(compat.RegexpLookbehindAssertions) { 12707 what = "Lookbehind assertions in regular expressions are not available" 12708 r = logger.Range{Loc: logger.Loc{Start: loc.Start + int32(i) + 1}, Len: 3} 12709 isUnsupported = true 12710 break pattern 12711 } 12712 } else if strings.HasPrefix(tail, "?<") { 12713 if p.options.unsupportedJSFeatures.Has(compat.RegexpNamedCaptureGroups) { 12714 if end := strings.IndexByte(tail, '>'); end >= 0 { 12715 what = "Named capture groups in regular expressions are not available" 12716 r = logger.Range{Loc: logger.Loc{Start: loc.Start + int32(i) + 1}, Len: int32(end) + 1} 12717 isUnsupported = true 12718 break pattern 12719 } 12720 } 12721 } 12722 12723 parenDepth++ 12724 12725 case ')': 12726 if parenDepth == 0 { 12727 r := logger.Range{Loc: logger.Loc{Start: loc.Start + int32(i)}, Len: 1} 12728 p.log.AddError(&p.tracker, r, "Unexpected \")\" in regular expression") 12729 return 12730 } 12731 12732 parenDepth-- 12733 12734 case '\\': 12735 tail := pattern[i:] 12736 12737 if isUnicode && (strings.HasPrefix(tail, "p{") || strings.HasPrefix(tail, "P{")) { 12738 if p.options.unsupportedJSFeatures.Has(compat.RegexpUnicodePropertyEscapes) { 12739 if end := strings.IndexByte(tail, '}'); end >= 0 { 12740 what = "Unicode property escapes in regular expressions are not available" 12741 r = logger.Range{Loc: logger.Loc{Start: loc.Start + int32(i)}, Len: int32(end) + 2} 12742 isUnsupported = true 12743 break pattern 12744 } 12745 } 12746 } 12747 12748 i++ // Skip the escaped character 12749 } 12750 } 12751 12752 if !isUnsupported { 12753 for i, c := range flags { 12754 switch c { 12755 case 'g', 'i', 'm': 12756 continue // These are part of ES5 and are always supported 12757 12758 case 's': 12759 if !p.options.unsupportedJSFeatures.Has(compat.RegexpDotAllFlag) { 12760 continue // This is part of ES2018 12761 } 12762 12763 case 'y', 'u': 12764 if !p.options.unsupportedJSFeatures.Has(compat.RegexpStickyAndUnicodeFlags) { 12765 continue // These are part of ES2018 12766 } 12767 12768 case 'd': 12769 if !p.options.unsupportedJSFeatures.Has(compat.RegexpMatchIndices) { 12770 continue // This is part of ES2022 12771 } 12772 12773 case 'v': 12774 if !p.options.unsupportedJSFeatures.Has(compat.RegexpSetNotation) { 12775 continue // This is from a proposal: https://github.com/tc39/proposal-regexp-v-flag 12776 } 12777 12778 default: 12779 // Unknown flags are never supported 12780 } 12781 12782 r = logger.Range{Loc: logger.Loc{Start: loc.Start + int32(end+1) + int32(i)}, Len: 1} 12783 what = fmt.Sprintf("The regular expression flag \"%c\" is not available", c) 12784 isUnsupported = true 12785 break 12786 } 12787 } 12788 12789 if isUnsupported { 12790 where := config.PrettyPrintTargetEnvironment(p.options.originalTargetEnv, p.options.unsupportedJSFeatureOverridesMask) 12791 p.log.AddIDWithNotes(logger.MsgID_JS_UnsupportedRegExp, logger.Debug, &p.tracker, r, fmt.Sprintf("%s in %s", what, where), []logger.MsgData{{ 12792 Text: "This regular expression literal has been converted to a \"new RegExp()\" constructor " + 12793 "to avoid generating code with a syntax error. However, you will need to include a " + 12794 "polyfill for \"RegExp\" for your code to have the correct behavior at run-time."}}) 12795 } 12796 12797 return 12798 } 12799 12800 // This function takes "exprIn" as input from the caller and produces "exprOut" 12801 // for the caller to pass along extra data. This is mostly for optional chaining. 12802 func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprOut) { 12803 if in.assignTarget != js_ast.AssignTargetNone && !p.isValidAssignmentTarget(expr) { 12804 p.log.AddError(&p.tracker, logger.Range{Loc: expr.Loc}, "Invalid assignment target") 12805 } 12806 12807 // Note: Anything added before or after this switch statement will be bypassed 12808 // when visiting nested "EBinary" nodes due to stack overflow mitigations for 12809 // deeply-nested ASTs. If anything like that is added, care must be taken that 12810 // it doesn't affect these mitigations by ensuring that the mitigations are not 12811 // applied in those cases (e.g. by adding an additional conditional check). 12812 switch e := expr.Data.(type) { 12813 case *js_ast.ENull, *js_ast.ESuper, *js_ast.EBoolean, *js_ast.EBigInt, *js_ast.EUndefined, *js_ast.EJSXText: 12814 12815 case *js_ast.ENameOfSymbol: 12816 e.Ref = p.symbolForMangledProp(p.loadNameFromRef(e.Ref)) 12817 12818 case *js_ast.ERegExp: 12819 // "/pattern/flags" => "new RegExp('pattern', 'flags')" 12820 if pattern, flags, ok := p.isUnsupportedRegularExpression(expr.Loc, e.Value); ok { 12821 args := []js_ast.Expr{{ 12822 Loc: logger.Loc{Start: expr.Loc.Start + 1}, 12823 Data: &js_ast.EString{Value: helpers.StringToUTF16(pattern)}, 12824 }} 12825 if flags != "" { 12826 args = append(args, js_ast.Expr{ 12827 Loc: logger.Loc{Start: expr.Loc.Start + int32(len(pattern)) + 2}, 12828 Data: &js_ast.EString{Value: helpers.StringToUTF16(flags)}, 12829 }) 12830 } 12831 regExpRef := p.makeRegExpRef() 12832 p.recordUsage(regExpRef) 12833 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENew{ 12834 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: regExpRef}}, 12835 Args: args, 12836 CloseParenLoc: logger.Loc{Start: expr.Loc.Start + int32(len(e.Value))}, 12837 }}, exprOut{} 12838 } 12839 12840 case *js_ast.ENewTarget: 12841 if !p.fnOnlyDataVisit.isNewTargetAllowed { 12842 p.log.AddError(&p.tracker, e.Range, "Cannot use \"new.target\" here:") 12843 } 12844 12845 case *js_ast.EString: 12846 if e.LegacyOctalLoc.Start > 0 { 12847 if e.PreferTemplate { 12848 p.log.AddError(&p.tracker, p.source.RangeOfLegacyOctalEscape(e.LegacyOctalLoc), 12849 "Legacy octal escape sequences cannot be used in template literals") 12850 } else if p.isStrictMode() { 12851 p.markStrictModeFeature(legacyOctalEscape, p.source.RangeOfLegacyOctalEscape(e.LegacyOctalLoc), "") 12852 } 12853 } 12854 12855 if in.shouldMangleStringsAsProps && p.options.mangleQuoted && !e.PreferTemplate { 12856 if name := helpers.UTF16ToString(e.Value); p.isMangledProp(name) { 12857 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENameOfSymbol{ 12858 Ref: p.symbolForMangledProp(name), 12859 }}, exprOut{} 12860 } 12861 } 12862 12863 case *js_ast.ENumber: 12864 if p.legacyOctalLiterals != nil && p.isStrictMode() { 12865 if r, ok := p.legacyOctalLiterals[expr.Data]; ok { 12866 p.markStrictModeFeature(legacyOctalLiteral, r, "") 12867 } 12868 } 12869 12870 case *js_ast.EThis: 12871 isDeleteTarget := e == p.deleteTarget 12872 isCallTarget := e == p.callTarget 12873 12874 if value, ok := p.valueForThis(expr.Loc, true /* shouldLog */, in.assignTarget, isDeleteTarget, isCallTarget); ok { 12875 return value, exprOut{} 12876 } 12877 12878 // Capture "this" inside arrow functions that will be lowered into normal 12879 // function expressions for older language environments 12880 if p.fnOrArrowDataVisit.isArrow && p.options.unsupportedJSFeatures.Has(compat.Arrow) && p.fnOnlyDataVisit.isThisNested { 12881 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: p.captureThis()}}, exprOut{} 12882 } 12883 12884 case *js_ast.EImportMeta: 12885 isDeleteTarget := e == p.deleteTarget 12886 isCallTarget := e == p.callTarget 12887 12888 // Check both user-specified defines and known globals 12889 if defines, ok := p.options.defines.DotDefines["meta"]; ok { 12890 for _, define := range defines { 12891 if p.isDotOrIndexDefineMatch(expr, define.Parts) { 12892 // Substitute user-specified defines 12893 if define.Data.DefineExpr != nil { 12894 return p.instantiateDefineExpr(expr.Loc, *define.Data.DefineExpr, identifierOpts{ 12895 assignTarget: in.assignTarget, 12896 isCallTarget: isCallTarget, 12897 isDeleteTarget: isDeleteTarget, 12898 }), exprOut{} 12899 } 12900 } 12901 } 12902 } 12903 12904 // Check injected dot names 12905 if names, ok := p.injectedDotNames["meta"]; ok { 12906 for _, name := range names { 12907 if p.isDotOrIndexDefineMatch(expr, name.parts) { 12908 // Note: We don't need to "ignoreRef" on the underlying identifier 12909 // because we have only parsed it but not visited it yet 12910 return p.instantiateInjectDotName(expr.Loc, name, in.assignTarget), exprOut{} 12911 } 12912 } 12913 } 12914 12915 // Warn about "import.meta" if it's not replaced by a define 12916 if p.options.unsupportedJSFeatures.Has(compat.ImportMeta) { 12917 r := logger.Range{Loc: expr.Loc, Len: e.RangeLen} 12918 p.markSyntaxFeature(compat.ImportMeta, r) 12919 } else if p.options.mode != config.ModePassThrough && !p.options.outputFormat.KeepESMImportExportSyntax() { 12920 r := logger.Range{Loc: expr.Loc, Len: e.RangeLen} 12921 kind := logger.Warning 12922 if p.suppressWarningsAboutWeirdCode || p.fnOrArrowDataVisit.tryBodyCount > 0 { 12923 kind = logger.Debug 12924 } 12925 p.log.AddIDWithNotes(logger.MsgID_JS_EmptyImportMeta, kind, &p.tracker, r, fmt.Sprintf( 12926 "\"import.meta\" is not available with the %q output format and will be empty", p.options.outputFormat.String()), 12927 []logger.MsgData{{Text: "You need to set the output format to \"esm\" for \"import.meta\" to work correctly."}}) 12928 } 12929 12930 // Convert "import.meta" to a variable if it's not supported in the output format 12931 if importMeta, ok := p.valueForImportMeta(expr.Loc); ok { 12932 return importMeta, exprOut{} 12933 } 12934 12935 case *js_ast.ESpread: 12936 e.Value = p.visitExpr(e.Value) 12937 12938 case *js_ast.EIdentifier: 12939 isCallTarget := e == p.callTarget 12940 isDeleteTarget := e == p.deleteTarget 12941 name := p.loadNameFromRef(e.Ref) 12942 if p.isStrictMode() && js_lexer.StrictModeReservedWords[name] { 12943 p.markStrictModeFeature(reservedWord, js_lexer.RangeOfIdentifier(p.source, expr.Loc), name) 12944 } 12945 result := p.findSymbol(expr.Loc, name) 12946 e.MustKeepDueToWithStmt = result.isInsideWithScope 12947 e.Ref = result.ref 12948 12949 // Handle referencing a class name within that class's computed property 12950 // key. This is not allowed, and must fail at run-time: 12951 // 12952 // class Foo { 12953 // static foo = 'bar' 12954 // static [Foo.foo] = 'foo' 12955 // } 12956 // 12957 if p.symbols[result.ref.InnerIndex].Kind == ast.SymbolClassInComputedPropertyKey { 12958 p.log.AddID(logger.MsgID_JS_ClassNameWillThrow, logger.Warning, &p.tracker, js_lexer.RangeOfIdentifier(p.source, expr.Loc), 12959 fmt.Sprintf("Accessing class %q before initialization will throw", name)) 12960 return p.callRuntime(expr.Loc, "__earlyAccess", []js_ast.Expr{{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(name)}}}), exprOut{} 12961 } 12962 12963 // Handle assigning to a constant 12964 if in.assignTarget != js_ast.AssignTargetNone { 12965 switch p.symbols[result.ref.InnerIndex].Kind { 12966 case ast.SymbolConst: 12967 r := js_lexer.RangeOfIdentifier(p.source, expr.Loc) 12968 notes := []logger.MsgData{p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, result.declareLoc), 12969 fmt.Sprintf("The symbol %q was declared a constant here:", name))} 12970 12971 // Make this an error when bundling because we may need to convert this 12972 // "const" into a "var" during bundling. Also make this an error when 12973 // the constant is inlined because we will otherwise generate code with 12974 // a syntax error. 12975 if _, isInlinedConstant := p.constValues[result.ref]; isInlinedConstant || p.options.mode == config.ModeBundle || 12976 (p.currentScope.Parent == nil && p.willWrapModuleInTryCatchForUsing) { 12977 p.log.AddErrorWithNotes(&p.tracker, r, 12978 fmt.Sprintf("Cannot assign to %q because it is a constant", name), notes) 12979 } else { 12980 p.log.AddIDWithNotes(logger.MsgID_JS_AssignToConstant, logger.Warning, &p.tracker, r, 12981 fmt.Sprintf("This assignment will throw because %q is a constant", name), notes) 12982 } 12983 12984 case ast.SymbolInjected: 12985 if where, ok := p.injectedSymbolSources[result.ref]; ok { 12986 r := js_lexer.RangeOfIdentifier(p.source, expr.Loc) 12987 tracker := logger.MakeLineColumnTracker(&where.source) 12988 p.log.AddErrorWithNotes(&p.tracker, r, 12989 fmt.Sprintf("Cannot assign to %q because it's an import from an injected file", name), 12990 []logger.MsgData{tracker.MsgData(js_lexer.RangeOfIdentifier(where.source, where.loc), 12991 fmt.Sprintf("The symbol %q was exported from %q here:", name, where.source.PrettyPath))}) 12992 } 12993 } 12994 } 12995 12996 // Substitute user-specified defines for unbound or injected symbols 12997 methodCallMustBeReplacedWithUndefined := false 12998 if p.symbols[e.Ref.InnerIndex].Kind.IsUnboundOrInjected() && !result.isInsideWithScope && e != p.deleteTarget { 12999 if data, ok := p.options.defines.IdentifierDefines[name]; ok { 13000 if data.DefineExpr != nil { 13001 new := p.instantiateDefineExpr(expr.Loc, *data.DefineExpr, identifierOpts{ 13002 assignTarget: in.assignTarget, 13003 isCallTarget: isCallTarget, 13004 isDeleteTarget: isDeleteTarget, 13005 }) 13006 if in.assignTarget == js_ast.AssignTargetNone || defineValueCanBeUsedInAssignTarget(new.Data) { 13007 p.ignoreUsage(e.Ref) 13008 return new, exprOut{} 13009 } else { 13010 p.logAssignToDefine(js_lexer.RangeOfIdentifier(p.source, expr.Loc), name, js_ast.Expr{}) 13011 } 13012 } 13013 13014 // Copy the side effect flags over in case this expression is unused 13015 if data.Flags.Has(config.CanBeRemovedIfUnused) { 13016 e.CanBeRemovedIfUnused = true 13017 } 13018 if data.Flags.Has(config.CallCanBeUnwrappedIfUnused) && !p.options.ignoreDCEAnnotations { 13019 e.CallCanBeUnwrappedIfUnused = true 13020 } 13021 if data.Flags.Has(config.MethodCallsMustBeReplacedWithUndefined) { 13022 methodCallMustBeReplacedWithUndefined = true 13023 } 13024 } 13025 } 13026 13027 return p.handleIdentifier(expr.Loc, e, identifierOpts{ 13028 assignTarget: in.assignTarget, 13029 isCallTarget: isCallTarget, 13030 isDeleteTarget: isDeleteTarget, 13031 wasOriginallyIdentifier: true, 13032 }), exprOut{ 13033 methodCallMustBeReplacedWithUndefined: methodCallMustBeReplacedWithUndefined, 13034 } 13035 13036 case *js_ast.EJSXElement: 13037 propsLoc := expr.Loc 13038 13039 // Resolving the location index to a specific line and column in 13040 // development mode is not too expensive because we seek from the 13041 // previous JSX element. It amounts to at most a single additional 13042 // scan over the source code. Note that this has to happen before 13043 // we visit anything about this JSX element to make sure that we 13044 // only ever need to scan forward, not backward. 13045 var jsxSourceLine int 13046 var jsxSourceColumn int 13047 if p.options.jsx.Development && p.options.jsx.AutomaticRuntime { 13048 for p.jsxSourceLoc < int(propsLoc.Start) { 13049 r, size := utf8.DecodeRuneInString(p.source.Contents[p.jsxSourceLoc:]) 13050 p.jsxSourceLoc += size 13051 if r == '\n' || r == '\r' || r == '\u2028' || r == '\u2029' { 13052 if r == '\r' && p.jsxSourceLoc < len(p.source.Contents) && p.source.Contents[p.jsxSourceLoc] == '\n' { 13053 p.jsxSourceLoc++ // Handle Windows-style CRLF newlines 13054 } 13055 p.jsxSourceLine++ 13056 p.jsxSourceColumn = 0 13057 } else { 13058 // Babel and TypeScript count columns in UTF-16 code units 13059 if r < 0xFFFF { 13060 p.jsxSourceColumn++ 13061 } else { 13062 p.jsxSourceColumn += 2 13063 } 13064 } 13065 } 13066 jsxSourceLine = p.jsxSourceLine 13067 jsxSourceColumn = p.jsxSourceColumn 13068 } 13069 13070 if e.TagOrNil.Data != nil { 13071 propsLoc = e.TagOrNil.Loc 13072 e.TagOrNil = p.visitExpr(e.TagOrNil) 13073 p.warnAboutImportNamespaceCall(e.TagOrNil, exprKindJSXTag) 13074 } 13075 13076 // Visit properties 13077 hasSpread := false 13078 for i, property := range e.Properties { 13079 if property.Kind == js_ast.PropertySpread { 13080 hasSpread = true 13081 } else { 13082 if mangled, ok := property.Key.Data.(*js_ast.ENameOfSymbol); ok { 13083 mangled.Ref = p.symbolForMangledProp(p.loadNameFromRef(mangled.Ref)) 13084 } else { 13085 property.Key = p.visitExpr(property.Key) 13086 } 13087 } 13088 if property.ValueOrNil.Data != nil { 13089 property.ValueOrNil = p.visitExpr(property.ValueOrNil) 13090 } 13091 if property.InitializerOrNil.Data != nil { 13092 property.InitializerOrNil = p.visitExpr(property.InitializerOrNil) 13093 } 13094 e.Properties[i] = property 13095 } 13096 13097 // "{a, ...{b, c}, d}" => "{a, b, c, d}" 13098 if p.options.minifySyntax && hasSpread { 13099 e.Properties = js_ast.MangleObjectSpread(e.Properties) 13100 } 13101 13102 // Visit children 13103 if len(e.NullableChildren) > 0 { 13104 for i, childOrNil := range e.NullableChildren { 13105 if childOrNil.Data != nil { 13106 e.NullableChildren[i] = p.visitExpr(childOrNil) 13107 } 13108 } 13109 } 13110 13111 if p.options.jsx.Preserve { 13112 // If the tag is an identifier, mark it as needing to be upper-case 13113 switch tag := e.TagOrNil.Data.(type) { 13114 case *js_ast.EIdentifier: 13115 p.symbols[tag.Ref.InnerIndex].Flags |= ast.MustStartWithCapitalLetterForJSX 13116 13117 case *js_ast.EImportIdentifier: 13118 p.symbols[tag.Ref.InnerIndex].Flags |= ast.MustStartWithCapitalLetterForJSX 13119 } 13120 } else { 13121 // Remove any nil children in the array (in place) before iterating over it 13122 children := e.NullableChildren 13123 { 13124 end := 0 13125 for _, childOrNil := range children { 13126 if childOrNil.Data != nil { 13127 children[end] = childOrNil 13128 end++ 13129 } 13130 } 13131 children = children[:end] 13132 } 13133 13134 // A missing tag is a fragment 13135 if e.TagOrNil.Data == nil { 13136 if p.options.jsx.AutomaticRuntime { 13137 e.TagOrNil = p.importJSXSymbol(expr.Loc, JSXImportFragment) 13138 } else { 13139 e.TagOrNil = p.instantiateDefineExpr(expr.Loc, p.options.jsx.Fragment, identifierOpts{ 13140 wasOriginallyIdentifier: true, 13141 matchAgainstDefines: true, // Allow defines to rewrite the JSX fragment factory 13142 }) 13143 } 13144 } 13145 13146 shouldUseCreateElement := !p.options.jsx.AutomaticRuntime 13147 if !shouldUseCreateElement { 13148 // Even for runtime="automatic", <div {...props} key={key} /> is special cased to createElement 13149 // See https://github.com/babel/babel/blob/e482c763466ba3f44cb9e3467583b78b7f030b4a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L352 13150 seenPropsSpread := false 13151 for _, property := range e.Properties { 13152 if seenPropsSpread && property.Kind == js_ast.PropertyField { 13153 if str, ok := property.Key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "key") { 13154 shouldUseCreateElement = true 13155 break 13156 } 13157 } else if property.Kind == js_ast.PropertySpread { 13158 seenPropsSpread = true 13159 } 13160 } 13161 } 13162 13163 if shouldUseCreateElement { 13164 // Arguments to createElement() 13165 args := []js_ast.Expr{e.TagOrNil} 13166 if len(e.Properties) > 0 { 13167 args = append(args, p.lowerObjectSpread(propsLoc, &js_ast.EObject{ 13168 Properties: e.Properties, 13169 IsSingleLine: e.IsTagSingleLine, 13170 })) 13171 } else { 13172 args = append(args, js_ast.Expr{Loc: propsLoc, Data: js_ast.ENullShared}) 13173 } 13174 if len(children) > 0 { 13175 args = append(args, children...) 13176 } 13177 13178 // Call createElement() 13179 var target js_ast.Expr 13180 kind := js_ast.NormalCall 13181 if p.options.jsx.AutomaticRuntime { 13182 target = p.importJSXSymbol(expr.Loc, JSXImportCreateElement) 13183 } else { 13184 target = p.instantiateDefineExpr(expr.Loc, p.options.jsx.Factory, identifierOpts{ 13185 wasOriginallyIdentifier: true, 13186 matchAgainstDefines: true, // Allow defines to rewrite the JSX factory 13187 }) 13188 if js_ast.IsPropertyAccess(target) { 13189 kind = js_ast.TargetWasOriginallyPropertyAccess 13190 } 13191 p.warnAboutImportNamespaceCall(target, exprKindCall) 13192 } 13193 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 13194 Target: target, 13195 Args: args, 13196 CloseParenLoc: e.CloseLoc, 13197 IsMultiLine: !e.IsTagSingleLine, 13198 Kind: kind, 13199 13200 // Enable tree shaking 13201 CanBeUnwrappedIfUnused: !p.options.ignoreDCEAnnotations && !p.options.jsx.SideEffects, 13202 }}, exprOut{} 13203 } else { 13204 // Arguments to jsx() 13205 args := []js_ast.Expr{e.TagOrNil} 13206 13207 // Props argument 13208 properties := make([]js_ast.Property, 0, len(e.Properties)+1) 13209 13210 // For jsx(), "key" is passed in as a separate argument, so filter it out 13211 // from the props here. Also, check for __source and __self, which might have 13212 // been added by some upstream plugin. Their presence here would represent a 13213 // configuration error. 13214 hasKey := false 13215 keyProperty := js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared} 13216 for _, property := range e.Properties { 13217 if str, ok := property.Key.Data.(*js_ast.EString); ok { 13218 propName := helpers.UTF16ToString(str.Value) 13219 switch propName { 13220 case "key": 13221 if boolean, ok := property.ValueOrNil.Data.(*js_ast.EBoolean); ok && boolean.Value && property.Flags.Has(js_ast.PropertyWasShorthand) { 13222 r := js_lexer.RangeOfIdentifier(p.source, property.Loc) 13223 msg := logger.Msg{ 13224 Kind: logger.Error, 13225 Data: p.tracker.MsgData(r, "Please provide an explicit value for \"key\":"), 13226 Notes: []logger.MsgData{{Text: "Using \"key\" as a shorthand for \"key={true}\" is not allowed when using React's \"automatic\" JSX transform."}}, 13227 } 13228 msg.Data.Location.Suggestion = "key={true}" 13229 p.log.AddMsg(msg) 13230 } else { 13231 keyProperty = property.ValueOrNil 13232 hasKey = true 13233 } 13234 continue 13235 13236 case "__source", "__self": 13237 r := js_lexer.RangeOfIdentifier(p.source, property.Loc) 13238 p.log.AddErrorWithNotes(&p.tracker, r, 13239 fmt.Sprintf("Duplicate \"%s\" prop found:", propName), 13240 []logger.MsgData{{Text: "Both \"__source\" and \"__self\" are set automatically by esbuild when using React's \"automatic\" JSX transform. " + 13241 "This duplicate prop may have come from a plugin."}}) 13242 continue 13243 } 13244 } 13245 properties = append(properties, property) 13246 } 13247 13248 isStaticChildren := len(children) > 1 13249 13250 // Children are passed in as an explicit prop 13251 if len(children) > 0 { 13252 childrenValue := children[0] 13253 13254 if len(children) > 1 { 13255 childrenValue.Data = &js_ast.EArray{Items: children} 13256 } else if _, ok := childrenValue.Data.(*js_ast.ESpread); ok { 13257 // TypeScript considers spread children to be static, but Babel considers 13258 // it to be an error ("Spread children are not supported in React."). 13259 // We'll follow TypeScript's behavior here because spread children may be 13260 // valid with non-React source runtimes. 13261 childrenValue.Data = &js_ast.EArray{Items: []js_ast.Expr{childrenValue}} 13262 isStaticChildren = true 13263 } 13264 13265 properties = append(properties, js_ast.Property{ 13266 Key: js_ast.Expr{ 13267 Data: &js_ast.EString{Value: helpers.StringToUTF16("children")}, 13268 Loc: childrenValue.Loc, 13269 }, 13270 ValueOrNil: childrenValue, 13271 Kind: js_ast.PropertyField, 13272 Loc: childrenValue.Loc, 13273 }) 13274 } 13275 13276 args = append(args, p.lowerObjectSpread(propsLoc, &js_ast.EObject{ 13277 Properties: properties, 13278 IsSingleLine: e.IsTagSingleLine, 13279 })) 13280 13281 // "key" 13282 if hasKey || p.options.jsx.Development { 13283 args = append(args, keyProperty) 13284 } 13285 13286 if p.options.jsx.Development { 13287 // "isStaticChildren" 13288 args = append(args, js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EBoolean{Value: isStaticChildren}}) 13289 13290 // "__source" 13291 args = append(args, js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EObject{ 13292 Properties: []js_ast.Property{ 13293 { 13294 Kind: js_ast.PropertyField, 13295 Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("fileName")}}, 13296 ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(p.source.PrettyPath)}}, 13297 }, 13298 { 13299 Kind: js_ast.PropertyField, 13300 Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("lineNumber")}}, 13301 ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(jsxSourceLine + 1)}}, // 1-based lines 13302 }, 13303 { 13304 Kind: js_ast.PropertyField, 13305 Key: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("columnNumber")}}, 13306 ValueOrNil: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(jsxSourceColumn + 1)}}, // 1-based columns 13307 }, 13308 }, 13309 }}) 13310 13311 // "__self" 13312 __self := js_ast.Expr{Loc: expr.Loc, Data: js_ast.EThisShared} 13313 { 13314 if p.fnOnlyDataVisit.shouldReplaceThisWithInnerClassNameRef { 13315 // Substitute "this" if we're inside a static class context 13316 p.recordUsage(*p.fnOnlyDataVisit.innerClassNameRef) 13317 __self.Data = &js_ast.EIdentifier{Ref: *p.fnOnlyDataVisit.innerClassNameRef} 13318 } else if !p.fnOnlyDataVisit.isThisNested && p.options.mode != config.ModePassThrough { 13319 // Replace top-level "this" with "undefined" if there's an output format 13320 __self.Data = js_ast.EUndefinedShared 13321 } else if p.fnOrArrowDataVisit.isDerivedClassCtor { 13322 // We can't use "this" here in case it comes before "super()" 13323 __self.Data = js_ast.EUndefinedShared 13324 } 13325 } 13326 if _, ok := __self.Data.(*js_ast.EUndefined); !ok { 13327 // Omit "__self" entirely if it's undefined 13328 args = append(args, __self) 13329 } 13330 } 13331 13332 jsx := JSXImportJSX 13333 if isStaticChildren { 13334 jsx = JSXImportJSXS 13335 } 13336 13337 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 13338 Target: p.importJSXSymbol(expr.Loc, jsx), 13339 Args: args, 13340 CloseParenLoc: e.CloseLoc, 13341 IsMultiLine: !e.IsTagSingleLine, 13342 13343 // Enable tree shaking 13344 CanBeUnwrappedIfUnused: !p.options.ignoreDCEAnnotations && !p.options.jsx.SideEffects, 13345 }}, exprOut{} 13346 } 13347 } 13348 13349 case *js_ast.ETemplate: 13350 if e.LegacyOctalLoc.Start > 0 { 13351 p.log.AddError(&p.tracker, p.source.RangeOfLegacyOctalEscape(e.LegacyOctalLoc), 13352 "Legacy octal escape sequences cannot be used in template literals") 13353 } 13354 13355 var tagThisFunc func() js_ast.Expr 13356 var tagWrapFunc func(js_ast.Expr) js_ast.Expr 13357 13358 if e.TagOrNil.Data != nil { 13359 // Capture the value for "this" if the tag is a lowered optional chain. 13360 // We'll need to manually apply this value later to preserve semantics. 13361 tagIsLoweredOptionalChain := false 13362 if p.options.unsupportedJSFeatures.Has(compat.OptionalChain) { 13363 switch target := e.TagOrNil.Data.(type) { 13364 case *js_ast.EDot: 13365 tagIsLoweredOptionalChain = target.OptionalChain != js_ast.OptionalChainNone 13366 case *js_ast.EIndex: 13367 tagIsLoweredOptionalChain = target.OptionalChain != js_ast.OptionalChainNone 13368 } 13369 } 13370 13371 p.templateTag = e.TagOrNil.Data 13372 tag, tagOut := p.visitExprInOut(e.TagOrNil, exprIn{storeThisArgForParentOptionalChain: tagIsLoweredOptionalChain}) 13373 e.TagOrNil = tag 13374 tagThisFunc = tagOut.thisArgFunc 13375 tagWrapFunc = tagOut.thisArgWrapFunc 13376 13377 // Copy the call side effect flag over if this is a known target 13378 if id, ok := tag.Data.(*js_ast.EIdentifier); ok && p.symbols[id.Ref.InnerIndex].Flags.Has(ast.CallCanBeUnwrappedIfUnused) { 13379 e.CanBeUnwrappedIfUnused = true 13380 } 13381 13382 // The value of "this" must be manually preserved for private member 13383 // accesses inside template tag expressions such as "this.#foo``". 13384 // The private member "this.#foo" must see the value of "this". 13385 if target, loc, private := p.extractPrivateIndex(e.TagOrNil); private != nil { 13386 // "foo.#bar`123`" => "__privateGet(_a = foo, #bar).bind(_a)`123`" 13387 targetFunc, targetWrapFunc := p.captureValueWithPossibleSideEffects(target.Loc, 2, target, valueCouldBeMutated) 13388 e.TagOrNil = targetWrapFunc(js_ast.Expr{Loc: target.Loc, Data: &js_ast.ECall{ 13389 Target: js_ast.Expr{Loc: target.Loc, Data: &js_ast.EDot{ 13390 Target: p.lowerPrivateGet(targetFunc(), loc, private), 13391 Name: "bind", 13392 NameLoc: target.Loc, 13393 }}, 13394 Args: []js_ast.Expr{targetFunc()}, 13395 Kind: js_ast.TargetWasOriginallyPropertyAccess, 13396 }}) 13397 } 13398 } 13399 13400 for i, part := range e.Parts { 13401 e.Parts[i].Value = p.visitExpr(part.Value) 13402 } 13403 13404 // When mangling, inline string values into the template literal. Note that 13405 // it may no longer be a template literal after this point (it may turn into 13406 // a plain string literal instead). 13407 if p.shouldFoldTypeScriptConstantExpressions || p.options.minifySyntax { 13408 expr = js_ast.InlinePrimitivesIntoTemplate(expr.Loc, e) 13409 } 13410 13411 shouldLowerTemplateLiteral := p.options.unsupportedJSFeatures.Has(compat.TemplateLiteral) 13412 13413 // If the tag was originally an optional chaining property access, then 13414 // we'll need to lower this template literal as well to preserve the value 13415 // for "this". 13416 if tagThisFunc != nil { 13417 shouldLowerTemplateLiteral = true 13418 } 13419 13420 // Lower tagged template literals that include "</script" 13421 // since we won't be able to escape it without lowering it 13422 if !shouldLowerTemplateLiteral && !p.options.unsupportedJSFeatures.Has(compat.InlineScript) && e.TagOrNil.Data != nil { 13423 if containsClosingScriptTag(e.HeadRaw) { 13424 shouldLowerTemplateLiteral = true 13425 } else { 13426 for _, part := range e.Parts { 13427 if containsClosingScriptTag(part.TailRaw) { 13428 shouldLowerTemplateLiteral = true 13429 break 13430 } 13431 } 13432 } 13433 } 13434 13435 // Convert template literals to older syntax if this is still a template literal 13436 if shouldLowerTemplateLiteral { 13437 if e, ok := expr.Data.(*js_ast.ETemplate); ok { 13438 return p.lowerTemplateLiteral(expr.Loc, e, tagThisFunc, tagWrapFunc), exprOut{} 13439 } 13440 } 13441 13442 case *js_ast.EBinary: 13443 // The handling of binary expressions is convoluted because we're using 13444 // iteration on the heap instead of recursion on the call stack to avoid 13445 // stack overflow for deeply-nested ASTs. See the comment before the 13446 // definition of "binaryExprVisitor" for details. 13447 v := binaryExprVisitor{ 13448 e: e, 13449 loc: expr.Loc, 13450 in: in, 13451 } 13452 13453 // Everything uses a single stack to reduce allocation overhead. This stack 13454 // should almost always be very small, and almost all visits should reuse 13455 // existing memory without allocating anything. 13456 stackBottom := len(p.binaryExprStack) 13457 13458 // Iterate down into the AST along the left node of the binary operation. 13459 // Continue iterating until we encounter something that's not a binary node. 13460 for { 13461 // Check whether this node is a special case. If it is, a result will be 13462 // provided which ends our iteration. Otherwise, the visitor object will 13463 // be prepared for visiting. 13464 if result := v.checkAndPrepare(p); result.Data != nil { 13465 expr = result 13466 break 13467 } 13468 13469 // Grab the arguments to our nested "visitExprInOut" call for the left 13470 // node. We only care about deeply-nested left nodes because most binary 13471 // operators in JavaScript are left-associative and the problematic edge 13472 // cases we're trying to avoid crashing on have lots of left-associative 13473 // binary operators chained together without parentheses (e.g. "1+2+..."). 13474 left := v.e.Left 13475 leftIn := v.leftIn 13476 leftBinary, ok := left.Data.(*js_ast.EBinary) 13477 13478 // Stop iterating if iteration doesn't apply to the left node. This checks 13479 // the assignment target because "visitExprInOut" has additional behavior 13480 // in that case that we don't want to miss (before the top-level "switch" 13481 // statement). 13482 if !ok || leftIn.assignTarget != js_ast.AssignTargetNone { 13483 v.e.Left, _ = p.visitExprInOut(left, leftIn) 13484 expr = v.visitRightAndFinish(p) 13485 break 13486 } 13487 13488 // Note that we only append to the stack (and therefore allocate memory 13489 // on the heap) when there are nested binary expressions. A single binary 13490 // expression doesn't add anything to the stack. 13491 p.binaryExprStack = append(p.binaryExprStack, v) 13492 v = binaryExprVisitor{ 13493 e: leftBinary, 13494 loc: left.Loc, 13495 in: leftIn, 13496 } 13497 } 13498 13499 // Process all binary operations from the deepest-visited node back toward 13500 // our original top-level binary operation. 13501 for { 13502 n := len(p.binaryExprStack) - 1 13503 if n < stackBottom { 13504 break 13505 } 13506 v := p.binaryExprStack[n] 13507 p.binaryExprStack = p.binaryExprStack[:n] 13508 v.e.Left = expr 13509 expr = v.visitRightAndFinish(p) 13510 } 13511 13512 return expr, exprOut{} 13513 13514 case *js_ast.EDot: 13515 isDeleteTarget := e == p.deleteTarget 13516 isCallTarget := e == p.callTarget 13517 isTemplateTag := e == p.templateTag 13518 13519 // Check both user-specified defines and known globals 13520 if defines, ok := p.options.defines.DotDefines[e.Name]; ok { 13521 for _, define := range defines { 13522 if p.isDotOrIndexDefineMatch(expr, define.Parts) { 13523 // Substitute user-specified defines 13524 if define.Data.DefineExpr != nil { 13525 new := p.instantiateDefineExpr(expr.Loc, *define.Data.DefineExpr, identifierOpts{ 13526 assignTarget: in.assignTarget, 13527 isCallTarget: isCallTarget, 13528 isDeleteTarget: isDeleteTarget, 13529 }) 13530 if in.assignTarget == js_ast.AssignTargetNone || defineValueCanBeUsedInAssignTarget(new.Data) { 13531 // Note: We don't need to "ignoreRef" on the underlying identifier 13532 // because we have only parsed it but not visited it yet 13533 return new, exprOut{} 13534 } else { 13535 r := logger.Range{Loc: expr.Loc, Len: js_lexer.RangeOfIdentifier(p.source, e.NameLoc).End() - expr.Loc.Start} 13536 p.logAssignToDefine(r, "", expr) 13537 } 13538 } 13539 13540 // Copy the side effect flags over in case this expression is unused 13541 if define.Data.Flags.Has(config.CanBeRemovedIfUnused) { 13542 e.CanBeRemovedIfUnused = true 13543 } 13544 if define.Data.Flags.Has(config.CallCanBeUnwrappedIfUnused) && !p.options.ignoreDCEAnnotations { 13545 e.CallCanBeUnwrappedIfUnused = true 13546 } 13547 if define.Data.Flags.Has(config.IsSymbolInstance) { 13548 e.IsSymbolInstance = true 13549 } 13550 break 13551 } 13552 } 13553 } 13554 13555 // Check injected dot names 13556 if names, ok := p.injectedDotNames[e.Name]; ok { 13557 for _, name := range names { 13558 if p.isDotOrIndexDefineMatch(expr, name.parts) { 13559 // Note: We don't need to "ignoreRef" on the underlying identifier 13560 // because we have only parsed it but not visited it yet 13561 return p.instantiateInjectDotName(expr.Loc, name, in.assignTarget), exprOut{} 13562 } 13563 } 13564 } 13565 13566 // Track ".then().catch()" chains 13567 if isCallTarget && p.thenCatchChain.nextTarget == e { 13568 if e.Name == "catch" { 13569 p.thenCatchChain = thenCatchChain{ 13570 nextTarget: e.Target.Data, 13571 hasCatch: true, 13572 catchLoc: e.NameLoc, 13573 } 13574 } else if e.Name == "then" { 13575 p.thenCatchChain = thenCatchChain{ 13576 nextTarget: e.Target.Data, 13577 hasCatch: p.thenCatchChain.hasCatch || p.thenCatchChain.hasMultipleArgs, 13578 catchLoc: p.thenCatchChain.catchLoc, 13579 } 13580 } 13581 } 13582 13583 p.dotOrIndexTarget = e.Target.Data 13584 target, out := p.visitExprInOut(e.Target, exprIn{ 13585 hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue, 13586 }) 13587 e.Target = target 13588 13589 // Lower "super.prop" if necessary 13590 if e.OptionalChain == js_ast.OptionalChainNone && in.assignTarget == js_ast.AssignTargetNone && 13591 !isCallTarget && p.shouldLowerSuperPropertyAccess(e.Target) { 13592 // "super.foo" => "__superGet('foo')" 13593 key := js_ast.Expr{Loc: e.NameLoc, Data: &js_ast.EString{Value: helpers.StringToUTF16(e.Name)}} 13594 value := p.lowerSuperPropertyGet(expr.Loc, key) 13595 if isTemplateTag { 13596 value.Data = &js_ast.ECall{ 13597 Target: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EDot{ 13598 Target: value, 13599 Name: "bind", 13600 NameLoc: value.Loc, 13601 }}, 13602 Args: []js_ast.Expr{{Loc: value.Loc, Data: js_ast.EThisShared}}, 13603 Kind: js_ast.TargetWasOriginallyPropertyAccess, 13604 } 13605 } 13606 return value, exprOut{} 13607 } 13608 13609 // Lower optional chaining if we're the top of the chain 13610 containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart || 13611 (e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain) 13612 if containsOptionalChain && !in.hasChainParent { 13613 return p.lowerOptionalChain(expr, in, out) 13614 } 13615 13616 // Potentially rewrite this property access 13617 out = exprOut{ 13618 childContainsOptionalChain: containsOptionalChain, 13619 methodCallMustBeReplacedWithUndefined: out.methodCallMustBeReplacedWithUndefined, 13620 thisArgFunc: out.thisArgFunc, 13621 thisArgWrapFunc: out.thisArgWrapFunc, 13622 } 13623 if !in.hasChainParent { 13624 out.thisArgFunc = nil 13625 out.thisArgWrapFunc = nil 13626 } 13627 if e.OptionalChain == js_ast.OptionalChainNone { 13628 if value, ok := p.maybeRewritePropertyAccess(expr.Loc, in.assignTarget, 13629 isDeleteTarget, e.Target, e.Name, e.NameLoc, isCallTarget, isTemplateTag, false); ok { 13630 return value, out 13631 } 13632 } 13633 return js_ast.Expr{Loc: expr.Loc, Data: e}, out 13634 13635 case *js_ast.EIndex: 13636 isCallTarget := e == p.callTarget 13637 isTemplateTag := e == p.templateTag 13638 isDeleteTarget := e == p.deleteTarget 13639 13640 // Check both user-specified defines and known globals 13641 if str, ok := e.Index.Data.(*js_ast.EString); ok { 13642 if defines, ok := p.options.defines.DotDefines[helpers.UTF16ToString(str.Value)]; ok { 13643 for _, define := range defines { 13644 if p.isDotOrIndexDefineMatch(expr, define.Parts) { 13645 // Substitute user-specified defines 13646 if define.Data.DefineExpr != nil { 13647 new := p.instantiateDefineExpr(expr.Loc, *define.Data.DefineExpr, identifierOpts{ 13648 assignTarget: in.assignTarget, 13649 isCallTarget: isCallTarget, 13650 isDeleteTarget: isDeleteTarget, 13651 }) 13652 if in.assignTarget == js_ast.AssignTargetNone || defineValueCanBeUsedInAssignTarget(new.Data) { 13653 // Note: We don't need to "ignoreRef" on the underlying identifier 13654 // because we have only parsed it but not visited it yet 13655 return new, exprOut{} 13656 } else { 13657 r := logger.Range{Loc: expr.Loc} 13658 afterIndex := logger.Loc{Start: p.source.RangeOfString(e.Index.Loc).End()} 13659 if closeBracket := p.source.RangeOfOperatorAfter(afterIndex, "]"); closeBracket.Len > 0 { 13660 r.Len = closeBracket.End() - r.Loc.Start 13661 } 13662 p.logAssignToDefine(r, "", expr) 13663 } 13664 } 13665 13666 // Copy the side effect flags over in case this expression is unused 13667 if define.Data.Flags.Has(config.CanBeRemovedIfUnused) { 13668 e.CanBeRemovedIfUnused = true 13669 } 13670 if define.Data.Flags.Has(config.CallCanBeUnwrappedIfUnused) && !p.options.ignoreDCEAnnotations { 13671 e.CallCanBeUnwrappedIfUnused = true 13672 } 13673 if define.Data.Flags.Has(config.IsSymbolInstance) { 13674 e.IsSymbolInstance = true 13675 } 13676 break 13677 } 13678 } 13679 } 13680 } 13681 13682 // "a['b']" => "a.b" 13683 if p.options.minifySyntax { 13684 if str, ok := e.Index.Data.(*js_ast.EString); ok && js_ast.IsIdentifierUTF16(str.Value) { 13685 dot := p.dotOrMangledPropParse(e.Target, js_lexer.MaybeSubstring{String: helpers.UTF16ToString(str.Value)}, e.Index.Loc, e.OptionalChain, wasOriginallyIndex) 13686 if isCallTarget { 13687 p.callTarget = dot 13688 } 13689 if isTemplateTag { 13690 p.templateTag = dot 13691 } 13692 if isDeleteTarget { 13693 p.deleteTarget = dot 13694 } 13695 return p.visitExprInOut(js_ast.Expr{Loc: expr.Loc, Data: dot}, in) 13696 } 13697 } 13698 13699 p.dotOrIndexTarget = e.Target.Data 13700 target, out := p.visitExprInOut(e.Target, exprIn{ 13701 hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue, 13702 }) 13703 e.Target = target 13704 13705 // Special-case private identifiers 13706 if private, ok := e.Index.Data.(*js_ast.EPrivateIdentifier); ok { 13707 name := p.loadNameFromRef(private.Ref) 13708 result := p.findSymbol(e.Index.Loc, name) 13709 private.Ref = result.ref 13710 13711 // Unlike regular identifiers, there are no unbound private identifiers 13712 kind := p.symbols[result.ref.InnerIndex].Kind 13713 if !kind.IsPrivate() { 13714 r := logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} 13715 p.log.AddError(&p.tracker, r, fmt.Sprintf("Private name %q must be declared in an enclosing class", name)) 13716 } else { 13717 var r logger.Range 13718 var text string 13719 if in.assignTarget != js_ast.AssignTargetNone && (kind == ast.SymbolPrivateMethod || kind == ast.SymbolPrivateStaticMethod) { 13720 r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} 13721 text = fmt.Sprintf("Writing to read-only method %q will throw", name) 13722 } else if in.assignTarget != js_ast.AssignTargetNone && (kind == ast.SymbolPrivateGet || kind == ast.SymbolPrivateStaticGet) { 13723 r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} 13724 text = fmt.Sprintf("Writing to getter-only property %q will throw", name) 13725 } else if in.assignTarget != js_ast.AssignTargetReplace && (kind == ast.SymbolPrivateSet || kind == ast.SymbolPrivateStaticSet) { 13726 r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} 13727 text = fmt.Sprintf("Reading from setter-only property %q will throw", name) 13728 } 13729 if text != "" { 13730 kind := logger.Warning 13731 if p.suppressWarningsAboutWeirdCode { 13732 kind = logger.Debug 13733 } 13734 p.log.AddID(logger.MsgID_JS_PrivateNameWillThrow, kind, &p.tracker, r, text) 13735 } 13736 } 13737 13738 // Lower private member access only if we're sure the target isn't needed 13739 // for the value of "this" for a call expression. All other cases will be 13740 // taken care of by the enclosing call expression. 13741 if p.privateSymbolNeedsToBeLowered(private) && e.OptionalChain == js_ast.OptionalChainNone && 13742 in.assignTarget == js_ast.AssignTargetNone && !isCallTarget && !isTemplateTag { 13743 // "foo.#bar" => "__privateGet(foo, #bar)" 13744 return p.lowerPrivateGet(e.Target, e.Index.Loc, private), exprOut{} 13745 } 13746 } else { 13747 e.Index, _ = p.visitExprInOut(e.Index, exprIn{ 13748 shouldMangleStringsAsProps: true, 13749 }) 13750 } 13751 13752 // Lower "super[prop]" if necessary 13753 if e.OptionalChain == js_ast.OptionalChainNone && in.assignTarget == js_ast.AssignTargetNone && 13754 !isCallTarget && p.shouldLowerSuperPropertyAccess(e.Target) { 13755 // "super[foo]" => "__superGet(foo)" 13756 value := p.lowerSuperPropertyGet(expr.Loc, e.Index) 13757 if isTemplateTag { 13758 value.Data = &js_ast.ECall{ 13759 Target: js_ast.Expr{Loc: value.Loc, Data: &js_ast.EDot{ 13760 Target: value, 13761 Name: "bind", 13762 NameLoc: value.Loc, 13763 }}, 13764 Args: []js_ast.Expr{{Loc: value.Loc, Data: js_ast.EThisShared}}, 13765 Kind: js_ast.TargetWasOriginallyPropertyAccess, 13766 } 13767 } 13768 return value, exprOut{} 13769 } 13770 13771 // Lower optional chaining if we're the top of the chain 13772 containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart || 13773 (e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain) 13774 if containsOptionalChain && !in.hasChainParent { 13775 return p.lowerOptionalChain(expr, in, out) 13776 } 13777 13778 // Potentially rewrite this property access 13779 out = exprOut{ 13780 childContainsOptionalChain: containsOptionalChain, 13781 methodCallMustBeReplacedWithUndefined: out.methodCallMustBeReplacedWithUndefined, 13782 thisArgFunc: out.thisArgFunc, 13783 thisArgWrapFunc: out.thisArgWrapFunc, 13784 } 13785 if !in.hasChainParent { 13786 out.thisArgFunc = nil 13787 out.thisArgWrapFunc = nil 13788 } 13789 if str, ok := e.Index.Data.(*js_ast.EString); ok && e.OptionalChain == js_ast.OptionalChainNone { 13790 preferQuotedKey := !p.options.minifySyntax 13791 if value, ok := p.maybeRewritePropertyAccess(expr.Loc, in.assignTarget, isDeleteTarget, 13792 e.Target, helpers.UTF16ToString(str.Value), e.Index.Loc, isCallTarget, isTemplateTag, preferQuotedKey); ok { 13793 return value, out 13794 } 13795 } 13796 13797 // Create an error for assigning to an import namespace when bundling. Even 13798 // though this is a run-time error, we make it a compile-time error when 13799 // bundling because scope hoisting means these will no longer be run-time 13800 // errors. 13801 if p.options.mode == config.ModeBundle && (in.assignTarget != js_ast.AssignTargetNone || isDeleteTarget) { 13802 if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok && p.symbols[id.Ref.InnerIndex].Kind == ast.SymbolImport { 13803 r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) 13804 p.log.AddErrorWithNotes(&p.tracker, r, 13805 fmt.Sprintf("Cannot assign to property on import %q", p.symbols[id.Ref.InnerIndex].OriginalName), 13806 []logger.MsgData{{Text: "Imports are immutable in JavaScript. " + 13807 "To modify the value of this import, you must export a setter function in the " + 13808 "imported file and then import and call that function here instead."}}) 13809 13810 } 13811 } 13812 13813 if p.options.minifySyntax { 13814 switch index := e.Index.Data.(type) { 13815 case *js_ast.EString: 13816 // "a['x' + 'y']" => "a.xy" (this is done late to allow for constant folding) 13817 if js_ast.IsIdentifierUTF16(index.Value) { 13818 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EDot{ 13819 Target: e.Target, 13820 Name: helpers.UTF16ToString(index.Value), 13821 NameLoc: e.Index.Loc, 13822 OptionalChain: e.OptionalChain, 13823 CanBeRemovedIfUnused: e.CanBeRemovedIfUnused, 13824 CallCanBeUnwrappedIfUnused: e.CallCanBeUnwrappedIfUnused, 13825 }}, out 13826 } 13827 13828 // "a['123']" => "a[123]" (this is done late to allow "'123'" to be mangled) 13829 if numberValue, ok := js_ast.StringToEquivalentNumberValue(index.Value); ok { 13830 e.Index.Data = &js_ast.ENumber{Value: numberValue} 13831 } 13832 13833 case *js_ast.ENumber: 13834 // "'abc'[1]" => "'b'" 13835 if target, ok := e.Target.Data.(*js_ast.EString); ok { 13836 if intValue := math.Floor(index.Value); index.Value == intValue && intValue >= 0 && intValue < float64(len(target.Value)) { 13837 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: []uint16{target.Value[int(intValue)]}}}, out 13838 } 13839 } 13840 } 13841 } 13842 13843 return js_ast.Expr{Loc: expr.Loc, Data: e}, out 13844 13845 case *js_ast.EUnary: 13846 switch e.Op { 13847 case js_ast.UnOpTypeof: 13848 e.Value, _ = p.visitExprInOut(e.Value, exprIn{assignTarget: e.Op.UnaryAssignTarget()}) 13849 13850 // Compile-time "typeof" evaluation 13851 if typeof, ok := js_ast.TypeofWithoutSideEffects(e.Value.Data); ok { 13852 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(typeof)}}, exprOut{} 13853 } 13854 13855 case js_ast.UnOpDelete: 13856 // Warn about code that tries to do "delete super.foo" 13857 var superPropLoc logger.Loc 13858 switch e2 := e.Value.Data.(type) { 13859 case *js_ast.EDot: 13860 if _, ok := e2.Target.Data.(*js_ast.ESuper); ok { 13861 superPropLoc = e2.Target.Loc 13862 } 13863 case *js_ast.EIndex: 13864 if _, ok := e2.Target.Data.(*js_ast.ESuper); ok { 13865 superPropLoc = e2.Target.Loc 13866 } 13867 case *js_ast.EIdentifier: 13868 p.markStrictModeFeature(deleteBareName, js_lexer.RangeOfIdentifier(p.source, e.Value.Loc), "") 13869 } 13870 if superPropLoc.Start != 0 { 13871 r := js_lexer.RangeOfIdentifier(p.source, superPropLoc) 13872 text := "Attempting to delete a property of \"super\" will throw a ReferenceError" 13873 kind := logger.Warning 13874 if p.suppressWarningsAboutWeirdCode { 13875 kind = logger.Debug 13876 } 13877 p.log.AddID(logger.MsgID_JS_DeleteSuperProperty, kind, &p.tracker, r, text) 13878 } 13879 13880 p.deleteTarget = e.Value.Data 13881 value, out := p.visitExprInOut(e.Value, exprIn{hasChainParent: true}) 13882 e.Value = value 13883 13884 // Lower optional chaining if present since we're guaranteed to be the 13885 // end of the chain 13886 if out.childContainsOptionalChain { 13887 return p.lowerOptionalChain(expr, in, out) 13888 } 13889 13890 default: 13891 e.Value, _ = p.visitExprInOut(e.Value, exprIn{assignTarget: e.Op.UnaryAssignTarget()}) 13892 13893 // Post-process the unary expression 13894 switch e.Op { 13895 case js_ast.UnOpNot: 13896 if p.options.minifySyntax { 13897 e.Value = p.astHelpers.SimplifyBooleanExpr(e.Value) 13898 } 13899 13900 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(e.Value.Data); ok && sideEffects == js_ast.NoSideEffects { 13901 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EBoolean{Value: !boolean}}, exprOut{} 13902 } 13903 13904 if p.options.minifySyntax { 13905 if result, ok := js_ast.MaybeSimplifyNot(e.Value); ok { 13906 return result, exprOut{} 13907 } 13908 } 13909 13910 case js_ast.UnOpVoid: 13911 if p.astHelpers.ExprCanBeRemovedIfUnused(e.Value) { 13912 return js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared}, exprOut{} 13913 } 13914 13915 case js_ast.UnOpPos: 13916 if number, ok := js_ast.ToNumberWithoutSideEffects(e.Value.Data); ok { 13917 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: number}}, exprOut{} 13918 } 13919 13920 case js_ast.UnOpNeg: 13921 if number, ok := js_ast.ToNumberWithoutSideEffects(e.Value.Data); ok { 13922 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: -number}}, exprOut{} 13923 } 13924 13925 case js_ast.UnOpCpl: 13926 if p.shouldFoldTypeScriptConstantExpressions || p.options.minifySyntax { 13927 // Minification folds complement operations since they are unlikely to result in larger output 13928 if number, ok := js_ast.ToNumberWithoutSideEffects(e.Value.Data); ok { 13929 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(^js_ast.ToInt32(number))}}, exprOut{} 13930 } 13931 } 13932 13933 //////////////////////////////////////////////////////////////////////////////// 13934 // All assignment operators below here 13935 13936 case js_ast.UnOpPreDec, js_ast.UnOpPreInc, js_ast.UnOpPostDec, js_ast.UnOpPostInc: 13937 if target, loc, private := p.extractPrivateIndex(e.Value); private != nil { 13938 return p.lowerPrivateSetUnOp(target, loc, private, e.Op), exprOut{} 13939 } 13940 if property := p.extractSuperProperty(e.Value); property.Data != nil { 13941 e.Value = p.callSuperPropertyWrapper(expr.Loc, property) 13942 } 13943 } 13944 } 13945 13946 // "-(a, b)" => "a, -b" 13947 if p.options.minifySyntax && e.Op != js_ast.UnOpDelete && e.Op != js_ast.UnOpTypeof { 13948 if comma, ok := e.Value.Data.(*js_ast.EBinary); ok && comma.Op == js_ast.BinOpComma { 13949 return js_ast.JoinWithComma(comma.Left, js_ast.Expr{ 13950 Loc: comma.Right.Loc, 13951 Data: &js_ast.EUnary{ 13952 Op: e.Op, 13953 Value: comma.Right, 13954 }, 13955 }), exprOut{} 13956 } 13957 } 13958 13959 case *js_ast.EIf: 13960 e.Test = p.visitExpr(e.Test) 13961 13962 if p.options.minifySyntax { 13963 e.Test = p.astHelpers.SimplifyBooleanExpr(e.Test) 13964 } 13965 13966 // Propagate these flags into the branches 13967 childIn := exprIn{ 13968 shouldMangleStringsAsProps: in.shouldMangleStringsAsProps, 13969 } 13970 13971 // Fold constants 13972 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(e.Test.Data); !ok { 13973 e.Yes, _ = p.visitExprInOut(e.Yes, childIn) 13974 e.No, _ = p.visitExprInOut(e.No, childIn) 13975 } else { 13976 // Mark the control flow as dead if the branch is never taken 13977 if boolean { 13978 // "true ? live : dead" 13979 e.Yes, _ = p.visitExprInOut(e.Yes, childIn) 13980 old := p.isControlFlowDead 13981 p.isControlFlowDead = true 13982 e.No, _ = p.visitExprInOut(e.No, childIn) 13983 p.isControlFlowDead = old 13984 13985 if p.options.minifySyntax { 13986 // "(a, true) ? b : c" => "a, b" 13987 if sideEffects == js_ast.CouldHaveSideEffects { 13988 return js_ast.JoinWithComma(p.astHelpers.SimplifyUnusedExpr(e.Test, p.options.unsupportedJSFeatures), e.Yes), exprOut{} 13989 } 13990 13991 return e.Yes, exprOut{} 13992 } 13993 } else { 13994 // "false ? dead : live" 13995 old := p.isControlFlowDead 13996 p.isControlFlowDead = true 13997 e.Yes, _ = p.visitExprInOut(e.Yes, childIn) 13998 p.isControlFlowDead = old 13999 e.No, _ = p.visitExprInOut(e.No, childIn) 14000 14001 if p.options.minifySyntax { 14002 // "(a, false) ? b : c" => "a, c" 14003 if sideEffects == js_ast.CouldHaveSideEffects { 14004 return js_ast.JoinWithComma(p.astHelpers.SimplifyUnusedExpr(e.Test, p.options.unsupportedJSFeatures), e.No), exprOut{} 14005 } 14006 14007 return e.No, exprOut{} 14008 } 14009 } 14010 } 14011 14012 if p.options.minifySyntax { 14013 return p.astHelpers.MangleIfExpr(expr.Loc, e, p.options.unsupportedJSFeatures), exprOut{} 14014 } 14015 14016 case *js_ast.EAwait: 14017 // Silently remove unsupported top-level "await" in dead code branches 14018 if p.fnOrArrowDataVisit.isOutsideFnOrArrow { 14019 if p.isControlFlowDead && (p.options.unsupportedJSFeatures.Has(compat.TopLevelAwait) || !p.options.outputFormat.KeepESMImportExportSyntax()) { 14020 return p.visitExprInOut(e.Value, in) 14021 } else { 14022 p.liveTopLevelAwaitKeyword = logger.Range{Loc: expr.Loc, Len: 5} 14023 p.markSyntaxFeature(compat.TopLevelAwait, logger.Range{Loc: expr.Loc, Len: 5}) 14024 } 14025 } 14026 14027 p.awaitTarget = e.Value.Data 14028 e.Value = p.visitExpr(e.Value) 14029 14030 // "await" expressions turn into "yield" expressions when lowering 14031 return p.maybeLowerAwait(expr.Loc, e), exprOut{} 14032 14033 case *js_ast.EYield: 14034 if e.ValueOrNil.Data != nil { 14035 e.ValueOrNil = p.visitExpr(e.ValueOrNil) 14036 } 14037 14038 // "yield* x" turns into "yield* __yieldStar(x)" when lowering async generator functions 14039 if e.IsStar && p.options.unsupportedJSFeatures.Has(compat.AsyncGenerator) && p.fnOrArrowDataVisit.isGenerator { 14040 e.ValueOrNil = p.callRuntime(expr.Loc, "__yieldStar", []js_ast.Expr{e.ValueOrNil}) 14041 } 14042 14043 case *js_ast.EArray: 14044 if in.assignTarget != js_ast.AssignTargetNone { 14045 if e.CommaAfterSpread.Start != 0 { 14046 p.log.AddError(&p.tracker, logger.Range{Loc: e.CommaAfterSpread, Len: 1}, "Unexpected \",\" after rest pattern") 14047 } 14048 p.markSyntaxFeature(compat.Destructuring, logger.Range{Loc: expr.Loc, Len: 1}) 14049 } 14050 hasSpread := false 14051 for i, item := range e.Items { 14052 switch e2 := item.Data.(type) { 14053 case *js_ast.EMissing: 14054 case *js_ast.ESpread: 14055 e2.Value, _ = p.visitExprInOut(e2.Value, exprIn{assignTarget: in.assignTarget}) 14056 hasSpread = true 14057 case *js_ast.EBinary: 14058 if in.assignTarget != js_ast.AssignTargetNone && e2.Op == js_ast.BinOpAssign { 14059 e2.Left, _ = p.visitExprInOut(e2.Left, exprIn{assignTarget: js_ast.AssignTargetReplace}) 14060 14061 // Propagate the name to keep from the binding into the initializer 14062 if id, ok := e2.Left.Data.(*js_ast.EIdentifier); ok { 14063 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 14064 p.nameToKeepIsFor = e2.Right.Data 14065 } 14066 14067 e2.Right = p.visitExpr(e2.Right) 14068 } else { 14069 item, _ = p.visitExprInOut(item, exprIn{assignTarget: in.assignTarget}) 14070 } 14071 default: 14072 item, _ = p.visitExprInOut(item, exprIn{assignTarget: in.assignTarget}) 14073 } 14074 e.Items[i] = item 14075 } 14076 14077 // "[1, ...[2, 3], 4]" => "[1, 2, 3, 4]" 14078 if p.options.minifySyntax && hasSpread && in.assignTarget == js_ast.AssignTargetNone { 14079 e.Items = js_ast.InlineSpreadsOfArrayLiterals(e.Items) 14080 } 14081 14082 case *js_ast.EObject: 14083 if in.assignTarget != js_ast.AssignTargetNone { 14084 if e.CommaAfterSpread.Start != 0 { 14085 p.log.AddError(&p.tracker, logger.Range{Loc: e.CommaAfterSpread, Len: 1}, "Unexpected \",\" after rest pattern") 14086 } 14087 p.markSyntaxFeature(compat.Destructuring, logger.Range{Loc: expr.Loc, Len: 1}) 14088 } 14089 14090 hasSpread := false 14091 protoRange := logger.Range{} 14092 innerClassNameRef := ast.InvalidRef 14093 14094 for i := range e.Properties { 14095 property := &e.Properties[i] 14096 14097 if property.Kind != js_ast.PropertySpread { 14098 key := property.Key 14099 if mangled, ok := key.Data.(*js_ast.ENameOfSymbol); ok { 14100 mangled.Ref = p.symbolForMangledProp(p.loadNameFromRef(mangled.Ref)) 14101 } else { 14102 key, _ = p.visitExprInOut(property.Key, exprIn{ 14103 shouldMangleStringsAsProps: true, 14104 }) 14105 property.Key = key 14106 } 14107 14108 // Forbid duplicate "__proto__" properties according to the specification 14109 if !property.Flags.Has(js_ast.PropertyIsComputed) && !property.Flags.Has(js_ast.PropertyWasShorthand) && 14110 property.Kind == js_ast.PropertyField && in.assignTarget == js_ast.AssignTargetNone { 14111 if str, ok := key.Data.(*js_ast.EString); ok && helpers.UTF16EqualsString(str.Value, "__proto__") { 14112 r := js_lexer.RangeOfIdentifier(p.source, key.Loc) 14113 if protoRange.Len > 0 { 14114 p.log.AddErrorWithNotes(&p.tracker, r, 14115 "Cannot specify the \"__proto__\" property more than once per object", 14116 []logger.MsgData{p.tracker.MsgData(protoRange, "The earlier \"__proto__\" property is here:")}) 14117 } else { 14118 protoRange = r 14119 } 14120 } 14121 } 14122 14123 // "{['x']: y}" => "{x: y}" 14124 if p.options.minifySyntax && property.Flags.Has(js_ast.PropertyIsComputed) { 14125 if inlined, ok := key.Data.(*js_ast.EInlinedEnum); ok { 14126 switch inlined.Value.Data.(type) { 14127 case *js_ast.EString, *js_ast.ENumber: 14128 key.Data = inlined.Value.Data 14129 property.Key.Data = key.Data 14130 } 14131 } 14132 switch k := key.Data.(type) { 14133 case *js_ast.ENumber, *js_ast.ENameOfSymbol: 14134 property.Flags &= ^js_ast.PropertyIsComputed 14135 case *js_ast.EString: 14136 if !helpers.UTF16EqualsString(k.Value, "__proto__") { 14137 property.Flags &= ^js_ast.PropertyIsComputed 14138 } 14139 } 14140 } 14141 } else { 14142 hasSpread = true 14143 } 14144 14145 // Extract the initializer for expressions like "({ a: b = c } = d)" 14146 if in.assignTarget != js_ast.AssignTargetNone && property.InitializerOrNil.Data == nil && property.ValueOrNil.Data != nil { 14147 if binary, ok := property.ValueOrNil.Data.(*js_ast.EBinary); ok && binary.Op == js_ast.BinOpAssign { 14148 property.InitializerOrNil = binary.Right 14149 property.ValueOrNil = binary.Left 14150 } 14151 } 14152 14153 if property.ValueOrNil.Data != nil { 14154 oldIsInStaticClassContext := p.fnOnlyDataVisit.isInStaticClassContext 14155 oldInnerClassNameRef := p.fnOnlyDataVisit.innerClassNameRef 14156 14157 // If this is an async method and async methods are unsupported, 14158 // generate a temporary variable in case this async method contains a 14159 // "super" property reference. If that happens, the "super" expression 14160 // must be lowered which will need a reference to this object literal. 14161 if property.Kind == js_ast.PropertyMethod && p.options.unsupportedJSFeatures.Has(compat.AsyncAwait) { 14162 if fn, ok := property.ValueOrNil.Data.(*js_ast.EFunction); ok && fn.Fn.IsAsync { 14163 if innerClassNameRef == ast.InvalidRef { 14164 innerClassNameRef = p.generateTempRef(tempRefNeedsDeclareMayBeCapturedInsideLoop, "") 14165 } 14166 p.fnOnlyDataVisit.isInStaticClassContext = true 14167 p.fnOnlyDataVisit.innerClassNameRef = &innerClassNameRef 14168 } 14169 } 14170 14171 // Propagate the name to keep from the property into the value 14172 if str, ok := property.Key.Data.(*js_ast.EString); ok { 14173 p.nameToKeep = helpers.UTF16ToString(str.Value) 14174 p.nameToKeepIsFor = property.ValueOrNil.Data 14175 } 14176 14177 property.ValueOrNil, _ = p.visitExprInOut(property.ValueOrNil, exprIn{ 14178 isMethod: property.Kind.IsMethodDefinition(), 14179 assignTarget: in.assignTarget, 14180 }) 14181 14182 p.fnOnlyDataVisit.innerClassNameRef = oldInnerClassNameRef 14183 p.fnOnlyDataVisit.isInStaticClassContext = oldIsInStaticClassContext 14184 } 14185 14186 if property.InitializerOrNil.Data != nil { 14187 // Propagate the name to keep from the binding into the initializer 14188 if id, ok := property.ValueOrNil.Data.(*js_ast.EIdentifier); ok { 14189 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 14190 p.nameToKeepIsFor = property.InitializerOrNil.Data 14191 } 14192 14193 property.InitializerOrNil = p.visitExpr(property.InitializerOrNil) 14194 } 14195 14196 // "{ '123': 4 }" => "{ 123: 4 }" (this is done late to allow "'123'" to be mangled) 14197 if p.options.minifySyntax { 14198 if str, ok := property.Key.Data.(*js_ast.EString); ok { 14199 if numberValue, ok := js_ast.StringToEquivalentNumberValue(str.Value); ok && numberValue >= 0 { 14200 property.Key.Data = &js_ast.ENumber{Value: numberValue} 14201 } 14202 } 14203 } 14204 } 14205 14206 // Check for and warn about duplicate keys in object literals 14207 if !p.suppressWarningsAboutWeirdCode { 14208 p.warnAboutDuplicateProperties(e.Properties, duplicatePropertiesInObject) 14209 } 14210 14211 if in.assignTarget == js_ast.AssignTargetNone { 14212 // "{a, ...{b, c}, d}" => "{a, b, c, d}" 14213 if p.options.minifySyntax && hasSpread { 14214 e.Properties = js_ast.MangleObjectSpread(e.Properties) 14215 } 14216 14217 // Object expressions represent both object literals and binding patterns. 14218 // Only lower object spread if we're an object literal, not a binding pattern. 14219 value := p.lowerObjectSpread(expr.Loc, e) 14220 14221 // If we generated and used the temporary variable for a lowered "super" 14222 // property reference inside a lowered "async" method, then initialize 14223 // the temporary with this object literal. 14224 if innerClassNameRef != ast.InvalidRef && p.symbols[innerClassNameRef.InnerIndex].UseCountEstimate > 0 { 14225 p.recordUsage(innerClassNameRef) 14226 value = js_ast.Assign(js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: innerClassNameRef}}, value) 14227 } 14228 14229 return value, exprOut{} 14230 } 14231 14232 case *js_ast.EImportCall: 14233 isAwaitTarget := e == p.awaitTarget 14234 isThenCatchTarget := e == p.thenCatchChain.nextTarget && p.thenCatchChain.hasCatch 14235 e.Expr = p.visitExpr(e.Expr) 14236 14237 var assertOrWith *ast.ImportAssertOrWith 14238 var flags ast.ImportRecordFlags 14239 if e.OptionsOrNil.Data != nil { 14240 e.OptionsOrNil = p.visitExpr(e.OptionsOrNil) 14241 14242 // If there's an additional argument, this can't be split because the 14243 // additional argument requires evaluation and our AST nodes can't be 14244 // reused in different places in the AST (e.g. function scopes must be 14245 // unique). Also the additional argument may have side effects and we 14246 // don't currently account for that. 14247 why := "the second argument was not an object literal" 14248 whyLoc := e.OptionsOrNil.Loc 14249 14250 // However, make a special case for an additional argument that contains 14251 // only an "assert" or a "with" clause. In that case we can split this 14252 // AST node. 14253 if object, ok := e.OptionsOrNil.Data.(*js_ast.EObject); ok { 14254 if len(object.Properties) == 1 { 14255 if prop := object.Properties[0]; prop.Kind == js_ast.PropertyField && !prop.Flags.Has(js_ast.PropertyIsComputed) { 14256 if str, ok := prop.Key.Data.(*js_ast.EString); ok && (helpers.UTF16EqualsString(str.Value, "assert") || helpers.UTF16EqualsString(str.Value, "with")) { 14257 keyword := ast.WithKeyword 14258 if helpers.UTF16EqualsString(str.Value, "assert") { 14259 keyword = ast.AssertKeyword 14260 } 14261 if value, ok := prop.ValueOrNil.Data.(*js_ast.EObject); ok { 14262 entries := []ast.AssertOrWithEntry{} 14263 for _, p := range value.Properties { 14264 if p.Kind == js_ast.PropertyField && !p.Flags.Has(js_ast.PropertyIsComputed) { 14265 if key, ok := p.Key.Data.(*js_ast.EString); ok { 14266 if value, ok := p.ValueOrNil.Data.(*js_ast.EString); ok { 14267 entries = append(entries, ast.AssertOrWithEntry{ 14268 Key: key.Value, 14269 KeyLoc: p.Key.Loc, 14270 Value: value.Value, 14271 ValueLoc: p.ValueOrNil.Loc, 14272 PreferQuotedKey: p.Flags.Has(js_ast.PropertyPreferQuotedKey), 14273 }) 14274 if keyword == ast.AssertKeyword && helpers.UTF16EqualsString(key.Value, "type") && helpers.UTF16EqualsString(value.Value, "json") { 14275 flags |= ast.AssertTypeJSON 14276 } 14277 continue 14278 } else { 14279 why = fmt.Sprintf("the value for the property %q was not a string literal", 14280 helpers.UTF16ToString(key.Value)) 14281 whyLoc = p.ValueOrNil.Loc 14282 } 14283 } else { 14284 why = "this property was not a string literal" 14285 whyLoc = p.Key.Loc 14286 } 14287 } else { 14288 why = "this property was invalid" 14289 whyLoc = p.Key.Loc 14290 } 14291 entries = nil 14292 break 14293 } 14294 if entries != nil { 14295 if keyword == ast.AssertKeyword { 14296 p.maybeWarnAboutAssertKeyword(prop.Key.Loc) 14297 } 14298 assertOrWith = &ast.ImportAssertOrWith{ 14299 Entries: entries, 14300 Keyword: keyword, 14301 KeywordLoc: prop.Key.Loc, 14302 InnerOpenBraceLoc: prop.ValueOrNil.Loc, 14303 InnerCloseBraceLoc: value.CloseBraceLoc, 14304 OuterOpenBraceLoc: e.OptionsOrNil.Loc, 14305 OuterCloseBraceLoc: object.CloseBraceLoc, 14306 } 14307 why = "" 14308 } 14309 } else { 14310 why = "the value for \"assert\" was not an object literal" 14311 whyLoc = prop.ValueOrNil.Loc 14312 } 14313 } else { 14314 why = "this property was not called \"assert\" or \"with\"" 14315 whyLoc = prop.Key.Loc 14316 } 14317 } else { 14318 why = "this property was invalid" 14319 whyLoc = prop.Key.Loc 14320 } 14321 } else { 14322 why = "the second argument was not an object literal with a single property called \"assert\" or \"with\"" 14323 whyLoc = e.OptionsOrNil.Loc 14324 } 14325 } 14326 14327 // Handle the case that isn't just an import assertion or attribute clause 14328 if why != "" { 14329 // Only warn when bundling 14330 if p.options.mode == config.ModeBundle { 14331 text := "This \"import()\" was not recognized because " + why 14332 kind := logger.Warning 14333 if p.suppressWarningsAboutWeirdCode { 14334 kind = logger.Debug 14335 } 14336 p.log.AddID(logger.MsgID_JS_UnsupportedDynamicImport, kind, &p.tracker, logger.Range{Loc: whyLoc}, text) 14337 } 14338 14339 // If import assertions and/attributes are both not supported in the 14340 // target platform, then "import()" cannot accept a second argument 14341 // and keeping them would be a syntax error, so we need to get rid of 14342 // them. We can't just not print them because they may have important 14343 // side effects. Attempt to discard them without changing side effects 14344 // and generate an error if that isn't possible. 14345 if p.options.unsupportedJSFeatures.Has(compat.ImportAssertions) && p.options.unsupportedJSFeatures.Has(compat.ImportAttributes) { 14346 if p.astHelpers.ExprCanBeRemovedIfUnused(e.OptionsOrNil) { 14347 e.OptionsOrNil = js_ast.Expr{} 14348 } else { 14349 p.markSyntaxFeature(compat.ImportAttributes, logger.Range{Loc: e.OptionsOrNil.Loc}) 14350 } 14351 } 14352 14353 // Stop now so we don't try to split "?:" expressions below and 14354 // potentially end up with an AST node reused multiple times 14355 break 14356 } 14357 } 14358 14359 return p.maybeTransposeIfExprChain(e.Expr, func(arg js_ast.Expr) js_ast.Expr { 14360 // The argument must be a string 14361 if str, ok := arg.Data.(*js_ast.EString); ok { 14362 // Ignore calls to import() if the control flow is provably dead here. 14363 // We don't want to spend time scanning the required files if they will 14364 // never be used. 14365 if p.isControlFlowDead { 14366 return js_ast.Expr{Loc: arg.Loc, Data: js_ast.ENullShared} 14367 } 14368 14369 importRecordIndex := p.addImportRecord(ast.ImportDynamic, p.source.RangeOfString(arg.Loc), helpers.UTF16ToString(str.Value), assertOrWith, flags) 14370 if isAwaitTarget && p.fnOrArrowDataVisit.tryBodyCount != 0 { 14371 record := &p.importRecords[importRecordIndex] 14372 record.Flags |= ast.HandlesImportErrors 14373 record.ErrorHandlerLoc = p.fnOrArrowDataVisit.tryCatchLoc 14374 } else if isThenCatchTarget { 14375 record := &p.importRecords[importRecordIndex] 14376 record.Flags |= ast.HandlesImportErrors 14377 record.ErrorHandlerLoc = p.thenCatchChain.catchLoc 14378 } 14379 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, importRecordIndex) 14380 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EImportString{ 14381 ImportRecordIndex: importRecordIndex, 14382 CloseParenLoc: e.CloseParenLoc, 14383 }} 14384 } 14385 14386 // Handle glob patterns 14387 if p.options.mode == config.ModeBundle { 14388 if value := p.handleGlobPattern(arg, ast.ImportDynamic, "globImport", assertOrWith); value.Data != nil { 14389 return value 14390 } 14391 } 14392 14393 // Use a debug log so people can see this if they want to 14394 r := js_lexer.RangeOfIdentifier(p.source, expr.Loc) 14395 p.log.AddID(logger.MsgID_JS_UnsupportedDynamicImport, logger.Debug, &p.tracker, r, 14396 "This \"import\" expression will not be bundled because the argument is not a string literal") 14397 14398 // We need to convert this into a call to "require()" if ES6 syntax is 14399 // not supported in the current output format. The full conversion: 14400 // 14401 // Before: 14402 // import(foo) 14403 // 14404 // After: 14405 // Promise.resolve().then(() => __toESM(require(foo))) 14406 // 14407 // This is normally done by the printer since we don't know during the 14408 // parsing stage whether this module is external or not. However, it's 14409 // guaranteed to be external if the argument isn't a string. We handle 14410 // this case here instead of in the printer because both the printer 14411 // and the linker currently need an import record to handle this case 14412 // correctly, and you need a string literal to get an import record. 14413 if p.options.unsupportedJSFeatures.Has(compat.DynamicImport) { 14414 var then js_ast.Expr 14415 value := p.callRuntime(arg.Loc, "__toESM", []js_ast.Expr{{Loc: expr.Loc, Data: &js_ast.ECall{ 14416 Target: p.valueToSubstituteForRequire(expr.Loc), 14417 Args: []js_ast.Expr{arg}, 14418 CloseParenLoc: e.CloseParenLoc, 14419 }}}) 14420 body := js_ast.FnBody{Loc: expr.Loc, Block: js_ast.SBlock{Stmts: []js_ast.Stmt{{Loc: expr.Loc, Data: &js_ast.SReturn{ValueOrNil: value}}}}} 14421 if p.options.unsupportedJSFeatures.Has(compat.Arrow) { 14422 then = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EFunction{Fn: js_ast.Fn{Body: body}}} 14423 } else { 14424 then = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EArrow{Body: body, PreferExpr: true}} 14425 } 14426 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 14427 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EDot{ 14428 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 14429 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EDot{ 14430 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: p.makePromiseRef()}}, 14431 Name: "resolve", 14432 NameLoc: expr.Loc, 14433 }}, 14434 Kind: js_ast.TargetWasOriginallyPropertyAccess, 14435 }}, 14436 Name: "then", 14437 NameLoc: expr.Loc, 14438 }}, 14439 Args: []js_ast.Expr{then}, 14440 Kind: js_ast.TargetWasOriginallyPropertyAccess, 14441 }} 14442 } 14443 14444 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EImportCall{ 14445 Expr: arg, 14446 OptionsOrNil: e.OptionsOrNil, 14447 CloseParenLoc: e.CloseParenLoc, 14448 }} 14449 }), exprOut{} 14450 14451 case *js_ast.ECall: 14452 p.callTarget = e.Target.Data 14453 14454 // Track ".then().catch()" chains 14455 p.thenCatchChain = thenCatchChain{ 14456 nextTarget: e.Target.Data, 14457 hasMultipleArgs: len(e.Args) >= 2, 14458 hasCatch: p.thenCatchChain.nextTarget == e && p.thenCatchChain.hasCatch, 14459 catchLoc: p.thenCatchChain.catchLoc, 14460 } 14461 if p.thenCatchChain.hasMultipleArgs { 14462 p.thenCatchChain.catchLoc = e.Args[1].Loc 14463 } 14464 14465 // Prepare to recognize "require.resolve()" and "Object.create" calls 14466 couldBeRequireResolve := false 14467 couldBeObjectCreate := false 14468 if len(e.Args) == 1 { 14469 if dot, ok := e.Target.Data.(*js_ast.EDot); ok && dot.OptionalChain == js_ast.OptionalChainNone { 14470 if p.options.mode != config.ModePassThrough && dot.Name == "resolve" { 14471 couldBeRequireResolve = true 14472 } else if dot.Name == "create" { 14473 couldBeObjectCreate = true 14474 } 14475 } 14476 } 14477 14478 wasIdentifierBeforeVisit := false 14479 isParenthesizedOptionalChain := false 14480 switch e2 := e.Target.Data.(type) { 14481 case *js_ast.EIdentifier: 14482 wasIdentifierBeforeVisit = true 14483 case *js_ast.EDot: 14484 isParenthesizedOptionalChain = e.OptionalChain == js_ast.OptionalChainNone && e2.OptionalChain != js_ast.OptionalChainNone 14485 case *js_ast.EIndex: 14486 isParenthesizedOptionalChain = e.OptionalChain == js_ast.OptionalChainNone && e2.OptionalChain != js_ast.OptionalChainNone 14487 } 14488 target, out := p.visitExprInOut(e.Target, exprIn{ 14489 hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue, 14490 14491 // Signal to our child if this is an ECall at the start of an optional 14492 // chain. If so, the child will need to stash the "this" context for us 14493 // that we need for the ".call(this, ...args)". 14494 storeThisArgForParentOptionalChain: e.OptionalChain == js_ast.OptionalChainStart || isParenthesizedOptionalChain, 14495 }) 14496 e.Target = target 14497 p.warnAboutImportNamespaceCall(e.Target, exprKindCall) 14498 14499 hasSpread := false 14500 oldIsControlFlowDead := p.isControlFlowDead 14501 14502 // If we're removing this call, don't count any arguments as symbol uses 14503 if out.methodCallMustBeReplacedWithUndefined { 14504 if js_ast.IsPropertyAccess(e.Target) { 14505 p.isControlFlowDead = true 14506 } else { 14507 out.methodCallMustBeReplacedWithUndefined = false 14508 } 14509 } 14510 14511 // Visit the arguments 14512 for i, arg := range e.Args { 14513 arg = p.visitExpr(arg) 14514 if _, ok := arg.Data.(*js_ast.ESpread); ok { 14515 hasSpread = true 14516 } 14517 e.Args[i] = arg 14518 } 14519 14520 // Mark side-effect free IIFEs with "/* @__PURE__ */" 14521 if !e.CanBeUnwrappedIfUnused { 14522 switch target := e.Target.Data.(type) { 14523 case *js_ast.EArrow: 14524 if !target.IsAsync && p.iifeCanBeRemovedIfUnused(target.Args, target.Body) { 14525 e.CanBeUnwrappedIfUnused = true 14526 } 14527 case *js_ast.EFunction: 14528 if !target.Fn.IsAsync && !target.Fn.IsGenerator && p.iifeCanBeRemovedIfUnused(target.Fn.Args, target.Fn.Body) { 14529 e.CanBeUnwrappedIfUnused = true 14530 } 14531 } 14532 } 14533 14534 // Our hack for reading Yarn PnP files is implemented here: 14535 if p.options.decodeHydrateRuntimeStateYarnPnP { 14536 if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok && p.symbols[id.Ref.InnerIndex].OriginalName == "hydrateRuntimeState" && len(e.Args) >= 1 { 14537 switch arg := e.Args[0].Data.(type) { 14538 case *js_ast.EObject: 14539 // "hydrateRuntimeState(<object literal>)" 14540 if arg := e.Args[0]; isValidJSON(arg) { 14541 p.manifestForYarnPnP = arg 14542 } 14543 14544 case *js_ast.ECall: 14545 // "hydrateRuntimeState(JSON.parse(<something>))" 14546 if len(arg.Args) == 1 { 14547 if dot, ok := arg.Target.Data.(*js_ast.EDot); ok && dot.Name == "parse" { 14548 if id, ok := dot.Target.Data.(*js_ast.EIdentifier); ok { 14549 if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound && symbol.OriginalName == "JSON" { 14550 arg := arg.Args[0] 14551 switch a := arg.Data.(type) { 14552 case *js_ast.EString: 14553 // "hydrateRuntimeState(JSON.parse(<string literal>))" 14554 source := logger.Source{KeyPath: p.source.KeyPath, Contents: helpers.UTF16ToString(a.Value)} 14555 stringInJSTable := logger.GenerateStringInJSTable(p.source.Contents, arg.Loc, source.Contents) 14556 log := logger.NewStringInJSLog(p.log, &p.tracker, stringInJSTable) 14557 p.manifestForYarnPnP, _ = ParseJSON(log, source, JSONOptions{}) 14558 remapExprLocsInJSON(&p.manifestForYarnPnP, stringInJSTable) 14559 14560 case *js_ast.EIdentifier: 14561 // "hydrateRuntimeState(JSON.parse(<identifier>))" 14562 if data, ok := p.stringLocalsForYarnPnP[a.Ref]; ok { 14563 source := logger.Source{KeyPath: p.source.KeyPath, Contents: helpers.UTF16ToString(data.value)} 14564 stringInJSTable := logger.GenerateStringInJSTable(p.source.Contents, data.loc, source.Contents) 14565 log := logger.NewStringInJSLog(p.log, &p.tracker, stringInJSTable) 14566 p.manifestForYarnPnP, _ = ParseJSON(log, source, JSONOptions{}) 14567 remapExprLocsInJSON(&p.manifestForYarnPnP, stringInJSTable) 14568 } 14569 } 14570 } 14571 } 14572 } 14573 } 14574 } 14575 } 14576 } 14577 14578 // Stop now if this call must be removed 14579 if out.methodCallMustBeReplacedWithUndefined { 14580 p.isControlFlowDead = oldIsControlFlowDead 14581 return js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared}, exprOut{} 14582 } 14583 14584 // "foo(1, ...[2, 3], 4)" => "foo(1, 2, 3, 4)" 14585 if p.options.minifySyntax && hasSpread { 14586 e.Args = js_ast.InlineSpreadsOfArrayLiterals(e.Args) 14587 } 14588 14589 switch t := target.Data.(type) { 14590 case *js_ast.EImportIdentifier: 14591 // If this function is inlined, allow it to be tree-shaken 14592 if p.options.minifySyntax && !p.isControlFlowDead { 14593 p.convertSymbolUseToCall(t.Ref, len(e.Args) == 1 && !hasSpread) 14594 } 14595 14596 case *js_ast.EIdentifier: 14597 // Detect if this is a direct eval. Note that "(1 ? eval : 0)(x)" will 14598 // become "eval(x)" after we visit the target due to dead code elimination, 14599 // but that doesn't mean it should become a direct eval. 14600 // 14601 // Note that "eval?.(x)" is considered an indirect eval. There was debate 14602 // about this after everyone implemented it as a direct eval, but the 14603 // language committee said it was indirect and everyone had to change it: 14604 // https://github.com/tc39/ecma262/issues/2062. 14605 if e.OptionalChain == js_ast.OptionalChainNone { 14606 symbol := p.symbols[t.Ref.InnerIndex] 14607 if wasIdentifierBeforeVisit && symbol.OriginalName == "eval" { 14608 e.Kind = js_ast.DirectEval 14609 14610 // Pessimistically assume that if this looks like a CommonJS module 14611 // (e.g. no "export" keywords), a direct call to "eval" means that 14612 // code could potentially access "module" or "exports". 14613 if p.options.mode == config.ModeBundle && !p.isFileConsideredToHaveESMExports { 14614 p.recordUsage(p.moduleRef) 14615 p.recordUsage(p.exportsRef) 14616 } 14617 14618 // Mark this scope and all parent scopes as containing a direct eval. 14619 // This will prevent us from renaming any symbols. 14620 for s := p.currentScope; s != nil; s = s.Parent { 14621 s.ContainsDirectEval = true 14622 } 14623 14624 // Warn when direct eval is used in an ESM file. There is no way we 14625 // can guarantee that this will work correctly for top-level imported 14626 // and exported symbols due to scope hoisting. Except don't warn when 14627 // this code is in a 3rd-party library because there's nothing people 14628 // will be able to do about the warning. 14629 text := "Using direct eval with a bundler is not recommended and may cause problems" 14630 kind := logger.Debug 14631 if p.options.mode == config.ModeBundle && p.isFileConsideredESM && !p.suppressWarningsAboutWeirdCode { 14632 kind = logger.Warning 14633 } 14634 p.log.AddIDWithNotes(logger.MsgID_JS_DirectEval, kind, &p.tracker, js_lexer.RangeOfIdentifier(p.source, e.Target.Loc), text, 14635 []logger.MsgData{{Text: "You can read more about direct eval and bundling here: https://esbuild.github.io/link/direct-eval"}}) 14636 } else if symbol.Flags.Has(ast.CallCanBeUnwrappedIfUnused) { 14637 // Automatically add a "/* @__PURE__ */" comment to file-local calls 14638 // of functions declared with a "/* @__NO_SIDE_EFFECTS__ */" comment 14639 t.CallCanBeUnwrappedIfUnused = true 14640 } 14641 } 14642 14643 // Optimize references to global constructors 14644 if p.options.minifySyntax && t.CanBeRemovedIfUnused && len(e.Args) <= 1 && !hasSpread { 14645 if symbol := &p.symbols[t.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound { 14646 // Note: We construct expressions by assigning to "expr.Data" so 14647 // that the source map position for the constructor is preserved 14648 switch symbol.OriginalName { 14649 case "Boolean": 14650 if len(e.Args) == 0 { 14651 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EBoolean{Value: false}}, exprOut{} 14652 } else { 14653 expr.Data = &js_ast.EUnary{Value: p.astHelpers.SimplifyBooleanExpr(e.Args[0]), Op: js_ast.UnOpNot} 14654 return js_ast.Not(expr), exprOut{} 14655 } 14656 14657 case "Number": 14658 if len(e.Args) == 0 { 14659 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: 0}}, exprOut{} 14660 } else { 14661 arg := e.Args[0] 14662 14663 switch js_ast.KnownPrimitiveType(arg.Data) { 14664 case js_ast.PrimitiveNumber: 14665 return arg, exprOut{} 14666 14667 case 14668 js_ast.PrimitiveUndefined, // NaN 14669 js_ast.PrimitiveNull, // 0 14670 js_ast.PrimitiveBoolean, // 0 or 1 14671 js_ast.PrimitiveString: // StringToNumber 14672 if number, ok := js_ast.ToNumberWithoutSideEffects(arg.Data); ok { 14673 expr.Data = &js_ast.ENumber{Value: number} 14674 } else { 14675 expr.Data = &js_ast.EUnary{Value: arg, Op: js_ast.UnOpPos} 14676 } 14677 return expr, exprOut{} 14678 } 14679 } 14680 14681 case "String": 14682 if len(e.Args) == 0 { 14683 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: nil}}, exprOut{} 14684 } else { 14685 arg := e.Args[0] 14686 14687 switch js_ast.KnownPrimitiveType(arg.Data) { 14688 case js_ast.PrimitiveString: 14689 return arg, exprOut{} 14690 } 14691 } 14692 14693 case "BigInt": 14694 if len(e.Args) == 1 { 14695 arg := e.Args[0] 14696 14697 switch js_ast.KnownPrimitiveType(arg.Data) { 14698 case js_ast.PrimitiveBigInt: 14699 return arg, exprOut{} 14700 } 14701 } 14702 } 14703 } 14704 } 14705 14706 // Copy the call side effect flag over if this is a known target 14707 if t.CallCanBeUnwrappedIfUnused { 14708 e.CanBeUnwrappedIfUnused = true 14709 } 14710 14711 // If this function is inlined, allow it to be tree-shaken 14712 if p.options.minifySyntax && !p.isControlFlowDead { 14713 p.convertSymbolUseToCall(t.Ref, len(e.Args) == 1 && !hasSpread) 14714 } 14715 14716 case *js_ast.EDot: 14717 // Recognize "require.resolve()" calls 14718 if couldBeRequireResolve && t.Name == "resolve" { 14719 if id, ok := t.Target.Data.(*js_ast.EIdentifier); ok && id.Ref == p.requireRef { 14720 p.ignoreUsage(p.requireRef) 14721 return p.maybeTransposeIfExprChain(e.Args[0], func(arg js_ast.Expr) js_ast.Expr { 14722 if str, ok := e.Args[0].Data.(*js_ast.EString); ok { 14723 // Ignore calls to require.resolve() if the control flow is provably 14724 // dead here. We don't want to spend time scanning the required files 14725 // if they will never be used. 14726 if p.isControlFlowDead { 14727 return js_ast.Expr{Loc: expr.Loc, Data: js_ast.ENullShared} 14728 } 14729 14730 importRecordIndex := p.addImportRecord(ast.ImportRequireResolve, p.source.RangeOfString(e.Args[0].Loc), helpers.UTF16ToString(str.Value), nil, 0) 14731 if p.fnOrArrowDataVisit.tryBodyCount != 0 { 14732 record := &p.importRecords[importRecordIndex] 14733 record.Flags |= ast.HandlesImportErrors 14734 record.ErrorHandlerLoc = p.fnOrArrowDataVisit.tryCatchLoc 14735 } 14736 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, importRecordIndex) 14737 14738 // Create a new expression to represent the operation 14739 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ERequireResolveString{ 14740 ImportRecordIndex: importRecordIndex, 14741 CloseParenLoc: e.CloseParenLoc, 14742 }} 14743 } 14744 14745 // Otherwise just return a clone of the "require.resolve()" call 14746 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 14747 Target: js_ast.Expr{Loc: e.Target.Loc, Data: &js_ast.EDot{ 14748 Target: p.valueToSubstituteForRequire(t.Target.Loc), 14749 Name: t.Name, 14750 NameLoc: t.NameLoc, 14751 }}, 14752 Args: []js_ast.Expr{arg}, 14753 Kind: e.Kind, 14754 CloseParenLoc: e.CloseParenLoc, 14755 }} 14756 }), exprOut{} 14757 } 14758 } 14759 14760 // Recognize "Object.create()" calls 14761 if couldBeObjectCreate && t.Name == "create" { 14762 if id, ok := t.Target.Data.(*js_ast.EIdentifier); ok { 14763 if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound && symbol.OriginalName == "Object" { 14764 switch e.Args[0].Data.(type) { 14765 case *js_ast.ENull, *js_ast.EObject: 14766 // Mark "Object.create(null)" and "Object.create({})" as pure 14767 e.CanBeUnwrappedIfUnused = true 14768 } 14769 } 14770 } 14771 } 14772 14773 if p.options.minifySyntax { 14774 switch t.Name { 14775 case "charCodeAt": 14776 // Recognize "charCodeAt()" calls 14777 if str, ok := t.Target.Data.(*js_ast.EString); ok && len(e.Args) <= 1 { 14778 index := 0 14779 hasIndex := false 14780 if len(e.Args) == 0 { 14781 hasIndex = true 14782 } else if num, ok := e.Args[0].Data.(*js_ast.ENumber); ok && num.Value == math.Trunc(num.Value) && math.Abs(num.Value) <= 0x7FFF_FFFF { 14783 index = int(num.Value) 14784 hasIndex = true 14785 } 14786 if hasIndex { 14787 if index >= 0 && index < len(str.Value) { 14788 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(str.Value[index])}}, exprOut{} 14789 } else { 14790 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: math.NaN()}}, exprOut{} 14791 } 14792 } 14793 } 14794 14795 case "fromCharCode": 14796 // Recognize "fromCharCode()" calls 14797 if id, ok := t.Target.Data.(*js_ast.EIdentifier); ok { 14798 if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound && symbol.OriginalName == "String" { 14799 charCodes := make([]uint16, 0, len(e.Args)) 14800 for _, arg := range e.Args { 14801 arg, ok := js_ast.ToNumberWithoutSideEffects(arg.Data) 14802 if !ok { 14803 break 14804 } 14805 charCodes = append(charCodes, uint16(js_ast.ToInt32(arg))) 14806 } 14807 if len(charCodes) == len(e.Args) { 14808 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: charCodes}}, exprOut{} 14809 } 14810 } 14811 } 14812 14813 case "toString": 14814 switch target := t.Target.Data.(type) { 14815 case *js_ast.ENumber: 14816 radix := 0 14817 if len(e.Args) == 0 { 14818 radix = 10 14819 } else if len(e.Args) == 1 { 14820 if num, ok := e.Args[0].Data.(*js_ast.ENumber); ok && num.Value == math.Trunc(num.Value) && num.Value >= 2 && num.Value <= 36 { 14821 radix = int(num.Value) 14822 } 14823 } 14824 if radix != 0 { 14825 if str, ok := js_ast.TryToStringOnNumberSafely(target.Value, radix); ok { 14826 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(str)}}, exprOut{} 14827 } 14828 } 14829 14830 case *js_ast.ERegExp: 14831 if len(e.Args) == 0 { 14832 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16(target.Value)}}, exprOut{} 14833 } 14834 14835 case *js_ast.EBoolean: 14836 if len(e.Args) == 0 { 14837 if target.Value { 14838 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("true")}}, exprOut{} 14839 } else { 14840 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: helpers.StringToUTF16("false")}}, exprOut{} 14841 } 14842 } 14843 14844 case *js_ast.EString: 14845 if len(e.Args) == 0 { 14846 return t.Target, exprOut{} 14847 } 14848 } 14849 } 14850 } 14851 14852 // Copy the call side effect flag over if this is a known target 14853 if t.CallCanBeUnwrappedIfUnused { 14854 e.CanBeUnwrappedIfUnused = true 14855 } 14856 14857 case *js_ast.EIndex: 14858 // Copy the call side effect flag over if this is a known target 14859 if t.CallCanBeUnwrappedIfUnused { 14860 e.CanBeUnwrappedIfUnused = true 14861 } 14862 14863 case *js_ast.ESuper: 14864 // If we're shimming "super()" calls, replace this call with "__super()" 14865 if p.superCtorRef != ast.InvalidRef { 14866 p.recordUsage(p.superCtorRef) 14867 target.Data = &js_ast.EIdentifier{Ref: p.superCtorRef} 14868 e.Target.Data = target.Data 14869 } 14870 } 14871 14872 // Handle parenthesized optional chains 14873 if isParenthesizedOptionalChain && out.thisArgFunc != nil && out.thisArgWrapFunc != nil { 14874 return p.lowerParenthesizedOptionalChain(expr.Loc, e, out), exprOut{} 14875 } 14876 14877 // Lower optional chaining if we're the top of the chain 14878 containsOptionalChain := e.OptionalChain == js_ast.OptionalChainStart || 14879 (e.OptionalChain == js_ast.OptionalChainContinue && out.childContainsOptionalChain) 14880 if containsOptionalChain && !in.hasChainParent { 14881 return p.lowerOptionalChain(expr, in, out) 14882 } 14883 14884 // If this is a plain call expression (instead of an optional chain), lower 14885 // private member access in the call target now if there is one 14886 if !containsOptionalChain { 14887 if target, loc, private := p.extractPrivateIndex(e.Target); private != nil { 14888 // "foo.#bar(123)" => "__privateGet(_a = foo, #bar).call(_a, 123)" 14889 targetFunc, targetWrapFunc := p.captureValueWithPossibleSideEffects(target.Loc, 2, target, valueCouldBeMutated) 14890 return targetWrapFunc(js_ast.Expr{Loc: target.Loc, Data: &js_ast.ECall{ 14891 Target: js_ast.Expr{Loc: target.Loc, Data: &js_ast.EDot{ 14892 Target: p.lowerPrivateGet(targetFunc(), loc, private), 14893 Name: "call", 14894 NameLoc: target.Loc, 14895 }}, 14896 Args: append([]js_ast.Expr{targetFunc()}, e.Args...), 14897 CanBeUnwrappedIfUnused: e.CanBeUnwrappedIfUnused, 14898 Kind: js_ast.TargetWasOriginallyPropertyAccess, 14899 }}), exprOut{} 14900 } 14901 p.maybeLowerSuperPropertyGetInsideCall(e) 14902 } 14903 14904 // Track calls to require() so we can use them while bundling 14905 if p.options.mode != config.ModePassThrough && e.OptionalChain == js_ast.OptionalChainNone { 14906 if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok && id.Ref == p.requireRef { 14907 // Heuristic: omit warnings inside try/catch blocks because presumably 14908 // the try/catch statement is there to handle the potential run-time 14909 // error from the unbundled require() call failing. 14910 omitWarnings := p.fnOrArrowDataVisit.tryBodyCount != 0 14911 14912 if p.options.mode != config.ModePassThrough { 14913 // There must be one argument 14914 if len(e.Args) == 1 { 14915 p.ignoreUsage(p.requireRef) 14916 return p.maybeTransposeIfExprChain(e.Args[0], func(arg js_ast.Expr) js_ast.Expr { 14917 // The argument must be a string 14918 if str, ok := arg.Data.(*js_ast.EString); ok { 14919 // Ignore calls to require() if the control flow is provably dead here. 14920 // We don't want to spend time scanning the required files if they will 14921 // never be used. 14922 if p.isControlFlowDead { 14923 return js_ast.Expr{Loc: expr.Loc, Data: js_ast.ENullShared} 14924 } 14925 14926 importRecordIndex := p.addImportRecord(ast.ImportRequire, p.source.RangeOfString(arg.Loc), helpers.UTF16ToString(str.Value), nil, 0) 14927 if p.fnOrArrowDataVisit.tryBodyCount != 0 { 14928 record := &p.importRecords[importRecordIndex] 14929 record.Flags |= ast.HandlesImportErrors 14930 record.ErrorHandlerLoc = p.fnOrArrowDataVisit.tryCatchLoc 14931 } 14932 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, importRecordIndex) 14933 14934 // Currently "require" is not converted into "import" for ESM 14935 if p.options.mode != config.ModeBundle && p.options.outputFormat == config.FormatESModule && !omitWarnings { 14936 r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) 14937 p.log.AddID(logger.MsgID_JS_UnsupportedRequireCall, logger.Warning, &p.tracker, r, "Converting \"require\" to \"esm\" is currently not supported") 14938 } 14939 14940 // Create a new expression to represent the operation 14941 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ERequireString{ 14942 ImportRecordIndex: importRecordIndex, 14943 CloseParenLoc: e.CloseParenLoc, 14944 }} 14945 } 14946 14947 // Handle glob patterns 14948 if p.options.mode == config.ModeBundle { 14949 if value := p.handleGlobPattern(arg, ast.ImportRequire, "globRequire", nil); value.Data != nil { 14950 return value 14951 } 14952 } 14953 14954 // Use a debug log so people can see this if they want to 14955 r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) 14956 p.log.AddID(logger.MsgID_JS_UnsupportedRequireCall, logger.Debug, &p.tracker, r, 14957 "This call to \"require\" will not be bundled because the argument is not a string literal") 14958 14959 // Otherwise just return a clone of the "require()" call 14960 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 14961 Target: p.valueToSubstituteForRequire(e.Target.Loc), 14962 Args: []js_ast.Expr{arg}, 14963 CloseParenLoc: e.CloseParenLoc, 14964 }} 14965 }), exprOut{} 14966 } else { 14967 // Use a debug log so people can see this if they want to 14968 r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) 14969 p.log.AddIDWithNotes(logger.MsgID_JS_UnsupportedRequireCall, logger.Debug, &p.tracker, r, 14970 fmt.Sprintf("This call to \"require\" will not be bundled because it has %d arguments", len(e.Args)), 14971 []logger.MsgData{{Text: "To be bundled by esbuild, a \"require\" call must have exactly 1 argument."}}) 14972 } 14973 14974 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 14975 Target: p.valueToSubstituteForRequire(e.Target.Loc), 14976 Args: e.Args, 14977 CloseParenLoc: e.CloseParenLoc, 14978 }}, exprOut{} 14979 } 14980 } 14981 } 14982 14983 out = exprOut{ 14984 childContainsOptionalChain: containsOptionalChain, 14985 thisArgFunc: out.thisArgFunc, 14986 thisArgWrapFunc: out.thisArgWrapFunc, 14987 } 14988 if !in.hasChainParent { 14989 out.thisArgFunc = nil 14990 out.thisArgWrapFunc = nil 14991 } 14992 return expr, out 14993 14994 case *js_ast.ENew: 14995 hasSpread := false 14996 14997 e.Target = p.visitExpr(e.Target) 14998 p.warnAboutImportNamespaceCall(e.Target, exprKindNew) 14999 15000 for i, arg := range e.Args { 15001 arg = p.visitExpr(arg) 15002 if _, ok := arg.Data.(*js_ast.ESpread); ok { 15003 hasSpread = true 15004 } 15005 e.Args[i] = arg 15006 } 15007 15008 // "new foo(1, ...[2, 3], 4)" => "new foo(1, 2, 3, 4)" 15009 if p.options.minifySyntax && hasSpread { 15010 e.Args = js_ast.InlineSpreadsOfArrayLiterals(e.Args) 15011 } 15012 15013 p.maybeMarkKnownGlobalConstructorAsPure(e) 15014 15015 case *js_ast.EArrow: 15016 // Check for a propagated name to keep from the parent context 15017 var nameToKeep string 15018 if p.nameToKeepIsFor == e { 15019 nameToKeep = p.nameToKeep 15020 } 15021 15022 // Prepare for suspicious logical operator checking 15023 if e.PreferExpr && len(e.Args) == 1 && e.Args[0].DefaultOrNil.Data == nil && len(e.Body.Block.Stmts) == 1 { 15024 if _, ok := e.Args[0].Binding.Data.(*js_ast.BIdentifier); ok { 15025 if stmt, ok := e.Body.Block.Stmts[0].Data.(*js_ast.SReturn); ok { 15026 if binary, ok := stmt.ValueOrNil.Data.(*js_ast.EBinary); ok && (binary.Op == js_ast.BinOpLogicalAnd || binary.Op == js_ast.BinOpLogicalOr) { 15027 p.suspiciousLogicalOperatorInsideArrow = binary 15028 } 15029 } 15030 } 15031 } 15032 15033 asyncArrowNeedsToBeLowered := e.IsAsync && p.options.unsupportedJSFeatures.Has(compat.AsyncAwait) 15034 oldFnOrArrowData := p.fnOrArrowDataVisit 15035 p.fnOrArrowDataVisit = fnOrArrowDataVisit{ 15036 isArrow: true, 15037 isAsync: e.IsAsync, 15038 shouldLowerSuperPropertyAccess: oldFnOrArrowData.shouldLowerSuperPropertyAccess || asyncArrowNeedsToBeLowered, 15039 } 15040 15041 // Mark if we're inside an async arrow function. This value should be true 15042 // even if we're inside multiple arrow functions and the closest inclosing 15043 // arrow function isn't async, as long as at least one enclosing arrow 15044 // function within the current enclosing function is async. 15045 oldInsideAsyncArrowFn := p.fnOnlyDataVisit.isInsideAsyncArrowFn 15046 if e.IsAsync { 15047 p.fnOnlyDataVisit.isInsideAsyncArrowFn = true 15048 } 15049 15050 p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, expr.Loc) 15051 p.visitArgs(e.Args, visitArgsOpts{ 15052 hasRestArg: e.HasRestArg, 15053 body: e.Body.Block.Stmts, 15054 isUniqueFormalParameters: true, 15055 }) 15056 p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, e.Body.Loc) 15057 e.Body.Block.Stmts = p.visitStmtsAndPrependTempRefs(e.Body.Block.Stmts, prependTempRefsOpts{kind: stmtsFnBody}) 15058 p.popScope() 15059 p.lowerFunction(&e.IsAsync, nil, &e.Args, e.Body.Loc, &e.Body.Block, &e.PreferExpr, &e.HasRestArg, true /* isArrow */) 15060 p.popScope() 15061 15062 if p.options.minifySyntax && len(e.Body.Block.Stmts) == 1 { 15063 if s, ok := e.Body.Block.Stmts[0].Data.(*js_ast.SReturn); ok { 15064 if s.ValueOrNil.Data == nil { 15065 // "() => { return }" => "() => {}" 15066 e.Body.Block.Stmts = []js_ast.Stmt{} 15067 } else { 15068 // "() => { return x }" => "() => x" 15069 e.PreferExpr = true 15070 } 15071 } 15072 } 15073 15074 p.fnOnlyDataVisit.isInsideAsyncArrowFn = oldInsideAsyncArrowFn 15075 p.fnOrArrowDataVisit = oldFnOrArrowData 15076 15077 // Convert arrow functions to function expressions when lowering 15078 if p.options.unsupportedJSFeatures.Has(compat.Arrow) { 15079 expr.Data = &js_ast.EFunction{Fn: js_ast.Fn{ 15080 Args: e.Args, 15081 Body: e.Body, 15082 ArgumentsRef: ast.InvalidRef, 15083 IsAsync: e.IsAsync, 15084 HasRestArg: e.HasRestArg, 15085 }} 15086 } 15087 15088 // Optionally preserve the name 15089 if p.options.keepNames && nameToKeep != "" { 15090 expr = p.keepExprSymbolName(expr, nameToKeep) 15091 } 15092 15093 case *js_ast.EFunction: 15094 // Check for a propagated name to keep from the parent context 15095 var nameToKeep string 15096 if p.nameToKeepIsFor == e { 15097 nameToKeep = p.nameToKeep 15098 } 15099 15100 p.visitFn(&e.Fn, expr.Loc, visitFnOpts{ 15101 isMethod: in.isMethod, 15102 isDerivedClassCtor: e == p.propDerivedCtorValue, 15103 isLoweredPrivateMethod: in.isLoweredPrivateMethod, 15104 }) 15105 name := e.Fn.Name 15106 15107 // Remove unused function names when minifying 15108 if p.options.minifySyntax && !p.currentScope.ContainsDirectEval && 15109 name != nil && p.symbols[name.Ref.InnerIndex].UseCountEstimate == 0 { 15110 e.Fn.Name = nil 15111 } 15112 15113 // Optionally preserve the name for functions, but not for methods 15114 if p.options.keepNames && (!in.isMethod || in.isLoweredPrivateMethod) { 15115 if name != nil { 15116 expr = p.keepExprSymbolName(expr, p.symbols[name.Ref.InnerIndex].OriginalName) 15117 } else if nameToKeep != "" { 15118 expr = p.keepExprSymbolName(expr, nameToKeep) 15119 } 15120 } 15121 15122 case *js_ast.EClass: 15123 // Check for a propagated name to keep from the parent context 15124 var nameToKeep string 15125 if p.nameToKeepIsFor == e { 15126 nameToKeep = p.nameToKeep 15127 } 15128 15129 result := p.visitClass(expr.Loc, &e.Class, ast.InvalidRef, nameToKeep) 15130 15131 // Lower class field syntax for browsers that don't support it 15132 _, expr = p.lowerClass(js_ast.Stmt{}, expr, result, nameToKeep) 15133 15134 // We may be able to determine that a class is side-effect before lowering 15135 // but not after lowering (e.g. due to "--keep-names" mutating the object). 15136 // If that's the case, add a special annotation so this doesn't prevent 15137 // tree-shaking from happening. 15138 if result.canBeRemovedIfUnused { 15139 expr.Data = &js_ast.EAnnotation{ 15140 Value: expr, 15141 Flags: js_ast.CanBeRemovedIfUnusedFlag, 15142 } 15143 } 15144 15145 default: 15146 // Note: EPrivateIdentifier should have already been handled 15147 panic(fmt.Sprintf("Unexpected expression of type %T", expr.Data)) 15148 } 15149 15150 return expr, exprOut{} 15151 } 15152 15153 // This exists to handle very deeply-nested ASTs. For example, the "grapheme-splitter" 15154 // package contains this monstrosity: 15155 // 15156 // if ( 15157 // (0x0300 <= code && code <= 0x036F) || 15158 // (0x0483 <= code && code <= 0x0487) || 15159 // (0x0488 <= code && code <= 0x0489) || 15160 // (0x0591 <= code && code <= 0x05BD) || 15161 // ... many hundreds of lines later ... 15162 // ) { 15163 // return; 15164 // } 15165 // 15166 // If "checkAndPrepare" returns non-nil, then the return value is the final 15167 // expression. Otherwise, the final expression can be obtained by manually 15168 // visiting the left child and then calling "visitRightAndFinish": 15169 // 15170 // if result := v.checkAndPrepare(p); result.Data != nil { 15171 // return result 15172 // } 15173 // v.e.Left, _ = p.visitExprInOut(v.e.Left, v.leftIn) 15174 // return v.visitRightAndFinish(p) 15175 // 15176 // This code is convoluted this way so that we can use our own stack on the 15177 // heap instead of the call stack when there are additional levels of nesting. 15178 // Before this transformation, the code previously looked something like this: 15179 // 15180 // ... The code in "checkAndPrepare" ... 15181 // e.Left, _ = p.visitExprInOut(e.Left, in) 15182 // ... The code in "visitRightAndFinish" ... 15183 // 15184 // If this code is still confusing, it may be helpful to look back in git 15185 // history at the commit that introduced this transformation. 15186 // 15187 // Go normally has growable call stacks so this code transformation normally 15188 // doesn't do anything, but WebAssembly doesn't allow stack pointer manipulation 15189 // so Go's WebAssembly implementation doesn't support growable call stacks and 15190 // is therefore vulnerable to stack overflow. So this code transformation is 15191 // only really relevant for esbuild's WebAssembly-based API. 15192 type binaryExprVisitor struct { 15193 // Inputs 15194 e *js_ast.EBinary 15195 loc logger.Loc 15196 in exprIn 15197 15198 // Input for visiting the left child 15199 leftIn exprIn 15200 15201 // "Local variables" passed from "checkAndPrepare" to "visitRightAndFinish" 15202 isStmtExpr bool 15203 oldSilenceWarningAboutThisBeingUndefined bool 15204 } 15205 15206 func (v *binaryExprVisitor) checkAndPrepare(p *parser) js_ast.Expr { 15207 e := v.e 15208 15209 // Special-case EPrivateIdentifier to allow it here 15210 if private, ok := e.Left.Data.(*js_ast.EPrivateIdentifier); ok && e.Op == js_ast.BinOpIn { 15211 name := p.loadNameFromRef(private.Ref) 15212 result := p.findSymbol(e.Left.Loc, name) 15213 private.Ref = result.ref 15214 15215 // Unlike regular identifiers, there are no unbound private identifiers 15216 symbol := &p.symbols[result.ref.InnerIndex] 15217 if !symbol.Kind.IsPrivate() { 15218 r := logger.Range{Loc: e.Left.Loc, Len: int32(len(name))} 15219 p.log.AddError(&p.tracker, r, fmt.Sprintf("Private name %q must be declared in an enclosing class", name)) 15220 } 15221 15222 e.Right = p.visitExpr(e.Right) 15223 15224 if p.privateSymbolNeedsToBeLowered(private) { 15225 return p.lowerPrivateBrandCheck(e.Right, v.loc, private) 15226 } 15227 return js_ast.Expr{Loc: v.loc, Data: e} 15228 } 15229 15230 v.isStmtExpr = e == p.stmtExprValue 15231 v.oldSilenceWarningAboutThisBeingUndefined = p.fnOnlyDataVisit.silenceMessageAboutThisBeingUndefined 15232 15233 if _, ok := e.Left.Data.(*js_ast.EThis); ok && e.Op == js_ast.BinOpLogicalAnd { 15234 p.fnOnlyDataVisit.silenceMessageAboutThisBeingUndefined = true 15235 } 15236 v.leftIn = exprIn{ 15237 assignTarget: e.Op.BinaryAssignTarget(), 15238 shouldMangleStringsAsProps: e.Op == js_ast.BinOpIn, 15239 } 15240 return js_ast.Expr{} 15241 } 15242 15243 func (v *binaryExprVisitor) visitRightAndFinish(p *parser) js_ast.Expr { 15244 e := v.e 15245 15246 // Mark the control flow as dead if the branch is never taken 15247 switch e.Op { 15248 case js_ast.BinOpLogicalOr: 15249 if boolean, _, ok := js_ast.ToBooleanWithSideEffects(e.Left.Data); ok && boolean { 15250 // "true || dead" 15251 old := p.isControlFlowDead 15252 p.isControlFlowDead = true 15253 e.Right = p.visitExpr(e.Right) 15254 p.isControlFlowDead = old 15255 } else { 15256 e.Right = p.visitExpr(e.Right) 15257 } 15258 15259 case js_ast.BinOpLogicalAnd: 15260 if boolean, _, ok := js_ast.ToBooleanWithSideEffects(e.Left.Data); ok && !boolean { 15261 // "false && dead" 15262 old := p.isControlFlowDead 15263 p.isControlFlowDead = true 15264 e.Right = p.visitExpr(e.Right) 15265 p.isControlFlowDead = old 15266 } else { 15267 e.Right = p.visitExpr(e.Right) 15268 } 15269 15270 case js_ast.BinOpNullishCoalescing: 15271 if isNullOrUndefined, _, ok := js_ast.ToNullOrUndefinedWithSideEffects(e.Left.Data); ok && !isNullOrUndefined { 15272 // "notNullOrUndefined ?? dead" 15273 old := p.isControlFlowDead 15274 p.isControlFlowDead = true 15275 e.Right = p.visitExpr(e.Right) 15276 p.isControlFlowDead = old 15277 } else { 15278 e.Right = p.visitExpr(e.Right) 15279 } 15280 15281 case js_ast.BinOpComma: 15282 e.Right, _ = p.visitExprInOut(e.Right, exprIn{ 15283 shouldMangleStringsAsProps: v.in.shouldMangleStringsAsProps, 15284 }) 15285 15286 case js_ast.BinOpAssign, js_ast.BinOpLogicalOrAssign, js_ast.BinOpLogicalAndAssign, js_ast.BinOpNullishCoalescingAssign: 15287 // Check for a propagated name to keep from the parent context 15288 if id, ok := e.Left.Data.(*js_ast.EIdentifier); ok { 15289 p.nameToKeep = p.symbols[id.Ref.InnerIndex].OriginalName 15290 p.nameToKeepIsFor = e.Right.Data 15291 } 15292 15293 e.Right = p.visitExpr(e.Right) 15294 15295 default: 15296 e.Right = p.visitExpr(e.Right) 15297 } 15298 p.fnOnlyDataVisit.silenceMessageAboutThisBeingUndefined = v.oldSilenceWarningAboutThisBeingUndefined 15299 15300 // Always put constants consistently on the same side for equality 15301 // comparisons to help improve compression. In theory, dictionary-based 15302 // compression methods may already have a dictionary entry for code that 15303 // is similar to previous code. Note that we can only reorder expressions 15304 // that do not have any side effects. 15305 // 15306 // Constants are currently ordered on the right instead of the left because 15307 // it results in slightly smalller gzip size on our primary benchmark 15308 // (although slightly larger uncompressed size). The size difference is 15309 // less than 0.1% so it really isn't that important an optimization. 15310 if p.options.minifySyntax { 15311 switch e.Op { 15312 case js_ast.BinOpLooseEq, js_ast.BinOpLooseNe, js_ast.BinOpStrictEq, js_ast.BinOpStrictNe: 15313 // "1 === x" => "x === 1" 15314 if js_ast.IsPrimitiveLiteral(e.Left.Data) && !js_ast.IsPrimitiveLiteral(e.Right.Data) { 15315 e.Left, e.Right = e.Right, e.Left 15316 } 15317 } 15318 } 15319 15320 if p.shouldFoldTypeScriptConstantExpressions || (p.options.minifySyntax && js_ast.ShouldFoldBinaryOperatorWhenMinifying(e)) { 15321 if result := js_ast.FoldBinaryOperator(v.loc, e); result.Data != nil { 15322 return result 15323 } 15324 } 15325 15326 // Post-process the binary expression 15327 switch e.Op { 15328 case js_ast.BinOpComma: 15329 // "(1, 2)" => "2" 15330 // "(sideEffects(), 2)" => "(sideEffects(), 2)" 15331 if p.options.minifySyntax { 15332 e.Left = p.astHelpers.SimplifyUnusedExpr(e.Left, p.options.unsupportedJSFeatures) 15333 if e.Left.Data == nil { 15334 return e.Right 15335 } 15336 } 15337 15338 case js_ast.BinOpLooseEq: 15339 if result, ok := js_ast.CheckEqualityIfNoSideEffects(e.Left.Data, e.Right.Data, js_ast.LooseEquality); ok { 15340 return js_ast.Expr{Loc: v.loc, Data: &js_ast.EBoolean{Value: result}} 15341 } 15342 afterOpLoc := locAfterOp(e) 15343 if !p.warnAboutEqualityCheck("==", e.Left, afterOpLoc) { 15344 p.warnAboutEqualityCheck("==", e.Right, afterOpLoc) 15345 } 15346 p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders) 15347 15348 if p.options.minifySyntax { 15349 // "x == void 0" => "x == null" 15350 if _, ok := e.Left.Data.(*js_ast.EUndefined); ok { 15351 e.Left.Data = js_ast.ENullShared 15352 } else if _, ok := e.Right.Data.(*js_ast.EUndefined); ok { 15353 e.Right.Data = js_ast.ENullShared 15354 } 15355 15356 if result, ok := js_ast.MaybeSimplifyEqualityComparison(v.loc, e, p.options.unsupportedJSFeatures); ok { 15357 return result 15358 } 15359 } 15360 15361 case js_ast.BinOpStrictEq: 15362 if result, ok := js_ast.CheckEqualityIfNoSideEffects(e.Left.Data, e.Right.Data, js_ast.StrictEquality); ok { 15363 return js_ast.Expr{Loc: v.loc, Data: &js_ast.EBoolean{Value: result}} 15364 } 15365 afterOpLoc := locAfterOp(e) 15366 if !p.warnAboutEqualityCheck("===", e.Left, afterOpLoc) { 15367 p.warnAboutEqualityCheck("===", e.Right, afterOpLoc) 15368 } 15369 p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders) 15370 15371 if p.options.minifySyntax { 15372 // "typeof x === 'undefined'" => "typeof x == 'undefined'" 15373 if js_ast.CanChangeStrictToLoose(e.Left, e.Right) { 15374 e.Op = js_ast.BinOpLooseEq 15375 } 15376 15377 if result, ok := js_ast.MaybeSimplifyEqualityComparison(v.loc, e, p.options.unsupportedJSFeatures); ok { 15378 return result 15379 } 15380 } 15381 15382 case js_ast.BinOpLooseNe: 15383 if result, ok := js_ast.CheckEqualityIfNoSideEffects(e.Left.Data, e.Right.Data, js_ast.LooseEquality); ok { 15384 return js_ast.Expr{Loc: v.loc, Data: &js_ast.EBoolean{Value: !result}} 15385 } 15386 afterOpLoc := locAfterOp(e) 15387 if !p.warnAboutEqualityCheck("!=", e.Left, afterOpLoc) { 15388 p.warnAboutEqualityCheck("!=", e.Right, afterOpLoc) 15389 } 15390 p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders) 15391 15392 if p.options.minifySyntax { 15393 // "x != void 0" => "x != null" 15394 if _, ok := e.Left.Data.(*js_ast.EUndefined); ok { 15395 e.Left.Data = js_ast.ENullShared 15396 } else if _, ok := e.Right.Data.(*js_ast.EUndefined); ok { 15397 e.Right.Data = js_ast.ENullShared 15398 } 15399 15400 if result, ok := js_ast.MaybeSimplifyEqualityComparison(v.loc, e, p.options.unsupportedJSFeatures); ok { 15401 return result 15402 } 15403 } 15404 15405 case js_ast.BinOpStrictNe: 15406 if result, ok := js_ast.CheckEqualityIfNoSideEffects(e.Left.Data, e.Right.Data, js_ast.StrictEquality); ok { 15407 return js_ast.Expr{Loc: v.loc, Data: &js_ast.EBoolean{Value: !result}} 15408 } 15409 afterOpLoc := locAfterOp(e) 15410 if !p.warnAboutEqualityCheck("!==", e.Left, afterOpLoc) { 15411 p.warnAboutEqualityCheck("!==", e.Right, afterOpLoc) 15412 } 15413 p.warnAboutTypeofAndString(e.Left, e.Right, checkBothOrders) 15414 15415 if p.options.minifySyntax { 15416 // "typeof x !== 'undefined'" => "typeof x != 'undefined'" 15417 if js_ast.CanChangeStrictToLoose(e.Left, e.Right) { 15418 e.Op = js_ast.BinOpLooseNe 15419 } 15420 15421 if result, ok := js_ast.MaybeSimplifyEqualityComparison(v.loc, e, p.options.unsupportedJSFeatures); ok { 15422 return result 15423 } 15424 } 15425 15426 case js_ast.BinOpNullishCoalescing: 15427 if isNullOrUndefined, sideEffects, ok := js_ast.ToNullOrUndefinedWithSideEffects(e.Left.Data); ok { 15428 // Warn about potential bugs 15429 if !js_ast.IsPrimitiveLiteral(e.Left.Data) { 15430 // "return props.flag === flag ?? true" is "return (props.flag === flag) ?? true" not "return props.flag === (flag ?? true)" 15431 var which string 15432 var leftIsNullOrUndefined string 15433 var leftIsReturned string 15434 if !isNullOrUndefined { 15435 which = "left" 15436 leftIsNullOrUndefined = "never" 15437 leftIsReturned = "always" 15438 } else { 15439 which = "right" 15440 leftIsNullOrUndefined = "always" 15441 leftIsReturned = "never" 15442 } 15443 kind := logger.Warning 15444 if p.suppressWarningsAboutWeirdCode { 15445 kind = logger.Debug 15446 } 15447 rOp := p.source.RangeOfOperatorBefore(e.Right.Loc, "??") 15448 rLeft := logger.Range{Loc: e.Left.Loc, Len: p.source.LocBeforeWhitespace(rOp.Loc).Start - e.Left.Loc.Start} 15449 p.log.AddIDWithNotes(logger.MsgID_JS_SuspiciousNullishCoalescing, kind, &p.tracker, rOp, 15450 fmt.Sprintf("The \"??\" operator here will always return the %s operand", which), []logger.MsgData{ 15451 p.tracker.MsgData(rLeft, fmt.Sprintf( 15452 "The left operand of the \"??\" operator here will %s be null or undefined, so it will %s be returned. This usually indicates a bug in your code:", 15453 leftIsNullOrUndefined, leftIsReturned))}) 15454 } 15455 15456 if !isNullOrUndefined { 15457 return e.Left 15458 } else if sideEffects == js_ast.NoSideEffects { 15459 return e.Right 15460 } 15461 } 15462 15463 if p.options.minifySyntax { 15464 // "a ?? (b ?? c)" => "a ?? b ?? c" 15465 if right, ok := e.Right.Data.(*js_ast.EBinary); ok && right.Op == js_ast.BinOpNullishCoalescing { 15466 e.Left = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpNullishCoalescing, e.Left, right.Left) 15467 e.Right = right.Right 15468 } 15469 } 15470 15471 if p.options.unsupportedJSFeatures.Has(compat.NullishCoalescing) { 15472 return p.lowerNullishCoalescing(v.loc, e.Left, e.Right) 15473 } 15474 15475 case js_ast.BinOpLogicalOr: 15476 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(e.Left.Data); ok { 15477 // Warn about potential bugs 15478 if e == p.suspiciousLogicalOperatorInsideArrow { 15479 if arrowLoc := p.source.RangeOfOperatorBefore(v.loc, "=>"); arrowLoc.Loc.Start+2 == p.source.LocBeforeWhitespace(v.loc).Start { 15480 // "return foo => 1 || foo <= 0" 15481 var which string 15482 if boolean { 15483 which = "left" 15484 } else { 15485 which = "right" 15486 } 15487 kind := logger.Warning 15488 if p.suppressWarningsAboutWeirdCode { 15489 kind = logger.Debug 15490 } 15491 note := p.tracker.MsgData(arrowLoc, 15492 "The \"=>\" symbol creates an arrow function expression in JavaScript. Did you mean to use the greater-than-or-equal-to operator \">=\" here instead?") 15493 note.Location.Suggestion = ">=" 15494 rOp := p.source.RangeOfOperatorBefore(e.Right.Loc, "||") 15495 p.log.AddIDWithNotes(logger.MsgID_JS_SuspiciousLogicalOperator, kind, &p.tracker, rOp, 15496 fmt.Sprintf("The \"||\" operator here will always return the %s operand", which), []logger.MsgData{note}) 15497 } 15498 } 15499 15500 if boolean { 15501 return e.Left 15502 } else if sideEffects == js_ast.NoSideEffects { 15503 return e.Right 15504 } 15505 } 15506 15507 if p.options.minifySyntax { 15508 // "a || (b || c)" => "a || b || c" 15509 if right, ok := e.Right.Data.(*js_ast.EBinary); ok && right.Op == js_ast.BinOpLogicalOr { 15510 e.Left = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalOr, e.Left, right.Left) 15511 e.Right = right.Right 15512 } 15513 15514 // "a === null || a === undefined" => "a == null" 15515 if left, right, ok := js_ast.IsBinaryNullAndUndefined(e.Left, e.Right, js_ast.BinOpStrictEq); ok { 15516 e.Op = js_ast.BinOpLooseEq 15517 e.Left = left 15518 e.Right = right 15519 } 15520 } 15521 15522 case js_ast.BinOpLogicalAnd: 15523 if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(e.Left.Data); ok { 15524 // Warn about potential bugs 15525 if e == p.suspiciousLogicalOperatorInsideArrow { 15526 if arrowLoc := p.source.RangeOfOperatorBefore(v.loc, "=>"); arrowLoc.Loc.Start+2 == p.source.LocBeforeWhitespace(v.loc).Start { 15527 // "return foo => 0 && foo <= 1" 15528 var which string 15529 if !boolean { 15530 which = "left" 15531 } else { 15532 which = "right" 15533 } 15534 kind := logger.Warning 15535 if p.suppressWarningsAboutWeirdCode { 15536 kind = logger.Debug 15537 } 15538 note := p.tracker.MsgData(arrowLoc, 15539 "The \"=>\" symbol creates an arrow function expression in JavaScript. Did you mean to use the greater-than-or-equal-to operator \">=\" here instead?") 15540 note.Location.Suggestion = ">=" 15541 rOp := p.source.RangeOfOperatorBefore(e.Right.Loc, "&&") 15542 p.log.AddIDWithNotes(logger.MsgID_JS_SuspiciousLogicalOperator, kind, &p.tracker, rOp, 15543 fmt.Sprintf("The \"&&\" operator here will always return the %s operand", which), []logger.MsgData{note}) 15544 } 15545 } 15546 15547 if !boolean { 15548 return e.Left 15549 } else if sideEffects == js_ast.NoSideEffects { 15550 return e.Right 15551 } 15552 } 15553 15554 if p.options.minifySyntax { 15555 // "a && (b && c)" => "a && b && c" 15556 if right, ok := e.Right.Data.(*js_ast.EBinary); ok && right.Op == js_ast.BinOpLogicalAnd { 15557 e.Left = js_ast.JoinWithLeftAssociativeOp(js_ast.BinOpLogicalAnd, e.Left, right.Left) 15558 e.Right = right.Right 15559 } 15560 15561 // "a !== null && a !== undefined" => "a != null" 15562 if left, right, ok := js_ast.IsBinaryNullAndUndefined(e.Left, e.Right, js_ast.BinOpStrictNe); ok { 15563 e.Op = js_ast.BinOpLooseNe 15564 e.Left = left 15565 e.Right = right 15566 } 15567 } 15568 15569 case js_ast.BinOpAdd: 15570 // "'abc' + 'xyz'" => "'abcxyz'" 15571 if result := js_ast.FoldStringAddition(e.Left, e.Right, js_ast.StringAdditionNormal); result.Data != nil { 15572 return result 15573 } 15574 15575 if left, ok := e.Left.Data.(*js_ast.EBinary); ok && left.Op == js_ast.BinOpAdd { 15576 // "x + 'abc' + 'xyz'" => "x + 'abcxyz'" 15577 if result := js_ast.FoldStringAddition(left.Right, e.Right, js_ast.StringAdditionWithNestedLeft); result.Data != nil { 15578 return js_ast.Expr{Loc: v.loc, Data: &js_ast.EBinary{Op: left.Op, Left: left.Left, Right: result}} 15579 } 15580 } 15581 15582 case js_ast.BinOpPow: 15583 // Lower the exponentiation operator for browsers that don't support it 15584 if p.options.unsupportedJSFeatures.Has(compat.ExponentOperator) { 15585 return p.callRuntime(v.loc, "__pow", []js_ast.Expr{e.Left, e.Right}) 15586 } 15587 15588 //////////////////////////////////////////////////////////////////////////////// 15589 // All assignment operators below here 15590 15591 case js_ast.BinOpAssign: 15592 if target, loc, private := p.extractPrivateIndex(e.Left); private != nil { 15593 return p.lowerPrivateSet(target, loc, private, e.Right) 15594 } 15595 15596 if property := p.extractSuperProperty(e.Left); property.Data != nil { 15597 return p.lowerSuperPropertySet(e.Left.Loc, property, e.Right) 15598 } 15599 15600 // Lower assignment destructuring patterns for browsers that don't 15601 // support them. Note that assignment expressions are used to represent 15602 // initializers in binding patterns, so only do this if we're not 15603 // ourselves the target of an assignment. Example: "[a = b] = c" 15604 if v.in.assignTarget == js_ast.AssignTargetNone { 15605 mode := objRestMustReturnInitExpr 15606 if v.isStmtExpr { 15607 mode = objRestReturnValueIsUnused 15608 } 15609 if result, ok := p.lowerAssign(e.Left, e.Right, mode); ok { 15610 return result 15611 } 15612 15613 // If CommonJS-style exports are disabled, then references to them are 15614 // treated as global variable references. This is consistent with how 15615 // they work in node and the browser, so it's the correct interpretation. 15616 // 15617 // However, people sometimes try to use both types of exports within the 15618 // same module and expect it to work. We warn about this when module 15619 // format conversion is enabled. 15620 // 15621 // Only warn about this for uses in assignment position since there are 15622 // some legitimate other uses. For example, some people do "typeof module" 15623 // to check for a CommonJS environment, and we shouldn't warn on that. 15624 if p.options.mode != config.ModePassThrough && p.isFileConsideredToHaveESMExports && !p.isControlFlowDead { 15625 if dot, ok := e.Left.Data.(*js_ast.EDot); ok { 15626 var name string 15627 var loc logger.Loc 15628 15629 switch target := dot.Target.Data.(type) { 15630 case *js_ast.EIdentifier: 15631 if symbol := &p.symbols[target.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound && 15632 ((symbol.OriginalName == "module" && dot.Name == "exports") || symbol.OriginalName == "exports") && 15633 !symbol.Flags.Has(ast.DidWarnAboutCommonJSInESM) { 15634 // "module.exports = ..." 15635 // "exports.something = ..." 15636 name = symbol.OriginalName 15637 loc = dot.Target.Loc 15638 symbol.Flags |= ast.DidWarnAboutCommonJSInESM 15639 } 15640 15641 case *js_ast.EDot: 15642 if target.Name == "exports" { 15643 if id, ok := target.Target.Data.(*js_ast.EIdentifier); ok { 15644 if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound && 15645 symbol.OriginalName == "module" && !symbol.Flags.Has(ast.DidWarnAboutCommonJSInESM) { 15646 // "module.exports.foo = ..." 15647 name = symbol.OriginalName 15648 loc = target.Target.Loc 15649 symbol.Flags |= ast.DidWarnAboutCommonJSInESM 15650 } 15651 } 15652 } 15653 } 15654 15655 if name != "" { 15656 kind := logger.Warning 15657 if p.suppressWarningsAboutWeirdCode { 15658 kind = logger.Debug 15659 } 15660 why, notes := p.whyESModule() 15661 if why == whyESMTypeModulePackageJSON { 15662 text := "Node's package format requires that CommonJS files in a \"type\": \"module\" package use the \".cjs\" file extension." 15663 if p.options.ts.Parse { 15664 text += " If you are using TypeScript, you can use the \".cts\" file extension with esbuild instead." 15665 } 15666 notes = append(notes, logger.MsgData{Text: text}) 15667 } 15668 p.log.AddIDWithNotes(logger.MsgID_JS_CommonJSVariableInESM, kind, &p.tracker, js_lexer.RangeOfIdentifier(p.source, loc), 15669 fmt.Sprintf("The CommonJS %q variable is treated as a global variable in an ECMAScript module and may not work as expected", name), 15670 notes) 15671 } 15672 } 15673 } 15674 } 15675 15676 case js_ast.BinOpAddAssign: 15677 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpAdd, e.Right); result.Data != nil { 15678 return result 15679 } 15680 15681 case js_ast.BinOpSubAssign: 15682 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpSub, e.Right); result.Data != nil { 15683 return result 15684 } 15685 15686 case js_ast.BinOpMulAssign: 15687 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpMul, e.Right); result.Data != nil { 15688 return result 15689 } 15690 15691 case js_ast.BinOpDivAssign: 15692 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpDiv, e.Right); result.Data != nil { 15693 return result 15694 } 15695 15696 case js_ast.BinOpRemAssign: 15697 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpRem, e.Right); result.Data != nil { 15698 return result 15699 } 15700 15701 case js_ast.BinOpPowAssign: 15702 // Lower the exponentiation operator for browsers that don't support it 15703 if p.options.unsupportedJSFeatures.Has(compat.ExponentOperator) { 15704 return p.lowerExponentiationAssignmentOperator(v.loc, e) 15705 } 15706 15707 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpPow, e.Right); result.Data != nil { 15708 return result 15709 } 15710 15711 case js_ast.BinOpShlAssign: 15712 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpShl, e.Right); result.Data != nil { 15713 return result 15714 } 15715 15716 case js_ast.BinOpShrAssign: 15717 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpShr, e.Right); result.Data != nil { 15718 return result 15719 } 15720 15721 case js_ast.BinOpUShrAssign: 15722 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpUShr, e.Right); result.Data != nil { 15723 return result 15724 } 15725 15726 case js_ast.BinOpBitwiseOrAssign: 15727 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpBitwiseOr, e.Right); result.Data != nil { 15728 return result 15729 } 15730 15731 case js_ast.BinOpBitwiseAndAssign: 15732 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpBitwiseAnd, e.Right); result.Data != nil { 15733 return result 15734 } 15735 15736 case js_ast.BinOpBitwiseXorAssign: 15737 if result := p.maybeLowerSetBinOp(e.Left, js_ast.BinOpBitwiseXor, e.Right); result.Data != nil { 15738 return result 15739 } 15740 15741 case js_ast.BinOpNullishCoalescingAssign: 15742 if value, ok := p.lowerNullishCoalescingAssignmentOperator(v.loc, e); ok { 15743 return value 15744 } 15745 15746 case js_ast.BinOpLogicalAndAssign: 15747 if value, ok := p.lowerLogicalAssignmentOperator(v.loc, e, js_ast.BinOpLogicalAnd); ok { 15748 return value 15749 } 15750 15751 case js_ast.BinOpLogicalOrAssign: 15752 if value, ok := p.lowerLogicalAssignmentOperator(v.loc, e, js_ast.BinOpLogicalOr); ok { 15753 return value 15754 } 15755 } 15756 15757 // "(a, b) + c" => "a, b + c" 15758 if p.options.minifySyntax && e.Op != js_ast.BinOpComma { 15759 if comma, ok := e.Left.Data.(*js_ast.EBinary); ok && comma.Op == js_ast.BinOpComma { 15760 return js_ast.JoinWithComma(comma.Left, js_ast.Expr{ 15761 Loc: comma.Right.Loc, 15762 Data: &js_ast.EBinary{ 15763 Op: e.Op, 15764 Left: comma.Right, 15765 Right: e.Right, 15766 }, 15767 }) 15768 } 15769 } 15770 15771 return js_ast.Expr{Loc: v.loc, Data: e} 15772 } 15773 15774 func remapExprLocsInJSON(expr *js_ast.Expr, table []logger.StringInJSTableEntry) { 15775 expr.Loc = logger.RemapStringInJSLoc(table, expr.Loc) 15776 15777 switch e := expr.Data.(type) { 15778 case *js_ast.EArray: 15779 e.CloseBracketLoc = logger.RemapStringInJSLoc(table, e.CloseBracketLoc) 15780 for i := range e.Items { 15781 remapExprLocsInJSON(&e.Items[i], table) 15782 } 15783 15784 case *js_ast.EObject: 15785 e.CloseBraceLoc = logger.RemapStringInJSLoc(table, e.CloseBraceLoc) 15786 for i := range e.Properties { 15787 remapExprLocsInJSON(&e.Properties[i].Key, table) 15788 remapExprLocsInJSON(&e.Properties[i].ValueOrNil, table) 15789 } 15790 } 15791 } 15792 15793 func (p *parser) handleGlobPattern(expr js_ast.Expr, kind ast.ImportKind, prefix string, assertOrWith *ast.ImportAssertOrWith) js_ast.Expr { 15794 pattern, approximateRange := p.globPatternFromExpr(expr) 15795 if pattern == nil { 15796 return js_ast.Expr{} 15797 } 15798 15799 var last helpers.GlobPart 15800 var parts []helpers.GlobPart 15801 15802 for _, part := range pattern { 15803 if part.isWildcard { 15804 if last.Wildcard == helpers.GlobNone { 15805 if !strings.HasSuffix(last.Prefix, "/") { 15806 // "`a${b}c`" => "a*c" 15807 last.Wildcard = helpers.GlobAllExceptSlash 15808 } else { 15809 // "`a/${b}c`" => "a/**/*c" 15810 last.Wildcard = helpers.GlobAllIncludingSlash 15811 parts = append(parts, last) 15812 last = helpers.GlobPart{Prefix: "/", Wildcard: helpers.GlobAllExceptSlash} 15813 } 15814 } 15815 } else if part.text != "" { 15816 if last.Wildcard != helpers.GlobNone { 15817 parts = append(parts, last) 15818 last = helpers.GlobPart{} 15819 } 15820 last.Prefix += part.text 15821 } 15822 } 15823 15824 parts = append(parts, last) 15825 15826 // Don't handle this if it's a string constant 15827 if len(parts) == 1 && parts[0].Wildcard == helpers.GlobNone { 15828 return js_ast.Expr{} 15829 } 15830 15831 // We currently only support relative globs 15832 if prefix := parts[0].Prefix; !strings.HasPrefix(prefix, "./") && !strings.HasPrefix(prefix, "../") { 15833 return js_ast.Expr{} 15834 } 15835 15836 ref := ast.InvalidRef 15837 15838 // Don't generate duplicate glob imports 15839 outer: 15840 for _, globPattern := range p.globPatternImports { 15841 // Check the kind 15842 if globPattern.kind != kind { 15843 continue 15844 } 15845 15846 // Check the parts 15847 if len(globPattern.parts) != len(parts) { 15848 continue 15849 } 15850 for i := range parts { 15851 if globPattern.parts[i] != parts[i] { 15852 continue outer 15853 } 15854 } 15855 15856 // Check the import assertions/attributes 15857 if assertOrWith == nil { 15858 if globPattern.assertOrWith != nil { 15859 continue 15860 } 15861 } else { 15862 if globPattern.assertOrWith == nil { 15863 continue 15864 } 15865 if assertOrWith.Keyword != globPattern.assertOrWith.Keyword { 15866 continue 15867 } 15868 a := assertOrWith.Entries 15869 b := globPattern.assertOrWith.Entries 15870 if len(a) != len(b) { 15871 continue 15872 } 15873 for i := range a { 15874 ai := a[i] 15875 bi := b[i] 15876 if !helpers.UTF16EqualsUTF16(ai.Key, bi.Key) || !helpers.UTF16EqualsUTF16(ai.Value, bi.Value) { 15877 continue outer 15878 } 15879 } 15880 } 15881 15882 // If we get here, then these are the same glob pattern 15883 ref = globPattern.ref 15884 break 15885 } 15886 15887 // If there's no duplicate glob import, then generate a new glob import 15888 if ref == ast.InvalidRef && prefix != "" { 15889 sb := strings.Builder{} 15890 sb.WriteString(prefix) 15891 15892 for _, part := range parts { 15893 gap := true 15894 for _, c := range part.Prefix { 15895 if !js_ast.IsIdentifierContinue(c) { 15896 gap = true 15897 } else { 15898 if gap { 15899 sb.WriteByte('_') 15900 gap = false 15901 } 15902 sb.WriteRune(c) 15903 } 15904 } 15905 } 15906 15907 name := sb.String() 15908 ref = p.newSymbol(ast.SymbolOther, name) 15909 p.moduleScope.Generated = append(p.moduleScope.Generated, ref) 15910 15911 p.globPatternImports = append(p.globPatternImports, globPatternImport{ 15912 assertOrWith: assertOrWith, 15913 parts: parts, 15914 name: name, 15915 approximateRange: approximateRange, 15916 ref: ref, 15917 kind: kind, 15918 }) 15919 } 15920 15921 p.recordUsage(ref) 15922 return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ 15923 Target: js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EIdentifier{Ref: ref}}, 15924 Args: []js_ast.Expr{expr}, 15925 }} 15926 } 15927 15928 type globPart struct { 15929 text string 15930 isWildcard bool 15931 } 15932 15933 func (p *parser) globPatternFromExpr(expr js_ast.Expr) ([]globPart, logger.Range) { 15934 switch e := expr.Data.(type) { 15935 case *js_ast.EString: 15936 return []globPart{{text: helpers.UTF16ToString(e.Value)}}, p.source.RangeOfString(expr.Loc) 15937 15938 case *js_ast.ETemplate: 15939 if e.TagOrNil.Data != nil { 15940 break 15941 } 15942 15943 pattern := make([]globPart, 0, 1+2*len(e.Parts)) 15944 pattern = append(pattern, globPart{text: helpers.UTF16ToString(e.HeadCooked)}) 15945 15946 for _, part := range e.Parts { 15947 if partPattern, _ := p.globPatternFromExpr(part.Value); partPattern != nil { 15948 pattern = append(pattern, partPattern...) 15949 } else { 15950 pattern = append(pattern, globPart{isWildcard: true}) 15951 } 15952 pattern = append(pattern, globPart{text: helpers.UTF16ToString(part.TailCooked)}) 15953 } 15954 15955 if len(e.Parts) == 0 { 15956 return pattern, p.source.RangeOfString(expr.Loc) 15957 } 15958 15959 text := p.source.Contents 15960 templateRange := logger.Range{Loc: e.HeadLoc} 15961 15962 for i := e.Parts[len(e.Parts)-1].TailLoc.Start; i < int32(len(text)); i++ { 15963 c := text[i] 15964 if c == '`' { 15965 templateRange.Len = i + 1 - templateRange.Loc.Start 15966 break 15967 } else if c == '\\' { 15968 i += 1 15969 } 15970 } 15971 15972 return pattern, templateRange 15973 15974 case *js_ast.EBinary: 15975 if e.Op != js_ast.BinOpAdd { 15976 break 15977 } 15978 15979 pattern, leftRange := p.globPatternFromExpr(e.Left) 15980 if pattern == nil { 15981 break 15982 } 15983 15984 if rightPattern, rightRange := p.globPatternFromExpr(e.Right); rightPattern != nil { 15985 pattern = append(pattern, rightPattern...) 15986 leftRange.Len = rightRange.End() - leftRange.Loc.Start 15987 return pattern, leftRange 15988 } 15989 15990 pattern = append(pattern, globPart{isWildcard: true}) 15991 15992 // Try to extend the left range by the right operand in some common cases 15993 switch right := e.Right.Data.(type) { 15994 case *js_ast.EIdentifier: 15995 leftRange.Len = js_lexer.RangeOfIdentifier(p.source, e.Right.Loc).End() - leftRange.Loc.Start 15996 15997 case *js_ast.ECall: 15998 if right.CloseParenLoc.Start > 0 { 15999 leftRange.Len = right.CloseParenLoc.Start + 1 - leftRange.Loc.Start 16000 } 16001 } 16002 16003 return pattern, leftRange 16004 } 16005 16006 return nil, logger.Range{} 16007 } 16008 16009 func (p *parser) convertSymbolUseToCall(ref ast.Ref, isSingleNonSpreadArgCall bool) { 16010 // Remove the normal symbol use 16011 use := p.symbolUses[ref] 16012 use.CountEstimate-- 16013 if use.CountEstimate == 0 { 16014 delete(p.symbolUses, ref) 16015 } else { 16016 p.symbolUses[ref] = use 16017 } 16018 16019 // Add a special symbol use instead 16020 if p.symbolCallUses == nil { 16021 p.symbolCallUses = make(map[ast.Ref]js_ast.SymbolCallUse) 16022 } 16023 callUse := p.symbolCallUses[ref] 16024 callUse.CallCountEstimate++ 16025 if isSingleNonSpreadArgCall { 16026 callUse.SingleArgNonSpreadCallCountEstimate++ 16027 } 16028 p.symbolCallUses[ref] = callUse 16029 } 16030 16031 func (p *parser) warnAboutImportNamespaceCall(target js_ast.Expr, kind importNamespaceCallKind) { 16032 if p.options.outputFormat != config.FormatPreserve { 16033 if id, ok := target.Data.(*js_ast.EIdentifier); ok && p.importItemsForNamespace[id.Ref].entries != nil { 16034 key := importNamespaceCall{ 16035 ref: id.Ref, 16036 kind: kind, 16037 } 16038 if p.importNamespaceCCMap == nil { 16039 p.importNamespaceCCMap = make(map[importNamespaceCall]bool) 16040 } 16041 16042 // Don't log a warning for the same identifier more than once 16043 if _, ok := p.importNamespaceCCMap[key]; ok { 16044 return 16045 } 16046 16047 p.importNamespaceCCMap[key] = true 16048 r := js_lexer.RangeOfIdentifier(p.source, target.Loc) 16049 16050 var notes []logger.MsgData 16051 name := p.symbols[id.Ref.InnerIndex].OriginalName 16052 if member, ok := p.moduleScope.Members[name]; ok && member.Ref == id.Ref { 16053 if star := p.source.RangeOfOperatorBefore(member.Loc, "*"); star.Len > 0 { 16054 if as := p.source.RangeOfOperatorBefore(member.Loc, "as"); as.Len > 0 && as.Loc.Start > star.Loc.Start { 16055 note := p.tracker.MsgData( 16056 logger.Range{Loc: star.Loc, Len: js_lexer.RangeOfIdentifier(p.source, member.Loc).End() - star.Loc.Start}, 16057 fmt.Sprintf("Consider changing %q to a default import instead:", name)) 16058 note.Location.Suggestion = name 16059 notes = append(notes, note) 16060 } 16061 } 16062 } 16063 16064 if p.options.ts.Parse { 16065 notes = append(notes, logger.MsgData{ 16066 Text: "Make sure to enable TypeScript's \"esModuleInterop\" setting so that TypeScript's type checker generates an error when you try to do this. " + 16067 "You can read more about this setting here: https://www.typescriptlang.org/tsconfig#esModuleInterop", 16068 }) 16069 } 16070 16071 var verb string 16072 var where string 16073 var noun string 16074 16075 switch kind { 16076 case exprKindCall: 16077 verb = "Calling" 16078 noun = "function" 16079 16080 case exprKindNew: 16081 verb = "Constructing" 16082 noun = "constructor" 16083 16084 case exprKindJSXTag: 16085 verb = "Using" 16086 where = " in a JSX expression" 16087 noun = "component" 16088 } 16089 16090 p.log.AddIDWithNotes(logger.MsgID_JS_CallImportNamespace, logger.Warning, &p.tracker, r, fmt.Sprintf( 16091 "%s %q%s will crash at run-time because it's an import namespace object, not a %s", 16092 verb, 16093 p.symbols[id.Ref.InnerIndex].OriginalName, 16094 where, 16095 noun, 16096 ), notes) 16097 } 16098 } 16099 } 16100 16101 func (p *parser) maybeMarkKnownGlobalConstructorAsPure(e *js_ast.ENew) { 16102 if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok { 16103 if symbol := p.symbols[id.Ref.InnerIndex]; symbol.Kind == ast.SymbolUnbound { 16104 switch symbol.OriginalName { 16105 case "WeakSet", "WeakMap": 16106 n := len(e.Args) 16107 16108 if n == 0 { 16109 // "new WeakSet()" is pure 16110 e.CanBeUnwrappedIfUnused = true 16111 break 16112 } 16113 16114 if n == 1 { 16115 switch arg := e.Args[0].Data.(type) { 16116 case *js_ast.ENull, *js_ast.EUndefined: 16117 // "new WeakSet(null)" is pure 16118 // "new WeakSet(void 0)" is pure 16119 e.CanBeUnwrappedIfUnused = true 16120 16121 case *js_ast.EArray: 16122 if len(arg.Items) == 0 { 16123 // "new WeakSet([])" is pure 16124 e.CanBeUnwrappedIfUnused = true 16125 } else { 16126 // "new WeakSet([x])" is impure because an exception is thrown if "x" is not an object 16127 } 16128 16129 default: 16130 // "new WeakSet(x)" is impure because the iterator for "x" could have side effects 16131 } 16132 } 16133 16134 case "Date": 16135 n := len(e.Args) 16136 16137 if n == 0 { 16138 // "new Date()" is pure 16139 e.CanBeUnwrappedIfUnused = true 16140 break 16141 } 16142 16143 if n == 1 { 16144 switch js_ast.KnownPrimitiveType(e.Args[0].Data) { 16145 case js_ast.PrimitiveNull, js_ast.PrimitiveUndefined, js_ast.PrimitiveBoolean, js_ast.PrimitiveNumber, js_ast.PrimitiveString: 16146 // "new Date('')" is pure 16147 // "new Date(0)" is pure 16148 // "new Date(null)" is pure 16149 // "new Date(true)" is pure 16150 // "new Date(false)" is pure 16151 // "new Date(undefined)" is pure 16152 e.CanBeUnwrappedIfUnused = true 16153 16154 default: 16155 // "new Date(x)" is impure because converting "x" to a string could have side effects 16156 } 16157 } 16158 16159 case "Set": 16160 n := len(e.Args) 16161 16162 if n == 0 { 16163 // "new Set()" is pure 16164 e.CanBeUnwrappedIfUnused = true 16165 break 16166 } 16167 16168 if n == 1 { 16169 switch e.Args[0].Data.(type) { 16170 case *js_ast.EArray, *js_ast.ENull, *js_ast.EUndefined: 16171 // "new Set([a, b, c])" is pure 16172 // "new Set(null)" is pure 16173 // "new Set(void 0)" is pure 16174 e.CanBeUnwrappedIfUnused = true 16175 16176 default: 16177 // "new Set(x)" is impure because the iterator for "x" could have side effects 16178 } 16179 } 16180 16181 case "Map": 16182 n := len(e.Args) 16183 16184 if n == 0 { 16185 // "new Map()" is pure 16186 e.CanBeUnwrappedIfUnused = true 16187 break 16188 } 16189 16190 if n == 1 { 16191 switch arg := e.Args[0].Data.(type) { 16192 case *js_ast.ENull, *js_ast.EUndefined: 16193 // "new Map(null)" is pure 16194 // "new Map(void 0)" is pure 16195 e.CanBeUnwrappedIfUnused = true 16196 16197 case *js_ast.EArray: 16198 allEntriesAreArrays := true 16199 for _, item := range arg.Items { 16200 if _, ok := item.Data.(*js_ast.EArray); !ok { 16201 // "new Map([x])" is impure because "x[0]" could have side effects 16202 allEntriesAreArrays = false 16203 break 16204 } 16205 } 16206 16207 // "new Map([[a, b], [c, d]])" is pure 16208 if allEntriesAreArrays { 16209 e.CanBeUnwrappedIfUnused = true 16210 } 16211 16212 default: 16213 // "new Map(x)" is impure because the iterator for "x" could have side effects 16214 } 16215 } 16216 } 16217 } 16218 } 16219 } 16220 16221 type identifierOpts struct { 16222 assignTarget js_ast.AssignTarget 16223 isCallTarget bool 16224 isDeleteTarget bool 16225 preferQuotedKey bool 16226 wasOriginallyIdentifier bool 16227 matchAgainstDefines bool 16228 } 16229 16230 func (p *parser) handleIdentifier(loc logger.Loc, e *js_ast.EIdentifier, opts identifierOpts) js_ast.Expr { 16231 ref := e.Ref 16232 16233 // Substitute inlined constants 16234 if p.options.minifySyntax { 16235 if value, ok := p.constValues[ref]; ok { 16236 p.ignoreUsage(ref) 16237 return js_ast.ConstValueToExpr(loc, value) 16238 } 16239 } 16240 16241 // Capture the "arguments" variable if necessary 16242 if p.fnOnlyDataVisit.argumentsRef != nil && ref == *p.fnOnlyDataVisit.argumentsRef { 16243 isInsideUnsupportedArrow := p.fnOrArrowDataVisit.isArrow && p.options.unsupportedJSFeatures.Has(compat.Arrow) 16244 isInsideUnsupportedAsyncArrow := p.fnOnlyDataVisit.isInsideAsyncArrowFn && p.options.unsupportedJSFeatures.Has(compat.AsyncAwait) 16245 if isInsideUnsupportedArrow || isInsideUnsupportedAsyncArrow { 16246 return js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: p.captureArguments()}} 16247 } 16248 } 16249 16250 // Create an error for assigning to an import namespace 16251 if (opts.assignTarget != js_ast.AssignTargetNone || 16252 (opts.isDeleteTarget && p.symbols[ref.InnerIndex].ImportItemStatus == ast.ImportItemGenerated)) && 16253 p.symbols[ref.InnerIndex].Kind == ast.SymbolImport { 16254 r := js_lexer.RangeOfIdentifier(p.source, loc) 16255 16256 // Try to come up with a setter name to try to make this message more understandable 16257 var setterHint string 16258 originalName := p.symbols[ref.InnerIndex].OriginalName 16259 if js_ast.IsIdentifier(originalName) && originalName != "_" { 16260 if len(originalName) == 1 || (len(originalName) > 1 && originalName[0] < utf8.RuneSelf) { 16261 setterHint = fmt.Sprintf(" (e.g. \"set%s%s\")", strings.ToUpper(originalName[:1]), originalName[1:]) 16262 } else { 16263 setterHint = fmt.Sprintf(" (e.g. \"set_%s\")", originalName) 16264 } 16265 } 16266 16267 notes := []logger.MsgData{{Text: "Imports are immutable in JavaScript. " + 16268 fmt.Sprintf("To modify the value of this import, you must export a setter function in the "+ 16269 "imported file%s and then import and call that function here instead.", setterHint)}} 16270 16271 if p.options.mode == config.ModeBundle { 16272 p.log.AddErrorWithNotes(&p.tracker, r, fmt.Sprintf("Cannot assign to import %q", originalName), notes) 16273 } else { 16274 kind := logger.Warning 16275 if p.suppressWarningsAboutWeirdCode { 16276 kind = logger.Debug 16277 } 16278 p.log.AddIDWithNotes(logger.MsgID_JS_AssignToImport, kind, &p.tracker, r, 16279 fmt.Sprintf("This assignment will throw because %q is an import", originalName), notes) 16280 } 16281 } 16282 16283 // Substitute an EImportIdentifier now if this has a namespace alias 16284 if opts.assignTarget == js_ast.AssignTargetNone && !opts.isDeleteTarget { 16285 symbol := &p.symbols[ref.InnerIndex] 16286 if nsAlias := symbol.NamespaceAlias; nsAlias != nil { 16287 data := p.dotOrMangledPropVisit( 16288 js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: nsAlias.NamespaceRef}}, 16289 symbol.OriginalName, loc) 16290 16291 // Handle references to namespaces or namespace members 16292 if tsMemberData, ok := p.refToTSNamespaceMemberData[nsAlias.NamespaceRef]; ok { 16293 if ns, ok := tsMemberData.(*js_ast.TSNamespaceMemberNamespace); ok { 16294 if member, ok := ns.ExportedMembers[nsAlias.Alias]; ok { 16295 switch m := member.Data.(type) { 16296 case *js_ast.TSNamespaceMemberEnumNumber: 16297 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: m.Value}}, nsAlias.Alias) 16298 16299 case *js_ast.TSNamespaceMemberEnumString: 16300 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: m.Value}}, nsAlias.Alias) 16301 16302 case *js_ast.TSNamespaceMemberNamespace: 16303 p.tsNamespaceTarget = data 16304 p.tsNamespaceMemberData = member.Data 16305 } 16306 } 16307 } 16308 } 16309 16310 return js_ast.Expr{Loc: loc, Data: data} 16311 } 16312 } 16313 16314 // Substitute an EImportIdentifier now if this is an import item 16315 if p.isImportItem[ref] { 16316 return js_ast.Expr{Loc: loc, Data: &js_ast.EImportIdentifier{ 16317 Ref: ref, 16318 PreferQuotedKey: opts.preferQuotedKey, 16319 WasOriginallyIdentifier: opts.wasOriginallyIdentifier, 16320 }} 16321 } 16322 16323 // Handle references to namespaces or namespace members 16324 if tsMemberData, ok := p.refToTSNamespaceMemberData[ref]; ok { 16325 switch m := tsMemberData.(type) { 16326 case *js_ast.TSNamespaceMemberEnumNumber: 16327 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.ENumber{Value: m.Value}}, p.symbols[ref.InnerIndex].OriginalName) 16328 16329 case *js_ast.TSNamespaceMemberEnumString: 16330 return p.wrapInlinedEnum(js_ast.Expr{Loc: loc, Data: &js_ast.EString{Value: m.Value}}, p.symbols[ref.InnerIndex].OriginalName) 16331 16332 case *js_ast.TSNamespaceMemberNamespace: 16333 p.tsNamespaceTarget = e 16334 p.tsNamespaceMemberData = tsMemberData 16335 } 16336 } 16337 16338 // Substitute a namespace export reference now if appropriate 16339 if p.options.ts.Parse { 16340 if nsRef, ok := p.isExportedInsideNamespace[ref]; ok { 16341 name := p.symbols[ref.InnerIndex].OriginalName 16342 16343 // Otherwise, create a property access on the namespace 16344 p.recordUsage(nsRef) 16345 propertyAccess := p.dotOrMangledPropVisit(js_ast.Expr{Loc: loc, Data: &js_ast.EIdentifier{Ref: nsRef}}, name, loc) 16346 if p.tsNamespaceTarget == e { 16347 p.tsNamespaceTarget = propertyAccess 16348 } 16349 return js_ast.Expr{Loc: loc, Data: propertyAccess} 16350 } 16351 } 16352 16353 // Swap references to the global "require" function with our "__require" stub 16354 if ref == p.requireRef && !opts.isCallTarget { 16355 if p.options.mode == config.ModeBundle && p.source.Index != runtime.SourceIndex && e != p.dotOrIndexTarget { 16356 p.log.AddID(logger.MsgID_JS_IndirectRequire, logger.Debug, &p.tracker, js_lexer.RangeOfIdentifier(p.source, loc), 16357 "Indirect calls to \"require\" will not be bundled") 16358 } 16359 16360 return p.valueToSubstituteForRequire(loc) 16361 } 16362 16363 // Mark any mutated symbols as mutable 16364 if opts.assignTarget != js_ast.AssignTargetNone { 16365 p.symbols[e.Ref.InnerIndex].Flags |= ast.CouldPotentiallyBeMutated 16366 } 16367 16368 return js_ast.Expr{Loc: loc, Data: e} 16369 } 16370 16371 type visitFnOpts struct { 16372 isMethod bool 16373 isDerivedClassCtor bool 16374 isLoweredPrivateMethod bool 16375 } 16376 16377 func (p *parser) visitFn(fn *js_ast.Fn, scopeLoc logger.Loc, opts visitFnOpts) { 16378 var decoratorScope *js_ast.Scope 16379 oldFnOrArrowData := p.fnOrArrowDataVisit 16380 oldFnOnlyData := p.fnOnlyDataVisit 16381 p.fnOrArrowDataVisit = fnOrArrowDataVisit{ 16382 isAsync: fn.IsAsync, 16383 isGenerator: fn.IsGenerator, 16384 isDerivedClassCtor: opts.isDerivedClassCtor, 16385 shouldLowerSuperPropertyAccess: (fn.IsAsync && p.options.unsupportedJSFeatures.Has(compat.AsyncAwait)) || opts.isLoweredPrivateMethod, 16386 } 16387 p.fnOnlyDataVisit = fnOnlyDataVisit{ 16388 isThisNested: true, 16389 isNewTargetAllowed: true, 16390 argumentsRef: &fn.ArgumentsRef, 16391 } 16392 16393 if opts.isMethod { 16394 decoratorScope = p.propMethodDecoratorScope 16395 p.fnOnlyDataVisit.innerClassNameRef = oldFnOnlyData.innerClassNameRef 16396 p.fnOnlyDataVisit.isInStaticClassContext = oldFnOnlyData.isInStaticClassContext 16397 } 16398 16399 if fn.Name != nil { 16400 p.recordDeclaredSymbol(fn.Name.Ref) 16401 } 16402 16403 p.pushScopeForVisitPass(js_ast.ScopeFunctionArgs, scopeLoc) 16404 p.visitArgs(fn.Args, visitArgsOpts{ 16405 hasRestArg: fn.HasRestArg, 16406 body: fn.Body.Block.Stmts, 16407 isUniqueFormalParameters: fn.IsUniqueFormalParameters, 16408 decoratorScope: decoratorScope, 16409 }) 16410 p.pushScopeForVisitPass(js_ast.ScopeFunctionBody, fn.Body.Loc) 16411 if fn.Name != nil { 16412 p.validateDeclaredSymbolName(fn.Name.Loc, p.symbols[fn.Name.Ref.InnerIndex].OriginalName) 16413 } 16414 fn.Body.Block.Stmts = p.visitStmtsAndPrependTempRefs(fn.Body.Block.Stmts, prependTempRefsOpts{fnBodyLoc: &fn.Body.Loc, kind: stmtsFnBody}) 16415 p.popScope() 16416 p.lowerFunction(&fn.IsAsync, &fn.IsGenerator, &fn.Args, fn.Body.Loc, &fn.Body.Block, nil, &fn.HasRestArg, false /* isArrow */) 16417 p.popScope() 16418 16419 p.fnOrArrowDataVisit = oldFnOrArrowData 16420 p.fnOnlyDataVisit = oldFnOnlyData 16421 } 16422 16423 func (p *parser) recordExport(loc logger.Loc, alias string, ref ast.Ref) { 16424 if name, ok := p.namedExports[alias]; ok { 16425 // Duplicate exports are an error 16426 p.log.AddErrorWithNotes(&p.tracker, js_lexer.RangeOfIdentifier(p.source, loc), 16427 fmt.Sprintf("Multiple exports with the same name %q", alias), 16428 []logger.MsgData{p.tracker.MsgData(js_lexer.RangeOfIdentifier(p.source, name.AliasLoc), 16429 fmt.Sprintf("The name %q was originally exported here:", alias))}) 16430 } else { 16431 p.namedExports[alias] = js_ast.NamedExport{AliasLoc: loc, Ref: ref} 16432 } 16433 } 16434 16435 type importsExportsScanResult struct { 16436 stmts []js_ast.Stmt 16437 keptImportEquals bool 16438 removedImportEquals bool 16439 } 16440 16441 // Returns true if this is an unused TypeScript import-equals statement 16442 func (p *parser) checkForUnusedTSImportEquals(s *js_ast.SLocal, result *importsExportsScanResult) bool { 16443 if s.WasTSImportEquals && !s.IsExport { 16444 decl := s.Decls[0] 16445 16446 // Skip to the underlying reference 16447 value := s.Decls[0].ValueOrNil 16448 for { 16449 if dot, ok := value.Data.(*js_ast.EDot); ok { 16450 value = dot.Target 16451 } else { 16452 break 16453 } 16454 } 16455 16456 // Is this an identifier reference and not a require() call? 16457 valueRef := ast.InvalidRef 16458 switch v := value.Data.(type) { 16459 case *js_ast.EIdentifier: 16460 valueRef = v.Ref 16461 case *js_ast.EImportIdentifier: 16462 valueRef = v.Ref 16463 } 16464 if valueRef != ast.InvalidRef { 16465 // Is this import statement unused? 16466 if ref := decl.Binding.Data.(*js_ast.BIdentifier).Ref; p.symbols[ref.InnerIndex].UseCountEstimate == 0 { 16467 // Also don't count the referenced identifier 16468 p.ignoreUsage(valueRef) 16469 16470 // Import-equals statements can come in any order. Removing one 16471 // could potentially cause another one to be removable too. 16472 // Continue iterating until a fixed point has been reached to make 16473 // sure we get them all. 16474 result.removedImportEquals = true 16475 return true 16476 } else { 16477 result.keptImportEquals = true 16478 } 16479 } 16480 } 16481 16482 return false 16483 } 16484 16485 func (p *parser) scanForUnusedTSImportEquals(stmts []js_ast.Stmt) (result importsExportsScanResult) { 16486 stmtsEnd := 0 16487 16488 for _, stmt := range stmts { 16489 if s, ok := stmt.Data.(*js_ast.SLocal); ok && p.checkForUnusedTSImportEquals(s, &result) { 16490 // Remove unused import-equals statements, since those likely 16491 // correspond to types instead of values 16492 continue 16493 } 16494 16495 // Filter out statements we skipped over 16496 stmts[stmtsEnd] = stmt 16497 stmtsEnd++ 16498 } 16499 16500 result.stmts = stmts[:stmtsEnd] 16501 return 16502 } 16503 16504 func (p *parser) scanForImportsAndExports(stmts []js_ast.Stmt) (result importsExportsScanResult) { 16505 unusedImportFlags := p.options.ts.Config.UnusedImportFlags() 16506 stmtsEnd := 0 16507 16508 for _, stmt := range stmts { 16509 switch s := stmt.Data.(type) { 16510 case *js_ast.SImport: 16511 record := &p.importRecords[s.ImportRecordIndex] 16512 16513 // We implement TypeScript's "preserveValueImports" tsconfig.json setting 16514 // to support the use case of compiling partial modules for compile-to- 16515 // JavaScript languages such as Svelte. These languages try to reference 16516 // imports in ways that are impossible for TypeScript and esbuild to know 16517 // about when they are only given a partial module to compile. Here is an 16518 // example of some Svelte code that contains a TypeScript snippet: 16519 // 16520 // <script lang="ts"> 16521 // import Counter from './Counter.svelte'; 16522 // export let name: string = 'world'; 16523 // </script> 16524 // <main> 16525 // <h1>Hello {name}!</h1> 16526 // <Counter /> 16527 // </main> 16528 // 16529 // Tools that use esbuild to compile TypeScript code inside a Svelte 16530 // file like this only give esbuild the contents of the <script> tag. 16531 // The "preserveValueImports" setting avoids removing unused import 16532 // names, which means additional code appended after the TypeScript- 16533 // to-JavaScript conversion can still access those unused imports. 16534 // 16535 // There are two scenarios where we don't do this: 16536 // 16537 // * If we're bundling, then we know we aren't being used to compile 16538 // a partial module. The parser is seeing the entire code for the 16539 // module so it's safe to remove unused imports. And also we don't 16540 // want the linker to generate errors about missing imports if the 16541 // imported file is also in the bundle. 16542 // 16543 // * If identifier minification is enabled, then using esbuild as a 16544 // partial-module transform library wouldn't work anyway because 16545 // the names wouldn't match. And that means we're minifying so the 16546 // user is expecting the output to be as small as possible. So we 16547 // should omit unused imports. 16548 // 16549 keepUnusedImports := p.options.ts.Parse && (unusedImportFlags&config.TSUnusedImport_KeepValues) != 0 && 16550 p.options.mode != config.ModeBundle && !p.options.minifyIdentifiers 16551 16552 // Forbid non-default imports for JSON import assertions 16553 if (record.Flags&ast.AssertTypeJSON) != 0 && p.options.mode == config.ModeBundle && s.Items != nil { 16554 for _, item := range *s.Items { 16555 if p.options.ts.Parse && p.tsUseCounts[item.Name.Ref.InnerIndex] == 0 && (unusedImportFlags&config.TSUnusedImport_KeepValues) == 0 { 16556 // Do not count imports that TypeScript interprets as type annotations 16557 continue 16558 } 16559 if item.Alias != "default" { 16560 p.log.AddErrorWithNotes(&p.tracker, js_lexer.RangeOfIdentifier(p.source, item.AliasLoc), 16561 fmt.Sprintf("Cannot use non-default import %q with a JSON import assertion", item.Alias), 16562 p.notesForAssertTypeJSON(record, item.Alias)) 16563 } 16564 } 16565 } 16566 16567 // TypeScript always trims unused imports. This is important for 16568 // correctness since some imports might be fake (only in the type 16569 // system and used for type-only imports). 16570 if (p.options.minifySyntax || p.options.ts.Parse) && !keepUnusedImports { 16571 foundImports := false 16572 isUnusedInTypeScript := true 16573 16574 // Remove the default name if it's unused 16575 if s.DefaultName != nil { 16576 foundImports = true 16577 symbol := p.symbols[s.DefaultName.Ref.InnerIndex] 16578 16579 // TypeScript has a separate definition of unused 16580 if p.options.ts.Parse && (p.tsUseCounts[s.DefaultName.Ref.InnerIndex] != 0 || (p.options.ts.Config.UnusedImportFlags()&config.TSUnusedImport_KeepValues) != 0) { 16581 isUnusedInTypeScript = false 16582 } 16583 16584 // Remove the symbol if it's never used outside a dead code region 16585 if symbol.UseCountEstimate == 0 && (p.options.ts.Parse || !p.moduleScope.ContainsDirectEval) { 16586 s.DefaultName = nil 16587 } 16588 } 16589 16590 // Remove the star import if it's unused 16591 if s.StarNameLoc != nil { 16592 foundImports = true 16593 symbol := p.symbols[s.NamespaceRef.InnerIndex] 16594 16595 // TypeScript has a separate definition of unused 16596 if p.options.ts.Parse && (p.tsUseCounts[s.NamespaceRef.InnerIndex] != 0 || (p.options.ts.Config.UnusedImportFlags()&config.TSUnusedImport_KeepValues) != 0) { 16597 isUnusedInTypeScript = false 16598 } 16599 16600 // Remove the symbol if it's never used outside a dead code region 16601 if symbol.UseCountEstimate == 0 && (p.options.ts.Parse || !p.moduleScope.ContainsDirectEval) { 16602 // Make sure we don't remove this if it was used for a property 16603 // access while bundling 16604 if importItems, ok := p.importItemsForNamespace[s.NamespaceRef]; ok && len(importItems.entries) == 0 { 16605 s.StarNameLoc = nil 16606 } 16607 } 16608 } 16609 16610 // Remove items if they are unused 16611 if s.Items != nil { 16612 foundImports = true 16613 itemsEnd := 0 16614 16615 for _, item := range *s.Items { 16616 symbol := p.symbols[item.Name.Ref.InnerIndex] 16617 16618 // TypeScript has a separate definition of unused 16619 if p.options.ts.Parse && (p.tsUseCounts[item.Name.Ref.InnerIndex] != 0 || (p.options.ts.Config.UnusedImportFlags()&config.TSUnusedImport_KeepValues) != 0) { 16620 isUnusedInTypeScript = false 16621 } 16622 16623 // Remove the symbol if it's never used outside a dead code region 16624 if symbol.UseCountEstimate != 0 || (!p.options.ts.Parse && p.moduleScope.ContainsDirectEval) { 16625 (*s.Items)[itemsEnd] = item 16626 itemsEnd++ 16627 } 16628 } 16629 16630 // Filter the array by taking a slice 16631 if itemsEnd == 0 { 16632 s.Items = nil 16633 } else { 16634 *s.Items = (*s.Items)[:itemsEnd] 16635 } 16636 } 16637 16638 // Omit this statement if we're parsing TypeScript and all imports are 16639 // unused. Note that this is distinct from the case where there were 16640 // no imports at all (e.g. "import 'foo'"). In that case we want to keep 16641 // the statement because the user is clearly trying to import the module 16642 // for side effects. 16643 // 16644 // This culling is important for correctness when parsing TypeScript 16645 // because a) the TypeScript compiler does ths and we want to match it 16646 // and b) this may be a fake module that only exists in the type system 16647 // and doesn't actually exist in reality. 16648 // 16649 // We do not want to do this culling in JavaScript though because the 16650 // module may have side effects even if all imports are unused. 16651 if p.options.ts.Parse && foundImports && isUnusedInTypeScript && (unusedImportFlags&config.TSUnusedImport_KeepStmt) == 0 { 16652 // Ignore import records with a pre-filled source index. These are 16653 // for injected files and we definitely do not want to trim these. 16654 if !record.SourceIndex.IsValid() && !record.CopySourceIndex.IsValid() { 16655 record.Flags |= ast.IsUnused 16656 continue 16657 } 16658 } 16659 } 16660 16661 if p.options.mode != config.ModePassThrough { 16662 if s.StarNameLoc != nil { 16663 // "importItemsForNamespace" has property accesses off the namespace 16664 if importItems, ok := p.importItemsForNamespace[s.NamespaceRef]; ok && len(importItems.entries) > 0 { 16665 // Sort keys for determinism 16666 sorted := make([]string, 0, len(importItems.entries)) 16667 for alias := range importItems.entries { 16668 sorted = append(sorted, alias) 16669 } 16670 sort.Strings(sorted) 16671 16672 // Create named imports for these property accesses. This will 16673 // cause missing imports to generate useful warnings. 16674 // 16675 // It will also improve bundling efficiency for internal imports 16676 // by still converting property accesses off the namespace into 16677 // bare identifiers even if the namespace is still needed. 16678 for _, alias := range sorted { 16679 name := importItems.entries[alias] 16680 p.namedImports[name.Ref] = js_ast.NamedImport{ 16681 Alias: alias, 16682 AliasLoc: name.Loc, 16683 NamespaceRef: s.NamespaceRef, 16684 ImportRecordIndex: s.ImportRecordIndex, 16685 } 16686 16687 // Make sure the printer prints this as a property access 16688 p.symbols[name.Ref.InnerIndex].NamespaceAlias = &ast.NamespaceAlias{ 16689 NamespaceRef: s.NamespaceRef, 16690 Alias: alias, 16691 } 16692 16693 // Also record these automatically-generated top-level namespace alias symbols 16694 p.declaredSymbols = append(p.declaredSymbols, js_ast.DeclaredSymbol{ 16695 Ref: name.Ref, 16696 IsTopLevel: true, 16697 }) 16698 } 16699 } 16700 } 16701 16702 if s.DefaultName != nil { 16703 p.namedImports[s.DefaultName.Ref] = js_ast.NamedImport{ 16704 Alias: "default", 16705 AliasLoc: s.DefaultName.Loc, 16706 NamespaceRef: s.NamespaceRef, 16707 ImportRecordIndex: s.ImportRecordIndex, 16708 } 16709 } 16710 16711 if s.StarNameLoc != nil { 16712 p.namedImports[s.NamespaceRef] = js_ast.NamedImport{ 16713 AliasIsStar: true, 16714 AliasLoc: *s.StarNameLoc, 16715 NamespaceRef: ast.InvalidRef, 16716 ImportRecordIndex: s.ImportRecordIndex, 16717 } 16718 } 16719 16720 if s.Items != nil { 16721 for _, item := range *s.Items { 16722 p.namedImports[item.Name.Ref] = js_ast.NamedImport{ 16723 Alias: item.Alias, 16724 AliasLoc: item.AliasLoc, 16725 NamespaceRef: s.NamespaceRef, 16726 ImportRecordIndex: s.ImportRecordIndex, 16727 } 16728 } 16729 } 16730 } 16731 16732 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, s.ImportRecordIndex) 16733 16734 if s.StarNameLoc != nil { 16735 record.Flags |= ast.ContainsImportStar 16736 } 16737 16738 if s.DefaultName != nil { 16739 record.Flags |= ast.ContainsDefaultAlias 16740 } else if s.Items != nil { 16741 for _, item := range *s.Items { 16742 if item.Alias == "default" { 16743 record.Flags |= ast.ContainsDefaultAlias 16744 } else if item.Alias == "__esModule" { 16745 record.Flags |= ast.ContainsESModuleAlias 16746 } 16747 } 16748 } 16749 16750 case *js_ast.SFunction: 16751 if s.IsExport { 16752 p.recordExport(s.Fn.Name.Loc, p.symbols[s.Fn.Name.Ref.InnerIndex].OriginalName, s.Fn.Name.Ref) 16753 } 16754 16755 case *js_ast.SClass: 16756 if s.IsExport { 16757 p.recordExport(s.Class.Name.Loc, p.symbols[s.Class.Name.Ref.InnerIndex].OriginalName, s.Class.Name.Ref) 16758 } 16759 16760 case *js_ast.SLocal: 16761 if s.IsExport { 16762 js_ast.ForEachIdentifierBindingInDecls(s.Decls, func(loc logger.Loc, b *js_ast.BIdentifier) { 16763 p.recordExport(loc, p.symbols[b.Ref.InnerIndex].OriginalName, b.Ref) 16764 }) 16765 } 16766 16767 // Remove unused import-equals statements, since those likely 16768 // correspond to types instead of values 16769 if p.checkForUnusedTSImportEquals(s, &result) { 16770 continue 16771 } 16772 16773 case *js_ast.SExportDefault: 16774 p.recordExport(s.DefaultName.Loc, "default", s.DefaultName.Ref) 16775 16776 case *js_ast.SExportClause: 16777 for _, item := range s.Items { 16778 p.recordExport(item.AliasLoc, item.Alias, item.Name.Ref) 16779 } 16780 16781 case *js_ast.SExportStar: 16782 record := &p.importRecords[s.ImportRecordIndex] 16783 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, s.ImportRecordIndex) 16784 16785 if s.Alias != nil { 16786 // "export * as ns from 'path'" 16787 p.namedImports[s.NamespaceRef] = js_ast.NamedImport{ 16788 AliasIsStar: true, 16789 AliasLoc: s.Alias.Loc, 16790 NamespaceRef: ast.InvalidRef, 16791 ImportRecordIndex: s.ImportRecordIndex, 16792 IsExported: true, 16793 } 16794 p.recordExport(s.Alias.Loc, s.Alias.OriginalName, s.NamespaceRef) 16795 16796 record.Flags |= ast.ContainsImportStar 16797 } else { 16798 // "export * from 'path'" 16799 p.exportStarImportRecords = append(p.exportStarImportRecords, s.ImportRecordIndex) 16800 } 16801 16802 case *js_ast.SExportFrom: 16803 record := &p.importRecords[s.ImportRecordIndex] 16804 p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, s.ImportRecordIndex) 16805 16806 for _, item := range s.Items { 16807 // Note that the imported alias is not item.Alias, which is the 16808 // exported alias. This is somewhat confusing because each 16809 // SExportFrom statement is basically SImport + SExportClause in one. 16810 p.namedImports[item.Name.Ref] = js_ast.NamedImport{ 16811 Alias: item.OriginalName, 16812 AliasLoc: item.Name.Loc, 16813 NamespaceRef: s.NamespaceRef, 16814 ImportRecordIndex: s.ImportRecordIndex, 16815 IsExported: true, 16816 } 16817 p.recordExport(item.Name.Loc, item.Alias, item.Name.Ref) 16818 16819 if item.OriginalName == "default" { 16820 record.Flags |= ast.ContainsDefaultAlias 16821 } else if item.OriginalName == "__esModule" { 16822 record.Flags |= ast.ContainsESModuleAlias 16823 } 16824 } 16825 16826 // Forbid non-default imports for JSON import assertions 16827 if (record.Flags&ast.AssertTypeJSON) != 0 && p.options.mode == config.ModeBundle { 16828 for _, item := range s.Items { 16829 if item.OriginalName != "default" { 16830 p.log.AddErrorWithNotes(&p.tracker, js_lexer.RangeOfIdentifier(p.source, item.Name.Loc), 16831 fmt.Sprintf("Cannot use non-default import %q with a JSON import assertion", item.OriginalName), 16832 p.notesForAssertTypeJSON(record, item.OriginalName)) 16833 } 16834 } 16835 } 16836 16837 // TypeScript always trims unused re-exports. This is important for 16838 // correctness since some re-exports might be fake (only in the type 16839 // system and used for type-only stuff). 16840 if p.options.ts.Parse && len(s.Items) == 0 && (unusedImportFlags&config.TSUnusedImport_KeepStmt) == 0 { 16841 continue 16842 } 16843 } 16844 16845 // Filter out statements we skipped over 16846 stmts[stmtsEnd] = stmt 16847 stmtsEnd++ 16848 } 16849 16850 result.stmts = stmts[:stmtsEnd] 16851 return 16852 } 16853 16854 func (p *parser) appendPart(parts []js_ast.Part, stmts []js_ast.Stmt) []js_ast.Part { 16855 p.symbolUses = make(map[ast.Ref]js_ast.SymbolUse) 16856 p.importSymbolPropertyUses = nil 16857 p.symbolCallUses = nil 16858 p.declaredSymbols = nil 16859 p.importRecordsForCurrentPart = nil 16860 p.scopesForCurrentPart = nil 16861 16862 part := js_ast.Part{ 16863 Stmts: p.visitStmtsAndPrependTempRefs(stmts, prependTempRefsOpts{}), 16864 SymbolUses: p.symbolUses, 16865 } 16866 16867 // Sanity check 16868 if p.currentScope != p.moduleScope { 16869 panic("Internal error: Scope stack imbalance") 16870 } 16871 16872 // Insert any relocated variable statements now 16873 if len(p.relocatedTopLevelVars) > 0 { 16874 alreadyDeclared := make(map[ast.Ref]bool) 16875 for _, local := range p.relocatedTopLevelVars { 16876 // Follow links because "var" declarations may be merged due to hoisting 16877 for { 16878 link := p.symbols[local.Ref.InnerIndex].Link 16879 if link == ast.InvalidRef { 16880 break 16881 } 16882 local.Ref = link 16883 } 16884 16885 // Only declare a given relocated variable once 16886 if !alreadyDeclared[local.Ref] { 16887 alreadyDeclared[local.Ref] = true 16888 part.Stmts = append(part.Stmts, js_ast.Stmt{Loc: local.Loc, Data: &js_ast.SLocal{ 16889 Decls: []js_ast.Decl{{ 16890 Binding: js_ast.Binding{Loc: local.Loc, Data: &js_ast.BIdentifier{Ref: local.Ref}}, 16891 }}, 16892 }}) 16893 } 16894 } 16895 p.relocatedTopLevelVars = nil 16896 } 16897 16898 if len(part.Stmts) > 0 { 16899 var flags js_ast.StmtsCanBeRemovedIfUnusedFlags 16900 if p.options.mode == config.ModePassThrough { 16901 // Exports are tracked separately, so export clauses can normally always 16902 // be removed. Except we should keep them if we're not doing any format 16903 // conversion because exports are not re-emitted in that case. 16904 flags |= js_ast.KeepExportClauses 16905 } 16906 part.CanBeRemovedIfUnused = p.astHelpers.StmtsCanBeRemovedIfUnused(part.Stmts, flags) 16907 part.DeclaredSymbols = p.declaredSymbols 16908 part.ImportRecordIndices = p.importRecordsForCurrentPart 16909 part.ImportSymbolPropertyUses = p.importSymbolPropertyUses 16910 part.SymbolCallUses = p.symbolCallUses 16911 part.Scopes = p.scopesForCurrentPart 16912 parts = append(parts, part) 16913 } 16914 return parts 16915 } 16916 16917 func newParser(log logger.Log, source logger.Source, lexer js_lexer.Lexer, options *Options) *parser { 16918 if options.defines == nil { 16919 defaultDefines := config.ProcessDefines(nil) 16920 options.defines = &defaultDefines 16921 } 16922 16923 p := &parser{ 16924 log: log, 16925 source: source, 16926 tracker: logger.MakeLineColumnTracker(&source), 16927 lexer: lexer, 16928 allowIn: true, 16929 options: *options, 16930 runtimeImports: make(map[string]ast.LocRef), 16931 promiseRef: ast.InvalidRef, 16932 regExpRef: ast.InvalidRef, 16933 afterArrowBodyLoc: logger.Loc{Start: -1}, 16934 firstJSXElementLoc: logger.Loc{Start: -1}, 16935 importMetaRef: ast.InvalidRef, 16936 superCtorRef: ast.InvalidRef, 16937 16938 // For lowering private methods 16939 weakMapRef: ast.InvalidRef, 16940 weakSetRef: ast.InvalidRef, 16941 privateGetters: make(map[ast.Ref]ast.Ref), 16942 privateSetters: make(map[ast.Ref]ast.Ref), 16943 16944 // These are for TypeScript 16945 refToTSNamespaceMemberData: make(map[ast.Ref]js_ast.TSNamespaceMemberData), 16946 emittedNamespaceVars: make(map[ast.Ref]bool), 16947 isExportedInsideNamespace: make(map[ast.Ref]ast.Ref), 16948 localTypeNames: make(map[string]bool), 16949 16950 // These are for handling ES6 imports and exports 16951 importItemsForNamespace: make(map[ast.Ref]namespaceImportItems), 16952 isImportItem: make(map[ast.Ref]bool), 16953 namedImports: make(map[ast.Ref]js_ast.NamedImport), 16954 namedExports: make(map[string]js_ast.NamedExport), 16955 16956 // For JSX runtime imports 16957 jsxRuntimeImports: make(map[string]ast.LocRef), 16958 jsxLegacyImports: make(map[string]ast.LocRef), 16959 16960 suppressWarningsAboutWeirdCode: helpers.IsInsideNodeModules(source.KeyPath.Text), 16961 } 16962 16963 if len(options.dropLabels) > 0 { 16964 p.dropLabelsMap = make(map[string]struct{}) 16965 for _, name := range options.dropLabels { 16966 p.dropLabelsMap[name] = struct{}{} 16967 } 16968 } 16969 16970 if !options.minifyWhitespace { 16971 p.exprComments = make(map[logger.Loc][]string) 16972 } 16973 16974 p.astHelpers = js_ast.MakeHelperContext(func(ref ast.Ref) bool { 16975 return p.symbols[ref.InnerIndex].Kind == ast.SymbolUnbound 16976 }) 16977 16978 p.pushScopeForParsePass(js_ast.ScopeEntry, logger.Loc{Start: locModuleScope}) 16979 16980 return p 16981 } 16982 16983 var defaultJSXFactory = []string{"React", "createElement"} 16984 var defaultJSXFragment = []string{"React", "Fragment"} 16985 16986 const defaultJSXImportSource = "react" 16987 16988 func Parse(log logger.Log, source logger.Source, options Options) (result js_ast.AST, ok bool) { 16989 ok = true 16990 defer func() { 16991 r := recover() 16992 if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic { 16993 ok = false 16994 } else if r != nil { 16995 panic(r) 16996 } 16997 }() 16998 16999 // Default options for JSX elements 17000 if len(options.jsx.Factory.Parts) == 0 { 17001 options.jsx.Factory = config.DefineExpr{Parts: defaultJSXFactory} 17002 } 17003 if len(options.jsx.Fragment.Parts) == 0 && options.jsx.Fragment.Constant == nil { 17004 options.jsx.Fragment = config.DefineExpr{Parts: defaultJSXFragment} 17005 } 17006 if len(options.jsx.ImportSource) == 0 { 17007 options.jsx.ImportSource = defaultJSXImportSource 17008 } 17009 17010 p := newParser(log, source, js_lexer.NewLexer(log, source, options.ts), &options) 17011 17012 // Consume a leading hashbang comment 17013 hashbang := "" 17014 if p.lexer.Token == js_lexer.THashbang { 17015 hashbang = p.lexer.Identifier.String 17016 p.lexer.Next() 17017 } 17018 17019 // Allow top-level await 17020 p.fnOrArrowDataParse.await = allowExpr 17021 p.fnOrArrowDataParse.isTopLevel = true 17022 17023 // Parse the file in the first pass, but do not bind symbols 17024 stmts := p.parseStmtsUpTo(js_lexer.TEndOfFile, parseStmtOpts{ 17025 isModuleScope: true, 17026 allowDirectivePrologue: true, 17027 }) 17028 p.prepareForVisitPass() 17029 17030 // Insert a "use strict" directive if "alwaysStrict" is active 17031 var directives []string 17032 if tsAlwaysStrict := p.options.tsAlwaysStrict; tsAlwaysStrict != nil && tsAlwaysStrict.Value { 17033 directives = append(directives, "use strict") 17034 } 17035 17036 // Strip off all leading directives 17037 { 17038 totalCount := 0 17039 keptCount := 0 17040 17041 for _, stmt := range stmts { 17042 switch s := stmt.Data.(type) { 17043 case *js_ast.SComment: 17044 stmts[keptCount] = stmt 17045 keptCount++ 17046 totalCount++ 17047 continue 17048 17049 case *js_ast.SDirective: 17050 if p.isStrictMode() && s.LegacyOctalLoc.Start > 0 { 17051 p.markStrictModeFeature(legacyOctalEscape, p.source.RangeOfLegacyOctalEscape(s.LegacyOctalLoc), "") 17052 } 17053 directive := helpers.UTF16ToString(s.Value) 17054 17055 // Remove duplicate directives 17056 found := false 17057 for _, existing := range directives { 17058 if existing == directive { 17059 found = true 17060 break 17061 } 17062 } 17063 if !found { 17064 directives = append(directives, directive) 17065 } 17066 17067 // Remove this directive from the statement list 17068 totalCount++ 17069 continue 17070 } 17071 17072 // Stop when the directive prologue ends 17073 break 17074 } 17075 17076 if keptCount < totalCount { 17077 stmts = append(stmts[:keptCount], stmts[totalCount:]...) 17078 } 17079 } 17080 17081 // Add an empty part for the namespace export that we can fill in later 17082 nsExportPart := js_ast.Part{ 17083 SymbolUses: make(map[ast.Ref]js_ast.SymbolUse), 17084 CanBeRemovedIfUnused: true, 17085 } 17086 17087 var before = []js_ast.Part{nsExportPart} 17088 var parts []js_ast.Part 17089 var after []js_ast.Part 17090 17091 // Insert any injected import statements now that symbols have been declared 17092 for _, file := range p.options.injectedFiles { 17093 exportsNoConflict := make([]string, 0, len(file.Exports)) 17094 symbols := make(map[string]ast.LocRef) 17095 17096 if file.DefineName != "" { 17097 ref := p.newSymbol(ast.SymbolOther, file.DefineName) 17098 p.moduleScope.Generated = append(p.moduleScope.Generated, ref) 17099 symbols["default"] = ast.LocRef{Ref: ref} 17100 exportsNoConflict = append(exportsNoConflict, "default") 17101 p.injectedDefineSymbols = append(p.injectedDefineSymbols, ref) 17102 } else { 17103 nextExport: 17104 for _, export := range file.Exports { 17105 // Skip injecting this symbol if it's already declared locally (i.e. it's not a reference to a global) 17106 if _, ok := p.moduleScope.Members[export.Alias]; ok { 17107 continue 17108 } 17109 17110 parts := strings.Split(export.Alias, ".") 17111 17112 // The key must be a dot-separated identifier list 17113 for _, part := range parts { 17114 if !js_ast.IsIdentifier(part) { 17115 continue nextExport 17116 } 17117 } 17118 17119 ref := p.newSymbol(ast.SymbolInjected, export.Alias) 17120 symbols[export.Alias] = ast.LocRef{Ref: ref} 17121 if len(parts) == 1 { 17122 // Handle the identifier case by generating an injected symbol directly 17123 p.moduleScope.Members[export.Alias] = js_ast.ScopeMember{Ref: ref} 17124 } else { 17125 // Handle the dot case using a map. This map is similar to the map 17126 // "options.defines.DotDefines" but is kept separate instead of being 17127 // implemented using the same mechanism because we allow you to use 17128 // "define" to rewrite something to an injected symbol (i.e. we allow 17129 // two levels of mappings). This was historically necessary to be able 17130 // to map a dot name to an injected symbol because we previously didn't 17131 // support dot names as injected symbols. But now dot names as injected 17132 // symbols has been implemented, so supporting two levels of mappings 17133 // is only for backward-compatibility. 17134 if p.injectedDotNames == nil { 17135 p.injectedDotNames = make(map[string][]injectedDotName) 17136 } 17137 tail := parts[len(parts)-1] 17138 p.injectedDotNames[tail] = append(p.injectedDotNames[tail], injectedDotName{parts: parts, injectedDefineIndex: uint32(len(p.injectedDefineSymbols))}) 17139 p.injectedDefineSymbols = append(p.injectedDefineSymbols, ref) 17140 } 17141 exportsNoConflict = append(exportsNoConflict, export.Alias) 17142 if p.injectedSymbolSources == nil { 17143 p.injectedSymbolSources = make(map[ast.Ref]injectedSymbolSource) 17144 } 17145 p.injectedSymbolSources[ref] = injectedSymbolSource{ 17146 source: file.Source, 17147 loc: export.Loc, 17148 } 17149 } 17150 } 17151 17152 if file.IsCopyLoader { 17153 before, _ = p.generateImportStmt(file.Source.KeyPath.Text, logger.Range{}, exportsNoConflict, before, symbols, nil, &file.Source.Index) 17154 } else { 17155 before, _ = p.generateImportStmt(file.Source.KeyPath.Text, logger.Range{}, exportsNoConflict, before, symbols, &file.Source.Index, nil) 17156 } 17157 } 17158 17159 // When "using" declarations appear at the top level, we change all TDZ 17160 // variables in the top-level scope into "var" so that they aren't harmed 17161 // when they are moved into the try/catch statement that lowering will 17162 // generate. 17163 // 17164 // This is necessary because exported function declarations must be hoisted 17165 // outside of the try/catch statement because they can be evaluated before 17166 // this module is evaluated due to ESM cross-file function hoisting. And 17167 // these function bodies might reference anything else in this scope, which 17168 // must still work when those things are moved inside a try/catch statement. 17169 // 17170 // Before: 17171 // 17172 // using foo = get() 17173 // export function fn() { 17174 // return [foo, new Bar] 17175 // } 17176 // class Bar {} 17177 // 17178 // After ("fn" is hoisted, "Bar" is converted to "var"): 17179 // 17180 // export function fn() { 17181 // return [foo, new Bar] 17182 // } 17183 // try { 17184 // var foo = get(); 17185 // var Bar = class {}; 17186 // } catch (_) { 17187 // ... 17188 // } finally { 17189 // ... 17190 // } 17191 // 17192 // This is also necessary because other code might be appended to the code 17193 // that we're processing and expect to be able to access top-level variables. 17194 p.willWrapModuleInTryCatchForUsing = p.shouldLowerUsingDeclarations(stmts) 17195 17196 // Bind symbols in a second pass over the AST. I started off doing this in a 17197 // single pass, but it turns out it's pretty much impossible to do this 17198 // correctly while handling arrow functions because of the grammar 17199 // ambiguities. 17200 // 17201 // Note that top-level lowered "using" declarations disable tree-shaking 17202 // because we only do tree-shaking on top-level statements and lowering 17203 // a top-level "using" declaration moves all top-level statements into a 17204 // nested scope. 17205 if !p.options.treeShaking || p.willWrapModuleInTryCatchForUsing { 17206 // When tree shaking is disabled, everything comes in a single part 17207 parts = p.appendPart(parts, stmts) 17208 } else { 17209 var preprocessedEnums map[int][]js_ast.Part 17210 if p.scopesInOrderForEnum != nil { 17211 // Preprocess TypeScript enums to improve code generation. Otherwise 17212 // uses of an enum before that enum has been declared won't be inlined: 17213 // 17214 // console.log(Foo.FOO) // We want "FOO" to be inlined here 17215 // const enum Foo { FOO = 0 } 17216 // 17217 // The TypeScript compiler itself contains code with this pattern, so 17218 // it's important to implement this optimization. 17219 for i, stmt := range stmts { 17220 if _, ok := stmt.Data.(*js_ast.SEnum); ok { 17221 if preprocessedEnums == nil { 17222 preprocessedEnums = make(map[int][]js_ast.Part) 17223 } 17224 oldScopesInOrder := p.scopesInOrder 17225 p.scopesInOrder = p.scopesInOrderForEnum[stmt.Loc] 17226 preprocessedEnums[i] = p.appendPart(nil, []js_ast.Stmt{stmt}) 17227 p.scopesInOrder = oldScopesInOrder 17228 } 17229 } 17230 } 17231 17232 // When tree shaking is enabled, each top-level statement is potentially a separate part 17233 for i, stmt := range stmts { 17234 switch s := stmt.Data.(type) { 17235 case *js_ast.SLocal: 17236 // Split up top-level multi-declaration variable statements 17237 for _, decl := range s.Decls { 17238 clone := *s 17239 clone.Decls = []js_ast.Decl{decl} 17240 parts = p.appendPart(parts, []js_ast.Stmt{{Loc: stmt.Loc, Data: &clone}}) 17241 } 17242 17243 case *js_ast.SImport, *js_ast.SExportFrom, *js_ast.SExportStar: 17244 if p.options.mode != config.ModePassThrough { 17245 // Move imports (and import-like exports) to the top of the file to 17246 // ensure that if they are converted to a require() call, the effects 17247 // will take place before any other statements are evaluated. 17248 before = p.appendPart(before, []js_ast.Stmt{stmt}) 17249 } else { 17250 // If we aren't doing any format conversion, just keep these statements 17251 // inline where they were. Exports are sorted so order doesn't matter: 17252 // https://262.ecma-international.org/6.0/#sec-module-namespace-exotic-objects. 17253 // However, this is likely an aesthetic issue that some people will 17254 // complain about. In addition, there are code transformation tools 17255 // such as TypeScript and Babel with bugs where the order of exports 17256 // in the file is incorrectly preserved instead of sorted, so preserving 17257 // the order of exports ourselves here may be preferable. 17258 parts = p.appendPart(parts, []js_ast.Stmt{stmt}) 17259 } 17260 17261 case *js_ast.SExportEquals: 17262 // TypeScript "export = value;" becomes "module.exports = value;". This 17263 // must happen at the end after everything is parsed because TypeScript 17264 // moves this statement to the end when it generates code. 17265 after = p.appendPart(after, []js_ast.Stmt{stmt}) 17266 17267 case *js_ast.SEnum: 17268 parts = append(parts, preprocessedEnums[i]...) 17269 p.scopesInOrder = p.scopesInOrder[len(p.scopesInOrderForEnum[stmt.Loc]):] 17270 17271 default: 17272 parts = p.appendPart(parts, []js_ast.Stmt{stmt}) 17273 } 17274 } 17275 } 17276 17277 // Insert a variable for "import.meta" at the top of the file if it was used. 17278 // We don't need to worry about "use strict" directives because this only 17279 // happens when bundling, in which case we are flatting the module scopes of 17280 // all modules together anyway so such directives are meaningless. 17281 if p.importMetaRef != ast.InvalidRef { 17282 importMetaStmt := js_ast.Stmt{Data: &js_ast.SLocal{ 17283 Kind: p.selectLocalKind(js_ast.LocalConst), 17284 Decls: []js_ast.Decl{{ 17285 Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: p.importMetaRef}}, 17286 ValueOrNil: js_ast.Expr{Data: &js_ast.EObject{}}, 17287 }}, 17288 }} 17289 before = append(before, js_ast.Part{ 17290 Stmts: []js_ast.Stmt{importMetaStmt}, 17291 SymbolUses: make(map[ast.Ref]js_ast.SymbolUse), 17292 DeclaredSymbols: []js_ast.DeclaredSymbol{{Ref: p.importMetaRef, IsTopLevel: true}}, 17293 CanBeRemovedIfUnused: true, 17294 }) 17295 } 17296 17297 // Pop the module scope to apply the "ContainsDirectEval" rules 17298 p.popScope() 17299 17300 result = p.toAST(before, parts, after, hashbang, directives) 17301 result.SourceMapComment = p.lexer.SourceMappingURL 17302 return 17303 } 17304 17305 func LazyExportAST(log logger.Log, source logger.Source, options Options, expr js_ast.Expr, apiCall string) js_ast.AST { 17306 // Don't create a new lexer using js_lexer.NewLexer() here since that will 17307 // actually attempt to parse the first token, which might cause a syntax 17308 // error. 17309 p := newParser(log, source, js_lexer.Lexer{}, &options) 17310 p.prepareForVisitPass() 17311 17312 // Optionally call a runtime API function to transform the expression 17313 if apiCall != "" { 17314 p.symbolUses = make(map[ast.Ref]js_ast.SymbolUse) 17315 expr = p.callRuntime(expr.Loc, apiCall, []js_ast.Expr{expr}) 17316 } 17317 17318 // Add an empty part for the namespace export that we can fill in later 17319 nsExportPart := js_ast.Part{ 17320 SymbolUses: make(map[ast.Ref]js_ast.SymbolUse), 17321 CanBeRemovedIfUnused: true, 17322 } 17323 17324 // Defer the actual code generation until linking 17325 part := js_ast.Part{ 17326 Stmts: []js_ast.Stmt{{Loc: expr.Loc, Data: &js_ast.SLazyExport{Value: expr}}}, 17327 SymbolUses: p.symbolUses, 17328 } 17329 p.symbolUses = nil 17330 17331 ast := p.toAST([]js_ast.Part{nsExportPart}, []js_ast.Part{part}, nil, "", nil) 17332 ast.HasLazyExport = true 17333 return ast 17334 } 17335 17336 func GlobResolveAST(log logger.Log, source logger.Source, importRecords []ast.ImportRecord, object *js_ast.EObject, name string) js_ast.AST { 17337 // Don't create a new lexer using js_lexer.NewLexer() here since that will 17338 // actually attempt to parse the first token, which might cause a syntax 17339 // error. 17340 p := newParser(log, source, js_lexer.Lexer{}, &Options{}) 17341 p.prepareForVisitPass() 17342 17343 // Add an empty part for the namespace export that we can fill in later 17344 nsExportPart := js_ast.Part{ 17345 SymbolUses: make(map[ast.Ref]js_ast.SymbolUse), 17346 CanBeRemovedIfUnused: true, 17347 } 17348 17349 if len(p.importRecords) != 0 { 17350 panic("Internal error") 17351 } 17352 p.importRecords = importRecords 17353 17354 importRecordIndices := make([]uint32, 0, len(importRecords)) 17355 for importRecordIndex := range importRecords { 17356 importRecordIndices = append(importRecordIndices, uint32(importRecordIndex)) 17357 } 17358 17359 p.symbolUses = make(map[ast.Ref]js_ast.SymbolUse) 17360 ref := p.newSymbol(ast.SymbolOther, name) 17361 p.moduleScope.Generated = append(p.moduleScope.Generated, ref) 17362 17363 part := js_ast.Part{ 17364 Stmts: []js_ast.Stmt{{Data: &js_ast.SLocal{ 17365 IsExport: true, 17366 Decls: []js_ast.Decl{{ 17367 Binding: js_ast.Binding{Data: &js_ast.BIdentifier{Ref: ref}}, 17368 ValueOrNil: p.callRuntime(logger.Loc{}, "__glob", []js_ast.Expr{{Data: object}}), 17369 }}, 17370 }}}, 17371 ImportRecordIndices: importRecordIndices, 17372 SymbolUses: p.symbolUses, 17373 } 17374 p.symbolUses = nil 17375 17376 p.esmExportKeyword.Len = 1 17377 return p.toAST([]js_ast.Part{nsExportPart}, []js_ast.Part{part}, nil, "", nil) 17378 } 17379 17380 func ParseDefineExprOrJSON(text string) (config.DefineExpr, js_ast.E) { 17381 if text == "" { 17382 return config.DefineExpr{}, nil 17383 } 17384 17385 // Try a property chain 17386 parts := strings.Split(text, ".") 17387 for i, part := range parts { 17388 if !js_ast.IsIdentifier(part) { 17389 parts = nil 17390 break 17391 } 17392 17393 // Don't allow most keywords as the identifier 17394 if i == 0 { 17395 if token, ok := js_lexer.Keywords[part]; ok && token != js_lexer.TNull && token != js_lexer.TThis && 17396 (token != js_lexer.TImport || len(parts) < 2 || parts[1] != "meta") { 17397 parts = nil 17398 break 17399 } 17400 } 17401 } 17402 if parts != nil { 17403 return config.DefineExpr{Parts: parts}, nil 17404 } 17405 17406 // Try parsing a JSON value 17407 log := logger.NewDeferLog(logger.DeferLogNoVerboseOrDebug, nil) 17408 expr, ok := ParseJSON(log, logger.Source{Contents: text}, JSONOptions{}) 17409 if !ok { 17410 return config.DefineExpr{}, nil 17411 } 17412 17413 // Only primitive literals are inlined directly 17414 switch expr.Data.(type) { 17415 case *js_ast.ENull, *js_ast.EBoolean, *js_ast.EString, *js_ast.ENumber: 17416 return config.DefineExpr{Constant: expr.Data}, nil 17417 } 17418 17419 // If it's not a primitive, return the whole compound JSON value to be injected out-of-line 17420 return config.DefineExpr{}, expr.Data 17421 } 17422 17423 type whyESM uint8 17424 17425 const ( 17426 whyESMUnknown whyESM = iota 17427 whyESMExportKeyword 17428 whyESMImportMeta 17429 whyESMTopLevelAwait 17430 whyESMFileMJS 17431 whyESMFileMTS 17432 whyESMTypeModulePackageJSON 17433 whyESMImportStatement 17434 ) 17435 17436 // Say why this the current file is being considered an ES module 17437 func (p *parser) whyESModule() (whyESM, []logger.MsgData) { 17438 because := "This file is considered to be an ECMAScript module because" 17439 switch { 17440 case p.esmExportKeyword.Len > 0: 17441 return whyESMExportKeyword, []logger.MsgData{p.tracker.MsgData(p.esmExportKeyword, 17442 because+" of the \"export\" keyword here:")} 17443 17444 case p.esmImportMeta.Len > 0: 17445 return whyESMImportMeta, []logger.MsgData{p.tracker.MsgData(p.esmImportMeta, 17446 because+" of the use of \"import.meta\" here:")} 17447 17448 case p.topLevelAwaitKeyword.Len > 0: 17449 return whyESMTopLevelAwait, []logger.MsgData{p.tracker.MsgData(p.topLevelAwaitKeyword, 17450 because+" of the top-level \"await\" keyword here:")} 17451 17452 case p.options.moduleTypeData.Type == js_ast.ModuleESM_MJS: 17453 return whyESMFileMJS, []logger.MsgData{{Text: because + " the file name ends in \".mjs\"."}} 17454 17455 case p.options.moduleTypeData.Type == js_ast.ModuleESM_MTS: 17456 return whyESMFileMTS, []logger.MsgData{{Text: because + " the file name ends in \".mts\"."}} 17457 17458 case p.options.moduleTypeData.Type == js_ast.ModuleESM_PackageJSON: 17459 tracker := logger.MakeLineColumnTracker(p.options.moduleTypeData.Source) 17460 return whyESMTypeModulePackageJSON, []logger.MsgData{tracker.MsgData(p.options.moduleTypeData.Range, 17461 because+" the enclosing \"package.json\" file sets the type of this file to \"module\":")} 17462 17463 // This case must come last because some code cares about the "import" 17464 // statement keyword and some doesn't, and we don't want to give code 17465 // that doesn't care about the "import" statement the wrong error message. 17466 case p.esmImportStatementKeyword.Len > 0: 17467 return whyESMImportStatement, []logger.MsgData{p.tracker.MsgData(p.esmImportStatementKeyword, 17468 because+" of the \"import\" keyword here:")} 17469 } 17470 return whyESMUnknown, nil 17471 } 17472 17473 func (p *parser) prepareForVisitPass() { 17474 p.pushScopeForVisitPass(js_ast.ScopeEntry, logger.Loc{Start: locModuleScope}) 17475 p.fnOrArrowDataVisit.isOutsideFnOrArrow = true 17476 p.moduleScope = p.currentScope 17477 17478 // Force-enable strict mode if that's the way TypeScript is configured 17479 if tsAlwaysStrict := p.options.tsAlwaysStrict; tsAlwaysStrict != nil && tsAlwaysStrict.Value { 17480 p.currentScope.StrictMode = js_ast.ImplicitStrictModeTSAlwaysStrict 17481 } 17482 17483 // Determine whether or not this file is ESM 17484 p.isFileConsideredToHaveESMExports = 17485 p.esmExportKeyword.Len > 0 || 17486 p.esmImportMeta.Len > 0 || 17487 p.topLevelAwaitKeyword.Len > 0 || 17488 p.options.moduleTypeData.Type.IsESM() 17489 p.isFileConsideredESM = 17490 p.isFileConsideredToHaveESMExports || 17491 p.esmImportStatementKeyword.Len > 0 17492 17493 // Legacy HTML comments are not allowed in ESM files 17494 if p.isFileConsideredESM && p.lexer.LegacyHTMLCommentRange.Len > 0 { 17495 _, notes := p.whyESModule() 17496 p.log.AddErrorWithNotes(&p.tracker, p.lexer.LegacyHTMLCommentRange, 17497 "Legacy HTML single-line comments are not allowed in ECMAScript modules", notes) 17498 } 17499 17500 // ECMAScript modules are always interpreted as strict mode. This has to be 17501 // done before "hoistSymbols" because strict mode can alter hoisting (!). 17502 if p.isFileConsideredESM { 17503 p.moduleScope.RecursiveSetStrictMode(js_ast.ImplicitStrictModeESM) 17504 } 17505 17506 p.hoistSymbols(p.moduleScope) 17507 17508 if p.options.mode != config.ModePassThrough { 17509 p.requireRef = p.declareCommonJSSymbol(ast.SymbolUnbound, "require") 17510 } else { 17511 p.requireRef = p.newSymbol(ast.SymbolUnbound, "require") 17512 } 17513 17514 // CommonJS-style exports are only enabled if this isn't using ECMAScript- 17515 // style exports. You can still use "require" in ESM, just not "module" or 17516 // "exports". You can also still use "import" in CommonJS. 17517 if p.options.mode != config.ModePassThrough && !p.isFileConsideredToHaveESMExports { 17518 // CommonJS-style exports 17519 p.exportsRef = p.declareCommonJSSymbol(ast.SymbolHoisted, "exports") 17520 p.moduleRef = p.declareCommonJSSymbol(ast.SymbolHoisted, "module") 17521 } else { 17522 // ESM-style exports 17523 p.exportsRef = p.newSymbol(ast.SymbolHoisted, "exports") 17524 p.moduleRef = p.newSymbol(ast.SymbolHoisted, "module") 17525 } 17526 17527 // Handle "@jsx" and "@jsxFrag" pragmas now that lexing is done 17528 if p.options.jsx.Parse { 17529 if jsxRuntime := p.lexer.JSXRuntimePragmaComment; jsxRuntime.Text != "" { 17530 if jsxRuntime.Text == "automatic" { 17531 p.options.jsx.AutomaticRuntime = true 17532 } else if jsxRuntime.Text == "classic" { 17533 p.options.jsx.AutomaticRuntime = false 17534 } else { 17535 p.log.AddIDWithNotes(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxRuntime.Range, 17536 fmt.Sprintf("Invalid JSX runtime: %q", jsxRuntime.Text), 17537 []logger.MsgData{{Text: "The JSX runtime can only be set to either \"classic\" or \"automatic\"."}}) 17538 } 17539 } 17540 17541 if jsxFactory := p.lexer.JSXFactoryPragmaComment; jsxFactory.Text != "" { 17542 if p.options.jsx.AutomaticRuntime { 17543 p.log.AddID(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxFactory.Range, 17544 "The JSX factory cannot be set when using React's \"automatic\" JSX transform") 17545 } else if expr, _ := ParseDefineExprOrJSON(jsxFactory.Text); len(expr.Parts) > 0 { 17546 p.options.jsx.Factory = expr 17547 } else { 17548 p.log.AddID(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxFactory.Range, 17549 fmt.Sprintf("Invalid JSX factory: %s", jsxFactory.Text)) 17550 } 17551 } 17552 17553 if jsxFragment := p.lexer.JSXFragmentPragmaComment; jsxFragment.Text != "" { 17554 if p.options.jsx.AutomaticRuntime { 17555 p.log.AddID(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxFragment.Range, 17556 "The JSX fragment cannot be set when using React's \"automatic\" JSX transform") 17557 } else if expr, _ := ParseDefineExprOrJSON(jsxFragment.Text); len(expr.Parts) > 0 || expr.Constant != nil { 17558 p.options.jsx.Fragment = expr 17559 } else { 17560 p.log.AddID(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxFragment.Range, 17561 fmt.Sprintf("Invalid JSX fragment: %s", jsxFragment.Text)) 17562 } 17563 } 17564 17565 if jsxImportSource := p.lexer.JSXImportSourcePragmaComment; jsxImportSource.Text != "" { 17566 if !p.options.jsx.AutomaticRuntime { 17567 p.log.AddIDWithNotes(logger.MsgID_JS_UnsupportedJSXComment, logger.Warning, &p.tracker, jsxImportSource.Range, 17568 "The JSX import source cannot be set without also enabling React's \"automatic\" JSX transform", 17569 []logger.MsgData{{Text: "You can enable React's \"automatic\" JSX transform for this file by using a \"@jsxRuntime automatic\" comment."}}) 17570 } else { 17571 p.options.jsx.ImportSource = jsxImportSource.Text 17572 } 17573 } 17574 } 17575 17576 // Force-enable strict mode if the JSX "automatic" runtime is enabled and 17577 // there is at least one JSX element. This is because the automatically- 17578 // generated import statement turns the file into an ES module. This behavior 17579 // matches TypeScript which also does this. See this PR for more information: 17580 // https://github.com/microsoft/TypeScript/pull/39199 17581 if p.currentScope.StrictMode == js_ast.SloppyMode && p.options.jsx.AutomaticRuntime && p.firstJSXElementLoc.Start != -1 { 17582 p.currentScope.StrictMode = js_ast.ImplicitStrictModeJSXAutomaticRuntime 17583 } 17584 } 17585 17586 func (p *parser) declareCommonJSSymbol(kind ast.SymbolKind, name string) ast.Ref { 17587 member, ok := p.moduleScope.Members[name] 17588 17589 // If the code declared this symbol using "var name", then this is actually 17590 // not a collision. For example, node will let you do this: 17591 // 17592 // var exports; 17593 // module.exports.foo = 123; 17594 // console.log(exports.foo); 17595 // 17596 // This works because node's implementation of CommonJS wraps the entire 17597 // source file like this: 17598 // 17599 // (function(require, exports, module, __filename, __dirname) { 17600 // var exports; 17601 // module.exports.foo = 123; 17602 // console.log(exports.foo); 17603 // }) 17604 // 17605 // Both the "exports" argument and "var exports" are hoisted variables, so 17606 // they don't collide. 17607 if ok && p.symbols[member.Ref.InnerIndex].Kind == ast.SymbolHoisted && 17608 kind == ast.SymbolHoisted && !p.isFileConsideredToHaveESMExports { 17609 return member.Ref 17610 } 17611 17612 // Create a new symbol if we didn't merge with an existing one above 17613 ref := p.newSymbol(kind, name) 17614 17615 // If the variable wasn't declared, declare it now. This means any references 17616 // to this name will become bound to this symbol after this (since we haven't 17617 // run the visit pass yet). 17618 if !ok { 17619 p.moduleScope.Members[name] = js_ast.ScopeMember{Ref: ref, Loc: logger.Loc{Start: -1}} 17620 return ref 17621 } 17622 17623 // If the variable was declared, then it shadows this symbol. The code in 17624 // this module will be unable to reference this symbol. However, we must 17625 // still add the symbol to the scope so it gets minified (automatically- 17626 // generated code may still reference the symbol). 17627 p.moduleScope.Generated = append(p.moduleScope.Generated, ref) 17628 return ref 17629 } 17630 17631 // Compute a character frequency histogram for everything that's not a bound 17632 // symbol. This is used to modify how minified names are generated for slightly 17633 // better gzip compression. Even though it's a very small win, we still do it 17634 // because it's simple to do and very cheap to compute. 17635 func (p *parser) computeCharacterFrequency() *ast.CharFreq { 17636 if !p.options.minifyIdentifiers || p.source.Index == runtime.SourceIndex { 17637 return nil 17638 } 17639 17640 // Add everything in the file to the histogram 17641 charFreq := &ast.CharFreq{} 17642 charFreq.Scan(p.source.Contents, 1) 17643 17644 // Subtract out all comments 17645 for _, commentRange := range p.lexer.AllComments { 17646 charFreq.Scan(p.source.TextForRange(commentRange), -1) 17647 } 17648 17649 // Subtract out all import paths 17650 for _, record := range p.importRecords { 17651 if !record.SourceIndex.IsValid() { 17652 charFreq.Scan(record.Path.Text, -1) 17653 } 17654 } 17655 17656 // Subtract out all symbols that will be minified 17657 var visit func(*js_ast.Scope) 17658 visit = func(scope *js_ast.Scope) { 17659 for _, member := range scope.Members { 17660 symbol := &p.symbols[member.Ref.InnerIndex] 17661 if symbol.SlotNamespace() != ast.SlotMustNotBeRenamed { 17662 charFreq.Scan(symbol.OriginalName, -int32(symbol.UseCountEstimate)) 17663 } 17664 } 17665 if scope.Label.Ref != ast.InvalidRef { 17666 symbol := &p.symbols[scope.Label.Ref.InnerIndex] 17667 if symbol.SlotNamespace() != ast.SlotMustNotBeRenamed { 17668 charFreq.Scan(symbol.OriginalName, -int32(symbol.UseCountEstimate)-1) 17669 } 17670 } 17671 for _, child := range scope.Children { 17672 visit(child) 17673 } 17674 } 17675 visit(p.moduleScope) 17676 17677 // Subtract out all properties that will be mangled 17678 for _, ref := range p.mangledProps { 17679 symbol := &p.symbols[ref.InnerIndex] 17680 charFreq.Scan(symbol.OriginalName, -int32(symbol.UseCountEstimate)) 17681 } 17682 17683 return charFreq 17684 } 17685 17686 func (p *parser) generateImportStmt( 17687 path string, 17688 pathRange logger.Range, 17689 imports []string, 17690 parts []js_ast.Part, 17691 symbols map[string]ast.LocRef, 17692 sourceIndex *uint32, 17693 copySourceIndex *uint32, 17694 ) ([]js_ast.Part, uint32) { 17695 if pathRange.Len == 0 { 17696 isFirst := true 17697 for _, it := range symbols { 17698 if isFirst || it.Loc.Start < pathRange.Loc.Start { 17699 pathRange.Loc = it.Loc 17700 } 17701 isFirst = false 17702 } 17703 } 17704 17705 namespaceRef := p.newSymbol(ast.SymbolOther, "import_"+js_ast.GenerateNonUniqueNameFromPath(path)) 17706 p.moduleScope.Generated = append(p.moduleScope.Generated, namespaceRef) 17707 declaredSymbols := make([]js_ast.DeclaredSymbol, 1+len(imports)) 17708 clauseItems := make([]js_ast.ClauseItem, len(imports)) 17709 importRecordIndex := p.addImportRecord(ast.ImportStmt, pathRange, path, nil, 0) 17710 if sourceIndex != nil { 17711 p.importRecords[importRecordIndex].SourceIndex = ast.MakeIndex32(*sourceIndex) 17712 } 17713 if copySourceIndex != nil { 17714 p.importRecords[importRecordIndex].CopySourceIndex = ast.MakeIndex32(*copySourceIndex) 17715 } 17716 declaredSymbols[0] = js_ast.DeclaredSymbol{Ref: namespaceRef, IsTopLevel: true} 17717 17718 // Create per-import information 17719 for i, alias := range imports { 17720 it := symbols[alias] 17721 declaredSymbols[i+1] = js_ast.DeclaredSymbol{Ref: it.Ref, IsTopLevel: true} 17722 clauseItems[i] = js_ast.ClauseItem{ 17723 Alias: alias, 17724 AliasLoc: it.Loc, 17725 Name: ast.LocRef{Loc: it.Loc, Ref: it.Ref}, 17726 } 17727 p.isImportItem[it.Ref] = true 17728 p.namedImports[it.Ref] = js_ast.NamedImport{ 17729 Alias: alias, 17730 AliasLoc: it.Loc, 17731 NamespaceRef: namespaceRef, 17732 ImportRecordIndex: importRecordIndex, 17733 } 17734 } 17735 17736 // Append a single import to the end of the file (ES6 imports are hoisted 17737 // so we don't need to worry about where the import statement goes) 17738 return append(parts, js_ast.Part{ 17739 DeclaredSymbols: declaredSymbols, 17740 ImportRecordIndices: []uint32{importRecordIndex}, 17741 Stmts: []js_ast.Stmt{{Loc: pathRange.Loc, Data: &js_ast.SImport{ 17742 NamespaceRef: namespaceRef, 17743 Items: &clauseItems, 17744 ImportRecordIndex: importRecordIndex, 17745 IsSingleLine: true, 17746 }}}, 17747 }), importRecordIndex 17748 } 17749 17750 // Sort the keys for determinism 17751 func sortedKeysOfMapStringLocRef(in map[string]ast.LocRef) []string { 17752 keys := make([]string, 0, len(in)) 17753 for key := range in { 17754 keys = append(keys, key) 17755 } 17756 sort.Strings(keys) 17757 return keys 17758 } 17759 17760 func (p *parser) toAST(before, parts, after []js_ast.Part, hashbang string, directives []string) js_ast.AST { 17761 // Insert an import statement for any runtime imports we generated 17762 if len(p.runtimeImports) > 0 && !p.options.omitRuntimeForTests { 17763 keys := sortedKeysOfMapStringLocRef(p.runtimeImports) 17764 sourceIndex := runtime.SourceIndex 17765 before, _ = p.generateImportStmt("<runtime>", logger.Range{}, keys, before, p.runtimeImports, &sourceIndex, nil) 17766 } 17767 17768 // Insert an import statement for any jsx runtime imports we generated 17769 if len(p.jsxRuntimeImports) > 0 && !p.options.omitJSXRuntimeForTests { 17770 keys := sortedKeysOfMapStringLocRef(p.jsxRuntimeImports) 17771 17772 // Determine the runtime source and whether it's prod or dev 17773 path := p.options.jsx.ImportSource 17774 if p.options.jsx.Development { 17775 path = path + "/jsx-dev-runtime" 17776 } else { 17777 path = path + "/jsx-runtime" 17778 } 17779 17780 before, _ = p.generateImportStmt(path, logger.Range{}, keys, before, p.jsxRuntimeImports, nil, nil) 17781 } 17782 17783 // Insert an import statement for any legacy jsx imports we generated (i.e., createElement) 17784 if len(p.jsxLegacyImports) > 0 && !p.options.omitJSXRuntimeForTests { 17785 keys := sortedKeysOfMapStringLocRef(p.jsxLegacyImports) 17786 path := p.options.jsx.ImportSource 17787 before, _ = p.generateImportStmt(path, logger.Range{}, keys, before, p.jsxLegacyImports, nil, nil) 17788 } 17789 17790 // Insert imports for each glob pattern 17791 for _, glob := range p.globPatternImports { 17792 symbols := map[string]ast.LocRef{glob.name: {Loc: glob.approximateRange.Loc, Ref: glob.ref}} 17793 var importRecordIndex uint32 17794 before, importRecordIndex = p.generateImportStmt(helpers.GlobPatternToString(glob.parts), glob.approximateRange, []string{glob.name}, before, symbols, nil, nil) 17795 record := &p.importRecords[importRecordIndex] 17796 record.AssertOrWith = glob.assertOrWith 17797 record.GlobPattern = &ast.GlobPattern{ 17798 Parts: glob.parts, 17799 ExportAlias: glob.name, 17800 Kind: glob.kind, 17801 } 17802 } 17803 17804 // Generated imports are inserted before other code instead of appending them 17805 // to the end of the file. Appending them should work fine because JavaScript 17806 // import statements are "hoisted" to run before the importing file. However, 17807 // some buggy JavaScript toolchains such as the TypeScript compiler convert 17808 // ESM into CommonJS by replacing "import" statements inline without doing 17809 // any hoisting, which is incorrect. See the following issue for more info: 17810 // https://github.com/microsoft/TypeScript/issues/16166. Since JSX-related 17811 // imports are present in the generated code when bundling is disabled, and 17812 // could therefore be processed by these buggy tools, it's more robust to put 17813 // them at the top even though it means potentially reallocating almost the 17814 // entire array of parts. 17815 if len(before) > 0 { 17816 parts = append(before, parts...) 17817 } 17818 parts = append(parts, after...) 17819 17820 // Handle import paths after the whole file has been visited because we need 17821 // symbol usage counts to be able to remove unused type-only imports in 17822 // TypeScript code. 17823 keptImportEquals := false 17824 removedImportEquals := false 17825 partsEnd := 0 17826 for partIndex, part := range parts { 17827 p.importRecordsForCurrentPart = nil 17828 p.declaredSymbols = nil 17829 17830 result := p.scanForImportsAndExports(part.Stmts) 17831 part.Stmts = result.stmts 17832 keptImportEquals = keptImportEquals || result.keptImportEquals 17833 removedImportEquals = removedImportEquals || result.removedImportEquals 17834 17835 part.ImportRecordIndices = append(part.ImportRecordIndices, p.importRecordsForCurrentPart...) 17836 part.DeclaredSymbols = append(part.DeclaredSymbols, p.declaredSymbols...) 17837 17838 if len(part.Stmts) > 0 || uint32(partIndex) == js_ast.NSExportPartIndex { 17839 if p.moduleScope.ContainsDirectEval && len(part.DeclaredSymbols) > 0 { 17840 // If this file contains a direct call to "eval()", all parts that 17841 // declare top-level symbols must be kept since the eval'd code may 17842 // reference those symbols. 17843 part.CanBeRemovedIfUnused = false 17844 } 17845 parts[partsEnd] = part 17846 partsEnd++ 17847 } 17848 } 17849 parts = parts[:partsEnd] 17850 17851 // We need to iterate multiple times if an import-equals statement was 17852 // removed and there are more import-equals statements that may be removed. 17853 // In the example below, a/b/c should be kept but x/y/z should be removed 17854 // (and removal requires multiple passes): 17855 // 17856 // import a = foo.a 17857 // import b = a.b 17858 // import c = b.c 17859 // 17860 // import x = foo.x 17861 // import y = x.y 17862 // import z = y.z 17863 // 17864 // export let bar = c 17865 // 17866 // This is a smaller version of the general import/export scanning loop above. 17867 // We only want to repeat the code that eliminates TypeScript import-equals 17868 // statements, not the other code in the loop above. 17869 for keptImportEquals && removedImportEquals { 17870 keptImportEquals = false 17871 removedImportEquals = false 17872 partsEnd := 0 17873 for partIndex, part := range parts { 17874 result := p.scanForUnusedTSImportEquals(part.Stmts) 17875 part.Stmts = result.stmts 17876 keptImportEquals = keptImportEquals || result.keptImportEquals 17877 removedImportEquals = removedImportEquals || result.removedImportEquals 17878 if len(part.Stmts) > 0 || uint32(partIndex) == js_ast.NSExportPartIndex { 17879 parts[partsEnd] = part 17880 partsEnd++ 17881 } 17882 } 17883 parts = parts[:partsEnd] 17884 } 17885 17886 // Do a second pass for exported items now that imported items are filled out 17887 for _, part := range parts { 17888 for _, stmt := range part.Stmts { 17889 if s, ok := stmt.Data.(*js_ast.SExportClause); ok { 17890 for _, item := range s.Items { 17891 // Mark re-exported imports as such 17892 if namedImport, ok := p.namedImports[item.Name.Ref]; ok { 17893 namedImport.IsExported = true 17894 p.namedImports[item.Name.Ref] = namedImport 17895 } 17896 } 17897 } 17898 } 17899 } 17900 17901 // Analyze cross-part dependencies for tree shaking and code splitting 17902 { 17903 // Map locals to parts 17904 p.topLevelSymbolToParts = make(map[ast.Ref][]uint32) 17905 for partIndex, part := range parts { 17906 for _, declared := range part.DeclaredSymbols { 17907 if declared.IsTopLevel { 17908 // If this symbol was merged, use the symbol at the end of the 17909 // linked list in the map. This is the case for multiple "var" 17910 // declarations with the same name, for example. 17911 ref := declared.Ref 17912 for p.symbols[ref.InnerIndex].Link != ast.InvalidRef { 17913 ref = p.symbols[ref.InnerIndex].Link 17914 } 17915 p.topLevelSymbolToParts[ref] = append( 17916 p.topLevelSymbolToParts[ref], uint32(partIndex)) 17917 } 17918 } 17919 } 17920 17921 // Pulling in the exports of this module always pulls in the export part 17922 p.topLevelSymbolToParts[p.exportsRef] = append(p.topLevelSymbolToParts[p.exportsRef], js_ast.NSExportPartIndex) 17923 } 17924 17925 // Make a wrapper symbol in case we need to be wrapped in a closure 17926 wrapperRef := p.newSymbol(ast.SymbolOther, "require_"+p.source.IdentifierName) 17927 17928 // Assign slots to symbols in nested scopes. This is some precomputation for 17929 // the symbol renaming pass that will happen later in the linker. It's done 17930 // now in the parser because we want it to be done in parallel per file and 17931 // we're already executing code in a dedicated goroutine for this file. 17932 var nestedScopeSlotCounts ast.SlotCounts 17933 if p.options.minifyIdentifiers { 17934 nestedScopeSlotCounts = renamer.AssignNestedScopeSlots(p.moduleScope, p.symbols) 17935 } 17936 17937 exportsKind := js_ast.ExportsNone 17938 usesExportsRef := p.symbols[p.exportsRef.InnerIndex].UseCountEstimate > 0 17939 usesModuleRef := p.symbols[p.moduleRef.InnerIndex].UseCountEstimate > 0 17940 17941 if p.esmExportKeyword.Len > 0 || p.esmImportMeta.Len > 0 || p.topLevelAwaitKeyword.Len > 0 { 17942 exportsKind = js_ast.ExportsESM 17943 } else if usesExportsRef || usesModuleRef || p.hasTopLevelReturn { 17944 exportsKind = js_ast.ExportsCommonJS 17945 } else { 17946 // If this module has no exports, try to determine what kind of module it 17947 // is by looking at node's "type" field in "package.json" and/or whether 17948 // the file extension is ".mjs"/".mts" or ".cjs"/".cts". 17949 switch { 17950 case p.options.moduleTypeData.Type.IsCommonJS(): 17951 // ".cjs" or ".cts" or ("type: commonjs" and (".js" or ".jsx" or ".ts" or ".tsx")) 17952 exportsKind = js_ast.ExportsCommonJS 17953 17954 case p.options.moduleTypeData.Type.IsESM(): 17955 // ".mjs" or ".mts" or ("type: module" and (".js" or ".jsx" or ".ts" or ".tsx")) 17956 exportsKind = js_ast.ExportsESM 17957 17958 default: 17959 // Treat unknown modules containing an import statement as ESM. Otherwise 17960 // the bundler will treat this file as CommonJS if it's imported and ESM 17961 // if it's not imported. 17962 if p.esmImportStatementKeyword.Len > 0 { 17963 exportsKind = js_ast.ExportsESM 17964 } 17965 } 17966 } 17967 17968 return js_ast.AST{ 17969 Parts: parts, 17970 ModuleTypeData: p.options.moduleTypeData, 17971 ModuleScope: p.moduleScope, 17972 CharFreq: p.computeCharacterFrequency(), 17973 Symbols: p.symbols, 17974 ExportsRef: p.exportsRef, 17975 ModuleRef: p.moduleRef, 17976 WrapperRef: wrapperRef, 17977 Hashbang: hashbang, 17978 Directives: directives, 17979 NamedImports: p.namedImports, 17980 NamedExports: p.namedExports, 17981 TSEnums: p.tsEnums, 17982 ConstValues: p.constValues, 17983 ExprComments: p.exprComments, 17984 NestedScopeSlotCounts: nestedScopeSlotCounts, 17985 TopLevelSymbolToPartsFromParser: p.topLevelSymbolToParts, 17986 ExportStarImportRecords: p.exportStarImportRecords, 17987 ImportRecords: p.importRecords, 17988 ApproximateLineCount: int32(p.lexer.ApproximateNewlineCount) + 1, 17989 MangledProps: p.mangledProps, 17990 ReservedProps: p.reservedProps, 17991 ManifestForYarnPnP: p.manifestForYarnPnP, 17992 17993 // CommonJS features 17994 UsesExportsRef: usesExportsRef, 17995 UsesModuleRef: usesModuleRef, 17996 ExportsKind: exportsKind, 17997 17998 // ES6 features 17999 ExportKeyword: p.esmExportKeyword, 18000 TopLevelAwaitKeyword: p.topLevelAwaitKeyword, 18001 LiveTopLevelAwaitKeyword: p.liveTopLevelAwaitKeyword, 18002 } 18003 }