package js_printer

import (
	
	
	
	
	
	

	
	
	
	
	
	
	
	
)

var positiveInfinity = math.Inf(1)
var negativeInfinity = math.Inf(-1)
Coordinates in source maps are stored using relative offsets for size reasons. When joining together chunks of a source map that were emitted in parallel for different parts of a file, we need to fix up the first segment of each chunk to be relative to the end of the previous chunk.
This isn't stored in the source map. It's only used by the bundler to join source map chunks together correctly.
These are stored in the source map in VLQ format.
Source map chunks are computed in parallel for speed. Each chunk is relative to the zero state instead of being relative to the end state of the previous chunk, since it's impossible to know the end state of the previous chunk in a parallel computation. After all chunks are computed, they are joined together in a second pass. This rewrites the first mapping in each chunk to be relative to the end state of the previous chunk.
Handle line breaks in between this mapping and the previous one
	if .GeneratedLine != 0 {
		.AddBytes(bytes.Repeat([]byte{';'}, .GeneratedLine))
		.GeneratedColumn = 0
	}
Skip past any leading semicolons, which indicate line breaks
	 := 0
	for [] == ';' {
		++
	}
	if  > 0 {
		.AddBytes([:])
		 = [:]
		.GeneratedColumn = 0
		.GeneratedColumn = 0
	}
Strip off the first mapping from the buffer. The first mapping should be for the start of the original file (the printer always generates one for the start of the file).
	,  := sourcemap.DecodeVLQ(, 0)
	,  := sourcemap.DecodeVLQ(, )
	,  := sourcemap.DecodeVLQ(, )
	,  := sourcemap.DecodeVLQ(, )
	 = [:]
Rewrite the first mapping to be relative to the end state of the previous chunk. We now know what the end state is because we're in the second pass where all chunks have already been generated.
	.SourceIndex += 
	.GeneratedColumn += 
	.OriginalLine += 
	.OriginalColumn += 
	.AddBytes(appendMapping(nil, .lastByte, , ))
Then append everything after that without modification.
	.AddBytes()
}

Put commas in between mappings
	if  != 0 &&  != ';' &&  != '"' {
		 = append(, ',')
	}
Record the generated column (the line is recorded using ';' elsewhere)
	 = append(, sourcemap.EncodeVLQ(.GeneratedColumn-.GeneratedColumn)...)
	.GeneratedColumn = .GeneratedColumn
Record the generated source
	 = append(, sourcemap.EncodeVLQ(.SourceIndex-.SourceIndex)...)
	.SourceIndex = .SourceIndex
Record the original line
	 = append(, sourcemap.EncodeVLQ(.OriginalLine-.OriginalLine)...)
	.OriginalLine = .OriginalLine
Record the original column
	 = append(, sourcemap.EncodeVLQ(.OriginalColumn-.OriginalColumn)...)
	.OriginalColumn = .OriginalColumn

	return 
}
This provides an efficient way to join lots of big string and byte slices together. It avoids the cost of repeatedly reallocating as the buffer grows by measuring exactly how big the buffer should be and then allocating once. This is a measurable speedup.
type Joiner struct {
	lastByte byte
	strings  []joinerString
	bytes    []joinerBytes
	length   uint32
}

type joinerString struct {
	data   string
	offset uint32
}

type joinerBytes struct {
	data   []byte
	offset uint32
}

func ( *Joiner) ( string) {
	if len() > 0 {
		.lastByte = [len()-1]
	}
	.strings = append(.strings, joinerString{, .length})
	.length += uint32(len())
}

func ( *Joiner) ( []byte) {
	if len() > 0 {
		.lastByte = [len()-1]
	}
	.bytes = append(.bytes, joinerBytes{, .length})
	.length += uint32(len())
}

func ( *Joiner) () byte {
	return .lastByte
}

func ( *Joiner) () uint32 {
	return .length
}

func ( *Joiner) () []byte {
	 := make([]byte, .length)
	for ,  := range .strings {
		copy([.offset:], .data)
	}
	for ,  := range .bytes {
		copy([.offset:], .data)
	}
	return 
}

const hexChars = "0123456789ABCDEF"
const firstASCII = 0x20
const lastASCII = 0x7E
const firstHighSurrogate = 0xD800
const lastHighSurrogate = 0xDBFF
const firstLowSurrogate = 0xDC00
const lastLowSurrogate = 0xDFFF

func ( rune,  bool) bool {
	if  <= lastASCII {
		return  >= firstASCII &&  != '\\' &&  != '"'
	} else {
		return ! &&  != '\uFEFF' && ( < firstHighSurrogate ||  > lastLowSurrogate)
	}
}

Estimate the required length
	 := 2
	for ,  := range  {
		if canPrintWithoutEscape(, ) {
			 += utf8.RuneLen()
		} else {
			switch  {
			case '\b', '\f', '\n', '\r', '\t', '\\', '"':
				 += 2
			default:
				if  <= 0xFFFF {
					 += 6
				} else {
					 += 12
				}
			}
		}
	}
Preallocate the array
	 := make([]byte, 0, )
	 := 0
	 := len()
	 = append(, '"')

	for  <  {
		,  := js_lexer.DecodeWTF8Rune([:])
Fast path: a run of characters that don't need escaping
		if canPrintWithoutEscape(, ) {
			 := 
			 += 
			for  <  {
				,  = js_lexer.DecodeWTF8Rune([:])
				if !canPrintWithoutEscape(, ) {
					break
				}
				 += 
			}
			 = append(, [:]...)
			continue
		}

		switch  {
		case '\b':
			 = append(, "\\b"...)
			++

		case '\f':
			 = append(, "\\f"...)
			++

		case '\n':
			 = append(, "\\n"...)
			++

		case '\r':
			 = append(, "\\r"...)
			++

		case '\t':
			 = append(, "\\t"...)
			++

		case '\\':
			 = append(, "\\\\"...)
			++

		case '"':
			 = append(, "\\\""...)
			++

		default:
			 += 
			if  <= 0xFFFF {
				 = append(
					,
					'\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15],
				)
			} else {
				 -= 0x10000
				 := firstHighSurrogate + (( >> 10) & 0x3FF)
				 := firstLowSurrogate + ( & 0x3FF)
				 = append(
					,
					'\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15],
					'\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15],
				)
			}
		}
	}

	return append(, '"')
}

func ( []byte,  string,  compat.JSFeature) []byte {
	 := false
	 := 0
	for ,  := range  {
Fast path: a run of ASCII characters
			if ! {
				 = true
				 = 
			}
Slow path: escape non-ACSII characters
			if  {
				 = append(, [:]...)
				 = false
			}
			if  <= 0xFFFF {
				 = append(, '\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15])
			} else if !.Has(compat.UnicodeEscapes) {
				 = append(, fmt.Sprintf("\\u{%X}", )...)
			} else {
				panic("Internal error: Cannot encode identifier: Unicode escapes are unsupported")
			}
		}
	}
Print one final run of ASCII characters
		 = append(, [:]...)
	}
	return 
}

func ( *printer) ( []uint16,  rune) {
	 := make([]byte, utf8.UTFMax)
	 := .js
	 := 0
	 := len()

	for  <  {
		 := []
		++

Special-case the null character since it may mess with code written in C that treats null characters as the end of the string.
We don't want "\x001" to be written as "\01"
			if  <  && [] >= '0' && [] <= '9' {
				 = append(, "\\x00"...)
			} else {
				 = append(, "\\0"...)
			}
Special-case the bell character since it may cause dumping this file to the terminal to make a sound, which is undesirable. Note that we can't use an octal literal to print this shorter since octal literals are not allowed in strict mode (or in template strings).
		case '\x07':
			 = append(, "\\x07"...)

		case '\b':
			 = append(, "\\b"...)

		case '\f':
			 = append(, "\\f"...)

		case '\n':
			if  == '`' {
				 = append(, '\n')
			} else {
				 = append(, "\\n"...)
			}

		case '\r':
			 = append(, "\\r"...)

		case '\v':
			 = append(, "\\v"...)

		case '\\':
			 = append(, "\\\\"...)

		case '\'':
			if  == '\'' {
				 = append(, '\\')
			}
			 = append(, '\'')

		case '"':
			if  == '"' {
				 = append(, '\\')
			}
			 = append(, '"')

		case '`':
			if  == '`' {
				 = append(, '\\')
			}
			 = append(, '`')

		case '$':
			if  == '`' &&  <  && [] == '{' {
				 = append(, '\\')
			}
			 = append(, '$')

		case '\u2028':
			 = append(, "\\u2028"...)

		case '\u2029':
			 = append(, "\\u2029"...)

		case '\uFEFF':
			 = append(, "\\uFEFF"...)

		default:
Common case: just append a single byte
			case  <= lastASCII:
				 = append(, byte())
Is this a high surrogate?
Is there a next character?
				if  <  {
					 := []
Is it a low surrogate?
					if  >= firstLowSurrogate &&  <= lastLowSurrogate {
						 := (rune() << 10) + rune() + (0x10000 - (firstHighSurrogate << 10) - firstLowSurrogate)
						++
Escape this character if UTF-8 isn't allowed
						if .options.ASCIIOnly {
							if !.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) {
								 = append(, fmt.Sprintf("\\u{%X}", )...)
							} else {
								 = append(,
									'\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15],
									'\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15],
								)
							}
							continue
						}
Otherwise, encode to UTF-8
						 := utf8.EncodeRune(, )
						 = append(, [:]...)
						continue
					}
				}
Write an unpaired high surrogate
				 = append(, '\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15])
Is this an unpaired low surrogate or four-digit hex escape?
			case ( >= firstLowSurrogate &&  <= lastLowSurrogate) || (.options.ASCIIOnly &&  > 0xFF):
				 = append(, '\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15])
Can this be a two-digit hex escape?
			case .options.ASCIIOnly:
				 = append(, '\\', 'x', hexChars[>>4], hexChars[&15])
This is a workaround for a bug in the popular "source-map" library: https://github.com/mozilla/source-map/issues/261. The library will sometimes return null when querying a source map unless every line starts with a mapping at column zero. The workaround is to replicate the previous mapping if a line ends up not starting with a mapping. This is done lazily because we want to avoid replicating the previous mapping if we don't need to.
The source map specification is very loose and does not specify what column numbers actually mean. The popular "source-map" library from Mozilla appears to interpret them as counts of UTF-16 code units, so we generate those too for compatibility. We keep mapping tables around to accelerate conversion from byte offsets to UTF-16 code unit counts. However, this mapping takes up a lot of memory and generates a lot of garbage. Since most JavaScript is ASCII and the mapping for ASCII is 1:1, we avoid creating a table for ASCII-only lines as an optimization.
This is the same as "print(string(bytes))" without any unnecessary temporary allocations
func ( *printer) ( []byte) {
	.js = append(.js, ...)
}

func ( *printer) ( string,  bool) {
	 := js_lexer.StringToUTF16()
	 := .bestQuoteCharForString(, )
	.print()
	.printQuotedUTF16(, rune([0]))
	.print()
}

func ( *printer) ( logger.Loc) {
	if !.options.AddSourceMappings ||  == .prevLoc {
		return
	}
	.prevLoc = 
Binary search to find the line
	 := .lineOffsetTables
	 := len()
	 := 0
	for  > 0 {
		 :=  / 2
		 :=  + 
		if [].byteOffsetToStartOfLine <= .Start {
			 =  + 1
			 =  -  - 1
		} else {
			 = 
		}
	}
	--
Use the line to compute the column
	 := &[]
	 := int(.Start - .byteOffsetToStartOfLine)
	if .columnsForNonASCII != nil &&  >= int(.byteOffsetToFirstNonASCII) {
		 = int(.columnsForNonASCII[-int(.byteOffsetToFirstNonASCII)])
	}

	.updateGeneratedLineAndColumn()
If this line doesn't start with a mapping and we're about to add a mapping that's not at the start, insert a mapping first so the line starts with one.
This line now has a mapping on it, so don't insert another one
Scan over the printed text since the last source mapping and update the generated line and column numbers
func ( *printer) () {
	for ,  := range string(.js[.lastGeneratedUpdate:]) {
		switch  {
Handle Windows-specific "\r\n" newlines
			if  == '\r' {
				 := .lastGeneratedUpdate +  + 1
				if  < len(.js) && .js[] == '\n' {
					continue
				}
			}
If we're about to move to the next line and the previous line didn't have any mappings, add a mapping at the start of the previous line.
This new line doesn't have a mapping yet
Mozilla's "source-map" library counts columns using UTF-16 code units
			if  <= 0xFFFF {
				.generatedColumn++
			} else {
				.generatedColumn += 2
			}
		}
	}

	.lastGeneratedUpdate = len(.js)
}

func ( string,  int32) []LineOffsetTable {
	var  []int32
	 := int32(0)
	 := 0
	 := 0
	 := int32(0)
Preallocate the top-level table using the approximate line count from the lexer
	 := make([]LineOffsetTable, 0, )

Mark the start of the next line
		if  == 0 {
			 = 
		}
Start the mapping if this character is non-ASCII
		if  > 0x7F &&  == nil {
			 =  - 
			 = int32()
			 = []int32{}
		}
Update the per-byte column offsets
		if  != nil {
			for  :=  - ;  <= ; ++ {
				 = append(, )
			}
		}

		switch  {
Handle Windows-specific "\r\n" newlines
			if  == '\r' {
				if +1 < len() && [+1] == '\n' {
					++
					continue
				}
			}

			 = append(, LineOffsetTable{
				byteOffsetToStartOfLine:   int32(),
				byteOffsetToFirstNonASCII: ,
				columnsForNonASCII:        ,
			})
			 = 0
			 = 0
			 = nil
			 = 0

Mozilla's "source-map" library counts columns using UTF-16 code units
			if  <= 0xFFFF {
				++
			} else {
				 += 2
			}
		}
	}
Mark the start of the next line
	if  == 0 {
		 = len()
	}
Do one last update for the column at the end of the file
	if  != nil {
		for  := len() - ;  <= ; ++ {
			 = append(, )
		}
	}

	 = append(, LineOffsetTable{
		byteOffsetToStartOfLine:   int32(),
		byteOffsetToFirstNonASCII: ,
		columnsForNonASCII:        ,
	})
	return 
}

If the input file had a source map, map all the way back to the original
	if .options.InputSourceMap != nil {
		 := .options.InputSourceMap.Find(
			int32(.OriginalLine),
			int32(.OriginalColumn))
Some locations won't have a mapping
		if  == nil {
			return
		}

		.SourceIndex = int(.SourceIndex)
		.OriginalLine = int(.OriginalLine)
		.OriginalColumn = int(.OriginalColumn)
	}

	.appendMappingWithoutRemapping()
}

func ( *printer) ( SourceMapState) {
	var  byte
	if len(.sourceMap) != 0 {
		 = .sourceMap[len(.sourceMap)-1]
	}

	.sourceMap = appendMapping(.sourceMap, , .prevState, )
	.prevState = 
	.hasPrevState = true
}

func ( *printer) () {
	if !.options.RemoveWhitespace {
		for  := 0;  < .options.Indent; ++ {
			.print("  ")
		}
	}
}

func ( *printer) ( js_ast.Ref) {
	.printSpaceBeforeIdentifier()
	.printIdentifier(.renamer.NameForSymbol())
}

func ( string,  compat.JSFeature,  bool) bool {
	return js_lexer.IsIdentifier() && (! ||
		!.Has(compat.UnicodeEscapes) ||
		!js_lexer.ContainsNonBMPCodePoint())
}

func ( *printer) ( string) bool {
	return js_lexer.IsIdentifier() && (!.options.ASCIIOnly ||
		!.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) ||
		!js_lexer.ContainsNonBMPCodePoint())
}

func ( *printer) ( []uint16) bool {
	return js_lexer.IsIdentifierUTF16() && (!.options.ASCIIOnly ||
		!.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) ||
		!js_lexer.ContainsNonBMPCodePointUTF16())
}

func ( *printer) ( string) {
	if .options.ASCIIOnly {
		.js = QuoteIdentifier(.js, , .options.UnsupportedFeatures)
	} else {
		.print()
	}
}
This is the same as "printIdentifier(StringToUTF16(bytes))" without any unnecessary temporary allocations
func ( *printer) ( []uint16) {
	 := make([]byte, utf8.UTFMax)
	 := len()

	for  := 0;  < ; ++ {
		 := rune([])

		if  >= firstHighSurrogate &&  <= lastHighSurrogate && +1 <  {
			if  := rune([+1]);  >= firstLowSurrogate &&  <= lastLowSurrogate {
				 = ( << 10) +  + (0x10000 - (firstHighSurrogate << 10) - firstLowSurrogate)
				++
			}
		}

		if .options.ASCIIOnly &&  > lastASCII {
			if  <= 0xFFFF {
				.js = append(.js, '\\', 'u', hexChars[>>12], hexChars[(>>8)&15], hexChars[(>>4)&15], hexChars[&15])
			} else if !.options.UnsupportedFeatures.Has(compat.UnicodeEscapes) {
				.js = append(.js, fmt.Sprintf("\\u{%X}", )...)
			} else {
				panic("Internal error: Cannot encode identifier: Unicode escapes are unsupported")
			}
			continue
		}

		 := utf8.EncodeRune(, )
		.js = append(.js, [:]...)
	}
}

func ( *printer) ( js_ast.Binding) {
	.addSourceMapping(.Loc)

	switch b := .Data.(type) {
	case *js_ast.BMissing:

	case *js_ast.BIdentifier:
		.printSymbol(.Ref)

	case *js_ast.BArray:
		.print("[")
		if len(.Items) > 0 {
			if !.IsSingleLine {
				.options.Indent++
			}

			for ,  := range .Items {
				if  != 0 {
					.print(",")
					if .IsSingleLine {
						.printSpace()
					}
				}
				if !.IsSingleLine {
					.printNewline()
					.printIndent()
				}
				if .HasSpread && +1 == len(.Items) {
					.print("...")
				}
				.(.Binding)

				if .DefaultValue != nil {
					.printSpace()
					.print("=")
					.printSpace()
					.printExpr(*.DefaultValue, js_ast.LComma, 0)
				}
Make sure there's a comma after trailing missing items
				if ,  := .Binding.Data.(*js_ast.BMissing);  &&  == len(.Items)-1 {
					.print(",")
				}
			}

			if !.IsSingleLine {
				.options.Indent--
				.printNewline()
				.printIndent()
			}
		}
		.print("]")

	case *js_ast.BObject:
		.print("{")
		if len(.Properties) > 0 {
			if !.IsSingleLine {
				.options.Indent++
			}

			for ,  := range .Properties {
				if  != 0 {
					.print(",")
					if .IsSingleLine {
						.printSpace()
					}
				}
				if !.IsSingleLine {
					.printNewline()
					.printIndent()
				}

				if .IsSpread {
					.print("...")
				} else {
					if .IsComputed {
						.print("[")
						.printExpr(.Key, js_ast.LComma, 0)
						.print("]:")
						.printSpace()
						.(.Value)

						if .DefaultValue != nil {
							.printSpace()
							.print("=")
							.printSpace()
							.printExpr(*.DefaultValue, js_ast.LComma, 0)
						}
						continue
					}

					if ,  := .Key.Data.(*js_ast.EString);  {
						if .canPrintIdentifierUTF16(.Value) {
							.addSourceMapping(.Key.Loc)
							.printSpaceBeforeIdentifier()
							.printIdentifierUTF16(.Value)
Use a shorthand property if the names are the same
							if ,  := .Value.Data.(*js_ast.BIdentifier);  && js_lexer.UTF16EqualsString(.Value, .renamer.NameForSymbol(.Ref)) {
								if .DefaultValue != nil {
									.printSpace()
									.print("=")
									.printSpace()
									.printExpr(*.DefaultValue, js_ast.LComma, 0)
								}
								continue
							}
						} else {
							.printExpr(.Key, js_ast.LLowest, 0)
						}
					} else {
						.printExpr(.Key, js_ast.LLowest, 0)
					}

					.print(":")
					.printSpace()
				}
				.(.Value)

				if .DefaultValue != nil {
					.printSpace()
					.print("=")
					.printSpace()
					.printExpr(*.DefaultValue, js_ast.LComma, 0)
				}
			}

			if !.IsSingleLine {
				.options.Indent--
				.printNewline()
				.printIndent()
			}
		}
		.print("}")

	default:
		panic(fmt.Sprintf("Unexpected binding of type %T", .Data))
	}
}

func ( *printer) () {
	if !.options.RemoveWhitespace {
		.print(" ")
	}
}

func ( *printer) () {
	if !.options.RemoveWhitespace {
		.print("\n")
	}
}

func ( *printer) ( js_ast.OpCode) {
	if .prevOpEnd == len(.js) {
		 := .prevOp
"+ + y" => "+ +y" "+ ++ y" => "+ ++y" "x + + y" => "x+ +y" "x ++ + y" => "x+++y" "x + ++ y" => "x+ ++y" "-- >" => "-- >" "< ! --" => "<! --"
		if (( == js_ast.BinOpAdd ||  == js_ast.UnOpPos) && ( == js_ast.BinOpAdd ||  == js_ast.UnOpPos ||  == js_ast.UnOpPreInc)) ||
			(( == js_ast.BinOpSub ||  == js_ast.UnOpNeg) && ( == js_ast.BinOpSub ||  == js_ast.UnOpNeg ||  == js_ast.UnOpPreDec)) ||
			( == js_ast.UnOpPostDec &&  == js_ast.BinOpGt) ||
			( == js_ast.UnOpNot &&  == js_ast.UnOpPreDec && len(.js) > 1 && .js[len(.js)-2] == '<') {
			.print(" ")
		}
	}
}

func ( *printer) () {
	if !.options.RemoveWhitespace {
		.print(";\n")
	} else {
		.needsSemicolon = true
	}
}

func ( *printer) () {
	if .needsSemicolon {
		.print(";")
		.needsSemicolon = false
	}
}

func ( *printer) () {
	 := .js
	 := len()
	if  > 0 && (js_lexer.IsIdentifierContinue(rune([-1])) ||  == .prevRegExpEnd) {
		.print(" ")
	}
}

func ( *printer) ( []js_ast.Arg,  bool,  bool) {
	 := true
Minify "(a) => {}" as "a=>{}"
	if .options.RemoveWhitespace && ! &&  && len() == 1 {
		if ,  := [0].Binding.Data.(*js_ast.BIdentifier);  && [0].Default == nil {
			 = false
		}
	}

	if  {
		.print("(")
	}

	for ,  := range  {
		if  != 0 {
			.print(",")
			.printSpace()
		}
		if  && +1 == len() {
			.print("...")
		}
		.printBinding(.Binding)

		if .Default != nil {
			.printSpace()
			.print("=")
			.printSpace()
			.printExpr(*.Default, js_ast.LComma, 0)
		}
	}

	if  {
		.print(")")
	}
}

func ( *printer) ( js_ast.Fn) {
	.printFnArgs(.Args, .HasRestArg, false /* isArrow */)
	.printSpace()
	.printBlock(.Body.Loc, .Body.Stmts)
}

func ( *printer) ( js_ast.Class) {
	if .Extends != nil {
		.print(" extends")
		.printSpace()
		.printExpr(*.Extends, js_ast.LNew-1, 0)
	}
	.printSpace()

	.addSourceMapping(.BodyLoc)
	.print("{")
	.printNewline()
	.options.Indent++

	for ,  := range .Properties {
		.printSemicolonIfNeeded()
		.printIndent()
		.printProperty()
Need semicolons after class fields
		if .Value == nil {
			.printSemicolonAfterStatement()
		} else {
			.printNewline()
		}
	}

	.needsSemicolon = false
	.options.Indent--
	.printIndent()
	.print("}")
}

func ( *printer) ( js_ast.Property) {
	if .Kind == js_ast.PropertySpread {
		.print("...")
		.printExpr(*.Value, js_ast.LComma, 0)
		return
	}

	if .IsStatic {
		.print("static")
		.printSpace()
	}

	switch .Kind {
	case js_ast.PropertyGet:
		.printSpaceBeforeIdentifier()
		.print("get")
		.printSpace()

	case js_ast.PropertySet:
		.printSpaceBeforeIdentifier()
		.print("set")
		.printSpace()
	}

	if .Value != nil {
		if ,  := .Value.Data.(*js_ast.EFunction); .IsMethod &&  {
			if .Fn.IsAsync {
				.printSpaceBeforeIdentifier()
				.print("async")
				.printSpace()
			}
			if .Fn.IsGenerator {
				.print("*")
			}
		}
	}

	if .IsComputed {
		.print("[")
		.printExpr(.Key, js_ast.LComma, 0)
		.print("]")

		if .Value != nil {
			if ,  := .Value.Data.(*js_ast.EFunction); .IsMethod &&  {
				.printFn(.Fn)
				return
			}

			.print(":")
			.printSpace()
			.printExpr(*.Value, js_ast.LComma, 0)
		}

		if .Initializer != nil {
			.printSpace()
			.print("=")
			.printSpace()
			.printExpr(*.Initializer, js_ast.LComma, 0)
		}
		return
	}

	switch key := .Key.Data.(type) {
	case *js_ast.EPrivateIdentifier:
		.printSymbol(.Ref)

	case *js_ast.EString:
		.addSourceMapping(.Key.Loc)
		if .canPrintIdentifierUTF16(.Value) {
			.printSpaceBeforeIdentifier()
			.printIdentifierUTF16(.Value)
Use a shorthand property if the names are the same
			if !.options.UnsupportedFeatures.Has(compat.ObjectExtensions) && .Value != nil {
				switch e := .Value.Data.(type) {
				case *js_ast.EIdentifier:
					if js_lexer.UTF16EqualsString(.Value, .renamer.NameForSymbol(.Ref)) {
						if .Initializer != nil {
							.printSpace()
							.print("=")
							.printSpace()
							.printExpr(*.Initializer, js_ast.LComma, 0)
						}
						return
					}

Make sure we're not using a property access instead of an identifier
					 := js_ast.FollowSymbols(.symbols, .Ref)
					 := .symbols.Get()
					if .NamespaceAlias == nil && js_lexer.UTF16EqualsString(.Value, .renamer.NameForSymbol(.Ref)) {
						if .Initializer != nil {
							.printSpace()
							.print("=")
							.printSpace()
							.printExpr(*.Initializer, js_ast.LComma, 0)
						}
						return
					}
				}
			}
		} else {
			 := .bestQuoteCharForString(.Value, false /* allowBacktick */)
			.print()
			.printQuotedUTF16(.Value, rune([0]))
			.print()
		}

	default:
		.printExpr(.Key, js_ast.LLowest, 0)
	}

	if .Kind != js_ast.PropertyNormal {
		,  := .Value.Data.(*js_ast.EFunction)
		if  {
			.printFn(.Fn)
			return
		}
	}

	if .Value != nil {
		if ,  := .Value.Data.(*js_ast.EFunction); .IsMethod &&  {
			.printFn(.Fn)
			return
		}

		.print(":")
		.printSpace()
		.printExpr(*.Value, js_ast.LComma, 0)
	}

	if .Initializer != nil {
		.printSpace()
		.print("=")
		.printSpace()
		.printExpr(*.Initializer, js_ast.LComma, 0)
	}
}

func ( *printer) ( []uint16,  bool) string {
	if .options.UnsupportedFeatures.Has(compat.TemplateLiteral) {
		 = false
	}

	 := 0
	 := 0
	 := 0

	for ,  := range  {
		switch  {
		case '\n':
The backslash for the newline costs an extra character for old-style string literals when compared to a template literal
				--
			}
		case '\'':
			++
		case '"':
			++
		case '`':
			++
"${" sequences need to be escaped in template literals
			if +1 < len() && [+1] == '{' {
				++
			}
		}
	}

	 := "\""
	if  >  {
		 = "'"
		if  >  &&  {
			 = "`"
		}
	} else if  >  &&  {
		 = "`"
	}
	return 
}

type requireCallArgs struct {
	isES6Import       bool
	mustReturnPromise bool
}

func ( *printer) ( uint32,  []js_ast.Comment) {
	 := &.importRecords[]
	.printSpaceBeforeIdentifier()
Preserve "import()" expressions that don't point inside the bundle
	if !.SourceIndex.IsValid() && .Kind == ast.ImportDynamic && .options.OutputFormat.KeepES6ImportExportSyntax() {
		.print("import(")
		if len() > 0 {
			.printNewline()
			.options.Indent++
			for ,  := range  {
				.printIndentedComment(.Text)
			}
			.printIndent()
		}
		.printQuotedUTF8(.Path.Text, true /* allowBacktick */)
		if len() > 0 {
			.printNewline()
			.options.Indent--
			.printIndent()
		}
		.print(")")
		return
	}
Make sure "import()" expressions return promises
	if .Kind == ast.ImportDynamic {
		if .options.UnsupportedFeatures.Has(compat.Arrow) {
			.print("Promise.resolve().then(function()")
			.printSpace()
			.print("{")
			.printNewline()
			.options.Indent++
			.printIndent()
			.print("return ")
		} else {
			if .options.RemoveWhitespace {
				.print("Promise.resolve().then(()=>")
			} else {
				.print("Promise.resolve().then(() => ")
			}
		}
	}
Make sure CommonJS imports are converted to ES6 if necessary
If this import points inside the bundle, then call the "require()" function for that module directly. The linker must ensure that the module's require function exists by this point. Otherwise, fall back to a bare "require()" call. Then it's up to the user to provide it.
	if .SourceIndex.IsValid() {
		.printSymbol(.options.WrapperRefForSource(.SourceIndex.GetIndex()))
		.print("()")
	} else {
		.print("require(")
		.printQuotedUTF8(.Path.Text, true /* allowBacktick */)
		.print(")")
	}

	if .WrapWithToModule {
		.print(")")
	}

	if .Kind == ast.ImportDynamic {
		if .options.UnsupportedFeatures.Has(compat.Arrow) {
			if !.options.RemoveWhitespace {
				.print(";")
			}
			.printNewline()
			.options.Indent--
			.printIndent()
			.print("})")
		} else {
			.print(")")
		}
	}
}

const (
	forbidCall = 1 << iota
	forbidIn
	hasNonOptionalChainParent
)

func ( *printer) ( js_ast.L) {
	if  >= js_ast.LPrefix {
		.print("(void 0)")
	} else {
		.printSpaceBeforeIdentifier()
		.print("void 0")
		.prevNumEnd = len(.js)
	}
}

func ( *printer) ( js_ast.Expr,  js_ast.L,  int) {
	.addSourceMapping(.Loc)

	switch e := .Data.(type) {
	case *js_ast.EMissing:

	case *js_ast.EUndefined:
		.printUndefined()

	case *js_ast.ESuper:
		.printSpaceBeforeIdentifier()
		.print("super")

	case *js_ast.ENull:
		.printSpaceBeforeIdentifier()
		.print("null")

	case *js_ast.EThis:
		.printSpaceBeforeIdentifier()
		.print("this")

	case *js_ast.ESpread:
		.print("...")
		.(.Value, js_ast.LComma, 0)

	case *js_ast.ENewTarget:
		.printSpaceBeforeIdentifier()
		.print("new.target")

	case *js_ast.EImportMeta:
		.printSpaceBeforeIdentifier()
		.print("import.meta")

	case *js_ast.ENew:
		 :=  >= js_ast.LCall

		 := !.options.RemoveWhitespace && .CanBeUnwrappedIfUnused
		if  &&  >= js_ast.LPostfix {
			 = true
		}

		if  {
			.print("(")
		}

		if  {
			.print("/* @__PURE__ */ ")
		}

		.printSpaceBeforeIdentifier()
		.print("new")
		.printSpace()
		.(.Target, js_ast.LNew, forbidCall)
Omit the "()" when minifying, but only when safe to do so
		if !.options.RemoveWhitespace || len(.Args) > 0 ||  >= js_ast.LPostfix {
			.print("(")
			for ,  := range .Args {
				if  != 0 {
					.print(",")
					.printSpace()
				}
				.(, js_ast.LComma, 0)
			}
			.print(")")
		}

		if  {
			.print(")")
		}

	case *js_ast.ECall:
		 :=  >= js_ast.LNew || (&forbidCall) != 0
		 := 0
		if .OptionalChain == js_ast.OptionalChainNone {
			 = hasNonOptionalChainParent
		} else if ( & hasNonOptionalChainParent) != 0 {
			 = true
		}

		 := !.options.RemoveWhitespace && .CanBeUnwrappedIfUnused
		if  &&  >= js_ast.LPostfix {
			 = true
		}

		if  {
			.print("(")
		}

		if  {
			 := .stmtStart == len(.js)
			.print("/* @__PURE__ */ ")
			if  {
				.stmtStart = len(.js)
			}
		}
We don't ever want to accidentally generate a direct eval expression here
		.callTarget = .Target.Data
		if !.IsDirectEval && .isUnboundEvalIdentifier(.Target) {
			if .options.RemoveWhitespace {
				.print("(0,")
			} else {
				.print("(0, ")
			}
			.(.Target, js_ast.LPostfix, 0)
			.print(")")
		} else {
			.(.Target, js_ast.LPostfix, )
		}

		if .OptionalChain == js_ast.OptionalChainStart {
			.print("?.")
		}
		.print("(")
		for ,  := range .Args {
			if  != 0 {
				.print(",")
				.printSpace()
			}
			.(, js_ast.LComma, 0)
		}
		.print(")")
		if  {
			.print(")")
		}

	case *js_ast.ERequire:
		 :=  >= js_ast.LNew || (&forbidCall) != 0
		if  {
			.print("(")
		}
		.printRequireOrImportExpr(.ImportRecordIndex, nil)
		if  {
			.print(")")
		}

	case *js_ast.ERequireResolve:
		 :=  >= js_ast.LNew || (&forbidCall) != 0
		if  {
			.print("(")
		}
		.printSpaceBeforeIdentifier()
		.print("require.resolve(")
		.printQuotedUTF8(.importRecords[.ImportRecordIndex].Path.Text, true /* allowBacktick */)
		.print(")")
		if  {
			.print(")")
		}

	case *js_ast.EImport:
		 :=  >= js_ast.LNew || (&forbidCall) != 0
		if  {
			.print("(")
		}

		var  []js_ast.Comment
		if !.options.RemoveWhitespace {
			 = .LeadingInteriorComments
		}

		if .ImportRecordIndex.IsValid() {
			.printRequireOrImportExpr(.ImportRecordIndex.GetIndex(), )
Handle non-string expressions
			.printSpaceBeforeIdentifier()
			.print("import(")
			if len() > 0 {
				.printNewline()
				.options.Indent++
				for ,  := range .LeadingInteriorComments {
					.printIndentedComment(.Text)
				}
				.printIndent()
			}
			.(.Expr, js_ast.LComma, 0)
			if len() > 0 {
				.printNewline()
				.options.Indent--
				.printIndent()
			}
			.print(")")
		}

		if  {
			.print(")")
		}

	case *js_ast.EDot:
		 := false
		if .OptionalChain == js_ast.OptionalChainNone {
			 |= hasNonOptionalChainParent
		} else {
			if ( & hasNonOptionalChainParent) != 0 {
				 = true
				.print("(")
			}
			 &= ^hasNonOptionalChainParent
		}
		.(.Target, js_ast.LPostfix, )
		if .OptionalChain == js_ast.OptionalChainStart {
			.print("?")
		}
		if .canPrintIdentifier(.Name) {
"1.toString" is a syntax error, so print "1 .toString" instead
				.print(" ")
			}
			.print(".")
			.addSourceMapping(.NameLoc)
			.printIdentifier(.Name)
		} else {
			.print("[")
			.addSourceMapping(.NameLoc)
			.printQuotedUTF8(.Name, true /* allowBacktick */)
			.print("]")
		}
		if  {
			.print(")")
		}

	case *js_ast.EIndex:
		 := false
		if .OptionalChain == js_ast.OptionalChainNone {
			 |= hasNonOptionalChainParent
		} else {
			if ( & hasNonOptionalChainParent) != 0 {
				 = true
				.print("(")
			}
			 &= ^hasNonOptionalChainParent
		}
		.(.Target, js_ast.LPostfix, )
		if .OptionalChain == js_ast.OptionalChainStart {
			.print("?.")
		}
		if ,  := .Index.Data.(*js_ast.EPrivateIdentifier);  {
			if .OptionalChain != js_ast.OptionalChainStart {
				.print(".")
			}
			.printSymbol(.Ref)
		} else {
			.print("[")
			.(.Index, js_ast.LLowest, 0)
			.print("]")
		}
		if  {
			.print(")")
		}

	case *js_ast.EIf:
		 :=  >= js_ast.LConditional
		if  {
			.print("(")
			 &= ^forbidIn
		}
		.(.Test, js_ast.LConditional, &forbidIn)
		.printSpace()
		.print("?")
		.printSpace()
		.(.Yes, js_ast.LYield, 0)
		.printSpace()
		.print(":")
		.printSpace()
		.(.No, js_ast.LYield, &forbidIn)
		if  {
			.print(")")
		}

	case *js_ast.EArrow:
		 :=  >= js_ast.LAssign

		if  {
			.print("(")
		}
		if .IsAsync {
			.printSpaceBeforeIdentifier()
			.print("async")
			.printSpace()
		}

		.printFnArgs(.Args, .HasRestArg, true /* isArrow */)
		.printSpace()
		.print("=>")
		.printSpace()

		 := false
		if len(.Body.Stmts) == 1 && .PreferExpr {
			if ,  := .Body.Stmts[0].Data.(*js_ast.SReturn);  && .Value != nil {
				.arrowExprStart = len(.js)
				.(*.Value, js_ast.LComma, 0)
				 = true
			}
		}
		if ! {
			.printBlock(.Body.Loc, .Body.Stmts)
		}
		if  {
			.print(")")
		}

	case *js_ast.EFunction:
		 := len(.js)
		 := .stmtStart ==  || .exportDefaultStart == 
		if  {
			.print("(")
		}
		.printSpaceBeforeIdentifier()
		if .Fn.IsAsync {
			.print("async ")
		}
		.print("function")
		if .Fn.IsGenerator {
			.print("*")
			.printSpace()
		}
		if .Fn.Name != nil {
			.printSymbol(.Fn.Name.Ref)
		}
		.printFn(.Fn)
		if  {
			.print(")")
		}

	case *js_ast.EClass:
		 := len(.js)
		 := .stmtStart ==  || .exportDefaultStart == 
		if  {
			.print("(")
		}
		.printSpaceBeforeIdentifier()
		.print("class")
		if .Class.Name != nil {
			.printSymbol(.Class.Name.Ref)
		}
		.printClass(.Class)
		if  {
			.print(")")
		}

	case *js_ast.EArray:
		.print("[")
		if len(.Items) > 0 {
			if !.IsSingleLine {
				.options.Indent++
			}

			for ,  := range .Items {
				if  != 0 {
					.print(",")
					if .IsSingleLine {
						.printSpace()
					}
				}
				if !.IsSingleLine {
					.printNewline()
					.printIndent()
				}
				.(, js_ast.LComma, 0)
Make sure there's a comma after trailing missing items
				,  := .Data.(*js_ast.EMissing)
				if  &&  == len(.Items)-1 {
					.print(",")
				}
			}

			if !.IsSingleLine {
				.options.Indent--
				.printNewline()
				.printIndent()
			}
		}
		.print("]")

	case *js_ast.EObject:
		 := len(.js)
		 := .stmtStart ==  || .arrowExprStart == 
		if  {
			.print("(")
		}
		.print("{")
		if len(.Properties) != 0 {
			if !.IsSingleLine {
				.options.Indent++
			}

			for ,  := range .Properties {
				if  != 0 {
					.print(",")
					if .IsSingleLine {
						.printSpace()
					}
				}
				if !.IsSingleLine {
					.printNewline()
					.printIndent()
				}
				.printProperty()
			}

			if !.IsSingleLine {
				.options.Indent--
				.printNewline()
				.printIndent()
			}
		}
		.print("}")
		if  {
			.print(")")
		}

	case *js_ast.EBoolean:
		if .options.MangleSyntax {
			if  >= js_ast.LPrefix {
				if .Value {
					.print("(!0)")
				} else {
					.print("(!1)")
				}
			} else {
				if .Value {
					.print("!0")
				} else {
					.print("!1")
				}
			}
		} else {
			.printSpaceBeforeIdentifier()
			if .Value {
				.print("true")
			} else {
				.print("false")
			}
		}

If this was originally a template literal, print it as one as long as we're not minifying
		if .PreferTemplate && !.options.MangleSyntax && !.options.UnsupportedFeatures.Has(compat.TemplateLiteral) {
			.print("`")
			.printQuotedUTF16(.Value, '`')
			.print("`")
			return
		}

		 := .bestQuoteCharForString(.Value, true /* allowBacktick */)
		.print()
		.printQuotedUTF16(.Value, rune([0]))
		.print()

Convert no-substitution template literals into strings if it's smaller
		if .options.MangleSyntax && .Tag == nil && len(.Parts) == 0 {
			 := .bestQuoteCharForString(.Head, true /* allowBacktick */)
			.print()
			.printQuotedUTF16(.Head, rune([0]))
			.print()
			return
		}

Optional chains are forbidden in template tags
			if js_ast.IsOptionalChain(*.Tag) {
				.print("(")
				.(*.Tag, js_ast.LLowest, 0)
				.print(")")
			} else {
				.(*.Tag, js_ast.LPostfix, 0)
			}
		}
		.print("`")
		if .Tag != nil {
			.print(.HeadRaw)
		} else {
			.printQuotedUTF16(.Head, '`')
		}
		for ,  := range .Parts {
			.print("${")
			.(.Value, js_ast.LLowest, 0)
			.print("}")
			if .Tag != nil {
				.print(.TailRaw)
			} else {
				.printQuotedUTF16(.Tail, '`')
			}
		}
		.print("`")

	case *js_ast.ERegExp:
		 := .js
		 := len()
Avoid forming a single-line comment
		if  > 0 && [-1] == '/' {
			.print(" ")
		}
		.print(.Value)
Need a space before the next identifier to avoid it turning into flags
		.prevRegExpEnd = len(.js)

	case *js_ast.EBigInt:
		.printSpaceBeforeIdentifier()
		.print(.Value)
		.print("n")

	case *js_ast.ENumber:
		 := .Value
		 := math.Abs()

		if  !=  {
			.printSpaceBeforeIdentifier()
			.print("NaN")
		} else if  == positiveInfinity {
			.printSpaceBeforeIdentifier()
			.print("Infinity")
		} else if  == negativeInfinity {
			if  >= js_ast.LPrefix {
				.print("(-Infinity)")
			} else {
				.printSpaceBeforeOperator(js_ast.UnOpNeg)
				.print("-Infinity")
			}
		} else {
			if !math.Signbit() {
				.printSpaceBeforeIdentifier()
				.printNonNegativeFloat()
Remember the end of the latest number
				.prevNumEnd = len(.js)
Expressions such as "(-1).toString" need to wrap negative numbers. Instead of testing for "value < 0" we test for "signbit(value)" and "!isNaN(value)" because we need this to be true for "-0" and "-0 < 0" is false.
				.print("(-")
				.printNonNegativeFloat()
				.print(")")
			} else {
				.printSpaceBeforeOperator(js_ast.UnOpNeg)
				.print("-")
				.printNonNegativeFloat()
Remember the end of the latest number
				.prevNumEnd = len(.js)
			}
		}

	case *js_ast.EIdentifier:
		 := .renamer.NameForSymbol(.Ref)
		 := len(.js) == .forOfInitStart &&  == "let"

		if  {
			.print("(")
		}

		.printSpaceBeforeIdentifier()
		.printIdentifier()

		if  {
			.print(")")
		}

Potentially use a property access instead of an identifier
		 := js_ast.FollowSymbols(.symbols, .Ref)
		 := .symbols.Get()

		if .ImportItemStatus == js_ast.ImportItemMissing {
			.printUndefined()
		} else if .NamespaceAlias != nil {
			 := .callTarget ==  && .WasOriginallyIdentifier
			if  {
				if .options.RemoveWhitespace {
					.print("(0,")
				} else {
					.print("(0, ")
				}
			}
			.printSymbol(.NamespaceAlias.NamespaceRef)
			 := .NamespaceAlias.Alias
			if .canPrintIdentifier() {
				.print(".")
				.printIdentifier()
			} else {
				.print("[")
				.printQuotedUTF8(, true /* allowBacktick */)
				.print("]")
			}
			if  {
				.print(")")
			}
		} else {
			.printSymbol(.Ref)
		}

	case *js_ast.EAwait:
		 :=  >= js_ast.LPrefix

		if  {
			.print("(")
		}

		.printSpaceBeforeIdentifier()
		.print("await")
		.printSpace()
		.(.Value, js_ast.LPrefix-1, 0)

		if  {
			.print(")")
		}

	case *js_ast.EYield:
		 :=  >= js_ast.LAssign

		if  {
			.print("(")
		}

		.printSpaceBeforeIdentifier()
		.print("yield")

		if .Value != nil {
			if .IsStar {
				.print("*")
			}
			.printSpace()
			.(*.Value, js_ast.LYield, 0)
		}

		if  {
			.print(")")
		}

	case *js_ast.EUnary:
		 := js_ast.OpTable[.Op]
		 :=  >= .Level

		if  {
			.print("(")
		}

		if !.Op.IsPrefix() {
			.(.Value, js_ast.LPostfix-1, 0)
		}

		if .IsKeyword {
			.printSpaceBeforeIdentifier()
			.print(.Text)
			.printSpace()
		} else {
			.printSpaceBeforeOperator(.Op)
			.print(.Text)
			.prevOp = .Op
			.prevOpEnd = len(.js)
		}

		if .Op.IsPrefix() {
			.(.Value, js_ast.LPrefix-1, 0)
		}

		if  {
			.print(")")
		}

	case *js_ast.EBinary:
		 := js_ast.OpTable[.Op]
		 :=  >= .Level || (.Op == js_ast.BinOpIn && (&forbidIn) != 0)
Destructuring assignments must be parenthesized
		if  := len(.js); .stmtStart ==  || .arrowExprStart ==  {
			if ,  := .Left.Data.(*js_ast.EObject);  {
				 = true
			}
		}

		if  {
			.print("(")
			 &= ^forbidIn
		}

		 := .Level - 1
		 := .Level - 1

		if .Op.IsRightAssociative() {
			 = .Level
		}
		if .Op.IsLeftAssociative() {
			 = .Level
		}

		switch .Op {
"??" can't directly contain "||" or "&&" without being wrapped in parentheses
			if ,  := .Left.Data.(*js_ast.EBinary);  && (.Op == js_ast.BinOpLogicalOr || .Op == js_ast.BinOpLogicalAnd) {
				 = js_ast.LPrefix
			}
			if ,  := .Right.Data.(*js_ast.EBinary);  && (.Op == js_ast.BinOpLogicalOr || .Op == js_ast.BinOpLogicalAnd) {
				 = js_ast.LPrefix
			}

"**" can't contain certain unary expressions
			if ,  := .Left.Data.(*js_ast.EUnary);  && .Op.UnaryAssignTarget() == js_ast.AssignTargetNone {
				 = js_ast.LCall
			} else if ,  := .Left.Data.(*js_ast.EAwait);  {
				 = js_ast.LCall
Undefined is printed as "void 0"
				 = js_ast.LCall
Negative numbers are printed using a unary operator
				 = js_ast.LCall
When minifying, booleans are printed as "!0 and "!1"
				if ,  := .Left.Data.(*js_ast.EBoolean);  {
					 = js_ast.LCall
				}
			}
		}

		.(.Left, , &forbidIn)

		if .Op != js_ast.BinOpComma {
			.printSpace()
		}

		if .IsKeyword {
			.printSpaceBeforeIdentifier()
			.print(.Text)
		} else {
			.printSpaceBeforeOperator(.Op)
			.print(.Text)
			.prevOp = .Op
			.prevOpEnd = len(.js)
		}

		.printSpace()

		.(.Right, , &forbidIn)

		if  {
			.print(")")
		}

	default:
		panic(fmt.Sprintf("Unexpected expression of type %T", .Data))
	}
}

func ( *printer) ( js_ast.Expr) bool {
Using the original name here is ok since unbound symbols are not renamed
		 := .symbols.Get(js_ast.FollowSymbols(.symbols, .Ref))
		return .Kind == js_ast.SymbolUnbound && .OriginalName == "eval"
	}
	return false
}
Convert an integer to a byte slice without any allocations
func ( *printer) ( int) []byte {
	 :=  < 0
This assumes that -math.MinInt isn't a problem. This is fine because these integers are floating-point exponents which never go up that high.
		 = -
	}

	 := .intToBytesBuffer[:]
	 := len()
Write out the number from the end to the front
	for {
		--
		[] = '0' + byte(%10)
		 /= 10
		if  == 0 {
			break
		}
	}
Stick a negative sign on the front if needed
	if  {
		--
		[] = '-'
	}

	return [:]
}

func ( []byte) int {
	 := [0] == '-'
	if  {
		 = [1:]
	}
Parse the integer without any error checking. This doesn't need to handle integer overflow because these integers are floating-point exponents which never go up that high.
	 := 0
	for ,  := range  {
		 = *10 + int(-'0')
	}

	if  {
		return -
	}
	return 
}

We can avoid the slow call to strconv.FormatFloat() for integers less than 1000 because we know that exponential notation will always be longer than the integer representation. This is not the case for 1000 which is "1e3".
	if  < 1000 {
		if  := int64();  == float64() {
			.printBytes(.smallIntToBytes(int()))
			return
		}
	}
Format this number into a byte slice so we can mutate it in place without further reallocation
	 := []byte(strconv.FormatFloat(, 'g', -1, 64))
Simplify the exponent "e+05" => "e5" "e-05" => "e-5"
	if  := bytes.LastIndexByte(, 'e');  != -1 {
		 :=  + 1
		 := 

		switch [] {
Strip off the leading "+"
			++

Skip past the leading "-"
			++
			++
		}
Strip off leading zeros
		for  < len() && [] == '0' {
			++
		}

		 = append([:], [:]...)
	}

	 := bytes.IndexByte(, '.')

Simplify numbers starting with "0."
		 := 2
Strip off the leading zero when minifying "0.5" => ".5"
		if .options.RemoveWhitespace {
			 = [1:]
			--
		}
Try using an exponent "0.001" => "1e-3"
		if [] == '0' {
			 :=  + 1
			for [] == '0' {
				++
			}
			 := [:]
			 := .smallIntToBytes( -  - len())
Only switch if it's actually shorter
			if len() > len()+1+len() {
				 = append(append(, 'e'), ...)
			}
		}
Try to get rid of a "." and maybe also an "e"
		if  := bytes.LastIndexByte(, 'e');  != -1 {
			 := [:]
			 := [+1 : ]
			 := parseSmallInt([+1:]) - len()
Handle small exponents by appending zeros instead
"1.2e1" => "12" "1.2e2" => "120" "1.2e3" => "1200"
				if len() >= len()+len()+ {
					 = append(, ...)
					for  := 0;  < ; ++ {
						 = append(, '0')
					}
				}
"1.2e4" => "12e3"
				 := .smallIntToBytes()
				if len() >= len()+len()+1+len() {
					 = append(append(append(, ...), 'e'), ...)
				}
			}
		}
Simplify numbers ending with "0" by trying to use an exponent "1000" => "1e3"
		 := len() - 1
		for  > 0 && [-1] == '0' {
			--
		}
		 := [:]
		 := .smallIntToBytes(len() - )
Only switch if it's actually shorter
		if len() > len()+1+len() {
			 = append(append(, 'e'), ...)
		}
	}

	.printBytes()
}

func ( *printer) ( bool,  string,  []js_ast.Decl) {
	.printIndent()
	.printSpaceBeforeIdentifier()
	if  {
		.print("export ")
	}
	.printDecls(, , 0)
	.printSemicolonAfterStatement()
}

func ( *printer) ( js_ast.Stmt) {
	switch s := .Data.(type) {
	case *js_ast.SExpr:
		.printExpr(.Value, js_ast.LLowest, forbidIn)
	case *js_ast.SLocal:
		switch .Kind {
		case js_ast.LocalVar:
			.printDecls("var", .Decls, forbidIn)
		case js_ast.LocalLet:
			.printDecls("let", .Decls, forbidIn)
		case js_ast.LocalConst:
			.printDecls("const", .Decls, forbidIn)
		}
	default:
		panic("Internal error")
	}
}

func ( *printer) ( string,  []js_ast.Decl,  int) {
	.print()
	.printSpace()

	for ,  := range  {
		if  != 0 {
			.print(",")
			.printSpace()
		}
		.printBinding(.Binding)

		if .Value != nil {
			.printSpace()
			.print("=")
			.printSpace()
			.printExpr(*.Value, js_ast.LComma, )
		}
	}
}

func ( *printer) ( js_ast.Stmt) {
	if ,  := .Data.(*js_ast.SBlock);  {
		.printSpace()
		.printBlock(.Loc, .Stmts)
		.printNewline()
	} else {
		.printNewline()
		.options.Indent++
		.printStmt()
		.options.Indent--
	}
}

func ( *printer) ( logger.Loc,  []js_ast.Stmt) {
	.addSourceMapping()
	.print("{")
	.printNewline()

	.options.Indent++
	for ,  := range  {
		.printSemicolonIfNeeded()
		.printStmt()
	}
	.options.Indent--
	.needsSemicolon = false

	.printIndent()
	.print("}")
}

func ( js_ast.S) bool {
	for {
		switch current := .(type) {
		case *js_ast.SIf:
			if .No == nil {
				return true
			}
			 = .No.Data

		case *js_ast.SFor:
			 = .Body.Data

		case *js_ast.SForIn:
			 = .Body.Data

		case *js_ast.SForOf:
			 = .Body.Data

		case *js_ast.SWhile:
			 = .Body.Data

		case *js_ast.SWith:
			 = .Body.Data

		default:
			return false
		}
	}
}

func ( *printer) ( *js_ast.SIf) {
	.printSpaceBeforeIdentifier()
	.print("if")
	.printSpace()
	.print("(")
	.printExpr(.Test, js_ast.LLowest, 0)
	.print(")")

	if ,  := .Yes.Data.(*js_ast.SBlock);  {
		.printSpace()
		.printBlock(.Yes.Loc, .Stmts)

		if .No != nil {
			.printSpace()
		} else {
			.printNewline()
		}
	} else if wrapToAvoidAmbiguousElse(.Yes.Data) {
		.printSpace()
		.print("{")
		.printNewline()

		.options.Indent++
		.printStmt(.Yes)
		.options.Indent--
		.needsSemicolon = false

		.printIndent()
		.print("}")

		if .No != nil {
			.printSpace()
		} else {
			.printNewline()
		}
	} else {
		.printNewline()
		.options.Indent++
		.printStmt(.Yes)
		.options.Indent--

		if .No != nil {
			.printIndent()
		}
	}

	if .No != nil {
		.printSemicolonIfNeeded()
		.printSpaceBeforeIdentifier()
		.print("else")

		if ,  := .No.Data.(*js_ast.SBlock);  {
			.printSpace()
			.printBlock(.No.Loc, .Stmts)
			.printNewline()
		} else if ,  := .No.Data.(*js_ast.SIf);  {
			.()
		} else {
			.printNewline()
			.options.Indent++
			.printStmt(*.No)
			.options.Indent--
		}
	}
}

func ( *printer) ( string) {
Re-indent multi-line comments
		for {
			 := strings.IndexByte(, '\n')
			if  == -1 {
				break
			}
			.printIndent()
			.print([:+1])
			 = [+1:]
		}
		.printIndent()
		.print()
		.printNewline()
Print a mandatory newline after single-line comments
		.printIndent()
		.print()
		.print("\n")
	}
}

func ( *printer) ( js_ast.Stmt) {
	.addSourceMapping(.Loc)

	switch s := .Data.(type) {
	case *js_ast.SComment:
		 := .Text
		if .options.ExtractComments {
			if .extractedComments == nil {
				.extractedComments = make(map[string]bool)
			}
			.extractedComments[] = true
			break
		}
		.printIndentedComment()

	case *js_ast.SFunction:
		.printIndent()
		.printSpaceBeforeIdentifier()
		if .IsExport {
			.print("export ")
		}
		if .Fn.IsAsync {
			.print("async ")
		}
		.print("function")
		if .Fn.IsGenerator {
			.print("*")
			.printSpace()
		}
		.printSymbol(.Fn.Name.Ref)
		.printFn(.Fn)
		.printNewline()

	case *js_ast.SClass:
		.printIndent()
		.printSpaceBeforeIdentifier()
		if .IsExport {
			.print("export ")
		}
		.print("class")
		.printSymbol(.Class.Name.Ref)
		.printClass(.Class)
		.printNewline()

	case *js_ast.SEmpty:
		.printIndent()
		.print(";")
		.printNewline()

	case *js_ast.SExportDefault:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("export default")
		.printSpace()

Functions and classes must be wrapped to avoid confusion with their statement forms
			.exportDefaultStart = len(.js)

			.printExpr(*.Value.Expr, js_ast.LComma, 0)
			.printSemicolonAfterStatement()
			return
		}

		switch s2 := .Value.Stmt.Data.(type) {
		case *js_ast.SFunction:
			.printSpaceBeforeIdentifier()
			if .Fn.IsAsync {
				.print("async ")
			}
			.print("function")
			if .Fn.IsGenerator {
				.print("*")
				.printSpace()
			}
			if .Fn.Name != nil {
				.printSymbol(.Fn.Name.Ref)
			}
			.printFn(.Fn)
			.printNewline()

		case *js_ast.SClass:
			.printSpaceBeforeIdentifier()
			.print("class")
			if .Class.Name != nil {
				.printSymbol(.Class.Name.Ref)
			}
			.printClass(.Class)
			.printNewline()

		default:
			panic("Internal error")
		}

	case *js_ast.SExportStar:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("export")
		.printSpace()
		.print("*")
		.printSpace()
		if .Alias != nil {
			.print("as")
			.printSpace()
			.printSpaceBeforeIdentifier()
			.printIdentifier(.Alias.OriginalName)
			.printSpace()
			.printSpaceBeforeIdentifier()
		}
		.print("from")
		.printSpace()
		.printQuotedUTF8(.importRecords[.ImportRecordIndex].Path.Text, false /* allowBacktick */)
		.printSemicolonAfterStatement()

	case *js_ast.SExportClause:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("export")
		.printSpace()
		.print("{")

		if !.IsSingleLine {
			.options.Indent++
		}

		for ,  := range .Items {
			if  != 0 {
				.print(",")
				if .IsSingleLine {
					.printSpace()
				}
			}

			if !.IsSingleLine {
				.printNewline()
				.printIndent()
			}
			 := .renamer.NameForSymbol(.Name.Ref)
			.printIdentifier()
			if  != .Alias {
				.print(" as ")
				.printIdentifier(.Alias)
			}
		}

		if !.IsSingleLine {
			.options.Indent--
			.printNewline()
			.printIndent()
		}

		.print("}")
		.printSemicolonAfterStatement()

	case *js_ast.SExportFrom:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("export")
		.printSpace()
		.print("{")

		if !.IsSingleLine {
			.options.Indent++
		}

		for ,  := range .Items {
			if  != 0 {
				.print(",")
				if .IsSingleLine {
					.printSpace()
				}
			}

			if !.IsSingleLine {
				.printNewline()
				.printIndent()
			}
			.printIdentifier(.OriginalName)
			if .OriginalName != .Alias {
				.print(" as ")
				.printIdentifier(.Alias)
			}
		}

		if !.IsSingleLine {
			.options.Indent--
			.printNewline()
			.printIndent()
		}

		.print("}")
		.printSpace()
		.print("from")
		.printSpace()
		.printQuotedUTF8(.importRecords[.ImportRecordIndex].Path.Text, false /* allowBacktick */)
		.printSemicolonAfterStatement()

	case *js_ast.SLocal:
		switch .Kind {
		case js_ast.LocalConst:
			.printDeclStmt(.IsExport, "const", .Decls)
		case js_ast.LocalLet:
			.printDeclStmt(.IsExport, "let", .Decls)
		case js_ast.LocalVar:
			.printDeclStmt(.IsExport, "var", .Decls)
		}

	case *js_ast.SIf:
		.printIndent()
		.printIf()

	case *js_ast.SDoWhile:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("do")
		if ,  := .Body.Data.(*js_ast.SBlock);  {
			.printSpace()
			.printBlock(.Body.Loc, .Stmts)
			.printSpace()
		} else {
			.printNewline()
			.options.Indent++
			.(.Body)
			.printSemicolonIfNeeded()
			.options.Indent--
			.printIndent()
		}
		.print("while")
		.printSpace()
		.print("(")
		.printExpr(.Test, js_ast.LLowest, 0)
		.print(")")
		.printSemicolonAfterStatement()

	case *js_ast.SForIn:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("for")
		.printSpace()
		.print("(")
		.printForLoopInit(.Init)
		.printSpace()
		.printSpaceBeforeIdentifier()
		.print("in")
		.printSpace()
		.printExpr(.Value, js_ast.LLowest, 0)
		.print(")")
		.printBody(.Body)

	case *js_ast.SForOf:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("for")
		if .IsAwait {
			.print(" await")
		}
		.printSpace()
		.print("(")
		.forOfInitStart = len(.js)
		.printForLoopInit(.Init)
		.printSpace()
		.printSpaceBeforeIdentifier()
		.print("of")
		.printSpace()
		.printExpr(.Value, js_ast.LComma, 0)
		.print(")")
		.printBody(.Body)

	case *js_ast.SWhile:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("while")
		.printSpace()
		.print("(")
		.printExpr(.Test, js_ast.LLowest, 0)
		.print(")")
		.printBody(.Body)

	case *js_ast.SWith:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("with")
		.printSpace()
		.print("(")
		.printExpr(.Value, js_ast.LLowest, 0)
		.print(")")
		.printBody(.Body)

	case *js_ast.SLabel:
		.printIndent()
		.printSymbol(.Name.Ref)
		.print(":")
		.printBody(.Stmt)

	case *js_ast.STry:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("try")
		.printSpace()
		.printBlock(.BodyLoc, .Body)

		if .Catch != nil {
			.printSpace()
			.print("catch")
			if .Catch.Binding != nil {
				.printSpace()
				.print("(")
				.printBinding(*.Catch.Binding)
				.print(")")
			}
			.printSpace()
			.printBlock(.Catch.Loc, .Catch.Body)
		}

		if .Finally != nil {
			.printSpace()
			.print("finally")
			.printSpace()
			.printBlock(.Finally.Loc, .Finally.Stmts)
		}

		.printNewline()

	case *js_ast.SFor:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("for")
		.printSpace()
		.print("(")
		if .Init != nil {
			.printForLoopInit(*.Init)
		}
		.print(";")
		.printSpace()
		if .Test != nil {
			.printExpr(*.Test, js_ast.LLowest, 0)
		}
		.print(";")
		.printSpace()
		if .Update != nil {
			.printExpr(*.Update, js_ast.LLowest, 0)
		}
		.print(")")
		.printBody(.Body)

	case *js_ast.SSwitch:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("switch")
		.printSpace()
		.print("(")
		.printExpr(.Test, js_ast.LLowest, 0)
		.print(")")
		.printSpace()
		.print("{")
		.printNewline()
		.options.Indent++

		for ,  := range .Cases {
			.printSemicolonIfNeeded()
			.printIndent()

			if .Value != nil {
				.print("case")
				.printSpace()
				.printExpr(*.Value, js_ast.LLogicalAnd, 0)
			} else {
				.print("default")
			}
			.print(":")

			if len(.Body) == 1 {
				if ,  := .Body[0].Data.(*js_ast.SBlock);  {
					.printSpace()
					.printBlock(.Body[0].Loc, .Stmts)
					.printNewline()
					continue
				}
			}

			.printNewline()
			.options.Indent++
			for ,  := range .Body {
				.printSemicolonIfNeeded()
				.()
			}
			.options.Indent--
		}

		.options.Indent--
		.printIndent()
		.print("}")
		.printNewline()
		.needsSemicolon = false

	case *js_ast.SImport:
		 := 0

		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("import")
		.printSpace()

		if .DefaultName != nil {
			.printSymbol(.DefaultName.Ref)
			++
		}

		if .Items != nil {
			if  > 0 {
				.print(",")
				.printSpace()
			}

			.print("{")
			if !.IsSingleLine {
				.options.Indent++
			}

			for ,  := range *.Items {
				if  != 0 {
					.print(",")
					if .IsSingleLine {
						.printSpace()
					}
				}

				if !.IsSingleLine {
					.printNewline()
					.printIndent()
				}
				.printIdentifier(.Alias)
				 := .renamer.NameForSymbol(.Name.Ref)
				if  != .Alias {
					.print(" as ")
					.printIdentifier()
				}
			}

			if !.IsSingleLine {
				.options.Indent--
				.printNewline()
				.printIndent()
			}
			.print("}")
			++
		}

		if .StarNameLoc != nil {
			if  > 0 {
				.print(",")
				.printSpace()
			}

			.print("*")
			.printSpace()
			.print("as ")
			.printSymbol(.NamespaceRef)
			++
		}

		if  > 0 {
			.printSpace()
			.printSpaceBeforeIdentifier()
			.print("from")
			.printSpace()
		}

		.printQuotedUTF8(.importRecords[.ImportRecordIndex].Path.Text, false /* allowBacktick */)
		.printSemicolonAfterStatement()

	case *js_ast.SBlock:
		.printIndent()
		.printBlock(.Loc, .Stmts)
		.printNewline()

	case *js_ast.SDebugger:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("debugger")
		.printSemicolonAfterStatement()

	case *js_ast.SDirective:
		 := .bestQuoteCharForString(.Value, false /* allowBacktick */)
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print()
		.printQuotedUTF16(.Value, rune([0]))
		.print()
		.printSemicolonAfterStatement()

	case *js_ast.SBreak:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("break")
		if .Label != nil {
			.print(" ")
			.printSymbol(.Label.Ref)
		}
		.printSemicolonAfterStatement()

	case *js_ast.SContinue:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("continue")
		if .Label != nil {
			.print(" ")
			.printSymbol(.Label.Ref)
		}
		.printSemicolonAfterStatement()

	case *js_ast.SReturn:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("return")
		if .Value != nil {
			.printSpace()
			.printExpr(*.Value, js_ast.LLowest, 0)
		}
		.printSemicolonAfterStatement()

	case *js_ast.SThrow:
		.printIndent()
		.printSpaceBeforeIdentifier()
		.print("throw")
		.printSpace()
		.printExpr(.Value, js_ast.LLowest, 0)
		.printSemicolonAfterStatement()

	case *js_ast.SExpr:
		.printIndent()
		.stmtStart = len(.js)
		.printExpr(.Value, js_ast.LLowest, 0)
		.printSemicolonAfterStatement()

	default:
		panic(fmt.Sprintf("Unexpected statement of type %T", .Data))
	}
}

func ( *printer) () bool {
	for ,  := range .sourceMap {
		if  != ';' {
			return false
		}
	}
	return true
}

type Options struct {
	OutputFormat        config.Format
	RemoveWhitespace    bool
	MangleSyntax        bool
	ASCIIOnly           bool
	ExtractComments     bool
	AddSourceMappings   bool
	Indent              int
	ToModuleRef         js_ast.Ref
	WrapperRefForSource func(uint32) js_ast.Ref
	UnsupportedFeatures compat.JSFeature
If we're writing out a source map, this table of line start indices lets us do binary search on to figure out what line a given AST node came from
This will be present if the input file had a source map. In that case we want to map all the way back to the original input file(s).
This end state will be used to rewrite the start of the following source map chunk so that the delta-encoded VLQ numbers are preserved.
There probably isn't a source mapping at the end of the file (nor should there be) but if we're appending another source map chunk after this one, we'll need to know how many characters were in the last line we generated.
This source map chunk just contains the VLQ-encoded offsets for the "JS" field above. It's not a full source map. The bundler will be joining many source map chunks together to form the final source map.
We automatically repeat the previous source mapping if we ever generate a line that doesn't start with a mapping. This helps give files more complete mapping coverage without gaps. However, we probably shouldn't do this if the input file has a nested source map that we will be remapping through. We have no idea what state that source map is in and it could be pretty scrambled. I've seen cases where blindly repeating the last mapping for subsequent lines gives very strange and unhelpful results with source maps from other tools.
Add the top-level directive if present