2023-03-20 14:54:55 +08:00

217 lines
4.9 KiB
TypeScript

/**
* just for legao
* @author: jiushen
*/
// 需要通过 Env 来判断是否需要
import { some } from 'lodash';
const pseudoMap = ['hover', 'focus', 'active', 'visited'];
const RE_CAMEL = /[A-Z]/g;
const RE_HYPHEN = /[-\s]+(.)?/g;
const PROPS_REG = /([^:]*):\s?(.*)/i;
// 给 css 分组
function groupingCss(css: string) {
let stackLength = 0;
let startIndex = 0;
const group: string[] = [];
css.split('').forEach((char, index) => {
if (char === '{') {
stackLength++;
}
if (char === '}') {
if (stackLength === 1) {
group.push(css.substring(startIndex, index + 1));
startIndex = index + 1;
}
stackLength--;
}
});
return group;
}
function isString(str: any): str is string {
return {}.toString.call(str) === '[object String]';
}
function hyphenate(str: string): string {
return str.replace(RE_CAMEL, w => `-${w}`).toLowerCase();
}
function camelize(str: string): string {
return str.replace(RE_HYPHEN, (m, w) => (w ? w.toUpperCase() : ''));
}
/**
* convert
* {background-color: "red"}
* to
* background-color: red;
*/
function runtimeToCss(runtime: Record<string, string>) {
const css: string[] = [];
Object.keys(runtime).forEach((key) => {
css.push(` ${key}: ${runtime[key]};`);
});
return css.join('\n');
}
function toNativeStyle(runtime: Record<string, string> | undefined) {
if (!runtime) {
return {};
}
if (runtime.default) {
const normalized: Record<string, string> = {};
Object.keys(runtime).forEach((pseudo) => {
if (pseudo === 'extra') {
normalized[pseudo] = runtime[pseudo];
return;
}
normalized[pseudo] = toNativeStyle(runtime[pseudo]);
});
return normalized;
}
const normalized = {};
Object.keys(runtime).forEach((key) => {
normalized[camelize(key)] = runtime[key];
});
return normalized;
}
function normalizeStyle(style) {
if (!style) {
return {};
}
if (style.default) {
const normalized = {};
Object.keys(style).forEach((pseudo) => {
if (pseudo === 'extra') {
normalized[pseudo] = style[pseudo];
return;
}
normalized[pseudo] = normalizeStyle(style[pseudo]);
});
return normalized;
}
const normalized: Record<string, string | Record<string, string>> = {};
Object.keys(style).forEach((key) => {
normalized[hyphenate(key)] = style[key];
});
return normalized;
}
function toCss(runtime) {
if (!runtime) {
return (
`:root {
}`);
}
if (runtime.default) {
const css: string[] = [];
Object.keys(runtime).forEach((pseudo) => {
if (pseudo === 'extra') {
Array.isArray(runtime.extra) && css.push(runtime.extra.join('\n'));
return;
}
// 只需要对这四种做兼容
const prefix = pseudoMap.indexOf(pseudo) > -1 ? ':' : '';
css.push(
`:root${pseudo === 'default' ? '' : `${prefix}${pseudo}`} {
${runtimeToCss(normalizeStyle(runtime[pseudo]))}
}\n`,
);
});
return css.join('\n');
}
return (
`:root {
${runtimeToCss(normalizeStyle(runtime))}
}
`
);
}
function cssToRuntime(css: string) {
if (!css) {
return {};
}
const runtime: {
extra?: string[];
default?: Record<string, string>;
} = {};
const groups = groupingCss(css);
groups.forEach((cssItem) => {
if (!cssItem.startsWith(':root')) {
runtime.extra = runtime.extra || [];
runtime.extra.push(cssItem.trim());
} else {
const res = /:root:?(.*)?{(.*)/ig.exec(cssItem.replace(/[\r\n]+/ig, '').trim());
if (res) {
let pseudo: string | undefined;
if (res[1] && res[1].trim() && some(pseudoMap, pse => res[1].indexOf(pse) === 0)) {
pseudo = res[1].trim();
} else if (res[1] && res[1].trim()) {
pseudo = res[1];
}
const s: Record<string, string> = {};
res[2].split(';').reduce<string[]>((prev, next) => {
if (next.indexOf('base64') > -1) {
prev[prev.length - 1] += `;${next}`;
} else {
prev.push(next);
}
return prev;
}, []).forEach((item) => {
if (item) {
if (PROPS_REG.test(item)) {
const props = item.match(PROPS_REG);
const key = props?.[1];
const value = props?.[2];
if (key && value) {
s[key.trim()] = value.trim();
}
}
}
});
runtime[pseudo || 'default'] = s;
}
}
});
return runtime;
}
function cssToStyle(css) {
try {
if (isString(css)) {
return toNativeStyle(cssToRuntime(css).default);
}
if (css.default) {
return toNativeStyle(normalizeStyle(css.default));
}
return toNativeStyle(normalizeStyle(css));
} catch (e) {
// do nothing
}
return {};
}
export {
hyphenate,
camelize,
toNativeStyle,
normalizeStyle,
toCss,
cssToRuntime,
cssToStyle,
};