zss-engine
Version:
Zero-runtime StyleSheet Engine
118 lines (117 loc) • 5.87 kB
JavaScript
import { camelToKebabCase, applyCssValue } from './helper.js';
const createKeyframes = (property, content) => {
let keyframesRules = `${property} {\n`;
for (const key in content) {
if (Object.prototype.hasOwnProperty.call(content, key)) {
const keyframeValue = content[key];
keyframesRules += ` ${key} {\n`;
for (const prop in keyframeValue) {
if (Object.prototype.hasOwnProperty.call(keyframeValue, prop)) {
const CSSProp = camelToKebabCase(prop);
const value = keyframeValue[prop];
if (typeof value === 'string' || typeof value === 'number') {
const applyValue = applyCssValue(value, CSSProp);
keyframesRules += ` ${CSSProp}: ${applyValue};\n`;
}
}
}
keyframesRules += ` }\n`;
}
}
keyframesRules += `}\n`;
return keyframesRules;
};
export function transpile(object, base36Hash, core) {
let styleSheet = '';
const mediaQueries = [];
const classNameApply = (property) => {
return core === '--global' ? property : `.${base36Hash}`;
};
const rules = (indent, rulesValue, property) => {
const value = rulesValue[property];
const cssProp = camelToKebabCase(property);
return `${indent}${cssProp}: ${value};\n`;
};
const stringConverter = (className, properties, indentLevel) => {
const classSelector = {};
const indent = ''.repeat(indentLevel);
const innerIndent = ' '.repeat(indentLevel + 1);
let cssRule = '';
for (const property in properties) {
if (Object.prototype.hasOwnProperty.call(properties, property)) {
const value = properties[property];
if (typeof value === 'string' || typeof value === 'number') {
let CSSProp = camelToKebabCase(property);
const applyValue = applyCssValue(value, CSSProp);
cssRule += ` ${CSSProp}: ${applyValue};\n`;
}
else if (!property.startsWith('@')) {
const kebabPseudoSelector = camelToKebabCase(property.replace('&', ''));
const styles = stringConverter(className + kebabPseudoSelector, value, indentLevel);
Object.assign(classSelector, styles);
}
else if (property.startsWith('@media') || property.startsWith('@container')) {
const mediaRule = property;
let nestedRules = '';
let regularRules = '';
for (const mediaProp in value) {
if (Object.prototype.hasOwnProperty.call(value, mediaProp)) {
const mediaValue = value[mediaProp];
const isColon = mediaProp.startsWith(':');
const isAnd = mediaProp.startsWith('&');
if (isColon || isAnd) {
const kebabMediaProp = camelToKebabCase(mediaProp.replace('&', ''));
let pseudoClassRule = '';
if (typeof mediaValue === 'object' && mediaValue !== null) {
for (const pseudoProp in mediaValue) {
if (Object.prototype.hasOwnProperty.call(mediaValue, pseudoProp)) {
const CSSProp = camelToKebabCase(pseudoProp);
const applyValue = applyCssValue(mediaValue[pseudoProp], CSSProp);
pseudoClassRule += rules(innerIndent + ' ', { [pseudoProp]: applyValue }, pseudoProp);
}
}
}
nestedRules += `${innerIndent}${className}${kebabMediaProp} {\n${pseudoClassRule}${innerIndent}}\n`;
}
else {
const CSSProp = camelToKebabCase(mediaProp);
const applyValue = applyCssValue(mediaValue, CSSProp);
regularRules += rules(innerIndent + ' ', { [mediaProp]: applyValue }, mediaProp);
}
}
}
if (regularRules) {
mediaQueries.push({
media: mediaRule,
css: `${mediaRule} {\n${innerIndent}${className} {\n${regularRules} }\n${nestedRules}${indent}}${indent}\n`,
});
}
else {
mediaQueries.push({
media: mediaRule,
css: `${mediaRule} {\n${nestedRules}${indent}}\n`,
});
}
}
}
}
classSelector[className] = cssRule;
return classSelector;
};
for (const property in object) {
if (property.startsWith('@keyframes')) {
const keyframesContent = object[property];
styleSheet += createKeyframes(property, keyframesContent);
}
const classSelectors = stringConverter(classNameApply(property), object[property], 1);
for (const selector in classSelectors) {
if (!selector.startsWith('@keyframes') && classSelectors[selector]) {
styleSheet += selector + ' {\n' + classSelectors[selector] + '}\n';
}
}
}
mediaQueries.forEach(({ css }) => {
styleSheet += css;
});
return { styleSheet };
}