mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-05 02:29:30 +08:00
254 lines
5.4 KiB
Text
254 lines
5.4 KiB
Text
import { parseColor } from './color'
|
|
import { parseBoxShadowValue } from './parseBoxShadowValue'
|
|
|
|
let cssFunctions = ['min', 'max', 'clamp', 'calc']
|
|
|
|
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
|
|
|
|
let COMMA = /,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
|
let UNDERSCORE = /_(?![^(]*\))/g // Underscore separator that is not located between brackets. E.g.: `rgba(255,_255,_255)_black` these don't count.
|
|
|
|
// This is not a data type, but rather a function that can normalize the
|
|
// correct values.
|
|
export function normalize(value, isRoot = true) {
|
|
// Keep raw strings if it starts with `url(`
|
|
if (value.includes('url(')) {
|
|
return value
|
|
.split(/(url\(.*?\))/g)
|
|
.filter(Boolean)
|
|
.map((part) => {
|
|
if (/^url\(.*?\)$/.test(part)) {
|
|
return part
|
|
}
|
|
|
|
return normalize(part, false)
|
|
})
|
|
.join('')
|
|
}
|
|
|
|
// Convert `_` to ` `, except for escaped underscores `\_`
|
|
value = value
|
|
.replace(
|
|
/([^\\])_+/g,
|
|
(fullMatch, characterBefore) => characterBefore + ' '.repeat(fullMatch.length - 1)
|
|
)
|
|
.replace(/^_/g, ' ')
|
|
.replace(/\\_/g, '_')
|
|
|
|
// Remove leftover whitespace
|
|
if (isRoot) {
|
|
value = value.trim()
|
|
}
|
|
|
|
// Add spaces around operators inside math functions like calc() that do not follow an operator
|
|
// or '('.
|
|
value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
|
|
return match.replace(
|
|
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
|
|
'$1 $2 '
|
|
)
|
|
})
|
|
|
|
return value
|
|
}
|
|
|
|
export function url(value) {
|
|
return value.startsWith('url(')
|
|
}
|
|
|
|
export function number(value) {
|
|
return !isNaN(Number(value)) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?`).test(value))
|
|
}
|
|
|
|
export function percentage(value) {
|
|
return value.split(UNDERSCORE).every((part) => {
|
|
return /%$/g.test(part) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(part))
|
|
})
|
|
}
|
|
|
|
let lengthUnits = [
|
|
'cm',
|
|
'mm',
|
|
'Q',
|
|
'in',
|
|
'pc',
|
|
'pt',
|
|
'px',
|
|
'em',
|
|
'ex',
|
|
'ch',
|
|
'rem',
|
|
'lh',
|
|
'vw',
|
|
'vh',
|
|
'vmin',
|
|
'vmax',
|
|
]
|
|
let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
|
|
export function length(value) {
|
|
return value.split(UNDERSCORE).every((part) => {
|
|
return (
|
|
part === '0' ||
|
|
new RegExp(`${lengthUnitsPattern}$`).test(part) ||
|
|
cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?${lengthUnitsPattern}`).test(part))
|
|
)
|
|
})
|
|
}
|
|
|
|
let lineWidths = new Set(['thin', 'medium', 'thick'])
|
|
export function lineWidth(value) {
|
|
return lineWidths.has(value)
|
|
}
|
|
|
|
export function shadow(value) {
|
|
let parsedShadows = parseBoxShadowValue(normalize(value))
|
|
|
|
for (let parsedShadow of parsedShadows) {
|
|
if (!parsedShadow.valid) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export function color(value) {
|
|
let colors = 0
|
|
|
|
let result = value.split(UNDERSCORE).every((part) => {
|
|
part = normalize(part)
|
|
|
|
if (part.startsWith('var(')) return true
|
|
if (parseColor(part, { loose: true }) !== null) return colors++, true
|
|
|
|
return false
|
|
})
|
|
|
|
if (!result) return false
|
|
return colors > 0
|
|
}
|
|
|
|
export function image(value) {
|
|
let images = 0
|
|
let result = value.split(COMMA).every((part) => {
|
|
part = normalize(part)
|
|
|
|
if (part.startsWith('var(')) return true
|
|
if (
|
|
url(part) ||
|
|
gradient(part) ||
|
|
['element(', 'image(', 'cross-fade(', 'image-set('].some((fn) => part.startsWith(fn))
|
|
) {
|
|
images++
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
|
|
if (!result) return false
|
|
return images > 0
|
|
}
|
|
|
|
let gradientTypes = new Set([
|
|
'linear-gradient',
|
|
'radial-gradient',
|
|
'repeating-linear-gradient',
|
|
'repeating-radial-gradient',
|
|
'conic-gradient',
|
|
])
|
|
export function gradient(value) {
|
|
value = normalize(value)
|
|
|
|
for (let type of gradientTypes) {
|
|
if (value.startsWith(`${type}(`)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
let validPositions = new Set(['center', 'top', 'right', 'bottom', 'left'])
|
|
export function position(value) {
|
|
let positions = 0
|
|
let result = value.split(UNDERSCORE).every((part) => {
|
|
part = normalize(part)
|
|
|
|
if (part.startsWith('var(')) return true
|
|
if (validPositions.has(part) || length(part) || percentage(part)) {
|
|
positions++
|
|
return true
|
|
}
|
|
|
|
return false
|
|
})
|
|
|
|
if (!result) return false
|
|
return positions > 0
|
|
}
|
|
|
|
export function familyName(value) {
|
|
let fonts = 0
|
|
let result = value.split(COMMA).every((part) => {
|
|
part = normalize(part)
|
|
|
|
if (part.startsWith('var(')) return true
|
|
|
|
// If it contains spaces, then it should be quoted
|
|
if (part.includes(' ')) {
|
|
if (!/(['"])([^"']+)\1/g.test(part)) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// If it starts with a number, it's invalid
|
|
if (/^\d/g.test(part)) {
|
|
return false
|
|
}
|
|
|
|
fonts++
|
|
|
|
return true
|
|
})
|
|
|
|
if (!result) return false
|
|
return fonts > 0
|
|
}
|
|
|
|
let genericNames = new Set([
|
|
'serif',
|
|
'sans-serif',
|
|
'monospace',
|
|
'cursive',
|
|
'fantasy',
|
|
'system-ui',
|
|
'ui-serif',
|
|
'ui-sans-serif',
|
|
'ui-monospace',
|
|
'ui-rounded',
|
|
'math',
|
|
'emoji',
|
|
'fangsong',
|
|
])
|
|
export function genericName(value) {
|
|
return genericNames.has(value)
|
|
}
|
|
|
|
let absoluteSizes = new Set([
|
|
'xx-small',
|
|
'x-small',
|
|
'small',
|
|
'medium',
|
|
'large',
|
|
'x-large',
|
|
'x-large',
|
|
'xxx-large',
|
|
])
|
|
export function absoluteSize(value) {
|
|
return absoluteSizes.has(value)
|
|
}
|
|
|
|
let relativeSizes = new Set(['larger', 'smaller'])
|
|
export function relativeSize(value) {
|
|
return relativeSizes.has(value)
|
|
}
|