github.com/evanw/esbuild@v0.21.4/internal/css_parser/css_decls_list_style.go (about) 1 package css_parser 2 3 import ( 4 "strings" 5 6 "github.com/evanw/esbuild/internal/css_ast" 7 "github.com/evanw/esbuild/internal/css_lexer" 8 ) 9 10 // list-style-image: <image> | none 11 // <image>: <url> | <gradient> 12 // <url>: <url()> | <src()> 13 // <gradient>: <linear-gradient()> | <repeating-linear-gradient()> | <radial-gradient()> | <repeating-radial-gradient()> 14 // 15 // list-style-type: <counter-style> | <string> | none (where the string is a literal bullet marker) 16 // <counter-style>: <counter-style-name> | <symbols()> 17 // <counter-style-name>: not: decimal | disc | square | circle | disclosure-open | disclosure-closed | <wide keyword> 18 // when parsing a <custom-ident> with conflicts, only parse one if no other thing can claim it 19 20 func (p *parser) processListStyleShorthand(tokens []css_ast.Token) { 21 if len(tokens) < 1 || len(tokens) > 3 { 22 return 23 } 24 25 foundImage := false 26 foundPosition := false 27 typeIndex := -1 28 noneCount := 0 29 30 for i, t := range tokens { 31 switch t.Kind { 32 case css_lexer.TString: 33 // "list-style-type" is definitely not a <custom-ident> 34 return 35 36 case css_lexer.TURL: 37 if !foundImage { 38 foundImage = true 39 continue 40 } 41 42 case css_lexer.TFunction: 43 if !foundImage { 44 switch strings.ToLower(t.Text) { 45 case "src", "linear-gradient", "repeating-linear-gradient", "radial-gradient", "radial-linear-gradient": 46 foundImage = true 47 continue 48 } 49 } 50 51 case css_lexer.TIdent: 52 lower := strings.ToLower(t.Text) 53 54 // Note: If "none" is present, it's ambiguous whether it applies to 55 // "list-style-image" or "list-style-type". To resolve ambiguity it's 56 // applied at the end to whichever property isn't otherwise set. 57 if lower == "none" { 58 noneCount++ 59 continue 60 } 61 62 if !foundPosition && (lower == "inside" || lower == "outside") { 63 foundPosition = true 64 continue 65 } 66 67 if typeIndex == -1 { 68 if cssWideAndReservedKeywords[lower] || predefinedCounterStyles[lower] { 69 // "list-style-type" is definitely not a <custom-ident> 70 return 71 } 72 typeIndex = i 73 continue 74 } 75 } 76 77 // Bail if we hit an unexpected token 78 return 79 } 80 81 if typeIndex != -1 { 82 // The first "none" applies to "list-style-image" if it's missing 83 if !foundImage && noneCount > 0 { 84 noneCount-- 85 } 86 87 if noneCount > 0 { 88 // "list-style-type" is "none", not a <custom-ident> 89 return 90 } 91 92 if t := &tokens[typeIndex]; t.Kind == css_lexer.TIdent { 93 t.Kind = css_lexer.TSymbol 94 t.PayloadIndex = p.symbolForName(t.Loc, t.Text).Ref.InnerIndex 95 } 96 } 97 } 98 99 func (p *parser) processListStyleType(t *css_ast.Token) { 100 if t.Kind == css_lexer.TIdent { 101 if lower := strings.ToLower(t.Text); lower != "none" && !cssWideAndReservedKeywords[lower] && !predefinedCounterStyles[lower] { 102 t.Kind = css_lexer.TSymbol 103 t.PayloadIndex = p.symbolForName(t.Loc, t.Text).Ref.InnerIndex 104 } 105 } 106 } 107 108 // https://drafts.csswg.org/css-counter-styles-3/#predefined-counters 109 var predefinedCounterStyles = map[string]bool{ 110 // 6.1. Numeric: 111 "arabic-indic": true, 112 "armenian": true, 113 "bengali": true, 114 "cambodian": true, 115 "cjk-decimal": true, 116 "decimal-leading-zero": true, 117 "decimal": true, 118 "devanagari": true, 119 "georgian": true, 120 "gujarati": true, 121 "gurmukhi": true, 122 "hebrew": true, 123 "kannada": true, 124 "khmer": true, 125 "lao": true, 126 "lower-armenian": true, 127 "lower-roman": true, 128 "malayalam": true, 129 "mongolian": true, 130 "myanmar": true, 131 "oriya": true, 132 "persian": true, 133 "tamil": true, 134 "telugu": true, 135 "thai": true, 136 "tibetan": true, 137 "upper-armenian": true, 138 "upper-roman": true, 139 140 // 6.2. Alphabetic: 141 "hiragana-iroha": true, 142 "hiragana": true, 143 "katakana-iroha": true, 144 "katakana": true, 145 "lower-alpha": true, 146 "lower-greek": true, 147 "lower-latin": true, 148 "upper-alpha": true, 149 "upper-latin": true, 150 151 // 6.3. Symbolic: 152 "circle": true, 153 "disc": true, 154 "disclosure-closed": true, 155 "disclosure-open": true, 156 "square": true, 157 158 // 6.4. Fixed: 159 "cjk-earthly-branch": true, 160 "cjk-heavenly-stem": true, 161 162 // 7.1.1. Japanese: 163 "japanese-formal": true, 164 "japanese-informal": true, 165 166 // 7.1.2. Korean: 167 "korean-hangul-formal": true, 168 "korean-hanja-formal": true, 169 "korean-hanja-informal": true, 170 171 // 7.1.3. Chinese: 172 "simp-chinese-formal": true, 173 "simp-chinese-informal": true, 174 "trad-chinese-formal": true, 175 "trad-chinese-informal": true, 176 177 // 7.2. Ethiopic Numeric Counter Style: 178 "ethiopic-numeric": true, 179 }