@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
824 lines (819 loc) • 36 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.componentToBuilder = exports.blockToBuilder = exports.builderBlockPrefixes = void 0;
const media_sizes_1 = require("../../constants/media-sizes");
const dedent_1 = require("../../helpers/dedent");
const fast_clone_1 = require("../../helpers/fast-clone");
const filter_empty_text_nodes_1 = require("../../helpers/filter-empty-text-nodes");
const get_state_object_string_1 = require("../../helpers/get-state-object-string");
const has_props_1 = require("../../helpers/has-props");
const is_component_1 = require("../../helpers/is-component");
const is_mitosis_node_1 = require("../../helpers/is-mitosis-node");
const is_upper_case_1 = require("../../helpers/is-upper-case");
const parsers_1 = require("../../helpers/parsers");
const remove_surrounding_block_1 = require("../../helpers/remove-surrounding-block");
const replace_identifiers_1 = require("../../helpers/replace-identifiers");
const state_1 = require("../../helpers/state");
const builder_1 = require("../../parsers/builder");
const symbol_processor_1 = require("../../symbols/symbol-processor");
const mitosis_node_1 = require("../../types/mitosis-node");
const core_1 = require("@babel/core");
const generator_1 = __importDefault(require("@babel/generator"));
const parser_1 = require("@babel/parser");
const json5_1 = __importDefault(require("json5"));
const lodash_1 = require("lodash");
const legacy_1 = __importDefault(require("neotraverse/legacy"));
const standalone_1 = require("prettier/standalone");
const on_mount_1 = require("../helpers/on-mount");
const isValidCollection = (code) => {
if (!code || typeof code !== 'string') {
return false;
}
// Pattern: Array.from({ length: number })
// Examples: "Array.from({ length: 10 })", "Array.from({ length: 5 })"
const arrayFromPattern = /^Array\.from\(\{\s*length\s*:\s*\d+\s*\}\)$/;
if (arrayFromPattern.test(code)) {
return false;
}
// Pattern: alphanumeric strings separated by dots
// Examples: "abc.def", "state.list1", "data.items"
const dotPattern = /^[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*$/;
return dotPattern.test(code);
};
const replaceWithStateVariable = (code, stateMap) => {
if (!code) {
return '';
}
if (stateMap === null || stateMap === void 0 ? void 0 : stateMap.has(code)) {
return 'state.' + (stateMap.get(code) || '');
}
return code;
};
const generateUniqueKey = (state) => {
let newKeyPrefix = 'dataBuilderList';
let counter = 1;
while (state[newKeyPrefix + counter]) {
counter++;
}
return newKeyPrefix + counter;
};
const convertMitosisStateToBuilderState = (state) => {
return Object.entries(state).reduce((acc, [key, value]) => {
if ((value === null || value === void 0 ? void 0 : value.type) === 'property' && (value === null || value === void 0 ? void 0 : value.code)) {
if (value.code === 'true' || value.code === 'false') {
acc[key] = value.code === 'true';
}
else if (value.code === 'null') {
acc[key] = null;
}
else if (value.code === 'undefined') {
acc[key] = undefined;
}
else if (!Number.isNaN(Number(value.code))) {
acc[key] = Number(value.code);
}
else {
try {
acc[key] = JSON.parse(value.code);
}
catch (e) {
acc[key] = value.code;
}
}
}
return acc;
}, {});
};
const findStateWithinMitosisNode = (node, options, state, stateMap) => {
var _a, _b, _c;
if ((0, mitosis_node_1.checkIsForNode)(node)) {
if (!isValidCollection((_a = node.bindings.each) === null || _a === void 0 ? void 0 : _a.code) &&
!stateMap.has((_b = node.bindings.each) === null || _b === void 0 ? void 0 : _b.code)) {
const newKey = generateUniqueKey(state);
const code = (_c = node.bindings.each) === null || _c === void 0 ? void 0 : _c.code;
try {
state[newKey] = JSON.parse(code);
stateMap.set(code, newKey);
}
catch (parseError) {
// The parsing error happens when `code` is a function or expression
// We would need `eval` to parse the code and then set the state. But because
// of security concerns we are not handling this case right now.
// Will revisit this if we need to support this.
console.log('Failed to parse:', code, parseError);
}
}
}
node.children.forEach((child) => findStateWithinMitosisNode(child, options, state, stateMap));
};
const findStateWithinMitosisComponent = (component, options, state, stateMap) => {
component.children.forEach((child) => findStateWithinMitosisNode(child, options, state, stateMap));
return state;
};
const omitMetaProperties = (obj) => (0, lodash_1.omitBy)(obj, (_value, key) => key.startsWith('$'));
exports.builderBlockPrefixes = ['Amp', 'Core', 'Builder', 'Raw', 'Form'];
const mapComponentName = (name, properties) => {
if (properties === null || properties === void 0 ? void 0 : properties['data-builder-originalName']) {
return properties['data-builder-originalName'];
}
if (name === 'CustomCode') {
return 'Custom Code';
}
for (const prefix of exports.builderBlockPrefixes) {
if (name.startsWith(prefix)) {
const suffix = name.replace(prefix, '');
const restOfName = suffix[0];
if (restOfName && (0, is_upper_case_1.isUpperCase)(restOfName)) {
return `${prefix}:${name.replace(prefix, '')}`;
}
}
}
return name;
};
const componentMappers = {
// TODO: add back if this direction (blocks as children not prop) is desired
...(!builder_1.symbolBlocksAsChildren
? {}
: {
Symbol(node, options) {
const child = node.children[0];
const symbolOptions = (node.bindings.symbol && json5_1.default.parse(node.bindings.symbol.code)) || {};
if (child) {
(0, lodash_1.set)(symbolOptions, 'content.data.blocks', child.children.map((item) => (0, exports.blockToBuilder)(item, options)));
}
return el({
component: {
name: 'Symbol',
options: {
// TODO: forward other symbol options
symbol: symbolOptions,
},
},
}, options);
},
}),
Columns(node, options) {
const block = (0, exports.blockToBuilder)(node, options, { skipMapper: true });
const columns = block.children.map((item) => {
var _a, _b;
return ({
blocks: item.children,
width: (_b = (_a = item.component) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.width,
});
});
block.component.options.columns = columns;
block.children = [];
return block;
},
Fragment(node, options) {
const block = (0, exports.blockToBuilder)(node, options, { skipMapper: true });
block.component = { name: 'Core:Fragment' };
block.tagName = undefined;
return block;
},
PersonalizationContainer(node, options) {
const block = (0, exports.blockToBuilder)(node, options, { skipMapper: true });
const variants = [];
let defaultVariant = [];
const validFakeNodeNames = [
'Variant',
'PersonalizationOption',
'PersonalizationVariant',
'Personalization',
];
block.children.forEach((item) => {
var _a;
if (item.component && validFakeNodeNames.includes((_a = item.component) === null || _a === void 0 ? void 0 : _a.name)) {
let query;
if (item.component.options.query) {
const optionsQuery = item.component.options.query;
if (Array.isArray(optionsQuery)) {
query = optionsQuery.map((q) => ({
'@type': '@builder.io/core:Query',
...q,
}));
}
else {
query = [
{
'@type': '@builder.io/core:Query',
...optionsQuery,
},
];
}
const newVariant = {
...item.component.options,
query,
blocks: item.children,
};
variants.push(newVariant);
}
else if (item.children) {
defaultVariant.push(...item.children);
}
}
else {
defaultVariant.push(item);
}
});
delete block.properties;
delete block.bindings;
block.component.options.variants = variants;
block.children = defaultVariant;
return block;
},
For(_node, options) {
var _a, _b, _c;
const node = _node;
const stateMap = options.stateMap;
const replaceIndexNode = (str) => (0, replace_identifiers_1.replaceNodes)({
code: str,
nodeMaps: [
{
from: core_1.types.identifier(target),
to: core_1.types.memberExpression(core_1.types.identifier('state'), core_1.types.identifier('$index')),
},
],
});
// rename `index` var to `state.$index`
const target = node.scope.indexName || 'index';
const replaceIndex = (node) => {
(0, legacy_1.default)(node).forEach(function (thing) {
if (!(0, is_mitosis_node_1.isMitosisNode)(thing))
return;
for (const [key, value] of Object.entries(thing.bindings)) {
if (!value)
continue;
if (!value.code.includes(target))
continue;
if (value.type === 'single' && value.bindingType === 'function') {
try {
const code = value.code;
const programNode = (0, parsers_1.parseCodeToAst)(code);
if (!programNode)
continue;
(0, core_1.traverse)(programNode, {
Program(path) {
if (path.scope.hasBinding(target))
return;
const x = {
id: core_1.types.identifier(target),
init: core_1.types.identifier('PLACEHOLDER'),
};
path.scope.push(x);
path.scope.rename(target, 'state.$index');
path.traverse({
VariableDeclaration(p) {
if (p.node.declarations.length === 1 && p.node.declarations[0].id === x.id) {
p.remove();
}
},
});
},
});
thing.bindings[key].code = (0, generator_1.default)(programNode).code;
}
catch (error) {
console.error('Error processing function binding. Falling back to simple replacement.', error);
thing.bindings[key].code = replaceIndexNode(value.code);
}
}
else {
thing.bindings[key].code = replaceIndexNode(value.code);
}
}
});
return node;
};
return el({
component: {
name: 'Core:Fragment',
},
repeat: {
collection: isValidCollection((_a = node.bindings.each) === null || _a === void 0 ? void 0 : _a.code)
? ((_b = node.bindings.each) === null || _b === void 0 ? void 0 : _b.code) || ''
: replaceWithStateVariable((_c = node.bindings.each) === null || _c === void 0 ? void 0 : _c.code, stateMap),
itemName: node.scope.forName,
},
children: node.children
.filter(filter_empty_text_nodes_1.filterEmptyTextNodes)
.map((node) => (0, exports.blockToBuilder)(replaceIndex(node), options)),
}, options);
},
Show(node, options) {
var _a, _b, _c;
const elseCase = node.meta.else;
const children = node.children.filter(filter_empty_text_nodes_1.filterEmptyTextNodes);
const showNode = children.length > 0
? el({
// TODO: the reverse mapping for this
component: {
name: 'Core:Fragment',
},
bindings: {
show: (_a = node.bindings.when) === null || _a === void 0 ? void 0 : _a.code,
},
children: children.map((node) => (0, exports.blockToBuilder)(node, options)),
}, options)
: undefined;
const elseNode = elseCase && (0, filter_empty_text_nodes_1.filterEmptyTextNodes)(elseCase)
? el({
// TODO: the reverse mapping for this
component: {
name: 'Core:Fragment',
},
bindings: {
hide: (_b = node.bindings.when) === null || _b === void 0 ? void 0 : _b.code,
},
children: [(0, exports.blockToBuilder)(elseCase, options)],
}, options)
: undefined;
if (elseNode && showNode) {
return el({
component: {
name: 'Core:Fragment',
},
children: [showNode, elseNode],
}, options);
}
else if (showNode) {
return showNode;
}
else if (elseNode) {
return elseNode;
}
return el({
// TODO: the reverse mapping for this
component: {
name: 'Core:Fragment',
},
bindings: {
show: (_c = node.bindings.when) === null || _c === void 0 ? void 0 : _c.code,
},
children: [],
}, options);
},
};
const el = (options, toBuilderOptions) => ({
'@type': '@builder.io/sdk:Element',
...(toBuilderOptions.includeIds && {
id: 'builder-' + (0, symbol_processor_1.hashCodeAsString)(options),
}),
...options,
});
function tryFormat(code) {
let str = code;
try {
str = (0, standalone_1.format)(str, {
parser: 'babel',
plugins: [
require('prettier/parser-babel'), // To support running in browsers
],
});
}
catch (err) {
console.error('Format error for code:', str);
throw err;
}
return str;
}
const processLocalizedValues = (element, node) => {
if (node.localizedValues) {
for (const [path, value] of Object.entries(node.localizedValues)) {
(0, lodash_1.set)(element, path, value);
}
}
return element;
};
/**
* Turns a stringified object into an object that can be looped over.
* Since values in the stringified object could be JS expressions, all
* values in the resulting object will remain strings.
* @param input - The stringified object
*/
const parseJSObject = (input) => {
var _a;
const unparsed = [];
let parsed = {};
try {
const ast = (0, parser_1.parseExpression)(`(${input})`, {
plugins: ['jsx', 'typescript'],
sourceType: 'module',
});
if (ast.type !== 'ObjectExpression') {
return { parsed, unparsed: input };
}
for (const prop of ast.properties) {
/**
* If the object includes spread or method, we stop. We can't really break the component into Key/Value
* and the whole expression is considered dynamic. We return `false` to signify that.
*/
if (prop.type === 'ObjectMethod' || prop.type === 'SpreadElement') {
if (!!prop.start && !!prop.end) {
if (typeof input === 'string') {
unparsed.push(input.slice(prop.start - 1, prop.end - 1));
}
}
continue;
}
/**
* Ignore shorthand objects when processing incomplete objects. Otherwise we may
* create identifiers unintentionally.
* Example: When accounting for shorthand objects, "{ color" would become
* { color: color } thus creating a "color" identifier that does not exist.
*/
if (prop.type === 'ObjectProperty') {
if ((_a = prop.extra) === null || _a === void 0 ? void 0 : _a.shorthand) {
if (typeof input === 'string') {
unparsed.push(input.slice(prop.start - 1, prop.end - 1));
}
continue;
}
let key = '';
if (prop.key.type === 'Identifier') {
key = prop.key.name;
}
else if (prop.key.type === 'StringLiteral') {
key = prop.key.value;
}
else {
continue;
}
if (typeof input === 'string') {
const [val, err] = extractValue(input, prop.value);
if (err === null) {
parsed[key] = val;
}
}
}
}
return {
parsed,
unparsed: unparsed.length > 0 ? `{${unparsed.join('\n')}}` : undefined,
};
}
catch (err) {
return {
parsed,
unparsed: unparsed.length > 0 ? `{${unparsed.join('\n')}}` : undefined,
};
}
};
const extractValue = (input, node) => {
var _a, _b;
const start = (_a = node === null || node === void 0 ? void 0 : node.loc) === null || _a === void 0 ? void 0 : _a.start;
const end = (_b = node === null || node === void 0 ? void 0 : node.loc) === null || _b === void 0 ? void 0 : _b.end;
const startIndex = start !== undefined && 'index' in start && typeof start['index'] === 'number'
? start['index']
: undefined;
const endIndex = end !== undefined && 'index' in end && typeof end['index'] === 'number'
? end['index']
: undefined;
if (startIndex === undefined || endIndex === undefined || node === null) {
const err = `bad value: ${node}`;
return [null, err];
}
const value = input.slice(startIndex - 1, endIndex - 1);
return [value, null];
};
/**
* Maps and styles that are bound with dynamic values onto their respective
* binding keys for Builder elements. This function also maps media queries
* with dynamic values.
* @param - bindings - The bindings object that has your styles. This param
* will be modified in-place, and the old "style" key will be removed.
*/
const mapBoundStyles = (bindings) => {
const styles = bindings['style'];
if (!styles) {
return;
}
const { parsed, unparsed } = parseJSObject(styles.code);
for (const key in parsed) {
const mediaQueryMatch = key.match(media_sizes_1.mediaQueryRegex);
if (mediaQueryMatch) {
const { parsed: mParsed } = parseJSObject(parsed[key]);
const [_, pixelSize] = mediaQueryMatch;
const size = media_sizes_1.sizes.getSizeForWidth(Number(pixelSize));
for (const mKey in mParsed) {
bindings[`responsiveStyles.${size}.${mKey}`] = {
code: mParsed[mKey],
bindingType: 'expression',
type: 'single',
};
}
}
else {
if (isGlobalStyle(key)) {
console.warn(`The following bound styles are not supported by Builder JSON and have been removed:
"${key}": ${parsed[key]}
`);
}
else {
bindings[`style.${key}`] = {
code: parsed[key],
bindingType: 'expression',
type: 'single',
};
}
}
}
delete bindings['style'];
// unparsed data could be something else such as a function call
if (unparsed) {
try {
const ast = (0, parser_1.parseExpression)(`(${unparsed})`, {
plugins: ['jsx', 'typescript'],
sourceType: 'module',
});
// style={state.getStyles()}
if (ast.type === 'CallExpression') {
bindings['style'] = {
code: unparsed,
bindingType: 'expression',
type: 'single',
};
}
else {
throw 'unsupported style';
}
}
catch (_a) {
console.warn(`The following bound styles are invalid and have been removed: ${unparsed}`);
}
}
};
function isGlobalStyle(key) {
// These are mapped to their respective responsiveStyle and support bindings
if (/max-width: (.*?)px/gm.exec(key)) {
return false;
}
return (
// pseudo class
key.startsWith('&:') ||
key.startsWith(':') ||
// @ rules
key.startsWith('@'));
}
const blockToBuilder = (json, options = {}, _internalOptions = {}) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
const mapper = !_internalOptions.skipMapper && componentMappers[json.name];
if (mapper) {
const element = mapper(json, options);
return processLocalizedValues(element, json);
}
if (json.properties._text || ((_a = json.bindings._text) === null || _a === void 0 ? void 0 : _a.code)) {
const element = el({
tagName: 'span',
bindings: {
...(((_b = json.bindings._text) === null || _b === void 0 ? void 0 : _b.code)
? {
'component.options.text': json.bindings._text.code,
'json.bindings._text.code': undefined,
}
: {}),
},
component: {
name: 'Text',
options: {
// Mitosis uses {} for bindings, but Builder expects {{}} so we need to convert
text: (_c = json.properties._text) === null || _c === void 0 ? void 0 : _c.replace(/\{(.*?)\}/g, '{{$1}}'),
},
},
}, options);
return processLocalizedValues(element, json);
}
const thisIsComponent = (0, is_component_1.isComponent)(json);
let bindings = json.bindings;
const actions = {};
for (const key in bindings) {
const eventBindingKeyRegex = /^on([A-Z])/;
const firstCharMatchForEventBindingKey = (_d = key.match(eventBindingKeyRegex)) === null || _d === void 0 ? void 0 : _d[1];
if (firstCharMatchForEventBindingKey) {
let actionBody = ((_e = bindings[key]) === null || _e === void 0 ? void 0 : _e.async)
? `(async () => ${(_f = bindings[key]) === null || _f === void 0 ? void 0 : _f.code})()`
: (0, remove_surrounding_block_1.removeSurroundingBlock)((_g = bindings[key]) === null || _g === void 0 ? void 0 : _g.code);
const eventIdentifier = (_j = (_h = bindings[key]) === null || _h === void 0 ? void 0 : _h.arguments) === null || _j === void 0 ? void 0 : _j[0];
if (typeof eventIdentifier === 'string' && eventIdentifier !== 'event') {
actionBody = (0, replace_identifiers_1.replaceNodes)({
code: actionBody,
nodeMaps: [{ from: core_1.types.identifier(eventIdentifier), to: core_1.types.identifier('event') }],
});
}
actions[key.replace(eventBindingKeyRegex, firstCharMatchForEventBindingKey.toLowerCase())] =
actionBody;
delete bindings[key];
}
if (key === 'style') {
mapBoundStyles(bindings);
}
}
const builderBindings = {};
const componentOptions = omitMetaProperties(json.properties);
if (thisIsComponent) {
for (const key in bindings) {
if (key === 'css') {
continue;
}
const value = bindings[key];
const parsed = (0, lodash_1.attempt)(() => json5_1.default.parse(value === null || value === void 0 ? void 0 : value.code));
if (!(parsed instanceof Error)) {
componentOptions[key] = parsed;
}
else {
if (!((_k = json.slots) === null || _k === void 0 ? void 0 : _k[key])) {
builderBindings[`component.options.${key}`] = bindings[key].code;
}
}
}
}
for (const key in json.slots) {
componentOptions[key] = json.slots[key].map((node) => (0, exports.blockToBuilder)(node, options));
}
for (const key in json.blocksSlots) {
const value = json.blocksSlots[key];
(0, legacy_1.default)(value).forEach(function (v) {
if ((0, is_mitosis_node_1.isMitosisNode)(v)) {
this.update((0, exports.blockToBuilder)(v, options, _internalOptions));
}
});
componentOptions[key] = value;
}
const hasCss = !!((_l = bindings.css) === null || _l === void 0 ? void 0 : _l.code);
let responsiveStyles = {
large: {},
};
if (hasCss) {
const cssRules = json5_1.default.parse((_m = bindings.css) === null || _m === void 0 ? void 0 : _m.code);
const cssRuleKeys = Object.keys(cssRules);
for (const ruleKey of cssRuleKeys) {
const mediaQueryMatch = ruleKey.match(media_sizes_1.mediaQueryRegex);
if (mediaQueryMatch) {
const [fullmatch, pixelSize] = mediaQueryMatch;
const sizeForWidth = media_sizes_1.sizes.getSizeForWidth(Number(pixelSize));
const currentSizeStyles = responsiveStyles[sizeForWidth] || {};
responsiveStyles[sizeForWidth] = {
...currentSizeStyles,
...cssRules[ruleKey],
};
}
else {
responsiveStyles.large = {
...responsiveStyles.large,
[ruleKey]: cssRules[ruleKey],
};
}
}
delete json.bindings.css;
}
const element = el({
tagName: thisIsComponent ? undefined : json.name,
...(hasCss && {
responsiveStyles,
}),
layerName: json.properties.$name,
...(json.properties['data-builder-layerLocked'] !== undefined && {
layerLocked: json.properties['data-builder-layerLocked'] === 'true',
}),
...(json.properties['data-builder-groupLocked'] !== undefined && {
groupLocked: json.properties['data-builder-groupLocked'] === 'true',
}),
...(thisIsComponent && {
component: {
name: mapComponentName(json.name, json.properties),
options: (0, lodash_1.omit)(componentOptions, ['data-builder-originalName']),
},
}),
code: {
bindings: builderBindings,
actions,
},
properties: thisIsComponent ? undefined : omitMetaProperties(json.properties),
bindings: thisIsComponent
? builderBindings
: (0, lodash_1.omit)((0, lodash_1.mapValues)(bindings, (value) => value === null || value === void 0 ? void 0 : value.code), 'css'),
actions,
children: json.children
.filter(filter_empty_text_nodes_1.filterEmptyTextNodes)
.map((child) => (0, exports.blockToBuilder)(child, options)),
}, options);
return processLocalizedValues(element, json);
};
exports.blockToBuilder = blockToBuilder;
const recursivelyCheckForChildrenWithSameComponent = (elementOrContent, componentName, path = '') => {
var _a, _b, _c, _d, _e;
if ((0, builder_1.isBuilderElement)(elementOrContent)) {
if (((_a = elementOrContent.component) === null || _a === void 0 ? void 0 : _a.name) === componentName) {
return path;
}
return (((_b = elementOrContent.children) === null || _b === void 0 ? void 0 : _b.map((child, index) => recursivelyCheckForChildrenWithSameComponent(child, componentName, `${path}.children[${index}]`)).find(Boolean)) || '');
}
if ((_c = elementOrContent.data) === null || _c === void 0 ? void 0 : _c.blocks) {
return (((_e = (_d = elementOrContent.data) === null || _d === void 0 ? void 0 : _d.blocks) === null || _e === void 0 ? void 0 : _e.map((block, index) => recursivelyCheckForChildrenWithSameComponent(block, componentName, `${path ? `${path}.` : ''}data.blocks[${index}]`)).find(Boolean)) || '');
}
return '';
};
function removeItem(obj, path, indexToRemove) {
return (0, lodash_1.set)((0, lodash_1.cloneDeep)(obj), // Clone to ensure immutability
path, (0, lodash_1.filter)((0, lodash_1.get)(obj, path), (item, index) => index !== indexToRemove));
}
const componentToBuilder = (options = {}) => ({ component }) => {
var _a, _b, _c;
const hasState = (0, state_1.checkHasState)(component);
if (!options.stateMap) {
options.stateMap = new Map();
}
const result = (0, fast_clone_1.fastClone)({
data: {
httpRequests: (_b = (_a = component === null || component === void 0 ? void 0 : component.meta) === null || _a === void 0 ? void 0 : _a.useMetadata) === null || _b === void 0 ? void 0 : _b.httpRequests,
jsCode: tryFormat((0, dedent_1.dedent) `
${!(0, has_props_1.hasProps)(component) ? '' : `var props = state;`}
${!hasState ? '' : `Object.assign(state, ${(0, get_state_object_string_1.getStateObjectStringFromComponent)(component)});`}
${(0, on_mount_1.stringifySingleScopeOnMount)(component)}
`),
tsCode: tryFormat((0, dedent_1.dedent) `
${!(0, has_props_1.hasProps)(component) ? '' : `var props = state;`}
${!hasState ? '' : `useStore(${(0, get_state_object_string_1.getStateObjectStringFromComponent)(component)});`}
${!component.hooks.onMount.length
? ''
: `onMount(() => {
${(0, on_mount_1.stringifySingleScopeOnMount)(component)}
})`}
`),
cssCode: component === null || component === void 0 ? void 0 : component.style,
...(() => {
const stateData = findStateWithinMitosisComponent(component, options, { ...convertMitosisStateToBuilderState(component.state) }, options.stateMap);
return { state: stateData };
})(),
blocks: component.children
.filter(filter_empty_text_nodes_1.filterEmptyTextNodes)
.map((child) => (0, exports.blockToBuilder)(child, options)),
},
});
if (((_c = result.data) === null || _c === void 0 ? void 0 : _c.state) && Object.keys(result.data.state).length === 0) {
delete result.data.state;
}
const subComponentMap = {};
for (const subComponent of component.subComponents) {
const name = subComponent.name;
subComponentMap[name] = (0, exports.componentToBuilder)(options)({
component: subComponent,
});
}
(0, legacy_1.default)([result, subComponentMap]).forEach(function (el) {
var _a, _b, _c, _d, _e;
if ((0, builder_1.isBuilderElement)(el) && !((_a = el.meta) === null || _a === void 0 ? void 0 : _a.preventRecursion)) {
const value = subComponentMap[(_b = el.component) === null || _b === void 0 ? void 0 : _b.name];
if (value) {
// Recursive Components are handled in the following steps :
// 1. Find out the path in which the component is self-referenced ( where that component reoccurs within it’s tree ).
// 2. We populate that component recursively for 4 times in a row.
// 3. Finally remove the recursive part from the last component which was populated.
// Also note that it doesn’t mean that component will render that many times, the rendering logic depends on the logic in it's parent. (Eg. show property binding)
const path = recursivelyCheckForChildrenWithSameComponent(value, (_c = el.component) === null || _c === void 0 ? void 0 : _c.name);
if (path) {
let tempElement = el;
for (let i = 0; i < 4; i++) {
const tempValue = (0, lodash_1.cloneDeep)(value);
(0, lodash_1.set)(tempElement, 'component.options.symbol.content', tempValue);
(0, lodash_1.set)(tempElement, 'meta.preventRecursion', true);
tempElement = (0, lodash_1.get)(tempValue, path);
}
// Finally remove the recursive part.
const arrayPath = path.replace(/\[\d+\]$/, '');
const newValue = removeItem(value, arrayPath, Number((_d = path.match(/\[(\d+)\]$/)) === null || _d === void 0 ? void 0 : _d[1]));
(0, lodash_1.set)(tempElement, 'component.options.symbol.content', newValue);
(0, lodash_1.set)(tempElement, 'meta.preventRecursion', true);
}
else {
(0, lodash_1.set)(el, 'component.options.symbol.content', value);
}
}
if (el.bindings) {
for (const [key, value] of Object.entries(el.bindings)) {
if (value.match(/\n|;/)) {
if (!el.code) {
el.code = {};
}
if (!el.code.bindings) {
el.code.bindings = {};
}
el.code.bindings[key] = value;
el.bindings[key] = ` return ${value}`;
}
}
}
}
if ((0, builder_1.isBuilderElement)(el) && ((_e = el.meta) === null || _e === void 0 ? void 0 : _e.preventRecursion)) {
delete el.meta.preventRecursion;
if (el.meta && Object.keys(el.meta).length === 0) {
delete el.meta;
}
}
});
return result;
};
exports.componentToBuilder = componentToBuilder;