github.com/evanw/esbuild@v0.21.4/internal/css_parser/css_decls_font.go (about)

     1  package css_parser
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  
     7  	"github.com/evanw/esbuild/internal/css_ast"
     8  	"github.com/evanw/esbuild/internal/css_lexer"
     9  )
    10  
    11  // Specification: https://drafts.csswg.org/css-fonts/#font-prop
    12  // [ <font-style> || <font-variant-css2> || <font-weight> || <font-stretch-css3> ]? <font-size> [ / <line-height> ]? <font-family>
    13  func (p *parser) mangleFont(tokens []css_ast.Token) []css_ast.Token {
    14  	var result []css_ast.Token
    15  
    16  	// Scan up to the font size
    17  	pos := 0
    18  	for ; pos < len(tokens); pos++ {
    19  		token := tokens[pos]
    20  		if isFontSize(token) {
    21  			break
    22  		}
    23  
    24  		switch token.Kind {
    25  		case css_lexer.TIdent:
    26  			switch strings.ToLower(token.Text) {
    27  			case "normal":
    28  				// "All subproperties of the font property are first reset to their initial values"
    29  				// This implies that "normal" doesn't do anything. Also all of the optional values
    30  				// contain "normal" as an option and they are unordered so it's impossible to say
    31  				// what property "normal" corresponds to. Just drop these tokens to save space.
    32  				continue
    33  
    34  			// <font-style>
    35  			case "italic":
    36  			case "oblique":
    37  				if pos+1 < len(tokens) && tokens[pos+1].IsAngle() {
    38  					result = append(result, token, tokens[pos+1])
    39  					pos++
    40  					continue
    41  				}
    42  
    43  			// <font-variant-css2>
    44  			case "small-caps":
    45  
    46  			// <font-weight>
    47  			case "bold", "bolder", "lighter":
    48  				result = append(result, p.mangleFontWeight(token))
    49  				continue
    50  
    51  			// <font-stretch-css3>
    52  			case "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
    53  				"semi-expanded", "expanded", "extra-expanded", "ultra-expanded":
    54  
    55  			default:
    56  				// All other tokens are unrecognized, so we bail if we hit one
    57  				return tokens
    58  			}
    59  			result = append(result, token)
    60  
    61  		case css_lexer.TNumber:
    62  			// "Only values greater than or equal to 1, and less than or equal to
    63  			// 1000, are valid, and all other values are invalid."
    64  			if value, err := strconv.ParseFloat(token.Text, 64); err != nil || value < 1 || value > 1000 {
    65  				return tokens
    66  			}
    67  			result = append(result, token)
    68  
    69  		default:
    70  			// All other tokens are unrecognized, so we bail if we hit one
    71  			return tokens
    72  		}
    73  	}
    74  
    75  	// <font-size>
    76  	if pos == len(tokens) {
    77  		return tokens
    78  	}
    79  	result = append(result, tokens[pos])
    80  	pos++
    81  
    82  	// / <line-height>
    83  	if pos < len(tokens) && tokens[pos].Kind == css_lexer.TDelimSlash {
    84  		if pos+1 == len(tokens) {
    85  			return tokens
    86  		}
    87  		result = append(result, tokens[pos], tokens[pos+1])
    88  		pos += 2
    89  
    90  		// Remove the whitespace around the "/" character
    91  		if p.options.minifyWhitespace {
    92  			result[len(result)-3].Whitespace &= ^css_ast.WhitespaceAfter
    93  			result[len(result)-2].Whitespace = 0
    94  			result[len(result)-1].Whitespace &= ^css_ast.WhitespaceBefore
    95  		}
    96  	}
    97  
    98  	// <font-family>
    99  	if family, ok := p.mangleFontFamily(tokens[pos:]); ok {
   100  		if len(result) > 0 && len(family) > 0 && family[0].Kind != css_lexer.TString {
   101  			family[0].Whitespace |= css_ast.WhitespaceBefore
   102  		}
   103  		return append(result, family...)
   104  	}
   105  	return tokens
   106  }
   107  
   108  var fontSizeKeywords = map[string]bool{
   109  	// <absolute-size>: https://drafts.csswg.org/css-fonts/#valdef-font-size-absolute-size
   110  	"xx-small":  true,
   111  	"x-small":   true,
   112  	"small":     true,
   113  	"medium":    true,
   114  	"large":     true,
   115  	"x-large":   true,
   116  	"xx-large":  true,
   117  	"xxx-large": true,
   118  
   119  	// <relative-size>: https://drafts.csswg.org/css-fonts/#valdef-font-size-relative-size
   120  	"larger":  true,
   121  	"smaller": true,
   122  }
   123  
   124  // Specification: https://drafts.csswg.org/css-fonts/#font-size-prop
   125  func isFontSize(token css_ast.Token) bool {
   126  	// <length-percentage>
   127  	if token.Kind == css_lexer.TDimension || token.Kind == css_lexer.TPercentage {
   128  		return true
   129  	}
   130  
   131  	// <absolute-size> or <relative-size>
   132  	if token.Kind == css_lexer.TIdent {
   133  		_, ok := fontSizeKeywords[strings.ToLower(token.Text)]
   134  		return ok
   135  	}
   136  
   137  	return false
   138  }