1package js_ast 2 3import ( 4 "math" 5 "sort" 6 7 "github.com/evanw/esbuild/internal/ast" 8 "github.com/evanw/esbuild/internal/compat" 9 "github.com/evanw/esbuild/internal/logger" 10) 11 12// Every module (i.e. file) is parsed into a separate AST data structure. For 13// efficiency, the parser also resolves all scopes and binds all symbols in the 14// tree. 15// 16// Identifiers in the tree are referenced by a Ref, which is a pointer into the 17// symbol table for the file. The symbol table is stored as a top-level field 18// in the AST so it can be accessed without traversing the tree. For example, 19// a renaming pass can iterate over the symbol table without touching the tree. 20// 21// Parse trees are intended to be immutable. That makes it easy to build an 22// incremental compiler with a "watch" mode that can avoid re-parsing files 23// that have already been parsed. Any passes that operate on an AST after it 24// has been parsed should create a copy of the mutated parts of the tree 25// instead of mutating the original tree. 26 27type L int 28 29// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence 30const ( 31 LLowest L = iota 32 LComma 33 LSpread 34 LYield 35 LAssign 36 LConditional 37 LNullishCoalescing 38 LLogicalOr 39 LLogicalAnd 40 LBitwiseOr 41 LBitwiseXor 42 LBitwiseAnd 43 LEquals 44 LCompare 45 LShift 46 LAdd 47 LMultiply 48 LExponentiation 49 LPrefix 50 LPostfix 51 LNew 52 LCall 53 LMember 54) 55 56type OpCode int 57 58func (op OpCode) IsPrefix() bool { 59 return op < UnOpPostDec 60} 61 62func (op OpCode) UnaryAssignTarget() AssignTarget { 63 if op >= UnOpPreDec && op <= UnOpPostInc { 64 return AssignTargetUpdate 65 } 66 return AssignTargetNone 67} 68 69func (op OpCode) IsLeftAssociative() bool { 70 return op >= BinOpAdd && op < BinOpComma && op != BinOpPow 71} 72 73func (op OpCode) IsRightAssociative() bool { 74 return op >= BinOpAssign || op == BinOpPow 75} 76 77func (op OpCode) BinaryAssignTarget() AssignTarget { 78 if op == BinOpAssign { 79 return AssignTargetReplace 80 } 81 if op > BinOpAssign { 82 return AssignTargetUpdate 83 } 84 return AssignTargetNone 85} 86 87func (op OpCode) IsShortCircuit() bool { 88 switch op { 89 case BinOpLogicalOr, BinOpLogicalOrAssign, 90 BinOpLogicalAnd, BinOpLogicalAndAssign, 91 BinOpNullishCoalescing, BinOpNullishCoalescingAssign: 92 return true 93 } 94 return false 95} 96 97type AssignTarget uint8 98 99const ( 100 AssignTargetNone AssignTarget = iota 101 AssignTargetReplace // "a = b" 102 AssignTargetUpdate // "a += b" 103) 104 105// If you add a new token, remember to add it to "OpTable" too 106const ( 107 // Prefix 108 UnOpPos OpCode = iota 109 UnOpNeg 110 UnOpCpl 111 UnOpNot 112 UnOpVoid 113 UnOpTypeof 114 UnOpDelete 115 116 // Prefix update 117 UnOpPreDec 118 UnOpPreInc 119 120 // Postfix update 121 UnOpPostDec 122 UnOpPostInc 123 124 // Left-associative 125 BinOpAdd 126 BinOpSub 127 BinOpMul 128 BinOpDiv 129 BinOpRem 130 BinOpPow 131 BinOpLt 132 BinOpLe 133 BinOpGt 134 BinOpGe 135 BinOpIn 136 BinOpInstanceof 137 BinOpShl 138 BinOpShr 139 BinOpUShr 140 BinOpLooseEq 141 BinOpLooseNe 142 BinOpStrictEq 143 BinOpStrictNe 144 BinOpNullishCoalescing 145 BinOpLogicalOr 146 BinOpLogicalAnd 147 BinOpBitwiseOr 148 BinOpBitwiseAnd 149 BinOpBitwiseXor 150 151 // Non-associative 152 BinOpComma 153 154 // Right-associative 155 BinOpAssign 156 BinOpAddAssign 157 BinOpSubAssign 158 BinOpMulAssign 159 BinOpDivAssign 160 BinOpRemAssign 161 BinOpPowAssign 162 BinOpShlAssign 163 BinOpShrAssign 164 BinOpUShrAssign 165 BinOpBitwiseOrAssign 166 BinOpBitwiseAndAssign 167 BinOpBitwiseXorAssign 168 BinOpNullishCoalescingAssign 169 BinOpLogicalOrAssign 170 BinOpLogicalAndAssign 171) 172 173type opTableEntry struct { 174 Text string 175 Level L 176 IsKeyword bool 177} 178 179var OpTable = []opTableEntry{ 180 // Prefix 181 {"+", LPrefix, false}, 182 {"-", LPrefix, false}, 183 {"~", LPrefix, false}, 184 {"!", LPrefix, false}, 185 {"void", LPrefix, true}, 186 {"typeof", LPrefix, true}, 187 {"delete", LPrefix, true}, 188 189 // Prefix update 190 {"--", LPrefix, false}, 191 {"++", LPrefix, false}, 192 193 // Postfix update 194 {"--", LPostfix, false}, 195 {"++", LPostfix, false}, 196 197 // Left-associative 198 {"+", LAdd, false}, 199 {"-", LAdd, false}, 200 {"*", LMultiply, false}, 201 {"/", LMultiply, false}, 202 {"%", LMultiply, false}, 203 {"**", LExponentiation, false}, // Right-associative 204 {"<", LCompare, false}, 205 {"<=", LCompare, false}, 206 {">", LCompare, false}, 207 {">=", LCompare, false}, 208 {"in", LCompare, true}, 209 {"instanceof", LCompare, true}, 210 {"<<", LShift, false}, 211 {">>", LShift, false}, 212 {">>>", LShift, false}, 213 {"==", LEquals, false}, 214 {"!=", LEquals, false}, 215 {"===", LEquals, false}, 216 {"!==", LEquals, false}, 217 {"??", LNullishCoalescing, false}, 218 {"||", LLogicalOr, false}, 219 {"&&", LLogicalAnd, false}, 220 {"|", LBitwiseOr, false}, 221 {"&", LBitwiseAnd, false}, 222 {"^", LBitwiseXor, false}, 223 224 // Non-associative 225 {",", LComma, false}, 226 227 // Right-associative 228 {"=", LAssign, false}, 229 {"+=", LAssign, false}, 230 {"-=", LAssign, false}, 231 {"*=", LAssign, false}, 232 {"/=", LAssign, false}, 233 {"%=", LAssign, false}, 234 {"**=", LAssign, false}, 235 {"<<=", LAssign, false}, 236 {">>=", LAssign, false}, 237 {">>>=", LAssign, false}, 238 {"|=", LAssign, false}, 239 {"&=", LAssign, false}, 240 {"^=", LAssign, false}, 241 {"??=", LAssign, false}, 242 {"||=", LAssign, false}, 243 {"&&=", LAssign, false}, 244} 245 246type LocRef struct { 247 Loc logger.Loc 248 Ref Ref 249} 250 251type Comment struct { 252 Loc logger.Loc 253 Text string 254} 255 256type PropertyKind int 257 258const ( 259 PropertyNormal PropertyKind = iota 260 PropertyGet 261 PropertySet 262 PropertySpread 263 PropertyDeclare 264 PropertyClassStaticBlock 265) 266 267type ClassStaticBlock struct { 268 Loc logger.Loc 269 Stmts []Stmt 270} 271 272type Property struct { 273 TSDecorators []Expr 274 ClassStaticBlock *ClassStaticBlock 275 276 Key Expr 277 278 // This is omitted for class fields 279 ValueOrNil Expr 280 281 // This is used when parsing a pattern that uses default values: 282 // 283 // [a = 1] = []; 284 // ({a = 1} = {}); 285 // 286 // It's also used for class fields: 287 // 288 // class Foo { a = 1 } 289 // 290 InitializerOrNil Expr 291 292 Kind PropertyKind 293 IsComputed bool 294 IsMethod bool 295 IsStatic bool 296 WasShorthand bool 297 PreferQuotedKey bool 298} 299 300type PropertyBinding struct { 301 Key Expr 302 Value Binding 303 DefaultValueOrNil Expr 304 IsComputed bool 305 IsSpread bool 306 PreferQuotedKey bool 307} 308 309type Arg struct { 310 TSDecorators []Expr 311 Binding Binding 312 DefaultOrNil Expr 313 314 // "constructor(public x: boolean) {}" 315 IsTypeScriptCtorField bool 316} 317 318type Fn struct { 319 Name *LocRef 320 OpenParenLoc logger.Loc 321 Args []Arg 322 Body FnBody 323 ArgumentsRef Ref 324 325 IsAsync bool 326 IsGenerator bool 327 HasRestArg bool 328 HasIfScope bool 329 330 // This is true if the function is a method 331 IsUniqueFormalParameters bool 332} 333 334type FnBody struct { 335 Loc logger.Loc 336 Stmts []Stmt 337} 338 339type Class struct { 340 ClassKeyword logger.Range 341 TSDecorators []Expr 342 Name *LocRef 343 ExtendsOrNil Expr 344 BodyLoc logger.Loc 345 Properties []Property 346} 347 348type ArrayBinding struct { 349 Binding Binding 350 DefaultValueOrNil Expr 351} 352 353type Binding struct { 354 Loc logger.Loc 355 Data B 356} 357 358// This interface is never called. Its purpose is to encode a variant type in 359// Go's type system. 360type B interface{ isBinding() } 361 362func (*BMissing) isBinding() {} 363func (*BIdentifier) isBinding() {} 364func (*BArray) isBinding() {} 365func (*BObject) isBinding() {} 366 367type BMissing struct{} 368 369type BIdentifier struct{ Ref Ref } 370 371type BArray struct { 372 Items []ArrayBinding 373 HasSpread bool 374 IsSingleLine bool 375} 376 377type BObject struct { 378 Properties []PropertyBinding 379 IsSingleLine bool 380} 381 382type Expr struct { 383 Loc logger.Loc 384 Data E 385} 386 387// This interface is never called. Its purpose is to encode a variant type in 388// Go's type system. 389type E interface{ isExpr() } 390 391func (*EArray) isExpr() {} 392func (*EUnary) isExpr() {} 393func (*EBinary) isExpr() {} 394func (*EBoolean) isExpr() {} 395func (*ESuper) isExpr() {} 396func (*ENull) isExpr() {} 397func (*EUndefined) isExpr() {} 398func (*EThis) isExpr() {} 399func (*ENew) isExpr() {} 400func (*ENewTarget) isExpr() {} 401func (*EImportMeta) isExpr() {} 402func (*ECall) isExpr() {} 403func (*EDot) isExpr() {} 404func (*EIndex) isExpr() {} 405func (*EArrow) isExpr() {} 406func (*EFunction) isExpr() {} 407func (*EClass) isExpr() {} 408func (*EIdentifier) isExpr() {} 409func (*EImportIdentifier) isExpr() {} 410func (*EPrivateIdentifier) isExpr() {} 411func (*EJSXElement) isExpr() {} 412func (*EMissing) isExpr() {} 413func (*ENumber) isExpr() {} 414func (*EBigInt) isExpr() {} 415func (*EObject) isExpr() {} 416func (*ESpread) isExpr() {} 417func (*EString) isExpr() {} 418func (*ETemplate) isExpr() {} 419func (*ERegExp) isExpr() {} 420func (*EInlinedEnum) isExpr() {} 421func (*EAwait) isExpr() {} 422func (*EYield) isExpr() {} 423func (*EIf) isExpr() {} 424func (*ERequireString) isExpr() {} 425func (*ERequireResolveString) isExpr() {} 426func (*EImportString) isExpr() {} 427func (*EImportCall) isExpr() {} 428 429type EArray struct { 430 Items []Expr 431 CommaAfterSpread logger.Loc 432 IsSingleLine bool 433 IsParenthesized bool 434} 435 436type EUnary struct { 437 Op OpCode 438 Value Expr 439} 440 441type EBinary struct { 442 Left Expr 443 Right Expr 444 Op OpCode 445} 446 447type EBoolean struct{ Value bool } 448 449type EMissing struct{} 450 451type ESuper struct{} 452 453type ENull struct{} 454 455type EUndefined struct{} 456 457type EThis struct{} 458 459type ENewTarget struct { 460 Range logger.Range 461} 462 463type EImportMeta struct { 464 RangeLen int32 465} 466 467// These help reduce unnecessary memory allocations 468var BMissingShared = &BMissing{} 469var EMissingShared = &EMissing{} 470var ESuperShared = &ESuper{} 471var ENullShared = &ENull{} 472var EUndefinedShared = &EUndefined{} 473var EThisShared = &EThis{} 474 475type ENew struct { 476 Target Expr 477 Args []Expr 478 479 // True if there is a comment containing "@__PURE__" or "#__PURE__" preceding 480 // this call expression. See the comment inside ECall for more details. 481 CanBeUnwrappedIfUnused bool 482} 483 484type OptionalChain uint8 485 486const ( 487 // "a.b" 488 OptionalChainNone OptionalChain = iota 489 490 // "a?.b" 491 OptionalChainStart 492 493 // "a?.b.c" => ".c" is OptionalChainContinue 494 // "(a?.b).c" => ".c" is OptionalChainNone 495 OptionalChainContinue 496) 497 498type ECall struct { 499 Target Expr 500 Args []Expr 501 OptionalChain OptionalChain 502 IsDirectEval bool 503 504 // True if there is a comment containing "@__PURE__" or "#__PURE__" preceding 505 // this call expression. This is an annotation used for tree shaking, and 506 // means that the call can be removed if it's unused. It does not mean the 507 // call is pure (e.g. it may still return something different if called twice). 508 // 509 // Note that the arguments are not considered to be part of the call. If the 510 // call itself is removed due to this annotation, the arguments must remain 511 // if they have side effects. 512 CanBeUnwrappedIfUnused bool 513} 514 515func (a *ECall) HasSameFlagsAs(b *ECall) bool { 516 return a.OptionalChain == b.OptionalChain && 517 a.IsDirectEval == b.IsDirectEval && 518 a.CanBeUnwrappedIfUnused == b.CanBeUnwrappedIfUnused 519} 520 521type EDot struct { 522 Target Expr 523 Name string 524 NameLoc logger.Loc 525 OptionalChain OptionalChain 526 527 // If true, this property access is known to be free of side-effects. That 528 // means it can be removed if the resulting value isn't used. 529 CanBeRemovedIfUnused bool 530 531 // If true, this property access is a function that, when called, can be 532 // unwrapped if the resulting value is unused. Unwrapping means discarding 533 // the call target but keeping any arguments with side effects. 534 CallCanBeUnwrappedIfUnused bool 535} 536 537func (a *EDot) HasSameFlagsAs(b *EDot) bool { 538 return a.OptionalChain == b.OptionalChain && 539 a.CanBeRemovedIfUnused == b.CanBeRemovedIfUnused && 540 a.CallCanBeUnwrappedIfUnused == b.CallCanBeUnwrappedIfUnused 541} 542 543type EIndex struct { 544 Target Expr 545 Index Expr 546 OptionalChain OptionalChain 547} 548 549func (a *EIndex) HasSameFlagsAs(b *EIndex) bool { 550 return a.OptionalChain == b.OptionalChain 551} 552 553type EArrow struct { 554 Args []Arg 555 Body FnBody 556 557 IsAsync bool 558 HasRestArg bool 559 PreferExpr bool // Use shorthand if true and "Body" is a single return statement 560} 561 562type EFunction struct{ Fn Fn } 563 564type EClass struct{ Class Class } 565 566type EIdentifier struct { 567 Ref Ref 568 569 // If we're inside a "with" statement, this identifier may be a property 570 // access. In that case it would be incorrect to remove this identifier since 571 // the property access may be a getter or setter with side effects. 572 MustKeepDueToWithStmt bool 573 574 // If true, this identifier is known to not have a side effect (i.e. to not 575 // throw an exception) when referenced. If false, this identifier may or may 576 // not have side effects when referenced. This is used to allow the removal 577 // of known globals such as "Object" if they aren't used. 578 CanBeRemovedIfUnused bool 579 580 // If true, this identifier represents a function that, when called, can be 581 // unwrapped if the resulting value is unused. Unwrapping means discarding 582 // the call target but keeping any arguments with side effects. 583 CallCanBeUnwrappedIfUnused bool 584} 585 586// This is similar to an EIdentifier but it represents a reference to an ES6 587// import item. 588// 589// Depending on how the code is linked, the file containing this EImportIdentifier 590// may or may not be in the same module group as the file it was imported from. 591// 592// If it's the same module group than we can just merge the import item symbol 593// with the corresponding symbol that was imported, effectively renaming them 594// to be the same thing and statically binding them together. 595// 596// But if it's a different module group, then the import must be dynamically 597// evaluated using a property access off the corresponding namespace symbol, 598// which represents the result of a require() call. 599// 600// It's stored as a separate type so it's not easy to confuse with a plain 601// identifier. For example, it'd be bad if code trying to convert "{x: x}" into 602// "{x}" shorthand syntax wasn't aware that the "x" in this case is actually 603// "{x: importedNamespace.x}". This separate type forces code to opt-in to 604// doing this instead of opt-out. 605type EImportIdentifier struct { 606 Ref Ref 607 PreferQuotedKey bool 608 609 // If true, this was originally an identifier expression such as "foo". If 610 // false, this could potentially have been a member access expression such 611 // as "ns.foo" off of an imported namespace object. 612 WasOriginallyIdentifier bool 613} 614 615// This is similar to EIdentifier but it represents class-private fields and 616// methods. It can be used where computed properties can be used, such as 617// EIndex and Property. 618type EPrivateIdentifier struct { 619 Ref Ref 620} 621 622type EJSXElement struct { 623 TagOrNil Expr 624 Properties []Property 625 Children []Expr 626 CloseLoc logger.Loc 627} 628 629type ENumber struct{ Value float64 } 630 631type EBigInt struct{ Value string } 632 633type EObject struct { 634 Properties []Property 635 CommaAfterSpread logger.Loc 636 IsSingleLine bool 637 IsParenthesized bool 638} 639 640type ESpread struct{ Value Expr } 641 642// This is used for both strings and no-substitution template literals to reduce 643// the number of cases that need to be checked for string optimization code 644type EString struct { 645 Value []uint16 646 LegacyOctalLoc logger.Loc 647 PreferTemplate bool 648} 649 650type TemplatePart struct { 651 Value Expr 652 TailLoc logger.Loc 653 TailCooked []uint16 // Only use when "TagOrNil" is nil 654 TailRaw string // Only use when "TagOrNil" is not nil 655} 656 657type ETemplate struct { 658 TagOrNil Expr 659 HeadLoc logger.Loc 660 HeadCooked []uint16 // Only use when "TagOrNil" is nil 661 HeadRaw string // Only use when "TagOrNil" is not nil 662 Parts []TemplatePart 663 LegacyOctalLoc logger.Loc 664} 665 666type ERegExp struct{ Value string } 667 668type EInlinedEnum struct { 669 Value Expr 670 Comment string 671} 672 673type EAwait struct { 674 Value Expr 675} 676 677type EYield struct { 678 ValueOrNil Expr 679 IsStar bool 680} 681 682type EIf struct { 683 Test Expr 684 Yes Expr 685 No Expr 686} 687 688type ERequireString struct { 689 ImportRecordIndex uint32 690} 691 692type ERequireResolveString struct { 693 ImportRecordIndex uint32 694} 695 696type EImportString struct { 697 ImportRecordIndex uint32 698 699 // Comments inside "import()" expressions have special meaning for Webpack. 700 // Preserving comments inside these expressions makes it possible to use 701 // esbuild as a TypeScript-to-JavaScript frontend for Webpack to improve 702 // performance. We intentionally do not interpret these comments in esbuild 703 // because esbuild is not Webpack. But we do preserve them since doing so is 704 // harmless, easy to maintain, and useful to people. See the Webpack docs for 705 // more info: https://webpack.js.org/api/module-methods/#magic-comments. 706 LeadingInteriorComments []Comment 707} 708 709type EImportCall struct { 710 Expr Expr 711 OptionsOrNil Expr 712 713 // See the comment for this same field on "EImportCall" for more information 714 LeadingInteriorComments []Comment 715} 716 717func IsOptionalChain(value Expr) bool { 718 switch e := value.Data.(type) { 719 case *EDot: 720 return e.OptionalChain != OptionalChainNone 721 case *EIndex: 722 return e.OptionalChain != OptionalChainNone 723 case *ECall: 724 return e.OptionalChain != OptionalChainNone 725 } 726 return false 727} 728 729func Assign(a Expr, b Expr) Expr { 730 return Expr{Loc: a.Loc, Data: &EBinary{Op: BinOpAssign, Left: a, Right: b}} 731} 732 733func AssignStmt(a Expr, b Expr) Stmt { 734 return Stmt{Loc: a.Loc, Data: &SExpr{Value: Assign(a, b)}} 735} 736 737// Wraps the provided expression in the "!" prefix operator. The expression 738// will potentially be simplified to avoid generating unnecessary extra "!" 739// operators. For example, calling this with "!!x" will return "!x" instead 740// of returning "!!!x". 741func Not(expr Expr) Expr { 742 if result, ok := MaybeSimplifyNot(expr); ok { 743 return result 744 } 745 return Expr{Loc: expr.Loc, Data: &EUnary{Op: UnOpNot, Value: expr}} 746} 747 748// The given "expr" argument should be the operand of a "!" prefix operator 749// (i.e. the "x" in "!x"). This returns a simplified expression for the 750// whole operator (i.e. the "!x") if it can be simplified, or false if not. 751// It's separate from "Not()" above to avoid allocation on failure in case 752// that is undesired. 753func MaybeSimplifyNot(expr Expr) (Expr, bool) { 754 switch e := expr.Data.(type) { 755 case *EInlinedEnum: 756 if value, ok := MaybeSimplifyNot(e.Value); ok { 757 return value, true 758 } 759 760 case *ENull, *EUndefined: 761 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: true}}, true 762 763 case *EBoolean: 764 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: !e.Value}}, true 765 766 case *ENumber: 767 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: e.Value == 0 || math.IsNaN(e.Value)}}, true 768 769 case *EBigInt: 770 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: e.Value == "0"}}, true 771 772 case *EString: 773 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: len(e.Value) == 0}}, true 774 775 case *EFunction, *EArrow, *ERegExp: 776 return Expr{Loc: expr.Loc, Data: &EBoolean{Value: false}}, true 777 778 case *EUnary: 779 // "!!!a" => "!a" 780 if e.Op == UnOpNot && KnownPrimitiveType(e.Value) == PrimitiveBoolean { 781 return e.Value, true 782 } 783 784 case *EBinary: 785 // Make sure that these transformations are all safe for special values. 786 // For example, "!(a < b)" is not the same as "a >= b" if a and/or b are 787 // NaN (or undefined, or null, or possibly other problem cases too). 788 switch e.Op { 789 case BinOpLooseEq: 790 // "!(a == b)" => "a != b" 791 e.Op = BinOpLooseNe 792 return expr, true 793 794 case BinOpLooseNe: 795 // "!(a != b)" => "a == b" 796 e.Op = BinOpLooseEq 797 return expr, true 798 799 case BinOpStrictEq: 800 // "!(a === b)" => "a !== b" 801 e.Op = BinOpStrictNe 802 return expr, true 803 804 case BinOpStrictNe: 805 // "!(a !== b)" => "a === b" 806 e.Op = BinOpStrictEq 807 return expr, true 808 809 case BinOpComma: 810 // "!(a, b)" => "a, !b" 811 e.Right = Not(e.Right) 812 return expr, true 813 } 814 } 815 816 return Expr{}, false 817} 818 819type PrimitiveType uint8 820 821const ( 822 PrimitiveUnknown PrimitiveType = iota 823 PrimitiveMixed 824 PrimitiveNull 825 PrimitiveUndefined 826 PrimitiveBoolean 827 PrimitiveNumber 828 PrimitiveString 829 PrimitiveBigInt 830) 831 832// This can be used when the returned type is either one or the other 833func MergedKnownPrimitiveTypes(a Expr, b Expr) PrimitiveType { 834 x := KnownPrimitiveType(a) 835 y := KnownPrimitiveType(b) 836 if x == PrimitiveUnknown || y == PrimitiveUnknown { 837 return PrimitiveUnknown 838 } 839 if x == y { 840 return x 841 } 842 return PrimitiveMixed // Definitely some kind of primitive 843} 844 845func KnownPrimitiveType(a Expr) PrimitiveType { 846 switch e := a.Data.(type) { 847 case *EInlinedEnum: 848 return KnownPrimitiveType(e.Value) 849 850 case *ENull: 851 return PrimitiveNull 852 853 case *EUndefined: 854 return PrimitiveUndefined 855 856 case *EBoolean: 857 return PrimitiveBoolean 858 859 case *ENumber: 860 return PrimitiveNumber 861 862 case *EString: 863 return PrimitiveString 864 865 case *EBigInt: 866 return PrimitiveBigInt 867 868 case *ETemplate: 869 if e.TagOrNil.Data == nil { 870 return PrimitiveString 871 } 872 873 case *EIf: 874 return MergedKnownPrimitiveTypes(e.Yes, e.No) 875 876 case *EUnary: 877 switch e.Op { 878 case UnOpVoid: 879 return PrimitiveUndefined 880 881 case UnOpTypeof: 882 return PrimitiveString 883 884 case UnOpNot, UnOpDelete: 885 return PrimitiveBoolean 886 887 case UnOpPos: 888 return PrimitiveNumber // Cannot be bigint because that throws an exception 889 890 case UnOpNeg, UnOpCpl: 891 value := KnownPrimitiveType(e.Value) 892 if value == PrimitiveBigInt { 893 return PrimitiveBigInt 894 } 895 if value != PrimitiveUnknown && value != PrimitiveMixed { 896 return PrimitiveNumber 897 } 898 return PrimitiveMixed // Can be number or bigint 899 900 case UnOpPreDec, UnOpPreInc, UnOpPostDec, UnOpPostInc: 901 return PrimitiveMixed // Can be number or bigint 902 } 903 904 case *EBinary: 905 switch e.Op { 906 case BinOpStrictEq, BinOpStrictNe, BinOpLooseEq, BinOpLooseNe, 907 BinOpLt, BinOpGt, BinOpLe, BinOpGe, 908 BinOpInstanceof, BinOpIn: 909 return PrimitiveBoolean 910 911 case BinOpLogicalOr, BinOpLogicalAnd: 912 return MergedKnownPrimitiveTypes(e.Left, e.Right) 913 914 case BinOpNullishCoalescing: 915 left := KnownPrimitiveType(e.Left) 916 right := KnownPrimitiveType(e.Right) 917 if left == PrimitiveNull || left == PrimitiveUndefined { 918 return right 919 } 920 if left != PrimitiveUnknown { 921 if left != PrimitiveMixed { 922 return left // Definitely not null or undefined 923 } 924 if right != PrimitiveUnknown { 925 return PrimitiveMixed // Definitely some kind of primitive 926 } 927 } 928 929 case BinOpAdd: 930 left := KnownPrimitiveType(e.Left) 931 right := KnownPrimitiveType(e.Right) 932 if left == PrimitiveString || right == PrimitiveString { 933 return PrimitiveString 934 } 935 if left == PrimitiveBigInt && right == PrimitiveBigInt { 936 return PrimitiveBigInt 937 } 938 if left != PrimitiveUnknown && left != PrimitiveMixed && left != PrimitiveBigInt && 939 right != PrimitiveUnknown && right != PrimitiveMixed && right != PrimitiveBigInt { 940 return PrimitiveNumber 941 } 942 return PrimitiveMixed // Can be number or bigint or string (or an exception) 943 944 case BinOpAddAssign: 945 right := KnownPrimitiveType(e.Right) 946 if right == PrimitiveString { 947 return PrimitiveString 948 } 949 return PrimitiveMixed // Can be number or bigint or string (or an exception) 950 951 case 952 BinOpSub, BinOpSubAssign, 953 BinOpMul, BinOpMulAssign, 954 BinOpDiv, BinOpDivAssign, 955 BinOpRem, BinOpRemAssign, 956 BinOpPow, BinOpPowAssign, 957 BinOpBitwiseAnd, BinOpBitwiseAndAssign, 958 BinOpBitwiseOr, BinOpBitwiseOrAssign, 959 BinOpBitwiseXor, BinOpBitwiseXorAssign, 960 BinOpShl, BinOpShlAssign, 961 BinOpShr, BinOpShrAssign, 962 BinOpUShr, BinOpUShrAssign: 963 return PrimitiveMixed // Can be number or bigint (or an exception) 964 965 case BinOpAssign, BinOpComma: 966 return KnownPrimitiveType(e.Right) 967 } 968 } 969 970 return PrimitiveUnknown 971} 972 973// The goal of this function is to "rotate" the AST if it's possible to use the 974// left-associative property of the operator to avoid unnecessary parentheses. 975// 976// When using this, make absolutely sure that the operator is actually 977// associative. For example, the "-" operator is not associative for 978// floating-point numbers. 979func JoinWithLeftAssociativeOp(op OpCode, a Expr, b Expr) Expr { 980 // "(a, b) op c" => "a, b op c" 981 if comma, ok := a.Data.(*EBinary); ok && comma.Op == BinOpComma { 982 comma.Right = JoinWithLeftAssociativeOp(op, comma.Right, b) 983 return a 984 } 985 986 // "a op (b op c)" => "(a op b) op c" 987 // "a op (b op (c op d))" => "((a op b) op c) op d" 988 if binary, ok := b.Data.(*EBinary); ok && binary.Op == op { 989 return JoinWithLeftAssociativeOp( 990 op, 991 JoinWithLeftAssociativeOp(op, a, binary.Left), 992 binary.Right, 993 ) 994 } 995 996 // "a op b" => "a op b" 997 // "(a op b) op c" => "(a op b) op c" 998 return Expr{Loc: a.Loc, Data: &EBinary{Op: op, Left: a, Right: b}} 999} 1000 1001func JoinWithComma(a Expr, b Expr) Expr { 1002 if a.Data == nil { 1003 return b 1004 } 1005 if b.Data == nil { 1006 return a 1007 } 1008 return Expr{Loc: a.Loc, Data: &EBinary{Op: BinOpComma, Left: a, Right: b}} 1009} 1010 1011func JoinAllWithComma(all []Expr) (result Expr) { 1012 for _, value := range all { 1013 result = JoinWithComma(result, value) 1014 } 1015 return 1016} 1017 1018type Stmt struct { 1019 Loc logger.Loc 1020 Data S 1021} 1022 1023// This interface is never called. Its purpose is to encode a variant type in 1024// Go's type system. 1025type S interface{ isStmt() } 1026 1027func (*SBlock) isStmt() {} 1028func (*SComment) isStmt() {} 1029func (*SDebugger) isStmt() {} 1030func (*SDirective) isStmt() {} 1031func (*SEmpty) isStmt() {} 1032func (*STypeScript) isStmt() {} 1033func (*SExportClause) isStmt() {} 1034func (*SExportFrom) isStmt() {} 1035func (*SExportDefault) isStmt() {} 1036func (*SExportStar) isStmt() {} 1037func (*SExportEquals) isStmt() {} 1038func (*SLazyExport) isStmt() {} 1039func (*SExpr) isStmt() {} 1040func (*SEnum) isStmt() {} 1041func (*SNamespace) isStmt() {} 1042func (*SFunction) isStmt() {} 1043func (*SClass) isStmt() {} 1044func (*SLabel) isStmt() {} 1045func (*SIf) isStmt() {} 1046func (*SFor) isStmt() {} 1047func (*SForIn) isStmt() {} 1048func (*SForOf) isStmt() {} 1049func (*SDoWhile) isStmt() {} 1050func (*SWhile) isStmt() {} 1051func (*SWith) isStmt() {} 1052func (*STry) isStmt() {} 1053func (*SSwitch) isStmt() {} 1054func (*SImport) isStmt() {} 1055func (*SReturn) isStmt() {} 1056func (*SThrow) isStmt() {} 1057func (*SLocal) isStmt() {} 1058func (*SBreak) isStmt() {} 1059func (*SContinue) isStmt() {} 1060 1061type SBlock struct { 1062 Stmts []Stmt 1063} 1064 1065type SEmpty struct{} 1066 1067// This is a stand-in for a TypeScript type declaration 1068type STypeScript struct{} 1069 1070type SComment struct { 1071 Text string 1072 IsLegalComment bool 1073} 1074 1075type SDebugger struct{} 1076 1077type SDirective struct { 1078 Value []uint16 1079 LegacyOctalLoc logger.Loc 1080} 1081 1082type SExportClause struct { 1083 Items []ClauseItem 1084 IsSingleLine bool 1085} 1086 1087type SExportFrom struct { 1088 Items []ClauseItem 1089 NamespaceRef Ref 1090 ImportRecordIndex uint32 1091 IsSingleLine bool 1092} 1093 1094type SExportDefault struct { 1095 DefaultName LocRef 1096 Value Stmt // May be a SExpr or SFunction or SClass 1097} 1098 1099type ExportStarAlias struct { 1100 Loc logger.Loc 1101 1102 // Although this alias name starts off as being the same as the statement's 1103 // namespace symbol, it may diverge if the namespace symbol name is minified. 1104 // The original alias name is preserved here to avoid this scenario. 1105 OriginalName string 1106} 1107 1108type SExportStar struct { 1109 NamespaceRef Ref 1110 Alias *ExportStarAlias 1111 ImportRecordIndex uint32 1112} 1113 1114// This is an "export = value;" statement in TypeScript 1115type SExportEquals struct { 1116 Value Expr 1117} 1118 1119// The decision of whether to export an expression using "module.exports" or 1120// "export default" is deferred until linking using this statement kind 1121type SLazyExport struct { 1122 Value Expr 1123} 1124 1125type SExpr struct { 1126 Value Expr 1127 1128 // This is set to true for automatically-generated expressions that should 1129 // not affect tree shaking. For example, calling a function from the runtime 1130 // that doesn't have externally-visible side effects. 1131 DoesNotAffectTreeShaking bool 1132} 1133 1134type EnumValue struct { 1135 Name []uint16 1136 ValueOrNil Expr 1137 Ref Ref 1138 Loc logger.Loc 1139} 1140 1141type SEnum struct { 1142 Name LocRef 1143 Arg Ref 1144 Values []EnumValue 1145 IsExport bool 1146} 1147 1148type SNamespace struct { 1149 Name LocRef 1150 Arg Ref 1151 Stmts []Stmt 1152 IsExport bool 1153} 1154 1155type SFunction struct { 1156 Fn Fn 1157 IsExport bool 1158} 1159 1160type SClass struct { 1161 Class Class 1162 IsExport bool 1163} 1164 1165type SLabel struct { 1166 Name LocRef 1167 Stmt Stmt 1168} 1169 1170type SIf struct { 1171 Test Expr 1172 Yes Stmt 1173 NoOrNil Stmt 1174} 1175 1176type SFor struct { 1177 InitOrNil Stmt // May be a SConst, SLet, SVar, or SExpr 1178 TestOrNil Expr 1179 UpdateOrNil Expr 1180 Body Stmt 1181} 1182 1183type SForIn struct { 1184 Init Stmt // May be a SConst, SLet, SVar, or SExpr 1185 Value Expr 1186 Body Stmt 1187} 1188 1189type SForOf struct { 1190 IsAwait bool 1191 Init Stmt // May be a SConst, SLet, SVar, or SExpr 1192 Value Expr 1193 Body Stmt 1194} 1195 1196type SDoWhile struct { 1197 Body Stmt 1198 Test Expr 1199} 1200 1201type SWhile struct { 1202 Test Expr 1203 Body Stmt 1204} 1205 1206type SWith struct { 1207 Value Expr 1208 BodyLoc logger.Loc 1209 Body Stmt 1210} 1211 1212type Catch struct { 1213 BindingOrNil Binding 1214 Body []Stmt 1215 Loc logger.Loc 1216 BodyLoc logger.Loc 1217} 1218 1219type Finally struct { 1220 Loc logger.Loc 1221 Stmts []Stmt 1222} 1223 1224type STry struct { 1225 BodyLoc logger.Loc 1226 Body []Stmt 1227 Catch *Catch 1228 Finally *Finally 1229} 1230 1231type Case struct { 1232 ValueOrNil Expr // If this is nil, this is "default" instead of "case" 1233 Body []Stmt 1234} 1235 1236type SSwitch struct { 1237 Test Expr 1238 BodyLoc logger.Loc 1239 Cases []Case 1240} 1241 1242// This object represents all of these types of import statements: 1243// 1244// import 'path' 1245// import {item1, item2} from 'path' 1246// import * as ns from 'path' 1247// import defaultItem, {item1, item2} from 'path' 1248// import defaultItem, * as ns from 'path' 1249// 1250// Many parts are optional and can be combined in different ways. The only 1251// restriction is that you cannot have both a clause and a star namespace. 1252type SImport struct { 1253 // If this is a star import: This is a Ref for the namespace symbol. The Loc 1254 // for the symbol is StarLoc. 1255 // 1256 // Otherwise: This is an auto-generated Ref for the namespace representing 1257 // the imported file. In this case StarLoc is nil. The NamespaceRef is used 1258 // when converting this module to a CommonJS module. 1259 NamespaceRef Ref 1260 1261 DefaultName *LocRef 1262 Items *[]ClauseItem 1263 StarNameLoc *logger.Loc 1264 ImportRecordIndex uint32 1265 IsSingleLine bool 1266} 1267 1268type SReturn struct { 1269 ValueOrNil Expr 1270} 1271 1272type SThrow struct { 1273 Value Expr 1274} 1275 1276type LocalKind uint8 1277 1278const ( 1279 LocalVar LocalKind = iota 1280 LocalLet 1281 LocalConst 1282) 1283 1284type SLocal struct { 1285 Decls []Decl 1286 Kind LocalKind 1287 IsExport bool 1288 1289 // The TypeScript compiler doesn't generate code for "import foo = bar" 1290 // statements where the import is never used. 1291 WasTSImportEquals bool 1292} 1293 1294type SBreak struct { 1295 Label *LocRef 1296} 1297 1298type SContinue struct { 1299 Label *LocRef 1300} 1301 1302func IsSuperCall(stmt Stmt) bool { 1303 if expr, ok := stmt.Data.(*SExpr); ok { 1304 if call, ok := expr.Value.Data.(*ECall); ok { 1305 if _, ok := call.Target.Data.(*ESuper); ok { 1306 return true 1307 } 1308 } 1309 } 1310 return false 1311} 1312 1313type ClauseItem struct { 1314 Alias string 1315 AliasLoc logger.Loc 1316 Name LocRef 1317 1318 // This is the original name of the symbol stored in "Name". It's needed for 1319 // "SExportClause" statements such as this: 1320 // 1321 // export {foo as bar} from 'path' 1322 // 1323 // In this case both "foo" and "bar" are aliases because it's a re-export. 1324 // We need to preserve both aliases in case the symbol is renamed. In this 1325 // example, "foo" is "OriginalName" and "bar" is "Alias". 1326 OriginalName string 1327} 1328 1329type Decl struct { 1330 Binding Binding 1331 ValueOrNil Expr 1332} 1333 1334type SymbolKind uint8 1335 1336const ( 1337 // An unbound symbol is one that isn't declared in the file it's referenced 1338 // in. For example, using "window" without declaring it will be unbound. 1339 SymbolUnbound SymbolKind = iota 1340 1341 // This has special merging behavior. You're allowed to re-declare these 1342 // symbols more than once in the same scope. These symbols are also hoisted 1343 // out of the scope they are declared in to the closest containing function 1344 // or module scope. These are the symbols with this kind: 1345 // 1346 // - Function arguments 1347 // - Function statements 1348 // - Variables declared using "var" 1349 // 1350 SymbolHoisted 1351 SymbolHoistedFunction 1352 1353 // There's a weird special case where catch variables declared using a simple 1354 // identifier (i.e. not a binding pattern) block hoisted variables instead of 1355 // becoming an error: 1356 // 1357 // var e = 0; 1358 // try { throw 1 } catch (e) { 1359 // print(e) // 1 1360 // var e = 2 1361 // print(e) // 2 1362 // } 1363 // print(e) // 0 (since the hoisting stops at the catch block boundary) 1364 // 1365 // However, other forms are still a syntax error: 1366 // 1367 // try {} catch (e) { let e } 1368 // try {} catch ({e}) { var e } 1369 // 1370 // This symbol is for handling this weird special case. 1371 SymbolCatchIdentifier 1372 1373 // Generator and async functions are not hoisted, but still have special 1374 // properties such as being able to overwrite previous functions with the 1375 // same name 1376 SymbolGeneratorOrAsyncFunction 1377 1378 // This is the special "arguments" variable inside functions 1379 SymbolArguments 1380 1381 // Classes can merge with TypeScript namespaces. 1382 SymbolClass 1383 1384 // A class-private identifier (i.e. "#foo"). 1385 SymbolPrivateField 1386 SymbolPrivateMethod 1387 SymbolPrivateGet 1388 SymbolPrivateSet 1389 SymbolPrivateGetSetPair 1390 SymbolPrivateStaticField 1391 SymbolPrivateStaticMethod 1392 SymbolPrivateStaticGet 1393 SymbolPrivateStaticSet 1394 SymbolPrivateStaticGetSetPair 1395 1396 // Labels are in their own namespace 1397 SymbolLabel 1398 1399 // TypeScript enums can merge with TypeScript namespaces and other TypeScript 1400 // enums. 1401 SymbolTSEnum 1402 1403 // TypeScript namespaces can merge with classes, functions, TypeScript enums, 1404 // and other TypeScript namespaces. 1405 SymbolTSNamespace 1406 1407 // In TypeScript, imports are allowed to silently collide with symbols within 1408 // the module. Presumably this is because the imports may be type-only. 1409 SymbolImport 1410 1411 // Assigning to a "const" symbol will throw a TypeError at runtime 1412 SymbolConst 1413 1414 // Injected symbols can be overridden by provided defines 1415 SymbolInjected 1416 1417 // This annotates all other symbols that don't have special behavior. 1418 SymbolOther 1419) 1420 1421func (kind SymbolKind) IsPrivate() bool { 1422 return kind >= SymbolPrivateField && kind <= SymbolPrivateStaticGetSetPair 1423} 1424 1425func (kind SymbolKind) Feature() compat.JSFeature { 1426 switch kind { 1427 case SymbolPrivateField: 1428 return compat.ClassPrivateField 1429 case SymbolPrivateMethod: 1430 return compat.ClassPrivateMethod 1431 case SymbolPrivateGet, SymbolPrivateSet, SymbolPrivateGetSetPair: 1432 return compat.ClassPrivateAccessor 1433 case SymbolPrivateStaticField: 1434 return compat.ClassPrivateStaticField 1435 case SymbolPrivateStaticMethod: 1436 return compat.ClassPrivateStaticMethod 1437 case SymbolPrivateStaticGet, SymbolPrivateStaticSet, SymbolPrivateStaticGetSetPair: 1438 return compat.ClassPrivateStaticAccessor 1439 default: 1440 return 0 1441 } 1442} 1443 1444func (kind SymbolKind) IsHoisted() bool { 1445 return kind == SymbolHoisted || kind == SymbolHoistedFunction 1446} 1447 1448func (kind SymbolKind) IsHoistedOrFunction() bool { 1449 return kind.IsHoisted() || kind == SymbolGeneratorOrAsyncFunction 1450} 1451 1452func (kind SymbolKind) IsFunction() bool { 1453 return kind == SymbolHoistedFunction || kind == SymbolGeneratorOrAsyncFunction 1454} 1455 1456func (kind SymbolKind) IsUnboundOrInjected() bool { 1457 return kind == SymbolUnbound || kind == SymbolInjected 1458} 1459 1460var InvalidRef Ref = Ref{^uint32(0), ^uint32(0)} 1461 1462// Files are parsed in parallel for speed. We want to allow each parser to 1463// generate symbol IDs that won't conflict with each other. We also want to be 1464// able to quickly merge symbol tables from all files into one giant symbol 1465// table. 1466// 1467// We can accomplish both goals by giving each symbol ID two parts: a source 1468// index that is unique to the parser goroutine, and an inner index that 1469// increments as the parser generates new symbol IDs. Then a symbol map can 1470// be an array of arrays indexed first by source index, then by inner index. 1471// The maps can be merged quickly by creating a single outer array containing 1472// all inner arrays from all parsed files. 1473type Ref struct { 1474 SourceIndex uint32 1475 InnerIndex uint32 1476} 1477 1478type ImportItemStatus uint8 1479 1480const ( 1481 ImportItemNone ImportItemStatus = iota 1482 1483 // The linker doesn't report import/export mismatch errors 1484 ImportItemGenerated 1485 1486 // The printer will replace this import with "undefined" 1487 ImportItemMissing 1488) 1489 1490type SymbolFlags uint8 1491 1492const ( 1493 // Certain symbols must not be renamed or minified. For example, the 1494 // "arguments" variable is declared by the runtime for every function. 1495 // Renaming can also break any identifier used inside a "with" statement. 1496 MustNotBeRenamed SymbolFlags = 1 << iota 1497 1498 // In React's version of JSX, lower-case names are strings while upper-case 1499 // names are identifiers. If we are preserving JSX syntax (i.e. not 1500 // transforming it), then we need to be careful to name the identifiers 1501 // something with a capital letter so further JSX processing doesn't treat 1502 // them as strings instead. 1503 MustStartWithCapitalLetterForJSX 1504 1505 // If true, this symbol is the target of a "__name" helper function call. 1506 // This call is special because it deliberately doesn't count as a use 1507 // of the symbol (otherwise keeping names would disable tree shaking) 1508 // so "UseCountEstimate" is not incremented. This flag helps us know to 1509 // avoid optimizing this symbol when "UseCountEstimate" is 1 in this case. 1510 DidKeepName 1511 1512 // Sometimes we lower private symbols even if they are supported. For example, 1513 // consider the following TypeScript code: 1514 // 1515 // class Foo { 1516 // #foo = 123 1517 // bar = this.#foo 1518 // } 1519 // 1520 // If "useDefineForClassFields: false" is set in "tsconfig.json", then "bar" 1521 // must use assignment semantics instead of define semantics. We can compile 1522 // that to this code: 1523 // 1524 // class Foo { 1525 // constructor() { 1526 // this.#foo = 123; 1527 // this.bar = this.#foo; 1528 // } 1529 // #foo; 1530 // } 1531 // 1532 // However, we can't do the same for static fields: 1533 // 1534 // class Foo { 1535 // static #foo = 123 1536 // static bar = this.#foo 1537 // } 1538 // 1539 // Compiling these static fields to something like this would be invalid: 1540 // 1541 // class Foo { 1542 // static #foo; 1543 // } 1544 // Foo.#foo = 123; 1545 // Foo.bar = Foo.#foo; 1546 // 1547 // Thus "#foo" must be lowered even though it's supported. Another case is 1548 // when we're converting top-level class declarations to class expressions 1549 // to avoid the TDZ and the class shadowing symbol is referenced within the 1550 // class body: 1551 // 1552 // class Foo { 1553 // static #foo = Foo 1554 // } 1555 // 1556 // This cannot be converted into something like this: 1557 // 1558 // var Foo = class { 1559 // static #foo; 1560 // }; 1561 // Foo.#foo = Foo; 1562 // 1563 PrivateSymbolMustBeLowered 1564 1565 // This is used to remove the all but the last function re-declaration if a 1566 // function is re-declared multiple times like this: 1567 // 1568 // function foo() { console.log(1) } 1569 // function foo() { console.log(2) } 1570 // 1571 RemoveOverwrittenFunctionDeclaration 1572) 1573 1574func (flags SymbolFlags) Has(flag SymbolFlags) bool { 1575 return (flags & flag) != 0 1576} 1577 1578// Note: the order of values in this struct matters to reduce struct size. 1579type Symbol struct { 1580 // This is the name that came from the parser. Printed names may be renamed 1581 // during minification or to avoid name collisions. Do not use the original 1582 // name during printing. 1583 OriginalName string 1584 1585 // This is used for symbols that represent items in the import clause of an 1586 // ES6 import statement. These should always be referenced by EImportIdentifier 1587 // instead of an EIdentifier. When this is present, the expression should 1588 // be printed as a property access off the namespace instead of as a bare 1589 // identifier. 1590 // 1591 // For correctness, this must be stored on the symbol instead of indirectly 1592 // associated with the Ref for the symbol somehow. In ES6 "flat bundling" 1593 // mode, re-exported symbols are collapsed using MergeSymbols() and renamed 1594 // symbols from other files that end up at this symbol must be able to tell 1595 // if it has a namespace alias. 1596 NamespaceAlias *NamespaceAlias 1597 1598 // Used by the parser for single pass parsing. Symbols that have been merged 1599 // form a linked-list where the last link is the symbol to use. This link is 1600 // an invalid ref if it's the last link. If this isn't invalid, you need to 1601 // FollowSymbols to get the real one. 1602 Link Ref 1603 1604 // An estimate of the number of uses of this symbol. This is used to detect 1605 // whether a symbol is used or not. For example, TypeScript imports that are 1606 // unused must be removed because they are probably type-only imports. This 1607 // is an estimate and may not be completely accurate due to oversights in the 1608 // code. But it should always be non-zero when the symbol is used. 1609 UseCountEstimate uint32 1610 1611 // This is for generating cross-chunk imports and exports for code splitting. 1612 ChunkIndex ast.Index32 1613 1614 // This is used for minification. Symbols that are declared in sibling scopes 1615 // can share a name. A good heuristic (from Google Closure Compiler) is to 1616 // assign names to symbols from sibling scopes in declaration order. That way 1617 // local variable names are reused in each global function like this, which 1618 // improves gzip compression: 1619 // 1620 // function x(a, b) { ... } 1621 // function y(a, b, c) { ... } 1622 // 1623 // The parser fills this in for symbols inside nested scopes. There are three 1624 // slot namespaces: regular symbols, label symbols, and private symbols. 1625 NestedScopeSlot ast.Index32 1626 1627 Kind SymbolKind 1628 1629 // We automatically generate import items for property accesses off of 1630 // namespace imports. This lets us remove the expensive namespace imports 1631 // while bundling in many cases, replacing them with a cheap import item 1632 // instead: 1633 // 1634 // import * as ns from 'path' 1635 // ns.foo() 1636 // 1637 // That can often be replaced by this, which avoids needing the namespace: 1638 // 1639 // import {foo} from 'path' 1640 // foo() 1641 // 1642 // However, if the import is actually missing then we don't want to report a 1643 // compile-time error like we do for real import items. This status lets us 1644 // avoid this. We also need to be able to replace such import items with 1645 // undefined, which this status is also used for. 1646 ImportItemStatus ImportItemStatus 1647 1648 // Boolean values should all be flags instead to save space 1649 Flags SymbolFlags 1650} 1651 1652// You should call "MergeSymbols" instead of calling this directly 1653func (newSymbol *Symbol) MergeContentsWith(oldSymbol *Symbol) { 1654 newSymbol.UseCountEstimate += oldSymbol.UseCountEstimate 1655 if oldSymbol.Flags.Has(MustNotBeRenamed) { 1656 newSymbol.OriginalName = oldSymbol.OriginalName 1657 newSymbol.Flags |= MustNotBeRenamed 1658 } 1659 if oldSymbol.Flags.Has(MustStartWithCapitalLetterForJSX) { 1660 newSymbol.Flags |= MustStartWithCapitalLetterForJSX 1661 } 1662} 1663 1664type SlotNamespace uint8 1665 1666const ( 1667 SlotDefault SlotNamespace = iota 1668 SlotLabel 1669 SlotPrivateName 1670 SlotMustNotBeRenamed 1671) 1672 1673func (s *Symbol) SlotNamespace() SlotNamespace { 1674 if s.Kind == SymbolUnbound || s.Flags.Has(MustNotBeRenamed) { 1675 return SlotMustNotBeRenamed 1676 } 1677 if s.Kind.IsPrivate() { 1678 return SlotPrivateName 1679 } 1680 if s.Kind == SymbolLabel { 1681 return SlotLabel 1682 } 1683 return SlotDefault 1684} 1685 1686type SlotCounts [3]uint32 1687 1688func (a *SlotCounts) UnionMax(b SlotCounts) { 1689 for i := range *a { 1690 ai := &(*a)[i] 1691 bi := b[i] 1692 if *ai < bi { 1693 *ai = bi 1694 } 1695 } 1696} 1697 1698type NamespaceAlias struct { 1699 NamespaceRef Ref 1700 Alias string 1701} 1702 1703type ScopeKind int 1704 1705const ( 1706 ScopeBlock ScopeKind = iota 1707 ScopeWith 1708 ScopeLabel 1709 ScopeClassName 1710 ScopeClassBody 1711 ScopeCatchBinding 1712 1713 // The scopes below stop hoisted variables from extending into parent scopes 1714 ScopeEntry // This is a module, TypeScript enum, or TypeScript namespace 1715 ScopeFunctionArgs 1716 ScopeFunctionBody 1717 ScopeClassStaticInit 1718) 1719 1720func (kind ScopeKind) StopsHoisting() bool { 1721 return kind >= ScopeEntry 1722} 1723 1724type ScopeMember struct { 1725 Ref Ref 1726 Loc logger.Loc 1727} 1728 1729type Scope struct { 1730 Kind ScopeKind 1731 Parent *Scope 1732 Children []*Scope 1733 Members map[string]ScopeMember 1734 Generated []Ref 1735 1736 // This will be non-nil if this is a TypeScript "namespace" or "enum" 1737 TSNamespace *TSNamespaceScope 1738 1739 // The location of the "use strict" directive for ExplicitStrictMode 1740 UseStrictLoc logger.Loc 1741 1742 // This is used to store the ref of the label symbol for ScopeLabel scopes. 1743 Label LocRef 1744 LabelStmtIsLoop bool 1745 1746 // If a scope contains a direct eval() expression, then none of the symbols 1747 // inside that scope can be renamed. We conservatively assume that the 1748 // evaluated code might reference anything that it has access to. 1749 ContainsDirectEval bool 1750 1751 // This is to help forbid "arguments" inside class body scopes 1752 ForbidArguments bool 1753 1754 StrictMode StrictModeKind 1755} 1756 1757type StrictModeKind uint8 1758 1759const ( 1760 SloppyMode StrictModeKind = iota 1761 ExplicitStrictMode 1762 ImplicitStrictModeImport 1763 ImplicitStrictModeExport 1764 ImplicitStrictModeTopLevelAwait 1765 ImplicitStrictModeClass 1766) 1767 1768func (s *Scope) RecursiveSetStrictMode(kind StrictModeKind) { 1769 if s.StrictMode == SloppyMode { 1770 s.StrictMode = kind 1771 for _, child := range s.Children { 1772 child.RecursiveSetStrictMode(kind) 1773 } 1774 } 1775} 1776 1777// This is for TypeScript "enum" and "namespace" blocks. Each block can 1778// potentially be instantiated multiple times. The exported members of each 1779// block are merged into a single namespace while the non-exported code is 1780// still scoped to just within that block: 1781// 1782// let x = 1; 1783// namespace Foo { 1784// let x = 2; 1785// export let y = 3; 1786// } 1787// namespace Foo { 1788// console.log(x); // 1 1789// console.log(y); // 3 1790// } 1791// 1792// Doing this also works inside an enum: 1793// 1794// enum Foo { 1795// A = 3, 1796// B = A + 1, 1797// } 1798// enum Foo { 1799// C = A + 2, 1800// } 1801// console.log(Foo.B) // 4 1802// console.log(Foo.C) // 5 1803// 1804// This is a form of identifier lookup that works differently than the 1805// hierarchical scope-based identifier lookup in JavaScript. Lookup now needs 1806// to search sibling scopes in addition to parent scopes. This is accomplished 1807// by sharing the map of exported members between all matching sibling scopes. 1808type TSNamespaceScope struct { 1809 // This is shared between all sibling namespace blocks 1810 ExportedMembers TSNamespaceMembers 1811 1812 // This is specific to this namespace block. It's the argument of the 1813 // immediately-invoked function expression that the namespace block is 1814 // compiled into: 1815 // 1816 // var ns; 1817 // (function (ns2) { 1818 // ns2.x = 123; 1819 // })(ns || (ns = {})); 1820 // 1821 // This variable is "ns2" in the above example. It's the symbol to use when 1822 // generating property accesses off of this namespace when it's in scope. 1823 ArgRef Ref 1824 1825 // This is a lazily-generated map of identifiers that actually represent 1826 // property accesses to this namespace's properties. For example: 1827 // 1828 // namespace x { 1829 // export let y = 123 1830 // } 1831 // namespace x { 1832 // export let z = y 1833 // } 1834 // 1835 // This should be compiled into the following code: 1836 // 1837 // var x; 1838 // (function(x2) { 1839 // x2.y = 123; 1840 // })(x || (x = {})); 1841 // (function(x3) { 1842 // x3.z = x3.y; 1843 // })(x || (x = {})); 1844 // 1845 // When we try to find the symbol "y", we instead return one of these lazily 1846 // generated proxy symbols that represent the property access "x3.y". This 1847 // map is unique per namespace block because "x3" is the argument symbol that 1848 // is specific to that particular namespace block. 1849 LazilyGeneratedProperyAccesses map[string]Ref 1850 1851 // Even though enums are like namespaces and both enums and namespaces allow 1852 // implicit references to properties of sibling scopes, they behave like 1853 // separate, er, namespaces. Implicit references only work namespace-to- 1854 // namespace and enum-to-enum. They do not work enum-to-namespace. And I'm 1855 // not sure what's supposed to happen for the namespace-to-enum case because 1856 // the compiler crashes: https://github.com/microsoft/TypeScript/issues/46891. 1857 // So basically these both work: 1858 // 1859 // enum a { b = 1 } 1860 // enum a { c = b } 1861 // 1862 // namespace x { export let y = 1 } 1863 // namespace x { export let z = y } 1864 // 1865 // This doesn't work: 1866 // 1867 // enum a { b = 1 } 1868 // namespace a { export let c = b } 1869 // 1870 // And this crashes the TypeScript compiler: 1871 // 1872 // namespace a { export let b = 1 } 1873 // enum a { c = b } 1874 // 1875 // Therefore we only allow enum/enum and namespace/namespace interactions. 1876 IsEnumScope bool 1877} 1878 1879type TSNamespaceMembers map[string]TSNamespaceMember 1880 1881type TSNamespaceMember struct { 1882 Data TSNamespaceMemberData 1883 Loc logger.Loc 1884 IsEnumValue bool 1885} 1886 1887type TSNamespaceMemberData interface { 1888 isTSNamespaceMember() 1889} 1890 1891func (TSNamespaceMemberProperty) isTSNamespaceMember() {} 1892func (TSNamespaceMemberNamespace) isTSNamespaceMember() {} 1893func (TSNamespaceMemberEnumNumber) isTSNamespaceMember() {} 1894func (TSNamespaceMemberEnumString) isTSNamespaceMember() {} 1895 1896// "namespace ns { export let it }" 1897type TSNamespaceMemberProperty struct{} 1898 1899// "namespace ns { export namespace it {} }" 1900type TSNamespaceMemberNamespace struct { 1901 ExportedMembers TSNamespaceMembers 1902} 1903 1904// "enum ns { it }" 1905type TSNamespaceMemberEnumNumber struct { 1906 Value float64 1907} 1908 1909// "enum ns { it = 'it' }" 1910type TSNamespaceMemberEnumString struct { 1911 Value []uint16 1912} 1913 1914type SymbolMap struct { 1915 // This could be represented as a "map[Ref]Symbol" but a two-level array was 1916 // more efficient in profiles. This appears to be because it doesn't involve 1917 // a hash. This representation also makes it trivial to quickly merge symbol 1918 // maps from multiple files together. Each file only generates symbols in a 1919 // single inner array, so you can join the maps together by just make a 1920 // single outer array containing all of the inner arrays. See the comment on 1921 // "Ref" for more detail. 1922 SymbolsForSource [][]Symbol 1923} 1924 1925func NewSymbolMap(sourceCount int) SymbolMap { 1926 return SymbolMap{make([][]Symbol, sourceCount)} 1927} 1928 1929func (sm SymbolMap) Get(ref Ref) *Symbol { 1930 return &sm.SymbolsForSource[ref.SourceIndex][ref.InnerIndex] 1931} 1932 1933type ExportsKind uint8 1934 1935const ( 1936 // This file doesn't have any kind of export, so it's impossible to say what 1937 // kind of file this is. An empty file is in this category, for example. 1938 ExportsNone ExportsKind = iota 1939 1940 // The exports are stored on "module" and/or "exports". Calling "require()" 1941 // on this module returns "module.exports". All imports to this module are 1942 // allowed but may return undefined. 1943 ExportsCommonJS 1944 1945 // All export names are known explicitly. Calling "require()" on this module 1946 // generates an exports object (stored in "exports") with getters for the 1947 // export names. Named imports to this module are only allowed if they are 1948 // in the set of export names. 1949 ExportsESM 1950 1951 // Some export names are known explicitly, but others fall back to a dynamic 1952 // run-time object. This is necessary when using the "export * from" syntax 1953 // with either a CommonJS module or an external module (i.e. a module whose 1954 // export names are not known at compile-time). 1955 // 1956 // Calling "require()" on this module generates an exports object (stored in 1957 // "exports") with getters for the export names. All named imports to this 1958 // module are allowed. Direct named imports reference the corresponding export 1959 // directly. Other imports go through property accesses on "exports". 1960 ExportsESMWithDynamicFallback 1961) 1962 1963func (kind ExportsKind) IsDynamic() bool { 1964 return kind == ExportsCommonJS || kind == ExportsESMWithDynamicFallback 1965} 1966 1967type ModuleType uint8 1968 1969const ( 1970 ModuleUnknown ModuleType = iota 1971 1972 // ".cjs" or ".cts" or "type: commonjs" in package.json 1973 ModuleCommonJS 1974 1975 // ".mjs" or ".mts" or "type: module" in package.json 1976 ModuleESM 1977) 1978 1979// This is the index to the automatically-generated part containing code that 1980// calls "__export(exports, { ... getters ... })". This is used to generate 1981// getters on an exports object for ES6 export statements, and is both for 1982// ES6 star imports and CommonJS-style modules. All files have one of these, 1983// although it may contain no statements if there is nothing to export. 1984const NSExportPartIndex = uint32(0) 1985 1986type AST struct { 1987 ApproximateLineCount int32 1988 NestedScopeSlotCounts SlotCounts 1989 HasLazyExport bool 1990 ModuleType ModuleType 1991 1992 // This is a list of CommonJS features. When a file uses CommonJS features, 1993 // it's not a candidate for "flat bundling" and must be wrapped in its own 1994 // closure. Note that this also includes top-level "return" but these aren't 1995 // here because only the parser checks those. 1996 UsesExportsRef bool 1997 UsesModuleRef bool 1998 ExportsKind ExportsKind 1999 2000 // This is a list of ES6 features. They are ranges instead of booleans so 2001 // that they can be used in log messages. Check to see if "Len > 0". 2002 ImportKeyword logger.Range // Does not include TypeScript-specific syntax or "import()" 2003 ExportKeyword logger.Range // Does not include TypeScript-specific syntax 2004 TopLevelAwaitKeyword logger.Range 2005 2006 Hashbang string 2007 Directive string 2008 URLForCSS string 2009 Parts []Part 2010 Symbols []Symbol 2011 ModuleScope *Scope 2012 CharFreq *CharFreq 2013 ExportsRef Ref 2014 ModuleRef Ref 2015 WrapperRef Ref 2016 2017 // This contains all top-level exported TypeScript enum constants. It exists 2018 // to enable cross-module inlining of constant enums. 2019 TSEnums map[Ref]map[string]TSEnumValue 2020 2021 // These are stored at the AST level instead of on individual AST nodes so 2022 // they can be manipulated efficiently without a full AST traversal 2023 ImportRecords []ast.ImportRecord 2024 2025 // These are used when bundling. They are filled in during the parser pass 2026 // since we already have to traverse the AST then anyway and the parser pass 2027 // is conveniently fully parallelized. 2028 NamedImports map[Ref]NamedImport 2029 NamedExports map[string]NamedExport 2030 ExportStarImportRecords []uint32 2031 2032 // Note: If you're in the linker, do not use this map directly. This map is 2033 // filled in by the parser and is considered immutable. For performance reasons, 2034 // the linker doesn't mutate this map (cloning a map is slow in Go). Instead the 2035 // linker super-imposes relevant information on top in a method call. You should 2036 // call "TopLevelSymbolToParts" instead. 2037 TopLevelSymbolToPartsFromParser map[Ref][]uint32 2038 2039 SourceMapComment logger.Span 2040} 2041 2042type TSEnumValue struct { 2043 String []uint16 // Use this if it's not nil 2044 Number float64 // Use this if "String" is nil 2045} 2046 2047// This is a histogram of character frequencies for minification 2048type CharFreq [64]int32 2049 2050func (freq *CharFreq) Scan(text string, delta int32) { 2051 if delta == 0 { 2052 return 2053 } 2054 2055 // This matches the order in "DefaultNameMinifier" 2056 for i, n := 0, len(text); i < n; i++ { 2057 c := text[i] 2058 switch { 2059 case c >= 'a' && c <= 'z': 2060 (*freq)[c-'a'] += delta 2061 case c >= 'A' && c <= 'Z': 2062 (*freq)[c-('A'-26)] += delta 2063 case c >= '0' && c <= '9': 2064 (*freq)[c+(52-'0')] += delta 2065 case c == '_': 2066 (*freq)[62] += delta 2067 case c == '$': 2068 (*freq)[63] += delta 2069 } 2070 } 2071} 2072 2073func (freq *CharFreq) Include(other *CharFreq) { 2074 for i := 0; i < 64; i++ { 2075 (*freq)[i] += (*other)[i] 2076 } 2077} 2078 2079type NameMinifier struct { 2080 head string 2081 tail string 2082} 2083 2084var DefaultNameMinifier = NameMinifier{ 2085 head: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$", 2086 tail: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$", 2087} 2088 2089type charAndCount struct { 2090 index byte 2091 count int32 2092 char string 2093} 2094 2095// This type is just so we can use Go's native sort function 2096type charAndCountArray []charAndCount 2097 2098func (a charAndCountArray) Len() int { return len(a) } 2099func (a charAndCountArray) Swap(i int, j int) { a[i], a[j] = a[j], a[i] } 2100 2101func (a charAndCountArray) Less(i int, j int) bool { 2102 ai := a[i] 2103 aj := a[j] 2104 return ai.count > aj.count || (ai.count == aj.count && ai.index < aj.index) 2105} 2106 2107func (freq *CharFreq) Compile() NameMinifier { 2108 // Sort the histogram in descending order by count 2109 array := make(charAndCountArray, 64) 2110 for i := 0; i < len(DefaultNameMinifier.tail); i++ { 2111 array[i] = charAndCount{ 2112 char: DefaultNameMinifier.tail[i : i+1], 2113 index: byte(i), 2114 count: freq[i], 2115 } 2116 } 2117 sort.Sort(array) 2118 2119 // Compute the identifier start and identifier continue sequences 2120 minifier := NameMinifier{} 2121 for _, item := range array { 2122 if item.char < "0" || item.char > "9" { 2123 minifier.head += item.char 2124 } 2125 minifier.tail += item.char 2126 } 2127 return minifier 2128} 2129 2130func (minifier *NameMinifier) NumberToMinifiedName(i int) string { 2131 j := i % 54 2132 name := minifier.head[j : j+1] 2133 i = i / 54 2134 2135 for i > 0 { 2136 i-- 2137 j := i % 64 2138 name += minifier.tail[j : j+1] 2139 i = i / 64 2140 } 2141 2142 return name 2143} 2144 2145type NamedImport struct { 2146 // Parts within this file that use this import 2147 LocalPartsWithUses []uint32 2148 2149 Alias string 2150 AliasLoc logger.Loc 2151 NamespaceRef Ref 2152 ImportRecordIndex uint32 2153 2154 // If true, the alias refers to the entire export namespace object of a 2155 // module. This is no longer represented as an alias called "*" because of 2156 // the upcoming "Arbitrary module namespace identifier names" feature: 2157 // https://github.com/tc39/ecma262/pull/2154 2158 AliasIsStar bool 2159 2160 // It's useful to flag exported imports because if they are in a TypeScript 2161 // file, we can't tell if they are a type or a value. 2162 IsExported bool 2163} 2164 2165type NamedExport struct { 2166 Ref Ref 2167 AliasLoc logger.Loc 2168} 2169 2170// Each file is made up of multiple parts, and each part consists of one or 2171// more top-level statements. Parts are used for tree shaking and code 2172// splitting analysis. Individual parts of a file can be discarded by tree 2173// shaking and can be assigned to separate chunks (i.e. output files) by code 2174// splitting. 2175type Part struct { 2176 Stmts []Stmt 2177 Scopes []*Scope 2178 2179 // Each is an index into the file-level import record list 2180 ImportRecordIndices []uint32 2181 2182 // All symbols that are declared in this part. Note that a given symbol may 2183 // have multiple declarations, and so may end up being declared in multiple 2184 // parts (e.g. multiple "var" declarations with the same name). Also note 2185 // that this list isn't deduplicated and may contain duplicates. 2186 DeclaredSymbols []DeclaredSymbol 2187 2188 // An estimate of the number of uses of all symbols used within this part. 2189 SymbolUses map[Ref]SymbolUse 2190 2191 // The indices of the other parts in this file that are needed if this part 2192 // is needed. 2193 Dependencies []Dependency 2194 2195 // If true, this part can be removed if none of the declared symbols are 2196 // used. If the file containing this part is imported, then all parts that 2197 // don't have this flag enabled must be included. 2198 CanBeRemovedIfUnused bool 2199 2200 // This is used for generated parts that we don't want to be present if they 2201 // aren't needed. This enables tree shaking for these parts even if global 2202 // tree shaking isn't enabled. 2203 ForceTreeShaking bool 2204 2205 // This is true if this file has been marked as live by the tree shaking 2206 // algorithm. 2207 IsLive bool 2208} 2209 2210type Dependency struct { 2211 SourceIndex uint32 2212 PartIndex uint32 2213} 2214 2215type DeclaredSymbol struct { 2216 Ref Ref 2217 IsTopLevel bool 2218} 2219 2220type SymbolUse struct { 2221 CountEstimate uint32 2222} 2223 2224// Returns the canonical ref that represents the ref for the provided symbol. 2225// This may not be the provided ref if the symbol has been merged with another 2226// symbol. 2227func FollowSymbols(symbols SymbolMap, ref Ref) Ref { 2228 symbol := symbols.Get(ref) 2229 if symbol.Link == InvalidRef { 2230 return ref 2231 } 2232 2233 link := FollowSymbols(symbols, symbol.Link) 2234 2235 // Only write if needed to avoid concurrent map update hazards 2236 if symbol.Link != link { 2237 symbol.Link = link 2238 } 2239 2240 return link 2241} 2242 2243// Use this before calling "FollowSymbols" from separate threads to avoid 2244// concurrent map update hazards. In Go, mutating a map is not threadsafe 2245// but reading from a map is. Calling "FollowAllSymbols" first ensures that 2246// all mutation is done up front. 2247func FollowAllSymbols(symbols SymbolMap) { 2248 for sourceIndex, inner := range symbols.SymbolsForSource { 2249 for symbolIndex := range inner { 2250 FollowSymbols(symbols, Ref{uint32(sourceIndex), uint32(symbolIndex)}) 2251 } 2252 } 2253} 2254 2255// Makes "old" point to "new" by joining the linked lists for the two symbols 2256// together. That way "FollowSymbols" on both "old" and "new" will result in 2257// the same ref. 2258func MergeSymbols(symbols SymbolMap, old Ref, new Ref) Ref { 2259 if old == new { 2260 return new 2261 } 2262 2263 oldSymbol := symbols.Get(old) 2264 if oldSymbol.Link != InvalidRef { 2265 oldSymbol.Link = MergeSymbols(symbols, oldSymbol.Link, new) 2266 return oldSymbol.Link 2267 } 2268 2269 newSymbol := symbols.Get(new) 2270 if newSymbol.Link != InvalidRef { 2271 newSymbol.Link = MergeSymbols(symbols, old, newSymbol.Link) 2272 return newSymbol.Link 2273 } 2274 2275 oldSymbol.Link = new 2276 newSymbol.MergeContentsWith(oldSymbol) 2277 return new 2278} 2279 2280// For readability, the names of certain automatically-generated symbols are 2281// derived from the file name. For example, instead of the CommonJS wrapper for 2282// a file being called something like "require273" it can be called something 2283// like "require_react" instead. This function generates the part of these 2284// identifiers that's specific to the file path. It can take both an absolute 2285// path (OS-specific) and a path in the source code (OS-independent). 2286// 2287// Note that these generated names do not at all relate to the correctness of 2288// the code as far as avoiding symbol name collisions. These names still go 2289// through the renaming logic that all other symbols go through to avoid name 2290// collisions. 2291func GenerateNonUniqueNameFromPath(path string) string { 2292 // Get the file name without the extension 2293 dir, base, _ := logger.PlatformIndependentPathDirBaseExt(path) 2294 2295 // If the name is "index", use the directory name instead. This is because 2296 // many packages in npm use the file name "index.js" because it triggers 2297 // node's implicit module resolution rules that allows you to import it by 2298 // just naming the directory. 2299 if base == "index" { 2300 _, dirBase, _ := logger.PlatformIndependentPathDirBaseExt(dir) 2301 if dirBase != "" { 2302 base = dirBase 2303 } 2304 } 2305 2306 return EnsureValidIdentifier(base) 2307} 2308 2309func EnsureValidIdentifier(base string) string { 2310 // Convert it to an ASCII identifier. Note: If you change this to a non-ASCII 2311 // identifier, you're going to potentially cause trouble with non-BMP code 2312 // points in target environments that don't support bracketed Unicode escapes. 2313 bytes := []byte{} 2314 needsGap := false 2315 for _, c := range base { 2316 if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (len(bytes) > 0 && c >= '0' && c <= '9') { 2317 if needsGap { 2318 bytes = append(bytes, '_') 2319 needsGap = false 2320 } 2321 bytes = append(bytes, byte(c)) 2322 } else if len(bytes) > 0 { 2323 needsGap = true 2324 } 2325 } 2326 2327 // Make sure the name isn't empty 2328 if len(bytes) == 0 { 2329 return "_" 2330 } 2331 return string(bytes) 2332} 2333 2334func ConvertBindingToExpr(binding Binding, wrapIdentifier func(logger.Loc, Ref) Expr) Expr { 2335 loc := binding.Loc 2336 2337 switch b := binding.Data.(type) { 2338 case *BMissing: 2339 return Expr{Loc: loc, Data: &EMissing{}} 2340 2341 case *BIdentifier: 2342 if wrapIdentifier != nil { 2343 return wrapIdentifier(loc, b.Ref) 2344 } 2345 return Expr{Loc: loc, Data: &EIdentifier{Ref: b.Ref}} 2346 2347 case *BArray: 2348 exprs := make([]Expr, len(b.Items)) 2349 for i, item := range b.Items { 2350 expr := ConvertBindingToExpr(item.Binding, wrapIdentifier) 2351 if b.HasSpread && i+1 == len(b.Items) { 2352 expr = Expr{Loc: expr.Loc, Data: &ESpread{Value: expr}} 2353 } else if item.DefaultValueOrNil.Data != nil { 2354 expr = Assign(expr, item.DefaultValueOrNil) 2355 } 2356 exprs[i] = expr 2357 } 2358 return Expr{Loc: loc, Data: &EArray{ 2359 Items: exprs, 2360 IsSingleLine: b.IsSingleLine, 2361 }} 2362 2363 case *BObject: 2364 properties := make([]Property, len(b.Properties)) 2365 for i, property := range b.Properties { 2366 value := ConvertBindingToExpr(property.Value, wrapIdentifier) 2367 kind := PropertyNormal 2368 if property.IsSpread { 2369 kind = PropertySpread 2370 } 2371 properties[i] = Property{ 2372 Kind: kind, 2373 IsComputed: property.IsComputed, 2374 Key: property.Key, 2375 ValueOrNil: value, 2376 InitializerOrNil: property.DefaultValueOrNil, 2377 } 2378 } 2379 return Expr{Loc: loc, Data: &EObject{ 2380 Properties: properties, 2381 IsSingleLine: b.IsSingleLine, 2382 }} 2383 2384 default: 2385 panic("Internal error") 2386 } 2387} 2388