-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
css_decls_font_family.go
142 lines (125 loc) · 3.57 KB
/
css_decls_font_family.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package css_parser
import (
"strings"
"github.com/evanw/esbuild/internal/css_ast"
"github.com/evanw/esbuild/internal/css_lexer"
)
// Specification: https://drafts.csswg.org/css-values-4/#common-keywords
var wideKeywords = map[string]bool{
"initial": true,
"inherit": true,
"unset": true,
}
// Specification: https://drafts.csswg.org/css-fonts/#generic-font-families
var genericFamilyNames = map[string]bool{
"serif": true,
"sans-serif": true,
"cursive": true,
"fantasy": true,
"monospace": true,
"system-ui": true,
"emoji": true,
"math": true,
"fangsong": true,
"ui-serif": true,
"ui-sans-serif": true,
"ui-monospace": true,
"ui-rounded": true,
}
// Specification: https://drafts.csswg.org/css-fonts/#font-family-prop
func (p *parser) mangleFontFamily(tokens []css_ast.Token) ([]css_ast.Token, bool) {
result, rest, ok := p.mangleFamilyNameOrGenericName(nil, tokens)
if !ok {
return nil, false
}
for len(rest) > 0 && rest[0].Kind == css_lexer.TComma {
result, rest, ok = p.mangleFamilyNameOrGenericName(append(result, rest[0]), rest[1:])
if !ok {
return nil, false
}
}
if len(rest) > 0 {
return nil, false
}
return result, true
}
func (p *parser) mangleFamilyNameOrGenericName(result []css_ast.Token, tokens []css_ast.Token) ([]css_ast.Token, []css_ast.Token, bool) {
if len(tokens) > 0 {
t := tokens[0]
// Handle <generic-family>
if t.Kind == css_lexer.TIdent && genericFamilyNames[t.Text] {
return append(result, t), tokens[1:], true
}
// Handle <family-name>
if t.Kind == css_lexer.TString {
// "If a sequence of identifiers is given as a <family-name>, the computed
// value is the name converted to a string by joining all the identifiers
// in the sequence by single spaces."
//
// More information: https://mathiasbynens.be/notes/unquoted-font-family
names := strings.Split(t.Text, " ")
for _, name := range names {
if !isValidCustomIdent(name, genericFamilyNames) {
return append(result, t), tokens[1:], true
}
}
for i, name := range names {
var whitespace css_ast.WhitespaceFlags
if i != 0 || !p.options.MinifyWhitespace {
whitespace = css_ast.WhitespaceBefore
}
result = append(result, css_ast.Token{
Kind: css_lexer.TIdent,
Text: name,
Whitespace: whitespace,
})
}
return result, tokens[1:], true
}
// "Font family names other than generic families must either be given
// quoted as <string>s, or unquoted as a sequence of one or more
// <custom-ident>."
if t.Kind == css_lexer.TIdent {
for {
if !isValidCustomIdent(t.Text, genericFamilyNames) {
return nil, nil, false
}
result = append(result, t)
tokens = tokens[1:]
if len(tokens) == 0 || tokens[0].Kind != css_lexer.TIdent {
break
}
t = tokens[0]
}
return result, tokens, true
}
}
// Anything other than the cases listed above causes us to bail
return nil, nil, false
}
// Specification: https://drafts.csswg.org/css-values-4/#custom-idents
func isValidCustomIdent(text string, predefinedKeywords map[string]bool) bool {
loweredText := strings.ToLower(text)
if predefinedKeywords[loweredText] {
return false
}
if wideKeywords[loweredText] {
return false
}
if loweredText == "default" {
return false
}
if loweredText == "" {
return false
}
// validate if it contains characters which needs to be escaped
if !css_lexer.WouldStartIdentifierWithoutEscapes(text) {
return false
}
for _, c := range text {
if !css_lexer.IsNameContinue(c) {
return false
}
}
return true
}