@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
1,141 lines • 51 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.builderContentToMitosisComponent = exports.isBuilderElement = exports.createBuilderElement = exports.convertExportDefaultToReturn = exports.extractStateHook = exports.getMetaFromBlock = exports.builderElementToMitosisNode = exports.symbolBlocksAsChildren = void 0;
const symbol_processor_1 = require("../../symbols/symbol-processor");
const babel = __importStar(require("@babel/core"));
const generator_1 = __importDefault(require("@babel/generator"));
const json5_1 = __importDefault(require("json5"));
const lodash_1 = require("lodash");
const legacy_1 = __importDefault(require("neotraverse/legacy"));
const media_sizes_1 = require("../../constants/media-sizes");
const bindings_1 = require("../../helpers/bindings");
const capitalize_1 = require("../../helpers/capitalize");
const create_mitosis_component_1 = require("../../helpers/create-mitosis-component");
const create_mitosis_node_1 = require("../../helpers/create-mitosis-node");
const fast_clone_1 = require("../../helpers/fast-clone");
const parsers_1 = require("../../helpers/parsers");
const jsx_1 = require("../jsx");
const state_1 = require("../jsx/state");
const helpers_1 = require("./helpers");
// Omit some superflous styles that can come from Builder's web importer
const styleOmitList = [
'backgroundRepeatX',
'backgroundRepeatY',
'backgroundPositionX',
'backgroundPositionY',
];
const getCssFromBlock = (block) => {
var _a;
const blockSizes = Object.keys(block.responsiveStyles || {}).filter((size) => media_sizes_1.sizeNames.includes(size));
let css = {};
for (const size of blockSizes) {
if (size === 'large') {
css = (0, lodash_1.omit)({
...css,
...(_a = block.responsiveStyles) === null || _a === void 0 ? void 0 : _a.large,
}, styleOmitList);
}
else if (block.responsiveStyles && block.responsiveStyles[size]) {
const mediaQueryKey = `@media (max-width: ${media_sizes_1.sizes[size].max}px)`;
css[mediaQueryKey] = (0, lodash_1.omit)({
...css[mediaQueryKey],
...block.responsiveStyles[size],
}, styleOmitList);
}
}
return css;
};
const verifyIsValid = (code) => {
try {
if (babel.parse(code)) {
return { valid: true, error: null };
}
}
catch (err) {
return { valid: false, error: null };
}
return { valid: false, error: null };
};
const getActionBindingsFromBlock = (block, options) => {
var _a;
const actions = {
...block.actions,
...(_a = block.code) === null || _a === void 0 ? void 0 : _a.actions,
};
const bindings = {};
const actionKeys = Object.keys(actions);
if (actionKeys.length) {
for (const key of actionKeys) {
let value = actions[key];
// Skip empty values
if (!value.trim()) {
continue;
}
const { error, valid } = verifyIsValid(value);
if (!valid) {
console.warn('Skipping invalid binding', error);
continue;
}
const useKey = `on${(0, lodash_1.upperFirst)(key)}`;
const asyncPrefix = `(async () =>`;
const asyncSuffix = ')()';
const isAsync = value.startsWith(asyncPrefix) && value.endsWith(asyncSuffix);
if (isAsync) {
value = value.slice(asyncPrefix.length, -asyncSuffix.length);
}
bindings[useKey] = (0, bindings_1.createSingleBinding)({
code: `${wrapBindingIfNeeded(value, options)}`,
async: isAsync ? true : undefined,
});
}
}
return bindings;
};
const getStyleStringFromBlock = (block, options) => {
var _a, _b;
const styleBindings = {};
const responsiveStyles = {};
let styleString = '';
if (block.bindings) {
for (const key in block.bindings) {
if (!key.includes('.')) {
continue;
}
let code = ((_b = (_a = block.code) === null || _a === void 0 ? void 0 : _a.bindings) === null || _b === void 0 ? void 0 : _b[key]) || block.bindings[key];
const verifyCode = verifyIsValid(code);
if (!verifyCode.valid) {
if (options.escapeInvalidCode) {
code = '`' + code + ' [INVALID CODE]`';
}
else {
console.warn(`Dropping binding "${key}" due to invalid code: ${code}`);
continue;
}
}
if (key.includes('style')) {
const styleProperty = key.split('.')[1];
styleBindings[styleProperty] = convertExportDefaultToReturn(code);
/**
* responsiveStyles that are bound need to be merged into media queries.
* Example:
* responsiveStyles.large.color: "state.color"
* responsiveStyles.large.background: "state.background"
* Should get mapped to:
* @media (max-width: 1200px): {
* color: state.color,
* background: state.background
* }
*/
}
else if (key.includes('responsiveStyles')) {
const [_, size, prop] = key.split('.');
const mediaKey = `@media (max-width: ${media_sizes_1.sizes[size].max}px)`;
/**
* The media query key has spaces/special characters so we need to ensure
* that the key is always a string otherwise there will be runtime errors.
*/
const objKey = `"${mediaKey}"`;
responsiveStyles[objKey] = {
...responsiveStyles[objKey],
[prop]: code,
};
}
}
/**
* All binding values are strings, but we don't want to stringify the values
* within the style object otherwise the bindings will be evaluated as strings.
* As a result, do not use JSON.stringify here.
*/
for (const key in responsiveStyles) {
const styles = Object.keys(responsiveStyles[key]);
const keyValues = styles.map((prop) => `${prop}: ${responsiveStyles[key][prop]}`);
const stringifiedObject = `{ ${keyValues.join(', ')} }`;
styleBindings[key] = stringifiedObject;
}
}
const styleKeys = Object.keys(styleBindings);
if (styleKeys.length) {
styleString = '{';
styleKeys.forEach((key) => {
// TODO: figure out how to have multiline style bindings here
// I tried (function{binding code})() and that did not work
styleString += ` ${key}: ${(options.includeBuilderExtras
? wrapBinding(styleBindings[key])
: styleBindings[key]
.replace(/var _virtual_index\s*=\s*/g, '')
.replace(/;*\s*return _virtual_index;*/, '')).replace(/;$/, '')},`;
});
styleString += ' }';
}
return styleString;
};
const hasComponent = (block) => {
var _a;
return Boolean((_a = block.component) === null || _a === void 0 ? void 0 : _a.name);
};
const hasProperties = (block) => {
return Boolean(block.properties && Object.keys(block.properties).length);
};
const hasBindings = (block) => {
return Boolean(block.bindings && Object.keys(block.bindings).length);
};
const hasStyles = (block) => {
if (block.responsiveStyles) {
for (const key in block.responsiveStyles) {
if (Object.keys(block.responsiveStyles[key]).length) {
return true;
}
}
}
return false;
};
const wrapBindingIfNeeded = (value, options) => {
if (options.includeBuilderExtras) {
return wrapBinding(value);
}
if ((value === null || value === void 0 ? void 0 : value.includes(';')) && !(value === null || value === void 0 ? void 0 : value.trim().startsWith('{'))) {
return `{ ${value} }`;
}
return value;
};
const getBlockActions = (block, options) => {
var _a;
const obj = {
...block.actions,
...(_a = block.code) === null || _a === void 0 ? void 0 : _a.actions,
};
if (options.includeBuilderExtras) {
for (const key in obj) {
const value = obj[key];
// TODO: plugin/option for for this
obj[key] = wrapBinding(value);
}
}
return obj;
};
const getBlockActionsAsBindings = (block, options) => {
return (0, lodash_1.mapKeys)(getBlockActions(block, options), (value, key) => `on${(0, capitalize_1.capitalize)(key)}`);
};
const isValidBindingKey = (str) => {
return Boolean(str && /^[a-z0-9_\.]$/i.test(str));
};
const getBlockNonActionBindings = (block, options) => {
var _a;
const obj = {
...block.bindings,
...(_a = block.code) === null || _a === void 0 ? void 0 : _a.bindings,
};
if (options.includeBuilderExtras) {
for (const key in obj) {
if (!isValidBindingKey(key)) {
console.warn('Skipping invalid binding key:', key);
continue;
}
const value = obj[key];
// TODO: verify the bindings are valid
let { valid, error } = verifyIsValid(value);
if (!valid) {
({ valid, error } = verifyIsValid(`function () { ${value} }`));
}
if (valid) {
obj[key] = wrapBinding(value);
}
else {
console.warn('Skipping invalid code:', error);
delete obj[key];
}
}
}
return obj;
};
function wrapBinding(value) {
if (!value) {
return value;
}
if (!(value.includes(';') || value.match(/(^|\s|;)return[^a-z0-9A-Z]/))) {
return value;
}
return `(() => {
try { ${(0, parsers_1.isExpression)(value) ? 'return ' : ''}${value} }
catch (err) {
console.warn('Builder code error', err);
}
})()`;
}
const getBlockBindings = (block, options) => {
const obj = {
...getBlockNonActionBindings(block, options),
...getBlockActionsAsBindings(block, options),
};
return obj;
};
// add back if this direction (blocks as children not prop) is desired
exports.symbolBlocksAsChildren = false;
const componentMappers = {
Symbol(block, options) {
var _a;
let css = getCssFromBlock(block);
const styleString = getStyleStringFromBlock(block, options);
const actionBindings = getActionBindingsFromBlock(block, options);
const bindings = {
symbol: (0, bindings_1.createSingleBinding)({
code: JSON.stringify({
...(_a = block.component) === null || _a === void 0 ? void 0 : _a.options.symbol,
}),
}),
...actionBindings,
...(styleString && {
style: (0, bindings_1.createSingleBinding)({ code: styleString }),
}),
...(Object.keys(css).length && {
css: (0, bindings_1.createSingleBinding)({ code: JSON.stringify(css) }),
}),
};
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'Symbol',
bindings: bindings,
meta: (0, exports.getMetaFromBlock)(block, options),
});
},
...(!exports.symbolBlocksAsChildren
? {}
: {
Symbol(block, options) {
var _a, _b, _c;
let css = getCssFromBlock(block);
const styleString = getStyleStringFromBlock(block, options);
const actionBindings = getActionBindingsFromBlock(block, options);
const content = (_a = block.component) === null || _a === void 0 ? void 0 : _a.options.symbol.content;
const blocks = (_b = content === null || content === void 0 ? void 0 : content.data) === null || _b === void 0 ? void 0 : _b.blocks;
if (blocks) {
content.data.blocks = null;
}
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'Symbol',
bindings: {
// TODO: this doesn't use all attrs
symbol: (0, bindings_1.createSingleBinding)({
code: JSON.stringify({
data: (_c = block.component) === null || _c === void 0 ? void 0 : _c.options.symbol.content.data,
content: content, // TODO: convert to <SymbolInternal>...</SymbolInternal> so can be parsed
}),
}),
...actionBindings,
...(styleString && {
style: (0, bindings_1.createSingleBinding)({ code: styleString }),
}),
...(Object.keys(css).length && {
css: (0, bindings_1.createSingleBinding)({ code: JSON.stringify(css) }),
}),
},
meta: (0, exports.getMetaFromBlock)(block, options),
children: !blocks
? []
: [
(0, create_mitosis_node_1.createMitosisNode)({
// TODO: the Builder generator side of this converting to blocks
name: 'BuilderSymbolContents',
children: blocks.map((item) => (0, exports.builderElementToMitosisNode)(item, options)),
}),
],
});
},
}),
Columns(block, options) {
var _a, _b;
const node = (0, exports.builderElementToMitosisNode)(block, options, {
skipMapper: true,
});
delete node.bindings.columns;
delete node.properties.columns;
node.children =
((_b = (_a = block.component) === null || _a === void 0 ? void 0 : _a.options.columns) === null || _b === void 0 ? void 0 : _b.map((col, index) => (0, create_mitosis_node_1.createMitosisNode)({
name: 'Column',
/**
* If width if undefined, do not create a binding otherwise its JSX will
* be <Column width={} /> which is not valid due to the empty expression.
*/
...(col.width != null && {
bindings: {
width: { code: col.width.toString() },
},
}),
...(col.link && {
properties: {
link: col.link,
},
}),
meta: (0, exports.getMetaFromBlock)(block, options),
children: col.blocks.map((col) => (0, exports.builderElementToMitosisNode)(col, options)),
}))) || [];
return node;
},
PersonalizationContainer(block, options) {
var _a, _b;
const node = (0, exports.builderElementToMitosisNode)(block, options, {
skipMapper: true,
});
delete node.bindings.variants;
delete node.properties.variants;
const newChildren = ((_b = (_a = block.component) === null || _a === void 0 ? void 0 : _a.options.variants) === null || _b === void 0 ? void 0 : _b.map((variant) => {
const variantNode = (0, create_mitosis_node_1.createMitosisNode)({
name: 'Variant',
properties: {
name: variant.name,
startDate: variant.startDate,
endDate: variant.endDate,
},
meta: (0, exports.getMetaFromBlock)(block, options),
children: variant.blocks.map((col) => (0, exports.builderElementToMitosisNode)(col, options)),
});
const queryOptions = variant.query;
if (Array.isArray(queryOptions)) {
variantNode.bindings.query = (0, bindings_1.createSingleBinding)({
code: JSON.stringify(queryOptions.map((q) => (0, lodash_1.omit)(q, '@type'))),
});
}
else if (queryOptions) {
variantNode.bindings.query = (0, bindings_1.createSingleBinding)({
code: JSON.stringify((0, lodash_1.omit)(queryOptions, '@type')),
});
}
return variantNode;
})) || [];
const defaultVariant = (0, create_mitosis_node_1.createMitosisNode)({
name: 'Variant',
properties: {
default: '',
},
children: node.children,
});
newChildren.push(defaultVariant);
node.children = newChildren;
return node;
},
'Shopify:For': (block, options) => {
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'For',
bindings: {
each: (0, bindings_1.createSingleBinding)({
code: `state.${block.component.options.repeat.collection}`,
}),
},
scope: {
forName: block.component.options.repeat.itemName,
},
meta: (0, exports.getMetaFromBlock)(block, options),
children: (block.children || []).map((child) => (0, exports.builderElementToMitosisNode)(updateBindings(child, 'state.$index', 'index'), options)),
});
},
Text: (block, options) => {
var _a, _b, _c;
let css = getCssFromBlock(block);
const styleString = getStyleStringFromBlock(block, options);
const actionBindings = getActionBindingsFromBlock(block, options);
const localizedValues = {};
const blockBindings = {
...mapBuilderBindingsToMitosisBindingWithCode(block.bindings),
...mapBuilderBindingsToMitosisBindingWithCode((_a = block.code) === null || _a === void 0 ? void 0 : _a.bindings),
};
const bindings = {
...(0, lodash_1.omitBy)(blockBindings, (value, key) => {
if (key === 'component.options.text') {
return true;
}
if (key && key.includes('style')) {
return true;
}
return false;
}),
...actionBindings,
...(styleString && {
style: { code: styleString },
}),
...(Object.keys(css).length && {
css: { code: JSON.stringify(css) },
}),
};
const properties = { ...block.properties };
for (const key in properties) {
if (typeof properties[key] === 'object' &&
properties[key] !== null &&
properties[key]['@type'] === '@builder.io/core:LocalizedValue') {
const localizedValue = properties[key];
localizedValues[`properties.${key}`] = localizedValue;
properties[key] = localizedValue.Default;
}
}
if (options.includeBuilderExtras && block.id)
properties['builder-id'] = block.id;
if (block.class)
properties['class'] = block.class;
if (block.layerName) {
properties.$name = block.layerName;
}
const innerBindings = {};
const componentOptionsText = blockBindings['component.options.text'];
if (componentOptionsText) {
innerBindings[options.preserveTextBlocks ? 'innerHTML' : '_text'] = (0, bindings_1.createSingleBinding)({
code: wrapBindingIfNeeded(componentOptionsText.code, options),
});
}
let text = ((_b = block.component.options) === null || _b === void 0 ? void 0 : _b.text) || '';
if (typeof text === 'object' &&
text !== null &&
text['@type'] === '@builder.io/core:LocalizedValue') {
localizedValues['component.options.text'] = (_c = block.component.options) === null || _c === void 0 ? void 0 : _c.text;
text = text.Default;
}
// Builder uses {{}} for bindings, but Mitosis expects {} so we need to convert
const innerProperties = innerBindings._text
? {}
: {
[options.preserveTextBlocks ? 'innerHTML' : '_text']: text.replace(/\{\{(.*?)\}\}/g, '{$1}'),
};
if (options.preserveTextBlocks) {
return (0, create_mitosis_node_1.createMitosisNode)({
name: block.tagName || 'div',
bindings,
properties,
meta: (0, exports.getMetaFromBlock)(block, options),
...(Object.keys(localizedValues).length && { localizedValues }),
children: [
(0, create_mitosis_node_1.createMitosisNode)({
bindings: innerBindings,
properties: {
...innerProperties,
class: 'builder-text',
},
...(Object.keys(localizedValues).length && { localizedValues }),
}),
],
});
}
// Disabling for now
const assumeLink = false;
const finalProperties = {
...(assumeLink
? {
href: '...',
}
: {}),
...properties,
};
const finalTagname = block.tagName || (assumeLink ? 'a' : 'div');
if ((block.tagName && block.tagName !== 'div') ||
hasStyles(block) ||
hasComponent(block) ||
hasBindings(block) ||
hasProperties(block)) {
return (0, create_mitosis_node_1.createMitosisNode)({
name: finalTagname,
bindings,
properties: finalProperties,
meta: (0, exports.getMetaFromBlock)(block, options),
children: [
(0, create_mitosis_node_1.createMitosisNode)({
bindings: innerBindings,
properties: innerProperties,
...(Object.keys(localizedValues).length && { localizedValues }),
}),
],
});
}
return (0, create_mitosis_node_1.createMitosisNode)({
name: finalTagname,
properties: {
...finalProperties,
...properties,
...innerProperties,
},
bindings: {
...bindings,
...innerBindings,
},
meta: (0, exports.getMetaFromBlock)(block, options),
...(Object.keys(localizedValues).length && { localizedValues }),
});
},
};
const builderElementToMitosisNode = (block, options, _internalOptions = {}) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
const { includeSpecialBindings = true } = options;
const localizedValues = {};
if (((_a = block.component) === null || _a === void 0 ? void 0 : _a.name) === 'Core:Fragment') {
block.component.name = 'Fragment';
}
const forBinding = (_b = block.repeat) === null || _b === void 0 ? void 0 : _b.collection;
if (forBinding) {
const isFragment = ((_c = block.component) === null || _c === void 0 ? void 0 : _c.name) === 'Fragment';
// TODO: handle having other things, like a repeat too
if (isFragment) {
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'For',
bindings: {
each: (0, bindings_1.createSingleBinding)({
code: wrapBindingIfNeeded((_d = block.repeat) === null || _d === void 0 ? void 0 : _d.collection, options),
}),
},
scope: {
forName: ((_e = block.repeat) === null || _e === void 0 ? void 0 : _e.itemName) || 'item',
},
meta: (0, exports.getMetaFromBlock)(block, options),
children: ((_f = block.children) === null || _f === void 0 ? void 0 : _f.map((child) => (0, exports.builderElementToMitosisNode)(updateBindings(child, 'state.$index', 'index'), options))) || [],
});
}
else {
const useBlock = ((_g = block.component) === null || _g === void 0 ? void 0 : _g.name) === 'Core:Fragment' && ((_h = block.children) === null || _h === void 0 ? void 0 : _h.length) === 1
? block.children[0]
: block;
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'For',
bindings: {
each: (0, bindings_1.createSingleBinding)({
code: wrapBindingIfNeeded((_j = block.repeat) === null || _j === void 0 ? void 0 : _j.collection, options),
}),
},
scope: {
forName: ((_k = block.repeat) === null || _k === void 0 ? void 0 : _k.itemName) || 'item',
indexName: '$index',
},
meta: (0, exports.getMetaFromBlock)(block, options),
children: [(0, exports.builderElementToMitosisNode)((0, lodash_1.omit)(useBlock, 'repeat'), options)],
});
}
}
// Special builder properties
// TODO: support hide and repeat
const blockBindings = getBlockBindings(block, options);
let code = undefined;
if (blockBindings.show) {
code = wrapBindingIfNeeded(blockBindings.show, options);
}
else if (blockBindings.hide) {
code = `!(${wrapBindingIfNeeded(blockBindings.hide, options)})`;
}
if (code) {
const isFragment = ((_l = block.component) === null || _l === void 0 ? void 0 : _l.name) === 'Fragment';
// TODO: handle having other things, like a repeat too
if (isFragment) {
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'Show',
bindings: { when: (0, bindings_1.createSingleBinding)({ code }) },
meta: (0, exports.getMetaFromBlock)(block, options),
children: ((_m = block.children) === null || _m === void 0 ? void 0 : _m.map((child) => (0, exports.builderElementToMitosisNode)(child, options))) || [],
});
}
else {
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'Show',
bindings: { when: (0, bindings_1.createSingleBinding)({ code }) },
meta: (0, exports.getMetaFromBlock)(block, options),
children: [
(0, exports.builderElementToMitosisNode)({
...block,
code: {
...block.code,
bindings: (0, lodash_1.omit)(blockBindings, 'show', 'hide'),
},
bindings: (0, lodash_1.omit)(blockBindings, 'show', 'hide'),
}, options),
],
});
}
}
const mapper = !_internalOptions.skipMapper && block.component && componentMappers[block.component.name];
if (mapper) {
return mapper(block, options);
}
const bindings = {};
const children = [];
const slots = {};
if (blockBindings) {
for (const key in blockBindings) {
if (key === 'css') {
continue;
}
const useKey = key.replace(/^(component\.)?options\./, '');
if (!useKey.includes('.')) {
let code = blockBindings[key].code || blockBindings[key];
const verifyCode = verifyIsValid(code);
if (!verifyCode.valid) {
if (options.escapeInvalidCode) {
code = '`' + code + ' [INVALID CODE]`';
}
else {
console.warn(`Dropping binding "${key}" due to invalid code: ${code}`);
continue;
}
}
bindings[useKey] = (0, bindings_1.createSingleBinding)({
code,
});
}
else if (useKey.includes('style') && useKey.includes('.')) {
const styleProperty = useKey.split('.')[1];
// TODO: add me in
// styleBindings[styleProperty] =
// block.code?.bindings?.[key] || blockBindings[key];
}
}
}
const properties = {
...block.properties,
...(options.includeBuilderExtras && {
['builder-id']: block.id,
// class: `builder-block ${block.id} ${block.properties?.class || ''}`,
}),
...(options.includeBuilderExtras && getBuilderPropsForSymbol(block)),
};
for (const key in properties) {
if (typeof properties[key] === 'object' &&
properties[key] !== null &&
properties[key]['@type'] === '@builder.io/core:LocalizedValue') {
const localizedValue = properties[key];
localizedValues[`properties.${key}`] = localizedValue;
properties[key] = localizedValue.Default;
}
}
if (block.layerName) {
properties.$name = block.layerName;
}
const linkUrl = block.linkUrl;
if (linkUrl) {
if (typeof linkUrl === 'object' &&
linkUrl !== null &&
linkUrl['@type'] === '@builder.io/core:LocalizedValue') {
properties.href = linkUrl.Default;
localizedValues['linkUrl'] = linkUrl;
}
else {
properties.href = linkUrl;
}
}
if ((_o = block.component) === null || _o === void 0 ? void 0 : _o.options) {
for (const key in block.component.options) {
const value = block.component.options[key];
const valueIsArrayOfBuilderElements = Array.isArray(value) && value.every(exports.isBuilderElement);
const transformBldrElementToMitosisNode = (item) => {
const node = (0, exports.builderElementToMitosisNode)(item, {
...options,
includeSpecialBindings: false,
});
return node;
};
if ((0, exports.isBuilderElement)(value)) {
slots[key] = [transformBldrElementToMitosisNode(value)];
}
else if (typeof value === 'string') {
properties[key] = value;
}
else if (typeof value === 'object' &&
value !== null &&
value['@type'] === '@builder.io/core:LocalizedValue') {
properties[key] = value.Default;
localizedValues[`component.options.${key}`] = value;
}
else if (valueIsArrayOfBuilderElements) {
const childrenElements = value
.filter((item) => {
var _a, _b;
if ((_b = (_a = item.properties) === null || _a === void 0 ? void 0 : _a.src) === null || _b === void 0 ? void 0 : _b.includes('/api/v1/pixel')) {
return false;
}
return true;
})
.map(transformBldrElementToMitosisNode);
slots[key] = childrenElements;
}
else {
bindings[key] = (0, bindings_1.createSingleBinding)({ code: json5_1.default.stringify(value) });
}
}
}
const css = getCssFromBlock(block);
let styleString = getStyleStringFromBlock(block, options);
const actionBindings = getActionBindingsFromBlock(block, options);
for (const binding in blockBindings) {
if (binding.startsWith('component.options') || binding.startsWith('options')) {
const value = blockBindings[binding];
const useKey = binding.replace(/^(component\.options\.|options\.)/, '');
bindings[useKey] = (0, bindings_1.createSingleBinding)({ code: value });
}
}
const node = (0, create_mitosis_node_1.createMitosisNode)({
name: ((_q = (_p = block.component) === null || _p === void 0 ? void 0 : _p.name) === null || _q === void 0 ? void 0 : _q.replace(/[^a-z0-9]/gi, '')) ||
block.tagName ||
(block.linkUrl ? 'a' : 'div'),
properties: {
...(block.component && includeSpecialBindings && { $tagName: block.tagName }),
...(block.class && { class: block.class }),
...properties,
},
bindings: {
...bindings,
...actionBindings,
...(styleString && {
style: (0, bindings_1.createSingleBinding)({ code: styleString }),
}),
...(css &&
Object.keys(css).length && {
css: (0, bindings_1.createSingleBinding)({ code: JSON.stringify(css) }),
}),
},
slots: {
...slots,
},
meta: (0, exports.getMetaFromBlock)(block, options),
...(Object.keys(localizedValues).length && { localizedValues }),
});
// Has single text node child
const firstChild = (_r = block.children) === null || _r === void 0 ? void 0 : _r[0];
if (((_s = block.children) === null || _s === void 0 ? void 0 : _s.length) === 1 &&
((_t = firstChild === null || firstChild === void 0 ? void 0 : firstChild.component) === null || _t === void 0 ? void 0 : _t.name) === 'Text' &&
!options.preserveTextBlocks) {
const textProperties = (0, exports.builderElementToMitosisNode)(firstChild, options);
const parsedNodeCss = json5_1.default.parse(((_u = node.bindings.css) === null || _u === void 0 ? void 0 : _u.code) || '{}');
const parsedTextCss = json5_1.default.parse(((_v = textProperties.bindings.css) === null || _v === void 0 ? void 0 : _v.code) || '{}');
const mergedCss = combineStyles(parsedNodeCss, parsedTextCss);
// Don't merge if text has styling that matters
const doNotMerge =
// Text has flex alignment
['end', 'right', 'center'].includes(parsedTextCss.alignSelf) ||
// Text has specific styling
parsedTextCss.backgroundColor ||
parsedTextCss.opacity ||
parsedTextCss.background;
if (!doNotMerge) {
return (0, lodash_1.merge)({}, textProperties, node, {
bindings: {
...(Object.keys(mergedCss).length && {
css: { code: json5_1.default.stringify(mergedCss) },
}),
},
});
}
}
node.children = children.concat((block.children || []).map((item) => (0, exports.builderElementToMitosisNode)(item, options)));
return node;
};
exports.builderElementToMitosisNode = builderElementToMitosisNode;
const getBuilderPropsForSymbol = (block) => {
var _a, _b;
if (((_a = block.children) === null || _a === void 0 ? void 0 : _a.length) === 1) {
const child = block.children[0];
const builderContentId = (_b = child.properties) === null || _b === void 0 ? void 0 : _b['builder-content-id'];
if (builderContentId) {
return { 'builder-content-id': builderContentId };
}
}
return undefined;
};
const getMetaFromBlock = (block, options) => {
const { includeMeta = false } = options;
return includeMeta
? {
'builder-id': block.id,
...block.meta,
}
: {};
};
exports.getMetaFromBlock = getMetaFromBlock;
const getHooks = (content) => {
var _a, _b;
const code = convertExportDefaultToReturn(((_a = content.data) === null || _a === void 0 ? void 0 : _a.tsCode) || ((_b = content.data) === null || _b === void 0 ? void 0 : _b.jsCode) || '');
try {
return (0, jsx_1.parseJsx)(`
export default function TemporaryComponent() {
${
// Mitosis parser looks for useStore to be a variable assignment,
// but in Builder that's not how it works. For now do a replace to
// easily resuse the same parsing code as this is the only difference
code.replace(`useStore(`, `var state = useStore(`)}
}`);
}
catch (err) {
console.warn('Could not parse js code as a Mitosis component body', err, code);
return null;
}
};
/**
* Take Builder custom jsCode and extract the contents of the useStore hook
* and return it as a JS object along with the inputted code with the hook
* code extracted
*/
function extractStateHook(code) {
const { types } = babel;
let state = {};
const body = (0, parsers_1.parseCode)(code);
const newBody = body.slice();
for (let i = 0; i < body.length; i++) {
const statement = body[i];
if (types.isExpressionStatement(statement)) {
const { expression } = statement;
// Check for useStore
if (types.isCallExpression(expression)) {
if (types.isIdentifier(expression.callee) && expression.callee.name === 'useStore') {
const arg = expression.arguments[0];
if (types.isObjectExpression(arg)) {
state = (0, state_1.parseStateObjectToMitosisState)(arg);
newBody.splice(i, 1);
}
}
if (types.isMemberExpression(expression.callee)) {
if (types.isIdentifier(expression.callee.object) &&
expression.callee.object.name === 'Object') {
if (types.isIdentifier(expression.callee.property) &&
expression.callee.property.name === 'assign') {
const arg = expression.arguments[1];
if (types.isObjectExpression(arg)) {
state = (0, state_1.parseStateObjectToMitosisState)(arg);
newBody.splice(i, 1);
}
}
}
}
}
}
}
const newCode = (0, generator_1.default)(types.program(newBody)).code || '';
return { code: newCode, state };
}
exports.extractStateHook = extractStateHook;
function convertExportDefaultToReturn(code) {
try {
const { types } = babel;
const body = (0, parsers_1.parseCode)(code);
if (body.length === 0)
return code;
const newBody = body.slice();
for (let i = 0; i < body.length; i++) {
const statement = body[i];
if (types.isExportDefaultDeclaration(statement)) {
if (types.isCallExpression(statement.declaration) ||
types.isExpression(statement.declaration)) {
newBody[i] = types.returnStatement(statement.declaration);
}
}
}
return (0, generator_1.default)(types.program(newBody)).code || '';
}
catch (e) {
const error = e;
if (error.code === 'BABEL_PARSE_ERROR') {
return code;
}
else {
throw e;
}
}
}
exports.convertExportDefaultToReturn = convertExportDefaultToReturn;
const updateBindings = (node, from, to) => {
(0, legacy_1.default)(node).forEach(function (item) {
if ((0, exports.isBuilderElement)(item)) {
if (item.bindings) {
for (const [key, value] of Object.entries(item.bindings)) {
if (value === null || value === void 0 ? void 0 : value.includes(from)) {
item.bindings[key] = value.replaceAll(from, to);
}
}
}
if (item.actions) {
for (const [key, value] of Object.entries(item.actions)) {
if (value === null || value === void 0 ? void 0 : value.includes(from)) {
item.actions[key] = value.replaceAll(from, to);
}
}
}
}
});
return node;
};
// TODO: maybe this should be part of the builder -> Mitosis part
function extractSymbols(json) {
var _a, _b, _c, _d;
const subComponents = [];
const symbols = [];
(0, legacy_1.default)(json).forEach(function (item) {
var _a;
if ((0, exports.isBuilderElement)(item)) {
if (((_a = item.component) === null || _a === void 0 ? void 0 : _a.name) === 'Symbol') {
symbols.push({ element: item, depth: this.path.length, id: item.id });
}
}
});
const symbolsSortedDeepestFirst = (0, lodash_1.sortBy)(symbols, (info) => info.depth)
.reverse()
.map((el) => el.element);
let symbolsFound = 0;
for (const el of symbolsSortedDeepestFirst) {
const symbolValue = (_b = (_a = el.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', el.id);
if ((_c = el.component) === null || _c === void 0 ? void 0 : _c.options.symbol.content) {
delete el.component.options.symbol.content;
}
continue;
}
const componentName = 'Symbol' + ++symbolsFound;
el.component.name = componentName;
if ((_d = el.component) === null || _d === void 0 ? void 0 : _d.options.symbol.content) {
delete el.component.options.symbol.content;
}
subComponents.push({
content: elContent,
name: componentName,
});
}
return {
content: json,
subComponents,
};
}
const createBuilderElement = (options) => ({
'@type': '@builder.io/sdk:Element',
id: 'builder-' + (0, symbol_processor_1.hashCodeAsString)(options),
...options,
});
exports.createBuilderElement = createBuilderElement;
const isBuilderElement = (el) => (el === null || el === void 0 ? void 0 : el['@type']) === '@builder.io/sdk:Element';
exports.isBuilderElement = isBuilderElement;
const builderContentPartToMitosisComponent = (builderContent, options = {}) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
builderContent = (0, fast_clone_1.fastClone)(builderContent);
(0, legacy_1.default)(builderContent).forEach(function (elem) {
var _a, _b;
if ((0, exports.isBuilderElement)(elem)) {
// Try adding self-closing tags to void elements, since Builder Text
// blocks can contain arbitrary HTML
// List taken from https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
// TODO: Maybe this should be using something more robust than a regular expression
const voidElemRegex = /(<area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr[^>]+)>/gm;
try {
if (((_a = elem.component) === null || _a === void 0 ? void 0 : _a.name) === 'Text') {
const text = elem.component.options.text;
elem.component.options.text = text.replace(voidElemRegex, '$1 />');
// Remove broken emojis
const hasUnpairedSurrogate = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/g;
if (hasUnpairedSurrogate.test(text)) {
elem.component.options.text = text.replace(hasUnpairedSurrogate, '');
}
}
}
catch (_error) {
// pass
}
try {
if (((_b = elem.component) === null || _b === void 0 ? void 0 : _b.name) === 'Custom Code') {
elem.component.options.code = elem.component.options.code.replace(voidElemRegex, '$1 />');
}
}
catch (_error) {
// pass
}
}
});
const { state, code } = extractStateHook(((_a = builderContent === null || builderContent === void 0 ? void 0 : builderContent.data) === null || _a === void 0 ? void 0 : _a.tsCode) || ((_b = builderContent === null || builderContent === void 0 ? void 0 : builderContent.data) === null || _b === void 0 ? void 0 : _b.jsCode) || '');
const customCode = convertExportDefaultToReturn(code);
const parsed = getHooks(builderContent);
const parsedState = (parsed === null || parsed === void 0 ? void 0 : parsed.state) || {};
const mitosisState = Object.keys(parsedState).length > 0
? parsedState
: {
...state,
...(0, helpers_1.mapBuilderContentStateToMitosisState)(((_c = builderContent.data) === null || _c === void 0 ? void 0 : _c.state) || {}),
};
const componentJson = (0, create_mitosis_component_1.createMitosisComponent)({
meta: {
useMetadata: {
httpRequests: (_d = builderContent.data) === null || _d === void 0 ? void 0 : _d.httpRequests,
},
// cmp.meta.cssCode exists for backwards compatibility, prefer cmp.style
...(((_e = builderContent.data) === null || _e === void 0 ? void 0 : _e.cssCode) && { cssCode: builderContent.data.cssCode }),
},
...(((_f = builderContent.data) === null || _f === void 0 ? void 0 : _f.cssCode) && { style: (_g = builderContent.data) === null || _g === void 0 ? void 0 : _g.cssCode }),
inputs: (_j = (_h = builderContent.data) === null || _h === void 0 ? void 0 : _h.inputs) === null || _j === void 0 ? void 0 : _j.map((input) => ({
name: input.name,
defaultValue: input.defaultValue,
})),
state: mitosisState,
hooks: {
onMount: [
...((parsed === null || parsed === void 0 ? void 0 : parsed.hooks.onMount.length)
? parsed === null || parsed === void 0 ? void 0 : parsed.hooks.onMount
: customCode
? [{ code: customCode }]
: []),
],
},
children: (((_k = builderContent.data) === null || _k === void 0 ? void 0 : _k.blocks) || [])
.filter((item) => {
var _a, _b;
if ((_b = (_a = item.properties) === null || _a === void 0 ? void 0 : _a.src) === null || _b === void 0 ? void 0 : _b.includes('/api/v1/pixel')) {
return false;
}
return true;
})
.map((item) => (0, exports.builderElementToMitosisNode)(item, options)),
});
return componentJson;
};
const builderContentToMitosisComponent = (builderContent, options = {}) => {
builderContent = (0, fast_clone_1.fastClone)(builderContent);
const separated = extractSymbols(builderContent);
const componentJson = {
...builderContentPartToMitosisComponent(separated.content, options),
subComponents: separated.subComponents.map((item) => ({
...builderContentPartToMitosisComponent(item.content, options),
name: item.name,
})),
};
return componentJson;
};
exports.builderContentToMitosisComponent = builderContentToMitosisComponent;
function mapBuilderBindingsToMitosisBindingWithCode(bindings) {
const result = {};
bindings &&
Object.keys(bindings).forEach((key) => {
const value = bindings[key];
if (typeof value === 'string') {
result[key] = (0, bindings_1.createSingleBinding)({ code: value });
}
else if (value && typeof value === 'object' && value.code) {
result[key] = (0, bindings_1.createSingleBinding)({ code: value.code });
}
else {
throw new Error('Unexpected binding value: ' + JSON.stringify(value));
}
});
return result;
}
function combineStyles(parent, child) {
const marginStyles = ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'];
const paddingStyles = ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRi