416 lines
11 KiB
Plaintext
416 lines
11 KiB
Plaintext
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
function _export(target, all) {
|
||
|
for(var name in all)Object.defineProperty(target, name, {
|
||
|
enumerable: true,
|
||
|
get: all[name]
|
||
|
});
|
||
|
}
|
||
|
_export(exports, {
|
||
|
normalize: function() {
|
||
|
return normalize;
|
||
|
},
|
||
|
url: function() {
|
||
|
return url;
|
||
|
},
|
||
|
number: function() {
|
||
|
return number;
|
||
|
},
|
||
|
percentage: function() {
|
||
|
return percentage;
|
||
|
},
|
||
|
length: function() {
|
||
|
return length;
|
||
|
},
|
||
|
lineWidth: function() {
|
||
|
return lineWidth;
|
||
|
},
|
||
|
shadow: function() {
|
||
|
return shadow;
|
||
|
},
|
||
|
color: function() {
|
||
|
return color;
|
||
|
},
|
||
|
image: function() {
|
||
|
return image;
|
||
|
},
|
||
|
gradient: function() {
|
||
|
return gradient;
|
||
|
},
|
||
|
position: function() {
|
||
|
return position;
|
||
|
},
|
||
|
familyName: function() {
|
||
|
return familyName;
|
||
|
},
|
||
|
genericName: function() {
|
||
|
return genericName;
|
||
|
},
|
||
|
absoluteSize: function() {
|
||
|
return absoluteSize;
|
||
|
},
|
||
|
relativeSize: function() {
|
||
|
return relativeSize;
|
||
|
}
|
||
|
});
|
||
|
const _color = require("./color");
|
||
|
const _parseBoxShadowValue = require("./parseBoxShadowValue");
|
||
|
const _splitAtTopLevelOnly = require("./splitAtTopLevelOnly");
|
||
|
let cssFunctions = [
|
||
|
"min",
|
||
|
"max",
|
||
|
"clamp",
|
||
|
"calc"
|
||
|
];
|
||
|
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
|
||
|
function isCSSFunction(value) {
|
||
|
return cssFunctions.some((fn)=>new RegExp(`^${fn}\\(.*\\)`).test(value));
|
||
|
}
|
||
|
// These properties accept a `<dashed-ident>` as one of the values. This means that you can use them
|
||
|
// as: `timeline-scope: --tl;`
|
||
|
//
|
||
|
// Without the `var(--tl)`, in these cases we don't want to normalize the value, and you should add
|
||
|
// the `var()` yourself.
|
||
|
//
|
||
|
// More info:
|
||
|
// - https://drafts.csswg.org/scroll-animations/#propdef-timeline-scope
|
||
|
// - https://developer.mozilla.org/en-US/docs/Web/CSS/timeline-scope#dashed-ident
|
||
|
//
|
||
|
const AUTO_VAR_INJECTION_EXCEPTIONS = new Set([
|
||
|
// Concrete properties
|
||
|
"scroll-timeline-name",
|
||
|
"timeline-scope",
|
||
|
"view-timeline-name",
|
||
|
"font-palette",
|
||
|
// Shorthand properties
|
||
|
"scroll-timeline",
|
||
|
"animation-timeline",
|
||
|
"view-timeline"
|
||
|
]);
|
||
|
function normalize(value, context = null, isRoot = true) {
|
||
|
let isVarException = context && AUTO_VAR_INJECTION_EXCEPTIONS.has(context.property);
|
||
|
if (value.startsWith("--") && !isVarException) {
|
||
|
return `var(${value})`;
|
||
|
}
|
||
|
// 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, context, 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();
|
||
|
}
|
||
|
value = normalizeMathOperatorSpacing(value);
|
||
|
return value;
|
||
|
}
|
||
|
/**
|
||
|
* Add spaces around operators inside math functions
|
||
|
* like calc() that do not follow an operator, '(', or `,`.
|
||
|
*
|
||
|
* @param {string} value
|
||
|
* @returns {string}
|
||
|
*/ function normalizeMathOperatorSpacing(value) {
|
||
|
let preventFormattingInFunctions = [
|
||
|
"theme"
|
||
|
];
|
||
|
let preventFormattingKeywords = [
|
||
|
"min-content",
|
||
|
"max-content",
|
||
|
"fit-content",
|
||
|
// Env
|
||
|
"safe-area-inset-top",
|
||
|
"safe-area-inset-right",
|
||
|
"safe-area-inset-bottom",
|
||
|
"safe-area-inset-left",
|
||
|
"titlebar-area-x",
|
||
|
"titlebar-area-y",
|
||
|
"titlebar-area-width",
|
||
|
"titlebar-area-height",
|
||
|
"keyboard-inset-top",
|
||
|
"keyboard-inset-right",
|
||
|
"keyboard-inset-bottom",
|
||
|
"keyboard-inset-left",
|
||
|
"keyboard-inset-width",
|
||
|
"keyboard-inset-height",
|
||
|
"radial-gradient",
|
||
|
"linear-gradient",
|
||
|
"conic-gradient",
|
||
|
"repeating-radial-gradient",
|
||
|
"repeating-linear-gradient",
|
||
|
"repeating-conic-gradient"
|
||
|
];
|
||
|
return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match)=>{
|
||
|
let result = "";
|
||
|
function lastChar() {
|
||
|
let char = result.trimEnd();
|
||
|
return char[char.length - 1];
|
||
|
}
|
||
|
for(let i = 0; i < match.length; i++){
|
||
|
function peek(word) {
|
||
|
return word.split("").every((char, j)=>match[i + j] === char);
|
||
|
}
|
||
|
function consumeUntil(chars) {
|
||
|
let minIndex = Infinity;
|
||
|
for (let char of chars){
|
||
|
let index = match.indexOf(char, i);
|
||
|
if (index !== -1 && index < minIndex) {
|
||
|
minIndex = index;
|
||
|
}
|
||
|
}
|
||
|
let result = match.slice(i, minIndex);
|
||
|
i += result.length - 1;
|
||
|
return result;
|
||
|
}
|
||
|
let char = match[i];
|
||
|
// Handle `var(--variable)`
|
||
|
if (peek("var")) {
|
||
|
// When we consume until `)`, then we are dealing with this scenario:
|
||
|
// `var(--example)`
|
||
|
//
|
||
|
// When we consume until `,`, then we are dealing with this scenario:
|
||
|
// `var(--example, 1rem)`
|
||
|
//
|
||
|
// In this case we do want to "format", the default value as well
|
||
|
result += consumeUntil([
|
||
|
")",
|
||
|
","
|
||
|
]);
|
||
|
} else if (preventFormattingKeywords.some((keyword)=>peek(keyword))) {
|
||
|
let keyword = preventFormattingKeywords.find((keyword)=>peek(keyword));
|
||
|
result += keyword;
|
||
|
i += keyword.length - 1;
|
||
|
} else if (preventFormattingInFunctions.some((fn)=>peek(fn))) {
|
||
|
result += consumeUntil([
|
||
|
")"
|
||
|
]);
|
||
|
} else if (peek("[")) {
|
||
|
result += consumeUntil([
|
||
|
"]"
|
||
|
]);
|
||
|
} else if ([
|
||
|
"+",
|
||
|
"-",
|
||
|
"*",
|
||
|
"/"
|
||
|
].includes(char) && ![
|
||
|
"(",
|
||
|
"+",
|
||
|
"-",
|
||
|
"*",
|
||
|
"/",
|
||
|
","
|
||
|
].includes(lastChar())) {
|
||
|
result += ` ${char} `;
|
||
|
} else {
|
||
|
result += char;
|
||
|
}
|
||
|
}
|
||
|
// Simplify multiple spaces
|
||
|
return result.replace(/\s+/g, " ");
|
||
|
});
|
||
|
}
|
||
|
function url(value) {
|
||
|
return value.startsWith("url(");
|
||
|
}
|
||
|
function number(value) {
|
||
|
return !isNaN(Number(value)) || isCSSFunction(value);
|
||
|
}
|
||
|
function percentage(value) {
|
||
|
return value.endsWith("%") && number(value.slice(0, -1)) || isCSSFunction(value);
|
||
|
}
|
||
|
// Please refer to MDN when updating this list:
|
||
|
// https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units
|
||
|
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries#container_query_length_units
|
||
|
let lengthUnits = [
|
||
|
"cm",
|
||
|
"mm",
|
||
|
"Q",
|
||
|
"in",
|
||
|
"pc",
|
||
|
"pt",
|
||
|
"px",
|
||
|
"em",
|
||
|
"ex",
|
||
|
"ch",
|
||
|
"rem",
|
||
|
"lh",
|
||
|
"rlh",
|
||
|
"vw",
|
||
|
"vh",
|
||
|
"vmin",
|
||
|
"vmax",
|
||
|
"vb",
|
||
|
"vi",
|
||
|
"svw",
|
||
|
"svh",
|
||
|
"lvw",
|
||
|
"lvh",
|
||
|
"dvw",
|
||
|
"dvh",
|
||
|
"cqw",
|
||
|
"cqh",
|
||
|
"cqi",
|
||
|
"cqb",
|
||
|
"cqmin",
|
||
|
"cqmax"
|
||
|
];
|
||
|
let lengthUnitsPattern = `(?:${lengthUnits.join("|")})`;
|
||
|
function length(value) {
|
||
|
return value === "0" || new RegExp(`^[+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`).test(value) || isCSSFunction(value);
|
||
|
}
|
||
|
let lineWidths = new Set([
|
||
|
"thin",
|
||
|
"medium",
|
||
|
"thick"
|
||
|
]);
|
||
|
function lineWidth(value) {
|
||
|
return lineWidths.has(value);
|
||
|
}
|
||
|
function shadow(value) {
|
||
|
let parsedShadows = (0, _parseBoxShadowValue.parseBoxShadowValue)(normalize(value));
|
||
|
for (let parsedShadow of parsedShadows){
|
||
|
if (!parsedShadow.valid) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
function color(value) {
|
||
|
let colors = 0;
|
||
|
let result = (0, _splitAtTopLevelOnly.splitAtTopLevelOnly)(value, "_").every((part)=>{
|
||
|
part = normalize(part);
|
||
|
if (part.startsWith("var(")) return true;
|
||
|
if ((0, _color.parseColor)(part, {
|
||
|
loose: true
|
||
|
}) !== null) return colors++, true;
|
||
|
return false;
|
||
|
});
|
||
|
if (!result) return false;
|
||
|
return colors > 0;
|
||
|
}
|
||
|
function image(value) {
|
||
|
let images = 0;
|
||
|
let result = (0, _splitAtTopLevelOnly.splitAtTopLevelOnly)(value, ",").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([
|
||
|
"conic-gradient",
|
||
|
"linear-gradient",
|
||
|
"radial-gradient",
|
||
|
"repeating-conic-gradient",
|
||
|
"repeating-linear-gradient",
|
||
|
"repeating-radial-gradient"
|
||
|
]);
|
||
|
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"
|
||
|
]);
|
||
|
function position(value) {
|
||
|
let positions = 0;
|
||
|
let result = (0, _splitAtTopLevelOnly.splitAtTopLevelOnly)(value, "_").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;
|
||
|
}
|
||
|
function familyName(value) {
|
||
|
let fonts = 0;
|
||
|
let result = (0, _splitAtTopLevelOnly.splitAtTopLevelOnly)(value, ",").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"
|
||
|
]);
|
||
|
function genericName(value) {
|
||
|
return genericNames.has(value);
|
||
|
}
|
||
|
let absoluteSizes = new Set([
|
||
|
"xx-small",
|
||
|
"x-small",
|
||
|
"small",
|
||
|
"medium",
|
||
|
"large",
|
||
|
"x-large",
|
||
|
"x-large",
|
||
|
"xxx-large"
|
||
|
]);
|
||
|
function absoluteSize(value) {
|
||
|
return absoluteSizes.has(value);
|
||
|
}
|
||
|
let relativeSizes = new Set([
|
||
|
"larger",
|
||
|
"smaller"
|
||
|
]);
|
||
|
function relativeSize(value) {
|
||
|
return relativeSizes.has(value);
|
||
|
}
|