UNPKG

@builder.io/mitosis

Version:

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

361 lines (360 loc) 18.7 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.componentFunctionToJson = void 0; const babel = __importStar(require("@babel/core")); const generator_1 = __importDefault(require("@babel/generator")); const types_1 = require("@babel/types"); const hooks_1 = require("../../constants/hooks"); const create_mitosis_component_1 = require("../../helpers/create-mitosis-component"); const get_bindings_1 = require("../../helpers/get-bindings"); const trace_reference_to_module_path_1 = require("../../helpers/trace-reference-to-module-path"); const component_types_1 = require("./component-types"); const element_parser_1 = require("./element-parser"); const helpers_1 = require("./helpers"); const hooks_2 = require("./hooks"); const helpers_2 = require("./hooks/helpers"); const state_1 = require("./state"); const { types } = babel; /** * Parses function declarations within the Mitosis copmonent's body to JSON */ const componentFunctionToJson = (node, context, stateToScope) => { var _a; const hooks = { onMount: [], onEvent: [], }; const state = {}; const accessedContext = {}; const setContext = {}; const refs = {}; for (const item of node.body.body) { if (types.isExpressionStatement(item)) { const expression = item.expression; if (types.isCallExpression(expression) && types.isIdentifier(expression.callee)) { switch (expression.callee.name) { case hooks_1.HOOKS.SET_CONTEXT: { const keyNode = expression.arguments[0]; const valueNode = expression.arguments[1]; if (types.isIdentifier(keyNode)) { const key = keyNode.name; const keyPath = (0, trace_reference_to_module_path_1.traceReferenceToModulePath)(context.builder.component.imports, key); if (valueNode) { if (types.isObjectExpression(valueNode)) { const value = (0, state_1.parseStateObjectToMitosisState)(valueNode); setContext[keyPath] = { name: keyNode.name, value, }; } else { const ref = (0, generator_1.default)(valueNode).code; setContext[keyPath] = { name: keyNode.name, ref, }; } } } else if (types.isStringLiteral(keyNode)) { if (types.isExpression(valueNode)) { setContext[keyNode.value] = { name: `"${keyNode.value}"`, ref: (0, generator_1.default)(valueNode).code, }; } } break; } case hooks_1.HOOKS.MOUNT: { const firstArg = expression.arguments[0]; const hookOptions = expression.arguments[1]; if (types.isFunctionExpression(firstArg) || types.isArrowFunctionExpression(firstArg)) { const code = (0, helpers_2.processHookCode)(firstArg); let onSSR = false; if (types.isObjectExpression(hookOptions)) { const onSSRProp = hookOptions.properties.find((property) => types.isProperty(property) && types.isIdentifier(property.key) && property.key.name === 'onSSR'); if (types.isObjectProperty(onSSRProp) && types.isBooleanLiteral(onSSRProp.value)) { onSSR = onSSRProp.value.value; } } hooks.onMount.push({ code, onSSR, }); } break; } case hooks_1.HOOKS.EVENT: { const firstArg = expression.arguments[0]; const secondArg = expression.arguments[1]; const thirdArg = expression.arguments[2]; const fourthArg = expression.arguments[3]; if (!types.isStringLiteral(firstArg)) { console.warn('`onEvent` hook skipped. Event name must be a string literal: ', (0, generator_1.default)(expression).code); break; } if (!types.isFunctionExpression(secondArg) && !types.isArrowFunctionExpression(secondArg)) { console.warn('`onEvent` hook skipped. Event handler must be a function: ', (0, generator_1.default)(expression).code); break; } if (!types.isIdentifier(thirdArg)) { console.warn('`onEvent` hook skipped. Element ref must be a value: ', (0, generator_1.default)(expression).code); break; } const isRoot = types.isBooleanLiteral(fourthArg) ? fourthArg.value : false; const eventArgName = types.isIdentifier(secondArg.params[0]) ? secondArg.params[0].name : 'event'; const elementArgName = types.isIdentifier(secondArg.params[1]) ? secondArg.params[1].name : 'element'; hooks.onEvent.push({ eventName: firstArg.value, code: (0, helpers_2.processHookCode)(secondArg), refName: thirdArg.name, isRoot, eventArgName, elementArgName, }); break; } case hooks_1.HOOKS.UPDATE: { const firstArg = expression.arguments[0]; const secondArg = expression.arguments[1]; if (types.isFunctionExpression(firstArg) || types.isArrowFunctionExpression(firstArg)) { const code = (0, helpers_2.processHookCode)(firstArg); if (!secondArg || (types.isArrayExpression(secondArg) && secondArg.elements.length > 0)) { const depsCode = secondArg ? (0, generator_1.default)(secondArg).code : ''; const depsArray = []; if (secondArg && secondArg.elements) { for (const element of secondArg.elements) { if ((0, types_1.isIdentifier)(element)) { depsArray.push(element.name); } else if ((0, types_1.isMemberExpression)(element) && (0, types_1.isIdentifier)(element.object) && (0, types_1.isIdentifier)(element.property)) { depsArray.push(`${element.object.name}.${element.property.name}`); } } } hooks.onUpdate = [ ...(hooks.onUpdate || []), { code, deps: depsCode, depsArray, }, ]; } } break; } case hooks_1.HOOKS.UNMOUNT: { const firstArg = expression.arguments[0]; if (types.isFunctionExpression(firstArg) || types.isArrowFunctionExpression(firstArg)) { const code = (0, helpers_2.processHookCode)(firstArg); hooks.onUnMount = { code }; } break; } case hooks_1.HOOKS.INIT: { const firstArg = expression.arguments[0]; if (types.isFunctionExpression(firstArg) || types.isArrowFunctionExpression(firstArg)) { const code = (0, helpers_2.processHookCode)(firstArg); hooks.onInit = { code }; } break; } case hooks_1.HOOKS.DEFAULT_PROPS: { (0, hooks_2.parseDefaultPropsHook)(context.builder.component, expression); break; } case hooks_1.HOOKS.STYLE: { context.builder.component.style = (0, hooks_2.generateUseStyleCode)(expression); break; } case hooks_1.HOOKS.METADATA: { context.builder.component.meta[hooks_1.HOOKS.METADATA] = { ...context.builder.component.meta[hooks_1.HOOKS.METADATA], ...(0, helpers_1.parseCodeJson)(expression.arguments[0]), }; break; } } } } if (types.isFunctionDeclaration(item)) { if (types.isIdentifier(item.id)) { state[item.id.name] = { code: (0, generator_1.default)(item).code, type: 'function', }; stateToScope.push(item.id.name); } } if (types.isVariableDeclaration(item)) { const declaration = item.declarations[0]; const init = declaration.init; if (types.isCallExpression(init) && types.isIdentifier(init.callee)) { // React format, like: // const [foo, setFoo] = useState(...) if (types.isArrayPattern(declaration.id) && init.callee.name === hooks_1.HOOKS.STATE) { const varName = types.isIdentifier(declaration.id.elements[0]) && declaration.id.elements[0].name; if (varName) { const value = init.arguments[0]; // Function as init, like: // useState(() => true) if (types.isArrowFunctionExpression(value)) { state[varName] = { code: (0, helpers_1.parseCode)(value.body), type: 'function', }; } else { const stateOptions = init.arguments[1]; let propertyType = 'normal'; if (types.isObjectExpression(stateOptions)) { for (const prop of stateOptions.properties) { if (!types.isProperty(prop) || !types.isIdentifier(prop.key)) continue; const isReactive = prop.key.name === 'reactive'; if (isReactive && types.isBooleanLiteral(prop.value) && prop.value.value) { propertyType = 'reactive'; } } } // Value as init, like: // useState(true) state[varName] = { code: (0, helpers_1.parseCode)(value), type: 'property', propertyType, }; } stateToScope.push(varName); // Typescript Parameter if (types.isTSTypeParameterInstantiation(init.typeParameters)) { state[varName].typeParameter = (0, generator_1.default)(init.typeParameters.params[0]).code; } } } else if (init.callee.name === hooks_1.HOOKS.STORE) { const firstArg = init.arguments[0]; if (types.isObjectExpression(firstArg)) { const useStoreState = (0, state_1.parseStateObjectToMitosisState)(firstArg); Object.assign(state, useStoreState); const stateKeys = Object.keys(useStoreState); if (types.isTSTypeParameterInstantiation(init.typeParameters)) { const type = (0, generator_1.default)(init.typeParameters.params[0]); // Type for store has to be an object so we can use it like this for (const key of stateKeys) { state[key].typeParameter = `${type.code}["${key}"]`; } } } } else if (init.callee.name === hooks_1.HOOKS.CONTEXT) { const firstArg = init.arguments[0]; if (types.isVariableDeclarator(declaration) && types.isIdentifier(declaration.id)) { if (types.isIdentifier(firstArg)) { const varName = declaration.id.name; const name = firstArg.name; accessedContext[varName] = { name, path: (0, trace_reference_to_module_path_1.traceReferenceToModulePath)(context.builder.component.imports, name), }; } else { const varName = declaration.id.name; const name = (0, generator_1.default)(firstArg).code; accessedContext[varName] = { name, path: '', }; } } } else if (init.callee.name === hooks_1.HOOKS.REF) { if (types.isIdentifier(declaration.id)) { const firstArg = init.arguments[0]; const varName = declaration.id.name; refs[varName] = { argument: (0, generator_1.default)(firstArg).code, }; // Typescript Parameter if (types.isTSTypeParameterInstantiation(init.typeParameters)) { refs[varName].typeParameter = (0, generator_1.default)(init.typeParameters.params[0]).code; } } } } } } const theReturn = node.body.body.find((item) => types.isReturnStatement(item)); const children = []; if (theReturn) { const value = theReturn.argument; if (types.isJSXElement(value) || types.isJSXFragment(value)) { const jsxElement = (0, element_parser_1.jsxElementToJson)(value); if (jsxElement) { children.push(jsxElement); } } } const { exports: localExports } = context.builder.component; if (localExports) { const bindingsCode = (0, get_bindings_1.getBindingsCode)(children); Object.keys(localExports).forEach((name) => { const found = bindingsCode.find((code) => code.match(new RegExp(`\\b${name}\\b`))); localExports[name].usedInLocal = Boolean(found); }); context.builder.component.exports = localExports; } const propsTypeRef = (0, component_types_1.getPropsTypeRef)(node, context); return (0, create_mitosis_component_1.createMitosisComponent)({ ...context.builder.component, name: (_a = node.id) === null || _a === void 0 ? void 0 : _a.name, state, children, refs: refs, hooks, context: { get: accessedContext, set: setContext, }, propsTypeRef, }); }; exports.componentFunctionToJson = componentFunctionToJson;