1154 lines
38 KiB
Plaintext
1154 lines
38 KiB
Plaintext
'use strict';
|
|
|
|
function escapeRegExp(string) {
|
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
}
|
|
function escapeSelector(str) {
|
|
const length = str.length;
|
|
let index = -1;
|
|
let codeUnit;
|
|
let result = "";
|
|
const firstCodeUnit = str.charCodeAt(0);
|
|
while (++index < length) {
|
|
codeUnit = str.charCodeAt(index);
|
|
if (codeUnit === 0) {
|
|
result += "\uFFFD";
|
|
continue;
|
|
}
|
|
if (codeUnit === 37) {
|
|
result += "\\%";
|
|
continue;
|
|
}
|
|
if (codeUnit === 44) {
|
|
result += "\\,";
|
|
continue;
|
|
}
|
|
if (
|
|
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
|
|
// U+007F, […]
|
|
codeUnit >= 1 && codeUnit <= 31 || codeUnit === 127 || index === 0 && codeUnit >= 48 && codeUnit <= 57 || index === 1 && codeUnit >= 48 && codeUnit <= 57 && firstCodeUnit === 45
|
|
) {
|
|
result += `\\${codeUnit.toString(16)} `;
|
|
continue;
|
|
}
|
|
if (
|
|
// If the character is the first character and is a `-` (U+002D), and
|
|
// there is no second character, […]
|
|
index === 0 && length === 1 && codeUnit === 45
|
|
) {
|
|
result += `\\${str.charAt(index)}`;
|
|
continue;
|
|
}
|
|
if (codeUnit >= 128 || codeUnit === 45 || codeUnit === 95 || codeUnit >= 48 && codeUnit <= 57 || codeUnit >= 65 && codeUnit <= 90 || codeUnit >= 97 && codeUnit <= 122) {
|
|
result += str.charAt(index);
|
|
continue;
|
|
}
|
|
result += `\\${str.charAt(index)}`;
|
|
}
|
|
return result;
|
|
}
|
|
const e = escapeSelector;
|
|
|
|
function toArray(value = []) {
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
function uniq(value) {
|
|
return Array.from(new Set(value));
|
|
}
|
|
function uniqueBy(array, equalFn) {
|
|
return array.reduce((acc, cur) => {
|
|
const index = acc.findIndex((item) => equalFn(cur, item));
|
|
if (index === -1)
|
|
acc.push(cur);
|
|
return acc;
|
|
}, []);
|
|
}
|
|
function isString(s) {
|
|
return typeof s === "string";
|
|
}
|
|
|
|
function normalizeCSSEntries(obj) {
|
|
if (isString(obj))
|
|
return obj;
|
|
return (!Array.isArray(obj) ? Object.entries(obj) : obj).filter((i) => i[1] != null);
|
|
}
|
|
function normalizeCSSValues(obj) {
|
|
if (Array.isArray(obj)) {
|
|
if (obj.find((i) => !Array.isArray(i) || Array.isArray(i[0])))
|
|
return obj.map((i) => normalizeCSSEntries(i));
|
|
else
|
|
return [obj];
|
|
} else {
|
|
return [normalizeCSSEntries(obj)];
|
|
}
|
|
}
|
|
function clearIdenticalEntries(entry) {
|
|
return entry.filter(([k, v], idx) => {
|
|
if (k.startsWith("$$"))
|
|
return false;
|
|
for (let i = idx - 1; i >= 0; i--) {
|
|
if (entry[i][0] === k && entry[i][1] === v)
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
function entriesToCss(arr) {
|
|
if (arr == null)
|
|
return "";
|
|
return clearIdenticalEntries(arr).map(([key, value]) => value != null ? `${key}:${value};` : void 0).filter(Boolean).join("");
|
|
}
|
|
function isObject(item) {
|
|
return item && typeof item === "object" && !Array.isArray(item);
|
|
}
|
|
function mergeDeep(original, patch, mergeArray = false) {
|
|
const o = original;
|
|
const p = patch;
|
|
if (Array.isArray(p)) {
|
|
if (mergeArray && Array.isArray(p))
|
|
return [...o, ...p];
|
|
else
|
|
return [...p];
|
|
}
|
|
const output = { ...o };
|
|
if (isObject(o) && isObject(p)) {
|
|
Object.keys(p).forEach((key) => {
|
|
if (isObject(o[key]) && isObject(p[key]) || Array.isArray(o[key]) && Array.isArray(p[key]))
|
|
output[key] = mergeDeep(o[key], p[key], mergeArray);
|
|
else
|
|
Object.assign(output, { [key]: p[key] });
|
|
});
|
|
}
|
|
return output;
|
|
}
|
|
function clone(val) {
|
|
let k, out, tmp;
|
|
if (Array.isArray(val)) {
|
|
out = Array(k = val.length);
|
|
while (k--)
|
|
out[k] = (tmp = val[k]) && typeof tmp === "object" ? clone(tmp) : tmp;
|
|
return out;
|
|
}
|
|
if (Object.prototype.toString.call(val) === "[object Object]") {
|
|
out = {};
|
|
for (k in val) {
|
|
if (k === "__proto__") {
|
|
Object.defineProperty(out, k, {
|
|
value: clone(val[k]),
|
|
configurable: true,
|
|
enumerable: true,
|
|
writable: true
|
|
});
|
|
} else {
|
|
out[k] = (tmp = val[k]) && typeof tmp === "object" ? clone(tmp) : tmp;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
return val;
|
|
}
|
|
function isStaticRule(rule) {
|
|
return isString(rule[0]);
|
|
}
|
|
function isStaticShortcut(sc) {
|
|
return isString(sc[0]);
|
|
}
|
|
|
|
const attributifyRE = /^\[(.+?)~?="(.*)"\]$/;
|
|
const cssIdRE = /\.(css|postcss|sass|scss|less|stylus|styl)($|\?)/;
|
|
const validateFilterRE = /[\w\u00A0-\uFFFF-_:%-?]/;
|
|
const CONTROL_SHORTCUT_NO_MERGE = "$$shortcut-no-merge";
|
|
function isAttributifySelector(selector) {
|
|
return selector.match(attributifyRE);
|
|
}
|
|
function isValidSelector(selector = "") {
|
|
return validateFilterRE.test(selector);
|
|
}
|
|
function normalizeVariant(variant) {
|
|
return typeof variant === "function" ? { match: variant } : variant;
|
|
}
|
|
function isRawUtil(util) {
|
|
return util.length === 3;
|
|
}
|
|
function notNull(value) {
|
|
return value != null;
|
|
}
|
|
function noop() {
|
|
}
|
|
|
|
var __defProp$2 = Object.defineProperty;
|
|
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField$2 = (obj, key, value) => {
|
|
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
class TwoKeyMap {
|
|
constructor() {
|
|
__publicField$2(this, "_map", /* @__PURE__ */ new Map());
|
|
}
|
|
get(key1, key2) {
|
|
const m2 = this._map.get(key1);
|
|
if (m2)
|
|
return m2.get(key2);
|
|
}
|
|
getFallback(key1, key2, fallback) {
|
|
let m2 = this._map.get(key1);
|
|
if (!m2) {
|
|
m2 = /* @__PURE__ */ new Map();
|
|
this._map.set(key1, m2);
|
|
}
|
|
if (!m2.has(key2))
|
|
m2.set(key2, fallback);
|
|
return m2.get(key2);
|
|
}
|
|
set(key1, key2, value) {
|
|
let m2 = this._map.get(key1);
|
|
if (!m2) {
|
|
m2 = /* @__PURE__ */ new Map();
|
|
this._map.set(key1, m2);
|
|
}
|
|
m2.set(key2, value);
|
|
return this;
|
|
}
|
|
has(key1, key2) {
|
|
return this._map.get(key1)?.has(key2);
|
|
}
|
|
delete(key1, key2) {
|
|
return this._map.get(key1)?.delete(key2) || false;
|
|
}
|
|
deleteTop(key1) {
|
|
return this._map.delete(key1);
|
|
}
|
|
map(fn) {
|
|
return Array.from(this._map.entries()).flatMap(([k1, m2]) => Array.from(m2.entries()).map(([k2, v]) => {
|
|
return fn(v, k1, k2);
|
|
}));
|
|
}
|
|
}
|
|
class BetterMap extends Map {
|
|
map(mapFn) {
|
|
const result = [];
|
|
this.forEach((v, k) => {
|
|
result.push(mapFn(v, k));
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
var __defProp$1 = Object.defineProperty;
|
|
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField$1 = (obj, key, value) => {
|
|
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
class CountableSet extends Set {
|
|
constructor(values) {
|
|
super(values);
|
|
__publicField$1(this, "_map");
|
|
this._map ?? (this._map = /* @__PURE__ */ new Map());
|
|
}
|
|
add(key) {
|
|
this._map ?? (this._map = /* @__PURE__ */ new Map());
|
|
this._map.set(key, (this._map.get(key) ?? 0) + 1);
|
|
return super.add(key);
|
|
}
|
|
delete(key) {
|
|
this._map.delete(key);
|
|
return super.delete(key);
|
|
}
|
|
clear() {
|
|
this._map.clear();
|
|
super.clear();
|
|
}
|
|
getCount(key) {
|
|
return this._map.get(key) ?? 0;
|
|
}
|
|
setCount(key, count) {
|
|
this._map.set(key, count);
|
|
return super.add(key);
|
|
}
|
|
}
|
|
function isCountableSet(value) {
|
|
return value instanceof CountableSet;
|
|
}
|
|
|
|
function withLayer(layer, rules) {
|
|
rules.forEach((r) => {
|
|
if (!r[2])
|
|
r[2] = { layer };
|
|
else
|
|
r[2].layer = layer;
|
|
});
|
|
return rules;
|
|
}
|
|
|
|
const regexCache = {};
|
|
function makeRegexClassGroup(separators = ["-", ":"]) {
|
|
const key = separators.join("|");
|
|
if (!regexCache[key])
|
|
regexCache[key] = new RegExp(`((?:[!@<~\\w+:_/-]|\\[&?>?:?\\S*\\])+?)(${key})\\(((?:[~!<>\\w\\s:/\\\\,%#.$?-]|\\[.*?\\])+?)\\)(?!\\s*?=>)`, "gm");
|
|
regexCache[key].lastIndex = 0;
|
|
return regexCache[key];
|
|
}
|
|
function parseVariantGroup(str, separators = ["-", ":"], depth = 5) {
|
|
const regexClassGroup = makeRegexClassGroup(separators);
|
|
let hasChanged;
|
|
let content = str.toString();
|
|
const prefixes = /* @__PURE__ */ new Set();
|
|
const groupsByOffset = /* @__PURE__ */ new Map();
|
|
do {
|
|
hasChanged = false;
|
|
content = content.replace(
|
|
regexClassGroup,
|
|
(from, pre, sep, body, groupOffset) => {
|
|
if (!separators.includes(sep))
|
|
return from;
|
|
hasChanged = true;
|
|
prefixes.add(pre + sep);
|
|
const bodyOffset = groupOffset + pre.length + sep.length + 1;
|
|
const group = { length: from.length, items: [] };
|
|
groupsByOffset.set(groupOffset, group);
|
|
for (const itemMatch of [...body.matchAll(/\S+/g)]) {
|
|
const itemOffset = bodyOffset + itemMatch.index;
|
|
let innerItems = groupsByOffset.get(itemOffset)?.items;
|
|
if (innerItems) {
|
|
groupsByOffset.delete(itemOffset);
|
|
} else {
|
|
innerItems = [{
|
|
offset: itemOffset,
|
|
length: itemMatch[0].length,
|
|
className: itemMatch[0]
|
|
}];
|
|
}
|
|
for (const item of innerItems) {
|
|
item.className = item.className === "~" ? pre : item.className.replace(/^(!?)(.*)/, `$1${pre}${sep}$2`);
|
|
group.items.push(item);
|
|
}
|
|
}
|
|
return "$".repeat(from.length);
|
|
}
|
|
);
|
|
depth -= 1;
|
|
} while (hasChanged && depth);
|
|
let expanded;
|
|
if (typeof str === "string") {
|
|
expanded = "";
|
|
let prevOffset = 0;
|
|
for (const [offset, group] of groupsByOffset) {
|
|
expanded += str.slice(prevOffset, offset);
|
|
expanded += group.items.map((item) => item.className).join(" ");
|
|
prevOffset = offset + group.length;
|
|
}
|
|
expanded += str.slice(prevOffset);
|
|
} else {
|
|
expanded = str;
|
|
for (const [offset, group] of groupsByOffset) {
|
|
expanded.overwrite(
|
|
offset,
|
|
offset + group.length,
|
|
group.items.map((item) => item.className).join(" ")
|
|
);
|
|
}
|
|
}
|
|
return {
|
|
prefixes: Array.from(prefixes),
|
|
hasChanged,
|
|
groupsByOffset,
|
|
// Computed lazily because MagicString's toString does a lot of work
|
|
get expanded() {
|
|
return expanded.toString();
|
|
}
|
|
};
|
|
}
|
|
function collapseVariantGroup(str, prefixes) {
|
|
const collection = /* @__PURE__ */ new Map();
|
|
const sortedPrefix = prefixes.sort((a, b) => b.length - a.length);
|
|
return str.split(/\s+/g).map((part) => {
|
|
const prefix = sortedPrefix.find((prefix2) => part.startsWith(prefix2));
|
|
if (!prefix)
|
|
return part;
|
|
const body = part.slice(prefix.length);
|
|
if (collection.has(prefix)) {
|
|
collection.get(prefix).push(body);
|
|
return null;
|
|
} else {
|
|
const items = [body];
|
|
collection.set(prefix, items);
|
|
return {
|
|
prefix,
|
|
items
|
|
};
|
|
}
|
|
}).filter(notNull).map((i) => {
|
|
if (typeof i === "string")
|
|
return i;
|
|
return `${i.prefix}(${i.items.join(" ")})`;
|
|
}).join(" ");
|
|
}
|
|
function expandVariantGroup(str, separators = ["-", ":"], depth = 5) {
|
|
const res = parseVariantGroup(str, separators, depth);
|
|
return typeof str === "string" ? res.expanded : str;
|
|
}
|
|
|
|
const warned = /* @__PURE__ */ new Set();
|
|
function warnOnce(msg) {
|
|
if (warned.has(msg))
|
|
return;
|
|
console.warn("[unocss]", msg);
|
|
warned.add(msg);
|
|
}
|
|
|
|
const defaultSplitRE = /[\\:]?[\s'"`;{}]+/g;
|
|
const splitWithVariantGroupRE = /([\\:]?[\s"'`;<>]|:\(|\)"|\)\s)/g;
|
|
function splitCode(code) {
|
|
return code.split(defaultSplitRE);
|
|
}
|
|
const extractorSplit = {
|
|
name: "@unocss/core/extractor-split",
|
|
order: 0,
|
|
extract({ code }) {
|
|
return splitCode(code);
|
|
}
|
|
};
|
|
|
|
function createNanoEvents() {
|
|
return {
|
|
events: {},
|
|
emit(event, ...args) {
|
|
(this.events[event] || []).forEach((i) => i(...args));
|
|
},
|
|
on(event, cb) {
|
|
(this.events[event] = this.events[event] || []).push(cb);
|
|
return () => this.events[event] = (this.events[event] || []).filter((i) => i !== cb);
|
|
}
|
|
};
|
|
}
|
|
|
|
const LAYER_DEFAULT = "default";
|
|
const LAYER_PREFLIGHTS = "preflights";
|
|
const LAYER_SHORTCUTS = "shortcuts";
|
|
const LAYER_IMPORTS = "imports";
|
|
const DEFAULT_LAYERS = {
|
|
[LAYER_IMPORTS]: -200,
|
|
[LAYER_PREFLIGHTS]: -100,
|
|
[LAYER_SHORTCUTS]: -10,
|
|
[LAYER_DEFAULT]: 0
|
|
};
|
|
|
|
function resolveShortcuts(shortcuts) {
|
|
return toArray(shortcuts).flatMap((s) => {
|
|
if (Array.isArray(s))
|
|
return [s];
|
|
return Object.entries(s);
|
|
});
|
|
}
|
|
const __RESOLVED = "_uno_resolved";
|
|
function resolvePreset(presetInput) {
|
|
let preset = typeof presetInput === "function" ? presetInput() : presetInput;
|
|
if (__RESOLVED in preset)
|
|
return preset;
|
|
preset = { ...preset };
|
|
Object.defineProperty(preset, __RESOLVED, {
|
|
value: true,
|
|
enumerable: false
|
|
});
|
|
const shortcuts = preset.shortcuts ? resolveShortcuts(preset.shortcuts) : void 0;
|
|
preset.shortcuts = shortcuts;
|
|
if (preset.prefix || preset.layer) {
|
|
const apply = (i) => {
|
|
if (!i[2])
|
|
i[2] = {};
|
|
const meta = i[2];
|
|
if (meta.prefix == null && preset.prefix)
|
|
meta.prefix = toArray(preset.prefix);
|
|
if (meta.layer == null && preset.layer)
|
|
meta.layer = preset.layer;
|
|
};
|
|
shortcuts?.forEach(apply);
|
|
preset.rules?.forEach(apply);
|
|
}
|
|
return preset;
|
|
}
|
|
function resolvePresets(preset) {
|
|
const root = resolvePreset(preset);
|
|
if (!root.presets)
|
|
return [root];
|
|
const nested = (root.presets || []).flatMap(toArray).flatMap(resolvePresets);
|
|
return [root, ...nested];
|
|
}
|
|
function resolveConfig(userConfig = {}, defaults = {}) {
|
|
const config = Object.assign({}, defaults, userConfig);
|
|
const rawPresets = uniqueBy((config.presets || []).flatMap(toArray).flatMap(resolvePresets), (a, b) => a.name === b.name);
|
|
const sortedPresets = [
|
|
...rawPresets.filter((p) => p.enforce === "pre"),
|
|
...rawPresets.filter((p) => !p.enforce),
|
|
...rawPresets.filter((p) => p.enforce === "post")
|
|
];
|
|
const sources = [
|
|
...sortedPresets,
|
|
config
|
|
];
|
|
const sourcesReversed = [...sources].reverse();
|
|
const layers = Object.assign({}, DEFAULT_LAYERS, ...sources.map((i) => i.layers));
|
|
function getMerged(key) {
|
|
return uniq(sources.flatMap((p) => toArray(p[key] || [])));
|
|
}
|
|
const extractors = getMerged("extractors");
|
|
let extractorDefault = sourcesReversed.find((i) => i.extractorDefault !== void 0)?.extractorDefault;
|
|
if (extractorDefault === void 0)
|
|
extractorDefault = extractorSplit;
|
|
if (extractorDefault && !extractors.includes(extractorDefault))
|
|
extractors.unshift(extractorDefault);
|
|
extractors.sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
const rules = getMerged("rules");
|
|
const rulesStaticMap = {};
|
|
const rulesSize = rules.length;
|
|
const rulesDynamic = rules.map((rule, i) => {
|
|
if (isStaticRule(rule)) {
|
|
const prefixes = toArray(rule[2]?.prefix || "");
|
|
prefixes.forEach((prefix) => {
|
|
rulesStaticMap[prefix + rule[0]] = [i, rule[1], rule[2], rule];
|
|
});
|
|
return void 0;
|
|
}
|
|
return [i, ...rule];
|
|
}).filter(Boolean).reverse();
|
|
let theme = mergeThemes(sources.map((p) => p.theme));
|
|
const extendThemes = getMerged("extendTheme");
|
|
for (const extendTheme of extendThemes)
|
|
theme = extendTheme(theme) || theme;
|
|
const autocomplete = {
|
|
templates: uniq(sources.flatMap((p) => toArray(p.autocomplete?.templates))),
|
|
extractors: sources.flatMap((p) => toArray(p.autocomplete?.extractors)).sort((a, b) => (a.order || 0) - (b.order || 0)),
|
|
shorthands: mergeAutocompleteShorthands(sources.map((p) => p.autocomplete?.shorthands || {}))
|
|
};
|
|
let separators = getMerged("separators");
|
|
if (!separators.length)
|
|
separators = [":", "-"];
|
|
const resolved = {
|
|
mergeSelectors: true,
|
|
warn: true,
|
|
sortLayers: (layers2) => layers2,
|
|
...config,
|
|
blocklist: getMerged("blocklist"),
|
|
presets: sortedPresets,
|
|
envMode: config.envMode || "build",
|
|
shortcutsLayer: config.shortcutsLayer || "shortcuts",
|
|
layers,
|
|
theme,
|
|
rulesSize,
|
|
rulesDynamic,
|
|
rulesStaticMap,
|
|
preprocess: getMerged("preprocess"),
|
|
postprocess: getMerged("postprocess"),
|
|
preflights: getMerged("preflights"),
|
|
autocomplete,
|
|
variants: getMerged("variants").map(normalizeVariant).sort((a, b) => (a.order || 0) - (b.order || 0)),
|
|
shortcuts: resolveShortcuts(getMerged("shortcuts")).reverse(),
|
|
extractors,
|
|
safelist: getMerged("safelist"),
|
|
separators,
|
|
details: config.details ?? config.envMode === "dev"
|
|
};
|
|
for (const p of sources)
|
|
p?.configResolved?.(resolved);
|
|
return resolved;
|
|
}
|
|
function mergeConfigs(configs) {
|
|
const maybeArrays = ["shortcuts", "preprocess", "postprocess"];
|
|
const config = configs.map((config2) => Object.entries(config2).reduce((acc, [key, value]) => ({
|
|
...acc,
|
|
[key]: maybeArrays.includes(key) ? toArray(value) : value
|
|
}), {})).reduce(({ theme: themeA, ...a }, { theme: themeB, ...b }) => {
|
|
const c = mergeDeep(a, b, true);
|
|
if (themeA || themeB)
|
|
c.theme = mergeThemes([themeA, themeB]);
|
|
return c;
|
|
}, {});
|
|
return config;
|
|
}
|
|
function mergeThemes(themes) {
|
|
return themes.map((theme) => theme ? clone(theme) : {}).reduce((a, b) => mergeDeep(a, b), {});
|
|
}
|
|
function mergeAutocompleteShorthands(shorthands) {
|
|
return shorthands.reduce((a, b) => {
|
|
const rs = {};
|
|
for (const key in b) {
|
|
const value = b[key];
|
|
if (Array.isArray(value))
|
|
rs[key] = `(${value.join("|")})`;
|
|
else
|
|
rs[key] = value;
|
|
}
|
|
return {
|
|
...a,
|
|
...rs
|
|
};
|
|
}, {});
|
|
}
|
|
function definePreset(preset) {
|
|
return preset;
|
|
}
|
|
|
|
const version = "0.58.5";
|
|
|
|
var __defProp = Object.defineProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
class UnoGenerator {
|
|
constructor(userConfig = {}, defaults = {}) {
|
|
this.userConfig = userConfig;
|
|
this.defaults = defaults;
|
|
__publicField(this, "version", version);
|
|
__publicField(this, "_cache", /* @__PURE__ */ new Map());
|
|
__publicField(this, "config");
|
|
__publicField(this, "blocked", /* @__PURE__ */ new Set());
|
|
__publicField(this, "parentOrders", /* @__PURE__ */ new Map());
|
|
__publicField(this, "events", createNanoEvents());
|
|
this.config = resolveConfig(userConfig, defaults);
|
|
this.events.emit("config", this.config);
|
|
}
|
|
setConfig(userConfig, defaults) {
|
|
if (!userConfig)
|
|
return;
|
|
if (defaults)
|
|
this.defaults = defaults;
|
|
this.userConfig = userConfig;
|
|
this.blocked.clear();
|
|
this.parentOrders.clear();
|
|
this._cache.clear();
|
|
this.config = resolveConfig(userConfig, this.defaults);
|
|
this.events.emit("config", this.config);
|
|
}
|
|
async applyExtractors(code, id, extracted = /* @__PURE__ */ new Set()) {
|
|
const context = {
|
|
original: code,
|
|
code,
|
|
id,
|
|
extracted,
|
|
envMode: this.config.envMode
|
|
};
|
|
for (const extractor of this.config.extractors) {
|
|
const result = await extractor.extract?.(context);
|
|
if (!result)
|
|
continue;
|
|
if (isCountableSet(result) && isCountableSet(extracted)) {
|
|
for (const token of result)
|
|
extracted.setCount(token, extracted.getCount(token) + result.getCount(token));
|
|
} else {
|
|
for (const token of result)
|
|
extracted.add(token);
|
|
}
|
|
}
|
|
return extracted;
|
|
}
|
|
makeContext(raw, applied) {
|
|
const context = {
|
|
rawSelector: raw,
|
|
currentSelector: applied[1],
|
|
theme: this.config.theme,
|
|
generator: this,
|
|
variantHandlers: applied[2],
|
|
constructCSS: (...args) => this.constructCustomCSS(context, ...args),
|
|
variantMatch: applied
|
|
};
|
|
return context;
|
|
}
|
|
async parseToken(raw, alias) {
|
|
if (this.blocked.has(raw))
|
|
return;
|
|
const cacheKey = `${raw}${alias ? ` ${alias}` : ""}`;
|
|
if (this._cache.has(cacheKey))
|
|
return this._cache.get(cacheKey);
|
|
let current = raw;
|
|
for (const p of this.config.preprocess)
|
|
current = p(raw);
|
|
if (this.isBlocked(current)) {
|
|
this.blocked.add(raw);
|
|
this._cache.set(cacheKey, null);
|
|
return;
|
|
}
|
|
const applied = await this.matchVariants(raw, current);
|
|
if (!applied || this.isBlocked(applied[1])) {
|
|
this.blocked.add(raw);
|
|
this._cache.set(cacheKey, null);
|
|
return;
|
|
}
|
|
const context = this.makeContext(raw, [alias || applied[0], applied[1], applied[2], applied[3]]);
|
|
if (this.config.details)
|
|
context.variants = [...applied[3]];
|
|
const expanded = await this.expandShortcut(context.currentSelector, context);
|
|
const utils = expanded ? await this.stringifyShortcuts(context.variantMatch, context, expanded[0], expanded[1]) : (await this.parseUtil(context.variantMatch, context))?.map((i) => this.stringifyUtil(i, context)).filter(notNull);
|
|
if (utils?.length) {
|
|
this._cache.set(cacheKey, utils);
|
|
return utils;
|
|
}
|
|
this._cache.set(cacheKey, null);
|
|
}
|
|
async generate(input, options = {}) {
|
|
const {
|
|
id,
|
|
scope,
|
|
preflights = true,
|
|
safelist = true,
|
|
minify = false,
|
|
extendedInfo = false
|
|
} = options;
|
|
const tokens = isString(input) ? await this.applyExtractors(
|
|
input,
|
|
id,
|
|
extendedInfo ? new CountableSet() : /* @__PURE__ */ new Set()
|
|
) : Array.isArray(input) ? new Set(input) : input;
|
|
if (safelist) {
|
|
this.config.safelist.forEach((s) => {
|
|
if (!tokens.has(s))
|
|
tokens.add(s);
|
|
});
|
|
}
|
|
const nl = minify ? "" : "\n";
|
|
const layerSet = /* @__PURE__ */ new Set([LAYER_DEFAULT]);
|
|
const matched = extendedInfo ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Set();
|
|
const sheet = /* @__PURE__ */ new Map();
|
|
let preflightsMap = {};
|
|
const tokenPromises = Array.from(tokens).map(async (raw) => {
|
|
if (matched.has(raw))
|
|
return;
|
|
const payload = await this.parseToken(raw);
|
|
if (payload == null)
|
|
return;
|
|
if (matched instanceof Map) {
|
|
matched.set(raw, {
|
|
data: payload,
|
|
count: isCountableSet(tokens) ? tokens.getCount(raw) : -1
|
|
});
|
|
} else {
|
|
matched.add(raw);
|
|
}
|
|
for (const item of payload) {
|
|
const parent = item[3] || "";
|
|
const layer = item[4]?.layer;
|
|
if (!sheet.has(parent))
|
|
sheet.set(parent, []);
|
|
sheet.get(parent).push(item);
|
|
if (layer)
|
|
layerSet.add(layer);
|
|
}
|
|
});
|
|
await Promise.all(tokenPromises);
|
|
await (async () => {
|
|
if (!preflights)
|
|
return;
|
|
const preflightContext = {
|
|
generator: this,
|
|
theme: this.config.theme
|
|
};
|
|
const preflightLayerSet = /* @__PURE__ */ new Set([]);
|
|
this.config.preflights.forEach(({ layer = LAYER_PREFLIGHTS }) => {
|
|
layerSet.add(layer);
|
|
preflightLayerSet.add(layer);
|
|
});
|
|
preflightsMap = Object.fromEntries(
|
|
await Promise.all(Array.from(preflightLayerSet).map(
|
|
async (layer) => {
|
|
const preflights2 = await Promise.all(
|
|
this.config.preflights.filter((i) => (i.layer || LAYER_PREFLIGHTS) === layer).map(async (i) => await i.getCSS(preflightContext))
|
|
);
|
|
const css = preflights2.filter(Boolean).join(nl);
|
|
return [layer, css];
|
|
}
|
|
))
|
|
);
|
|
})();
|
|
const layers = this.config.sortLayers(Array.from(layerSet).sort((a, b) => (this.config.layers[a] ?? 0) - (this.config.layers[b] ?? 0) || a.localeCompare(b)));
|
|
const layerCache = {};
|
|
const getLayer = (layer = LAYER_DEFAULT) => {
|
|
if (layerCache[layer])
|
|
return layerCache[layer];
|
|
let css = Array.from(sheet).sort((a, b) => (this.parentOrders.get(a[0]) ?? 0) - (this.parentOrders.get(b[0]) ?? 0) || a[0]?.localeCompare(b[0] || "") || 0).map(([parent, items]) => {
|
|
const size = items.length;
|
|
const sorted = items.filter((i) => (i[4]?.layer || LAYER_DEFAULT) === layer).sort((a, b) => {
|
|
return a[0] - b[0] || (a[4]?.sort || 0) - (b[4]?.sort || 0) || a[5]?.currentSelector?.localeCompare(b[5]?.currentSelector ?? "") || a[1]?.localeCompare(b[1] || "") || a[2]?.localeCompare(b[2] || "") || 0;
|
|
}).map(([, selector, body, , meta, , variantNoMerge]) => {
|
|
const scopedSelector = selector ? applyScope(selector, scope) : selector;
|
|
return [
|
|
[[scopedSelector ?? "", meta?.sort ?? 0]],
|
|
body,
|
|
!!(variantNoMerge ?? meta?.noMerge)
|
|
];
|
|
});
|
|
if (!sorted.length)
|
|
return void 0;
|
|
const rules = sorted.reverse().map(([selectorSortPair, body, noMerge], idx) => {
|
|
if (!noMerge && this.config.mergeSelectors) {
|
|
for (let i = idx + 1; i < size; i++) {
|
|
const current = sorted[i];
|
|
if (current && !current[2] && (selectorSortPair && current[0] || selectorSortPair == null && current[0] == null) && current[1] === body) {
|
|
if (selectorSortPair && current[0])
|
|
current[0].push(...selectorSortPair);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
const selectors = selectorSortPair ? uniq(selectorSortPair.sort((a, b) => a[1] - b[1] || a[0]?.localeCompare(b[0] || "") || 0).map((pair) => pair[0]).filter(Boolean)) : [];
|
|
return selectors.length ? `${selectors.join(`,${nl}`)}{${body}}` : body;
|
|
}).filter(Boolean).reverse().join(nl);
|
|
if (!parent)
|
|
return rules;
|
|
const parents = parent.split(" $$ ");
|
|
return `${parents.join("{")}{${nl}${rules}${nl}${"}".repeat(parents.length)}`;
|
|
}).filter(Boolean).join(nl);
|
|
if (preflights) {
|
|
css = [preflightsMap[layer], css].filter(Boolean).join(nl);
|
|
}
|
|
const layerMark = minify ? "" : `/* layer: ${layer} */${nl}`;
|
|
return layerCache[layer] = css ? layerMark + css : "";
|
|
};
|
|
const getLayers = (includes = layers, excludes) => {
|
|
return includes.filter((i) => !excludes?.includes(i)).map((i) => getLayer(i) || "").filter(Boolean).join(nl);
|
|
};
|
|
return {
|
|
get css() {
|
|
return getLayers();
|
|
},
|
|
layers,
|
|
matched,
|
|
getLayers,
|
|
getLayer
|
|
};
|
|
}
|
|
async matchVariants(raw, current) {
|
|
const variants = /* @__PURE__ */ new Set();
|
|
const handlers = [];
|
|
let processed = current || raw;
|
|
let applied = true;
|
|
const context = {
|
|
rawSelector: raw,
|
|
theme: this.config.theme,
|
|
generator: this
|
|
};
|
|
while (applied) {
|
|
applied = false;
|
|
for (const v of this.config.variants) {
|
|
if (!v.multiPass && variants.has(v))
|
|
continue;
|
|
let handler = await v.match(processed, context);
|
|
if (!handler)
|
|
continue;
|
|
if (isString(handler)) {
|
|
if (handler === processed)
|
|
continue;
|
|
handler = { matcher: handler };
|
|
}
|
|
processed = handler.matcher;
|
|
handlers.unshift(handler);
|
|
variants.add(v);
|
|
applied = true;
|
|
break;
|
|
}
|
|
if (!applied)
|
|
break;
|
|
if (handlers.length > 500)
|
|
throw new Error(`Too many variants applied to "${raw}"`);
|
|
}
|
|
return [raw, processed, handlers, variants];
|
|
}
|
|
applyVariants(parsed, variantHandlers = parsed[4], raw = parsed[1]) {
|
|
const handler = variantHandlers.slice().sort((a, b) => (a.order || 0) - (b.order || 0)).reduceRight(
|
|
(previous, v) => (input) => {
|
|
const entries = v.body?.(input.entries) || input.entries;
|
|
const parents = Array.isArray(v.parent) ? v.parent : [v.parent, void 0];
|
|
return (v.handle ?? defaultVariantHandler)({
|
|
...input,
|
|
entries,
|
|
selector: v.selector?.(input.selector, entries) || input.selector,
|
|
parent: parents[0] || input.parent,
|
|
parentOrder: parents[1] || input.parentOrder,
|
|
layer: v.layer || input.layer,
|
|
sort: v.sort || input.sort
|
|
}, previous);
|
|
},
|
|
(input) => input
|
|
);
|
|
const variantContextResult = handler({
|
|
prefix: "",
|
|
selector: toEscapedSelector(raw),
|
|
pseudo: "",
|
|
entries: parsed[2]
|
|
});
|
|
const { parent, parentOrder } = variantContextResult;
|
|
if (parent != null && parentOrder != null)
|
|
this.parentOrders.set(parent, parentOrder);
|
|
const obj = {
|
|
selector: [
|
|
variantContextResult.prefix,
|
|
variantContextResult.selector,
|
|
variantContextResult.pseudo
|
|
].join(""),
|
|
entries: variantContextResult.entries,
|
|
parent,
|
|
layer: variantContextResult.layer,
|
|
sort: variantContextResult.sort,
|
|
noMerge: variantContextResult.noMerge
|
|
};
|
|
for (const p of this.config.postprocess)
|
|
p(obj);
|
|
return obj;
|
|
}
|
|
constructCustomCSS(context, body, overrideSelector) {
|
|
const normalizedBody = normalizeCSSEntries(body);
|
|
if (isString(normalizedBody))
|
|
return normalizedBody;
|
|
const { selector, entries, parent } = this.applyVariants([0, overrideSelector || context.rawSelector, normalizedBody, void 0, context.variantHandlers]);
|
|
const cssBody = `${selector}{${entriesToCss(entries)}}`;
|
|
if (parent)
|
|
return `${parent}{${cssBody}}`;
|
|
return cssBody;
|
|
}
|
|
async parseUtil(input, context, internal = false, shortcutPrefix) {
|
|
const [raw, processed, variantHandlers] = isString(input) ? await this.matchVariants(input) : input;
|
|
if (this.config.details)
|
|
context.rules = context.rules ?? [];
|
|
const staticMatch = this.config.rulesStaticMap[processed];
|
|
if (staticMatch) {
|
|
if (staticMatch[1] && (internal || !staticMatch[2]?.internal)) {
|
|
if (this.config.details)
|
|
context.rules.push(staticMatch[3]);
|
|
const index = staticMatch[0];
|
|
const entry = normalizeCSSEntries(staticMatch[1]);
|
|
const meta = staticMatch[2];
|
|
if (isString(entry))
|
|
return [[index, entry, meta]];
|
|
else
|
|
return [[index, raw, entry, meta, variantHandlers]];
|
|
}
|
|
}
|
|
context.variantHandlers = variantHandlers;
|
|
const { rulesDynamic } = this.config;
|
|
for (const [i, matcher, handler, meta] of rulesDynamic) {
|
|
if (meta?.internal && !internal)
|
|
continue;
|
|
let unprefixed = processed;
|
|
if (meta?.prefix) {
|
|
const prefixes = toArray(meta.prefix);
|
|
if (shortcutPrefix) {
|
|
const shortcutPrefixes = toArray(shortcutPrefix);
|
|
if (!prefixes.some((i2) => shortcutPrefixes.includes(i2)))
|
|
continue;
|
|
} else {
|
|
const prefix = prefixes.find((i2) => processed.startsWith(i2));
|
|
if (prefix == null)
|
|
continue;
|
|
unprefixed = processed.slice(prefix.length);
|
|
}
|
|
}
|
|
const match = unprefixed.match(matcher);
|
|
if (!match)
|
|
continue;
|
|
const result = await handler(match, context);
|
|
if (!result)
|
|
continue;
|
|
if (this.config.details)
|
|
context.rules.push([matcher, handler, meta]);
|
|
const entries = normalizeCSSValues(result).filter((i2) => i2.length);
|
|
if (entries.length) {
|
|
return entries.map((e2) => {
|
|
if (isString(e2))
|
|
return [i, e2, meta];
|
|
else
|
|
return [i, raw, e2, meta, variantHandlers];
|
|
});
|
|
}
|
|
}
|
|
}
|
|
stringifyUtil(parsed, context) {
|
|
if (!parsed)
|
|
return;
|
|
if (isRawUtil(parsed))
|
|
return [parsed[0], void 0, parsed[1], void 0, parsed[2], this.config.details ? context : void 0, void 0];
|
|
const { selector, entries, parent, layer: variantLayer, sort: variantSort, noMerge } = this.applyVariants(parsed);
|
|
const body = entriesToCss(entries);
|
|
if (!body)
|
|
return;
|
|
const { layer: metaLayer, sort: metaSort, ...meta } = parsed[3] ?? {};
|
|
const ruleMeta = {
|
|
...meta,
|
|
layer: variantLayer ?? metaLayer,
|
|
sort: variantSort ?? metaSort
|
|
};
|
|
return [parsed[0], selector, body, parent, ruleMeta, this.config.details ? context : void 0, noMerge];
|
|
}
|
|
async expandShortcut(input, context, depth = 5) {
|
|
if (depth === 0)
|
|
return;
|
|
const recordShortcut = this.config.details ? (s) => {
|
|
context.shortcuts = context.shortcuts ?? [];
|
|
context.shortcuts.push(s);
|
|
} : noop;
|
|
let meta;
|
|
let result;
|
|
for (const s of this.config.shortcuts) {
|
|
let unprefixed = input;
|
|
if (s[2]?.prefix) {
|
|
const prefixes = toArray(s[2].prefix);
|
|
const prefix = prefixes.find((i) => input.startsWith(i));
|
|
if (prefix == null)
|
|
continue;
|
|
unprefixed = input.slice(prefix.length);
|
|
}
|
|
if (isStaticShortcut(s)) {
|
|
if (s[0] === unprefixed) {
|
|
meta = meta || s[2];
|
|
result = s[1];
|
|
recordShortcut(s);
|
|
break;
|
|
}
|
|
} else {
|
|
const match = unprefixed.match(s[0]);
|
|
if (match)
|
|
result = s[1](match, context);
|
|
if (result) {
|
|
meta = meta || s[2];
|
|
recordShortcut(s);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (isString(result))
|
|
result = expandVariantGroup(result.trim()).split(/\s+/g);
|
|
if (!result) {
|
|
const [raw, inputWithoutVariant] = isString(input) ? await this.matchVariants(input) : input;
|
|
if (raw !== inputWithoutVariant) {
|
|
const expanded = await this.expandShortcut(inputWithoutVariant, context, depth - 1);
|
|
if (expanded)
|
|
result = expanded[0].map((item) => isString(item) ? raw.replace(inputWithoutVariant, item) : item);
|
|
}
|
|
}
|
|
if (!result)
|
|
return;
|
|
return [
|
|
(await Promise.all(result.map(async (r) => (isString(r) ? (await this.expandShortcut(r, context, depth - 1))?.[0] : void 0) || [r]))).flat(1).filter(Boolean),
|
|
meta
|
|
];
|
|
}
|
|
async stringifyShortcuts(parent, context, expanded, meta = { layer: this.config.shortcutsLayer }) {
|
|
const selectorMap = new TwoKeyMap();
|
|
const parsed = (await Promise.all(uniq(expanded).map(async (i) => {
|
|
const result = isString(i) ? await this.parseUtil(i, context, true, meta.prefix) : [[Number.POSITIVE_INFINITY, "{inline}", normalizeCSSEntries(i), void 0, []]];
|
|
if (!result && this.config.warn)
|
|
warnOnce(`unmatched utility "${i}" in shortcut "${parent[1]}"`);
|
|
return result || [];
|
|
}))).flat(1).filter(Boolean).sort((a, b) => a[0] - b[0]);
|
|
const [raw, , parentVariants] = parent;
|
|
const rawStringifiedUtil = [];
|
|
for (const item of parsed) {
|
|
if (isRawUtil(item)) {
|
|
rawStringifiedUtil.push([item[0], void 0, item[1], void 0, item[2], context, void 0]);
|
|
continue;
|
|
}
|
|
const { selector, entries, parent: parent2, sort, noMerge } = this.applyVariants(item, [...item[4], ...parentVariants], raw);
|
|
const mapItem = selectorMap.getFallback(selector, parent2, [[], item[0]]);
|
|
mapItem[0].push([entries, !!(noMerge ?? item[3]?.noMerge), sort ?? 0]);
|
|
}
|
|
return rawStringifiedUtil.concat(selectorMap.map(([e2, index], selector, joinedParents) => {
|
|
const stringify = (flatten, noMerge, entrySortPair) => {
|
|
const maxSort = Math.max(...entrySortPair.map((e3) => e3[1]));
|
|
const entriesList = entrySortPair.map((e3) => e3[0]);
|
|
return (flatten ? [entriesList.flat(1)] : entriesList).map((entries) => {
|
|
const body = entriesToCss(entries);
|
|
if (body)
|
|
return [index, selector, body, joinedParents, { ...meta, noMerge, sort: maxSort }, context, void 0];
|
|
return void 0;
|
|
});
|
|
};
|
|
const merges = [
|
|
[e2.filter(([, noMerge]) => noMerge).map(([entries, , sort]) => [entries, sort]), true],
|
|
[e2.filter(([, noMerge]) => !noMerge).map(([entries, , sort]) => [entries, sort]), false]
|
|
];
|
|
return merges.map(([e3, noMerge]) => [
|
|
...stringify(false, noMerge, e3.filter(([entries]) => entries.some((entry) => entry[0] === CONTROL_SHORTCUT_NO_MERGE))),
|
|
...stringify(true, noMerge, e3.filter(([entries]) => entries.every((entry) => entry[0] !== CONTROL_SHORTCUT_NO_MERGE)))
|
|
]);
|
|
}).flat(2).filter(Boolean));
|
|
}
|
|
isBlocked(raw) {
|
|
return !raw || this.config.blocklist.some((e2) => typeof e2 === "function" ? e2(raw) : isString(e2) ? e2 === raw : e2.test(raw));
|
|
}
|
|
}
|
|
function createGenerator(config, defaults) {
|
|
return new UnoGenerator(config, defaults);
|
|
}
|
|
const regexScopePlaceholder = /\s\$\$\s+/g;
|
|
function hasScopePlaceholder(css) {
|
|
return regexScopePlaceholder.test(css);
|
|
}
|
|
function applyScope(css, scope) {
|
|
if (hasScopePlaceholder(css))
|
|
return css.replace(regexScopePlaceholder, scope ? ` ${scope} ` : " ");
|
|
else
|
|
return scope ? `${scope} ${css}` : css;
|
|
}
|
|
const attributifyRe = /^\[(.+?)(~?=)"(.*)"\]$/;
|
|
function toEscapedSelector(raw) {
|
|
if (attributifyRe.test(raw))
|
|
return raw.replace(attributifyRe, (_, n, s, i) => `[${e(n)}${s}"${e(i)}"]`);
|
|
return `.${e(raw)}`;
|
|
}
|
|
function defaultVariantHandler(input, next) {
|
|
return next(input);
|
|
}
|
|
|
|
exports.BetterMap = BetterMap;
|
|
exports.CONTROL_SHORTCUT_NO_MERGE = CONTROL_SHORTCUT_NO_MERGE;
|
|
exports.CountableSet = CountableSet;
|
|
exports.TwoKeyMap = TwoKeyMap;
|
|
exports.UnoGenerator = UnoGenerator;
|
|
exports.attributifyRE = attributifyRE;
|
|
exports.clearIdenticalEntries = clearIdenticalEntries;
|
|
exports.clone = clone;
|
|
exports.collapseVariantGroup = collapseVariantGroup;
|
|
exports.createGenerator = createGenerator;
|
|
exports.cssIdRE = cssIdRE;
|
|
exports.defaultSplitRE = defaultSplitRE;
|
|
exports.definePreset = definePreset;
|
|
exports.e = e;
|
|
exports.entriesToCss = entriesToCss;
|
|
exports.escapeRegExp = escapeRegExp;
|
|
exports.escapeSelector = escapeSelector;
|
|
exports.expandVariantGroup = expandVariantGroup;
|
|
exports.extractorDefault = extractorSplit;
|
|
exports.extractorSplit = extractorSplit;
|
|
exports.hasScopePlaceholder = hasScopePlaceholder;
|
|
exports.isAttributifySelector = isAttributifySelector;
|
|
exports.isCountableSet = isCountableSet;
|
|
exports.isObject = isObject;
|
|
exports.isRawUtil = isRawUtil;
|
|
exports.isStaticRule = isStaticRule;
|
|
exports.isStaticShortcut = isStaticShortcut;
|
|
exports.isString = isString;
|
|
exports.isValidSelector = isValidSelector;
|
|
exports.makeRegexClassGroup = makeRegexClassGroup;
|
|
exports.mergeConfigs = mergeConfigs;
|
|
exports.mergeDeep = mergeDeep;
|
|
exports.noop = noop;
|
|
exports.normalizeCSSEntries = normalizeCSSEntries;
|
|
exports.normalizeCSSValues = normalizeCSSValues;
|
|
exports.normalizeVariant = normalizeVariant;
|
|
exports.notNull = notNull;
|
|
exports.parseVariantGroup = parseVariantGroup;
|
|
exports.regexScopePlaceholder = regexScopePlaceholder;
|
|
exports.resolveConfig = resolveConfig;
|
|
exports.resolvePreset = resolvePreset;
|
|
exports.resolvePresets = resolvePresets;
|
|
exports.resolveShortcuts = resolveShortcuts;
|
|
exports.splitWithVariantGroupRE = splitWithVariantGroupRE;
|
|
exports.toArray = toArray;
|
|
exports.toEscapedSelector = toEscapedSelector;
|
|
exports.uniq = uniq;
|
|
exports.uniqueBy = uniqueBy;
|
|
exports.validateFilterRE = validateFilterRE;
|
|
exports.warnOnce = warnOnce;
|
|
exports.withLayer = withLayer;
|