UNPKG

@builder.io/mitosis

Version:

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

224 lines (223 loc) 9.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderJSXNodes = void 0; const is_mitosis_node_1 = require("../../helpers/is-mitosis-node"); const directives_1 = require("./directives"); const src_generator_1 = require("./src-generator"); /** * Convert a Mitosis nodes to a JSX nodes. * * @param file File into which the output will be written to. * @param directives Store for directives which we came across so that they can be imported. * @param handlers A set of handlers which we came across so that they can be rendered * @param children A list of children to convert to JSX * @param styles Store for styles which we came across so that they can be rendered. * @param key Key to be used for the node if needed * @param parentSymbolBindings A set of bindings from parent to be written into the child. * @param root True if this is the root JSX, and may need a Fragment wrapper. * @returns */ function renderJSXNodes(file, directives, handlers, children, styles, key, parentSymbolBindings, root = true) { return function () { const srcBuilder = this; if (children.length == 0) return; if (root) this.emit('('); const needsFragment = root && (children.length > 1 || (children.length && (isInlinedDirective(children[0]) || isTextNode(children[0])))); file.import(file.qwikModule, 'h'); const fragmentSymbol = file.import(file.qwikModule, 'Fragment'); if (needsFragment) { this.jsxBeginFragment(fragmentSymbol); } children.forEach((child) => { var _a, _b; if (isEmptyTextNode(child)) return; if (isTextNode(child)) { const text = child.properties._text; const textExpr = (_a = child.bindings._text) === null || _a === void 0 ? void 0 : _a.code; if (typeof text == 'string') { this.isJSX ? this.emit(text) : this.jsxTextBinding((0, src_generator_1.quote)(text)); } else if (typeof textExpr == 'string') { this.isJSX ? this.emit('{', textExpr, '}') : this.jsxTextBinding(textExpr); } } else if (isSlotProjection(child)) { this.file.import(this.file.qwikModule, 'Slot'); this.jsxBegin('Slot', {}, {}); this.jsxEnd('Slot'); } else { let childName = child.name; const directive = directives_1.DIRECTIVES[childName]; if (typeof directive == 'function') { const blockFn = mitosisNodeToRenderBlock(child.children); const meta = child.meta; Object.keys(meta).forEach((key) => { const value = meta[key]; if ((0, is_mitosis_node_1.isMitosisNode)(value)) { blockFn[key] = mitosisNodeToRenderBlock([value]); } }); this.emit(directive(child, blockFn)); !this.isJSX && this.emit(','); includedHelperDirectives(directive.toString(), directives); } else { if (childName === 'Slot') { this.file.import(this.file.qwikModule, 'Slot'); } else { if (typeof directive == 'string') { directives.set(childName, directive); includedHelperDirectives(directive, directives); if (file.module !== 'med' && file.imports.hasImport(childName)) { file.import('./med.js', childName); } } if (isSymbol(childName)) { // TODO(misko): We are hard coding './med.js' which is not right. !file.imports.hasImport(childName) && file.import('./med.js', childName); let exportedChildName = file.exports.get(childName); if (exportedChildName) { childName = exportedChildName; } } } let props = child.properties; const css = (_b = child.bindings.css) === null || _b === void 0 ? void 0 : _b.code; const specialBindings = {}; if (css) { props = { ...props }; const styleProps = styles.get(css); const imageMaxWidth = childName == 'Image' && styleProps.maxWidth; if (imageMaxWidth && imageMaxWidth.endsWith('px')) { // special case for Images. We want to make sure that we include the maxWidth in a srcset specialBindings.srcsetSizes = Number.parseInt(imageMaxWidth); } if (styleProps === null || styleProps === void 0 ? void 0 : styleProps.CLASS_NAME) { props.class = addClass(styleProps.CLASS_NAME, props.class); } } key = props['builder-id'] || key; if (props.innerHTML) { // Special case. innerHTML requires `key` in Qwik props = { key: key || 'default', ...props, }; } const symbolBindings = {}; const bindings = rewriteHandlers(file, handlers, child.bindings, symbolBindings); this.jsxBegin(childName, props, { ...bindings, ...parentSymbolBindings, ...specialBindings, }); renderJSXNodes(file, directives, handlers, child.children, styles, key, symbolBindings, false).call(this); this.jsxEnd(childName); } } }); if (needsFragment) { this.jsxEndFragment(); } if (root) this.emit(')'); function mitosisNodeToRenderBlock(children) { return () => { children = children.filter((c) => !isEmptyTextNode(c)); const childNeedsFragment = children.length > 1 || (children.length && isTextNode(children[0])); childNeedsFragment && srcBuilder.jsxBeginFragment(fragmentSymbol); renderJSXNodes(file, directives, handlers, children, styles, null, {}, false).call(srcBuilder); childNeedsFragment && srcBuilder.jsxEndFragment(); }; } }; } exports.renderJSXNodes = renderJSXNodes; function includedHelperDirectives(directive, directives) { Array.from(directive.matchAll(/(__[\w]+__)/g)).forEach((match) => { const name = match[0]; const code = directives_1.DIRECTIVES[name]; typeof code == 'string' && directives.set(name, code); }); } function isSymbol(name) { return (name.charAt(0) === name.charAt(0).toUpperCase() && // we want to exclude any property access, as that can't be a symbol !name.includes('.')); } function addClass(className, existingClass) { return [className, ...(existingClass ? existingClass.split(' ') : [])].join(' '); } function isEmptyTextNode(child) { var _a; return ((_a = child.properties._text) === null || _a === void 0 ? void 0 : _a.trim()) == ''; } function isTextNode(child) { var _a; if (child.properties._text !== undefined) { return true; } const code = (_a = child.bindings._text) === null || _a === void 0 ? void 0 : _a.code; if (code !== undefined && code !== 'props.children') { return true; } return false; } function isSlotProjection(child) { var _a; return ((_a = child.bindings._text) === null || _a === void 0 ? void 0 : _a.code) === 'props.children'; } /** * Rewrites bindings: * - Remove `css` * - Rewrites event handles * - Extracts symbol bindings. * * @param file * @param handlers * @param bindings * @param symbolBindings Options record which will receive the symbol bindings * @returns */ function rewriteHandlers(file, handlers, bindings, symbolBindings) { const outBindings = {}; for (let key in bindings) { if (Object.prototype.hasOwnProperty.call(bindings, key)) { const bindingValue = bindings[key]; let bindingExpr = bindingValue.code; const handlerBlock = handlers.get(bindingExpr); if (key == 'css') { continue; } else if (handlerBlock) { key = `${key}$`; bindingExpr = (0, src_generator_1.invoke)(file.import(file.qwikModule, 'qrl'), [ (0, src_generator_1.quote)(file.qrlPrefix + 'high.js'), (0, src_generator_1.quote)(handlerBlock), file.options.isBuilder ? '[s,l]' : '[state]', ]); } else if (symbolBindings && key.startsWith('symbol.data.')) { symbolBindings[(0, src_generator_1.lastProperty)(key)] = bindingExpr; } else if (key.startsWith('component.options.')) { key = (0, src_generator_1.lastProperty)(key); } outBindings[key] = { ...bindingValue, code: bindingExpr, }; } } return outBindings; } function isInlinedDirective(node) { return ((0, is_mitosis_node_1.isMitosisNode)(node) && node.name == 'Show') || node.name == 'For'; }