UNPKG

@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
"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