Source File
css_printer.go
Belonging Package
github.com/evanw/esbuild/internal/css_printer
package css_printer
import (
)
const quoteForURL rune = -1
type printer struct {
options Options
importRecords []ast.ImportRecord
sb strings.Builder
}
type Options struct {
MangleSyntax bool
RemoveWhitespace bool
ASCIIOnly bool
}
func ( css_ast.AST, Options) string {
:= printer{
options: ,
importRecords: .ImportRecords,
}
for , := range .Rules {
.printRule(, 0, false)
}
return .sb.String()
}
func ( *printer) ( css_ast.R, int32, bool) {
if !.options.RemoveWhitespace {
.printIndent()
}
switch r := .(type) {
.print("@charset ")
.printQuotedWithQuote(.Encoding, '"')
.print(";")
case *css_ast.RAtImport:
if .options.RemoveWhitespace {
.print("@import")
} else {
.print("@import ")
}
.printQuoted(.importRecords[.ImportRecordIndex].Path.Text)
.print(";")
case *css_ast.RAtKeyframes:
.print("@")
.printIdent(.AtToken, identNormal, mayNeedWhitespaceAfter)
.print(" ")
if .Name == "" {
.print("\"\"")
} else {
.printIdent(.Name, identNormal, canDiscardWhitespaceAfter)
}
if !.options.RemoveWhitespace {
.print(" ")
}
if .options.RemoveWhitespace {
.print("{")
} else {
.print("{\n")
}
++
if .options.MangleSyntax && len(.Rules) == 0 {
continue
}
if !.options.RemoveWhitespace {
.printIndent()
}
for , := range .Selectors {
if > 0 {
if .options.RemoveWhitespace {
.print(",")
} else {
.print(", ")
}
}
.print()
}
if !.options.RemoveWhitespace {
.print(" ")
}
.printRuleBlock(.Rules, )
if !.options.RemoveWhitespace {
.print("\n")
}
}
--
if !.options.RemoveWhitespace {
.printIndent()
}
.print("}")
if .options.MangleSyntax && len(.Rules) == 0 {
return
}
.print("@")
:= mayNeedWhitespaceAfter
if len(.Prelude) == 0 {
= canDiscardWhitespaceAfter
}
.printIdent(.AtToken, identNormal, )
if !.options.RemoveWhitespace || len(.Prelude) > 0 {
.print(" ")
}
.printTokens(.Prelude, printTokensOpts{})
if !.options.RemoveWhitespace && len(.Prelude) > 0 {
.print(" ")
}
.printRuleBlock(.Rules, )
case *css_ast.RUnknownAt:
.print("@")
:= mayNeedWhitespaceAfter
if len(.Prelude) == 0 {
= canDiscardWhitespaceAfter
}
.printIdent(.AtToken, identNormal, )
if (!.options.RemoveWhitespace && .Block != nil) || len(.Prelude) > 0 {
.print(" ")
}
.printTokens(.Prelude, printTokensOpts{})
if !.options.RemoveWhitespace && .Block != nil && len(.Prelude) > 0 {
.print(" ")
}
if .Block == nil {
.print(";")
} else {
.printTokens(.Block, printTokensOpts{})
}
if .options.MangleSyntax && len(.Rules) == 0 {
return
}
.printComplexSelectors(.Selectors, )
if !.options.RemoveWhitespace {
.print(" ")
}
.printRuleBlock(.Rules, )
case *css_ast.RQualified:
:= .printTokens(.Prelude, printTokensOpts{})
if ! && !.options.RemoveWhitespace {
.print(" ")
}
.printRuleBlock(.Rules, )
case *css_ast.RDeclaration:
.printIdent(.KeyText, identNormal, canDiscardWhitespaceAfter)
.print(":")
:= .printTokens(.Value, printTokensOpts{
indent: ,
isDeclaration: true,
})
if .Important {
if ! && !.options.RemoveWhitespace && len(.Value) > 0 {
.print(" ")
}
.print("!important")
}
if ! {
.print(";")
}
case *css_ast.RBadDeclaration:
.printTokens(.Tokens, printTokensOpts{})
if ! {
.print(";")
}
default:
panic("Internal error")
}
if !.options.RemoveWhitespace {
.print("\n")
}
}
func ( *printer) ( []css_ast.R, int32) {
if .options.RemoveWhitespace {
.print("{")
} else {
.print("{\n")
}
for , := range {
:= .options.RemoveWhitespace && +1 == len()
.printRule(, +1, )
}
if !.options.RemoveWhitespace {
.printIndent()
}
.print("}")
}
func ( *printer) ( []css_ast.ComplexSelector, int32) {
for , := range {
if > 0 {
if .options.RemoveWhitespace {
.print(",")
} else {
.print(",\n")
.printIndent()
}
}
for , := range .Selectors {
.printCompoundSelector(, == 0, +1 == len(.Selectors))
}
}
}
func ( *printer) ( css_ast.CompoundSelector, bool, bool) {
if .HasNestPrefix {
.print("&")
}
if .Combinator != "" {
if !.options.RemoveWhitespace {
.print(" ")
}
.print(.Combinator)
if !.options.RemoveWhitespace {
.print(" ")
}
} else if ! {
.print(" ")
}
if .TypeSelector != nil {
:= mayNeedWhitespaceAfter
= canDiscardWhitespaceAfter
}
.printNamespacedName(*.TypeSelector, )
}
for , := range .SubclassSelectors {
:= mayNeedWhitespaceAfter
if +1 < len(.SubclassSelectors) || len(.PseudoClassSelectors) > 0 {
= canDiscardWhitespaceAfter
}
switch s := .(type) {
case *css_ast.SSHash:
.print("#")
.printIdent(.Name, identNormal, )
case *css_ast.SSClass:
.print(".")
.printIdent(.Name, identNormal, )
case *css_ast.SSAttribute:
.print("[")
.printNamespacedName(.NamespacedName, canDiscardWhitespaceAfter)
if .MatcherOp != "" {
.print(.MatcherOp)
:= false
if css_lexer.WouldStartIdentifierWithoutEscapes(.MatcherValue) {
= true
for , := range .MatcherValue {
if !css_lexer.IsNameContinue() {
= false
break
}
}
}
if {
.printIdent(.MatcherValue, identNormal, canDiscardWhitespaceAfter)
} else {
.printQuoted(.MatcherValue)
}
}
if .MatcherModifier != 0 {
.print(" ")
.print(string(rune(.MatcherModifier)))
}
.print("]")
case *css_ast.SSPseudoClass:
.printPseudoClassSelector(*, )
}
}
if len(.PseudoClassSelectors) > 0 {
.print(":")
for , := range .PseudoClassSelectors {
:= mayNeedWhitespaceAfter
if +1 < len(.PseudoClassSelectors) || {
= canDiscardWhitespaceAfter
}
.printPseudoClassSelector(, )
}
}
}
func ( *printer) ( css_ast.NamespacedName, trailingWhitespace) {
if .NamespacePrefix != nil {
switch .NamespacePrefix.Kind {
case css_lexer.TIdent:
.printIdent(.NamespacePrefix.Text, identNormal, canDiscardWhitespaceAfter)
case css_lexer.TDelimAsterisk:
.print("*")
default:
panic("Internal error")
}
.print("|")
}
switch .Name.Kind {
case css_lexer.TIdent:
.printIdent(.Name.Text, identNormal, )
case css_lexer.TDelimAsterisk:
.print("*")
case css_lexer.TDelimAmpersand:
.print("&")
default:
panic("Internal error")
}
}
func ( *printer) ( css_ast.SSPseudoClass, trailingWhitespace) {
.print(":")
if len(.Args) > 0 {
.printIdent(.Name, identNormal, canDiscardWhitespaceAfter)
.print("(")
.printTokens(.Args, printTokensOpts{})
.print(")")
} else {
.printIdent(.Name, identNormal, )
}
}
func ( *printer) ( string) {
.sb.WriteString()
}
func ( string, bool) rune {
:= 0
:= 2
:= 2
for , := range {
switch {
case '\'':
++
++
case '"':
++
++
case '(', ')', ' ', '\t':
++
case '\\', '\n', '\r', '\f':
++
++
++
}
}
if && < && < {
return quoteForURL
}
if < {
return '\''
}
return '"'
}
func ( *printer) ( string) {
.printQuotedWithQuote(, bestQuoteCharForString(, false))
}
type escapeKind uint8
const (
escapeNone escapeKind = iota
escapeBackslash
escapeHex
)
func ( *printer) ( rune, escapeKind, string, bool) {
= escapeHex
}
switch {
case escapeNone:
.sb.WriteRune()
case escapeBackslash:
.sb.WriteRune('\\')
.sb.WriteRune()
case escapeHex:
:= fmt.Sprintf("\\%x", )
.sb.WriteString()
.sb.WriteRune(' ')
}
}
}
}
func ( *printer) ( string, rune) {
if != quoteForURL {
.sb.WriteRune()
}
for , := range {
:= escapeNone
switch {
= escapeHex
case '\\', :
= escapeBackslash
if == quoteForURL {
= escapeBackslash
}
default:
if .options.ASCIIOnly && >= 0x80 || == '\uFEFF' {
= escapeHex
}
}
.printWithEscape(, , [:], false)
}
if != quoteForURL {
.sb.WriteRune()
}
}
type identMode uint8
const (
identNormal identMode = iota
identHash
identDimensionUnit
)
type trailingWhitespace uint8
const (
mayNeedWhitespaceAfter trailingWhitespace = iota
canDiscardWhitespaceAfter
)
func ( *printer) ( string, identMode, trailingWhitespace) {
for , := range {
:= escapeNone
if .options.ASCIIOnly && >= 0x80 {
= escapeHex
if !css_lexer.IsNameContinue() {
= escapeBackslash
}
if == 0 {
switch {
case identNormal:
if !css_lexer.WouldStartIdentifierWithoutEscapes() {
= escapeBackslash
}
case identDimensionUnit:
if !css_lexer.WouldStartIdentifierWithoutEscapes() {
= escapeBackslash
= escapeHex
} else if == 'e' || == 'E' {
= escapeBackslash
}
}
}
}
}
:= == mayNeedWhitespaceAfter && != escapeNone && +utf8.RuneLen() == len()
.printWithEscape(, , [:], )
}
}
func ( *printer) ( int32) {
for , := 0, int(); < ; ++ {
.sb.WriteString(" ")
}
}
type printTokensOpts struct {
indent int32
isDeclaration bool
}
func ( *printer) ( []css_ast.Token, printTokensOpts) bool {
:= len() > 0 && ([0].Whitespace&css_ast.WhitespaceBefore) != 0
:= false
if !.options.RemoveWhitespace && .isDeclaration {
:= 0
for , := range {
if .Kind == css_lexer.TComma {
++
}
}
= >= 2
}
for , := range {
if .Kind == css_lexer.TWhitespace {
= true
continue
}
if {
if && ( == 0 || [-1].Kind == css_lexer.TComma) {
.print("\n")
.printIndent(.indent + 1)
} else {
.print(" ")
}
}
= (.Whitespace&css_ast.WhitespaceAfter) != 0 ||
(+1 < len() && ([+1].Whitespace&css_ast.WhitespaceBefore) != 0)
:= mayNeedWhitespaceAfter
if ! {
= canDiscardWhitespaceAfter
}
switch .Kind {
case css_lexer.TIdent:
.printIdent(.Text, identNormal, )
case css_lexer.TFunction:
.printIdent(.Text, identNormal, )
.print("(")
case css_lexer.TDimension:
.print(.DimensionValue())
.printIdent(.DimensionUnit(), identDimensionUnit, )
case css_lexer.TAtKeyword:
.print("@")
.printIdent(.Text, identNormal, )
case css_lexer.THash:
.print("#")
.printIdent(.Text, identHash, )
case css_lexer.TString:
.printQuoted(.Text)
case css_lexer.TURL:
:= .importRecords[.ImportRecordIndex].Path.Text
.print("url(")
.printQuotedWithQuote(, bestQuoteCharForString(, true))
.print(")")
default:
.print(.Text)
}
if .Children != nil {
.(*.Children, printTokensOpts{})
switch .Kind {
case css_lexer.TFunction:
.print(")")
case css_lexer.TOpenParen:
.print(")")
case css_lexer.TOpenBrace:
.print("}")
case css_lexer.TOpenBracket:
.print("]")
}
}
}
if {
.print(" ")
}
return
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |