/
css_decls_font.go
135 lines (114 loc) · 3.57 KB
/
css_decls_font.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
package css_parser
import (
"strconv"
"strings"
"github.com/evanw/esbuild/internal/css_ast"
"github.com/evanw/esbuild/internal/css_lexer"
)
// Specification: https://drafts.csswg.org/css-fonts/#font-prop
// [ <font-style> || <font-variant-css2> || <font-weight> || <font-stretch-css3> ]? <font-size> [ / <line-height> ]? <font-family>
func (p *parser) mangleFont(tokens []css_ast.Token) []css_ast.Token {
var result []css_ast.Token
// Scan up to the font size
pos := 0
for ; pos < len(tokens); pos++ {
token := tokens[pos]
if isFontSize(token) {
break
}
switch token.Kind {
case css_lexer.TIdent:
switch strings.ToLower(token.Text) {
case "normal":
// "All subproperties of the font property are first reset to their initial values"
// This implies that "normal" doesn't do anything. Also all of the optional values
// contain "normal" as an option and they are unordered so it's impossible to say
// what property "normal" corresponds to. Just drop these tokens to save space.
continue
// <font-style>
case "italic":
case "oblique":
if pos+1 < len(tokens) && tokens[pos+1].IsAngle() {
result = append(result, token, tokens[pos+1])
pos++
continue
}
// <font-variant-css2>
case "small-caps":
// <font-weight>
case "bold", "bolder", "lighter":
result = append(result, p.mangleFontWeight(token))
continue
// <font-stretch-css3>
case "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
"semi-expanded", "expanded", "extra-expanded", "ultra-expanded":
default:
// All other tokens are unrecognized, so we bail if we hit one
return tokens
}
result = append(result, token)
case css_lexer.TNumber:
// "Only values greater than or equal to 1, and less than or equal to
// 1000, are valid, and all other values are invalid."
if value, err := strconv.ParseFloat(token.Text, 64); err != nil || value < 1 || value > 1000 {
return tokens
}
result = append(result, token)
default:
// All other tokens are unrecognized, so we bail if we hit one
return tokens
}
}
// <font-size>
if pos == len(tokens) {
return tokens
}
result = append(result, tokens[pos])
pos++
// / <line-height>
if pos < len(tokens) && tokens[pos].Kind == css_lexer.TDelimSlash {
if pos+1 == len(tokens) {
return tokens
}
result = append(result, tokens[pos], tokens[pos+1])
pos += 2
// Remove the whitespace around the "/" character
if p.options.MinifyWhitespace {
result[len(result)-3].Whitespace &= ^css_ast.WhitespaceAfter
result[len(result)-2].Whitespace = 0
result[len(result)-1].Whitespace &= ^css_ast.WhitespaceBefore
}
}
// <font-family>
if family, ok := p.mangleFontFamily(tokens[pos:]); ok {
return append(result, family...)
}
return tokens
}
var fontSizeKeywords = map[string]bool{
// <absolute-size>: https://drafts.csswg.org/css-fonts/#valdef-font-size-absolute-size
"xx-small": true,
"x-small": true,
"small": true,
"medium": true,
"large": true,
"x-large": true,
"xx-large": true,
"xxx-large": true,
// <relative-size>: https://drafts.csswg.org/css-fonts/#valdef-font-size-relative-size
"larger": true,
"smaller": true,
}
// Specification: https://drafts.csswg.org/css-fonts/#font-size-prop
func isFontSize(token css_ast.Token) bool {
// <length-percentage>
if token.Kind == css_lexer.TDimension || token.Kind == css_lexer.TPercentage {
return true
}
// <absolute-size> or <relative-size>
if token.Kind == css_lexer.TIdent {
_, ok := fontSizeKeywords[strings.ToLower(token.Text)]
return ok
}
return false
}