github.com/evanw/esbuild@v0.21.4/internal/js_parser/ts_parser.go (about)

     1  // This file contains code for parsing TypeScript syntax. The parser just skips
     2  // over type expressions as if they are whitespace and doesn't bother generating
     3  // an AST because nothing uses type information.
     4  
     5  package js_parser
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/evanw/esbuild/internal/ast"
    12  	"github.com/evanw/esbuild/internal/compat"
    13  	"github.com/evanw/esbuild/internal/helpers"
    14  	"github.com/evanw/esbuild/internal/js_ast"
    15  	"github.com/evanw/esbuild/internal/js_lexer"
    16  	"github.com/evanw/esbuild/internal/logger"
    17  )
    18  
    19  func (p *parser) skipTypeScriptBinding() {
    20  	switch p.lexer.Token {
    21  	case js_lexer.TIdentifier, js_lexer.TThis:
    22  		p.lexer.Next()
    23  
    24  	case js_lexer.TOpenBracket:
    25  		p.lexer.Next()
    26  
    27  		// "[, , a]"
    28  		for p.lexer.Token == js_lexer.TComma {
    29  			p.lexer.Next()
    30  		}
    31  
    32  		// "[a, b]"
    33  		for p.lexer.Token != js_lexer.TCloseBracket {
    34  			// "[...a]"
    35  			if p.lexer.Token == js_lexer.TDotDotDot {
    36  				p.lexer.Next()
    37  			}
    38  
    39  			p.skipTypeScriptBinding()
    40  			if p.lexer.Token != js_lexer.TComma {
    41  				break
    42  			}
    43  			p.lexer.Next()
    44  		}
    45  
    46  		p.lexer.Expect(js_lexer.TCloseBracket)
    47  
    48  	case js_lexer.TOpenBrace:
    49  		p.lexer.Next()
    50  
    51  		for p.lexer.Token != js_lexer.TCloseBrace {
    52  			foundIdentifier := false
    53  
    54  			switch p.lexer.Token {
    55  			case js_lexer.TDotDotDot:
    56  				p.lexer.Next()
    57  
    58  				if p.lexer.Token != js_lexer.TIdentifier {
    59  					p.lexer.Unexpected()
    60  				}
    61  
    62  				// "{...x}"
    63  				foundIdentifier = true
    64  				p.lexer.Next()
    65  
    66  			case js_lexer.TIdentifier:
    67  				// "{x}"
    68  				// "{x: y}"
    69  				foundIdentifier = true
    70  				p.lexer.Next()
    71  
    72  				// "{1: y}"
    73  				// "{'x': y}"
    74  			case js_lexer.TStringLiteral, js_lexer.TNumericLiteral:
    75  				p.lexer.Next()
    76  
    77  			default:
    78  				if p.lexer.IsIdentifierOrKeyword() {
    79  					// "{if: x}"
    80  					p.lexer.Next()
    81  				} else {
    82  					p.lexer.Unexpected()
    83  				}
    84  			}
    85  
    86  			if p.lexer.Token == js_lexer.TColon || !foundIdentifier {
    87  				p.lexer.Expect(js_lexer.TColon)
    88  				p.skipTypeScriptBinding()
    89  			}
    90  
    91  			if p.lexer.Token != js_lexer.TComma {
    92  				break
    93  			}
    94  			p.lexer.Next()
    95  		}
    96  
    97  		p.lexer.Expect(js_lexer.TCloseBrace)
    98  
    99  	default:
   100  		p.lexer.Unexpected()
   101  	}
   102  }
   103  
   104  func (p *parser) skipTypeScriptFnArgs() {
   105  	p.lexer.Expect(js_lexer.TOpenParen)
   106  
   107  	for p.lexer.Token != js_lexer.TCloseParen {
   108  		// "(...a)"
   109  		if p.lexer.Token == js_lexer.TDotDotDot {
   110  			p.lexer.Next()
   111  		}
   112  
   113  		p.skipTypeScriptBinding()
   114  
   115  		// "(a?)"
   116  		if p.lexer.Token == js_lexer.TQuestion {
   117  			p.lexer.Next()
   118  		}
   119  
   120  		// "(a: any)"
   121  		if p.lexer.Token == js_lexer.TColon {
   122  			p.lexer.Next()
   123  			p.skipTypeScriptType(js_ast.LLowest)
   124  		}
   125  
   126  		// "(a, b)"
   127  		if p.lexer.Token != js_lexer.TComma {
   128  			break
   129  		}
   130  		p.lexer.Next()
   131  	}
   132  
   133  	p.lexer.Expect(js_lexer.TCloseParen)
   134  }
   135  
   136  // This is a spot where the TypeScript grammar is highly ambiguous. Here are
   137  // some cases that are valid:
   138  //
   139  //	let x = (y: any): (() => {}) => { };
   140  //	let x = (y: any): () => {} => { };
   141  //	let x = (y: any): (y) => {} => { };
   142  //	let x = (y: any): (y[]) => {};
   143  //	let x = (y: any): (a | b) => {};
   144  //
   145  // Here are some cases that aren't valid:
   146  //
   147  //	let x = (y: any): (y) => {};
   148  //	let x = (y: any): (y) => {return 0};
   149  //	let x = (y: any): asserts y is (y) => {};
   150  func (p *parser) skipTypeScriptParenOrFnType() {
   151  	if p.trySkipTypeScriptArrowArgsWithBacktracking() {
   152  		p.skipTypeScriptReturnType()
   153  	} else {
   154  		p.lexer.Expect(js_lexer.TOpenParen)
   155  		p.skipTypeScriptType(js_ast.LLowest)
   156  		p.lexer.Expect(js_lexer.TCloseParen)
   157  	}
   158  }
   159  
   160  func (p *parser) skipTypeScriptReturnType() {
   161  	p.skipTypeScriptTypeWithFlags(js_ast.LLowest, isReturnTypeFlag)
   162  }
   163  
   164  func (p *parser) skipTypeScriptType(level js_ast.L) {
   165  	p.skipTypeScriptTypeWithFlags(level, 0)
   166  }
   167  
   168  type skipTypeFlags uint8
   169  
   170  const (
   171  	isReturnTypeFlag skipTypeFlags = 1 << iota
   172  	isIndexSignatureFlag
   173  	allowTupleLabelsFlag
   174  	disallowConditionalTypesFlag
   175  )
   176  
   177  func (flags skipTypeFlags) has(flag skipTypeFlags) bool {
   178  	return (flags & flag) != 0
   179  }
   180  
   181  type tsTypeIdentifierKind uint8
   182  
   183  const (
   184  	tsTypeIdentifierNormal tsTypeIdentifierKind = iota
   185  	tsTypeIdentifierUnique
   186  	tsTypeIdentifierAbstract
   187  	tsTypeIdentifierAsserts
   188  	tsTypeIdentifierPrefix
   189  	tsTypeIdentifierPrimitive
   190  	tsTypeIdentifierInfer
   191  )
   192  
   193  // Use a map to improve lookup speed
   194  var tsTypeIdentifierMap = map[string]tsTypeIdentifierKind{
   195  	"unique":   tsTypeIdentifierUnique,
   196  	"abstract": tsTypeIdentifierAbstract,
   197  	"asserts":  tsTypeIdentifierAsserts,
   198  
   199  	"keyof":    tsTypeIdentifierPrefix,
   200  	"readonly": tsTypeIdentifierPrefix,
   201  
   202  	"any":       tsTypeIdentifierPrimitive,
   203  	"never":     tsTypeIdentifierPrimitive,
   204  	"unknown":   tsTypeIdentifierPrimitive,
   205  	"undefined": tsTypeIdentifierPrimitive,
   206  	"object":    tsTypeIdentifierPrimitive,
   207  	"number":    tsTypeIdentifierPrimitive,
   208  	"string":    tsTypeIdentifierPrimitive,
   209  	"boolean":   tsTypeIdentifierPrimitive,
   210  	"bigint":    tsTypeIdentifierPrimitive,
   211  	"symbol":    tsTypeIdentifierPrimitive,
   212  
   213  	"infer": tsTypeIdentifierInfer,
   214  }
   215  
   216  func (p *parser) skipTypeScriptTypeWithFlags(level js_ast.L, flags skipTypeFlags) {
   217  loop:
   218  	for {
   219  		switch p.lexer.Token {
   220  		case js_lexer.TNumericLiteral, js_lexer.TBigIntegerLiteral, js_lexer.TStringLiteral,
   221  			js_lexer.TNoSubstitutionTemplateLiteral, js_lexer.TTrue, js_lexer.TFalse,
   222  			js_lexer.TNull, js_lexer.TVoid:
   223  			p.lexer.Next()
   224  
   225  		case js_lexer.TConst:
   226  			r := p.lexer.Range()
   227  			p.lexer.Next()
   228  
   229  			// "[const: number]"
   230  			if flags.has(allowTupleLabelsFlag) && p.lexer.Token == js_lexer.TColon {
   231  				p.log.AddError(&p.tracker, r, "Unexpected \"const\"")
   232  			}
   233  
   234  		case js_lexer.TThis:
   235  			p.lexer.Next()
   236  
   237  			// "function check(): this is boolean"
   238  			if p.lexer.IsContextualKeyword("is") && !p.lexer.HasNewlineBefore {
   239  				p.lexer.Next()
   240  				p.skipTypeScriptType(js_ast.LLowest)
   241  				return
   242  			}
   243  
   244  		case js_lexer.TMinus:
   245  			// "-123"
   246  			// "-123n"
   247  			p.lexer.Next()
   248  			if p.lexer.Token == js_lexer.TBigIntegerLiteral {
   249  				p.lexer.Next()
   250  			} else {
   251  				p.lexer.Expect(js_lexer.TNumericLiteral)
   252  			}
   253  
   254  		case js_lexer.TAmpersand:
   255  		case js_lexer.TBar:
   256  			// Support things like "type Foo = | A | B" and "type Foo = & A & B"
   257  			p.lexer.Next()
   258  			continue
   259  
   260  		case js_lexer.TImport:
   261  			// "import('fs')"
   262  			p.lexer.Next()
   263  
   264  			// "[import: number]"
   265  			if flags.has(allowTupleLabelsFlag) && p.lexer.Token == js_lexer.TColon {
   266  				return
   267  			}
   268  
   269  			p.lexer.Expect(js_lexer.TOpenParen)
   270  			p.lexer.Expect(js_lexer.TStringLiteral)
   271  
   272  			// "import('./foo.json', { assert: { type: 'json' } })"
   273  			if p.lexer.Token == js_lexer.TComma {
   274  				p.lexer.Next()
   275  				p.skipTypeScriptObjectType()
   276  
   277  				// "import('./foo.json', { assert: { type: 'json' } }, )"
   278  				if p.lexer.Token == js_lexer.TComma {
   279  					p.lexer.Next()
   280  				}
   281  			}
   282  
   283  			p.lexer.Expect(js_lexer.TCloseParen)
   284  
   285  		case js_lexer.TNew:
   286  			// "new () => Foo"
   287  			// "new <T>() => Foo<T>"
   288  			p.lexer.Next()
   289  
   290  			// "[new: number]"
   291  			if flags.has(allowTupleLabelsFlag) && p.lexer.Token == js_lexer.TColon {
   292  				return
   293  			}
   294  
   295  			p.skipTypeScriptTypeParameters(allowConstModifier)
   296  			p.skipTypeScriptParenOrFnType()
   297  
   298  		case js_lexer.TLessThan:
   299  			// "<T>() => Foo<T>"
   300  			p.skipTypeScriptTypeParameters(allowConstModifier)
   301  			p.skipTypeScriptParenOrFnType()
   302  
   303  		case js_lexer.TOpenParen:
   304  			// "(number | string)"
   305  			p.skipTypeScriptParenOrFnType()
   306  
   307  		case js_lexer.TIdentifier:
   308  			kind := tsTypeIdentifierMap[p.lexer.Identifier.String]
   309  			checkTypeParameters := true
   310  
   311  			switch kind {
   312  			case tsTypeIdentifierPrefix:
   313  				p.lexer.Next()
   314  
   315  				// Valid:
   316  				//   "[keyof: string]"
   317  				//   "{[keyof: string]: number}"
   318  				//   "{[keyof in string]: number}"
   319  				//
   320  				// Invalid:
   321  				//   "A extends B ? keyof : string"
   322  				//
   323  				if (p.lexer.Token != js_lexer.TColon && p.lexer.Token != js_lexer.TIn) || (!flags.has(isIndexSignatureFlag) && !flags.has(allowTupleLabelsFlag)) {
   324  					p.skipTypeScriptType(js_ast.LPrefix)
   325  				}
   326  				break loop
   327  
   328  			case tsTypeIdentifierInfer:
   329  				p.lexer.Next()
   330  
   331  				// "type Foo = Bar extends [infer T] ? T : null"
   332  				// "type Foo = Bar extends [infer T extends string] ? T : null"
   333  				// "type Foo = Bar extends [infer T extends string ? infer T : never] ? T : null"
   334  				// "type Foo = { [infer in Bar]: number }"
   335  				if (p.lexer.Token != js_lexer.TColon && p.lexer.Token != js_lexer.TIn) || (!flags.has(isIndexSignatureFlag) && !flags.has(allowTupleLabelsFlag)) {
   336  					p.lexer.Expect(js_lexer.TIdentifier)
   337  					if p.lexer.Token == js_lexer.TExtends {
   338  						p.trySkipTypeScriptConstraintOfInferTypeWithBacktracking(flags)
   339  					}
   340  				}
   341  				break loop
   342  
   343  			case tsTypeIdentifierUnique:
   344  				p.lexer.Next()
   345  
   346  				// "let foo: unique symbol"
   347  				if p.lexer.IsContextualKeyword("symbol") {
   348  					p.lexer.Next()
   349  					break loop
   350  				}
   351  
   352  			case tsTypeIdentifierAbstract:
   353  				p.lexer.Next()
   354  
   355  				// "let foo: abstract new () => {}" added in TypeScript 4.2
   356  				if p.lexer.Token == js_lexer.TNew {
   357  					continue
   358  				}
   359  
   360  			case tsTypeIdentifierAsserts:
   361  				p.lexer.Next()
   362  
   363  				// "function assert(x: boolean): asserts x"
   364  				// "function assert(x: boolean): asserts x is boolean"
   365  				if flags.has(isReturnTypeFlag) && !p.lexer.HasNewlineBefore && (p.lexer.Token == js_lexer.TIdentifier || p.lexer.Token == js_lexer.TThis) {
   366  					p.lexer.Next()
   367  				}
   368  
   369  			case tsTypeIdentifierPrimitive:
   370  				p.lexer.Next()
   371  				checkTypeParameters = false
   372  
   373  			default:
   374  				p.lexer.Next()
   375  			}
   376  
   377  			// "function assert(x: any): x is boolean"
   378  			if p.lexer.IsContextualKeyword("is") && !p.lexer.HasNewlineBefore {
   379  				p.lexer.Next()
   380  				p.skipTypeScriptType(js_ast.LLowest)
   381  				return
   382  			}
   383  
   384  			// "let foo: any \n <number>foo" must not become a single type
   385  			if checkTypeParameters && !p.lexer.HasNewlineBefore {
   386  				p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{})
   387  			}
   388  
   389  		case js_lexer.TTypeof:
   390  			p.lexer.Next()
   391  
   392  			// "[typeof: number]"
   393  			if flags.has(allowTupleLabelsFlag) && p.lexer.Token == js_lexer.TColon {
   394  				return
   395  			}
   396  
   397  			if p.lexer.Token == js_lexer.TImport {
   398  				// "typeof import('fs')"
   399  				continue
   400  			} else {
   401  				// "typeof x"
   402  				if !p.lexer.IsIdentifierOrKeyword() {
   403  					p.lexer.Expected(js_lexer.TIdentifier)
   404  				}
   405  				p.lexer.Next()
   406  
   407  				// "typeof x.y"
   408  				// "typeof x.#y"
   409  				for p.lexer.Token == js_lexer.TDot {
   410  					p.lexer.Next()
   411  					if !p.lexer.IsIdentifierOrKeyword() && p.lexer.Token != js_lexer.TPrivateIdentifier {
   412  						p.lexer.Expected(js_lexer.TIdentifier)
   413  					}
   414  					p.lexer.Next()
   415  				}
   416  
   417  				if !p.lexer.HasNewlineBefore {
   418  					p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{})
   419  				}
   420  			}
   421  
   422  		case js_lexer.TOpenBracket:
   423  			// "[number, string]"
   424  			// "[first: number, second: string]"
   425  			p.lexer.Next()
   426  			for p.lexer.Token != js_lexer.TCloseBracket {
   427  				if p.lexer.Token == js_lexer.TDotDotDot {
   428  					p.lexer.Next()
   429  				}
   430  				p.skipTypeScriptTypeWithFlags(js_ast.LLowest, allowTupleLabelsFlag)
   431  				if p.lexer.Token == js_lexer.TQuestion {
   432  					p.lexer.Next()
   433  				}
   434  				if p.lexer.Token == js_lexer.TColon {
   435  					p.lexer.Next()
   436  					p.skipTypeScriptType(js_ast.LLowest)
   437  				}
   438  				if p.lexer.Token != js_lexer.TComma {
   439  					break
   440  				}
   441  				p.lexer.Next()
   442  			}
   443  			p.lexer.Expect(js_lexer.TCloseBracket)
   444  
   445  		case js_lexer.TOpenBrace:
   446  			p.skipTypeScriptObjectType()
   447  
   448  		case js_lexer.TTemplateHead:
   449  			// "`${'a' | 'b'}-${'c' | 'd'}`"
   450  			for {
   451  				p.lexer.Next()
   452  				p.skipTypeScriptType(js_ast.LLowest)
   453  				p.lexer.RescanCloseBraceAsTemplateToken()
   454  				if p.lexer.Token == js_lexer.TTemplateTail {
   455  					p.lexer.Next()
   456  					break
   457  				}
   458  			}
   459  
   460  		default:
   461  			// "[function: number]"
   462  			if flags.has(allowTupleLabelsFlag) && p.lexer.IsIdentifierOrKeyword() {
   463  				if p.lexer.Token != js_lexer.TFunction {
   464  					p.log.AddError(&p.tracker, p.lexer.Range(), fmt.Sprintf("Unexpected %q", p.lexer.Raw()))
   465  				}
   466  				p.lexer.Next()
   467  				if p.lexer.Token != js_lexer.TColon {
   468  					p.lexer.Expect(js_lexer.TColon)
   469  				}
   470  				return
   471  			}
   472  
   473  			p.lexer.Unexpected()
   474  		}
   475  		break
   476  	}
   477  
   478  	for {
   479  		switch p.lexer.Token {
   480  		case js_lexer.TBar:
   481  			if level >= js_ast.LBitwiseOr {
   482  				return
   483  			}
   484  			p.lexer.Next()
   485  			p.skipTypeScriptTypeWithFlags(js_ast.LBitwiseOr, flags)
   486  
   487  		case js_lexer.TAmpersand:
   488  			if level >= js_ast.LBitwiseAnd {
   489  				return
   490  			}
   491  			p.lexer.Next()
   492  			p.skipTypeScriptTypeWithFlags(js_ast.LBitwiseAnd, flags)
   493  
   494  		case js_lexer.TExclamation:
   495  			// A postfix "!" is allowed in JSDoc types in TypeScript, which are only
   496  			// present in comments. While it's not valid in a non-comment position,
   497  			// it's still parsed and turned into a soft error by the TypeScript
   498  			// compiler. It turns out parsing this is important for correctness for
   499  			// "as" casts because the "!" token must still be consumed.
   500  			if p.lexer.HasNewlineBefore {
   501  				return
   502  			}
   503  			p.lexer.Next()
   504  
   505  		case js_lexer.TDot:
   506  			p.lexer.Next()
   507  			if !p.lexer.IsIdentifierOrKeyword() {
   508  				p.lexer.Expect(js_lexer.TIdentifier)
   509  			}
   510  			p.lexer.Next()
   511  
   512  			// "{ <A extends B>(): c.d \n <E extends F>(): g.h }" must not become a single type
   513  			if !p.lexer.HasNewlineBefore {
   514  				p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{})
   515  			}
   516  
   517  		case js_lexer.TOpenBracket:
   518  			// "{ ['x']: string \n ['y']: string }" must not become a single type
   519  			if p.lexer.HasNewlineBefore {
   520  				return
   521  			}
   522  			p.lexer.Next()
   523  			if p.lexer.Token != js_lexer.TCloseBracket {
   524  				p.skipTypeScriptType(js_ast.LLowest)
   525  			}
   526  			p.lexer.Expect(js_lexer.TCloseBracket)
   527  
   528  		case js_lexer.TExtends:
   529  			// "{ x: number \n extends: boolean }" must not become a single type
   530  			if p.lexer.HasNewlineBefore || flags.has(disallowConditionalTypesFlag) {
   531  				return
   532  			}
   533  			p.lexer.Next()
   534  
   535  			// The type following "extends" is not permitted to be another conditional type
   536  			p.skipTypeScriptTypeWithFlags(js_ast.LLowest, disallowConditionalTypesFlag)
   537  			p.lexer.Expect(js_lexer.TQuestion)
   538  			p.skipTypeScriptType(js_ast.LLowest)
   539  			p.lexer.Expect(js_lexer.TColon)
   540  			p.skipTypeScriptType(js_ast.LLowest)
   541  
   542  		default:
   543  			return
   544  		}
   545  	}
   546  }
   547  
   548  func (p *parser) skipTypeScriptObjectType() {
   549  	p.lexer.Expect(js_lexer.TOpenBrace)
   550  
   551  	for p.lexer.Token != js_lexer.TCloseBrace {
   552  		// "{ -readonly [K in keyof T]: T[K] }"
   553  		// "{ +readonly [K in keyof T]: T[K] }"
   554  		if p.lexer.Token == js_lexer.TPlus || p.lexer.Token == js_lexer.TMinus {
   555  			p.lexer.Next()
   556  		}
   557  
   558  		// Skip over modifiers and the property identifier
   559  		foundKey := false
   560  		for p.lexer.IsIdentifierOrKeyword() ||
   561  			p.lexer.Token == js_lexer.TStringLiteral ||
   562  			p.lexer.Token == js_lexer.TNumericLiteral {
   563  			p.lexer.Next()
   564  			foundKey = true
   565  		}
   566  
   567  		if p.lexer.Token == js_lexer.TOpenBracket {
   568  			// Index signature or computed property
   569  			p.lexer.Next()
   570  			p.skipTypeScriptTypeWithFlags(js_ast.LLowest, isIndexSignatureFlag)
   571  
   572  			// "{ [key: string]: number }"
   573  			// "{ readonly [K in keyof T]: T[K] }"
   574  			if p.lexer.Token == js_lexer.TColon {
   575  				p.lexer.Next()
   576  				p.skipTypeScriptType(js_ast.LLowest)
   577  			} else if p.lexer.Token == js_lexer.TIn {
   578  				p.lexer.Next()
   579  				p.skipTypeScriptType(js_ast.LLowest)
   580  				if p.lexer.IsContextualKeyword("as") {
   581  					// "{ [K in keyof T as `get-${K}`]: T[K] }"
   582  					p.lexer.Next()
   583  					p.skipTypeScriptType(js_ast.LLowest)
   584  				}
   585  			}
   586  
   587  			p.lexer.Expect(js_lexer.TCloseBracket)
   588  
   589  			// "{ [K in keyof T]+?: T[K] }"
   590  			// "{ [K in keyof T]-?: T[K] }"
   591  			if p.lexer.Token == js_lexer.TPlus || p.lexer.Token == js_lexer.TMinus {
   592  				p.lexer.Next()
   593  			}
   594  
   595  			foundKey = true
   596  		}
   597  
   598  		// "?" indicates an optional property
   599  		// "!" indicates an initialization assertion
   600  		if foundKey && (p.lexer.Token == js_lexer.TQuestion || p.lexer.Token == js_lexer.TExclamation) {
   601  			p.lexer.Next()
   602  		}
   603  
   604  		// Type parameters come right after the optional mark
   605  		p.skipTypeScriptTypeParameters(allowConstModifier)
   606  
   607  		switch p.lexer.Token {
   608  		case js_lexer.TColon:
   609  			// Regular property
   610  			if !foundKey {
   611  				p.lexer.Expect(js_lexer.TIdentifier)
   612  			}
   613  			p.lexer.Next()
   614  			p.skipTypeScriptType(js_ast.LLowest)
   615  
   616  		case js_lexer.TOpenParen:
   617  			// Method signature
   618  			p.skipTypeScriptFnArgs()
   619  			if p.lexer.Token == js_lexer.TColon {
   620  				p.lexer.Next()
   621  				p.skipTypeScriptReturnType()
   622  			}
   623  
   624  		default:
   625  			if !foundKey {
   626  				p.lexer.Unexpected()
   627  			}
   628  		}
   629  
   630  		switch p.lexer.Token {
   631  		case js_lexer.TCloseBrace:
   632  
   633  		case js_lexer.TComma, js_lexer.TSemicolon:
   634  			p.lexer.Next()
   635  
   636  		default:
   637  			if !p.lexer.HasNewlineBefore {
   638  				p.lexer.Unexpected()
   639  			}
   640  		}
   641  	}
   642  
   643  	p.lexer.Expect(js_lexer.TCloseBrace)
   644  }
   645  
   646  type typeParameterFlags uint8
   647  
   648  const (
   649  	// TypeScript 4.7
   650  	allowInOutVarianceAnnotations typeParameterFlags = 1 << iota
   651  
   652  	// TypeScript 5.0
   653  	allowConstModifier
   654  
   655  	// Allow "<>" without any type parameters
   656  	allowEmptyTypeParameters
   657  )
   658  
   659  type skipTypeScriptTypeParametersResult uint8
   660  
   661  const (
   662  	didNotSkipAnything skipTypeScriptTypeParametersResult = iota
   663  	couldBeTypeCast
   664  	definitelyTypeParameters
   665  )
   666  
   667  // This is the type parameter declarations that go with other symbol
   668  // declarations (class, function, type, etc.)
   669  func (p *parser) skipTypeScriptTypeParameters(flags typeParameterFlags) skipTypeScriptTypeParametersResult {
   670  	if p.lexer.Token != js_lexer.TLessThan {
   671  		return didNotSkipAnything
   672  	}
   673  
   674  	p.lexer.Next()
   675  	result := couldBeTypeCast
   676  
   677  	if (flags&allowEmptyTypeParameters) != 0 && p.lexer.Token == js_lexer.TGreaterThan {
   678  		p.lexer.Next()
   679  		return definitelyTypeParameters
   680  	}
   681  
   682  	for {
   683  		hasIn := false
   684  		hasOut := false
   685  		expectIdentifier := true
   686  		invalidModifierRange := logger.Range{}
   687  
   688  		// Scan over a sequence of "in" and "out" modifiers (a.k.a. optional
   689  		// variance annotations) as well as "const" modifiers
   690  		for {
   691  			if p.lexer.Token == js_lexer.TConst {
   692  				if invalidModifierRange.Len == 0 && (flags&allowConstModifier) == 0 {
   693  					// Valid:
   694  					//   "class Foo<const T> {}"
   695  					// Invalid:
   696  					//   "interface Foo<const T> {}"
   697  					invalidModifierRange = p.lexer.Range()
   698  				}
   699  				result = definitelyTypeParameters
   700  				p.lexer.Next()
   701  				expectIdentifier = true
   702  				continue
   703  			}
   704  
   705  			if p.lexer.Token == js_lexer.TIn {
   706  				if invalidModifierRange.Len == 0 && ((flags&allowInOutVarianceAnnotations) == 0 || hasIn || hasOut) {
   707  					// Valid:
   708  					//   "type Foo<in T> = T"
   709  					// Invalid:
   710  					//   "type Foo<in in T> = T"
   711  					//   "type Foo<out in T> = T"
   712  					invalidModifierRange = p.lexer.Range()
   713  				}
   714  				p.lexer.Next()
   715  				hasIn = true
   716  				expectIdentifier = true
   717  				continue
   718  			}
   719  
   720  			if p.lexer.IsContextualKeyword("out") {
   721  				r := p.lexer.Range()
   722  				if invalidModifierRange.Len == 0 && (flags&allowInOutVarianceAnnotations) == 0 {
   723  					invalidModifierRange = r
   724  				}
   725  				p.lexer.Next()
   726  				if invalidModifierRange.Len == 0 && hasOut && (p.lexer.Token == js_lexer.TIn || p.lexer.Token == js_lexer.TIdentifier) {
   727  					// Valid:
   728  					//   "type Foo<out T> = T"
   729  					//   "type Foo<out out> = T"
   730  					//   "type Foo<out out, T> = T"
   731  					//   "type Foo<out out = T> = T"
   732  					//   "type Foo<out out extends T> = T"
   733  					// Invalid:
   734  					//   "type Foo<out out in T> = T"
   735  					//   "type Foo<out out T> = T"
   736  					invalidModifierRange = r
   737  				}
   738  				hasOut = true
   739  				expectIdentifier = false
   740  				continue
   741  			}
   742  
   743  			break
   744  		}
   745  
   746  		// Only report an error for the first invalid modifier
   747  		if invalidModifierRange.Len > 0 {
   748  			p.log.AddError(&p.tracker, invalidModifierRange, fmt.Sprintf(
   749  				"The modifier %q is not valid here:", p.source.TextForRange(invalidModifierRange)))
   750  		}
   751  
   752  		// expectIdentifier => Mandatory identifier (e.g. after "type Foo <in ___")
   753  		// !expectIdentifier => Optional identifier (e.g. after "type Foo <out ___" since "out" may be the identifier)
   754  		if expectIdentifier || p.lexer.Token == js_lexer.TIdentifier {
   755  			p.lexer.Expect(js_lexer.TIdentifier)
   756  		}
   757  
   758  		// "class Foo<T extends number> {}"
   759  		if p.lexer.Token == js_lexer.TExtends {
   760  			result = definitelyTypeParameters
   761  			p.lexer.Next()
   762  			p.skipTypeScriptType(js_ast.LLowest)
   763  		}
   764  
   765  		// "class Foo<T = void> {}"
   766  		if p.lexer.Token == js_lexer.TEquals {
   767  			result = definitelyTypeParameters
   768  			p.lexer.Next()
   769  			p.skipTypeScriptType(js_ast.LLowest)
   770  		}
   771  
   772  		if p.lexer.Token != js_lexer.TComma {
   773  			break
   774  		}
   775  		p.lexer.Next()
   776  		if p.lexer.Token == js_lexer.TGreaterThan {
   777  			result = definitelyTypeParameters
   778  			break
   779  		}
   780  	}
   781  
   782  	p.lexer.ExpectGreaterThan(false /* isInsideJSXElement */)
   783  	return result
   784  }
   785  
   786  type skipTypeScriptTypeArgumentsOpts struct {
   787  	isInsideJSXElement               bool
   788  	isParseTypeArgumentsInExpression bool
   789  }
   790  
   791  func (p *parser) skipTypeScriptTypeArguments(opts skipTypeScriptTypeArgumentsOpts) bool {
   792  	switch p.lexer.Token {
   793  	case js_lexer.TLessThan, js_lexer.TLessThanEquals,
   794  		js_lexer.TLessThanLessThan, js_lexer.TLessThanLessThanEquals:
   795  	default:
   796  		return false
   797  	}
   798  
   799  	p.lexer.ExpectLessThan(false /* isInsideJSXElement */)
   800  
   801  	for {
   802  		p.skipTypeScriptType(js_ast.LLowest)
   803  		if p.lexer.Token != js_lexer.TComma {
   804  			break
   805  		}
   806  		p.lexer.Next()
   807  	}
   808  
   809  	// This type argument list must end with a ">"
   810  	if !opts.isParseTypeArgumentsInExpression {
   811  		// Normally TypeScript allows any token starting with ">". For example,
   812  		// "Array<Array<number>>()" is a type argument list even though there's a
   813  		// ">>" token, because ">>" starts with ">".
   814  		p.lexer.ExpectGreaterThan(opts.isInsideJSXElement)
   815  	} else {
   816  		// However, if we're emulating the TypeScript compiler's function called
   817  		// "parseTypeArgumentsInExpression" function, then we must only allow the
   818  		// ">" token itself. For example, "x < y >= z" is not a type argument list.
   819  		//
   820  		// This doesn't detect ">>" in "Array<Array<number>>()" because the inner
   821  		// type argument list isn't a call to "parseTypeArgumentsInExpression"
   822  		// because it's within a type context, not an expression context. So the
   823  		// token that we see here is ">" in that case because the first ">" has
   824  		// already been stripped off of the ">>" by the inner call.
   825  		if opts.isInsideJSXElement {
   826  			p.lexer.ExpectInsideJSXElement(js_lexer.TGreaterThan)
   827  		} else {
   828  			p.lexer.Expect(js_lexer.TGreaterThan)
   829  		}
   830  	}
   831  	return true
   832  }
   833  
   834  func (p *parser) trySkipTypeArgumentsInExpressionWithBacktracking() bool {
   835  	oldLexer := p.lexer
   836  	p.lexer.IsLogDisabled = true
   837  
   838  	// Implement backtracking by restoring the lexer's memory to its original state
   839  	defer func() {
   840  		r := recover()
   841  		if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic {
   842  			p.lexer = oldLexer
   843  		} else if r != nil {
   844  			panic(r)
   845  		}
   846  	}()
   847  
   848  	if p.skipTypeScriptTypeArguments(skipTypeScriptTypeArgumentsOpts{isParseTypeArgumentsInExpression: true}) {
   849  		// Check the token after the type argument list and backtrack if it's invalid
   850  		if !p.tsCanFollowTypeArgumentsInExpression() {
   851  			p.lexer.Unexpected()
   852  		}
   853  	}
   854  
   855  	// Restore the log disabled flag. Note that we can't just set it back to false
   856  	// because it may have been true to start with.
   857  	p.lexer.IsLogDisabled = oldLexer.IsLogDisabled
   858  	return true
   859  }
   860  
   861  func (p *parser) trySkipTypeScriptTypeParametersThenOpenParenWithBacktracking() skipTypeScriptTypeParametersResult {
   862  	oldLexer := p.lexer
   863  	p.lexer.IsLogDisabled = true
   864  
   865  	// Implement backtracking by restoring the lexer's memory to its original state
   866  	defer func() {
   867  		r := recover()
   868  		if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic {
   869  			p.lexer = oldLexer
   870  		} else if r != nil {
   871  			panic(r)
   872  		}
   873  	}()
   874  
   875  	result := p.skipTypeScriptTypeParameters(allowConstModifier)
   876  	if p.lexer.Token != js_lexer.TOpenParen {
   877  		p.lexer.Unexpected()
   878  	}
   879  
   880  	// Restore the log disabled flag. Note that we can't just set it back to false
   881  	// because it may have been true to start with.
   882  	p.lexer.IsLogDisabled = oldLexer.IsLogDisabled
   883  	return result
   884  }
   885  
   886  func (p *parser) trySkipTypeScriptArrowReturnTypeWithBacktracking() bool {
   887  	oldLexer := p.lexer
   888  	p.lexer.IsLogDisabled = true
   889  
   890  	// Implement backtracking by restoring the lexer's memory to its original state
   891  	defer func() {
   892  		r := recover()
   893  		if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic {
   894  			p.lexer = oldLexer
   895  		} else if r != nil {
   896  			panic(r)
   897  		}
   898  	}()
   899  
   900  	p.lexer.Expect(js_lexer.TColon)
   901  	p.skipTypeScriptReturnType()
   902  
   903  	// Check the token after this and backtrack if it's the wrong one
   904  	if p.lexer.Token != js_lexer.TEqualsGreaterThan {
   905  		p.lexer.Unexpected()
   906  	}
   907  
   908  	// Restore the log disabled flag. Note that we can't just set it back to false
   909  	// because it may have been true to start with.
   910  	p.lexer.IsLogDisabled = oldLexer.IsLogDisabled
   911  	return true
   912  }
   913  
   914  func (p *parser) trySkipTypeScriptArrowArgsWithBacktracking() bool {
   915  	oldLexer := p.lexer
   916  	p.lexer.IsLogDisabled = true
   917  
   918  	// Implement backtracking by restoring the lexer's memory to its original state
   919  	defer func() {
   920  		r := recover()
   921  		if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic {
   922  			p.lexer = oldLexer
   923  		} else if r != nil {
   924  			panic(r)
   925  		}
   926  	}()
   927  
   928  	p.skipTypeScriptFnArgs()
   929  	p.lexer.Expect(js_lexer.TEqualsGreaterThan)
   930  
   931  	// Restore the log disabled flag. Note that we can't just set it back to false
   932  	// because it may have been true to start with.
   933  	p.lexer.IsLogDisabled = oldLexer.IsLogDisabled
   934  	return true
   935  }
   936  
   937  func (p *parser) trySkipTypeScriptConstraintOfInferTypeWithBacktracking(flags skipTypeFlags) bool {
   938  	oldLexer := p.lexer
   939  	p.lexer.IsLogDisabled = true
   940  
   941  	// Implement backtracking by restoring the lexer's memory to its original state
   942  	defer func() {
   943  		r := recover()
   944  		if _, isLexerPanic := r.(js_lexer.LexerPanic); isLexerPanic {
   945  			p.lexer = oldLexer
   946  		} else if r != nil {
   947  			panic(r)
   948  		}
   949  	}()
   950  
   951  	p.lexer.Expect(js_lexer.TExtends)
   952  	p.skipTypeScriptTypeWithFlags(js_ast.LPrefix, disallowConditionalTypesFlag)
   953  	if !flags.has(disallowConditionalTypesFlag) && p.lexer.Token == js_lexer.TQuestion {
   954  		p.lexer.Unexpected()
   955  	}
   956  
   957  	// Restore the log disabled flag. Note that we can't just set it back to false
   958  	// because it may have been true to start with.
   959  	p.lexer.IsLogDisabled = oldLexer.IsLogDisabled
   960  	return true
   961  }
   962  
   963  // Returns true if the current less-than token is considered to be an arrow
   964  // function under TypeScript's rules for files containing JSX syntax
   965  func (p *parser) isTSArrowFnJSX() (isTSArrowFn bool) {
   966  	oldLexer := p.lexer
   967  	p.lexer.Next()
   968  
   969  	// Look ahead to see if this should be an arrow function instead
   970  	if p.lexer.Token == js_lexer.TConst {
   971  		p.lexer.Next()
   972  	}
   973  	if p.lexer.Token == js_lexer.TIdentifier {
   974  		p.lexer.Next()
   975  		if p.lexer.Token == js_lexer.TComma || p.lexer.Token == js_lexer.TEquals {
   976  			isTSArrowFn = true
   977  		} else if p.lexer.Token == js_lexer.TExtends {
   978  			p.lexer.Next()
   979  			isTSArrowFn = p.lexer.Token != js_lexer.TEquals && p.lexer.Token != js_lexer.TGreaterThan && p.lexer.Token != js_lexer.TSlash
   980  		}
   981  	}
   982  
   983  	// Restore the lexer
   984  	p.lexer = oldLexer
   985  	return
   986  }
   987  
   988  // This function is taken from the official TypeScript compiler source code:
   989  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
   990  //
   991  // This function is pretty inefficient as written, and could be collapsed into
   992  // a single switch statement. But that would make it harder to keep this in
   993  // sync with the TypeScript compiler's source code, so we keep doing it the
   994  // slow way.
   995  func (p *parser) tsCanFollowTypeArgumentsInExpression() bool {
   996  	switch p.lexer.Token {
   997  	case
   998  		// These tokens can follow a type argument list in a call expression.
   999  		js_lexer.TOpenParen,                     // foo<x>(
  1000  		js_lexer.TNoSubstitutionTemplateLiteral, // foo<T> `...`
  1001  		js_lexer.TTemplateHead:                  // foo<T> `...${100}...`
  1002  		return true
  1003  
  1004  	// A type argument list followed by `<` never makes sense, and a type argument list followed
  1005  	// by `>` is ambiguous with a (re-scanned) `>>` operator, so we disqualify both. Also, in
  1006  	// this context, `+` and `-` are unary operators, not binary operators.
  1007  	case js_lexer.TLessThan,
  1008  		js_lexer.TGreaterThan,
  1009  		js_lexer.TPlus,
  1010  		js_lexer.TMinus,
  1011  		// TypeScript always sees "TGreaterThan" instead of these tokens since
  1012  		// their scanner works a little differently than our lexer. So since
  1013  		// "TGreaterThan" is forbidden above, we also forbid these too.
  1014  		js_lexer.TGreaterThanEquals,
  1015  		js_lexer.TGreaterThanGreaterThan,
  1016  		js_lexer.TGreaterThanGreaterThanEquals,
  1017  		js_lexer.TGreaterThanGreaterThanGreaterThan,
  1018  		js_lexer.TGreaterThanGreaterThanGreaterThanEquals:
  1019  		return false
  1020  	}
  1021  
  1022  	// We favor the type argument list interpretation when it is immediately followed by
  1023  	// a line break, a binary operator, or something that can't start an expression.
  1024  	return p.lexer.HasNewlineBefore || p.tsIsBinaryOperator() || !p.tsIsStartOfExpression()
  1025  }
  1026  
  1027  // This function is taken from the official TypeScript compiler source code:
  1028  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
  1029  func (p *parser) tsIsBinaryOperator() bool {
  1030  	switch p.lexer.Token {
  1031  	case js_lexer.TIn:
  1032  		return p.allowIn
  1033  
  1034  	case
  1035  		js_lexer.TQuestionQuestion,
  1036  		js_lexer.TBarBar,
  1037  		js_lexer.TAmpersandAmpersand,
  1038  		js_lexer.TBar,
  1039  		js_lexer.TCaret,
  1040  		js_lexer.TAmpersand,
  1041  		js_lexer.TEqualsEquals,
  1042  		js_lexer.TExclamationEquals,
  1043  		js_lexer.TEqualsEqualsEquals,
  1044  		js_lexer.TExclamationEqualsEquals,
  1045  		js_lexer.TLessThan,
  1046  		js_lexer.TGreaterThan,
  1047  		js_lexer.TLessThanEquals,
  1048  		js_lexer.TGreaterThanEquals,
  1049  		js_lexer.TInstanceof,
  1050  		js_lexer.TLessThanLessThan,
  1051  		js_lexer.TGreaterThanGreaterThan,
  1052  		js_lexer.TGreaterThanGreaterThanGreaterThan,
  1053  		js_lexer.TPlus,
  1054  		js_lexer.TMinus,
  1055  		js_lexer.TAsterisk,
  1056  		js_lexer.TSlash,
  1057  		js_lexer.TPercent,
  1058  		js_lexer.TAsteriskAsterisk:
  1059  		return true
  1060  
  1061  	case js_lexer.TIdentifier:
  1062  		if p.lexer.IsContextualKeyword("as") || p.lexer.IsContextualKeyword("satisfies") {
  1063  			return true
  1064  		}
  1065  	}
  1066  
  1067  	return false
  1068  }
  1069  
  1070  // This function is taken from the official TypeScript compiler source code:
  1071  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
  1072  func (p *parser) tsIsStartOfExpression() bool {
  1073  	if p.tsIsStartOfLeftHandSideExpression() {
  1074  		return true
  1075  	}
  1076  
  1077  	switch p.lexer.Token {
  1078  	case
  1079  		js_lexer.TPlus,
  1080  		js_lexer.TMinus,
  1081  		js_lexer.TTilde,
  1082  		js_lexer.TExclamation,
  1083  		js_lexer.TDelete,
  1084  		js_lexer.TTypeof,
  1085  		js_lexer.TVoid,
  1086  		js_lexer.TPlusPlus,
  1087  		js_lexer.TMinusMinus,
  1088  		js_lexer.TLessThan,
  1089  		js_lexer.TPrivateIdentifier,
  1090  		js_lexer.TAt:
  1091  		return true
  1092  
  1093  	default:
  1094  		if p.lexer.Token == js_lexer.TIdentifier && (p.lexer.Identifier.String == "await" || p.lexer.Identifier.String == "yield") {
  1095  			// Yield/await always starts an expression.  Either it is an identifier (in which case
  1096  			// it is definitely an expression).  Or it's a keyword (either because we're in
  1097  			// a generator or async function, or in strict mode (or both)) and it started a yield or await expression.
  1098  			return true
  1099  		}
  1100  
  1101  		// Error tolerance.  If we see the start of some binary operator, we consider
  1102  		// that the start of an expression.  That way we'll parse out a missing identifier,
  1103  		// give a good message about an identifier being missing, and then consume the
  1104  		// rest of the binary expression.
  1105  		if p.tsIsBinaryOperator() {
  1106  			return true
  1107  		}
  1108  
  1109  		return p.tsIsIdentifier()
  1110  	}
  1111  }
  1112  
  1113  // This function is taken from the official TypeScript compiler source code:
  1114  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
  1115  func (p *parser) tsIsStartOfLeftHandSideExpression() bool {
  1116  	switch p.lexer.Token {
  1117  	case
  1118  		js_lexer.TThis,
  1119  		js_lexer.TSuper,
  1120  		js_lexer.TNull,
  1121  		js_lexer.TTrue,
  1122  		js_lexer.TFalse,
  1123  		js_lexer.TNumericLiteral,
  1124  		js_lexer.TBigIntegerLiteral,
  1125  		js_lexer.TStringLiteral,
  1126  		js_lexer.TNoSubstitutionTemplateLiteral,
  1127  		js_lexer.TTemplateHead,
  1128  		js_lexer.TOpenParen,
  1129  		js_lexer.TOpenBracket,
  1130  		js_lexer.TOpenBrace,
  1131  		js_lexer.TFunction,
  1132  		js_lexer.TClass,
  1133  		js_lexer.TNew,
  1134  		js_lexer.TSlash,
  1135  		js_lexer.TSlashEquals,
  1136  		js_lexer.TIdentifier:
  1137  		return true
  1138  
  1139  	case js_lexer.TImport:
  1140  		return p.tsLookAheadNextTokenIsOpenParenOrLessThanOrDot()
  1141  
  1142  	default:
  1143  		return p.tsIsIdentifier()
  1144  	}
  1145  }
  1146  
  1147  // This function is taken from the official TypeScript compiler source code:
  1148  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
  1149  func (p *parser) tsLookAheadNextTokenIsOpenParenOrLessThanOrDot() (result bool) {
  1150  	oldLexer := p.lexer
  1151  	p.lexer.Next()
  1152  
  1153  	result = p.lexer.Token == js_lexer.TOpenParen ||
  1154  		p.lexer.Token == js_lexer.TLessThan ||
  1155  		p.lexer.Token == js_lexer.TDot
  1156  
  1157  	// Restore the lexer
  1158  	p.lexer = oldLexer
  1159  	return
  1160  }
  1161  
  1162  // This function is taken from the official TypeScript compiler source code:
  1163  // https://github.com/microsoft/TypeScript/blob/master/src/compiler/parser.ts
  1164  func (p *parser) tsIsIdentifier() bool {
  1165  	if p.lexer.Token == js_lexer.TIdentifier {
  1166  		// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
  1167  		// considered a keyword and is not an identifier.
  1168  		if p.fnOrArrowDataParse.yield != allowIdent && p.lexer.Identifier.String == "yield" {
  1169  			return false
  1170  		}
  1171  
  1172  		// If we have a 'await' keyword, and we're in the [Await] context, then 'await' is
  1173  		// considered a keyword and is not an identifier.
  1174  		if p.fnOrArrowDataParse.await != allowIdent && p.lexer.Identifier.String == "await" {
  1175  			return false
  1176  		}
  1177  
  1178  		return true
  1179  	}
  1180  
  1181  	return false
  1182  }
  1183  
  1184  func (p *parser) skipTypeScriptInterfaceStmt(opts parseStmtOpts) {
  1185  	name := p.lexer.Identifier.String
  1186  	p.lexer.Expect(js_lexer.TIdentifier)
  1187  
  1188  	if opts.isModuleScope {
  1189  		p.localTypeNames[name] = true
  1190  	}
  1191  
  1192  	p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowEmptyTypeParameters)
  1193  
  1194  	if p.lexer.Token == js_lexer.TExtends {
  1195  		p.lexer.Next()
  1196  		for {
  1197  			p.skipTypeScriptType(js_ast.LLowest)
  1198  			if p.lexer.Token != js_lexer.TComma {
  1199  				break
  1200  			}
  1201  			p.lexer.Next()
  1202  		}
  1203  	}
  1204  
  1205  	if p.lexer.IsContextualKeyword("implements") {
  1206  		p.lexer.Next()
  1207  		for {
  1208  			p.skipTypeScriptType(js_ast.LLowest)
  1209  			if p.lexer.Token != js_lexer.TComma {
  1210  				break
  1211  			}
  1212  			p.lexer.Next()
  1213  		}
  1214  	}
  1215  
  1216  	p.skipTypeScriptObjectType()
  1217  }
  1218  
  1219  func (p *parser) skipTypeScriptTypeStmt(opts parseStmtOpts) {
  1220  	if opts.isExport {
  1221  		switch p.lexer.Token {
  1222  		case js_lexer.TOpenBrace:
  1223  			// "export type {foo}"
  1224  			// "export type {foo} from 'bar'"
  1225  			p.parseExportClause()
  1226  			if p.lexer.IsContextualKeyword("from") {
  1227  				p.lexer.Next()
  1228  				p.parsePath()
  1229  			}
  1230  			p.lexer.ExpectOrInsertSemicolon()
  1231  			return
  1232  
  1233  		// This is invalid TypeScript, and is rejected by the TypeScript compiler:
  1234  		//
  1235  		//   example.ts:1:1 - error TS1383: Only named exports may use 'export type'.
  1236  		//
  1237  		//   1 export type * from './types'
  1238  		//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  1239  		//
  1240  		// However, people may not know this and then blame esbuild for it not
  1241  		// working. So we parse it anyway and then discard it (since we always
  1242  		// discard all types). People who do this should be running the TypeScript
  1243  		// type checker when using TypeScript, which will then report this error.
  1244  		case js_lexer.TAsterisk:
  1245  			// "export type * from 'path'"
  1246  			p.lexer.Next()
  1247  			if p.lexer.IsContextualKeyword("as") {
  1248  				// "export type * as ns from 'path'"
  1249  				p.lexer.Next()
  1250  				p.parseClauseAlias("export")
  1251  				p.lexer.Next()
  1252  			}
  1253  			p.lexer.ExpectContextualKeyword("from")
  1254  			p.parsePath()
  1255  			p.lexer.ExpectOrInsertSemicolon()
  1256  			return
  1257  		}
  1258  	}
  1259  
  1260  	name := p.lexer.Identifier.String
  1261  	p.lexer.Expect(js_lexer.TIdentifier)
  1262  
  1263  	if opts.isModuleScope {
  1264  		p.localTypeNames[name] = true
  1265  	}
  1266  
  1267  	p.skipTypeScriptTypeParameters(allowInOutVarianceAnnotations | allowEmptyTypeParameters)
  1268  	p.lexer.Expect(js_lexer.TEquals)
  1269  	p.skipTypeScriptType(js_ast.LLowest)
  1270  	p.lexer.ExpectOrInsertSemicolon()
  1271  }
  1272  
  1273  func (p *parser) parseTypeScriptEnumStmt(loc logger.Loc, opts parseStmtOpts) js_ast.Stmt {
  1274  	p.lexer.Expect(js_lexer.TEnum)
  1275  	nameLoc := p.lexer.Loc()
  1276  	nameText := p.lexer.Identifier.String
  1277  	p.lexer.Expect(js_lexer.TIdentifier)
  1278  	name := ast.LocRef{Loc: nameLoc, Ref: ast.InvalidRef}
  1279  
  1280  	// Generate the namespace object
  1281  	exportedMembers := p.getOrCreateExportedNamespaceMembers(nameText, opts.isExport)
  1282  	tsNamespace := &js_ast.TSNamespaceScope{
  1283  		ExportedMembers: exportedMembers,
  1284  		ArgRef:          ast.InvalidRef,
  1285  		IsEnumScope:     true,
  1286  	}
  1287  	enumMemberData := &js_ast.TSNamespaceMemberNamespace{
  1288  		ExportedMembers: exportedMembers,
  1289  	}
  1290  
  1291  	// Declare the enum and create the scope
  1292  	scopeIndex := len(p.scopesInOrder)
  1293  	if !opts.isTypeScriptDeclare {
  1294  		name.Ref = p.declareSymbol(ast.SymbolTSEnum, nameLoc, nameText)
  1295  		p.pushScopeForParsePass(js_ast.ScopeEntry, loc)
  1296  		p.currentScope.TSNamespace = tsNamespace
  1297  		p.refToTSNamespaceMemberData[name.Ref] = enumMemberData
  1298  	}
  1299  
  1300  	p.lexer.Expect(js_lexer.TOpenBrace)
  1301  	values := []js_ast.EnumValue{}
  1302  
  1303  	oldFnOrArrowData := p.fnOrArrowDataParse
  1304  	p.fnOrArrowDataParse = fnOrArrowDataParse{
  1305  		isThisDisallowed: true,
  1306  		needsAsyncLoc:    logger.Loc{Start: -1},
  1307  	}
  1308  
  1309  	// Parse the body
  1310  	for p.lexer.Token != js_lexer.TCloseBrace {
  1311  		nameRange := p.lexer.Range()
  1312  		value := js_ast.EnumValue{
  1313  			Loc: nameRange.Loc,
  1314  			Ref: ast.InvalidRef,
  1315  		}
  1316  
  1317  		// Parse the name
  1318  		var nameText string
  1319  		if p.lexer.Token == js_lexer.TStringLiteral {
  1320  			value.Name = p.lexer.StringLiteral()
  1321  			nameText = helpers.UTF16ToString(value.Name)
  1322  		} else if p.lexer.IsIdentifierOrKeyword() {
  1323  			nameText = p.lexer.Identifier.String
  1324  			value.Name = helpers.StringToUTF16(nameText)
  1325  		} else {
  1326  			p.lexer.Expect(js_lexer.TIdentifier)
  1327  		}
  1328  		p.lexer.Next()
  1329  
  1330  		// Identifiers can be referenced by other values
  1331  		if !opts.isTypeScriptDeclare && js_ast.IsIdentifierUTF16(value.Name) {
  1332  			value.Ref = p.declareSymbol(ast.SymbolOther, value.Loc, helpers.UTF16ToString(value.Name))
  1333  		}
  1334  
  1335  		// Parse the initializer
  1336  		if p.lexer.Token == js_lexer.TEquals {
  1337  			p.lexer.Next()
  1338  			value.ValueOrNil = p.parseExpr(js_ast.LComma)
  1339  		}
  1340  
  1341  		values = append(values, value)
  1342  
  1343  		// Add this enum value as a member of the enum's namespace
  1344  		exportedMembers[nameText] = js_ast.TSNamespaceMember{
  1345  			Loc:         value.Loc,
  1346  			Data:        &js_ast.TSNamespaceMemberProperty{},
  1347  			IsEnumValue: true,
  1348  		}
  1349  
  1350  		if p.lexer.Token != js_lexer.TComma && p.lexer.Token != js_lexer.TSemicolon {
  1351  			if p.lexer.IsIdentifierOrKeyword() || p.lexer.Token == js_lexer.TStringLiteral {
  1352  				var errorLoc logger.Loc
  1353  				var errorText string
  1354  
  1355  				if value.ValueOrNil.Data == nil {
  1356  					errorLoc = logger.Loc{Start: nameRange.End()}
  1357  					errorText = fmt.Sprintf("Expected \",\" after %q in enum", nameText)
  1358  				} else {
  1359  					var nextName string
  1360  					if p.lexer.Token == js_lexer.TStringLiteral {
  1361  						nextName = helpers.UTF16ToString(p.lexer.StringLiteral())
  1362  					} else {
  1363  						nextName = p.lexer.Identifier.String
  1364  					}
  1365  					errorLoc = p.lexer.Loc()
  1366  					errorText = fmt.Sprintf("Expected \",\" before %q in enum", nextName)
  1367  				}
  1368  
  1369  				data := p.tracker.MsgData(logger.Range{Loc: errorLoc}, errorText)
  1370  				data.Location.Suggestion = ","
  1371  				p.log.AddMsg(logger.Msg{Kind: logger.Error, Data: data})
  1372  				panic(js_lexer.LexerPanic{})
  1373  			}
  1374  			break
  1375  		}
  1376  		p.lexer.Next()
  1377  	}
  1378  
  1379  	p.fnOrArrowDataParse = oldFnOrArrowData
  1380  
  1381  	if !opts.isTypeScriptDeclare {
  1382  		// Avoid a collision with the enum closure argument variable if the
  1383  		// enum exports a symbol with the same name as the enum itself:
  1384  		//
  1385  		//   enum foo {
  1386  		//     foo = 123,
  1387  		//     bar = foo,
  1388  		//   }
  1389  		//
  1390  		// TypeScript generates the following code in this case:
  1391  		//
  1392  		//   var foo;
  1393  		//   (function (foo) {
  1394  		//     foo[foo["foo"] = 123] = "foo";
  1395  		//     foo[foo["bar"] = 123] = "bar";
  1396  		//   })(foo || (foo = {}));
  1397  		//
  1398  		// Whereas in this case:
  1399  		//
  1400  		//   enum foo {
  1401  		//     bar = foo as any,
  1402  		//   }
  1403  		//
  1404  		// TypeScript generates the following code:
  1405  		//
  1406  		//   var foo;
  1407  		//   (function (foo) {
  1408  		//     foo[foo["bar"] = foo] = "bar";
  1409  		//   })(foo || (foo = {}));
  1410  		//
  1411  		if _, ok := p.currentScope.Members[nameText]; ok {
  1412  			// Add a "_" to make tests easier to read, since non-bundler tests don't
  1413  			// run the renamer. For external-facing things the renamer will avoid
  1414  			// collisions automatically so this isn't important for correctness.
  1415  			tsNamespace.ArgRef = p.newSymbol(ast.SymbolHoisted, "_"+nameText)
  1416  			p.currentScope.Generated = append(p.currentScope.Generated, tsNamespace.ArgRef)
  1417  		} else {
  1418  			tsNamespace.ArgRef = p.declareSymbol(ast.SymbolHoisted, nameLoc, nameText)
  1419  		}
  1420  		p.refToTSNamespaceMemberData[tsNamespace.ArgRef] = enumMemberData
  1421  
  1422  		p.popScope()
  1423  	}
  1424  
  1425  	p.lexer.Expect(js_lexer.TCloseBrace)
  1426  
  1427  	if opts.isTypeScriptDeclare {
  1428  		if opts.isNamespaceScope && opts.isExport {
  1429  			p.hasNonLocalExportDeclareInsideNamespace = true
  1430  		}
  1431  
  1432  		return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared}
  1433  	}
  1434  
  1435  	// Save these for when we do out-of-order enum visiting
  1436  	if p.scopesInOrderForEnum == nil {
  1437  		p.scopesInOrderForEnum = make(map[logger.Loc][]scopeOrder)
  1438  	}
  1439  
  1440  	// Make a copy of "scopesInOrder" instead of a slice since the original
  1441  	// array may be flattened in the future by "popAndFlattenScope"
  1442  	p.scopesInOrderForEnum[loc] = append([]scopeOrder{}, p.scopesInOrder[scopeIndex:]...)
  1443  
  1444  	return js_ast.Stmt{Loc: loc, Data: &js_ast.SEnum{
  1445  		Name:     name,
  1446  		Arg:      tsNamespace.ArgRef,
  1447  		Values:   values,
  1448  		IsExport: opts.isExport,
  1449  	}}
  1450  }
  1451  
  1452  // This assumes the caller has already parsed the "import" token
  1453  func (p *parser) parseTypeScriptImportEqualsStmt(loc logger.Loc, opts parseStmtOpts, defaultNameLoc logger.Loc, defaultName string) js_ast.Stmt {
  1454  	p.lexer.Expect(js_lexer.TEquals)
  1455  
  1456  	kind := p.selectLocalKind(js_ast.LocalConst)
  1457  	name := p.lexer.Identifier
  1458  	value := js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EIdentifier{Ref: p.storeNameInRef(name)}}
  1459  	p.lexer.Expect(js_lexer.TIdentifier)
  1460  
  1461  	if name.String == "require" && p.lexer.Token == js_lexer.TOpenParen {
  1462  		// "import ns = require('x')"
  1463  		p.lexer.Next()
  1464  		path := js_ast.Expr{Loc: p.lexer.Loc(), Data: &js_ast.EString{Value: p.lexer.StringLiteral()}}
  1465  		p.lexer.Expect(js_lexer.TStringLiteral)
  1466  		p.lexer.Expect(js_lexer.TCloseParen)
  1467  		value.Data = &js_ast.ECall{
  1468  			Target: value,
  1469  			Args:   []js_ast.Expr{path},
  1470  		}
  1471  	} else {
  1472  		// "import Foo = Bar"
  1473  		// "import Foo = Bar.Baz"
  1474  		for p.lexer.Token == js_lexer.TDot {
  1475  			p.lexer.Next()
  1476  			value.Data = &js_ast.EDot{
  1477  				Target:               value,
  1478  				Name:                 p.lexer.Identifier.String,
  1479  				NameLoc:              p.lexer.Loc(),
  1480  				CanBeRemovedIfUnused: true,
  1481  			}
  1482  			p.lexer.Expect(js_lexer.TIdentifier)
  1483  		}
  1484  	}
  1485  
  1486  	p.lexer.ExpectOrInsertSemicolon()
  1487  
  1488  	if opts.isTypeScriptDeclare {
  1489  		// "import type foo = require('bar');"
  1490  		// "import type foo = bar.baz;"
  1491  		return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared}
  1492  	}
  1493  
  1494  	ref := p.declareSymbol(ast.SymbolConst, defaultNameLoc, defaultName)
  1495  	decls := []js_ast.Decl{{
  1496  		Binding:    js_ast.Binding{Loc: defaultNameLoc, Data: &js_ast.BIdentifier{Ref: ref}},
  1497  		ValueOrNil: value,
  1498  	}}
  1499  
  1500  	return js_ast.Stmt{Loc: loc, Data: &js_ast.SLocal{
  1501  		Kind:              kind,
  1502  		Decls:             decls,
  1503  		IsExport:          opts.isExport,
  1504  		WasTSImportEquals: true,
  1505  	}}
  1506  }
  1507  
  1508  // Generate a TypeScript namespace object for this namespace's scope. If this
  1509  // namespace is another block that is to be merged with an existing namespace,
  1510  // use that earlier namespace's object instead.
  1511  func (p *parser) getOrCreateExportedNamespaceMembers(name string, isExport bool) js_ast.TSNamespaceMembers {
  1512  	// Merge with a sibling namespace from the same scope
  1513  	if existingMember, ok := p.currentScope.Members[name]; ok {
  1514  		if memberData, ok := p.refToTSNamespaceMemberData[existingMember.Ref]; ok {
  1515  			if nsMemberData, ok := memberData.(*js_ast.TSNamespaceMemberNamespace); ok {
  1516  				return nsMemberData.ExportedMembers
  1517  			}
  1518  		}
  1519  	}
  1520  
  1521  	// Merge with a sibling namespace from a different scope
  1522  	if isExport {
  1523  		if parentNamespace := p.currentScope.TSNamespace; parentNamespace != nil {
  1524  			if existing, ok := parentNamespace.ExportedMembers[name]; ok {
  1525  				if existing, ok := existing.Data.(*js_ast.TSNamespaceMemberNamespace); ok {
  1526  					return existing.ExportedMembers
  1527  				}
  1528  			}
  1529  		}
  1530  	}
  1531  
  1532  	// Otherwise, generate a new namespace object
  1533  	return make(js_ast.TSNamespaceMembers)
  1534  }
  1535  
  1536  func (p *parser) parseTypeScriptNamespaceStmt(loc logger.Loc, opts parseStmtOpts) js_ast.Stmt {
  1537  	// "namespace Foo {}"
  1538  	nameLoc := p.lexer.Loc()
  1539  	nameText := p.lexer.Identifier.String
  1540  	p.lexer.Next()
  1541  
  1542  	// Generate the namespace object
  1543  	exportedMembers := p.getOrCreateExportedNamespaceMembers(nameText, opts.isExport)
  1544  	tsNamespace := &js_ast.TSNamespaceScope{
  1545  		ExportedMembers: exportedMembers,
  1546  		ArgRef:          ast.InvalidRef,
  1547  	}
  1548  	nsMemberData := &js_ast.TSNamespaceMemberNamespace{
  1549  		ExportedMembers: exportedMembers,
  1550  	}
  1551  
  1552  	// Declare the namespace and create the scope
  1553  	name := ast.LocRef{Loc: nameLoc, Ref: ast.InvalidRef}
  1554  	scopeIndex := p.pushScopeForParsePass(js_ast.ScopeEntry, loc)
  1555  	p.currentScope.TSNamespace = tsNamespace
  1556  
  1557  	oldHasNonLocalExportDeclareInsideNamespace := p.hasNonLocalExportDeclareInsideNamespace
  1558  	oldFnOrArrowData := p.fnOrArrowDataParse
  1559  	p.hasNonLocalExportDeclareInsideNamespace = false
  1560  	p.fnOrArrowDataParse = fnOrArrowDataParse{
  1561  		isThisDisallowed:   true,
  1562  		isReturnDisallowed: true,
  1563  		needsAsyncLoc:      logger.Loc{Start: -1},
  1564  	}
  1565  
  1566  	// Parse the statements inside the namespace
  1567  	var stmts []js_ast.Stmt
  1568  	if p.lexer.Token == js_lexer.TDot {
  1569  		dotLoc := p.lexer.Loc()
  1570  		p.lexer.Next()
  1571  		stmts = []js_ast.Stmt{p.parseTypeScriptNamespaceStmt(dotLoc, parseStmtOpts{
  1572  			isExport:            true,
  1573  			isNamespaceScope:    true,
  1574  			isTypeScriptDeclare: opts.isTypeScriptDeclare,
  1575  		})}
  1576  	} else if opts.isTypeScriptDeclare && p.lexer.Token != js_lexer.TOpenBrace {
  1577  		p.lexer.ExpectOrInsertSemicolon()
  1578  	} else {
  1579  		p.lexer.Expect(js_lexer.TOpenBrace)
  1580  		stmts = p.parseStmtsUpTo(js_lexer.TCloseBrace, parseStmtOpts{
  1581  			isNamespaceScope:    true,
  1582  			isTypeScriptDeclare: opts.isTypeScriptDeclare,
  1583  		})
  1584  		p.lexer.Next()
  1585  	}
  1586  
  1587  	hasNonLocalExportDeclareInsideNamespace := p.hasNonLocalExportDeclareInsideNamespace
  1588  	p.hasNonLocalExportDeclareInsideNamespace = oldHasNonLocalExportDeclareInsideNamespace
  1589  	p.fnOrArrowDataParse = oldFnOrArrowData
  1590  
  1591  	// Add any exported members from this namespace's body as members of the
  1592  	// associated namespace object.
  1593  	for _, stmt := range stmts {
  1594  		switch s := stmt.Data.(type) {
  1595  		case *js_ast.SFunction:
  1596  			if s.IsExport {
  1597  				name := p.symbols[s.Fn.Name.Ref.InnerIndex].OriginalName
  1598  				member := js_ast.TSNamespaceMember{
  1599  					Loc:  s.Fn.Name.Loc,
  1600  					Data: &js_ast.TSNamespaceMemberProperty{},
  1601  				}
  1602  				exportedMembers[name] = member
  1603  				p.refToTSNamespaceMemberData[s.Fn.Name.Ref] = member.Data
  1604  			}
  1605  
  1606  		case *js_ast.SClass:
  1607  			if s.IsExport {
  1608  				name := p.symbols[s.Class.Name.Ref.InnerIndex].OriginalName
  1609  				member := js_ast.TSNamespaceMember{
  1610  					Loc:  s.Class.Name.Loc,
  1611  					Data: &js_ast.TSNamespaceMemberProperty{},
  1612  				}
  1613  				exportedMembers[name] = member
  1614  				p.refToTSNamespaceMemberData[s.Class.Name.Ref] = member.Data
  1615  			}
  1616  
  1617  		case *js_ast.SNamespace:
  1618  			if s.IsExport {
  1619  				if memberData, ok := p.refToTSNamespaceMemberData[s.Name.Ref]; ok {
  1620  					if nsMemberData, ok := memberData.(*js_ast.TSNamespaceMemberNamespace); ok {
  1621  						member := js_ast.TSNamespaceMember{
  1622  							Loc: s.Name.Loc,
  1623  							Data: &js_ast.TSNamespaceMemberNamespace{
  1624  								ExportedMembers: nsMemberData.ExportedMembers,
  1625  							},
  1626  						}
  1627  						exportedMembers[p.symbols[s.Name.Ref.InnerIndex].OriginalName] = member
  1628  						p.refToTSNamespaceMemberData[s.Name.Ref] = member.Data
  1629  					}
  1630  				}
  1631  			}
  1632  
  1633  		case *js_ast.SEnum:
  1634  			if s.IsExport {
  1635  				if memberData, ok := p.refToTSNamespaceMemberData[s.Name.Ref]; ok {
  1636  					if nsMemberData, ok := memberData.(*js_ast.TSNamespaceMemberNamespace); ok {
  1637  						member := js_ast.TSNamespaceMember{
  1638  							Loc: s.Name.Loc,
  1639  							Data: &js_ast.TSNamespaceMemberNamespace{
  1640  								ExportedMembers: nsMemberData.ExportedMembers,
  1641  							},
  1642  						}
  1643  						exportedMembers[p.symbols[s.Name.Ref.InnerIndex].OriginalName] = member
  1644  						p.refToTSNamespaceMemberData[s.Name.Ref] = member.Data
  1645  					}
  1646  				}
  1647  			}
  1648  
  1649  		case *js_ast.SLocal:
  1650  			if s.IsExport {
  1651  				js_ast.ForEachIdentifierBindingInDecls(s.Decls, func(loc logger.Loc, b *js_ast.BIdentifier) {
  1652  					name := p.symbols[b.Ref.InnerIndex].OriginalName
  1653  					member := js_ast.TSNamespaceMember{
  1654  						Loc:  loc,
  1655  						Data: &js_ast.TSNamespaceMemberProperty{},
  1656  					}
  1657  					exportedMembers[name] = member
  1658  					p.refToTSNamespaceMemberData[b.Ref] = member.Data
  1659  				})
  1660  			}
  1661  		}
  1662  	}
  1663  
  1664  	// Import assignments may be only used in type expressions, not value
  1665  	// expressions. If this is the case, the TypeScript compiler removes
  1666  	// them entirely from the output. That can cause the namespace itself
  1667  	// to be considered empty and thus be removed.
  1668  	importEqualsCount := 0
  1669  	for _, stmt := range stmts {
  1670  		if local, ok := stmt.Data.(*js_ast.SLocal); ok && local.WasTSImportEquals && !local.IsExport {
  1671  			importEqualsCount++
  1672  		}
  1673  	}
  1674  
  1675  	// TypeScript omits namespaces without values. These namespaces
  1676  	// are only allowed to be used in type expressions. They are
  1677  	// allowed to be exported, but can also only be used in type
  1678  	// expressions when imported. So we shouldn't count them as a
  1679  	// real export either.
  1680  	//
  1681  	// TypeScript also strangely counts namespaces containing only
  1682  	// "export declare" statements as non-empty even though "declare"
  1683  	// statements are only type annotations. We cannot omit the namespace
  1684  	// in that case. See https://github.com/evanw/esbuild/issues/1158.
  1685  	if (len(stmts) == importEqualsCount && !hasNonLocalExportDeclareInsideNamespace) || opts.isTypeScriptDeclare {
  1686  		p.popAndDiscardScope(scopeIndex)
  1687  		if opts.isModuleScope {
  1688  			p.localTypeNames[nameText] = true
  1689  		}
  1690  		return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared}
  1691  	}
  1692  
  1693  	if !opts.isTypeScriptDeclare {
  1694  		// Avoid a collision with the namespace closure argument variable if the
  1695  		// namespace exports a symbol with the same name as the namespace itself:
  1696  		//
  1697  		//   namespace foo {
  1698  		//     export let foo = 123
  1699  		//     console.log(foo)
  1700  		//   }
  1701  		//
  1702  		// TypeScript generates the following code in this case:
  1703  		//
  1704  		//   var foo;
  1705  		//   (function (foo_1) {
  1706  		//     foo_1.foo = 123;
  1707  		//     console.log(foo_1.foo);
  1708  		//   })(foo || (foo = {}));
  1709  		//
  1710  		if _, ok := p.currentScope.Members[nameText]; ok {
  1711  			// Add a "_" to make tests easier to read, since non-bundler tests don't
  1712  			// run the renamer. For external-facing things the renamer will avoid
  1713  			// collisions automatically so this isn't important for correctness.
  1714  			tsNamespace.ArgRef = p.newSymbol(ast.SymbolHoisted, "_"+nameText)
  1715  			p.currentScope.Generated = append(p.currentScope.Generated, tsNamespace.ArgRef)
  1716  		} else {
  1717  			tsNamespace.ArgRef = p.declareSymbol(ast.SymbolHoisted, nameLoc, nameText)
  1718  		}
  1719  		p.refToTSNamespaceMemberData[tsNamespace.ArgRef] = nsMemberData
  1720  	}
  1721  
  1722  	p.popScope()
  1723  	if !opts.isTypeScriptDeclare {
  1724  		name.Ref = p.declareSymbol(ast.SymbolTSNamespace, nameLoc, nameText)
  1725  		p.refToTSNamespaceMemberData[name.Ref] = nsMemberData
  1726  	}
  1727  	return js_ast.Stmt{Loc: loc, Data: &js_ast.SNamespace{
  1728  		Name:     name,
  1729  		Arg:      tsNamespace.ArgRef,
  1730  		Stmts:    stmts,
  1731  		IsExport: opts.isExport,
  1732  	}}
  1733  }
  1734  
  1735  func (p *parser) generateClosureForTypeScriptNamespaceOrEnum(
  1736  	stmts []js_ast.Stmt, stmtLoc logger.Loc, isExport bool, nameLoc logger.Loc,
  1737  	nameRef ast.Ref, argRef ast.Ref, stmtsInsideClosure []js_ast.Stmt,
  1738  ) []js_ast.Stmt {
  1739  	// Follow the link chain in case symbols were merged
  1740  	symbol := p.symbols[nameRef.InnerIndex]
  1741  	for symbol.Link != ast.InvalidRef {
  1742  		nameRef = symbol.Link
  1743  		symbol = p.symbols[nameRef.InnerIndex]
  1744  	}
  1745  
  1746  	// Make sure to only emit a variable once for a given namespace, since there
  1747  	// can be multiple namespace blocks for the same namespace
  1748  	if (symbol.Kind == ast.SymbolTSNamespace || symbol.Kind == ast.SymbolTSEnum) && !p.emittedNamespaceVars[nameRef] {
  1749  		decls := []js_ast.Decl{{Binding: js_ast.Binding{Loc: nameLoc, Data: &js_ast.BIdentifier{Ref: nameRef}}}}
  1750  		p.emittedNamespaceVars[nameRef] = true
  1751  		if p.currentScope == p.moduleScope {
  1752  			// Top-level namespace: "var"
  1753  			stmts = append(stmts, js_ast.Stmt{Loc: stmtLoc, Data: &js_ast.SLocal{
  1754  				Kind:     js_ast.LocalVar,
  1755  				Decls:    decls,
  1756  				IsExport: isExport,
  1757  			}})
  1758  		} else {
  1759  			// Nested namespace: "let"
  1760  			stmts = append(stmts, js_ast.Stmt{Loc: stmtLoc, Data: &js_ast.SLocal{
  1761  				Kind:  js_ast.LocalLet,
  1762  				Decls: decls,
  1763  			}})
  1764  		}
  1765  	}
  1766  
  1767  	var argExpr js_ast.Expr
  1768  	if p.options.minifySyntax && !p.options.unsupportedJSFeatures.Has(compat.LogicalAssignment) {
  1769  		// If the "||=" operator is supported, our minified output can be slightly smaller
  1770  		if isExport && p.enclosingNamespaceArgRef != nil {
  1771  			// "name = (enclosing.name ||= {})"
  1772  			argExpr = js_ast.Assign(
  1773  				js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1774  				js_ast.Expr{Loc: nameLoc, Data: &js_ast.EBinary{
  1775  					Op: js_ast.BinOpLogicalOrAssign,
  1776  					Left: js_ast.Expr{Loc: nameLoc, Data: p.dotOrMangledPropVisit(
  1777  						js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}},
  1778  						p.symbols[nameRef.InnerIndex].OriginalName,
  1779  						nameLoc,
  1780  					)},
  1781  					Right: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EObject{}},
  1782  				}},
  1783  			)
  1784  			p.recordUsage(*p.enclosingNamespaceArgRef)
  1785  			p.recordUsage(nameRef)
  1786  		} else {
  1787  			// "name ||= {}"
  1788  			argExpr = js_ast.Expr{Loc: nameLoc, Data: &js_ast.EBinary{
  1789  				Op:    js_ast.BinOpLogicalOrAssign,
  1790  				Left:  js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1791  				Right: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EObject{}},
  1792  			}}
  1793  			p.recordUsage(nameRef)
  1794  		}
  1795  	} else {
  1796  		if isExport && p.enclosingNamespaceArgRef != nil {
  1797  			// "name = enclosing.name || (enclosing.name = {})"
  1798  			name := p.symbols[nameRef.InnerIndex].OriginalName
  1799  			argExpr = js_ast.Assign(
  1800  				js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1801  				js_ast.Expr{Loc: nameLoc, Data: &js_ast.EBinary{
  1802  					Op: js_ast.BinOpLogicalOr,
  1803  					Left: js_ast.Expr{Loc: nameLoc, Data: p.dotOrMangledPropVisit(
  1804  						js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}},
  1805  						name,
  1806  						nameLoc,
  1807  					)},
  1808  					Right: js_ast.Assign(
  1809  						js_ast.Expr{Loc: nameLoc, Data: p.dotOrMangledPropVisit(
  1810  							js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: *p.enclosingNamespaceArgRef}},
  1811  							name,
  1812  							nameLoc,
  1813  						)},
  1814  						js_ast.Expr{Loc: nameLoc, Data: &js_ast.EObject{}},
  1815  					),
  1816  				}},
  1817  			)
  1818  			p.recordUsage(*p.enclosingNamespaceArgRef)
  1819  			p.recordUsage(*p.enclosingNamespaceArgRef)
  1820  			p.recordUsage(nameRef)
  1821  		} else {
  1822  			// "name || (name = {})"
  1823  			argExpr = js_ast.Expr{Loc: nameLoc, Data: &js_ast.EBinary{
  1824  				Op:   js_ast.BinOpLogicalOr,
  1825  				Left: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1826  				Right: js_ast.Assign(
  1827  					js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1828  					js_ast.Expr{Loc: nameLoc, Data: &js_ast.EObject{}},
  1829  				),
  1830  			}}
  1831  			p.recordUsage(nameRef)
  1832  			p.recordUsage(nameRef)
  1833  		}
  1834  	}
  1835  
  1836  	// Try to use an arrow function if possible for compactness
  1837  	var targetExpr js_ast.Expr
  1838  	args := []js_ast.Arg{{Binding: js_ast.Binding{Loc: nameLoc, Data: &js_ast.BIdentifier{Ref: argRef}}}}
  1839  	if p.options.unsupportedJSFeatures.Has(compat.Arrow) {
  1840  		targetExpr = js_ast.Expr{Loc: stmtLoc, Data: &js_ast.EFunction{Fn: js_ast.Fn{
  1841  			Args: args,
  1842  			Body: js_ast.FnBody{Loc: stmtLoc, Block: js_ast.SBlock{Stmts: stmtsInsideClosure}},
  1843  		}}}
  1844  	} else {
  1845  		// "(() => { foo() })()" => "(() => foo())()"
  1846  		if p.options.minifySyntax && len(stmtsInsideClosure) == 1 {
  1847  			if expr, ok := stmtsInsideClosure[0].Data.(*js_ast.SExpr); ok {
  1848  				stmtsInsideClosure[0].Data = &js_ast.SReturn{ValueOrNil: expr.Value}
  1849  			}
  1850  		}
  1851  		targetExpr = js_ast.Expr{Loc: stmtLoc, Data: &js_ast.EArrow{
  1852  			Args:       args,
  1853  			Body:       js_ast.FnBody{Loc: stmtLoc, Block: js_ast.SBlock{Stmts: stmtsInsideClosure}},
  1854  			PreferExpr: true,
  1855  		}}
  1856  	}
  1857  
  1858  	// Call the closure with the name object
  1859  	stmts = append(stmts, js_ast.Stmt{Loc: stmtLoc, Data: &js_ast.SExpr{Value: js_ast.Expr{Loc: stmtLoc, Data: &js_ast.ECall{
  1860  		Target: targetExpr,
  1861  		Args:   []js_ast.Expr{argExpr},
  1862  	}}}})
  1863  
  1864  	return stmts
  1865  }
  1866  
  1867  func (p *parser) generateClosureForTypeScriptEnum(
  1868  	stmts []js_ast.Stmt, stmtLoc logger.Loc, isExport bool, nameLoc logger.Loc,
  1869  	nameRef ast.Ref, argRef ast.Ref, exprsInsideClosure []js_ast.Expr,
  1870  	allValuesArePure bool,
  1871  ) []js_ast.Stmt {
  1872  	// Bail back to the namespace code for enums that aren't at the top level.
  1873  	// Doing this for nested enums is problematic for two reasons. First of all
  1874  	// enums inside of namespaces must be property accesses off the namespace
  1875  	// object instead of variable declarations. Also we'd need to use "let"
  1876  	// instead of "var" which doesn't allow sibling declarations to be merged.
  1877  	if p.currentScope != p.moduleScope {
  1878  		stmtsInsideClosure := []js_ast.Stmt{}
  1879  		if len(exprsInsideClosure) > 0 {
  1880  			if p.options.minifySyntax {
  1881  				// "a; b; c;" => "a, b, c;"
  1882  				joined := js_ast.JoinAllWithComma(exprsInsideClosure)
  1883  				stmtsInsideClosure = append(stmtsInsideClosure, js_ast.Stmt{Loc: joined.Loc, Data: &js_ast.SExpr{Value: joined}})
  1884  			} else {
  1885  				for _, expr := range exprsInsideClosure {
  1886  					stmtsInsideClosure = append(stmtsInsideClosure, js_ast.Stmt{Loc: expr.Loc, Data: &js_ast.SExpr{Value: expr}})
  1887  				}
  1888  			}
  1889  		}
  1890  		return p.generateClosureForTypeScriptNamespaceOrEnum(
  1891  			stmts, stmtLoc, isExport, nameLoc, nameRef, argRef, stmtsInsideClosure)
  1892  	}
  1893  
  1894  	// This uses an output format for enums that's different but equivalent to
  1895  	// what TypeScript uses. Here is TypeScript's output:
  1896  	//
  1897  	//   var x;
  1898  	//   (function (x) {
  1899  	//     x[x["y"] = 1] = "y";
  1900  	//   })(x || (x = {}));
  1901  	//
  1902  	// And here's our output:
  1903  	//
  1904  	//   var x = /* @__PURE__ */ ((x) => {
  1905  	//     x[x["y"] = 1] = "y";
  1906  	//     return x;
  1907  	//   })(x || {});
  1908  	//
  1909  	// One benefit is that the minified output is smaller:
  1910  	//
  1911  	//   // Old output minified
  1912  	//   var x;(function(n){n[n.y=1]="y"})(x||(x={}));
  1913  	//
  1914  	//   // New output minified
  1915  	//   var x=(r=>(r[r.y=1]="y",r))(x||{});
  1916  	//
  1917  	// Another benefit is that the @__PURE__ annotation means it automatically
  1918  	// works with tree-shaking, even with more advanced features such as sibling
  1919  	// enum declarations and enum/namespace merges. Ideally all uses of the enum
  1920  	// are just direct references to enum members (and are therefore inlined as
  1921  	// long as the enum value is a constant) and the enum definition itself is
  1922  	// unused and can be removed as dead code.
  1923  
  1924  	// Follow the link chain in case symbols were merged
  1925  	symbol := p.symbols[nameRef.InnerIndex]
  1926  	for symbol.Link != ast.InvalidRef {
  1927  		nameRef = symbol.Link
  1928  		symbol = p.symbols[nameRef.InnerIndex]
  1929  	}
  1930  
  1931  	// Generate the body of the closure, including a return statement at the end
  1932  	stmtsInsideClosure := []js_ast.Stmt{}
  1933  	argExpr := js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: argRef}}
  1934  	if p.options.minifySyntax {
  1935  		// "a; b; return c;" => "return a, b, c;"
  1936  		joined := js_ast.JoinAllWithComma(exprsInsideClosure)
  1937  		joined = js_ast.JoinWithComma(joined, argExpr)
  1938  		stmtsInsideClosure = append(stmtsInsideClosure, js_ast.Stmt{Loc: joined.Loc, Data: &js_ast.SReturn{ValueOrNil: joined}})
  1939  	} else {
  1940  		for _, expr := range exprsInsideClosure {
  1941  			stmtsInsideClosure = append(stmtsInsideClosure, js_ast.Stmt{Loc: expr.Loc, Data: &js_ast.SExpr{Value: expr}})
  1942  		}
  1943  		stmtsInsideClosure = append(stmtsInsideClosure, js_ast.Stmt{Loc: argExpr.Loc, Data: &js_ast.SReturn{ValueOrNil: argExpr}})
  1944  	}
  1945  
  1946  	// Try to use an arrow function if possible for compactness
  1947  	var targetExpr js_ast.Expr
  1948  	args := []js_ast.Arg{{Binding: js_ast.Binding{Loc: nameLoc, Data: &js_ast.BIdentifier{Ref: argRef}}}}
  1949  	if p.options.unsupportedJSFeatures.Has(compat.Arrow) {
  1950  		targetExpr = js_ast.Expr{Loc: stmtLoc, Data: &js_ast.EFunction{Fn: js_ast.Fn{
  1951  			Args: args,
  1952  			Body: js_ast.FnBody{Loc: stmtLoc, Block: js_ast.SBlock{Stmts: stmtsInsideClosure}},
  1953  		}}}
  1954  	} else {
  1955  		targetExpr = js_ast.Expr{Loc: stmtLoc, Data: &js_ast.EArrow{
  1956  			Args:       args,
  1957  			Body:       js_ast.FnBody{Loc: stmtLoc, Block: js_ast.SBlock{Stmts: stmtsInsideClosure}},
  1958  			PreferExpr: p.options.minifySyntax,
  1959  		}}
  1960  	}
  1961  
  1962  	// Call the closure with the name object and store it to the variable
  1963  	decls := []js_ast.Decl{{
  1964  		Binding: js_ast.Binding{Loc: nameLoc, Data: &js_ast.BIdentifier{Ref: nameRef}},
  1965  		ValueOrNil: js_ast.Expr{Loc: stmtLoc, Data: &js_ast.ECall{
  1966  			Target: targetExpr,
  1967  			Args: []js_ast.Expr{{Loc: nameLoc, Data: &js_ast.EBinary{
  1968  				Op:    js_ast.BinOpLogicalOr,
  1969  				Left:  js_ast.Expr{Loc: nameLoc, Data: &js_ast.EIdentifier{Ref: nameRef}},
  1970  				Right: js_ast.Expr{Loc: nameLoc, Data: &js_ast.EObject{}},
  1971  			}}},
  1972  			CanBeUnwrappedIfUnused: allValuesArePure,
  1973  		}},
  1974  	}}
  1975  	p.recordUsage(nameRef)
  1976  
  1977  	// Use a "var" statement since this is a top-level enum, but only use "export" once
  1978  	stmts = append(stmts, js_ast.Stmt{Loc: stmtLoc, Data: &js_ast.SLocal{
  1979  		Kind:     js_ast.LocalVar,
  1980  		Decls:    decls,
  1981  		IsExport: isExport && !p.emittedNamespaceVars[nameRef],
  1982  	}})
  1983  	p.emittedNamespaceVars[nameRef] = true
  1984  
  1985  	return stmts
  1986  }
  1987  
  1988  func (p *parser) wrapInlinedEnum(value js_ast.Expr, comment string) js_ast.Expr {
  1989  	if strings.Contains(comment, "*/") {
  1990  		// Don't wrap with a comment
  1991  		return value
  1992  	}
  1993  
  1994  	// Wrap with a comment
  1995  	return js_ast.Expr{Loc: value.Loc, Data: &js_ast.EInlinedEnum{
  1996  		Value:   value,
  1997  		Comment: comment,
  1998  	}}
  1999  }