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,167 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', 'paddingRight'];
    const distanceStylesToCombine = [...paddingStyles, ...marginStyles];
    const merged = {
        ...(0, lodash_1.omit)(child, distanceStylesToCombine),
        ...parent,
    };
    for (const key of distanceStylesToCombine) {
        // Funky things happen if different alignment
        if (parent.alignSelf !== child.alignSelf && (key === 'marginLeft' || key === 'marginRight')) {
            merged[key] = parent[key];
            continue;
        }
        const childNum = parseFloat(child[key]) || 0;
        const parentKeyToUse = key.replace(/margin/, 'padding');
        const parentNum = parseFloat(parent[parentKeyToUse]) || 0;
        if (childNum || parentNum) {
            merged[parentKeyToUse] = `${childNum + parentNum}px`;
        }
    }
    for (const [key, value] of Object.entries(merged)) {
        if (value && typeof value === 'object') {
            merged[key] = combineStyles(parent[key] || {}, child[key] || {});
        }
    }
    return merged;
}