@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
203 lines (202 loc) • 8.59 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hashCode = exports.hashCodeAsString = exports.getJsxSymbolComponentName = exports.convertBuilderElementToMitosisComponent = exports.convertBuilderContentToSymbolHierarchy = exports.ensureAllSymbolsHaveIds = void 0;
const minify_1 = require("../generators/helpers/minify");
const builder_1 = require("../parsers/builder");
const legacy_1 = __importDefault(require("neotraverse/legacy"));
/**
* Ensure every symbol in a BuilderContent tree has a unique ID.
* Mutates the data tree directly.
*/
function ensureAllSymbolsHaveIds(content) {
let counter = 0;
const ids = new Set();
(0, legacy_1.default)(content).forEach(function (el) {
var _a, _b, _c;
if (this.key === 'jsCode' && isString(el) && el.endsWith('return _virtual_index')) {
// Sometimes rollup adds a final `return _virtual_index` but that causes VM evaluation to fail.
// Instead of a return on the last line, it needs a plain expression on the last line. Luckily
// because the rollup compile behavior is consistent this works pretty reliably
el = el.replace(/return _virtual_index$/, '_virtual_index');
this.parent && (this.parent.node.jsCode = el);
}
if ((0, builder_1.isBuilderElement)(el)) {
if (((_a = el === null || el === void 0 ? void 0 : el.component) === null || _a === void 0 ? void 0 : _a.name) === 'Symbol') {
const id = getIdFromSymbol(el);
if (id) {
if (ids.has(id)) {
if ((_c = (_b = el.component) === null || _b === void 0 ? void 0 : _b.options) === null || _c === void 0 ? void 0 : _c.symbol) {
const id = pad(counter++);
el.component.options.symbol.entry = id;
if (el.component.options.symbol.content) {
el.component.options.symbol.content.id = id;
}
ids.add(id);
}
}
else {
ids.add(id);
}
}
}
}
});
}
exports.ensureAllSymbolsHaveIds = ensureAllSymbolsHaveIds;
//TODO(misko): needs test
function convertBuilderContentToSymbolHierarchy(content, { collectComponentStyles, collectComponentState, } = {}) {
var _a, _b;
if (collectComponentState && ((_a = content.data) === null || _a === void 0 ? void 0 : _a.state)) {
const state = (_b = content.data) === null || _b === void 0 ? void 0 : _b.state;
collectComponentState['ROOT_COMPONENT_STATE'] = state;
}
const path = [-1, content.id];
const hierarchy = {
depthFirstSymbols: [],
[content.id]: [],
};
(0, legacy_1.default)(content).forEach(function (el) {
var _a, _b, _c;
let cssCode = el === null || el === void 0 ? void 0 : el.cssCode;
if (cssCode) {
collectComponentStyles && collectComponentStyles.push((0, minify_1.minify) `${cssCode}`);
}
while (path[0 /* Path.DEPTH */] >= this.path.length) {
path.shift();
path.shift();
}
if ((0, builder_1.isBuilderElement)(el)) {
if (((_a = el === null || el === void 0 ? void 0 : el.component) === null || _a === void 0 ? void 0 : _a.name) === 'Symbol') {
if (collectComponentState) {
const symbol = el.component.options.symbol;
const props = symbol.data || (symbol.data = {});
const state = (_c = (_b = symbol.content) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.state;
if (state) {
const id = hashCodeAsString(state);
props['serverStateId'] = id;
collectComponentState[id] = state;
}
}
if (path[0 /* Path.DEPTH */] < this.path.length) {
const id = getIdFromSymbol(el);
hierarchy[id] = [];
addIfMissing(hierarchy[path[1 /* Path.ID */]], id);
path.unshift(this.path.length, id);
}
// TODO(misko): This should be `el.content` not `el`
// Because we don't wante to take the `<Symbol>` with us.
// TODO(misko): Do we really want to add ALL symbols? Even duplicates?
hierarchy.depthFirstSymbols.unshift(el);
}
}
});
return hierarchy;
}
exports.convertBuilderContentToSymbolHierarchy = convertBuilderContentToSymbolHierarchy;
function convertBuilderElementToMitosisComponent(element) {
var _a, _b;
const symbolValue = (_b = (_a = element.component) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.symbol;
const elContent = symbolValue === null || symbolValue === void 0 ? void 0 : symbolValue.content;
if (!elContent) {
console.warn('Symbol missing content', element.id);
delete element.component; // TODO(misko): Should this be assign `undefined` for perf?
element.children = [];
return null;
}
const id = getIdFromSymbol(element);
const componentName = getJsxSymbolComponentName(id);
delete element.component; // TODO(misko): Should this be assign `undefined` for perf?
element.children = [
(0, builder_1.createBuilderElement)({
component: {
name: componentName,
options: symbolValue.data,
},
properties: {
'builder-content-id': id,
},
}),
];
const mitosisComponent = {
...(0, builder_1.builderContentToMitosisComponent)(elContent, {
includeBuilderExtras: true,
preserveTextBlocks: true,
}),
name: componentName,
};
mitosisComponent.meta.builderElId = element.id;
return mitosisComponent;
}
exports.convertBuilderElementToMitosisComponent = convertBuilderElementToMitosisComponent;
function getJsxSymbolComponentName(id) {
return 'Component' + id.toUpperCase().replace(/-/g, '');
}
exports.getJsxSymbolComponentName = getJsxSymbolComponentName;
function getIdFromSymbol(el) {
var _a, _b, _c;
// TODO(misko): Don't use entry us el.id???
return (_c = (_b = (_a = el.component) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.symbol) === null || _c === void 0 ? void 0 : _c.entry;
}
function addIfMissing(array, value) {
if (array.indexOf(value) == -1) {
array.push(value);
}
}
function isString(value) {
return typeof value == 'string';
}
function hashCodeAsString(obj) {
return Number(Math.abs(hashCode(obj))).toString(36);
}
exports.hashCodeAsString = hashCodeAsString;
function hashCode(obj, hash = 0) {
let value = 0;
switch (typeof obj) {
case 'number':
value = obj;
break;
case 'undefined':
value = Number.MIN_SAFE_INTEGER;
break;
case 'string':
for (let i = 0; i < obj.length; i++) {
hash = hashCodeApply(hash, obj.charCodeAt(i));
}
value = obj.length;
case 'boolean':
value = obj ? 1 : 0;
break;
case 'object':
if (obj === null) {
value = Number.MAX_SAFE_INTEGER;
}
else if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; i++) {
hash = hashCode(obj[i], hash);
}
}
else {
for (const key of Object.keys(obj).sort()) {
if (obj.hasOwnProperty(key)) {
hash = hashCode(obj[key], hash);
}
}
}
break;
}
return hashCodeApply(hash, value);
}
exports.hashCode = hashCode;
function hashCodeApply(hash, value) {
hash = (hash << 5) - hash + value;
hash |= 0; // Convert to 32bit integer
return hash;
}
function pad(value) {
const padding = '000000';
let result = padding + String(value);
return result.substring(result.length - padding.length);
}
;