UNPKG

@builder.io/mitosis

Version:

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

281 lines (279 loc) 11.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.blockToSvelte = void 0; const bindings_1 = require("../../helpers/bindings"); const event_handlers_1 = require("../../helpers/event-handlers"); const is_children_1 = __importDefault(require("../../helpers/is-children")); const is_upper_case_1 = require("../../helpers/is-upper-case"); const for_1 = require("../../helpers/nodes/for"); const remove_surrounding_block_1 = require("../../helpers/remove-surrounding-block"); const slots_1 = require("../../helpers/slots"); const html_tags_1 = require("../../constants/html_tags"); const helpers_1 = require("./helpers"); /** * blockToSvelte executed after stripStateAndProps, * when stripStateAndProps is executed, * SLOT_PREFIX from `slot` change to `$$slots.` */ const SLOT_PREFIX = '$$slots.'; const mappers = { style: ({ json, options, parentComponent }) => { var _a; let props = ''; for (const key in json.properties) { const value = json.properties[key]; props += ` ${key}="${value}" `; } let bindings = ''; for (const key in json.bindings) { const value = json.bindings[key]; if (value && key !== 'innerHTML') { bindings += ` ${key}=\${${value.code}} `; } } const innerText = ((_a = json.bindings.innerHTML) === null || _a === void 0 ? void 0 : _a.code) || ''; // We have to obfuscate `"style"` due to a limitation in the svelte-preprocessor plugin. // https://github.com/sveltejs/vite-plugin-svelte/issues/315#issuecomment-1109000027 return `{@html \`<\${'style'} ${bindings} ${props}>\${${innerText}}<\${'/style'}>\`}`; }, script: ({ json, options, parentComponent }) => { var _a; let props = ''; for (const key in json.properties) { const value = json.properties[key]; props += ` ${key}="${value}" `; } let bindings = ''; for (const key in json.bindings) { const value = json.bindings[key]; if (value && key !== 'innerHTML') { bindings += ` ${key}=\${${value.code}} `; } } const innerText = ((_a = json.bindings.innerHTML) === null || _a === void 0 ? void 0 : _a.code) || ''; return `{@html \`<script ${bindings} ${props}>\${${innerText}}</script>\`}`; }, Fragment: ({ json, options, parentComponent }) => { var _a; if ((_a = json.bindings.innerHTML) === null || _a === void 0 ? void 0 : _a.code) { return BINDINGS_MAPPER.innerHTML(json, options); } else if (json.children.length > 0) { return `${json.children .map((item) => (0, exports.blockToSvelte)({ json: item, options, parentComponent })) .join('\n')}`; } else { return ''; } }, For: ({ json, options, parentComponent }) => { var _a, _b; const firstChild = json.children[0]; const keyValue = firstChild.properties.key || ((_a = firstChild.bindings.key) === null || _a === void 0 ? void 0 : _a.code); if (keyValue) { // we remove extraneous prop which Svelte does not use delete firstChild.properties.key; delete firstChild.bindings.key; } const args = (0, for_1.getForArguments)(json, { excludeCollectionName: true }).join(', '); return ` {#each ${(_b = json.bindings.each) === null || _b === void 0 ? void 0 : _b.code} as ${args} ${keyValue ? `(${keyValue})` : ''}} ${json.children.map((item) => (0, exports.blockToSvelte)({ json: item, options, parentComponent })).join('\n')} {/each} `; }, Show: ({ json, options, parentComponent }) => { var _a; return ` {#if ${(_a = json.bindings.when) === null || _a === void 0 ? void 0 : _a.code} } ${json.children.map((item) => (0, exports.blockToSvelte)({ json: item, options, parentComponent })).join('\n')} ${json.meta.else ? ` {:else} ${(0, exports.blockToSvelte)({ json: json.meta.else, options, parentComponent, })} ` : ''} {/if}`; }, Slot({ json, options, parentComponent }) { var _a, _b, _c; const slotName = ((_a = json.bindings.name) === null || _a === void 0 ? void 0 : _a.code) || json.properties.name; const renderChildren = () => { var _a; return (_a = json.children) === null || _a === void 0 ? void 0 : _a.map((item) => (0, exports.blockToSvelte)({ json: item, options, parentComponent })).join('\n'); }; if (!slotName) { const key = Object.keys(json.bindings).find(Boolean); if (!key) { if (!((_b = json.children) === null || _b === void 0 ? void 0 : _b.length)) { return '<slot/>'; } return `<slot>${renderChildren()}</slot>`; } return ` <span #${key}> ${(_c = json.bindings[key]) === null || _c === void 0 ? void 0 : _c.code} </span> `; } return `<slot name="${(0, slots_1.toKebabSlot)(slotName, SLOT_PREFIX)}">${renderChildren()}</slot>`; }, }; const BINDINGS_MAPPER = { innerHTML: (json, options) => { var _a; return `{@html ${(_a = json.bindings.innerHTML) === null || _a === void 0 ? void 0 : _a.code}}`; }, }; const SVELTE_SPECIAL_TAGS = { COMPONENT: 'svelte:component', ELEMENT: 'svelte:element', SELF: 'svelte:self', }; const getTagName = ({ json, parentComponent, options, }) => { if (parentComponent && json.name === parentComponent.name) { return SVELTE_SPECIAL_TAGS.SELF; } /** * These are special HTML tags that svelte requires `<svelte:element this={TAG}>` */ const SPECIAL_HTML_TAGS = ['script', 'template']; if (SPECIAL_HTML_TAGS.includes(json.name)) { json.bindings.this = (0, bindings_1.createSingleBinding)({ code: `"${json.name}"`, }); return SVELTE_SPECIAL_TAGS.ELEMENT; } const isValidHtmlTag = html_tags_1.VALID_HTML_TAGS.includes(json.name); const isSpecialSvelteTag = json.name.startsWith('svelte:'); // Check if any import matches `json.name` const hasMatchingImport = parentComponent.imports.some(({ imports }) => Object.keys(imports).some((name) => name === json.name)); // If none of these are true, then we have some type of dynamic tag name if (!isValidHtmlTag && !isSpecialSvelteTag && !hasMatchingImport) { json.bindings.this = (0, bindings_1.createSingleBinding)({ code: (0, helpers_1.stripStateAndProps)({ json: parentComponent, options })(json.name), }); // TO-DO: no way to perfectly decide between <svelte:component> and <svelte:element> for dynamic // values...need to do that through metadata overrides for now. return SVELTE_SPECIAL_TAGS.COMPONENT; } return json.name; }; const stringifyBinding = (node, options) => ([key, binding]) => { if (key === 'innerHTML' || !binding) { return ''; } const { code, arguments: cusArgs = ['event'], type } = binding; const isValidHtmlTag = html_tags_1.VALID_HTML_TAGS.includes(node.name) || node.name === 'svelte:element'; if (type === 'spread') { const spreadValue = key === 'props' ? '$$props' : code; return ` {...${spreadValue}} `; } else if ((0, event_handlers_1.checkIsEvent)(key) && isValidHtmlTag) { const { async } = binding; // handle html native on[event] props const event = key.replace('on', '').toLowerCase(); // TODO: handle quotes in event handler values const valueWithoutBlock = (0, remove_surrounding_block_1.removeSurroundingBlock)(code); if (valueWithoutBlock === key && !async) { return ` on:${event}={${valueWithoutBlock}} `; } else { const asyncKeyword = async ? 'async ' : ''; return ` on:${event}="{${asyncKeyword}(${cusArgs.join(',')}) => {${valueWithoutBlock}}}" `; } } else if ((0, event_handlers_1.checkIsEvent)(key)) { // handle on[custom event] props const valueWithoutBlock = (0, remove_surrounding_block_1.removeSurroundingBlock)(code); if (valueWithoutBlock === key) { return ` ${key}={${valueWithoutBlock}} `; } else { return ` ${key}={(${cusArgs.join(',')}) => ${valueWithoutBlock}}`; } } else if (key === 'ref') { return ` bind:this={${code}} `; } else { return ` ${key}={${code}} `; } }; const blockToSvelte = ({ json, options, parentComponent }) => { var _a, _b, _c, _d; // Handling key binding by wrapping the element in a #key block if (json.bindings.key) { const keyCode = json.bindings.key.code; delete json.bindings.key; const str = ` {#key ${keyCode}} ${(0, exports.blockToSvelte)({ json, options, parentComponent })} {/key} `; return str; } if (mappers[json.name]) { return mappers[json.name]({ json: json, options, parentComponent, }); } const tagName = getTagName({ json, parentComponent, options }); if ((0, is_children_1.default)({ node: json, extraMatches: ['$$slots.default'] })) { return `<slot></slot>`; } if (json.properties._text) { return json.properties._text; } const textCode = (_a = json.bindings._text) === null || _a === void 0 ? void 0 : _a.code; if (textCode) { if ((0, slots_1.isSlotProperty)(textCode, SLOT_PREFIX)) { return `<slot name="${(0, slots_1.stripSlotPrefix)(textCode, SLOT_PREFIX).toLowerCase()}"/>`; } return `{${textCode}}`; } let str = ''; str += `<${tagName} `; const isComponent = Boolean(tagName[0] && (0, is_upper_case_1.isUpperCase)(tagName[0])); if ((((_b = json.bindings.style) === null || _b === void 0 ? void 0 : _b.code) || json.properties.style) && !isComponent) { const useValue = ((_c = json.bindings.style) === null || _c === void 0 ? void 0 : _c.code) || json.properties.style; str += `style={stringifyStyles(${useValue})}`; delete json.bindings.style; delete json.properties.style; } for (const key in json.properties) { const value = json.properties[key]; str += ` ${key}="${value}" `; } const stringifiedBindings = Object.entries(json.bindings) .map(stringifyBinding(json, options)) .join(''); str += stringifiedBindings; // if we have innerHTML, it doesn't matter whether we have closing tags or not, or children or not. // we use the innerHTML content as children and don't render the self-closing tag. if ((_d = json.bindings.innerHTML) === null || _d === void 0 ? void 0 : _d.code) { str += '>'; str += BINDINGS_MAPPER.innerHTML(json, options); str += `</${tagName}>`; return str; } if (html_tags_1.SELF_CLOSING_HTML_TAGS.has(tagName)) { return str + ' />'; } str += '>'; if (json.children) { str += json.children .map((item) => (0, exports.blockToSvelte)({ json: item, options, parentComponent })) .join(''); } str += `</${tagName}>`; return str; }; exports.blockToSvelte = blockToSvelte;