UNPKG

@builder.io/mitosis

Version:

Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io

162 lines (161 loc) 7.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.componentToLiquid = exports.isValidLiquidBinding = void 0; const event_handlers_1 = require("../../helpers/event-handlers"); const standalone_1 = require("prettier/standalone"); const html_tags_1 = require("../../constants/html_tags"); const fast_clone_1 = require("../../helpers/fast-clone"); const get_state_object_string_1 = require("../../helpers/get-state-object-string"); const strip_meta_properties_1 = require("../../helpers/strip-meta-properties"); const strip_state_and_props_refs_1 = require("../../helpers/strip-state-and-props-refs"); const collect_css_1 = require("../../helpers/styles/collect-css"); const plugins_1 = require("../../modules/plugins"); const mitosis_node_1 = require("../../types/mitosis-node"); /** * Test if the binding expression would be likely to generate * valid or invalid liquid. If we generate invalid liquid tags * Shopify will reject our PUT to update the template */ const isValidLiquidBinding = (str = '') => { const strictMatches = Boolean( // Test for our `context.shopify.liquid.*(expression), which // we regex out later to transform back into valid liquid expressions str.match(/(context|ctx)\s*(\.shopify\s*)?\.liquid\s*\./)); return (strictMatches || // Test is the expression is simple and would map to Shopify bindings // Test for our `context.shopify.liquid.*(expression), which // e.g. `state.product.price` -> `{{product.price}} // we regex out later to transform back into valid liquid expressions Boolean(str.match(/^[a-z0-9_\.\s]+$/i))); }; exports.isValidLiquidBinding = isValidLiquidBinding; const mappers = { Fragment: (json, options) => { return `<div>${json.children.map((item) => blockToLiquid(item, options)).join('\n')}</div>`; }, }; // TODO: spread support const blockToLiquid = (json, options = {}) => { var _a, _b, _c, _d, _e, _f; if (mappers[json.name]) { return mappers[json.name](json, options); } // TODO: Add support for `{props.children}` bindings if (json.properties._text) { return json.properties._text; } if ((_a = json.bindings._text) === null || _a === void 0 ? void 0 : _a.code) { if (!(0, exports.isValidLiquidBinding)(json.bindings._text.code)) { return ''; } return `{{${(0, strip_state_and_props_refs_1.stripStateAndPropsRefs)(json.bindings._text.code)}}}`; } let str = ''; if ((0, mitosis_node_1.checkIsForNode)(json)) { if (!((0, exports.isValidLiquidBinding)((_b = json.bindings.each) === null || _b === void 0 ? void 0 : _b.code) && (0, exports.isValidLiquidBinding)(json.scope.forName))) { return str; } str += `{% for ${json.scope.forName} in ${(0, strip_state_and_props_refs_1.stripStateAndPropsRefs)((_c = json.bindings.each) === null || _c === void 0 ? void 0 : _c.code)} %}`; if (json.children) { str += json.children.map((item) => blockToLiquid(item, options)).join('\n'); } str += '{% endfor %}'; } else if (json.name === 'Show') { if (!(0, exports.isValidLiquidBinding)((_d = json.bindings.when) === null || _d === void 0 ? void 0 : _d.code)) { return str; } str += `{% if ${(0, strip_state_and_props_refs_1.stripStateAndPropsRefs)((_e = json.bindings.when) === null || _e === void 0 ? void 0 : _e.code)} %}`; if (json.children) { str += json.children.map((item) => blockToLiquid(item, options)).join('\n'); } str += '{% endif %}'; } else { str += `<${json.name} `; if (((_f = json.bindings._spread) === null || _f === void 0 ? void 0 : _f.code) === '_spread' && (0, exports.isValidLiquidBinding)(json.bindings._spread.code)) { str += ` {% for _attr in ${json.bindings._spread.code} %} {{ _attr[0] }}="{{ _attr[1] }}" {% endfor %} `; } for (const key in json.properties) { const value = json.properties[key]; str += ` ${key}="${value}" `; } for (const key in json.bindings) { if (key === '_spread' || key === 'ref' || key === 'css') { continue; } const { code: value } = json.bindings[key]; // TODO: proper babel transform to replace. Util for this const useValue = (0, strip_state_and_props_refs_1.stripStateAndPropsRefs)(value); if ((0, event_handlers_1.checkIsEvent)(key)) { // Do nothing } else if ((0, exports.isValidLiquidBinding)(useValue)) { str += ` ${key}="{{${useValue}}}" `; } } if (html_tags_1.SELF_CLOSING_HTML_TAGS.has(json.name)) { return str + ' />'; } str += '>'; if (json.children) { str += json.children.map((item) => blockToLiquid(item, options)).join('\n'); } str += `</${json.name}>`; } return str; }; // TODO: add JS support similar to componentToHtml() const componentToLiquid = (options = {}) => ({ component }) => { let json = (0, fast_clone_1.fastClone)(component); if (options.plugins) { json = (0, plugins_1.runPreJsonPlugins)({ json, plugins: options.plugins }); } const css = (0, collect_css_1.collectCss)(json); (0, strip_meta_properties_1.stripMetaProperties)(json); if (options.plugins) { json = (0, plugins_1.runPostJsonPlugins)({ json, plugins: options.plugins }); } let str = json.children.map((item) => blockToLiquid(item)).join('\n'); if (css.trim().length) { str += `<style>${css}</style>`; } if (options.reactive) { const stateObjectString = (0, get_state_object_string_1.getStateObjectStringFromComponent)(json); if (stateObjectString.trim().length > 4) { str += `<script reactive> export default { state: ${stateObjectString} } </script>`; } } if (options.plugins) { str = (0, plugins_1.runPreCodePlugins)({ json, code: str, plugins: options.plugins }); } if (options.prettier !== false) { try { str = (0, standalone_1.format)(str, { parser: 'html', htmlWhitespaceSensitivity: 'ignore', plugins: [ // To support running in browsers require('prettier/parser-html'), require('prettier/parser-postcss'), require('prettier/parser-babel'), ], }); } catch (err) { console.warn('Could not prettify', { string: str }, err); } } if (options.plugins) { str = (0, plugins_1.runPostCodePlugins)({ json, code: str, plugins: options.plugins }); } return str; }; exports.componentToLiquid = componentToLiquid;