@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
114 lines (113 loc) • 5.17 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectCss = exports.normalizeName = void 0;
const legacy_1 = __importDefault(require("neotraverse/legacy"));
const object_hash_1 = __importDefault(require("object-hash"));
const dash_case_1 = require("../dash-case");
const is_mitosis_node_1 = require("../is-mitosis-node");
const helpers_1 = require("./helpers");
const trimClassStr = (classStr) => classStr.trim().replace(/\s{2,}/g, ' ');
const updateClassForNode = (item, className) => {
if (item.bindings.class) {
// combine className with existing binding. We use single quotes because in Vue, bindings are wrapped in double quotes
// e.g. <div :class="_classStringToObject(this.className + ' div-21azgz5avex')" />
item.bindings.class.code = trimClassStr(`${item.bindings.class.code} + ' ${className}'`);
}
else {
item.properties.class = trimClassStr(`${item.properties.class || ''} ${className}`);
}
};
function normalizeName(name) {
if (!name || name.trim() === '' || name.match(/^[^a-zA-Z0-9]*$/)) {
return '';
}
// Clean the name first
const cleaned = name.replace(/[^a-zA-Z0-9\-_]/g, '');
// If pure numeric or only contains numbers and dashes
if (cleaned.match(/^[0-9-]+$/)) {
// Extract just the numbers and format as css{number}
const numbers = cleaned.replace(/[^0-9]/g, '');
return `css${numbers}`;
}
// Remove leading numbers and dashes for other cases
const normalized = cleaned.replace(/^[0-9-]+(?=[a-zA-Z])/, '');
return normalized || '';
}
exports.normalizeName = normalizeName;
const collectStyles = (json, options = {}) => {
const styleMap = {};
const componentIndexes = {};
const componentHashes = {};
(0, legacy_1.default)(json).forEach(function (item) {
var _a;
if ((0, is_mitosis_node_1.isMitosisNode)(item)) {
if ((0, helpers_1.nodeHasCss)(item)) {
const value = (0, helpers_1.parseCssObject)((_a = item.bindings.css) === null || _a === void 0 ? void 0 : _a.code);
delete item.bindings.css;
const normalizedName = normalizeName(item.properties.$name);
const componentName = normalizedName
? (0, dash_case_1.dashCase)(normalizedName)
: /^h\d$/.test(item.name || '') // don't dashcase h1 into h-1
? item.name
: (0, dash_case_1.dashCase)(normalizeName(item.name) || 'div');
const classNameWPrefix = `${componentName}${options.prefix ? `-${options.prefix}` : ''}`;
const stylesHash = (0, object_hash_1.default)(value);
if (componentHashes[componentName] === stylesHash) {
const className = classNameWPrefix;
updateClassForNode(item, className);
return;
}
if (!componentHashes[componentName]) {
componentHashes[componentName] = stylesHash;
}
const index = (componentIndexes[componentName] =
(componentIndexes[componentName] || 0) + 1);
const className = `${classNameWPrefix}${index === 1 ? '' : `-${index}`}`;
updateClassForNode(item, className);
styleMap[className] = value;
}
delete item.bindings.css;
}
});
return styleMap;
};
const collectCss = (json, options = {}) => {
var _a;
const styles = collectStyles(json, options);
// TODO create and use a root selector
let css = '';
css += !!((_a = json.style) === null || _a === void 0 ? void 0 : _a.length) ? `${json.style}\n` : '';
css += classStyleMapToCss(styles);
return css;
};
exports.collectCss = collectCss;
const classStyleMapToCss = (map) => {
let str = '';
for (const key in map) {
const styles = (0, helpers_1.getStylesOnly)(map[key]);
str += `.${key} {\n${(0, helpers_1.styleMapToCss)(styles)}\n}`;
const nestedSelectors = (0, helpers_1.getNestedSelectors)(map[key]);
for (const nestedSelector in nestedSelectors) {
const value = nestedSelectors[nestedSelector];
if (nestedSelector.startsWith('@')) {
str += `${nestedSelector} { .${key} { ${(0, helpers_1.styleMapToCss)(value)} } }`;
}
else {
const getSelector = (nestedSelector) => {
if (nestedSelector.startsWith(':')) {
return `.${key}${nestedSelector}`;
}
if (nestedSelector.includes('&')) {
return nestedSelector.replace(/&/g, `.${key}`);
}
return `.${key} ${nestedSelector}`;
};
str += `${getSelector(nestedSelector)} {\n${(0, helpers_1.styleMapToCss)(value)}\n}`;
}
}
}
return str;
};
;