All files / src index.ts

100% Statements 46/46
100% Branches 2/2
100% Functions 4/4
100% Lines 46/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 1331x 1x 1x 1x 1x 1x 1x 1x                               1x   1x   1x       1x       6x                     43x 43x 43x     7x           28x   7x 7x         4x   14x 14x         1x   7x 7x         4x   10x 10x       41x 41x   11x   1x 1x     1x   40x     40x       1x       1x   5x     1x       4x       12x   8x   4x       1x  
const unquote = require('unquote')
const globalKeywords = require('css-global-keywords')
const systemFontKeywords = require('css-system-font-keywords')
const fontWeightKeywords = require('css-font-weight-keywords')
const fontStyleKeywords = require('css-font-style-keywords')
const fontStretchKeywords = require('css-font-stretch-keywords')
import * as cssListHelpers from 'css-list-helpers'
import * as helpers from './helpers'
 
export interface ISystemFont {
	system: string
}
 
export interface IFont {
	style?: string
	variant?: string
	weight?: string
	stretch?: string
	size?: string
	lineHeight?: string | number
	family?: string[]
}
 
const errorPrefix = '[parse-css-font] '
 
export default function parseCSSFont(value: string) {
	if (typeof value !== 'string') {
		throw new TypeError(errorPrefix + 'Expected a string.')
	}
 
	if (value === '') {
		throw error('Cannot parse an empty string.')
	}
 
	if (systemFontKeywords.indexOf(value) !== -1) {
		return { system: value } as ISystemFont
	}
 
	const font: IFont = {
		lineHeight: 'normal',
		stretch: 'normal',
		style: 'normal',
		variant: 'normal',
		weight: 'normal',
	}
 
	let isLocked = false
	const tokens = cssListHelpers.splitBySpaces(value)
	let token = tokens.shift()
	for (; !!token; token = tokens.shift()) {
		if (token === 'normal' || globalKeywords.indexOf(token) !== -1) {
			;(['style', 'variant', 'weight', 'stretch'] as [
				'style',
				'variant',
				'weight',
				'stretch'
			]).forEach(prop => {
				font[prop] = token
			})
			isLocked = true
			continue
		}
 
		if (fontWeightKeywords.indexOf(token) !== -1) {
			if (isLocked) {
				continue
			}
			font.weight = token
			continue
		}
 
		if (fontStyleKeywords.indexOf(token) !== -1) {
			if (isLocked) {
				continue
			}
			font.style = token
			continue
		}
 
		if (fontStretchKeywords.indexOf(token) !== -1) {
			if (isLocked) {
				continue
			}
			font.stretch = token
			continue
		}
 
		if (helpers.isSize(token)) {
			const parts = cssListHelpers.split(token, ['/'])
			font.size = parts[0]
			if (!!parts[1]) {
				font.lineHeight = parseLineHeight(parts[1])
			} else if (tokens[0] === '/') {
				tokens.shift()
				font.lineHeight = parseLineHeight(tokens.shift() as string)
			}
			if (!tokens.length) {
				throw error('Missing required font-family.')
			}
			font.family = cssListHelpers
				.splitByCommas(tokens.join(' '))
				.map(unquote)
			return font
		}
 
		if (font.variant !== 'normal') {
			throw error('Unknown or unsupported font token: ' + font.variant)
		}
 
		if (isLocked) {
			continue
		}
		font.variant = token
	}
 
	throw error('Missing required font-size.')
}
 
function error(message: string) {
	return new Error(errorPrefix + message)
}
 
function parseLineHeight(value: string) {
	const parsed = parseFloat(value)
	if (parsed.toString() === value) {
		return parsed
	}
	return value
}
 
// @ts-ignore
module.exports = Object.assign(exports.default, exports)