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

     1  package js_printer
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math"
     7  	"strconv"
     8  	"strings"
     9  	"unicode/utf8"
    10  
    11  	"github.com/evanw/esbuild/internal/ast"
    12  	"github.com/evanw/esbuild/internal/compat"
    13  	"github.com/evanw/esbuild/internal/config"
    14  	"github.com/evanw/esbuild/internal/helpers"
    15  	"github.com/evanw/esbuild/internal/js_ast"
    16  	"github.com/evanw/esbuild/internal/logger"
    17  	"github.com/evanw/esbuild/internal/renamer"
    18  	"github.com/evanw/esbuild/internal/sourcemap"
    19  )
    20  
    21  var positiveInfinity = math.Inf(1)
    22  var negativeInfinity = math.Inf(-1)
    23  
    24  const hexChars = "0123456789ABCDEF"
    25  const firstASCII = 0x20
    26  const lastASCII = 0x7E
    27  const firstHighSurrogate = 0xD800
    28  const lastHighSurrogate = 0xDBFF
    29  const firstLowSurrogate = 0xDC00
    30  const lastLowSurrogate = 0xDFFF
    31  
    32  func QuoteIdentifier(js []byte, name string, unsupportedFeatures compat.JSFeature) []byte {
    33  	isASCII := false
    34  	asciiStart := 0
    35  	for i, c := range name {
    36  		if c >= firstASCII && c <= lastASCII {
    37  			// Fast path: a run of ASCII characters
    38  			if !isASCII {
    39  				isASCII = true
    40  				asciiStart = i
    41  			}
    42  		} else {
    43  			// Slow path: escape non-ACSII characters
    44  			if isASCII {
    45  				js = append(js, name[asciiStart:i]...)
    46  				isASCII = false
    47  			}
    48  			if c <= 0xFFFF {
    49  				js = append(js, '\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15])
    50  			} else if !unsupportedFeatures.Has(compat.UnicodeEscapes) {
    51  				js = append(js, fmt.Sprintf("\\u{%X}", c)...)
    52  			} else {
    53  				panic("Internal error: Cannot encode identifier: Unicode escapes are unsupported")
    54  			}
    55  		}
    56  	}
    57  	if isASCII {
    58  		// Print one final run of ASCII characters
    59  		js = append(js, name[asciiStart:]...)
    60  	}
    61  	return js
    62  }
    63  
    64  func (p *printer) printUnquotedUTF16(text []uint16, quote rune, flags printQuotedFlags) {
    65  	temp := make([]byte, utf8.UTFMax)
    66  	js := p.js
    67  	i := 0
    68  	n := len(text)
    69  
    70  	// Only compute the line length if necessary
    71  	var startLineLength int
    72  	wrapLongLines := false
    73  	if p.options.LineLimit > 0 && (flags&printQuotedNoWrap) == 0 {
    74  		startLineLength = p.currentLineLength()
    75  		if startLineLength > p.options.LineLimit {
    76  			startLineLength = p.options.LineLimit
    77  		}
    78  		wrapLongLines = true
    79  	}
    80  
    81  	for i < n {
    82  		// Wrap long lines that are over the limit using escaped newlines
    83  		if wrapLongLines && startLineLength+i >= p.options.LineLimit {
    84  			js = append(js, "\\\n"...)
    85  			startLineLength -= p.options.LineLimit
    86  		}
    87  
    88  		c := text[i]
    89  		i++
    90  
    91  		switch c {
    92  		// Special-case the null character since it may mess with code written in C
    93  		// that treats null characters as the end of the string.
    94  		case '\x00':
    95  			// We don't want "\x001" to be written as "\01"
    96  			if i < n && text[i] >= '0' && text[i] <= '9' {
    97  				js = append(js, "\\x00"...)
    98  			} else {
    99  				js = append(js, "\\0"...)
   100  			}
   101  
   102  		// Special-case the bell character since it may cause dumping this file to
   103  		// the terminal to make a sound, which is undesirable. Note that we can't
   104  		// use an octal literal to print this shorter since octal literals are not
   105  		// allowed in strict mode (or in template strings).
   106  		case '\x07':
   107  			js = append(js, "\\x07"...)
   108  
   109  		case '\b':
   110  			js = append(js, "\\b"...)
   111  
   112  		case '\f':
   113  			js = append(js, "\\f"...)
   114  
   115  		case '\n':
   116  			if quote == '`' {
   117  				startLineLength = -i // Printing a real newline resets the line length
   118  				js = append(js, '\n')
   119  			} else {
   120  				js = append(js, "\\n"...)
   121  			}
   122  
   123  		case '\r':
   124  			js = append(js, "\\r"...)
   125  
   126  		case '\v':
   127  			js = append(js, "\\v"...)
   128  
   129  		case '\x1B':
   130  			js = append(js, "\\x1B"...)
   131  
   132  		case '\\':
   133  			js = append(js, "\\\\"...)
   134  
   135  		case '/':
   136  			// Avoid generating the sequence "</script" in JS code
   137  			if !p.options.UnsupportedFeatures.Has(compat.InlineScript) && i >= 2 && text[i-2] == '<' && i+6 <= len(text) {
   138  				script := "script"
   139  				matches := true
   140  				for j := 0; j < 6; j++ {
   141  					a := text[i+j]
   142  					b := uint16(script[j])
   143  					if a >= 'A' && a <= 'Z' {
   144  						a += 'a' - 'A'
   145  					}
   146  					if a != b {
   147  						matches = false
   148  						break
   149  					}
   150  				}
   151  				if matches {
   152  					js = append(js, '\\')
   153  				}
   154  			}
   155  			js = append(js, '/')
   156  
   157  		case '\'':
   158  			if quote == '\'' {
   159  				js = append(js, '\\')
   160  			}
   161  			js = append(js, '\'')
   162  
   163  		case '"':
   164  			if quote == '"' {
   165  				js = append(js, '\\')
   166  			}
   167  			js = append(js, '"')
   168  
   169  		case '`':
   170  			if quote == '`' {
   171  				js = append(js, '\\')
   172  			}
   173  			js = append(js, '`')
   174  
   175  		case '$':
   176  			if quote == '`' && i < n && text[i] == '{' {
   177  				js = append(js, '\\')
   178  			}
   179  			js = append(js, '$')
   180  
   181  		case '\u2028':
   182  			js = append(js, "\\u2028"...)
   183  
   184  		case '\u2029':
   185  			js = append(js, "\\u2029"...)
   186  
   187  		case '\uFEFF':
   188  			js = append(js, "\\uFEFF"...)
   189  
   190  		default:
   191  			switch {
   192  			// Common case: just append a single byte
   193  			case c <= lastASCII:
   194  				js = append(js, byte(c))
   195  
   196  			// Is this a high surrogate?
   197  			case c >= firstHighSurrogate && c <= lastHighSurrogate:
   198  				// Is there a next character?
   199  				if i < n {
   200  					c2 := text[i]
   201  
   202  					// Is it a low surrogate?
   203  					if c2 >= firstLowSurrogate && c2 <= lastLowSurrogate {
   204  						r := (rune(c) << 10) + rune(c2) + (0x10000 - (firstHighSurrogate << 10) - firstLowSurrogate)
   205  						i++
   206  
   207  						// Escape this character if UTF-8 isn't allowed
   208  						if p.options.ASCIIOnly {
   209  							if !p.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) {
   210  								js = append(js, fmt.Sprintf("\\u{%X}", r)...)
   211  							} else {
   212  								js = append(js,
   213  									'\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15],
   214  									'\\', 'u', hexChars[c2>>12], hexChars[(c2>>8)&15], hexChars[(c2>>4)&15], hexChars[c2&15],
   215  								)
   216  							}
   217  							continue
   218  						}
   219  
   220  						// Otherwise, encode to UTF-8
   221  						width := utf8.EncodeRune(temp, r)
   222  						js = append(js, temp[:width]...)
   223  						continue
   224  					}
   225  				}
   226  
   227  				// Write an unpaired high surrogate
   228  				js = append(js, '\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15])
   229  
   230  			// Is this an unpaired low surrogate or four-digit hex escape?
   231  			case (c >= firstLowSurrogate && c <= lastLowSurrogate) || (p.options.ASCIIOnly && c > 0xFF):
   232  				js = append(js, '\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15])
   233  
   234  			// Can this be a two-digit hex escape?
   235  			case p.options.ASCIIOnly:
   236  				js = append(js, '\\', 'x', hexChars[c>>4], hexChars[c&15])
   237  
   238  			// Otherwise, just encode to UTF-8
   239  			default:
   240  				width := utf8.EncodeRune(temp, rune(c))
   241  				js = append(js, temp[:width]...)
   242  			}
   243  		}
   244  	}
   245  
   246  	p.js = js
   247  }
   248  
   249  // JSX tag syntax doesn't support character escapes so non-ASCII identifiers
   250  // must be printed as UTF-8 even when the charset is set to ASCII.
   251  func (p *printer) printJSXTag(tagOrNil js_ast.Expr) {
   252  	switch e := tagOrNil.Data.(type) {
   253  	case *js_ast.EString:
   254  		p.addSourceMapping(tagOrNil.Loc)
   255  		p.print(helpers.UTF16ToString(e.Value))
   256  
   257  	case *js_ast.EIdentifier:
   258  		name := p.renamer.NameForSymbol(e.Ref)
   259  		p.addSourceMappingForName(tagOrNil.Loc, name, e.Ref)
   260  		p.print(name)
   261  
   262  	case *js_ast.EDot:
   263  		p.printJSXTag(e.Target)
   264  		p.print(".")
   265  		p.addSourceMapping(e.NameLoc)
   266  		p.print(e.Name)
   267  
   268  	default:
   269  		if tagOrNil.Data != nil {
   270  			p.printExpr(tagOrNil, js_ast.LLowest, 0)
   271  		}
   272  	}
   273  }
   274  
   275  type printer struct {
   276  	symbols                ast.SymbolMap
   277  	astHelpers             js_ast.HelperContext
   278  	renamer                renamer.Renamer
   279  	importRecords          []ast.ImportRecord
   280  	callTarget             js_ast.E
   281  	exprComments           map[logger.Loc][]string
   282  	printedExprComments    map[logger.Loc]bool
   283  	hasLegalComment        map[string]struct{}
   284  	extractedLegalComments []string
   285  	js                     []byte
   286  	jsonMetadataImports    []string
   287  	binaryExprStack        []binaryExprVisitor
   288  	options                Options
   289  	builder                sourcemap.ChunkBuilder
   290  	printNextIndentAsSpace bool
   291  
   292  	stmtStart          int
   293  	exportDefaultStart int
   294  	arrowExprStart     int
   295  	forOfInitStart     int
   296  
   297  	withNesting          int
   298  	prevOpEnd            int
   299  	needSpaceBeforeDot   int
   300  	prevRegExpEnd        int
   301  	noLeadingNewlineHere int
   302  	oldLineStart         int
   303  	oldLineEnd           int
   304  	intToBytesBuffer     [64]byte
   305  	needsSemicolon       bool
   306  	wasLazyExport        bool
   307  	prevOp               js_ast.OpCode
   308  	moduleType           js_ast.ModuleType
   309  }
   310  
   311  func (p *printer) print(text string) {
   312  	p.js = append(p.js, text...)
   313  }
   314  
   315  // This is the same as "print(string(bytes))" without any unnecessary temporary
   316  // allocations
   317  func (p *printer) printBytes(bytes []byte) {
   318  	p.js = append(p.js, bytes...)
   319  }
   320  
   321  type printQuotedFlags uint8
   322  
   323  const (
   324  	printQuotedAllowBacktick printQuotedFlags = 1 << iota
   325  	printQuotedNoWrap
   326  )
   327  
   328  func (p *printer) printQuotedUTF8(text string, flags printQuotedFlags) {
   329  	p.printQuotedUTF16(helpers.StringToUTF16(text), flags)
   330  }
   331  
   332  func (p *printer) addSourceMapping(loc logger.Loc) {
   333  	if p.options.AddSourceMappings {
   334  		p.builder.AddSourceMapping(loc, "", p.js)
   335  	}
   336  }
   337  
   338  func (p *printer) addSourceMappingForName(loc logger.Loc, name string, ref ast.Ref) {
   339  	if p.options.AddSourceMappings {
   340  		if originalName := p.symbols.Get(ast.FollowSymbols(p.symbols, ref)).OriginalName; originalName != name {
   341  			p.builder.AddSourceMapping(loc, originalName, p.js)
   342  		} else {
   343  			p.builder.AddSourceMapping(loc, "", p.js)
   344  		}
   345  	}
   346  }
   347  
   348  func (p *printer) printIndent() {
   349  	if p.options.MinifyWhitespace {
   350  		return
   351  	}
   352  
   353  	if p.printNextIndentAsSpace {
   354  		p.print(" ")
   355  		p.printNextIndentAsSpace = false
   356  		return
   357  	}
   358  
   359  	indent := p.options.Indent
   360  	if p.options.LineLimit > 0 && indent*2 >= p.options.LineLimit {
   361  		indent = p.options.LineLimit / 2
   362  	}
   363  	for i := 0; i < indent; i++ {
   364  		p.print("  ")
   365  	}
   366  }
   367  
   368  func (p *printer) mangledPropName(ref ast.Ref) string {
   369  	ref = ast.FollowSymbols(p.symbols, ref)
   370  	if name, ok := p.options.MangledProps[ref]; ok {
   371  		return name
   372  	}
   373  	return p.renamer.NameForSymbol(ref)
   374  }
   375  
   376  func (p *printer) tryToGetImportedEnumValue(target js_ast.Expr, name string) (js_ast.TSEnumValue, bool) {
   377  	if id, ok := target.Data.(*js_ast.EImportIdentifier); ok {
   378  		ref := ast.FollowSymbols(p.symbols, id.Ref)
   379  		if symbol := p.symbols.Get(ref); symbol.Kind == ast.SymbolTSEnum {
   380  			if enum, ok := p.options.TSEnums[ref]; ok {
   381  				value, ok := enum[name]
   382  				return value, ok
   383  			}
   384  		}
   385  	}
   386  	return js_ast.TSEnumValue{}, false
   387  }
   388  
   389  func (p *printer) tryToGetImportedEnumValueUTF16(target js_ast.Expr, name []uint16) (js_ast.TSEnumValue, string, bool) {
   390  	if id, ok := target.Data.(*js_ast.EImportIdentifier); ok {
   391  		ref := ast.FollowSymbols(p.symbols, id.Ref)
   392  		if symbol := p.symbols.Get(ref); symbol.Kind == ast.SymbolTSEnum {
   393  			if enum, ok := p.options.TSEnums[ref]; ok {
   394  				name := helpers.UTF16ToString(name)
   395  				value, ok := enum[name]
   396  				return value, name, ok
   397  			}
   398  		}
   399  	}
   400  	return js_ast.TSEnumValue{}, "", false
   401  }
   402  
   403  func (p *printer) printClauseAlias(loc logger.Loc, alias string) {
   404  	if js_ast.IsIdentifier(alias) {
   405  		p.printSpaceBeforeIdentifier()
   406  		p.addSourceMapping(loc)
   407  		p.printIdentifier(alias)
   408  	} else {
   409  		p.addSourceMapping(loc)
   410  		p.printQuotedUTF8(alias, 0)
   411  	}
   412  }
   413  
   414  // Note: The functions below check whether something can be printed as an
   415  // identifier or if it needs to be quoted (e.g. "x.y" vs. "x['y']") using the
   416  // ES5 identifier validity test to maximize cross-platform portability. Even
   417  // though newer JavaScript environments can handle more Unicode characters,
   418  // there isn't a published document that says which Unicode versions are
   419  // supported by which browsers. Even if a character is considered valid in the
   420  // latest version of Unicode, we don't know if the browser we're targeting
   421  // contains an older version of Unicode or not. So for safety, we quote
   422  // anything that isn't guaranteed to be compatible with ES5, the oldest
   423  // JavaScript language target that we support.
   424  
   425  func CanEscapeIdentifier(name string, UnsupportedFeatures compat.JSFeature, asciiOnly bool) bool {
   426  	return js_ast.IsIdentifierES5AndESNext(name) && (!asciiOnly ||
   427  		!UnsupportedFeatures.Has(compat.UnicodeEscapes) ||
   428  		!helpers.ContainsNonBMPCodePoint(name))
   429  }
   430  
   431  func (p *printer) canPrintIdentifier(name string) bool {
   432  	return js_ast.IsIdentifierES5AndESNext(name) && (!p.options.ASCIIOnly ||
   433  		!p.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) ||
   434  		!helpers.ContainsNonBMPCodePoint(name))
   435  }
   436  
   437  func (p *printer) canPrintIdentifierUTF16(name []uint16) bool {
   438  	return js_ast.IsIdentifierES5AndESNextUTF16(name) && (!p.options.ASCIIOnly ||
   439  		!p.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) ||
   440  		!helpers.ContainsNonBMPCodePointUTF16(name))
   441  }
   442  
   443  func (p *printer) printIdentifier(name string) {
   444  	if p.options.ASCIIOnly {
   445  		p.js = QuoteIdentifier(p.js, name, p.options.UnsupportedFeatures)
   446  	} else {
   447  		p.print(name)
   448  	}
   449  }
   450  
   451  // This is the same as "printIdentifier(StringToUTF16(bytes))" without any
   452  // unnecessary temporary allocations
   453  func (p *printer) printIdentifierUTF16(name []uint16) {
   454  	var temp [utf8.UTFMax]byte
   455  	n := len(name)
   456  
   457  	for i := 0; i < n; i++ {
   458  		c := rune(name[i])
   459  
   460  		if c >= firstHighSurrogate && c <= lastHighSurrogate && i+1 < n {
   461  			if c2 := rune(name[i+1]); c2 >= firstLowSurrogate && c2 <= lastLowSurrogate {
   462  				c = (c << 10) + c2 + (0x10000 - (firstHighSurrogate << 10) - firstLowSurrogate)
   463  				i++
   464  			}
   465  		}
   466  
   467  		if p.options.ASCIIOnly && c > lastASCII {
   468  			if c <= 0xFFFF {
   469  				p.js = append(p.js, '\\', 'u', hexChars[c>>12], hexChars[(c>>8)&15], hexChars[(c>>4)&15], hexChars[c&15])
   470  			} else if !p.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) {
   471  				p.js = append(p.js, fmt.Sprintf("\\u{%X}", c)...)
   472  			} else {
   473  				panic("Internal error: Cannot encode identifier: Unicode escapes are unsupported")
   474  			}
   475  			continue
   476  		}
   477  
   478  		width := utf8.EncodeRune(temp[:], c)
   479  		p.js = append(p.js, temp[:width]...)
   480  	}
   481  }
   482  
   483  func (p *printer) printNumber(value float64, level js_ast.L) {
   484  	absValue := math.Abs(value)
   485  
   486  	if value != value {
   487  		p.printSpaceBeforeIdentifier()
   488  		if p.withNesting != 0 {
   489  			// "with (x) NaN" really means "x.NaN" so avoid identifiers when "with" is present
   490  			wrap := level >= js_ast.LMultiply
   491  			if wrap {
   492  				p.print("(")
   493  			}
   494  			if p.options.MinifyWhitespace {
   495  				p.print("0/0")
   496  			} else {
   497  				p.print("0 / 0")
   498  			}
   499  			if wrap {
   500  				p.print(")")
   501  			}
   502  		} else {
   503  			p.print("NaN")
   504  		}
   505  	} else if value == positiveInfinity || value == negativeInfinity {
   506  		// "with (x) Infinity" really means "x.Infinity" so avoid identifiers when "with" is present
   507  		wrap := ((p.options.MinifySyntax || p.withNesting != 0) && level >= js_ast.LMultiply) ||
   508  			(value == negativeInfinity && level >= js_ast.LPrefix)
   509  		if wrap {
   510  			p.print("(")
   511  		}
   512  		if value == negativeInfinity {
   513  			p.printSpaceBeforeOperator(js_ast.UnOpNeg)
   514  			p.print("-")
   515  		} else {
   516  			p.printSpaceBeforeIdentifier()
   517  		}
   518  		if !p.options.MinifySyntax && p.withNesting == 0 {
   519  			p.print("Infinity")
   520  		} else if p.options.MinifyWhitespace {
   521  			p.print("1/0")
   522  		} else {
   523  			p.print("1 / 0")
   524  		}
   525  		if wrap {
   526  			p.print(")")
   527  		}
   528  	} else {
   529  		if !math.Signbit(value) {
   530  			p.printSpaceBeforeIdentifier()
   531  			p.printNonNegativeFloat(absValue)
   532  		} else if level >= js_ast.LPrefix {
   533  			// Expressions such as "(-1).toString" need to wrap negative numbers.
   534  			// Instead of testing for "value < 0" we test for "signbit(value)" and
   535  			// "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0"
   536  			// is false.
   537  			p.print("(-")
   538  			p.printNonNegativeFloat(absValue)
   539  			p.print(")")
   540  		} else {
   541  			p.printSpaceBeforeOperator(js_ast.UnOpNeg)
   542  			p.print("-")
   543  			p.printNonNegativeFloat(absValue)
   544  		}
   545  	}
   546  }
   547  
   548  func (p *printer) willPrintExprCommentsAtLoc(loc logger.Loc) bool {
   549  	return !p.options.MinifyWhitespace && p.exprComments[loc] != nil && !p.printedExprComments[loc]
   550  }
   551  
   552  func (p *printer) willPrintExprCommentsForAnyOf(exprs []js_ast.Expr) bool {
   553  	for _, expr := range exprs {
   554  		if p.willPrintExprCommentsAtLoc(expr.Loc) {
   555  			return true
   556  		}
   557  	}
   558  	return false
   559  }
   560  
   561  func (p *printer) printBinding(binding js_ast.Binding) {
   562  	switch b := binding.Data.(type) {
   563  	case *js_ast.BMissing:
   564  		p.addSourceMapping(binding.Loc)
   565  
   566  	case *js_ast.BIdentifier:
   567  		name := p.renamer.NameForSymbol(b.Ref)
   568  		p.printSpaceBeforeIdentifier()
   569  		p.addSourceMappingForName(binding.Loc, name, b.Ref)
   570  		p.printIdentifier(name)
   571  
   572  	case *js_ast.BArray:
   573  		isMultiLine := (len(b.Items) > 0 && !b.IsSingleLine) || p.willPrintExprCommentsAtLoc(b.CloseBracketLoc)
   574  		if !p.options.MinifyWhitespace && !isMultiLine {
   575  			for _, item := range b.Items {
   576  				if p.willPrintExprCommentsAtLoc(item.Loc) {
   577  					isMultiLine = true
   578  					break
   579  				}
   580  			}
   581  		}
   582  		p.addSourceMapping(binding.Loc)
   583  		p.print("[")
   584  		if len(b.Items) > 0 || isMultiLine {
   585  			if isMultiLine {
   586  				p.options.Indent++
   587  			}
   588  
   589  			for i, item := range b.Items {
   590  				if i != 0 {
   591  					p.print(",")
   592  				}
   593  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
   594  					if isMultiLine {
   595  						p.printNewline()
   596  						p.printIndent()
   597  					} else if i != 0 {
   598  						p.printSpace()
   599  					}
   600  				}
   601  				p.printExprCommentsAtLoc(item.Loc)
   602  				if b.HasSpread && i+1 == len(b.Items) {
   603  					p.addSourceMapping(item.Loc)
   604  					p.print("...")
   605  					p.printExprCommentsAtLoc(item.Binding.Loc)
   606  				}
   607  				p.printBinding(item.Binding)
   608  
   609  				if item.DefaultValueOrNil.Data != nil {
   610  					p.printSpace()
   611  					p.print("=")
   612  					p.printSpace()
   613  					p.printExprWithoutLeadingNewline(item.DefaultValueOrNil, js_ast.LComma, 0)
   614  				}
   615  
   616  				// Make sure there's a comma after trailing missing items
   617  				if _, ok := item.Binding.Data.(*js_ast.BMissing); ok && i == len(b.Items)-1 {
   618  					p.print(",")
   619  				}
   620  			}
   621  
   622  			if isMultiLine {
   623  				p.printNewline()
   624  				p.printExprCommentsAfterCloseTokenAtLoc(b.CloseBracketLoc)
   625  				p.options.Indent--
   626  				p.printIndent()
   627  			}
   628  		}
   629  		p.addSourceMapping(b.CloseBracketLoc)
   630  		p.print("]")
   631  
   632  	case *js_ast.BObject:
   633  		isMultiLine := (len(b.Properties) > 0 && !b.IsSingleLine) || p.willPrintExprCommentsAtLoc(b.CloseBraceLoc)
   634  		if !p.options.MinifyWhitespace && !isMultiLine {
   635  			for _, property := range b.Properties {
   636  				if p.willPrintExprCommentsAtLoc(property.Loc) {
   637  					isMultiLine = true
   638  					break
   639  				}
   640  			}
   641  		}
   642  		p.addSourceMapping(binding.Loc)
   643  		p.print("{")
   644  		if len(b.Properties) > 0 || isMultiLine {
   645  			if isMultiLine {
   646  				p.options.Indent++
   647  			}
   648  
   649  			for i, property := range b.Properties {
   650  				if i != 0 {
   651  					p.print(",")
   652  				}
   653  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
   654  					if isMultiLine {
   655  						p.printNewline()
   656  						p.printIndent()
   657  					} else {
   658  						p.printSpace()
   659  					}
   660  				}
   661  
   662  				p.printExprCommentsAtLoc(property.Loc)
   663  
   664  				if property.IsSpread {
   665  					p.addSourceMapping(property.Loc)
   666  					p.print("...")
   667  					p.printExprCommentsAtLoc(property.Value.Loc)
   668  				} else {
   669  					if property.IsComputed {
   670  						p.addSourceMapping(property.Loc)
   671  						isMultiLine := p.willPrintExprCommentsAtLoc(property.Key.Loc) || p.willPrintExprCommentsAtLoc(property.CloseBracketLoc)
   672  						p.print("[")
   673  						if isMultiLine {
   674  							p.printNewline()
   675  							p.options.Indent++
   676  							p.printIndent()
   677  						}
   678  						p.printExpr(property.Key, js_ast.LComma, 0)
   679  						if isMultiLine {
   680  							p.printNewline()
   681  							p.printExprCommentsAfterCloseTokenAtLoc(property.CloseBracketLoc)
   682  							p.options.Indent--
   683  							p.printIndent()
   684  						}
   685  						if property.CloseBracketLoc.Start > property.Loc.Start {
   686  							p.addSourceMapping(property.CloseBracketLoc)
   687  						}
   688  						p.print("]:")
   689  						p.printSpace()
   690  						p.printBinding(property.Value)
   691  
   692  						if property.DefaultValueOrNil.Data != nil {
   693  							p.printSpace()
   694  							p.print("=")
   695  							p.printSpace()
   696  							p.printExprWithoutLeadingNewline(property.DefaultValueOrNil, js_ast.LComma, 0)
   697  						}
   698  						continue
   699  					}
   700  
   701  					if str, ok := property.Key.Data.(*js_ast.EString); ok && !property.PreferQuotedKey && p.canPrintIdentifierUTF16(str.Value) {
   702  						// Use a shorthand property if the names are the same
   703  						if id, ok := property.Value.Data.(*js_ast.BIdentifier); ok &&
   704  							!p.willPrintExprCommentsAtLoc(property.Value.Loc) &&
   705  							helpers.UTF16EqualsString(str.Value, p.renamer.NameForSymbol(id.Ref)) {
   706  							if p.options.AddSourceMappings {
   707  								p.addSourceMappingForName(property.Key.Loc, helpers.UTF16ToString(str.Value), id.Ref)
   708  							}
   709  							p.printIdentifierUTF16(str.Value)
   710  							if property.DefaultValueOrNil.Data != nil {
   711  								p.printSpace()
   712  								p.print("=")
   713  								p.printSpace()
   714  								p.printExprWithoutLeadingNewline(property.DefaultValueOrNil, js_ast.LComma, 0)
   715  							}
   716  							continue
   717  						}
   718  
   719  						p.addSourceMapping(property.Key.Loc)
   720  						p.printIdentifierUTF16(str.Value)
   721  					} else if mangled, ok := property.Key.Data.(*js_ast.ENameOfSymbol); ok {
   722  						if name := p.mangledPropName(mangled.Ref); p.canPrintIdentifier(name) {
   723  							p.addSourceMappingForName(property.Key.Loc, name, mangled.Ref)
   724  							p.printIdentifier(name)
   725  
   726  							// Use a shorthand property if the names are the same
   727  							if id, ok := property.Value.Data.(*js_ast.BIdentifier); ok &&
   728  								!p.willPrintExprCommentsAtLoc(property.Value.Loc) &&
   729  								name == p.renamer.NameForSymbol(id.Ref) {
   730  								if property.DefaultValueOrNil.Data != nil {
   731  									p.printSpace()
   732  									p.print("=")
   733  									p.printSpace()
   734  									p.printExprWithoutLeadingNewline(property.DefaultValueOrNil, js_ast.LComma, 0)
   735  								}
   736  								continue
   737  							}
   738  						} else {
   739  							p.addSourceMapping(property.Key.Loc)
   740  							p.printQuotedUTF8(name, 0)
   741  						}
   742  					} else {
   743  						p.printExpr(property.Key, js_ast.LLowest, 0)
   744  					}
   745  
   746  					p.print(":")
   747  					p.printSpace()
   748  				}
   749  				p.printBinding(property.Value)
   750  
   751  				if property.DefaultValueOrNil.Data != nil {
   752  					p.printSpace()
   753  					p.print("=")
   754  					p.printSpace()
   755  					p.printExprWithoutLeadingNewline(property.DefaultValueOrNil, js_ast.LComma, 0)
   756  				}
   757  			}
   758  
   759  			if isMultiLine {
   760  				p.printNewline()
   761  				p.printExprCommentsAfterCloseTokenAtLoc(b.CloseBraceLoc)
   762  				p.options.Indent--
   763  				p.printIndent()
   764  			} else {
   765  				// This block is only reached if len(b.Properties) > 0
   766  				p.printSpace()
   767  			}
   768  		}
   769  		p.addSourceMapping(b.CloseBraceLoc)
   770  		p.print("}")
   771  
   772  	default:
   773  		panic(fmt.Sprintf("Unexpected binding of type %T", binding.Data))
   774  	}
   775  }
   776  
   777  func (p *printer) printSpace() {
   778  	if !p.options.MinifyWhitespace {
   779  		p.print(" ")
   780  	}
   781  }
   782  
   783  func (p *printer) printNewline() {
   784  	if !p.options.MinifyWhitespace {
   785  		p.print("\n")
   786  	}
   787  }
   788  
   789  func (p *printer) currentLineLength() int {
   790  	js := p.js
   791  	n := len(js)
   792  	stop := p.oldLineEnd
   793  
   794  	// Update "oldLineStart" to the start of the current line
   795  	for i := n; i > stop; i-- {
   796  		if c := js[i-1]; c == '\r' || c == '\n' {
   797  			p.oldLineStart = i
   798  			break
   799  		}
   800  	}
   801  
   802  	p.oldLineEnd = n
   803  	return n - p.oldLineStart
   804  }
   805  
   806  func (p *printer) printNewlinePastLineLimit() bool {
   807  	if p.currentLineLength() < p.options.LineLimit {
   808  		return false
   809  	}
   810  	p.print("\n")
   811  	p.printIndent()
   812  	return true
   813  }
   814  
   815  func (p *printer) printSpaceBeforeOperator(next js_ast.OpCode) {
   816  	if p.prevOpEnd == len(p.js) {
   817  		prev := p.prevOp
   818  
   819  		// "+ + y" => "+ +y"
   820  		// "+ ++ y" => "+ ++y"
   821  		// "x + + y" => "x+ +y"
   822  		// "x ++ + y" => "x+++y"
   823  		// "x + ++ y" => "x+ ++y"
   824  		// "-- >" => "-- >"
   825  		// "< ! --" => "<! --"
   826  		if ((prev == js_ast.BinOpAdd || prev == js_ast.UnOpPos) && (next == js_ast.BinOpAdd || next == js_ast.UnOpPos || next == js_ast.UnOpPreInc)) ||
   827  			((prev == js_ast.BinOpSub || prev == js_ast.UnOpNeg) && (next == js_ast.BinOpSub || next == js_ast.UnOpNeg || next == js_ast.UnOpPreDec)) ||
   828  			(prev == js_ast.UnOpPostDec && next == js_ast.BinOpGt) ||
   829  			(prev == js_ast.UnOpNot && next == js_ast.UnOpPreDec && len(p.js) > 1 && p.js[len(p.js)-2] == '<') {
   830  			p.print(" ")
   831  		}
   832  	}
   833  }
   834  
   835  func (p *printer) printSemicolonAfterStatement() {
   836  	if !p.options.MinifyWhitespace {
   837  		p.print(";\n")
   838  	} else {
   839  		p.needsSemicolon = true
   840  	}
   841  }
   842  
   843  func (p *printer) printSemicolonIfNeeded() {
   844  	if p.needsSemicolon {
   845  		p.print(";")
   846  		p.needsSemicolon = false
   847  	}
   848  }
   849  
   850  func (p *printer) printSpaceBeforeIdentifier() {
   851  	if c, _ := utf8.DecodeLastRune(p.js); js_ast.IsIdentifierContinue(c) || p.prevRegExpEnd == len(p.js) {
   852  		p.print(" ")
   853  	}
   854  }
   855  
   856  type fnArgsOpts struct {
   857  	openParenLoc              logger.Loc
   858  	addMappingForOpenParenLoc bool
   859  	hasRestArg                bool
   860  	isArrow                   bool
   861  }
   862  
   863  func (p *printer) printFnArgs(args []js_ast.Arg, opts fnArgsOpts) {
   864  	wrap := true
   865  
   866  	// Minify "(a) => {}" as "a=>{}"
   867  	if p.options.MinifyWhitespace && !opts.hasRestArg && opts.isArrow && len(args) == 1 {
   868  		if _, ok := args[0].Binding.Data.(*js_ast.BIdentifier); ok && args[0].DefaultOrNil.Data == nil {
   869  			wrap = false
   870  		}
   871  	}
   872  
   873  	if wrap {
   874  		if opts.addMappingForOpenParenLoc {
   875  			p.addSourceMapping(opts.openParenLoc)
   876  		}
   877  		p.print("(")
   878  	}
   879  
   880  	for i, arg := range args {
   881  		if i != 0 {
   882  			p.print(",")
   883  			p.printSpace()
   884  		}
   885  		p.printDecorators(arg.Decorators, printSpaceAfterDecorator)
   886  		if opts.hasRestArg && i+1 == len(args) {
   887  			p.print("...")
   888  		}
   889  		p.printBinding(arg.Binding)
   890  
   891  		if arg.DefaultOrNil.Data != nil {
   892  			p.printSpace()
   893  			p.print("=")
   894  			p.printSpace()
   895  			p.printExprWithoutLeadingNewline(arg.DefaultOrNil, js_ast.LComma, 0)
   896  		}
   897  	}
   898  
   899  	if wrap {
   900  		p.print(")")
   901  	}
   902  }
   903  
   904  func (p *printer) printFn(fn js_ast.Fn) {
   905  	p.printFnArgs(fn.Args, fnArgsOpts{hasRestArg: fn.HasRestArg})
   906  	p.printSpace()
   907  	p.printBlock(fn.Body.Loc, fn.Body.Block)
   908  }
   909  
   910  type printAfterDecorator uint8
   911  
   912  const (
   913  	printNewlineAfterDecorator printAfterDecorator = iota
   914  	printSpaceAfterDecorator
   915  )
   916  
   917  func (p *printer) printDecorators(decorators []js_ast.Decorator, defaultMode printAfterDecorator) (omitIndentAfter bool) {
   918  	oldMode := defaultMode
   919  
   920  	for _, decorator := range decorators {
   921  		wrap := false
   922  		wasCallTarget := false
   923  		expr := decorator.Value
   924  		mode := defaultMode
   925  		if decorator.OmitNewlineAfter {
   926  			mode = printSpaceAfterDecorator
   927  		}
   928  
   929  	outer:
   930  		for {
   931  			isCallTarget := wasCallTarget
   932  			wasCallTarget = false
   933  
   934  			switch e := expr.Data.(type) {
   935  			case *js_ast.EIdentifier:
   936  				// "@foo"
   937  				break outer
   938  
   939  			case *js_ast.ECall:
   940  				// "@foo()"
   941  				expr = e.Target
   942  				wasCallTarget = true
   943  				continue
   944  
   945  			case *js_ast.EDot:
   946  				// "@foo.bar"
   947  				if p.canPrintIdentifier(e.Name) {
   948  					expr = e.Target
   949  					continue
   950  				}
   951  
   952  				// "@foo.\u30FF" => "@(foo['\u30FF'])"
   953  				break
   954  
   955  			case *js_ast.EIndex:
   956  				if _, ok := e.Index.Data.(*js_ast.EPrivateIdentifier); ok {
   957  					// "@foo.#bar"
   958  					expr = e.Target
   959  					continue
   960  				}
   961  
   962  				// "@(foo[bar])"
   963  				break
   964  
   965  			case *js_ast.EImportIdentifier:
   966  				ref := ast.FollowSymbols(p.symbols, e.Ref)
   967  				symbol := p.symbols.Get(ref)
   968  
   969  				if symbol.ImportItemStatus == ast.ImportItemMissing {
   970  					// "@(void 0)"
   971  					break
   972  				}
   973  
   974  				if symbol.NamespaceAlias != nil && isCallTarget && e.WasOriginallyIdentifier {
   975  					// "@((0, import_ns.fn)())"
   976  					break
   977  				}
   978  
   979  				if value := p.options.ConstValues[ref]; value.Kind != js_ast.ConstValueNone {
   980  					// "@(<inlined constant>)"
   981  					break
   982  				}
   983  
   984  				// "@foo"
   985  				// "@import_ns.fn"
   986  				break outer
   987  
   988  			default:
   989  				// "@(foo + bar)"
   990  				// "@(() => {})"
   991  				break
   992  			}
   993  
   994  			wrap = true
   995  			break outer
   996  		}
   997  
   998  		p.addSourceMapping(decorator.AtLoc)
   999  		if oldMode == printNewlineAfterDecorator {
  1000  			p.printIndent()
  1001  		}
  1002  
  1003  		p.print("@")
  1004  		if wrap {
  1005  			p.print("(")
  1006  		}
  1007  		p.printExpr(decorator.Value, js_ast.LLowest, 0)
  1008  		if wrap {
  1009  			p.print(")")
  1010  		}
  1011  
  1012  		switch mode {
  1013  		case printNewlineAfterDecorator:
  1014  			p.printNewline()
  1015  
  1016  		case printSpaceAfterDecorator:
  1017  			p.printSpace()
  1018  		}
  1019  		oldMode = mode
  1020  	}
  1021  
  1022  	omitIndentAfter = oldMode == printSpaceAfterDecorator
  1023  	return
  1024  }
  1025  
  1026  func (p *printer) printClass(class js_ast.Class) {
  1027  	if class.ExtendsOrNil.Data != nil {
  1028  		p.print(" extends")
  1029  		p.printSpace()
  1030  		p.printExpr(class.ExtendsOrNil, js_ast.LNew-1, 0)
  1031  	}
  1032  	p.printSpace()
  1033  
  1034  	p.addSourceMapping(class.BodyLoc)
  1035  	p.print("{")
  1036  	p.printNewline()
  1037  	p.options.Indent++
  1038  
  1039  	for _, item := range class.Properties {
  1040  		p.printSemicolonIfNeeded()
  1041  		omitIndent := p.printDecorators(item.Decorators, printNewlineAfterDecorator)
  1042  		if !omitIndent {
  1043  			p.printIndent()
  1044  		}
  1045  
  1046  		if item.Kind == js_ast.PropertyClassStaticBlock {
  1047  			p.addSourceMapping(item.Loc)
  1048  			p.print("static")
  1049  			p.printSpace()
  1050  			p.printBlock(item.ClassStaticBlock.Loc, item.ClassStaticBlock.Block)
  1051  			p.printNewline()
  1052  			continue
  1053  		}
  1054  
  1055  		p.printProperty(item)
  1056  
  1057  		// Need semicolons after class fields
  1058  		if item.ValueOrNil.Data == nil {
  1059  			p.printSemicolonAfterStatement()
  1060  		} else {
  1061  			p.printNewline()
  1062  		}
  1063  	}
  1064  
  1065  	p.needsSemicolon = false
  1066  	p.printExprCommentsAfterCloseTokenAtLoc(class.CloseBraceLoc)
  1067  	p.options.Indent--
  1068  	p.printIndent()
  1069  	if class.CloseBraceLoc.Start > class.BodyLoc.Start {
  1070  		p.addSourceMapping(class.CloseBraceLoc)
  1071  	}
  1072  	p.print("}")
  1073  }
  1074  
  1075  func (p *printer) printProperty(property js_ast.Property) {
  1076  	p.printExprCommentsAtLoc(property.Loc)
  1077  
  1078  	if property.Kind == js_ast.PropertySpread {
  1079  		p.addSourceMapping(property.Loc)
  1080  		p.print("...")
  1081  		p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
  1082  		return
  1083  	}
  1084  
  1085  	// Handle key syntax compression for cross-module constant inlining of enums
  1086  	if p.options.MinifySyntax && property.Flags.Has(js_ast.PropertyIsComputed) {
  1087  		if dot, ok := property.Key.Data.(*js_ast.EDot); ok {
  1088  			if value, ok := p.tryToGetImportedEnumValue(dot.Target, dot.Name); ok {
  1089  				if value.String != nil {
  1090  					property.Key.Data = &js_ast.EString{Value: value.String}
  1091  
  1092  					// Problematic key names must stay computed for correctness
  1093  					if !helpers.UTF16EqualsString(value.String, "__proto__") &&
  1094  						!helpers.UTF16EqualsString(value.String, "constructor") &&
  1095  						!helpers.UTF16EqualsString(value.String, "prototype") {
  1096  						property.Flags &= ^js_ast.PropertyIsComputed
  1097  					}
  1098  				} else {
  1099  					property.Key.Data = &js_ast.ENumber{Value: value.Number}
  1100  					property.Flags &= ^js_ast.PropertyIsComputed
  1101  				}
  1102  			}
  1103  		}
  1104  	}
  1105  
  1106  	if property.Flags.Has(js_ast.PropertyIsStatic) {
  1107  		p.printSpaceBeforeIdentifier()
  1108  		p.addSourceMapping(property.Loc)
  1109  		p.print("static")
  1110  		p.printSpace()
  1111  	}
  1112  
  1113  	switch property.Kind {
  1114  	case js_ast.PropertyGetter:
  1115  		p.printSpaceBeforeIdentifier()
  1116  		p.addSourceMapping(property.Loc)
  1117  		p.print("get")
  1118  		p.printSpace()
  1119  
  1120  	case js_ast.PropertySetter:
  1121  		p.printSpaceBeforeIdentifier()
  1122  		p.addSourceMapping(property.Loc)
  1123  		p.print("set")
  1124  		p.printSpace()
  1125  
  1126  	case js_ast.PropertyAutoAccessor:
  1127  		p.printSpaceBeforeIdentifier()
  1128  		p.addSourceMapping(property.Loc)
  1129  		p.print("accessor")
  1130  		p.printSpace()
  1131  	}
  1132  
  1133  	if fn, ok := property.ValueOrNil.Data.(*js_ast.EFunction); property.Kind.IsMethodDefinition() && ok {
  1134  		if fn.Fn.IsAsync {
  1135  			p.printSpaceBeforeIdentifier()
  1136  			p.addSourceMapping(property.Loc)
  1137  			p.print("async")
  1138  			p.printSpace()
  1139  		}
  1140  		if fn.Fn.IsGenerator {
  1141  			p.addSourceMapping(property.Loc)
  1142  			p.print("*")
  1143  		}
  1144  	}
  1145  
  1146  	isComputed := property.Flags.Has(js_ast.PropertyIsComputed)
  1147  
  1148  	// Automatically print numbers that would cause a syntax error as computed properties
  1149  	if !isComputed {
  1150  		if key, ok := property.Key.Data.(*js_ast.ENumber); ok {
  1151  			if math.Signbit(key.Value) || (key.Value == positiveInfinity && p.options.MinifySyntax) {
  1152  				// "{ -1: 0 }" must be printed as "{ [-1]: 0 }"
  1153  				// "{ 1/0: 0 }" must be printed as "{ [1/0]: 0 }"
  1154  				isComputed = true
  1155  			}
  1156  		}
  1157  	}
  1158  
  1159  	if isComputed {
  1160  		p.addSourceMapping(property.Loc)
  1161  		isMultiLine := p.willPrintExprCommentsAtLoc(property.Key.Loc) || p.willPrintExprCommentsAtLoc(property.CloseBracketLoc)
  1162  		p.print("[")
  1163  		if isMultiLine {
  1164  			p.printNewline()
  1165  			p.options.Indent++
  1166  			p.printIndent()
  1167  		}
  1168  		p.printExpr(property.Key, js_ast.LComma, 0)
  1169  		if isMultiLine {
  1170  			p.printNewline()
  1171  			p.printExprCommentsAfterCloseTokenAtLoc(property.CloseBracketLoc)
  1172  			p.options.Indent--
  1173  			p.printIndent()
  1174  		}
  1175  		if property.CloseBracketLoc.Start > property.Loc.Start {
  1176  			p.addSourceMapping(property.CloseBracketLoc)
  1177  		}
  1178  		p.print("]")
  1179  
  1180  		if property.ValueOrNil.Data != nil {
  1181  			if fn, ok := property.ValueOrNil.Data.(*js_ast.EFunction); property.Kind.IsMethodDefinition() && ok {
  1182  				p.printFn(fn.Fn)
  1183  				return
  1184  			}
  1185  
  1186  			p.print(":")
  1187  			p.printSpace()
  1188  			p.printExprWithoutLeadingNewline(property.ValueOrNil, js_ast.LComma, 0)
  1189  		}
  1190  
  1191  		if property.InitializerOrNil.Data != nil {
  1192  			p.printSpace()
  1193  			p.print("=")
  1194  			p.printSpace()
  1195  			p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1196  		}
  1197  		return
  1198  	}
  1199  
  1200  	switch key := property.Key.Data.(type) {
  1201  	case *js_ast.EPrivateIdentifier:
  1202  		name := p.renamer.NameForSymbol(key.Ref)
  1203  		p.addSourceMappingForName(property.Key.Loc, name, key.Ref)
  1204  		p.printIdentifier(name)
  1205  
  1206  	case *js_ast.ENameOfSymbol:
  1207  		if name := p.mangledPropName(key.Ref); p.canPrintIdentifier(name) {
  1208  			p.printSpaceBeforeIdentifier()
  1209  			p.addSourceMappingForName(property.Key.Loc, name, key.Ref)
  1210  			p.printIdentifier(name)
  1211  
  1212  			// Use a shorthand property if the names are the same
  1213  			if !p.options.UnsupportedFeatures.Has(compat.ObjectExtensions) && property.ValueOrNil.Data != nil && !p.willPrintExprCommentsAtLoc(property.ValueOrNil.Loc) {
  1214  				switch e := property.ValueOrNil.Data.(type) {
  1215  				case *js_ast.EIdentifier:
  1216  					if name == p.renamer.NameForSymbol(e.Ref) {
  1217  						if property.InitializerOrNil.Data != nil {
  1218  							p.printSpace()
  1219  							p.print("=")
  1220  							p.printSpace()
  1221  							p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1222  						}
  1223  						return
  1224  					}
  1225  
  1226  				case *js_ast.EImportIdentifier:
  1227  					// Make sure we're not using a property access instead of an identifier
  1228  					ref := ast.FollowSymbols(p.symbols, e.Ref)
  1229  					if symbol := p.symbols.Get(ref); symbol.NamespaceAlias == nil && name == p.renamer.NameForSymbol(ref) &&
  1230  						p.options.ConstValues[ref].Kind == js_ast.ConstValueNone {
  1231  						if property.InitializerOrNil.Data != nil {
  1232  							p.printSpace()
  1233  							p.print("=")
  1234  							p.printSpace()
  1235  							p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1236  						}
  1237  						return
  1238  					}
  1239  				}
  1240  			}
  1241  		} else {
  1242  			p.addSourceMapping(property.Key.Loc)
  1243  			p.printQuotedUTF8(name, 0)
  1244  		}
  1245  
  1246  	case *js_ast.EString:
  1247  		if !property.Flags.Has(js_ast.PropertyPreferQuotedKey) && p.canPrintIdentifierUTF16(key.Value) {
  1248  			p.printSpaceBeforeIdentifier()
  1249  
  1250  			// Use a shorthand property if the names are the same
  1251  			if !p.options.UnsupportedFeatures.Has(compat.ObjectExtensions) && property.ValueOrNil.Data != nil && !p.willPrintExprCommentsAtLoc(property.ValueOrNil.Loc) {
  1252  				switch e := property.ValueOrNil.Data.(type) {
  1253  				case *js_ast.EIdentifier:
  1254  					if canUseShorthandProperty(key.Value, p.renamer.NameForSymbol(e.Ref), property.Flags) {
  1255  						if p.options.AddSourceMappings {
  1256  							p.addSourceMappingForName(property.Key.Loc, helpers.UTF16ToString(key.Value), e.Ref)
  1257  						}
  1258  						p.printIdentifierUTF16(key.Value)
  1259  						if property.InitializerOrNil.Data != nil {
  1260  							p.printSpace()
  1261  							p.print("=")
  1262  							p.printSpace()
  1263  							p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1264  						}
  1265  						return
  1266  					}
  1267  
  1268  				case *js_ast.EImportIdentifier:
  1269  					// Make sure we're not using a property access instead of an identifier
  1270  					ref := ast.FollowSymbols(p.symbols, e.Ref)
  1271  					if symbol := p.symbols.Get(ref); symbol.NamespaceAlias == nil && canUseShorthandProperty(key.Value, p.renamer.NameForSymbol(ref), property.Flags) &&
  1272  						p.options.ConstValues[ref].Kind == js_ast.ConstValueNone {
  1273  						if p.options.AddSourceMappings {
  1274  							p.addSourceMappingForName(property.Key.Loc, helpers.UTF16ToString(key.Value), ref)
  1275  						}
  1276  						p.printIdentifierUTF16(key.Value)
  1277  						if property.InitializerOrNil.Data != nil {
  1278  							p.printSpace()
  1279  							p.print("=")
  1280  							p.printSpace()
  1281  							p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1282  						}
  1283  						return
  1284  					}
  1285  				}
  1286  			}
  1287  
  1288  			// The JavaScript specification special-cases the property identifier
  1289  			// "__proto__" with a colon after it to set the prototype of the object.
  1290  			// If we keep the identifier but add a colon then we'll cause a behavior
  1291  			// change because the prototype will now be set. Avoid using an identifier
  1292  			// by using a computed property with a string instead. For more info see:
  1293  			// https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation
  1294  			if property.Flags.Has(js_ast.PropertyWasShorthand) && !p.options.UnsupportedFeatures.Has(compat.ObjectExtensions) &&
  1295  				helpers.UTF16EqualsString(key.Value, "__proto__") {
  1296  				p.print("[")
  1297  				p.addSourceMapping(property.Key.Loc)
  1298  				p.printQuotedUTF16(key.Value, 0)
  1299  				p.print("]")
  1300  				break
  1301  			}
  1302  
  1303  			p.addSourceMapping(property.Key.Loc)
  1304  			p.printIdentifierUTF16(key.Value)
  1305  		} else {
  1306  			p.addSourceMapping(property.Key.Loc)
  1307  			p.printQuotedUTF16(key.Value, 0)
  1308  		}
  1309  
  1310  	default:
  1311  		p.printExpr(property.Key, js_ast.LLowest, 0)
  1312  	}
  1313  
  1314  	if fn, ok := property.ValueOrNil.Data.(*js_ast.EFunction); property.Kind.IsMethodDefinition() && ok {
  1315  		p.printFn(fn.Fn)
  1316  		return
  1317  	}
  1318  
  1319  	if property.ValueOrNil.Data != nil {
  1320  		p.print(":")
  1321  		p.printSpace()
  1322  		p.printExprWithoutLeadingNewline(property.ValueOrNil, js_ast.LComma, 0)
  1323  	}
  1324  
  1325  	if property.InitializerOrNil.Data != nil {
  1326  		p.printSpace()
  1327  		p.print("=")
  1328  		p.printSpace()
  1329  		p.printExprWithoutLeadingNewline(property.InitializerOrNil, js_ast.LComma, 0)
  1330  	}
  1331  }
  1332  
  1333  func canUseShorthandProperty(key []uint16, name string, flags js_ast.PropertyFlags) bool {
  1334  	// The JavaScript specification special-cases the property identifier
  1335  	// "__proto__" with a colon after it to set the prototype of the object. If
  1336  	// we remove the colon then we'll cause a behavior change because the
  1337  	// prototype will no longer be set, but we also don't want to add a colon
  1338  	// if it was omitted. Always use a shorthand property if the property is not
  1339  	// "__proto__", otherwise try to preserve the original shorthand status. See:
  1340  	// https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation
  1341  	if !helpers.UTF16EqualsString(key, name) {
  1342  		return false
  1343  	}
  1344  	return helpers.UTF16EqualsString(key, name) && (name != "__proto__" || flags.Has(js_ast.PropertyWasShorthand))
  1345  }
  1346  
  1347  func (p *printer) printQuotedUTF16(data []uint16, flags printQuotedFlags) {
  1348  	if p.options.UnsupportedFeatures.Has(compat.TemplateLiteral) {
  1349  		flags &= ^printQuotedAllowBacktick
  1350  	}
  1351  
  1352  	singleCost := 0
  1353  	doubleCost := 0
  1354  	backtickCost := 0
  1355  
  1356  	for i, c := range data {
  1357  		switch c {
  1358  		case '\n':
  1359  			if p.options.MinifySyntax {
  1360  				// The backslash for the newline costs an extra character for old-style
  1361  				// string literals when compared to a template literal
  1362  				backtickCost--
  1363  			}
  1364  		case '\'':
  1365  			singleCost++
  1366  		case '"':
  1367  			doubleCost++
  1368  		case '`':
  1369  			backtickCost++
  1370  		case '$':
  1371  			// "${" sequences need to be escaped in template literals
  1372  			if i+1 < len(data) && data[i+1] == '{' {
  1373  				backtickCost++
  1374  			}
  1375  		}
  1376  	}
  1377  
  1378  	c := "\""
  1379  	if doubleCost > singleCost {
  1380  		c = "'"
  1381  		if singleCost > backtickCost && (flags&printQuotedAllowBacktick) != 0 {
  1382  			c = "`"
  1383  		}
  1384  	} else if doubleCost > backtickCost && (flags&printQuotedAllowBacktick) != 0 {
  1385  		c = "`"
  1386  	}
  1387  
  1388  	p.print(c)
  1389  	p.printUnquotedUTF16(data, rune(c[0]), flags)
  1390  	p.print(c)
  1391  }
  1392  
  1393  func (p *printer) printRequireOrImportExpr(importRecordIndex uint32, level js_ast.L, flags printExprFlags, closeParenLoc logger.Loc) {
  1394  	record := &p.importRecords[importRecordIndex]
  1395  
  1396  	if level >= js_ast.LNew || (flags&forbidCall) != 0 {
  1397  		p.print("(")
  1398  		defer p.print(")")
  1399  		level = js_ast.LLowest
  1400  	}
  1401  
  1402  	if !record.SourceIndex.IsValid() {
  1403  		// External "require()"
  1404  		if record.Kind != ast.ImportDynamic {
  1405  			// Wrap this with a call to "__toESM()" if this is a CommonJS file
  1406  			wrapWithToESM := record.Flags.Has(ast.WrapWithToESM)
  1407  			if wrapWithToESM {
  1408  				p.printSpaceBeforeIdentifier()
  1409  				p.printIdentifier(p.renamer.NameForSymbol(p.options.ToESMRef))
  1410  				p.print("(")
  1411  			}
  1412  
  1413  			// Potentially substitute our own "__require" stub for "require"
  1414  			p.printSpaceBeforeIdentifier()
  1415  			if record.Flags.Has(ast.CallRuntimeRequire) {
  1416  				p.printIdentifier(p.renamer.NameForSymbol(p.options.RuntimeRequireRef))
  1417  			} else {
  1418  				p.print("require")
  1419  			}
  1420  
  1421  			isMultiLine := p.willPrintExprCommentsAtLoc(record.Range.Loc) || p.willPrintExprCommentsAtLoc(closeParenLoc)
  1422  			p.print("(")
  1423  			if isMultiLine {
  1424  				p.printNewline()
  1425  				p.options.Indent++
  1426  				p.printIndent()
  1427  			}
  1428  			p.printExprCommentsAtLoc(record.Range.Loc)
  1429  			p.printPath(importRecordIndex, ast.ImportRequire)
  1430  			if isMultiLine {
  1431  				p.printNewline()
  1432  				p.printExprCommentsAfterCloseTokenAtLoc(closeParenLoc)
  1433  				p.options.Indent--
  1434  				p.printIndent()
  1435  			}
  1436  			if closeParenLoc.Start > record.Range.Loc.Start {
  1437  				p.addSourceMapping(closeParenLoc)
  1438  			}
  1439  			p.print(")")
  1440  
  1441  			// Finish the call to "__toESM()"
  1442  			if wrapWithToESM {
  1443  				if p.moduleType.IsESM() {
  1444  					p.print(",")
  1445  					p.printSpace()
  1446  					p.print("1")
  1447  				}
  1448  				p.print(")")
  1449  			}
  1450  			return
  1451  		}
  1452  
  1453  		// External "import()"
  1454  		kind := ast.ImportDynamic
  1455  		if !p.options.UnsupportedFeatures.Has(compat.DynamicImport) {
  1456  			p.printSpaceBeforeIdentifier()
  1457  			p.print("import(")
  1458  		} else {
  1459  			kind = ast.ImportRequire
  1460  			p.printSpaceBeforeIdentifier()
  1461  			p.print("Promise.resolve()")
  1462  			p.printDotThenPrefix()
  1463  			defer p.printDotThenSuffix()
  1464  
  1465  			// Wrap this with a call to "__toESM()" if this is a CommonJS file
  1466  			if record.Flags.Has(ast.WrapWithToESM) {
  1467  				p.printSpaceBeforeIdentifier()
  1468  				p.printIdentifier(p.renamer.NameForSymbol(p.options.ToESMRef))
  1469  				p.print("(")
  1470  				defer func() {
  1471  					if p.moduleType.IsESM() {
  1472  						p.print(",")
  1473  						p.printSpace()
  1474  						p.print("1")
  1475  					}
  1476  					p.print(")")
  1477  				}()
  1478  			}
  1479  
  1480  			// Potentially substitute our own "__require" stub for "require"
  1481  			p.printSpaceBeforeIdentifier()
  1482  			if record.Flags.Has(ast.CallRuntimeRequire) {
  1483  				p.printIdentifier(p.renamer.NameForSymbol(p.options.RuntimeRequireRef))
  1484  			} else {
  1485  				p.print("require")
  1486  			}
  1487  
  1488  			p.print("(")
  1489  		}
  1490  		isMultiLine := p.willPrintExprCommentsAtLoc(record.Range.Loc) ||
  1491  			p.willPrintExprCommentsAtLoc(closeParenLoc) ||
  1492  			(record.AssertOrWith != nil &&
  1493  				!p.options.UnsupportedFeatures.Has(compat.DynamicImport) &&
  1494  				(!p.options.UnsupportedFeatures.Has(compat.ImportAssertions) ||
  1495  					!p.options.UnsupportedFeatures.Has(compat.ImportAttributes)) &&
  1496  				p.willPrintExprCommentsAtLoc(record.AssertOrWith.OuterOpenBraceLoc))
  1497  		if isMultiLine {
  1498  			p.printNewline()
  1499  			p.options.Indent++
  1500  			p.printIndent()
  1501  		}
  1502  		p.printExprCommentsAtLoc(record.Range.Loc)
  1503  		p.printPath(importRecordIndex, kind)
  1504  		if !p.options.UnsupportedFeatures.Has(compat.DynamicImport) {
  1505  			p.printImportCallAssertOrWith(record.AssertOrWith, isMultiLine)
  1506  		}
  1507  		if isMultiLine {
  1508  			p.printNewline()
  1509  			p.printExprCommentsAfterCloseTokenAtLoc(closeParenLoc)
  1510  			p.options.Indent--
  1511  			p.printIndent()
  1512  		}
  1513  		if closeParenLoc.Start > record.Range.Loc.Start {
  1514  			p.addSourceMapping(closeParenLoc)
  1515  		}
  1516  		p.print(")")
  1517  		return
  1518  	}
  1519  
  1520  	meta := p.options.RequireOrImportMetaForSource(record.SourceIndex.GetIndex())
  1521  
  1522  	// Don't need the namespace object if the result is unused anyway
  1523  	if (flags & exprResultIsUnused) != 0 {
  1524  		meta.ExportsRef = ast.InvalidRef
  1525  	}
  1526  
  1527  	// Internal "import()" of async ESM
  1528  	if record.Kind == ast.ImportDynamic && meta.IsWrapperAsync {
  1529  		p.printSpaceBeforeIdentifier()
  1530  		p.printIdentifier(p.renamer.NameForSymbol(meta.WrapperRef))
  1531  		p.print("()")
  1532  		if meta.ExportsRef != ast.InvalidRef {
  1533  			p.printDotThenPrefix()
  1534  			p.printSpaceBeforeIdentifier()
  1535  			p.printIdentifier(p.renamer.NameForSymbol(meta.ExportsRef))
  1536  			p.printDotThenSuffix()
  1537  		}
  1538  		return
  1539  	}
  1540  
  1541  	// Internal "require()" or "import()"
  1542  	if record.Kind == ast.ImportDynamic {
  1543  		p.printSpaceBeforeIdentifier()
  1544  		p.print("Promise.resolve()")
  1545  		level = p.printDotThenPrefix()
  1546  		defer p.printDotThenSuffix()
  1547  	}
  1548  
  1549  	// Make sure the comma operator is properly wrapped
  1550  	if meta.ExportsRef != ast.InvalidRef && level >= js_ast.LComma {
  1551  		p.print("(")
  1552  		defer p.print(")")
  1553  	}
  1554  
  1555  	// Wrap this with a call to "__toESM()" if this is a CommonJS file
  1556  	wrapWithToESM := record.Flags.Has(ast.WrapWithToESM)
  1557  	if wrapWithToESM {
  1558  		p.printSpaceBeforeIdentifier()
  1559  		p.printIdentifier(p.renamer.NameForSymbol(p.options.ToESMRef))
  1560  		p.print("(")
  1561  	}
  1562  
  1563  	// Call the wrapper
  1564  	p.printSpaceBeforeIdentifier()
  1565  	p.printIdentifier(p.renamer.NameForSymbol(meta.WrapperRef))
  1566  	p.print("()")
  1567  
  1568  	// Return the namespace object if this is an ESM file
  1569  	if meta.ExportsRef != ast.InvalidRef {
  1570  		p.print(",")
  1571  		p.printSpace()
  1572  
  1573  		// Wrap this with a call to "__toCommonJS()" if this is an ESM file
  1574  		wrapWithTpCJS := record.Flags.Has(ast.WrapWithToCJS)
  1575  		if wrapWithTpCJS {
  1576  			p.printIdentifier(p.renamer.NameForSymbol(p.options.ToCommonJSRef))
  1577  			p.print("(")
  1578  		}
  1579  		p.printIdentifier(p.renamer.NameForSymbol(meta.ExportsRef))
  1580  		if wrapWithTpCJS {
  1581  			p.print(")")
  1582  		}
  1583  	}
  1584  
  1585  	// Finish the call to "__toESM()"
  1586  	if wrapWithToESM {
  1587  		if p.moduleType.IsESM() {
  1588  			p.print(",")
  1589  			p.printSpace()
  1590  			p.print("1")
  1591  		}
  1592  		p.print(")")
  1593  	}
  1594  }
  1595  
  1596  func (p *printer) printDotThenPrefix() js_ast.L {
  1597  	if p.options.UnsupportedFeatures.Has(compat.Arrow) {
  1598  		p.print(".then(function()")
  1599  		p.printSpace()
  1600  		p.print("{")
  1601  		p.printNewline()
  1602  		p.options.Indent++
  1603  		p.printIndent()
  1604  		p.print("return")
  1605  		p.printSpace()
  1606  		return js_ast.LLowest
  1607  	} else {
  1608  		p.print(".then(()")
  1609  		p.printSpace()
  1610  		p.print("=>")
  1611  		p.printSpace()
  1612  		return js_ast.LComma
  1613  	}
  1614  }
  1615  
  1616  func (p *printer) printDotThenSuffix() {
  1617  	if p.options.UnsupportedFeatures.Has(compat.Arrow) {
  1618  		if !p.options.MinifyWhitespace {
  1619  			p.print(";")
  1620  		}
  1621  		p.printNewline()
  1622  		p.options.Indent--
  1623  		p.printIndent()
  1624  		p.print("})")
  1625  	} else {
  1626  		p.print(")")
  1627  	}
  1628  }
  1629  
  1630  func (p *printer) printUndefined(loc logger.Loc, level js_ast.L) {
  1631  	if level >= js_ast.LPrefix {
  1632  		p.addSourceMapping(loc)
  1633  		p.print("(void 0)")
  1634  	} else {
  1635  		p.printSpaceBeforeIdentifier()
  1636  		p.addSourceMapping(loc)
  1637  		p.print("void 0")
  1638  	}
  1639  }
  1640  
  1641  // Call this before printing an expression to see if it turned out to be empty.
  1642  // We use this to do inlining of empty functions at print time. It can't happen
  1643  // during parse time because a) parse time only has two passes and we only know
  1644  // if a function can be inlined at the end of the second pass (due to is-mutated
  1645  // analysis) and b) we want to enable cross-module inlining of empty functions
  1646  // which has to happen after linking.
  1647  //
  1648  // This function returns "nil" to indicate that the expression should be removed
  1649  // completely.
  1650  //
  1651  // This function doesn't need to search everywhere inside the entire expression
  1652  // for calls to inline. Calls are automatically inlined when printed. However,
  1653  // the printer replaces the call with "undefined" since the result may still
  1654  // be needed by the caller. If the caller knows that it doesn't need the result,
  1655  // it should call this function first instead so we don't print "undefined".
  1656  //
  1657  // This is a separate function instead of trying to work this logic into the
  1658  // printer because it's too late to eliminate the expression entirely when we're
  1659  // in the printer. We may have already printed the leading indent, for example.
  1660  func (p *printer) simplifyUnusedExpr(expr js_ast.Expr) js_ast.Expr {
  1661  	switch e := expr.Data.(type) {
  1662  	case *js_ast.EBinary:
  1663  		// Calls to be inlined may be hidden inside a comma operator chain
  1664  		if e.Op == js_ast.BinOpComma {
  1665  			left := p.simplifyUnusedExpr(e.Left)
  1666  			right := p.simplifyUnusedExpr(e.Right)
  1667  			if left.Data != e.Left.Data || right.Data != e.Right.Data {
  1668  				return js_ast.JoinWithComma(left, right)
  1669  			}
  1670  		}
  1671  
  1672  	case *js_ast.ECall:
  1673  		var symbolFlags ast.SymbolFlags
  1674  		switch target := e.Target.Data.(type) {
  1675  		case *js_ast.EIdentifier:
  1676  			symbolFlags = p.symbols.Get(target.Ref).Flags
  1677  		case *js_ast.EImportIdentifier:
  1678  			ref := ast.FollowSymbols(p.symbols, target.Ref)
  1679  			symbolFlags = p.symbols.Get(ref).Flags
  1680  		}
  1681  
  1682  		// Replace non-mutated empty functions with their arguments at print time
  1683  		if (symbolFlags & (ast.IsEmptyFunction | ast.CouldPotentiallyBeMutated)) == ast.IsEmptyFunction {
  1684  			var replacement js_ast.Expr
  1685  			for _, arg := range e.Args {
  1686  				if _, ok := arg.Data.(*js_ast.ESpread); ok {
  1687  					arg.Data = &js_ast.EArray{Items: []js_ast.Expr{arg}, IsSingleLine: true}
  1688  				}
  1689  				replacement = js_ast.JoinWithComma(replacement, p.astHelpers.SimplifyUnusedExpr(p.simplifyUnusedExpr(arg), p.options.UnsupportedFeatures))
  1690  			}
  1691  			return replacement // Don't add "undefined" here because the result isn't used
  1692  		}
  1693  
  1694  		// Inline non-mutated identity functions at print time
  1695  		if (symbolFlags&(ast.IsIdentityFunction|ast.CouldPotentiallyBeMutated)) == ast.IsIdentityFunction && len(e.Args) == 1 {
  1696  			arg := e.Args[0]
  1697  			if _, ok := arg.Data.(*js_ast.ESpread); !ok {
  1698  				return p.astHelpers.SimplifyUnusedExpr(p.simplifyUnusedExpr(arg), p.options.UnsupportedFeatures)
  1699  			}
  1700  		}
  1701  	}
  1702  
  1703  	return expr
  1704  }
  1705  
  1706  // This assumes the original expression was some form of indirect value, such
  1707  // as a value returned from a function call or the result of a comma operator.
  1708  // In this case, there is no special behavior with the "delete" operator or
  1709  // with function calls. If we substitute this indirect value for another value
  1710  // due to inlining, we have to make sure we don't accidentally introduce special
  1711  // behavior.
  1712  func (p *printer) guardAgainstBehaviorChangeDueToSubstitution(expr js_ast.Expr, flags printExprFlags) js_ast.Expr {
  1713  	wrap := false
  1714  
  1715  	if (flags & isDeleteTarget) != 0 {
  1716  		// "delete id(x)" must not become "delete x"
  1717  		// "delete (empty(), x)" must not become "delete x"
  1718  		if binary, ok := expr.Data.(*js_ast.EBinary); !ok || binary.Op != js_ast.BinOpComma {
  1719  			wrap = true
  1720  		}
  1721  	} else if (flags & isCallTargetOrTemplateTag) != 0 {
  1722  		// "id(x.y)()" must not become "x.y()"
  1723  		// "id(x.y)``" must not become "x.y``"
  1724  		// "(empty(), x.y)()" must not become "x.y()"
  1725  		// "(empty(), eval)()" must not become "eval()"
  1726  		switch expr.Data.(type) {
  1727  		case *js_ast.EDot, *js_ast.EIndex:
  1728  			wrap = true
  1729  		case *js_ast.EIdentifier:
  1730  			if p.isUnboundEvalIdentifier(expr) {
  1731  				wrap = true
  1732  			}
  1733  		}
  1734  	}
  1735  
  1736  	if wrap {
  1737  		expr.Data = &js_ast.EBinary{
  1738  			Op:    js_ast.BinOpComma,
  1739  			Left:  js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: 0}},
  1740  			Right: expr,
  1741  		}
  1742  	}
  1743  
  1744  	return expr
  1745  }
  1746  
  1747  // Constant folding is already implemented once in the parser. A smaller form
  1748  // of constant folding (just for numbers) is implemented here to clean up cross-
  1749  // module numeric constants and bitwise operations. This is not an general-
  1750  // purpose/optimal approach and never will be. For example, we can't affect
  1751  // tree shaking at this stage because it has already happened.
  1752  func (p *printer) lateConstantFoldUnaryOrBinaryOrIfExpr(expr js_ast.Expr) js_ast.Expr {
  1753  	switch e := expr.Data.(type) {
  1754  	case *js_ast.EImportIdentifier:
  1755  		ref := ast.FollowSymbols(p.symbols, e.Ref)
  1756  		if value := p.options.ConstValues[ref]; value.Kind != js_ast.ConstValueNone {
  1757  			return js_ast.ConstValueToExpr(expr.Loc, value)
  1758  		}
  1759  
  1760  	case *js_ast.EDot:
  1761  		if value, ok := p.tryToGetImportedEnumValue(e.Target, e.Name); ok {
  1762  			var inlinedValue js_ast.Expr
  1763  			if value.String != nil {
  1764  				inlinedValue = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.EString{Value: value.String}}
  1765  			} else {
  1766  				inlinedValue = js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: value.Number}}
  1767  			}
  1768  
  1769  			if strings.Contains(e.Name, "*/") {
  1770  				// Don't wrap with a comment
  1771  				return inlinedValue
  1772  			}
  1773  
  1774  			// Wrap with a comment
  1775  			return js_ast.Expr{Loc: inlinedValue.Loc, Data: &js_ast.EInlinedEnum{
  1776  				Value:   inlinedValue,
  1777  				Comment: e.Name,
  1778  			}}
  1779  		}
  1780  
  1781  	case *js_ast.EUnary:
  1782  		value := p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.Value)
  1783  
  1784  		// Only fold again if something chained
  1785  		if value.Data != e.Value.Data {
  1786  			// Only fold certain operations (just like the parser)
  1787  			if v, ok := js_ast.ToNumberWithoutSideEffects(value.Data); ok {
  1788  				switch e.Op {
  1789  				case js_ast.UnOpPos:
  1790  					return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: v}}
  1791  
  1792  				case js_ast.UnOpNeg:
  1793  					return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: -v}}
  1794  
  1795  				case js_ast.UnOpCpl:
  1796  					return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ENumber{Value: float64(^js_ast.ToInt32(v))}}
  1797  				}
  1798  			}
  1799  
  1800  			// Don't mutate the original AST
  1801  			expr.Data = &js_ast.EUnary{Op: e.Op, Value: value}
  1802  		}
  1803  
  1804  	case *js_ast.EBinary:
  1805  		left := p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.Left)
  1806  		right := p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.Right)
  1807  
  1808  		// Only fold again if something changed
  1809  		if left.Data != e.Left.Data || right.Data != e.Right.Data {
  1810  			binary := &js_ast.EBinary{Op: e.Op, Left: left, Right: right}
  1811  
  1812  			// Only fold certain operations (just like the parser)
  1813  			if js_ast.ShouldFoldBinaryOperatorWhenMinifying(binary) {
  1814  				if result := js_ast.FoldBinaryOperator(expr.Loc, binary); result.Data != nil {
  1815  					return result
  1816  				}
  1817  			}
  1818  
  1819  			// Don't mutate the original AST
  1820  			expr.Data = binary
  1821  		}
  1822  
  1823  	case *js_ast.EIf:
  1824  		test := p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.Test)
  1825  
  1826  		// Only fold again if something changed
  1827  		if test.Data != e.Test.Data {
  1828  			if boolean, sideEffects, ok := js_ast.ToBooleanWithSideEffects(test.Data); ok && sideEffects == js_ast.NoSideEffects {
  1829  				if boolean {
  1830  					return p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.Yes)
  1831  				} else {
  1832  					return p.lateConstantFoldUnaryOrBinaryOrIfExpr(e.No)
  1833  				}
  1834  			}
  1835  
  1836  			// Don't mutate the original AST
  1837  			expr.Data = &js_ast.EIf{Test: test, Yes: e.Yes, No: e.No}
  1838  		}
  1839  	}
  1840  
  1841  	return expr
  1842  }
  1843  
  1844  func (p *printer) isUnboundIdentifier(expr js_ast.Expr) bool {
  1845  	id, ok := expr.Data.(*js_ast.EIdentifier)
  1846  	return ok && p.symbols.Get(ast.FollowSymbols(p.symbols, id.Ref)).Kind == ast.SymbolUnbound
  1847  }
  1848  
  1849  func (p *printer) isIdentifierOrNumericConstantOrPropertyAccess(expr js_ast.Expr) bool {
  1850  	switch e := expr.Data.(type) {
  1851  	case *js_ast.EIdentifier, *js_ast.EDot, *js_ast.EIndex:
  1852  		return true
  1853  	case *js_ast.ENumber:
  1854  		return math.IsInf(e.Value, 1) || math.IsNaN(e.Value)
  1855  	}
  1856  	return false
  1857  }
  1858  
  1859  type exprStartFlags uint8
  1860  
  1861  const (
  1862  	stmtStartFlag exprStartFlags = 1 << iota
  1863  	exportDefaultStartFlag
  1864  	arrowExprStartFlag
  1865  	forOfInitStartFlag
  1866  )
  1867  
  1868  func (p *printer) saveExprStartFlags() (flags exprStartFlags) {
  1869  	n := len(p.js)
  1870  	if p.stmtStart == n {
  1871  		flags |= stmtStartFlag
  1872  	}
  1873  	if p.exportDefaultStart == n {
  1874  		flags |= exportDefaultStartFlag
  1875  	}
  1876  	if p.arrowExprStart == n {
  1877  		flags |= arrowExprStartFlag
  1878  	}
  1879  	if p.forOfInitStart == n {
  1880  		flags |= forOfInitStartFlag
  1881  	}
  1882  	return
  1883  }
  1884  
  1885  func (p *printer) restoreExprStartFlags(flags exprStartFlags) {
  1886  	if flags != 0 {
  1887  		n := len(p.js)
  1888  		if (flags & stmtStartFlag) != 0 {
  1889  			p.stmtStart = n
  1890  		}
  1891  		if (flags & exportDefaultStartFlag) != 0 {
  1892  			p.exportDefaultStart = n
  1893  		}
  1894  		if (flags & arrowExprStartFlag) != 0 {
  1895  			p.arrowExprStart = n
  1896  		}
  1897  		if (flags & forOfInitStartFlag) != 0 {
  1898  			p.forOfInitStart = n
  1899  		}
  1900  	}
  1901  }
  1902  
  1903  // Print any stored comments that are associated with this location
  1904  func (p *printer) printExprCommentsAtLoc(loc logger.Loc) {
  1905  	if p.options.MinifyWhitespace {
  1906  		return
  1907  	}
  1908  	if comments := p.exprComments[loc]; comments != nil && !p.printedExprComments[loc] {
  1909  		flags := p.saveExprStartFlags()
  1910  
  1911  		// We must never generate a newline before certain expressions. For example,
  1912  		// generating a newline before the expression in a "return" statement will
  1913  		// cause a semicolon to be inserted, which would change the code's behavior.
  1914  		if p.noLeadingNewlineHere == len(p.js) {
  1915  			for _, comment := range comments {
  1916  				if strings.HasPrefix(comment, "//") {
  1917  					p.print("/*")
  1918  					p.print(comment[2:])
  1919  					if strings.HasPrefix(comment, "// ") {
  1920  						p.print(" ")
  1921  					}
  1922  					p.print("*/")
  1923  				} else {
  1924  					p.print(strings.Join(strings.Split(comment, "\n"), ""))
  1925  				}
  1926  				p.printSpace()
  1927  			}
  1928  		} else {
  1929  			for _, comment := range comments {
  1930  				p.printIndentedComment(comment)
  1931  				p.printIndent()
  1932  			}
  1933  		}
  1934  
  1935  		// Mark these comments as printed so we don't print them again
  1936  		p.printedExprComments[loc] = true
  1937  
  1938  		p.restoreExprStartFlags(flags)
  1939  	}
  1940  }
  1941  
  1942  func (p *printer) printExprCommentsAfterCloseTokenAtLoc(loc logger.Loc) {
  1943  	if comments := p.exprComments[loc]; comments != nil && !p.printedExprComments[loc] {
  1944  		flags := p.saveExprStartFlags()
  1945  
  1946  		for _, comment := range comments {
  1947  			p.printIndent()
  1948  			p.printIndentedComment(comment)
  1949  		}
  1950  
  1951  		// Mark these comments as printed so we don't print them again
  1952  		p.printedExprComments[loc] = true
  1953  
  1954  		p.restoreExprStartFlags(flags)
  1955  	}
  1956  }
  1957  
  1958  func (p *printer) printExprWithoutLeadingNewline(expr js_ast.Expr, level js_ast.L, flags printExprFlags) {
  1959  	if !p.options.MinifyWhitespace && p.willPrintExprCommentsAtLoc(expr.Loc) {
  1960  		p.print("(")
  1961  		p.printNewline()
  1962  		p.options.Indent++
  1963  		p.printIndent()
  1964  		p.printExpr(expr, level, flags)
  1965  		p.printNewline()
  1966  		p.options.Indent--
  1967  		p.printIndent()
  1968  		p.print(")")
  1969  		return
  1970  	}
  1971  
  1972  	p.noLeadingNewlineHere = len(p.js)
  1973  	p.printExpr(expr, level, flags)
  1974  }
  1975  
  1976  type printExprFlags uint16
  1977  
  1978  const (
  1979  	forbidCall printExprFlags = 1 << iota
  1980  	forbidIn
  1981  	hasNonOptionalChainParent
  1982  	exprResultIsUnused
  1983  	didAlreadySimplifyUnusedExprs
  1984  	isFollowedByOf
  1985  	isInsideForAwait
  1986  	isDeleteTarget
  1987  	isCallTargetOrTemplateTag
  1988  	isPropertyAccessTarget
  1989  	parentWasUnaryOrBinaryOrIfTest
  1990  )
  1991  
  1992  func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFlags) {
  1993  	// If syntax compression is enabled, do a pre-pass over unary and binary
  1994  	// operators to inline bitwise operations of cross-module inlined constants.
  1995  	// This makes the output a little tighter if people construct bit masks in
  1996  	// other files. This is not a general-purpose constant folding pass. In
  1997  	// particular, it has no effect on tree shaking because that pass has already
  1998  	// been run.
  1999  	//
  2000  	// This sets a flag to avoid doing this when the parent is a unary or binary
  2001  	// operator so that we don't trigger O(n^2) behavior when traversing over a
  2002  	// large expression tree.
  2003  	if p.options.MinifySyntax && (flags&parentWasUnaryOrBinaryOrIfTest) == 0 {
  2004  		switch expr.Data.(type) {
  2005  		case *js_ast.EUnary, *js_ast.EBinary, *js_ast.EIf:
  2006  			expr = p.lateConstantFoldUnaryOrBinaryOrIfExpr(expr)
  2007  		}
  2008  	}
  2009  
  2010  	p.printExprCommentsAtLoc(expr.Loc)
  2011  
  2012  	switch e := expr.Data.(type) {
  2013  	case *js_ast.EMissing:
  2014  		p.addSourceMapping(expr.Loc)
  2015  
  2016  	case *js_ast.EAnnotation:
  2017  		p.printExpr(e.Value, level, flags)
  2018  
  2019  	case *js_ast.EUndefined:
  2020  		p.printUndefined(expr.Loc, level)
  2021  
  2022  	case *js_ast.ESuper:
  2023  		p.printSpaceBeforeIdentifier()
  2024  		p.addSourceMapping(expr.Loc)
  2025  		p.print("super")
  2026  
  2027  	case *js_ast.ENull:
  2028  		p.printSpaceBeforeIdentifier()
  2029  		p.addSourceMapping(expr.Loc)
  2030  		p.print("null")
  2031  
  2032  	case *js_ast.EThis:
  2033  		p.printSpaceBeforeIdentifier()
  2034  		p.addSourceMapping(expr.Loc)
  2035  		p.print("this")
  2036  
  2037  	case *js_ast.ESpread:
  2038  		p.addSourceMapping(expr.Loc)
  2039  		p.print("...")
  2040  		p.printExpr(e.Value, js_ast.LComma, 0)
  2041  
  2042  	case *js_ast.ENewTarget:
  2043  		p.printSpaceBeforeIdentifier()
  2044  		p.addSourceMapping(expr.Loc)
  2045  		p.print("new.target")
  2046  
  2047  	case *js_ast.EImportMeta:
  2048  		p.printSpaceBeforeIdentifier()
  2049  		p.addSourceMapping(expr.Loc)
  2050  		p.print("import.meta")
  2051  
  2052  	case *js_ast.ENameOfSymbol:
  2053  		name := p.mangledPropName(e.Ref)
  2054  		p.addSourceMappingForName(expr.Loc, name, e.Ref)
  2055  
  2056  		if !p.options.MinifyWhitespace && e.HasPropertyKeyComment {
  2057  			p.print("/* @__KEY__ */ ")
  2058  		}
  2059  
  2060  		p.printQuotedUTF8(name, printQuotedAllowBacktick)
  2061  
  2062  	case *js_ast.EJSXElement:
  2063  		// Start the opening tag
  2064  		p.addSourceMapping(expr.Loc)
  2065  		p.print("<")
  2066  		p.printJSXTag(e.TagOrNil)
  2067  		if !e.IsTagSingleLine {
  2068  			p.options.Indent++
  2069  		}
  2070  
  2071  		// Print the attributes
  2072  		for _, property := range e.Properties {
  2073  			if e.IsTagSingleLine {
  2074  				p.printSpace()
  2075  			} else {
  2076  				p.printNewline()
  2077  				p.printIndent()
  2078  			}
  2079  
  2080  			if property.Kind == js_ast.PropertySpread {
  2081  				if p.willPrintExprCommentsAtLoc(property.Loc) {
  2082  					p.print("{")
  2083  					p.printNewline()
  2084  					p.options.Indent++
  2085  					p.printIndent()
  2086  					p.printExprCommentsAtLoc(property.Loc)
  2087  					p.print("...")
  2088  					p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
  2089  					p.printNewline()
  2090  					p.options.Indent--
  2091  					p.printIndent()
  2092  					p.print("}")
  2093  				} else {
  2094  					p.print("{...")
  2095  					p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
  2096  					p.print("}")
  2097  				}
  2098  				continue
  2099  			}
  2100  
  2101  			p.printSpaceBeforeIdentifier()
  2102  			if mangled, ok := property.Key.Data.(*js_ast.ENameOfSymbol); ok {
  2103  				name := p.mangledPropName(mangled.Ref)
  2104  				p.addSourceMappingForName(property.Key.Loc, name, mangled.Ref)
  2105  				p.printIdentifier(name)
  2106  			} else if str, ok := property.Key.Data.(*js_ast.EString); ok {
  2107  				p.addSourceMapping(property.Key.Loc)
  2108  				p.print(helpers.UTF16ToString(str.Value))
  2109  			} else {
  2110  				p.print("{...{")
  2111  				p.printSpace()
  2112  				p.print("[")
  2113  				p.printExpr(property.Key, js_ast.LComma, 0)
  2114  				p.print("]:")
  2115  				p.printSpace()
  2116  				p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
  2117  				p.printSpace()
  2118  				p.print("}}")
  2119  				continue
  2120  			}
  2121  
  2122  			isMultiLine := p.willPrintExprCommentsAtLoc(property.ValueOrNil.Loc)
  2123  
  2124  			if property.Flags.Has(js_ast.PropertyWasShorthand) {
  2125  				// Implicit "true" value
  2126  				if boolean, ok := property.ValueOrNil.Data.(*js_ast.EBoolean); ok && boolean.Value {
  2127  					continue
  2128  				}
  2129  
  2130  				// JSX element as JSX attribute value
  2131  				if _, ok := property.ValueOrNil.Data.(*js_ast.EJSXElement); ok {
  2132  					p.print("=")
  2133  					p.printExpr(property.ValueOrNil, js_ast.LLowest, 0)
  2134  					continue
  2135  				}
  2136  			}
  2137  
  2138  			// Special-case raw text
  2139  			if text, ok := property.ValueOrNil.Data.(*js_ast.EJSXText); ok {
  2140  				p.print("=")
  2141  				p.addSourceMapping(property.ValueOrNil.Loc)
  2142  				p.print(text.Raw)
  2143  				continue
  2144  			}
  2145  
  2146  			// Generic JS value
  2147  			p.print("={")
  2148  			if isMultiLine {
  2149  				p.printNewline()
  2150  				p.options.Indent++
  2151  				p.printIndent()
  2152  			}
  2153  			p.printExpr(property.ValueOrNil, js_ast.LComma, 0)
  2154  			if isMultiLine {
  2155  				p.printNewline()
  2156  				p.options.Indent--
  2157  				p.printIndent()
  2158  			}
  2159  			p.print("}")
  2160  		}
  2161  
  2162  		// End the opening tag
  2163  		if !e.IsTagSingleLine {
  2164  			p.options.Indent--
  2165  			if len(e.Properties) > 0 {
  2166  				p.printNewline()
  2167  				p.printIndent()
  2168  			}
  2169  		}
  2170  		if e.TagOrNil.Data != nil && len(e.NullableChildren) == 0 {
  2171  			if e.IsTagSingleLine || len(e.Properties) == 0 {
  2172  				p.printSpace()
  2173  			}
  2174  			p.addSourceMapping(e.CloseLoc)
  2175  			p.print("/>")
  2176  			break
  2177  		}
  2178  		p.print(">")
  2179  
  2180  		// Print the children
  2181  		for _, childOrNil := range e.NullableChildren {
  2182  			if _, ok := childOrNil.Data.(*js_ast.EJSXElement); ok {
  2183  				p.printExpr(childOrNil, js_ast.LLowest, 0)
  2184  			} else if text, ok := childOrNil.Data.(*js_ast.EJSXText); ok {
  2185  				p.addSourceMapping(childOrNil.Loc)
  2186  				p.print(text.Raw)
  2187  			} else if childOrNil.Data != nil {
  2188  				isMultiLine := p.willPrintExprCommentsAtLoc(childOrNil.Loc)
  2189  				p.print("{")
  2190  				if isMultiLine {
  2191  					p.printNewline()
  2192  					p.options.Indent++
  2193  					p.printIndent()
  2194  				}
  2195  				p.printExpr(childOrNil, js_ast.LComma, 0)
  2196  				if isMultiLine {
  2197  					p.printNewline()
  2198  					p.options.Indent--
  2199  					p.printIndent()
  2200  				}
  2201  				p.print("}")
  2202  			} else {
  2203  				p.print("{")
  2204  				if p.willPrintExprCommentsAtLoc(childOrNil.Loc) {
  2205  					// Note: Some people use these comments for AST transformations
  2206  					p.printNewline()
  2207  					p.options.Indent++
  2208  					p.printExprCommentsAfterCloseTokenAtLoc(childOrNil.Loc)
  2209  					p.options.Indent--
  2210  					p.printIndent()
  2211  				}
  2212  				p.print("}")
  2213  			}
  2214  		}
  2215  
  2216  		// Print the closing tag
  2217  		p.addSourceMapping(e.CloseLoc)
  2218  		p.print("</")
  2219  		p.printJSXTag(e.TagOrNil)
  2220  		p.print(">")
  2221  
  2222  	case *js_ast.ENew:
  2223  		wrap := level >= js_ast.LCall
  2224  
  2225  		hasPureComment := !p.options.MinifyWhitespace && e.CanBeUnwrappedIfUnused
  2226  		if hasPureComment && level >= js_ast.LPostfix {
  2227  			wrap = true
  2228  		}
  2229  
  2230  		if wrap {
  2231  			p.print("(")
  2232  		}
  2233  
  2234  		if hasPureComment {
  2235  			p.addSourceMapping(expr.Loc)
  2236  			p.print("/* @__PURE__ */ ")
  2237  		}
  2238  
  2239  		p.printSpaceBeforeIdentifier()
  2240  		p.addSourceMapping(expr.Loc)
  2241  		p.print("new")
  2242  		p.printSpace()
  2243  		p.printExpr(e.Target, js_ast.LNew, forbidCall)
  2244  
  2245  		// Omit the "()" when minifying, but only when safe to do so
  2246  		isMultiLine := !p.options.MinifyWhitespace && ((e.IsMultiLine && len(e.Args) > 0) ||
  2247  			p.willPrintExprCommentsForAnyOf(e.Args) ||
  2248  			p.willPrintExprCommentsAtLoc(e.CloseParenLoc))
  2249  		if !p.options.MinifyWhitespace || len(e.Args) > 0 || level >= js_ast.LPostfix || isMultiLine {
  2250  			needsNewline := true
  2251  			p.print("(")
  2252  			if isMultiLine {
  2253  				p.options.Indent++
  2254  			}
  2255  			for i, arg := range e.Args {
  2256  				if i != 0 {
  2257  					p.print(",")
  2258  				}
  2259  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2260  					if isMultiLine {
  2261  						if needsNewline {
  2262  							p.printNewline()
  2263  						}
  2264  						p.printIndent()
  2265  					} else if i != 0 {
  2266  						p.printSpace()
  2267  					}
  2268  				}
  2269  				p.printExpr(arg, js_ast.LComma, 0)
  2270  				needsNewline = true
  2271  			}
  2272  			if isMultiLine {
  2273  				if needsNewline || p.willPrintExprCommentsAtLoc(e.CloseParenLoc) {
  2274  					p.printNewline()
  2275  				}
  2276  				p.printExprCommentsAfterCloseTokenAtLoc(e.CloseParenLoc)
  2277  				p.options.Indent--
  2278  				p.printIndent()
  2279  			}
  2280  			if e.CloseParenLoc.Start > expr.Loc.Start {
  2281  				p.addSourceMapping(e.CloseParenLoc)
  2282  			}
  2283  			p.print(")")
  2284  		}
  2285  
  2286  		if wrap {
  2287  			p.print(")")
  2288  		}
  2289  
  2290  	case *js_ast.ECall:
  2291  		if p.options.MinifySyntax {
  2292  			var symbolFlags ast.SymbolFlags
  2293  			switch target := e.Target.Data.(type) {
  2294  			case *js_ast.EIdentifier:
  2295  				symbolFlags = p.symbols.Get(target.Ref).Flags
  2296  			case *js_ast.EImportIdentifier:
  2297  				ref := ast.FollowSymbols(p.symbols, target.Ref)
  2298  				symbolFlags = p.symbols.Get(ref).Flags
  2299  			}
  2300  
  2301  			// Replace non-mutated empty functions with their arguments at print time
  2302  			if (symbolFlags & (ast.IsEmptyFunction | ast.CouldPotentiallyBeMutated)) == ast.IsEmptyFunction {
  2303  				var replacement js_ast.Expr
  2304  				for _, arg := range e.Args {
  2305  					if _, ok := arg.Data.(*js_ast.ESpread); ok {
  2306  						arg.Data = &js_ast.EArray{Items: []js_ast.Expr{arg}, IsSingleLine: true}
  2307  					}
  2308  					replacement = js_ast.JoinWithComma(replacement, p.astHelpers.SimplifyUnusedExpr(arg, p.options.UnsupportedFeatures))
  2309  				}
  2310  				if replacement.Data == nil || (flags&exprResultIsUnused) == 0 {
  2311  					replacement = js_ast.JoinWithComma(replacement, js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared})
  2312  				}
  2313  				p.printExpr(p.guardAgainstBehaviorChangeDueToSubstitution(replacement, flags), level, flags)
  2314  				break
  2315  			}
  2316  
  2317  			// Inline non-mutated identity functions at print time
  2318  			if (symbolFlags&(ast.IsIdentityFunction|ast.CouldPotentiallyBeMutated)) == ast.IsIdentityFunction && len(e.Args) == 1 {
  2319  				arg := e.Args[0]
  2320  				if _, ok := arg.Data.(*js_ast.ESpread); !ok {
  2321  					if (flags & exprResultIsUnused) != 0 {
  2322  						arg = p.astHelpers.SimplifyUnusedExpr(arg, p.options.UnsupportedFeatures)
  2323  					}
  2324  					p.printExpr(p.guardAgainstBehaviorChangeDueToSubstitution(arg, flags), level, flags)
  2325  					break
  2326  				}
  2327  			}
  2328  
  2329  			// Inline IIFEs that return expressions at print time
  2330  			if len(e.Args) == 0 {
  2331  				// Note: Do not inline async arrow functions as they are not IIFEs. In
  2332  				// particular, they are not necessarily invoked immediately, and any
  2333  				// exceptions involved in their evaluation will be swallowed without
  2334  				// bubbling up to the surrounding context.
  2335  				if arrow, ok := e.Target.Data.(*js_ast.EArrow); ok && len(arrow.Args) == 0 && !arrow.IsAsync {
  2336  					stmts := arrow.Body.Block.Stmts
  2337  
  2338  					// "(() => {})()" => "void 0"
  2339  					if len(stmts) == 0 {
  2340  						value := js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared}
  2341  						p.printExpr(p.guardAgainstBehaviorChangeDueToSubstitution(value, flags), level, flags)
  2342  						break
  2343  					}
  2344  
  2345  					// "(() => 123)()" => "123"
  2346  					if len(stmts) == 1 {
  2347  						if stmt, ok := stmts[0].Data.(*js_ast.SReturn); ok {
  2348  							value := stmt.ValueOrNil
  2349  							if value.Data == nil {
  2350  								value.Data = js_ast.EUndefinedShared
  2351  							}
  2352  							p.printExpr(p.guardAgainstBehaviorChangeDueToSubstitution(value, flags), level, flags)
  2353  							break
  2354  						}
  2355  					}
  2356  				}
  2357  			}
  2358  		}
  2359  
  2360  		wrap := level >= js_ast.LNew || (flags&forbidCall) != 0
  2361  		var targetFlags printExprFlags
  2362  		if e.OptionalChain == js_ast.OptionalChainNone {
  2363  			targetFlags = hasNonOptionalChainParent
  2364  		} else if (flags & hasNonOptionalChainParent) != 0 {
  2365  			wrap = true
  2366  		}
  2367  
  2368  		hasPureComment := !p.options.MinifyWhitespace && e.CanBeUnwrappedIfUnused
  2369  		if hasPureComment && level >= js_ast.LPostfix {
  2370  			wrap = true
  2371  		}
  2372  
  2373  		if wrap {
  2374  			p.print("(")
  2375  		}
  2376  
  2377  		if hasPureComment {
  2378  			flags := p.saveExprStartFlags()
  2379  			p.addSourceMapping(expr.Loc)
  2380  			p.print("/* @__PURE__ */ ")
  2381  			p.restoreExprStartFlags(flags)
  2382  		}
  2383  
  2384  		// We don't ever want to accidentally generate a direct eval expression here
  2385  		p.callTarget = e.Target.Data
  2386  		if (e.Kind != js_ast.DirectEval && p.isUnboundEvalIdentifier(e.Target) && e.OptionalChain == js_ast.OptionalChainNone) ||
  2387  			(e.Kind != js_ast.TargetWasOriginallyPropertyAccess && js_ast.IsPropertyAccess(e.Target)) {
  2388  			p.print("(0,")
  2389  			p.printSpace()
  2390  			p.printExpr(e.Target, js_ast.LPostfix, isCallTargetOrTemplateTag)
  2391  			p.print(")")
  2392  		} else {
  2393  			p.printExpr(e.Target, js_ast.LPostfix, isCallTargetOrTemplateTag|targetFlags)
  2394  		}
  2395  
  2396  		if e.OptionalChain == js_ast.OptionalChainStart {
  2397  			p.print("?.")
  2398  		}
  2399  
  2400  		isMultiLine := !p.options.MinifyWhitespace && ((e.IsMultiLine && len(e.Args) > 0) ||
  2401  			p.willPrintExprCommentsForAnyOf(e.Args) ||
  2402  			p.willPrintExprCommentsAtLoc(e.CloseParenLoc))
  2403  		p.print("(")
  2404  		if isMultiLine {
  2405  			p.options.Indent++
  2406  		}
  2407  		for i, arg := range e.Args {
  2408  			if i != 0 {
  2409  				p.print(",")
  2410  			}
  2411  			if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2412  				if isMultiLine {
  2413  					p.printNewline()
  2414  					p.printIndent()
  2415  				} else if i != 0 {
  2416  					p.printSpace()
  2417  				}
  2418  			}
  2419  			p.printExpr(arg, js_ast.LComma, 0)
  2420  		}
  2421  		if isMultiLine {
  2422  			p.printNewline()
  2423  			p.printExprCommentsAfterCloseTokenAtLoc(e.CloseParenLoc)
  2424  			p.options.Indent--
  2425  			p.printIndent()
  2426  		}
  2427  		if e.CloseParenLoc.Start > expr.Loc.Start {
  2428  			p.addSourceMapping(e.CloseParenLoc)
  2429  		}
  2430  		p.print(")")
  2431  
  2432  		if wrap {
  2433  			p.print(")")
  2434  		}
  2435  
  2436  	case *js_ast.ERequireString:
  2437  		p.addSourceMapping(expr.Loc)
  2438  		p.printRequireOrImportExpr(e.ImportRecordIndex, level, flags, e.CloseParenLoc)
  2439  
  2440  	case *js_ast.ERequireResolveString:
  2441  		recordLoc := p.importRecords[e.ImportRecordIndex].Range.Loc
  2442  		isMultiLine := p.willPrintExprCommentsAtLoc(recordLoc) || p.willPrintExprCommentsAtLoc(e.CloseParenLoc)
  2443  		wrap := level >= js_ast.LNew || (flags&forbidCall) != 0
  2444  		if wrap {
  2445  			p.print("(")
  2446  		}
  2447  		p.printSpaceBeforeIdentifier()
  2448  		p.addSourceMapping(expr.Loc)
  2449  		p.print("require.resolve(")
  2450  		if isMultiLine {
  2451  			p.printNewline()
  2452  			p.options.Indent++
  2453  			p.printIndent()
  2454  			p.printExprCommentsAtLoc(recordLoc)
  2455  		}
  2456  		p.printPath(e.ImportRecordIndex, ast.ImportRequireResolve)
  2457  		if isMultiLine {
  2458  			p.printNewline()
  2459  			p.printExprCommentsAfterCloseTokenAtLoc(e.CloseParenLoc)
  2460  			p.options.Indent--
  2461  			p.printIndent()
  2462  		}
  2463  		if e.CloseParenLoc.Start > expr.Loc.Start {
  2464  			p.addSourceMapping(e.CloseParenLoc)
  2465  		}
  2466  		p.print(")")
  2467  		if wrap {
  2468  			p.print(")")
  2469  		}
  2470  
  2471  	case *js_ast.EImportString:
  2472  		p.addSourceMapping(expr.Loc)
  2473  		p.printRequireOrImportExpr(e.ImportRecordIndex, level, flags, e.CloseParenLoc)
  2474  
  2475  	case *js_ast.EImportCall:
  2476  		// Only print the second argument if either import assertions or import attributes are supported
  2477  		printImportAssertOrWith := e.OptionsOrNil.Data != nil && (!p.options.UnsupportedFeatures.Has(compat.ImportAssertions) || !p.options.UnsupportedFeatures.Has(compat.ImportAttributes))
  2478  		isMultiLine := !p.options.MinifyWhitespace &&
  2479  			(p.willPrintExprCommentsAtLoc(e.Expr.Loc) ||
  2480  				(printImportAssertOrWith && p.willPrintExprCommentsAtLoc(e.OptionsOrNil.Loc)) ||
  2481  				p.willPrintExprCommentsAtLoc(e.CloseParenLoc))
  2482  		wrap := level >= js_ast.LNew || (flags&forbidCall) != 0
  2483  		if wrap {
  2484  			p.print("(")
  2485  		}
  2486  		p.printSpaceBeforeIdentifier()
  2487  		p.addSourceMapping(expr.Loc)
  2488  		p.print("import(")
  2489  		if isMultiLine {
  2490  			p.printNewline()
  2491  			p.options.Indent++
  2492  			p.printIndent()
  2493  		}
  2494  		p.printExpr(e.Expr, js_ast.LComma, 0)
  2495  
  2496  		if printImportAssertOrWith {
  2497  			p.print(",")
  2498  			if isMultiLine {
  2499  				p.printNewline()
  2500  				p.printIndent()
  2501  			} else {
  2502  				p.printSpace()
  2503  			}
  2504  			p.printExpr(e.OptionsOrNil, js_ast.LComma, 0)
  2505  		}
  2506  
  2507  		if isMultiLine {
  2508  			p.printNewline()
  2509  			p.printExprCommentsAfterCloseTokenAtLoc(e.CloseParenLoc)
  2510  			p.options.Indent--
  2511  			p.printIndent()
  2512  		}
  2513  		p.print(")")
  2514  		if wrap {
  2515  			p.print(")")
  2516  		}
  2517  
  2518  	case *js_ast.EDot:
  2519  		wrap := false
  2520  		if e.OptionalChain == js_ast.OptionalChainNone {
  2521  			flags |= hasNonOptionalChainParent
  2522  
  2523  			// Inline cross-module TypeScript enum references here
  2524  			if value, ok := p.tryToGetImportedEnumValue(e.Target, e.Name); ok {
  2525  				if value.String != nil {
  2526  					p.printQuotedUTF16(value.String, printQuotedAllowBacktick)
  2527  				} else {
  2528  					p.printNumber(value.Number, level)
  2529  				}
  2530  				if !p.options.MinifyWhitespace && !p.options.MinifyIdentifiers && !strings.Contains(e.Name, "*/") {
  2531  					p.print(" /* ")
  2532  					p.print(e.Name)
  2533  					p.print(" */")
  2534  				}
  2535  				break
  2536  			}
  2537  		} else {
  2538  			if (flags & hasNonOptionalChainParent) != 0 {
  2539  				wrap = true
  2540  				p.print("(")
  2541  			}
  2542  			flags &= ^hasNonOptionalChainParent
  2543  		}
  2544  		p.printExpr(e.Target, js_ast.LPostfix, (flags&(forbidCall|hasNonOptionalChainParent))|isPropertyAccessTarget)
  2545  		if p.canPrintIdentifier(e.Name) {
  2546  			if e.OptionalChain != js_ast.OptionalChainStart && p.needSpaceBeforeDot == len(p.js) {
  2547  				// "1.toString" is a syntax error, so print "1 .toString" instead
  2548  				p.print(" ")
  2549  			}
  2550  			if e.OptionalChain == js_ast.OptionalChainStart {
  2551  				p.print("?.")
  2552  			} else {
  2553  				p.print(".")
  2554  			}
  2555  			if p.options.LineLimit > 0 {
  2556  				p.printNewlinePastLineLimit()
  2557  			}
  2558  			p.addSourceMapping(e.NameLoc)
  2559  			p.printIdentifier(e.Name)
  2560  		} else {
  2561  			if e.OptionalChain == js_ast.OptionalChainStart {
  2562  				p.print("?.")
  2563  			}
  2564  			p.print("[")
  2565  			p.addSourceMapping(e.NameLoc)
  2566  			p.printQuotedUTF8(e.Name, printQuotedAllowBacktick)
  2567  			p.print("]")
  2568  		}
  2569  		if wrap {
  2570  			p.print(")")
  2571  		}
  2572  
  2573  	case *js_ast.EIndex:
  2574  		if e.OptionalChain == js_ast.OptionalChainNone {
  2575  			flags |= hasNonOptionalChainParent
  2576  
  2577  			// Inline cross-module TypeScript enum references here
  2578  			if index, ok := e.Index.Data.(*js_ast.EString); ok {
  2579  				if value, name, ok := p.tryToGetImportedEnumValueUTF16(e.Target, index.Value); ok {
  2580  					if value.String != nil {
  2581  						p.printQuotedUTF16(value.String, printQuotedAllowBacktick)
  2582  					} else {
  2583  						p.printNumber(value.Number, level)
  2584  					}
  2585  					if !p.options.MinifyWhitespace && !p.options.MinifyIdentifiers && !strings.Contains(name, "*/") {
  2586  						p.print(" /* ")
  2587  						p.print(name)
  2588  						p.print(" */")
  2589  					}
  2590  					break
  2591  				}
  2592  			}
  2593  		} else {
  2594  			if (flags & hasNonOptionalChainParent) != 0 {
  2595  				p.print("(")
  2596  				defer p.print(")")
  2597  			}
  2598  			flags &= ^hasNonOptionalChainParent
  2599  		}
  2600  		p.printExpr(e.Target, js_ast.LPostfix, (flags&(forbidCall|hasNonOptionalChainParent))|isPropertyAccessTarget)
  2601  		if e.OptionalChain == js_ast.OptionalChainStart {
  2602  			p.print("?.")
  2603  		}
  2604  
  2605  		switch index := e.Index.Data.(type) {
  2606  		case *js_ast.EPrivateIdentifier:
  2607  			if e.OptionalChain != js_ast.OptionalChainStart {
  2608  				p.print(".")
  2609  			}
  2610  			name := p.renamer.NameForSymbol(index.Ref)
  2611  			p.addSourceMappingForName(e.Index.Loc, name, index.Ref)
  2612  			p.printIdentifier(name)
  2613  			return
  2614  
  2615  		case *js_ast.ENameOfSymbol:
  2616  			if name := p.mangledPropName(index.Ref); p.canPrintIdentifier(name) {
  2617  				if e.OptionalChain != js_ast.OptionalChainStart {
  2618  					p.print(".")
  2619  				}
  2620  				p.addSourceMappingForName(e.Index.Loc, name, index.Ref)
  2621  				p.printIdentifier(name)
  2622  				return
  2623  			}
  2624  
  2625  		case *js_ast.EInlinedEnum:
  2626  			if p.options.MinifySyntax {
  2627  				if str, ok := index.Value.Data.(*js_ast.EString); ok && p.canPrintIdentifierUTF16(str.Value) {
  2628  					if e.OptionalChain != js_ast.OptionalChainStart {
  2629  						p.print(".")
  2630  					}
  2631  					p.addSourceMapping(index.Value.Loc)
  2632  					p.printIdentifierUTF16(str.Value)
  2633  					return
  2634  				}
  2635  			}
  2636  
  2637  		case *js_ast.EDot:
  2638  			if p.options.MinifySyntax {
  2639  				if value, ok := p.tryToGetImportedEnumValue(index.Target, index.Name); ok && value.String != nil && p.canPrintIdentifierUTF16(value.String) {
  2640  					if e.OptionalChain != js_ast.OptionalChainStart {
  2641  						p.print(".")
  2642  					}
  2643  					p.addSourceMapping(e.Index.Loc)
  2644  					p.printIdentifierUTF16(value.String)
  2645  					return
  2646  				}
  2647  			}
  2648  		}
  2649  
  2650  		isMultiLine := p.willPrintExprCommentsAtLoc(e.Index.Loc) || p.willPrintExprCommentsAtLoc(e.CloseBracketLoc)
  2651  		p.print("[")
  2652  		if isMultiLine {
  2653  			p.printNewline()
  2654  			p.options.Indent++
  2655  			p.printIndent()
  2656  		}
  2657  		p.printExpr(e.Index, js_ast.LLowest, 0)
  2658  		if isMultiLine {
  2659  			p.printNewline()
  2660  			p.printExprCommentsAfterCloseTokenAtLoc(e.CloseBracketLoc)
  2661  			p.options.Indent--
  2662  			p.printIndent()
  2663  		}
  2664  		if e.CloseBracketLoc.Start > expr.Loc.Start {
  2665  			p.addSourceMapping(e.CloseBracketLoc)
  2666  		}
  2667  		p.print("]")
  2668  
  2669  	case *js_ast.EIf:
  2670  		wrap := level >= js_ast.LConditional
  2671  		if wrap {
  2672  			p.print("(")
  2673  			flags &= ^forbidIn
  2674  		}
  2675  		p.printExpr(e.Test, js_ast.LConditional, (flags&forbidIn)|parentWasUnaryOrBinaryOrIfTest)
  2676  		p.printSpace()
  2677  		p.print("?")
  2678  		if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2679  			p.printSpace()
  2680  		}
  2681  		p.printExprWithoutLeadingNewline(e.Yes, js_ast.LYield, 0)
  2682  		p.printSpace()
  2683  		p.print(":")
  2684  		if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2685  			p.printSpace()
  2686  		}
  2687  		p.printExprWithoutLeadingNewline(e.No, js_ast.LYield, flags&forbidIn)
  2688  		if wrap {
  2689  			p.print(")")
  2690  		}
  2691  
  2692  	case *js_ast.EArrow:
  2693  		wrap := level >= js_ast.LAssign
  2694  
  2695  		if wrap {
  2696  			p.print("(")
  2697  		}
  2698  		if !p.options.MinifyWhitespace && e.HasNoSideEffectsComment {
  2699  			p.print("/* @__NO_SIDE_EFFECTS__ */ ")
  2700  		}
  2701  		if e.IsAsync {
  2702  			p.addSourceMapping(expr.Loc)
  2703  			p.printSpaceBeforeIdentifier()
  2704  			p.print("async")
  2705  			p.printSpace()
  2706  		}
  2707  
  2708  		p.printFnArgs(e.Args, fnArgsOpts{
  2709  			openParenLoc:              expr.Loc,
  2710  			addMappingForOpenParenLoc: !e.IsAsync,
  2711  			hasRestArg:                e.HasRestArg,
  2712  			isArrow:                   true,
  2713  		})
  2714  		p.printSpace()
  2715  		p.print("=>")
  2716  		p.printSpace()
  2717  
  2718  		wasPrinted := false
  2719  		if len(e.Body.Block.Stmts) == 1 && e.PreferExpr {
  2720  			if s, ok := e.Body.Block.Stmts[0].Data.(*js_ast.SReturn); ok && s.ValueOrNil.Data != nil {
  2721  				p.arrowExprStart = len(p.js)
  2722  				p.printExprWithoutLeadingNewline(s.ValueOrNil, js_ast.LComma, flags&forbidIn)
  2723  				wasPrinted = true
  2724  			}
  2725  		}
  2726  		if !wasPrinted {
  2727  			p.printBlock(e.Body.Loc, e.Body.Block)
  2728  		}
  2729  		if wrap {
  2730  			p.print(")")
  2731  		}
  2732  
  2733  	case *js_ast.EFunction:
  2734  		n := len(p.js)
  2735  		wrap := p.stmtStart == n || p.exportDefaultStart == n ||
  2736  			((flags&isPropertyAccessTarget) != 0 && p.options.UnsupportedFeatures.Has(compat.FunctionOrClassPropertyAccess))
  2737  		if wrap {
  2738  			p.print("(")
  2739  		}
  2740  		if !p.options.MinifyWhitespace && e.Fn.HasNoSideEffectsComment {
  2741  			p.print("/* @__NO_SIDE_EFFECTS__ */ ")
  2742  		}
  2743  		p.printSpaceBeforeIdentifier()
  2744  		p.addSourceMapping(expr.Loc)
  2745  		if e.Fn.IsAsync {
  2746  			p.print("async ")
  2747  		}
  2748  		p.print("function")
  2749  		if e.Fn.IsGenerator {
  2750  			p.print("*")
  2751  			p.printSpace()
  2752  		}
  2753  		if e.Fn.Name != nil {
  2754  			p.printSpaceBeforeIdentifier()
  2755  			name := p.renamer.NameForSymbol(e.Fn.Name.Ref)
  2756  			p.addSourceMappingForName(e.Fn.Name.Loc, name, e.Fn.Name.Ref)
  2757  			p.printIdentifier(name)
  2758  		}
  2759  		p.printFn(e.Fn)
  2760  		if wrap {
  2761  			p.print(")")
  2762  		}
  2763  
  2764  	case *js_ast.EClass:
  2765  		n := len(p.js)
  2766  		wrap := p.stmtStart == n || p.exportDefaultStart == n ||
  2767  			((flags&isPropertyAccessTarget) != 0 && p.options.UnsupportedFeatures.Has(compat.FunctionOrClassPropertyAccess))
  2768  		if wrap {
  2769  			p.print("(")
  2770  		}
  2771  		p.printDecorators(e.Class.Decorators, printSpaceAfterDecorator)
  2772  		p.printSpaceBeforeIdentifier()
  2773  		p.addSourceMapping(expr.Loc)
  2774  		p.print("class")
  2775  		if e.Class.Name != nil {
  2776  			p.print(" ")
  2777  			name := p.renamer.NameForSymbol(e.Class.Name.Ref)
  2778  			p.addSourceMappingForName(e.Class.Name.Loc, name, e.Class.Name.Ref)
  2779  			p.printIdentifier(name)
  2780  		}
  2781  		p.printClass(e.Class)
  2782  		if wrap {
  2783  			p.print(")")
  2784  		}
  2785  
  2786  	case *js_ast.EArray:
  2787  		isMultiLine := (len(e.Items) > 0 && !e.IsSingleLine) || p.willPrintExprCommentsForAnyOf(e.Items) || p.willPrintExprCommentsAtLoc(e.CloseBracketLoc)
  2788  		p.addSourceMapping(expr.Loc)
  2789  		p.print("[")
  2790  		if len(e.Items) > 0 || isMultiLine {
  2791  			if isMultiLine {
  2792  				p.options.Indent++
  2793  			}
  2794  
  2795  			for i, item := range e.Items {
  2796  				if i != 0 {
  2797  					p.print(",")
  2798  				}
  2799  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2800  					if isMultiLine {
  2801  						p.printNewline()
  2802  						p.printIndent()
  2803  					} else if i != 0 {
  2804  						p.printSpace()
  2805  					}
  2806  				}
  2807  				p.printExpr(item, js_ast.LComma, 0)
  2808  
  2809  				// Make sure there's a comma after trailing missing items
  2810  				_, ok := item.Data.(*js_ast.EMissing)
  2811  				if ok && i == len(e.Items)-1 {
  2812  					p.print(",")
  2813  				}
  2814  			}
  2815  
  2816  			if isMultiLine {
  2817  				p.printNewline()
  2818  				p.printExprCommentsAfterCloseTokenAtLoc(e.CloseBracketLoc)
  2819  				p.options.Indent--
  2820  				p.printIndent()
  2821  			}
  2822  		}
  2823  		if e.CloseBracketLoc.Start > expr.Loc.Start {
  2824  			p.addSourceMapping(e.CloseBracketLoc)
  2825  		}
  2826  		p.print("]")
  2827  
  2828  	case *js_ast.EObject:
  2829  		isMultiLine := (len(e.Properties) > 0 && !e.IsSingleLine) || p.willPrintExprCommentsAtLoc(e.CloseBraceLoc)
  2830  		if !p.options.MinifyWhitespace && !isMultiLine {
  2831  			for _, property := range e.Properties {
  2832  				if p.willPrintExprCommentsAtLoc(property.Loc) {
  2833  					isMultiLine = true
  2834  					break
  2835  				}
  2836  			}
  2837  		}
  2838  		n := len(p.js)
  2839  		wrap := p.stmtStart == n || p.arrowExprStart == n
  2840  		if wrap {
  2841  			p.print("(")
  2842  		}
  2843  		p.addSourceMapping(expr.Loc)
  2844  		p.print("{")
  2845  		if len(e.Properties) > 0 || isMultiLine {
  2846  			if isMultiLine {
  2847  				p.options.Indent++
  2848  			}
  2849  
  2850  			for i, item := range e.Properties {
  2851  				if i != 0 {
  2852  					p.print(",")
  2853  				}
  2854  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  2855  					if isMultiLine {
  2856  						p.printNewline()
  2857  						p.printIndent()
  2858  					} else {
  2859  						p.printSpace()
  2860  					}
  2861  				}
  2862  				p.printProperty(item)
  2863  			}
  2864  
  2865  			if isMultiLine {
  2866  				p.printNewline()
  2867  				p.printExprCommentsAfterCloseTokenAtLoc(e.CloseBraceLoc)
  2868  				p.options.Indent--
  2869  				p.printIndent()
  2870  			} else if len(e.Properties) > 0 {
  2871  				p.printSpace()
  2872  			}
  2873  		}
  2874  		if e.CloseBraceLoc.Start > expr.Loc.Start {
  2875  			p.addSourceMapping(e.CloseBraceLoc)
  2876  		}
  2877  		p.print("}")
  2878  		if wrap {
  2879  			p.print(")")
  2880  		}
  2881  
  2882  	case *js_ast.EBoolean:
  2883  		p.addSourceMapping(expr.Loc)
  2884  		if p.options.MinifySyntax {
  2885  			if level >= js_ast.LPrefix {
  2886  				if e.Value {
  2887  					p.print("(!0)")
  2888  				} else {
  2889  					p.print("(!1)")
  2890  				}
  2891  			} else {
  2892  				if e.Value {
  2893  					p.print("!0")
  2894  				} else {
  2895  					p.print("!1")
  2896  				}
  2897  			}
  2898  		} else {
  2899  			p.printSpaceBeforeIdentifier()
  2900  			if e.Value {
  2901  				p.print("true")
  2902  			} else {
  2903  				p.print("false")
  2904  			}
  2905  		}
  2906  
  2907  	case *js_ast.EString:
  2908  		var flags printQuotedFlags
  2909  		if e.ContainsUniqueKey {
  2910  			flags = printQuotedNoWrap
  2911  		}
  2912  		p.addSourceMapping(expr.Loc)
  2913  
  2914  		if !p.options.MinifyWhitespace && e.HasPropertyKeyComment {
  2915  			p.print("/* @__KEY__ */ ")
  2916  		}
  2917  
  2918  		// If this was originally a template literal, print it as one as long as we're not minifying
  2919  		if e.PreferTemplate && !p.options.MinifySyntax && !p.options.UnsupportedFeatures.Has(compat.TemplateLiteral) {
  2920  			p.print("`")
  2921  			p.printUnquotedUTF16(e.Value, '`', flags)
  2922  			p.print("`")
  2923  			return
  2924  		}
  2925  
  2926  		p.printQuotedUTF16(e.Value, flags|printQuotedAllowBacktick)
  2927  
  2928  	case *js_ast.ETemplate:
  2929  		if e.TagOrNil.Data == nil && (p.options.MinifySyntax || p.wasLazyExport) {
  2930  			// Inline enums and mangled properties when minifying
  2931  			var replaced []js_ast.TemplatePart
  2932  			for i, part := range e.Parts {
  2933  				var inlinedValue js_ast.E
  2934  				switch e2 := part.Value.Data.(type) {
  2935  				case *js_ast.ENameOfSymbol:
  2936  					inlinedValue = &js_ast.EString{Value: helpers.StringToUTF16(p.mangledPropName(e2.Ref))}
  2937  				case *js_ast.EDot:
  2938  					if value, ok := p.tryToGetImportedEnumValue(e2.Target, e2.Name); ok {
  2939  						if value.String != nil {
  2940  							inlinedValue = &js_ast.EString{Value: value.String}
  2941  						} else {
  2942  							inlinedValue = &js_ast.ENumber{Value: value.Number}
  2943  						}
  2944  					}
  2945  				}
  2946  				if inlinedValue != nil {
  2947  					if replaced == nil {
  2948  						replaced = make([]js_ast.TemplatePart, 0, len(e.Parts))
  2949  						replaced = append(replaced, e.Parts[:i]...)
  2950  					}
  2951  					part.Value.Data = inlinedValue
  2952  					replaced = append(replaced, part)
  2953  				} else if replaced != nil {
  2954  					replaced = append(replaced, part)
  2955  				}
  2956  			}
  2957  			if replaced != nil {
  2958  				copy := *e
  2959  				copy.Parts = replaced
  2960  				switch e2 := js_ast.InlinePrimitivesIntoTemplate(logger.Loc{}, &copy).Data.(type) {
  2961  				case *js_ast.EString:
  2962  					p.printQuotedUTF16(e2.Value, printQuotedAllowBacktick)
  2963  					return
  2964  				case *js_ast.ETemplate:
  2965  					e = e2
  2966  				}
  2967  			}
  2968  
  2969  			// Convert no-substitution template literals into strings if it's smaller
  2970  			if len(e.Parts) == 0 {
  2971  				p.addSourceMapping(expr.Loc)
  2972  				p.printQuotedUTF16(e.HeadCooked, printQuotedAllowBacktick)
  2973  				return
  2974  			}
  2975  		}
  2976  
  2977  		if e.TagOrNil.Data != nil {
  2978  			tagIsPropertyAccess := false
  2979  			switch e.TagOrNil.Data.(type) {
  2980  			case *js_ast.EDot, *js_ast.EIndex:
  2981  				tagIsPropertyAccess = true
  2982  			}
  2983  			if !e.TagWasOriginallyPropertyAccess && tagIsPropertyAccess {
  2984  				// Prevent "x``" from becoming "y.z``"
  2985  				p.print("(0,")
  2986  				p.printSpace()
  2987  				p.printExpr(e.TagOrNil, js_ast.LLowest, isCallTargetOrTemplateTag)
  2988  				p.print(")")
  2989  			} else if js_ast.IsOptionalChain(e.TagOrNil) {
  2990  				// Optional chains are forbidden in template tags
  2991  				p.print("(")
  2992  				p.printExpr(e.TagOrNil, js_ast.LLowest, isCallTargetOrTemplateTag)
  2993  				p.print(")")
  2994  			} else {
  2995  				p.printExpr(e.TagOrNil, js_ast.LPostfix, isCallTargetOrTemplateTag)
  2996  			}
  2997  		} else {
  2998  			p.addSourceMapping(expr.Loc)
  2999  		}
  3000  		p.print("`")
  3001  		if e.TagOrNil.Data != nil {
  3002  			p.print(e.HeadRaw)
  3003  		} else {
  3004  			p.printUnquotedUTF16(e.HeadCooked, '`', 0)
  3005  		}
  3006  		for _, part := range e.Parts {
  3007  			p.print("${")
  3008  			p.printExpr(part.Value, js_ast.LLowest, 0)
  3009  			p.addSourceMapping(part.TailLoc)
  3010  			p.print("}")
  3011  			if e.TagOrNil.Data != nil {
  3012  				p.print(part.TailRaw)
  3013  			} else {
  3014  				p.printUnquotedUTF16(part.TailCooked, '`', 0)
  3015  			}
  3016  		}
  3017  		p.print("`")
  3018  
  3019  	case *js_ast.ERegExp:
  3020  		buffer := p.js
  3021  		n := len(buffer)
  3022  
  3023  		// Avoid forming a single-line comment or "</script" sequence
  3024  		if !p.options.UnsupportedFeatures.Has(compat.InlineScript) && n > 0 {
  3025  			if last := buffer[n-1]; last == '/' || (last == '<' && len(e.Value) >= 7 && strings.EqualFold(e.Value[:7], "/script")) {
  3026  				p.print(" ")
  3027  			}
  3028  		}
  3029  
  3030  		p.addSourceMapping(expr.Loc)
  3031  		p.print(e.Value)
  3032  
  3033  		// Need a space before the next identifier to avoid it turning into flags
  3034  		p.prevRegExpEnd = len(p.js)
  3035  
  3036  	case *js_ast.EInlinedEnum:
  3037  		p.printExpr(e.Value, level, flags)
  3038  
  3039  		if !p.options.MinifyWhitespace && !p.options.MinifyIdentifiers {
  3040  			p.print(" /* ")
  3041  			p.print(e.Comment)
  3042  			p.print(" */")
  3043  		}
  3044  
  3045  	case *js_ast.EBigInt:
  3046  		p.printSpaceBeforeIdentifier()
  3047  		p.addSourceMapping(expr.Loc)
  3048  		p.print(e.Value)
  3049  		p.print("n")
  3050  
  3051  	case *js_ast.ENumber:
  3052  		p.addSourceMapping(expr.Loc)
  3053  		p.printNumber(e.Value, level)
  3054  
  3055  	case *js_ast.EIdentifier:
  3056  		name := p.renamer.NameForSymbol(e.Ref)
  3057  		wrap := len(p.js) == p.forOfInitStart && (name == "let" ||
  3058  			((flags&isFollowedByOf) != 0 && (flags&isInsideForAwait) == 0 && name == "async"))
  3059  
  3060  		if wrap {
  3061  			p.print("(")
  3062  		}
  3063  
  3064  		p.printSpaceBeforeIdentifier()
  3065  		p.addSourceMappingForName(expr.Loc, name, e.Ref)
  3066  		p.printIdentifier(name)
  3067  
  3068  		if wrap {
  3069  			p.print(")")
  3070  		}
  3071  
  3072  	case *js_ast.EImportIdentifier:
  3073  		// Potentially use a property access instead of an identifier
  3074  		ref := ast.FollowSymbols(p.symbols, e.Ref)
  3075  		symbol := p.symbols.Get(ref)
  3076  
  3077  		if symbol.ImportItemStatus == ast.ImportItemMissing {
  3078  			p.printUndefined(expr.Loc, level)
  3079  		} else if symbol.NamespaceAlias != nil {
  3080  			wrap := p.callTarget == e && e.WasOriginallyIdentifier
  3081  			if wrap {
  3082  				p.print("(0,")
  3083  				p.printSpace()
  3084  			}
  3085  			p.printSpaceBeforeIdentifier()
  3086  			p.addSourceMapping(expr.Loc)
  3087  			p.printIdentifier(p.renamer.NameForSymbol(symbol.NamespaceAlias.NamespaceRef))
  3088  			alias := symbol.NamespaceAlias.Alias
  3089  			if !e.PreferQuotedKey && p.canPrintIdentifier(alias) {
  3090  				p.print(".")
  3091  				p.addSourceMappingForName(expr.Loc, alias, ref)
  3092  				p.printIdentifier(alias)
  3093  			} else {
  3094  				p.print("[")
  3095  				p.addSourceMappingForName(expr.Loc, alias, ref)
  3096  				p.printQuotedUTF8(alias, printQuotedAllowBacktick)
  3097  				p.print("]")
  3098  			}
  3099  			if wrap {
  3100  				p.print(")")
  3101  			}
  3102  		} else if value := p.options.ConstValues[ref]; value.Kind != js_ast.ConstValueNone {
  3103  			// Handle inlined constants
  3104  			p.printExpr(js_ast.ConstValueToExpr(expr.Loc, value), level, flags)
  3105  		} else {
  3106  			p.printSpaceBeforeIdentifier()
  3107  			name := p.renamer.NameForSymbol(ref)
  3108  			p.addSourceMappingForName(expr.Loc, name, ref)
  3109  			p.printIdentifier(name)
  3110  		}
  3111  
  3112  	case *js_ast.EAwait:
  3113  		wrap := level >= js_ast.LPrefix
  3114  
  3115  		if wrap {
  3116  			p.print("(")
  3117  		}
  3118  
  3119  		p.printSpaceBeforeIdentifier()
  3120  		p.addSourceMapping(expr.Loc)
  3121  		p.print("await")
  3122  		p.printSpace()
  3123  		p.printExpr(e.Value, js_ast.LPrefix-1, 0)
  3124  
  3125  		if wrap {
  3126  			p.print(")")
  3127  		}
  3128  
  3129  	case *js_ast.EYield:
  3130  		wrap := level >= js_ast.LAssign
  3131  
  3132  		if wrap {
  3133  			p.print("(")
  3134  		}
  3135  
  3136  		p.printSpaceBeforeIdentifier()
  3137  		p.addSourceMapping(expr.Loc)
  3138  		p.print("yield")
  3139  
  3140  		if e.ValueOrNil.Data != nil {
  3141  			if e.IsStar {
  3142  				p.print("*")
  3143  			}
  3144  			p.printSpace()
  3145  			p.printExprWithoutLeadingNewline(e.ValueOrNil, js_ast.LYield, 0)
  3146  		}
  3147  
  3148  		if wrap {
  3149  			p.print(")")
  3150  		}
  3151  
  3152  	case *js_ast.EUnary:
  3153  		entry := js_ast.OpTable[e.Op]
  3154  		wrap := level >= entry.Level
  3155  
  3156  		if wrap {
  3157  			p.print("(")
  3158  		}
  3159  
  3160  		if !e.Op.IsPrefix() {
  3161  			p.printExpr(e.Value, js_ast.LPostfix-1, parentWasUnaryOrBinaryOrIfTest)
  3162  		}
  3163  
  3164  		if entry.IsKeyword {
  3165  			p.printSpaceBeforeIdentifier()
  3166  			if e.Op.IsPrefix() {
  3167  				p.addSourceMapping(expr.Loc)
  3168  			}
  3169  			p.print(entry.Text)
  3170  			p.printSpace()
  3171  		} else {
  3172  			p.printSpaceBeforeOperator(e.Op)
  3173  			if e.Op.IsPrefix() {
  3174  				p.addSourceMapping(expr.Loc)
  3175  			}
  3176  			p.print(entry.Text)
  3177  			p.prevOp = e.Op
  3178  			p.prevOpEnd = len(p.js)
  3179  		}
  3180  
  3181  		if e.Op.IsPrefix() {
  3182  			valueFlags := parentWasUnaryOrBinaryOrIfTest
  3183  			if e.Op == js_ast.UnOpDelete {
  3184  				valueFlags |= isDeleteTarget
  3185  			}
  3186  
  3187  			// Never turn "typeof (0, x)" into "typeof x" or "delete (0, x)" into "delete x"
  3188  			if (e.Op == js_ast.UnOpTypeof && !e.WasOriginallyTypeofIdentifier && p.isUnboundIdentifier(e.Value)) ||
  3189  				(e.Op == js_ast.UnOpDelete && !e.WasOriginallyDeleteOfIdentifierOrPropertyAccess && p.isIdentifierOrNumericConstantOrPropertyAccess(e.Value)) {
  3190  				p.print("(0,")
  3191  				p.printSpace()
  3192  				p.printExpr(e.Value, js_ast.LPrefix-1, valueFlags)
  3193  				p.print(")")
  3194  			} else {
  3195  				p.printExpr(e.Value, js_ast.LPrefix-1, valueFlags)
  3196  			}
  3197  		}
  3198  
  3199  		if wrap {
  3200  			p.print(")")
  3201  		}
  3202  
  3203  	case *js_ast.EBinary:
  3204  		// The handling of binary expressions is convoluted because we're using
  3205  		// iteration on the heap instead of recursion on the call stack to avoid
  3206  		// stack overflow for deeply-nested ASTs. See the comments for the similar
  3207  		// code in the JavaScript parser for details.
  3208  		v := binaryExprVisitor{
  3209  			e:     e,
  3210  			level: level,
  3211  			flags: flags,
  3212  		}
  3213  
  3214  		// Use a single stack to reduce allocation overhead
  3215  		stackBottom := len(p.binaryExprStack)
  3216  
  3217  		for {
  3218  			// Check whether this node is a special case, and stop if it is
  3219  			if !v.checkAndPrepare(p) {
  3220  				break
  3221  			}
  3222  
  3223  			left := v.e.Left
  3224  			leftBinary, ok := left.Data.(*js_ast.EBinary)
  3225  
  3226  			// Stop iterating if iteration doesn't apply to the left node
  3227  			if !ok {
  3228  				p.printExpr(left, v.leftLevel, v.leftFlags)
  3229  				v.visitRightAndFinish(p)
  3230  				break
  3231  			}
  3232  
  3233  			// Manually run the code at the start of "printExpr"
  3234  			p.printExprCommentsAtLoc(left.Loc)
  3235  
  3236  			// Only allocate heap memory on the stack for nested binary expressions
  3237  			p.binaryExprStack = append(p.binaryExprStack, v)
  3238  			v = binaryExprVisitor{
  3239  				e:     leftBinary,
  3240  				level: v.leftLevel,
  3241  				flags: v.leftFlags,
  3242  			}
  3243  		}
  3244  
  3245  		// Process all binary operations from the deepest-visited node back toward
  3246  		// our original top-level binary operation
  3247  		for {
  3248  			n := len(p.binaryExprStack) - 1
  3249  			if n < stackBottom {
  3250  				break
  3251  			}
  3252  			v := p.binaryExprStack[n]
  3253  			p.binaryExprStack = p.binaryExprStack[:n]
  3254  			v.visitRightAndFinish(p)
  3255  		}
  3256  
  3257  	default:
  3258  		panic(fmt.Sprintf("Unexpected expression of type %T", expr.Data))
  3259  	}
  3260  }
  3261  
  3262  // The handling of binary expressions is convoluted because we're using
  3263  // iteration on the heap instead of recursion on the call stack to avoid
  3264  // stack overflow for deeply-nested ASTs. See the comments for the similar
  3265  // code in the JavaScript parser for details.
  3266  type binaryExprVisitor struct {
  3267  	// Inputs
  3268  	e     *js_ast.EBinary
  3269  	level js_ast.L
  3270  	flags printExprFlags
  3271  
  3272  	// Input for visiting the left child
  3273  	leftLevel js_ast.L
  3274  	leftFlags printExprFlags
  3275  
  3276  	// "Local variables" passed from "checkAndPrepare" to "visitRightAndFinish"
  3277  	entry      js_ast.OpTableEntry
  3278  	wrap       bool
  3279  	rightLevel js_ast.L
  3280  }
  3281  
  3282  func (v *binaryExprVisitor) checkAndPrepare(p *printer) bool {
  3283  	e := v.e
  3284  
  3285  	// If this is a comma operator then either the result is unused (and we
  3286  	// should have already simplified unused expressions), or the result is used
  3287  	// (and we can still simplify unused expressions inside the left operand)
  3288  	if e.Op == js_ast.BinOpComma {
  3289  		if (v.flags & didAlreadySimplifyUnusedExprs) == 0 {
  3290  			left := p.simplifyUnusedExpr(e.Left)
  3291  			right := e.Right
  3292  			if (v.flags & exprResultIsUnused) != 0 {
  3293  				right = p.simplifyUnusedExpr(right)
  3294  			}
  3295  			if left.Data != e.Left.Data || right.Data != e.Right.Data {
  3296  				// Pass a flag so we don't needlessly re-simplify the same expression
  3297  				p.printExpr(p.guardAgainstBehaviorChangeDueToSubstitution(js_ast.JoinWithComma(left, right), v.flags), v.level, v.flags|didAlreadySimplifyUnusedExprs)
  3298  				return false
  3299  			}
  3300  		} else {
  3301  			// Pass a flag so we don't needlessly re-simplify the same expression
  3302  			v.flags |= didAlreadySimplifyUnusedExprs
  3303  		}
  3304  	}
  3305  
  3306  	v.entry = js_ast.OpTable[e.Op]
  3307  	v.wrap = v.level >= v.entry.Level || (e.Op == js_ast.BinOpIn && (v.flags&forbidIn) != 0)
  3308  
  3309  	// Destructuring assignments must be parenthesized
  3310  	if n := len(p.js); p.stmtStart == n || p.arrowExprStart == n {
  3311  		if _, ok := e.Left.Data.(*js_ast.EObject); ok {
  3312  			v.wrap = true
  3313  		}
  3314  	}
  3315  
  3316  	if v.wrap {
  3317  		p.print("(")
  3318  		v.flags &= ^forbidIn
  3319  	}
  3320  
  3321  	v.leftLevel = v.entry.Level - 1
  3322  	v.rightLevel = v.entry.Level - 1
  3323  
  3324  	if e.Op.IsRightAssociative() {
  3325  		v.leftLevel = v.entry.Level
  3326  	}
  3327  	if e.Op.IsLeftAssociative() {
  3328  		v.rightLevel = v.entry.Level
  3329  	}
  3330  
  3331  	switch e.Op {
  3332  	case js_ast.BinOpNullishCoalescing:
  3333  		// "??" can't directly contain "||" or "&&" without being wrapped in parentheses
  3334  		if left, ok := e.Left.Data.(*js_ast.EBinary); ok && (left.Op == js_ast.BinOpLogicalOr || left.Op == js_ast.BinOpLogicalAnd) {
  3335  			v.leftLevel = js_ast.LPrefix
  3336  		}
  3337  		if right, ok := e.Right.Data.(*js_ast.EBinary); ok && (right.Op == js_ast.BinOpLogicalOr || right.Op == js_ast.BinOpLogicalAnd) {
  3338  			v.rightLevel = js_ast.LPrefix
  3339  		}
  3340  
  3341  	case js_ast.BinOpPow:
  3342  		// "**" can't contain certain unary expressions
  3343  		if left, ok := e.Left.Data.(*js_ast.EUnary); ok && left.Op.UnaryAssignTarget() == js_ast.AssignTargetNone {
  3344  			v.leftLevel = js_ast.LCall
  3345  		} else if _, ok := e.Left.Data.(*js_ast.EAwait); ok {
  3346  			v.leftLevel = js_ast.LCall
  3347  		} else if _, ok := e.Left.Data.(*js_ast.EUndefined); ok {
  3348  			// Undefined is printed as "void 0"
  3349  			v.leftLevel = js_ast.LCall
  3350  		} else if _, ok := e.Left.Data.(*js_ast.ENumber); ok {
  3351  			// Negative numbers are printed using a unary operator
  3352  			v.leftLevel = js_ast.LCall
  3353  		} else if p.options.MinifySyntax {
  3354  			// When minifying, booleans are printed as "!0 and "!1"
  3355  			if _, ok := e.Left.Data.(*js_ast.EBoolean); ok {
  3356  				v.leftLevel = js_ast.LCall
  3357  			}
  3358  		}
  3359  	}
  3360  
  3361  	// Special-case "#foo in bar"
  3362  	if private, ok := e.Left.Data.(*js_ast.EPrivateIdentifier); ok && e.Op == js_ast.BinOpIn {
  3363  		name := p.renamer.NameForSymbol(private.Ref)
  3364  		p.addSourceMappingForName(e.Left.Loc, name, private.Ref)
  3365  		p.printIdentifier(name)
  3366  		v.visitRightAndFinish(p)
  3367  		return false
  3368  	}
  3369  
  3370  	if e.Op == js_ast.BinOpComma {
  3371  		// The result of the left operand of the comma operator is unused
  3372  		v.leftFlags = (v.flags & forbidIn) | exprResultIsUnused | parentWasUnaryOrBinaryOrIfTest
  3373  	} else {
  3374  		v.leftFlags = (v.flags & forbidIn) | parentWasUnaryOrBinaryOrIfTest
  3375  	}
  3376  	return true
  3377  }
  3378  
  3379  func (v *binaryExprVisitor) visitRightAndFinish(p *printer) {
  3380  	e := v.e
  3381  
  3382  	if e.Op != js_ast.BinOpComma {
  3383  		p.printSpace()
  3384  	}
  3385  
  3386  	if v.entry.IsKeyword {
  3387  		p.printSpaceBeforeIdentifier()
  3388  		p.print(v.entry.Text)
  3389  	} else {
  3390  		p.printSpaceBeforeOperator(e.Op)
  3391  		p.print(v.entry.Text)
  3392  		p.prevOp = e.Op
  3393  		p.prevOpEnd = len(p.js)
  3394  	}
  3395  
  3396  	if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  3397  		p.printSpace()
  3398  	}
  3399  
  3400  	if e.Op == js_ast.BinOpComma {
  3401  		// The result of the right operand of the comma operator is unused if the caller doesn't use it
  3402  		p.printExpr(e.Right, v.rightLevel, (v.flags&(forbidIn|exprResultIsUnused))|parentWasUnaryOrBinaryOrIfTest)
  3403  	} else {
  3404  		p.printExpr(e.Right, v.rightLevel, (v.flags&forbidIn)|parentWasUnaryOrBinaryOrIfTest)
  3405  	}
  3406  
  3407  	if v.wrap {
  3408  		p.print(")")
  3409  	}
  3410  }
  3411  
  3412  func (p *printer) isUnboundEvalIdentifier(value js_ast.Expr) bool {
  3413  	if id, ok := value.Data.(*js_ast.EIdentifier); ok {
  3414  		// Using the original name here is ok since unbound symbols are not renamed
  3415  		symbol := p.symbols.Get(ast.FollowSymbols(p.symbols, id.Ref))
  3416  		return symbol.Kind == ast.SymbolUnbound && symbol.OriginalName == "eval"
  3417  	}
  3418  	return false
  3419  }
  3420  
  3421  // Convert an integer to a byte slice without any allocations
  3422  func (p *printer) smallIntToBytes(n int) []byte {
  3423  	wasNegative := n < 0
  3424  	if wasNegative {
  3425  		// This assumes that -math.MinInt isn't a problem. This is fine because
  3426  		// these integers are floating-point exponents which never go up that high.
  3427  		n = -n
  3428  	}
  3429  
  3430  	bytes := p.intToBytesBuffer[:]
  3431  	start := len(bytes)
  3432  
  3433  	// Write out the number from the end to the front
  3434  	for {
  3435  		start--
  3436  		bytes[start] = '0' + byte(n%10)
  3437  		n /= 10
  3438  		if n == 0 {
  3439  			break
  3440  		}
  3441  	}
  3442  
  3443  	// Stick a negative sign on the front if needed
  3444  	if wasNegative {
  3445  		start--
  3446  		bytes[start] = '-'
  3447  	}
  3448  
  3449  	return bytes[start:]
  3450  }
  3451  
  3452  func parseSmallInt(bytes []byte) int {
  3453  	wasNegative := bytes[0] == '-'
  3454  	if wasNegative {
  3455  		bytes = bytes[1:]
  3456  	}
  3457  
  3458  	// Parse the integer without any error checking. This doesn't need to handle
  3459  	// integer overflow because these integers are floating-point exponents which
  3460  	// never go up that high.
  3461  	n := 0
  3462  	for _, c := range bytes {
  3463  		n = n*10 + int(c-'0')
  3464  	}
  3465  
  3466  	if wasNegative {
  3467  		return -n
  3468  	}
  3469  	return n
  3470  }
  3471  
  3472  func (p *printer) printNonNegativeFloat(absValue float64) {
  3473  	// We can avoid the slow call to strconv.FormatFloat() for integers less than
  3474  	// 1000 because we know that exponential notation will always be longer than
  3475  	// the integer representation. This is not the case for 1000 which is "1e3".
  3476  	if absValue < 1000 {
  3477  		if asInt := int64(absValue); absValue == float64(asInt) {
  3478  			p.printBytes(p.smallIntToBytes(int(asInt)))
  3479  
  3480  			// Integers always need a space before "." to avoid making a decimal point
  3481  			p.needSpaceBeforeDot = len(p.js)
  3482  			return
  3483  		}
  3484  	}
  3485  
  3486  	// Format this number into a byte slice so we can mutate it in place without
  3487  	// further reallocation
  3488  	result := []byte(strconv.FormatFloat(absValue, 'g', -1, 64))
  3489  
  3490  	// Simplify the exponent
  3491  	// "e+05" => "e5"
  3492  	// "e-05" => "e-5"
  3493  	if e := bytes.LastIndexByte(result, 'e'); e != -1 {
  3494  		from := e + 1
  3495  		to := from
  3496  
  3497  		switch result[from] {
  3498  		case '+':
  3499  			// Strip off the leading "+"
  3500  			from++
  3501  
  3502  		case '-':
  3503  			// Skip past the leading "-"
  3504  			to++
  3505  			from++
  3506  		}
  3507  
  3508  		// Strip off leading zeros
  3509  		for from < len(result) && result[from] == '0' {
  3510  			from++
  3511  		}
  3512  
  3513  		result = append(result[:to], result[from:]...)
  3514  	}
  3515  
  3516  	dot := bytes.IndexByte(result, '.')
  3517  
  3518  	if dot == 1 && result[0] == '0' {
  3519  		// Simplify numbers starting with "0."
  3520  		afterDot := 2
  3521  
  3522  		// Strip off the leading zero when minifying
  3523  		// "0.5" => ".5"
  3524  		if p.options.MinifyWhitespace {
  3525  			result = result[1:]
  3526  			afterDot--
  3527  		}
  3528  
  3529  		// Try using an exponent
  3530  		// "0.001" => "1e-3"
  3531  		if result[afterDot] == '0' {
  3532  			i := afterDot + 1
  3533  			for result[i] == '0' {
  3534  				i++
  3535  			}
  3536  			remaining := result[i:]
  3537  			exponent := p.smallIntToBytes(afterDot - i - len(remaining))
  3538  
  3539  			// Only switch if it's actually shorter
  3540  			if len(result) > len(remaining)+1+len(exponent) {
  3541  				result = append(append(remaining, 'e'), exponent...)
  3542  			}
  3543  		}
  3544  	} else if dot != -1 {
  3545  		// Try to get rid of a "." and maybe also an "e"
  3546  		if e := bytes.LastIndexByte(result, 'e'); e != -1 {
  3547  			integer := result[:dot]
  3548  			fraction := result[dot+1 : e]
  3549  			exponent := parseSmallInt(result[e+1:]) - len(fraction)
  3550  
  3551  			// Handle small exponents by appending zeros instead
  3552  			if exponent >= 0 && exponent <= 2 {
  3553  				// "1.2e1" => "12"
  3554  				// "1.2e2" => "120"
  3555  				// "1.2e3" => "1200"
  3556  				if len(result) >= len(integer)+len(fraction)+exponent {
  3557  					result = append(integer, fraction...)
  3558  					for i := 0; i < exponent; i++ {
  3559  						result = append(result, '0')
  3560  					}
  3561  				}
  3562  			} else {
  3563  				// "1.2e4" => "12e3"
  3564  				exponent := p.smallIntToBytes(exponent)
  3565  				if len(result) >= len(integer)+len(fraction)+1+len(exponent) {
  3566  					result = append(append(append(integer, fraction...), 'e'), exponent...)
  3567  				}
  3568  			}
  3569  		}
  3570  	} else if result[len(result)-1] == '0' {
  3571  		// Simplify numbers ending with "0" by trying to use an exponent
  3572  		// "1000" => "1e3"
  3573  		i := len(result) - 1
  3574  		for i > 0 && result[i-1] == '0' {
  3575  			i--
  3576  		}
  3577  		remaining := result[:i]
  3578  		exponent := p.smallIntToBytes(len(result) - i)
  3579  
  3580  		// Only switch if it's actually shorter
  3581  		if len(result) > len(remaining)+1+len(exponent) {
  3582  			result = append(append(remaining, 'e'), exponent...)
  3583  		}
  3584  	}
  3585  
  3586  	// Numbers in this range can potentially be printed with one fewer byte as
  3587  	// hex. This compares against 0xFFFF_FFFF_FFFF_F800 instead of comparing
  3588  	// against 0xFFFF_FFFF_FFFF_FFFF because 0xFFFF_FFFF_FFFF_FFFF when converted
  3589  	// to float64 rounds up to 0x1_0000_0000_0000_0180, which can no longer fit
  3590  	// into uint64. In Go, the result of converting float64 to uint64 outside of
  3591  	// the uint64 range is implementation-dependent and is different on amd64 vs.
  3592  	// arm64. The float64 value 0xFFFF_FFFF_FFFF_F800 is the biggest value that
  3593  	// is below the float64 value 0x1_0000_0000_0000_0180, so we use that instead.
  3594  	if p.options.MinifyWhitespace && absValue >= 1_000_000_000_000 && absValue <= 0xFFFF_FFFF_FFFF_F800 {
  3595  		if asInt := uint64(absValue); absValue == float64(asInt) {
  3596  			if hex := strconv.FormatUint(asInt, 16); 2+len(hex) < len(result) {
  3597  				result = append(append(result[:0], '0', 'x'), hex...)
  3598  			}
  3599  		}
  3600  	}
  3601  
  3602  	p.printBytes(result)
  3603  
  3604  	// We'll need a space before "." if it could be parsed as a decimal point
  3605  	if !bytes.ContainsAny(result, ".ex") {
  3606  		p.needSpaceBeforeDot = len(p.js)
  3607  	}
  3608  }
  3609  
  3610  func (p *printer) printDeclStmt(isExport bool, keyword string, decls []js_ast.Decl) {
  3611  	p.printIndent()
  3612  	p.printSpaceBeforeIdentifier()
  3613  	if isExport {
  3614  		p.print("export ")
  3615  	}
  3616  	p.printDecls(keyword, decls, 0)
  3617  	p.printSemicolonAfterStatement()
  3618  }
  3619  
  3620  func (p *printer) printForLoopInit(init js_ast.Stmt, flags printExprFlags) {
  3621  	switch s := init.Data.(type) {
  3622  	case *js_ast.SExpr:
  3623  		p.printExpr(s.Value, js_ast.LLowest, flags|exprResultIsUnused)
  3624  	case *js_ast.SLocal:
  3625  		switch s.Kind {
  3626  		case js_ast.LocalAwaitUsing:
  3627  			p.printDecls("await using", s.Decls, flags)
  3628  		case js_ast.LocalConst:
  3629  			p.printDecls("const", s.Decls, flags)
  3630  		case js_ast.LocalLet:
  3631  			p.printDecls("let", s.Decls, flags)
  3632  		case js_ast.LocalUsing:
  3633  			p.printDecls("using", s.Decls, flags)
  3634  		case js_ast.LocalVar:
  3635  			p.printDecls("var", s.Decls, flags)
  3636  		}
  3637  	default:
  3638  		panic("Internal error")
  3639  	}
  3640  }
  3641  
  3642  func (p *printer) printDecls(keyword string, decls []js_ast.Decl, flags printExprFlags) {
  3643  	p.print(keyword)
  3644  	p.printSpace()
  3645  
  3646  	for i, decl := range decls {
  3647  		if i != 0 {
  3648  			p.print(",")
  3649  			if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  3650  				p.printSpace()
  3651  			}
  3652  		}
  3653  		p.printBinding(decl.Binding)
  3654  
  3655  		if decl.ValueOrNil.Data != nil {
  3656  			p.printSpace()
  3657  			p.print("=")
  3658  			p.printSpace()
  3659  			p.printExprWithoutLeadingNewline(decl.ValueOrNil, js_ast.LComma, flags)
  3660  		}
  3661  	}
  3662  }
  3663  
  3664  func (p *printer) printBody(body js_ast.Stmt, isSingleLine bool) {
  3665  	if block, ok := body.Data.(*js_ast.SBlock); ok {
  3666  		p.printSpace()
  3667  		p.printBlock(body.Loc, *block)
  3668  		p.printNewline()
  3669  	} else if isSingleLine {
  3670  		p.printNextIndentAsSpace = true
  3671  		p.printStmt(body, 0)
  3672  	} else {
  3673  		p.printNewline()
  3674  		p.options.Indent++
  3675  		p.printStmt(body, 0)
  3676  		p.options.Indent--
  3677  	}
  3678  }
  3679  
  3680  func (p *printer) printBlock(loc logger.Loc, block js_ast.SBlock) {
  3681  	p.addSourceMapping(loc)
  3682  	p.print("{")
  3683  	p.printNewline()
  3684  
  3685  	p.options.Indent++
  3686  	for _, stmt := range block.Stmts {
  3687  		p.printSemicolonIfNeeded()
  3688  		p.printStmt(stmt, canOmitStatement)
  3689  	}
  3690  	p.options.Indent--
  3691  	p.needsSemicolon = false
  3692  
  3693  	p.printIndent()
  3694  	if block.CloseBraceLoc.Start > loc.Start {
  3695  		p.addSourceMapping(block.CloseBraceLoc)
  3696  	}
  3697  	p.print("}")
  3698  }
  3699  
  3700  func wrapToAvoidAmbiguousElse(s js_ast.S) bool {
  3701  	for {
  3702  		switch current := s.(type) {
  3703  		case *js_ast.SIf:
  3704  			if current.NoOrNil.Data == nil {
  3705  				return true
  3706  			}
  3707  			s = current.NoOrNil.Data
  3708  
  3709  		case *js_ast.SFor:
  3710  			s = current.Body.Data
  3711  
  3712  		case *js_ast.SForIn:
  3713  			s = current.Body.Data
  3714  
  3715  		case *js_ast.SForOf:
  3716  			s = current.Body.Data
  3717  
  3718  		case *js_ast.SWhile:
  3719  			s = current.Body.Data
  3720  
  3721  		case *js_ast.SWith:
  3722  			s = current.Body.Data
  3723  
  3724  		case *js_ast.SLabel:
  3725  			s = current.Stmt.Data
  3726  
  3727  		default:
  3728  			return false
  3729  		}
  3730  	}
  3731  }
  3732  
  3733  func (p *printer) printIf(s *js_ast.SIf) {
  3734  	p.printSpaceBeforeIdentifier()
  3735  	p.print("if")
  3736  	p.printSpace()
  3737  	p.print("(")
  3738  	if p.willPrintExprCommentsAtLoc(s.Test.Loc) {
  3739  		p.printNewline()
  3740  		p.options.Indent++
  3741  		p.printIndent()
  3742  		p.printExpr(s.Test, js_ast.LLowest, 0)
  3743  		p.printNewline()
  3744  		p.options.Indent--
  3745  		p.printIndent()
  3746  	} else {
  3747  		p.printExpr(s.Test, js_ast.LLowest, 0)
  3748  	}
  3749  	p.print(")")
  3750  
  3751  	// Simplify the else branch, which may disappear entirely
  3752  	no := s.NoOrNil
  3753  	if expr, ok := no.Data.(*js_ast.SExpr); ok {
  3754  		if value := p.simplifyUnusedExpr(expr.Value); value.Data == nil {
  3755  			no.Data = nil
  3756  		} else if value.Data != expr.Value.Data {
  3757  			no.Data = &js_ast.SExpr{Value: value}
  3758  		}
  3759  	}
  3760  
  3761  	if yes, ok := s.Yes.Data.(*js_ast.SBlock); ok {
  3762  		p.printSpace()
  3763  		p.printBlock(s.Yes.Loc, *yes)
  3764  
  3765  		if no.Data != nil {
  3766  			p.printSpace()
  3767  		} else {
  3768  			p.printNewline()
  3769  		}
  3770  	} else if wrapToAvoidAmbiguousElse(s.Yes.Data) {
  3771  		p.printSpace()
  3772  		p.print("{")
  3773  		p.printNewline()
  3774  
  3775  		p.options.Indent++
  3776  		p.printStmt(s.Yes, canOmitStatement)
  3777  		p.options.Indent--
  3778  		p.needsSemicolon = false
  3779  
  3780  		p.printIndent()
  3781  		p.print("}")
  3782  
  3783  		if no.Data != nil {
  3784  			p.printSpace()
  3785  		} else {
  3786  			p.printNewline()
  3787  		}
  3788  	} else {
  3789  		p.printBody(s.Yes, s.IsSingleLineYes)
  3790  
  3791  		if no.Data != nil {
  3792  			p.printIndent()
  3793  		}
  3794  	}
  3795  
  3796  	if no.Data != nil {
  3797  		p.printSemicolonIfNeeded()
  3798  		p.printSpaceBeforeIdentifier()
  3799  		p.print("else")
  3800  
  3801  		if block, ok := no.Data.(*js_ast.SBlock); ok {
  3802  			p.printSpace()
  3803  			p.printBlock(no.Loc, *block)
  3804  			p.printNewline()
  3805  		} else if ifStmt, ok := no.Data.(*js_ast.SIf); ok {
  3806  			p.printIf(ifStmt)
  3807  		} else {
  3808  			p.printBody(no, s.IsSingleLineNo)
  3809  		}
  3810  	}
  3811  }
  3812  
  3813  func (p *printer) printIndentedComment(text string) {
  3814  	// Avoid generating a comment containing the character sequence "</script"
  3815  	if !p.options.UnsupportedFeatures.Has(compat.InlineScript) {
  3816  		text = helpers.EscapeClosingTag(text, "/script")
  3817  	}
  3818  
  3819  	if strings.HasPrefix(text, "/*") {
  3820  		// Re-indent multi-line comments
  3821  		for {
  3822  			newline := strings.IndexByte(text, '\n')
  3823  			if newline == -1 {
  3824  				break
  3825  			}
  3826  			p.print(text[:newline+1])
  3827  			p.printIndent()
  3828  			text = text[newline+1:]
  3829  		}
  3830  		p.print(text)
  3831  		p.printNewline()
  3832  	} else {
  3833  		// Print a mandatory newline after single-line comments
  3834  		p.print(text)
  3835  		p.print("\n")
  3836  	}
  3837  }
  3838  
  3839  func (p *printer) printPath(importRecordIndex uint32, importKind ast.ImportKind) {
  3840  	record := p.importRecords[importRecordIndex]
  3841  	p.addSourceMapping(record.Range.Loc)
  3842  	p.printQuotedUTF8(record.Path.Text, printQuotedNoWrap)
  3843  
  3844  	if p.options.NeedsMetafile {
  3845  		external := ""
  3846  		if (record.Flags & ast.ShouldNotBeExternalInMetafile) == 0 {
  3847  			external = ",\n          \"external\": true"
  3848  		}
  3849  		p.jsonMetadataImports = append(p.jsonMetadataImports, fmt.Sprintf("\n        {\n          \"path\": %s,\n          \"kind\": %s%s\n        }",
  3850  			helpers.QuoteForJSON(record.Path.Text, p.options.ASCIIOnly),
  3851  			helpers.QuoteForJSON(importKind.StringForMetafile(), p.options.ASCIIOnly),
  3852  			external))
  3853  	}
  3854  
  3855  	if record.AssertOrWith != nil && importKind == ast.ImportStmt {
  3856  		feature := compat.ImportAttributes
  3857  		if record.AssertOrWith.Keyword == ast.AssertKeyword {
  3858  			feature = compat.ImportAssertions
  3859  		}
  3860  
  3861  		// Omit import assertions/attributes on this import statement if they would cause a syntax error
  3862  		if p.options.UnsupportedFeatures.Has(feature) {
  3863  			return
  3864  		}
  3865  
  3866  		p.printSpace()
  3867  		p.addSourceMapping(record.AssertOrWith.KeywordLoc)
  3868  		p.print(record.AssertOrWith.Keyword.String())
  3869  		p.printSpace()
  3870  		p.printImportAssertOrWithClause(*record.AssertOrWith)
  3871  	}
  3872  }
  3873  
  3874  func (p *printer) printImportCallAssertOrWith(assertOrWith *ast.ImportAssertOrWith, outerIsMultiLine bool) {
  3875  	// Omit import assertions/attributes if we know the "import()" syntax doesn't
  3876  	// support a second argument (i.e. both import assertions and import
  3877  	// attributes aren't supported) and doing so would cause a syntax error
  3878  	if assertOrWith == nil || (p.options.UnsupportedFeatures.Has(compat.ImportAssertions) && p.options.UnsupportedFeatures.Has(compat.ImportAttributes)) {
  3879  		return
  3880  	}
  3881  
  3882  	isMultiLine := p.willPrintExprCommentsAtLoc(assertOrWith.KeywordLoc) ||
  3883  		p.willPrintExprCommentsAtLoc(assertOrWith.InnerOpenBraceLoc) ||
  3884  		p.willPrintExprCommentsAtLoc(assertOrWith.OuterCloseBraceLoc)
  3885  
  3886  	p.print(",")
  3887  	if outerIsMultiLine {
  3888  		p.printNewline()
  3889  		p.printIndent()
  3890  	} else {
  3891  		p.printSpace()
  3892  	}
  3893  	p.printExprCommentsAtLoc(assertOrWith.OuterOpenBraceLoc)
  3894  	p.addSourceMapping(assertOrWith.OuterOpenBraceLoc)
  3895  	p.print("{")
  3896  
  3897  	if isMultiLine {
  3898  		p.printNewline()
  3899  		p.options.Indent++
  3900  		p.printIndent()
  3901  	} else {
  3902  		p.printSpace()
  3903  	}
  3904  
  3905  	p.printExprCommentsAtLoc(assertOrWith.KeywordLoc)
  3906  	p.addSourceMapping(assertOrWith.KeywordLoc)
  3907  	p.print(assertOrWith.Keyword.String())
  3908  	p.print(":")
  3909  
  3910  	if p.willPrintExprCommentsAtLoc(assertOrWith.InnerOpenBraceLoc) {
  3911  		p.printNewline()
  3912  		p.options.Indent++
  3913  		p.printIndent()
  3914  		p.printExprCommentsAtLoc(assertOrWith.InnerOpenBraceLoc)
  3915  		p.printImportAssertOrWithClause(*assertOrWith)
  3916  		p.options.Indent--
  3917  	} else {
  3918  		p.printSpace()
  3919  		p.printImportAssertOrWithClause(*assertOrWith)
  3920  	}
  3921  
  3922  	if isMultiLine {
  3923  		p.printNewline()
  3924  		p.printExprCommentsAfterCloseTokenAtLoc(assertOrWith.OuterCloseBraceLoc)
  3925  		p.options.Indent--
  3926  		p.printIndent()
  3927  	} else {
  3928  		p.printSpace()
  3929  	}
  3930  
  3931  	p.addSourceMapping(assertOrWith.OuterCloseBraceLoc)
  3932  	p.print("}")
  3933  }
  3934  
  3935  func (p *printer) printImportAssertOrWithClause(assertOrWith ast.ImportAssertOrWith) {
  3936  	isMultiLine := p.willPrintExprCommentsAtLoc(assertOrWith.InnerCloseBraceLoc)
  3937  	if !isMultiLine {
  3938  		for _, entry := range assertOrWith.Entries {
  3939  			if p.willPrintExprCommentsAtLoc(entry.KeyLoc) || p.willPrintExprCommentsAtLoc(entry.ValueLoc) {
  3940  				isMultiLine = true
  3941  				break
  3942  			}
  3943  		}
  3944  	}
  3945  
  3946  	p.addSourceMapping(assertOrWith.InnerOpenBraceLoc)
  3947  	p.print("{")
  3948  	if isMultiLine {
  3949  		p.options.Indent++
  3950  	}
  3951  
  3952  	for i, entry := range assertOrWith.Entries {
  3953  		if i > 0 {
  3954  			p.print(",")
  3955  		}
  3956  		if isMultiLine {
  3957  			p.printNewline()
  3958  			p.printIndent()
  3959  		} else {
  3960  			p.printSpace()
  3961  		}
  3962  
  3963  		p.printExprCommentsAtLoc(entry.KeyLoc)
  3964  		p.addSourceMapping(entry.KeyLoc)
  3965  		if !entry.PreferQuotedKey && p.canPrintIdentifierUTF16(entry.Key) {
  3966  			p.printSpaceBeforeIdentifier()
  3967  			p.printIdentifierUTF16(entry.Key)
  3968  		} else {
  3969  			p.printQuotedUTF16(entry.Key, 0)
  3970  		}
  3971  
  3972  		p.print(":")
  3973  
  3974  		if p.willPrintExprCommentsAtLoc(entry.ValueLoc) {
  3975  			p.printNewline()
  3976  			p.options.Indent++
  3977  			p.printIndent()
  3978  			p.printExprCommentsAtLoc(entry.ValueLoc)
  3979  			p.addSourceMapping(entry.ValueLoc)
  3980  			p.printQuotedUTF16(entry.Value, 0)
  3981  			p.options.Indent--
  3982  		} else {
  3983  			p.printSpace()
  3984  			p.addSourceMapping(entry.ValueLoc)
  3985  			p.printQuotedUTF16(entry.Value, 0)
  3986  		}
  3987  	}
  3988  
  3989  	if isMultiLine {
  3990  		p.printNewline()
  3991  		p.printExprCommentsAfterCloseTokenAtLoc(assertOrWith.InnerCloseBraceLoc)
  3992  		p.options.Indent--
  3993  		p.printIndent()
  3994  	} else if len(assertOrWith.Entries) > 0 {
  3995  		p.printSpace()
  3996  	}
  3997  
  3998  	p.addSourceMapping(assertOrWith.InnerCloseBraceLoc)
  3999  	p.print("}")
  4000  }
  4001  
  4002  type printStmtFlags uint8
  4003  
  4004  const (
  4005  	canOmitStatement printStmtFlags = 1 << iota
  4006  )
  4007  
  4008  func (p *printer) printStmt(stmt js_ast.Stmt, flags printStmtFlags) {
  4009  	if p.options.LineLimit > 0 {
  4010  		p.printNewlinePastLineLimit()
  4011  	}
  4012  
  4013  	switch s := stmt.Data.(type) {
  4014  	case *js_ast.SComment:
  4015  		text := s.Text
  4016  
  4017  		if s.IsLegalComment {
  4018  			switch p.options.LegalComments {
  4019  			case config.LegalCommentsNone:
  4020  				return
  4021  
  4022  			case config.LegalCommentsEndOfFile,
  4023  				config.LegalCommentsLinkedWithComment,
  4024  				config.LegalCommentsExternalWithoutComment:
  4025  
  4026  				// Don't record the same legal comment more than once per file
  4027  				if p.hasLegalComment == nil {
  4028  					p.hasLegalComment = make(map[string]struct{})
  4029  				} else if _, ok := p.hasLegalComment[text]; ok {
  4030  					return
  4031  				}
  4032  				p.hasLegalComment[text] = struct{}{}
  4033  				p.extractedLegalComments = append(p.extractedLegalComments, text)
  4034  				return
  4035  			}
  4036  		}
  4037  
  4038  		p.printIndent()
  4039  		p.addSourceMapping(stmt.Loc)
  4040  		p.printIndentedComment(text)
  4041  
  4042  	case *js_ast.SFunction:
  4043  		if !p.options.MinifyWhitespace && s.Fn.HasNoSideEffectsComment {
  4044  			p.printIndent()
  4045  			p.print("// @__NO_SIDE_EFFECTS__\n")
  4046  		}
  4047  		p.addSourceMapping(stmt.Loc)
  4048  		p.printIndent()
  4049  		p.printSpaceBeforeIdentifier()
  4050  		if s.IsExport {
  4051  			p.print("export ")
  4052  		}
  4053  		if s.Fn.IsAsync {
  4054  			p.print("async ")
  4055  		}
  4056  		p.print("function")
  4057  		if s.Fn.IsGenerator {
  4058  			p.print("*")
  4059  			p.printSpace()
  4060  		}
  4061  		p.printSpaceBeforeIdentifier()
  4062  		name := p.renamer.NameForSymbol(s.Fn.Name.Ref)
  4063  		p.addSourceMappingForName(s.Fn.Name.Loc, name, s.Fn.Name.Ref)
  4064  		p.printIdentifier(name)
  4065  		p.printFn(s.Fn)
  4066  		p.printNewline()
  4067  
  4068  	case *js_ast.SClass:
  4069  		omitIndent := p.printDecorators(s.Class.Decorators, printNewlineAfterDecorator)
  4070  		if !omitIndent {
  4071  			p.printIndent()
  4072  		}
  4073  		p.printSpaceBeforeIdentifier()
  4074  		p.addSourceMapping(stmt.Loc)
  4075  		if s.IsExport {
  4076  			p.print("export ")
  4077  		}
  4078  		p.print("class ")
  4079  		name := p.renamer.NameForSymbol(s.Class.Name.Ref)
  4080  		p.addSourceMappingForName(s.Class.Name.Loc, name, s.Class.Name.Ref)
  4081  		p.printIdentifier(name)
  4082  		p.printClass(s.Class)
  4083  		p.printNewline()
  4084  
  4085  	case *js_ast.SEmpty:
  4086  		p.addSourceMapping(stmt.Loc)
  4087  		p.printIndent()
  4088  		p.print(";")
  4089  		p.printNewline()
  4090  
  4091  	case *js_ast.SExportDefault:
  4092  		if !p.options.MinifyWhitespace {
  4093  			if s2, ok := s.Value.Data.(*js_ast.SFunction); ok && s2.Fn.HasNoSideEffectsComment {
  4094  				p.printIndent()
  4095  				p.print("// @__NO_SIDE_EFFECTS__\n")
  4096  			}
  4097  		}
  4098  		omitIndent := false
  4099  		if s2, ok := s.Value.Data.(*js_ast.SClass); ok {
  4100  			omitIndent = p.printDecorators(s2.Class.Decorators, printNewlineAfterDecorator)
  4101  		}
  4102  		p.addSourceMapping(stmt.Loc)
  4103  		if !omitIndent {
  4104  			p.printIndent()
  4105  		}
  4106  		p.printSpaceBeforeIdentifier()
  4107  		p.print("export default")
  4108  		p.printSpace()
  4109  
  4110  		switch s2 := s.Value.Data.(type) {
  4111  		case *js_ast.SExpr:
  4112  			// Functions and classes must be wrapped to avoid confusion with their statement forms
  4113  			p.exportDefaultStart = len(p.js)
  4114  
  4115  			p.printExprWithoutLeadingNewline(s2.Value, js_ast.LComma, 0)
  4116  			p.printSemicolonAfterStatement()
  4117  			return
  4118  
  4119  		case *js_ast.SFunction:
  4120  			p.printSpaceBeforeIdentifier()
  4121  			if s2.Fn.IsAsync {
  4122  				p.print("async ")
  4123  			}
  4124  			p.print("function")
  4125  			if s2.Fn.IsGenerator {
  4126  				p.print("*")
  4127  				p.printSpace()
  4128  			}
  4129  			if s2.Fn.Name != nil {
  4130  				p.printSpaceBeforeIdentifier()
  4131  				name := p.renamer.NameForSymbol(s2.Fn.Name.Ref)
  4132  				p.addSourceMappingForName(s2.Fn.Name.Loc, name, s2.Fn.Name.Ref)
  4133  				p.printIdentifier(name)
  4134  			}
  4135  			p.printFn(s2.Fn)
  4136  			p.printNewline()
  4137  
  4138  		case *js_ast.SClass:
  4139  			p.printSpaceBeforeIdentifier()
  4140  			p.print("class")
  4141  			if s2.Class.Name != nil {
  4142  				p.print(" ")
  4143  				name := p.renamer.NameForSymbol(s2.Class.Name.Ref)
  4144  				p.addSourceMappingForName(s2.Class.Name.Loc, name, s2.Class.Name.Ref)
  4145  				p.printIdentifier(name)
  4146  			}
  4147  			p.printClass(s2.Class)
  4148  			p.printNewline()
  4149  
  4150  		default:
  4151  			panic("Internal error")
  4152  		}
  4153  
  4154  	case *js_ast.SExportStar:
  4155  		p.addSourceMapping(stmt.Loc)
  4156  		p.printIndent()
  4157  		p.printSpaceBeforeIdentifier()
  4158  		p.print("export")
  4159  		p.printSpace()
  4160  		p.print("*")
  4161  		p.printSpace()
  4162  		if s.Alias != nil {
  4163  			p.print("as")
  4164  			p.printSpace()
  4165  			p.printClauseAlias(s.Alias.Loc, s.Alias.OriginalName)
  4166  			p.printSpace()
  4167  			p.printSpaceBeforeIdentifier()
  4168  		}
  4169  		p.print("from")
  4170  		p.printSpace()
  4171  		p.printPath(s.ImportRecordIndex, ast.ImportStmt)
  4172  		p.printSemicolonAfterStatement()
  4173  
  4174  	case *js_ast.SExportClause:
  4175  		p.addSourceMapping(stmt.Loc)
  4176  		p.printIndent()
  4177  		p.printSpaceBeforeIdentifier()
  4178  		p.print("export")
  4179  		p.printSpace()
  4180  		p.print("{")
  4181  
  4182  		if !s.IsSingleLine {
  4183  			p.options.Indent++
  4184  		}
  4185  
  4186  		for i, item := range s.Items {
  4187  			if i != 0 {
  4188  				p.print(",")
  4189  			}
  4190  
  4191  			if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  4192  				if s.IsSingleLine {
  4193  					p.printSpace()
  4194  				} else {
  4195  					p.printNewline()
  4196  					p.printIndent()
  4197  				}
  4198  			}
  4199  
  4200  			name := p.renamer.NameForSymbol(item.Name.Ref)
  4201  			p.addSourceMappingForName(item.Name.Loc, name, item.Name.Ref)
  4202  			p.printIdentifier(name)
  4203  			if name != item.Alias {
  4204  				p.print(" as")
  4205  				p.printSpace()
  4206  				p.printClauseAlias(item.AliasLoc, item.Alias)
  4207  			}
  4208  		}
  4209  
  4210  		if !s.IsSingleLine {
  4211  			p.options.Indent--
  4212  			p.printNewline()
  4213  			p.printIndent()
  4214  		} else if len(s.Items) > 0 {
  4215  			p.printSpace()
  4216  		}
  4217  
  4218  		p.print("}")
  4219  		p.printSemicolonAfterStatement()
  4220  
  4221  	case *js_ast.SExportFrom:
  4222  		p.addSourceMapping(stmt.Loc)
  4223  		p.printIndent()
  4224  		p.printSpaceBeforeIdentifier()
  4225  		p.print("export")
  4226  		p.printSpace()
  4227  		p.print("{")
  4228  
  4229  		if !s.IsSingleLine {
  4230  			p.options.Indent++
  4231  		}
  4232  
  4233  		for i, item := range s.Items {
  4234  			if i != 0 {
  4235  				p.print(",")
  4236  			}
  4237  
  4238  			if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  4239  				if s.IsSingleLine {
  4240  					p.printSpace()
  4241  				} else {
  4242  					p.printNewline()
  4243  					p.printIndent()
  4244  				}
  4245  			}
  4246  
  4247  			p.printClauseAlias(item.Name.Loc, item.OriginalName)
  4248  			if item.OriginalName != item.Alias {
  4249  				p.printSpace()
  4250  				p.printSpaceBeforeIdentifier()
  4251  				p.print("as")
  4252  				p.printSpace()
  4253  				p.printClauseAlias(item.AliasLoc, item.Alias)
  4254  			}
  4255  		}
  4256  
  4257  		if !s.IsSingleLine {
  4258  			p.options.Indent--
  4259  			p.printNewline()
  4260  			p.printIndent()
  4261  		} else if len(s.Items) > 0 {
  4262  			p.printSpace()
  4263  		}
  4264  
  4265  		p.print("}")
  4266  		p.printSpace()
  4267  		p.print("from")
  4268  		p.printSpace()
  4269  		p.printPath(s.ImportRecordIndex, ast.ImportStmt)
  4270  		p.printSemicolonAfterStatement()
  4271  
  4272  	case *js_ast.SLocal:
  4273  		p.addSourceMapping(stmt.Loc)
  4274  		switch s.Kind {
  4275  		case js_ast.LocalAwaitUsing:
  4276  			p.printDeclStmt(s.IsExport, "await using", s.Decls)
  4277  		case js_ast.LocalConst:
  4278  			p.printDeclStmt(s.IsExport, "const", s.Decls)
  4279  		case js_ast.LocalLet:
  4280  			p.printDeclStmt(s.IsExport, "let", s.Decls)
  4281  		case js_ast.LocalUsing:
  4282  			p.printDeclStmt(s.IsExport, "using", s.Decls)
  4283  		case js_ast.LocalVar:
  4284  			p.printDeclStmt(s.IsExport, "var", s.Decls)
  4285  		}
  4286  
  4287  	case *js_ast.SIf:
  4288  		p.addSourceMapping(stmt.Loc)
  4289  		p.printIndent()
  4290  		p.printIf(s)
  4291  
  4292  	case *js_ast.SDoWhile:
  4293  		p.addSourceMapping(stmt.Loc)
  4294  		p.printIndent()
  4295  		p.printSpaceBeforeIdentifier()
  4296  		p.print("do")
  4297  		if block, ok := s.Body.Data.(*js_ast.SBlock); ok {
  4298  			p.printSpace()
  4299  			p.printBlock(s.Body.Loc, *block)
  4300  			p.printSpace()
  4301  		} else {
  4302  			p.printNewline()
  4303  			p.options.Indent++
  4304  			p.printStmt(s.Body, 0)
  4305  			p.printSemicolonIfNeeded()
  4306  			p.options.Indent--
  4307  			p.printIndent()
  4308  		}
  4309  		p.print("while")
  4310  		p.printSpace()
  4311  		p.print("(")
  4312  		if p.willPrintExprCommentsAtLoc(s.Test.Loc) {
  4313  			p.printNewline()
  4314  			p.options.Indent++
  4315  			p.printIndent()
  4316  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4317  			p.printNewline()
  4318  			p.options.Indent--
  4319  			p.printIndent()
  4320  		} else {
  4321  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4322  		}
  4323  		p.print(")")
  4324  		p.printSemicolonAfterStatement()
  4325  
  4326  	case *js_ast.SForIn:
  4327  		p.addSourceMapping(stmt.Loc)
  4328  		p.printIndent()
  4329  		p.printSpaceBeforeIdentifier()
  4330  		p.print("for")
  4331  		p.printSpace()
  4332  		p.print("(")
  4333  		hasInitComment := p.willPrintExprCommentsAtLoc(s.Init.Loc)
  4334  		hasValueComment := p.willPrintExprCommentsAtLoc(s.Value.Loc)
  4335  		if hasInitComment || hasValueComment {
  4336  			p.printNewline()
  4337  			p.options.Indent++
  4338  			p.printIndent()
  4339  		}
  4340  		p.printForLoopInit(s.Init, forbidIn)
  4341  		p.printSpace()
  4342  		p.printSpaceBeforeIdentifier()
  4343  		p.print("in")
  4344  		if hasValueComment {
  4345  			p.printNewline()
  4346  			p.printIndent()
  4347  		} else {
  4348  			p.printSpace()
  4349  		}
  4350  		p.printExpr(s.Value, js_ast.LLowest, 0)
  4351  		if hasInitComment || hasValueComment {
  4352  			p.printNewline()
  4353  			p.options.Indent--
  4354  			p.printIndent()
  4355  		}
  4356  		p.print(")")
  4357  		p.printBody(s.Body, s.IsSingleLineBody)
  4358  
  4359  	case *js_ast.SForOf:
  4360  		p.addSourceMapping(stmt.Loc)
  4361  		p.printIndent()
  4362  		p.printSpaceBeforeIdentifier()
  4363  		p.print("for")
  4364  		if s.Await.Len > 0 {
  4365  			p.print(" await")
  4366  		}
  4367  		p.printSpace()
  4368  		p.print("(")
  4369  		hasInitComment := p.willPrintExprCommentsAtLoc(s.Init.Loc)
  4370  		hasValueComment := p.willPrintExprCommentsAtLoc(s.Value.Loc)
  4371  		flags := forbidIn | isFollowedByOf
  4372  		if s.Await.Len > 0 {
  4373  			flags |= isInsideForAwait
  4374  		}
  4375  		if hasInitComment || hasValueComment {
  4376  			p.printNewline()
  4377  			p.options.Indent++
  4378  			p.printIndent()
  4379  		}
  4380  		p.forOfInitStart = len(p.js)
  4381  		p.printForLoopInit(s.Init, flags)
  4382  		p.printSpace()
  4383  		p.printSpaceBeforeIdentifier()
  4384  		p.print("of")
  4385  		if hasValueComment {
  4386  			p.printNewline()
  4387  			p.printIndent()
  4388  		} else {
  4389  			p.printSpace()
  4390  		}
  4391  		p.printExpr(s.Value, js_ast.LComma, 0)
  4392  		if hasInitComment || hasValueComment {
  4393  			p.printNewline()
  4394  			p.options.Indent--
  4395  			p.printIndent()
  4396  		}
  4397  		p.print(")")
  4398  		p.printBody(s.Body, s.IsSingleLineBody)
  4399  
  4400  	case *js_ast.SWhile:
  4401  		p.addSourceMapping(stmt.Loc)
  4402  		p.printIndent()
  4403  		p.printSpaceBeforeIdentifier()
  4404  		p.print("while")
  4405  		p.printSpace()
  4406  		p.print("(")
  4407  		if p.willPrintExprCommentsAtLoc(s.Test.Loc) {
  4408  			p.printNewline()
  4409  			p.options.Indent++
  4410  			p.printIndent()
  4411  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4412  			p.printNewline()
  4413  			p.options.Indent--
  4414  			p.printIndent()
  4415  		} else {
  4416  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4417  		}
  4418  		p.print(")")
  4419  		p.printBody(s.Body, s.IsSingleLineBody)
  4420  
  4421  	case *js_ast.SWith:
  4422  		p.addSourceMapping(stmt.Loc)
  4423  		p.printIndent()
  4424  		p.printSpaceBeforeIdentifier()
  4425  		p.print("with")
  4426  		p.printSpace()
  4427  		p.print("(")
  4428  		if p.willPrintExprCommentsAtLoc(s.Value.Loc) {
  4429  			p.printNewline()
  4430  			p.options.Indent++
  4431  			p.printIndent()
  4432  			p.printExpr(s.Value, js_ast.LLowest, 0)
  4433  			p.printNewline()
  4434  			p.options.Indent--
  4435  			p.printIndent()
  4436  		} else {
  4437  			p.printExpr(s.Value, js_ast.LLowest, 0)
  4438  		}
  4439  		p.print(")")
  4440  		p.withNesting++
  4441  		p.printBody(s.Body, s.IsSingleLineBody)
  4442  		p.withNesting--
  4443  
  4444  	case *js_ast.SLabel:
  4445  		// Avoid printing a source mapping that masks the one from the label
  4446  		if !p.options.MinifyWhitespace && (p.options.Indent > 0 || p.printNextIndentAsSpace) {
  4447  			p.addSourceMapping(stmt.Loc)
  4448  			p.printIndent()
  4449  		}
  4450  
  4451  		p.printSpaceBeforeIdentifier()
  4452  		name := p.renamer.NameForSymbol(s.Name.Ref)
  4453  		p.addSourceMappingForName(s.Name.Loc, name, s.Name.Ref)
  4454  		p.printIdentifier(name)
  4455  		p.print(":")
  4456  		p.printBody(s.Stmt, s.IsSingleLineStmt)
  4457  
  4458  	case *js_ast.STry:
  4459  		p.addSourceMapping(stmt.Loc)
  4460  		p.printIndent()
  4461  		p.printSpaceBeforeIdentifier()
  4462  		p.print("try")
  4463  		p.printSpace()
  4464  		p.printBlock(s.BlockLoc, s.Block)
  4465  
  4466  		if s.Catch != nil {
  4467  			p.printSpace()
  4468  			p.print("catch")
  4469  			if s.Catch.BindingOrNil.Data != nil {
  4470  				p.printSpace()
  4471  				p.print("(")
  4472  				p.printBinding(s.Catch.BindingOrNil)
  4473  				p.print(")")
  4474  			}
  4475  			p.printSpace()
  4476  			p.printBlock(s.Catch.BlockLoc, s.Catch.Block)
  4477  		}
  4478  
  4479  		if s.Finally != nil {
  4480  			p.printSpace()
  4481  			p.print("finally")
  4482  			p.printSpace()
  4483  			p.printBlock(s.Finally.Loc, s.Finally.Block)
  4484  		}
  4485  
  4486  		p.printNewline()
  4487  
  4488  	case *js_ast.SFor:
  4489  		init := s.InitOrNil
  4490  		update := s.UpdateOrNil
  4491  
  4492  		// Omit calls to empty functions from the output completely
  4493  		if p.options.MinifySyntax {
  4494  			if expr, ok := init.Data.(*js_ast.SExpr); ok {
  4495  				if value := p.simplifyUnusedExpr(expr.Value); value.Data == nil {
  4496  					init.Data = nil
  4497  				} else if value.Data != expr.Value.Data {
  4498  					init.Data = &js_ast.SExpr{Value: value}
  4499  				}
  4500  			}
  4501  			if update.Data != nil {
  4502  				update = p.simplifyUnusedExpr(update)
  4503  			}
  4504  		}
  4505  
  4506  		p.addSourceMapping(stmt.Loc)
  4507  		p.printIndent()
  4508  		p.printSpaceBeforeIdentifier()
  4509  		p.print("for")
  4510  		p.printSpace()
  4511  		p.print("(")
  4512  		isMultiLine :=
  4513  			(init.Data != nil && p.willPrintExprCommentsAtLoc(init.Loc)) ||
  4514  				(s.TestOrNil.Data != nil && p.willPrintExprCommentsAtLoc(s.TestOrNil.Loc)) ||
  4515  				(update.Data != nil && p.willPrintExprCommentsAtLoc(update.Loc))
  4516  		if isMultiLine {
  4517  			p.printNewline()
  4518  			p.options.Indent++
  4519  			p.printIndent()
  4520  		}
  4521  		if init.Data != nil {
  4522  			p.printForLoopInit(init, forbidIn)
  4523  		}
  4524  		p.print(";")
  4525  		if isMultiLine {
  4526  			p.printNewline()
  4527  			p.printIndent()
  4528  		} else {
  4529  			p.printSpace()
  4530  		}
  4531  		if s.TestOrNil.Data != nil {
  4532  			p.printExpr(s.TestOrNil, js_ast.LLowest, 0)
  4533  		}
  4534  		p.print(";")
  4535  		if !isMultiLine {
  4536  			p.printSpace()
  4537  		} else if update.Data != nil {
  4538  			p.printNewline()
  4539  			p.printIndent()
  4540  		}
  4541  		if update.Data != nil {
  4542  			p.printExpr(update, js_ast.LLowest, exprResultIsUnused)
  4543  		}
  4544  		if isMultiLine {
  4545  			p.printNewline()
  4546  			p.options.Indent--
  4547  			p.printIndent()
  4548  		}
  4549  		p.print(")")
  4550  		p.printBody(s.Body, s.IsSingleLineBody)
  4551  
  4552  	case *js_ast.SSwitch:
  4553  		p.addSourceMapping(stmt.Loc)
  4554  		p.printIndent()
  4555  		p.printSpaceBeforeIdentifier()
  4556  		p.print("switch")
  4557  		p.printSpace()
  4558  		p.print("(")
  4559  		if p.willPrintExprCommentsAtLoc(s.Test.Loc) {
  4560  			p.printNewline()
  4561  			p.options.Indent++
  4562  			p.printIndent()
  4563  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4564  			p.printNewline()
  4565  			p.options.Indent--
  4566  			p.printIndent()
  4567  		} else {
  4568  			p.printExpr(s.Test, js_ast.LLowest, 0)
  4569  		}
  4570  		p.print(")")
  4571  		p.printSpace()
  4572  		p.addSourceMapping(s.BodyLoc)
  4573  		p.print("{")
  4574  		p.printNewline()
  4575  		p.options.Indent++
  4576  
  4577  		for _, c := range s.Cases {
  4578  			p.printSemicolonIfNeeded()
  4579  			p.printIndent()
  4580  			p.addSourceMapping(c.Loc)
  4581  
  4582  			if c.ValueOrNil.Data != nil {
  4583  				p.print("case")
  4584  				p.printSpace()
  4585  				p.printExpr(c.ValueOrNil, js_ast.LLogicalAnd, 0)
  4586  			} else {
  4587  				p.print("default")
  4588  			}
  4589  			p.print(":")
  4590  
  4591  			if len(c.Body) == 1 {
  4592  				if block, ok := c.Body[0].Data.(*js_ast.SBlock); ok {
  4593  					p.printSpace()
  4594  					p.printBlock(c.Body[0].Loc, *block)
  4595  					p.printNewline()
  4596  					continue
  4597  				}
  4598  			}
  4599  
  4600  			p.printNewline()
  4601  			p.options.Indent++
  4602  			for _, stmt := range c.Body {
  4603  				p.printSemicolonIfNeeded()
  4604  				p.printStmt(stmt, canOmitStatement)
  4605  			}
  4606  			p.options.Indent--
  4607  		}
  4608  
  4609  		p.options.Indent--
  4610  		p.printIndent()
  4611  		p.addSourceMapping(s.CloseBraceLoc)
  4612  		p.print("}")
  4613  		p.printNewline()
  4614  		p.needsSemicolon = false
  4615  
  4616  	case *js_ast.SImport:
  4617  		itemCount := 0
  4618  
  4619  		p.addSourceMapping(stmt.Loc)
  4620  		p.printIndent()
  4621  		p.printSpaceBeforeIdentifier()
  4622  		p.print("import")
  4623  		p.printSpace()
  4624  
  4625  		if s.DefaultName != nil {
  4626  			p.printSpaceBeforeIdentifier()
  4627  			name := p.renamer.NameForSymbol(s.DefaultName.Ref)
  4628  			p.addSourceMappingForName(s.DefaultName.Loc, name, s.DefaultName.Ref)
  4629  			p.printIdentifier(name)
  4630  			itemCount++
  4631  		}
  4632  
  4633  		if s.Items != nil {
  4634  			if itemCount > 0 {
  4635  				p.print(",")
  4636  				p.printSpace()
  4637  			}
  4638  
  4639  			p.print("{")
  4640  			if !s.IsSingleLine {
  4641  				p.options.Indent++
  4642  			}
  4643  
  4644  			for i, item := range *s.Items {
  4645  				if i != 0 {
  4646  					p.print(",")
  4647  				}
  4648  
  4649  				if p.options.LineLimit <= 0 || !p.printNewlinePastLineLimit() {
  4650  					if s.IsSingleLine {
  4651  						p.printSpace()
  4652  					} else {
  4653  						p.printNewline()
  4654  						p.printIndent()
  4655  					}
  4656  				}
  4657  
  4658  				p.printClauseAlias(item.AliasLoc, item.Alias)
  4659  
  4660  				name := p.renamer.NameForSymbol(item.Name.Ref)
  4661  				if name != item.Alias {
  4662  					p.printSpace()
  4663  					p.printSpaceBeforeIdentifier()
  4664  					p.print("as ")
  4665  					p.addSourceMappingForName(item.Name.Loc, name, item.Name.Ref)
  4666  					p.printIdentifier(name)
  4667  				}
  4668  			}
  4669  
  4670  			if !s.IsSingleLine {
  4671  				p.options.Indent--
  4672  				p.printNewline()
  4673  				p.printIndent()
  4674  			} else if len(*s.Items) > 0 {
  4675  				p.printSpace()
  4676  			}
  4677  
  4678  			p.print("}")
  4679  			itemCount++
  4680  		}
  4681  
  4682  		if s.StarNameLoc != nil {
  4683  			if itemCount > 0 {
  4684  				p.print(",")
  4685  				p.printSpace()
  4686  			}
  4687  
  4688  			p.print("*")
  4689  			p.printSpace()
  4690  			p.print("as ")
  4691  			name := p.renamer.NameForSymbol(s.NamespaceRef)
  4692  			p.addSourceMappingForName(*s.StarNameLoc, name, s.NamespaceRef)
  4693  			p.printIdentifier(name)
  4694  			itemCount++
  4695  		}
  4696  
  4697  		if itemCount > 0 {
  4698  			p.printSpace()
  4699  			p.printSpaceBeforeIdentifier()
  4700  			p.print("from")
  4701  			p.printSpace()
  4702  		}
  4703  
  4704  		p.printPath(s.ImportRecordIndex, ast.ImportStmt)
  4705  		p.printSemicolonAfterStatement()
  4706  
  4707  	case *js_ast.SBlock:
  4708  		p.addSourceMapping(stmt.Loc)
  4709  		p.printIndent()
  4710  		p.printBlock(stmt.Loc, *s)
  4711  		p.printNewline()
  4712  
  4713  	case *js_ast.SDebugger:
  4714  		p.addSourceMapping(stmt.Loc)
  4715  		p.printIndent()
  4716  		p.printSpaceBeforeIdentifier()
  4717  		p.print("debugger")
  4718  		p.printSemicolonAfterStatement()
  4719  
  4720  	case *js_ast.SDirective:
  4721  		p.addSourceMapping(stmt.Loc)
  4722  		p.printIndent()
  4723  		p.printSpaceBeforeIdentifier()
  4724  		p.printQuotedUTF16(s.Value, 0)
  4725  		p.printSemicolonAfterStatement()
  4726  
  4727  	case *js_ast.SBreak:
  4728  		p.addSourceMapping(stmt.Loc)
  4729  		p.printIndent()
  4730  		p.printSpaceBeforeIdentifier()
  4731  		p.print("break")
  4732  		if s.Label != nil {
  4733  			p.print(" ")
  4734  			name := p.renamer.NameForSymbol(s.Label.Ref)
  4735  			p.addSourceMappingForName(s.Label.Loc, name, s.Label.Ref)
  4736  			p.printIdentifier(name)
  4737  		}
  4738  		p.printSemicolonAfterStatement()
  4739  
  4740  	case *js_ast.SContinue:
  4741  		p.addSourceMapping(stmt.Loc)
  4742  		p.printIndent()
  4743  		p.printSpaceBeforeIdentifier()
  4744  		p.print("continue")
  4745  		if s.Label != nil {
  4746  			p.print(" ")
  4747  			name := p.renamer.NameForSymbol(s.Label.Ref)
  4748  			p.addSourceMappingForName(s.Label.Loc, name, s.Label.Ref)
  4749  			p.printIdentifier(name)
  4750  		}
  4751  		p.printSemicolonAfterStatement()
  4752  
  4753  	case *js_ast.SReturn:
  4754  		p.addSourceMapping(stmt.Loc)
  4755  		p.printIndent()
  4756  		p.printSpaceBeforeIdentifier()
  4757  		p.print("return")
  4758  		if s.ValueOrNil.Data != nil {
  4759  			p.printSpace()
  4760  			p.printExprWithoutLeadingNewline(s.ValueOrNil, js_ast.LLowest, 0)
  4761  		}
  4762  		p.printSemicolonAfterStatement()
  4763  
  4764  	case *js_ast.SThrow:
  4765  		p.addSourceMapping(stmt.Loc)
  4766  		p.printIndent()
  4767  		p.printSpaceBeforeIdentifier()
  4768  		p.print("throw")
  4769  		p.printSpace()
  4770  		p.printExprWithoutLeadingNewline(s.Value, js_ast.LLowest, 0)
  4771  		p.printSemicolonAfterStatement()
  4772  
  4773  	case *js_ast.SExpr:
  4774  		value := s.Value
  4775  
  4776  		// Omit calls to empty functions from the output completely
  4777  		if p.options.MinifySyntax {
  4778  			value = p.simplifyUnusedExpr(value)
  4779  			if value.Data == nil {
  4780  				// If this statement is not in a block, then we still need to emit something
  4781  				if (flags & canOmitStatement) == 0 {
  4782  					// "if (x) empty();" => "if (x) ;"
  4783  					p.addSourceMapping(stmt.Loc)
  4784  					p.printIndent()
  4785  					p.print(";")
  4786  					p.printNewline()
  4787  				} else {
  4788  					// "if (x) { empty(); }" => "if (x) {}"
  4789  				}
  4790  				break
  4791  			}
  4792  		}
  4793  
  4794  		// Avoid printing a source mapping when the expression would print one in
  4795  		// the same spot. We don't want to accidentally mask the mapping it emits.
  4796  		if !p.options.MinifyWhitespace && (p.options.Indent > 0 || p.printNextIndentAsSpace) {
  4797  			p.addSourceMapping(stmt.Loc)
  4798  			p.printIndent()
  4799  		}
  4800  
  4801  		p.stmtStart = len(p.js)
  4802  		p.printExpr(value, js_ast.LLowest, exprResultIsUnused)
  4803  		p.printSemicolonAfterStatement()
  4804  
  4805  	default:
  4806  		panic(fmt.Sprintf("Unexpected statement of type %T", stmt.Data))
  4807  	}
  4808  }
  4809  
  4810  type Options struct {
  4811  	RequireOrImportMetaForSource func(uint32) RequireOrImportMeta
  4812  
  4813  	// Cross-module inlining of TypeScript enums is actually done during printing
  4814  	TSEnums map[ast.Ref]map[string]js_ast.TSEnumValue
  4815  
  4816  	// Cross-module inlining of detected inlinable constants is also done during printing
  4817  	ConstValues map[ast.Ref]js_ast.ConstValue
  4818  
  4819  	// Property mangling results go here
  4820  	MangledProps map[ast.Ref]string
  4821  
  4822  	// This will be present if the input file had a source map. In that case we
  4823  	// want to map all the way back to the original input file(s).
  4824  	InputSourceMap *sourcemap.SourceMap
  4825  
  4826  	// If we're writing out a source map, this table of line start indices lets
  4827  	// us do binary search on to figure out what line a given AST node came from
  4828  	LineOffsetTables []sourcemap.LineOffsetTable
  4829  
  4830  	ToCommonJSRef       ast.Ref
  4831  	ToESMRef            ast.Ref
  4832  	RuntimeRequireRef   ast.Ref
  4833  	UnsupportedFeatures compat.JSFeature
  4834  	Indent              int
  4835  	LineLimit           int
  4836  	OutputFormat        config.Format
  4837  	MinifyWhitespace    bool
  4838  	MinifyIdentifiers   bool
  4839  	MinifySyntax        bool
  4840  	ASCIIOnly           bool
  4841  	LegalComments       config.LegalComments
  4842  	SourceMap           config.SourceMap
  4843  	AddSourceMappings   bool
  4844  	NeedsMetafile       bool
  4845  }
  4846  
  4847  type RequireOrImportMeta struct {
  4848  	// CommonJS files will return the "require_*" wrapper function and an invalid
  4849  	// exports object reference. Lazily-initialized ESM files will return the
  4850  	// "init_*" wrapper function and the exports object for that file.
  4851  	WrapperRef     ast.Ref
  4852  	ExportsRef     ast.Ref
  4853  	IsWrapperAsync bool
  4854  }
  4855  
  4856  type PrintResult struct {
  4857  	JS                     []byte
  4858  	ExtractedLegalComments []string
  4859  	JSONMetadataImports    []string
  4860  
  4861  	// This source map chunk just contains the VLQ-encoded offsets for the "JS"
  4862  	// field above. It's not a full source map. The bundler will be joining many
  4863  	// source map chunks together to form the final source map.
  4864  	SourceMapChunk sourcemap.Chunk
  4865  }
  4866  
  4867  func Print(tree js_ast.AST, symbols ast.SymbolMap, r renamer.Renamer, options Options) PrintResult {
  4868  	p := &printer{
  4869  		symbols:       symbols,
  4870  		renamer:       r,
  4871  		importRecords: tree.ImportRecords,
  4872  		options:       options,
  4873  		moduleType:    tree.ModuleTypeData.Type,
  4874  		exprComments:  tree.ExprComments,
  4875  		wasLazyExport: tree.HasLazyExport,
  4876  
  4877  		stmtStart:          -1,
  4878  		exportDefaultStart: -1,
  4879  		arrowExprStart:     -1,
  4880  		forOfInitStart:     -1,
  4881  
  4882  		prevOpEnd:            -1,
  4883  		needSpaceBeforeDot:   -1,
  4884  		prevRegExpEnd:        -1,
  4885  		noLeadingNewlineHere: -1,
  4886  		builder:              sourcemap.MakeChunkBuilder(options.InputSourceMap, options.LineOffsetTables, options.ASCIIOnly),
  4887  	}
  4888  
  4889  	if p.exprComments != nil {
  4890  		p.printedExprComments = make(map[logger.Loc]bool)
  4891  	}
  4892  
  4893  	p.astHelpers = js_ast.MakeHelperContext(func(ref ast.Ref) bool {
  4894  		ref = ast.FollowSymbols(symbols, ref)
  4895  		return symbols.Get(ref).Kind == ast.SymbolUnbound
  4896  	})
  4897  
  4898  	// Add the top-level directive if present
  4899  	for _, directive := range tree.Directives {
  4900  		p.printIndent()
  4901  		p.printQuotedUTF8(directive, 0)
  4902  		p.print(";")
  4903  		p.printNewline()
  4904  	}
  4905  
  4906  	for _, part := range tree.Parts {
  4907  		for _, stmt := range part.Stmts {
  4908  			p.printStmt(stmt, canOmitStatement)
  4909  			p.printSemicolonIfNeeded()
  4910  		}
  4911  	}
  4912  
  4913  	result := PrintResult{
  4914  		JS:                     p.js,
  4915  		JSONMetadataImports:    p.jsonMetadataImports,
  4916  		ExtractedLegalComments: p.extractedLegalComments,
  4917  	}
  4918  	if options.SourceMap != config.SourceMapNone {
  4919  		// This is expensive. Only do this if it's necessary.
  4920  		result.SourceMapChunk = p.builder.GenerateChunk(p.js)
  4921  	}
  4922  	return result
  4923  }