UNPKG

@builder.io/mitosis

Version:

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

180 lines (179 loc) 9.04 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSignalAccessPlugin = exports.getSignalTypePlugin = exports.replaceSignalSetters = void 0; const core_1 = require("@babel/core"); const generator_1 = __importDefault(require("@babel/generator")); const babel_transform_1 = require("../babel-transform"); const capitalize_1 = require("../capitalize"); const nullable_1 = require("../nullable"); const replace_identifiers_1 = require("../replace-identifiers"); const signals_1 = require("../signals"); const process_code_1 = require("./process-code"); const replaceSignalSetters = ({ code, nodeMaps, }) => { for (const { from, setTo } of nodeMaps) { code = (0, babel_transform_1.babelTransformExpression)(code, { AssignmentExpression(path) { if (path.node.operator !== '=') return; const lhs = path.node.left; const rhs = path.node.right; if (!core_1.types.isMemberExpression(lhs)) return; if (!(core_1.types.isObjectExpression(rhs) || core_1.types.isIdentifier(rhs))) return; const signalAccess = lhs.object; if (!core_1.types.isMemberExpression(signalAccess)) return; if ((0, generator_1.default)(signalAccess).code !== (0, generator_1.default)(from).code) return; /** * Go from: * a.b.c.value.d = e * * to: * a.b.setC((PREVIOUS_VALUE) => ({ ...PREVIOUS_VALUE, d: e })) */ const setter = core_1.types.cloneNode(setTo); // TO-DO: replace all `value` references inside of the set logic with `PREVIOUS_VALUE`. const prevValueIdentifier = core_1.types.identifier('PREVIOUS_VALUE'); const setFn = core_1.types.arrowFunctionExpression([prevValueIdentifier], core_1.types.objectExpression([ core_1.types.spreadElement(prevValueIdentifier), core_1.types.objectProperty(lhs.property, rhs), ])); const setterExpression = core_1.types.callExpression(setter, [setFn]); path.replaceWith(setterExpression); }, }); } return code; }; exports.replaceSignalSetters = replaceSignalSetters; /** * Processes `Signal` type imports, transforming them to the target's equivalent and adding the import to the component. */ const getSignalTypePlugin = ({ target }) => () => ({ json: { pre: (json) => { var _a; (0, process_code_1.createCodeProcessorPlugin)((codeType, json) => { switch (codeType) { // Skip these for now because they break for svelte: `<svelte:element>` becomes `<svelte: element>`. // Besides, fairly impossible to endup with a Signal generic there like `<MyComponent<Signal<number>> />`. case 'dynamic-jsx-elements': return (x) => x; default: return (code) => { var _a; if ((_a = json.signals) === null || _a === void 0 ? void 0 : _a.signalTypeImportName) { return (0, signals_1.mapSignalType)({ code, signalImportName: json.signals.signalTypeImportName, target, }); } return code; }; } })(json); if ((_a = json.signals) === null || _a === void 0 ? void 0 : _a.signalTypeImportName) { json.imports = json.imports || []; const signalMappedImport = (0, signals_1.getSignalMitosisImportForTarget)(target); if (signalMappedImport) { json.imports.push(signalMappedImport); } } }, }, }); exports.getSignalTypePlugin = getSignalTypePlugin; const getSignalMapperForTarget = (target) => { switch (target) { case 'svelte': return { getter: (name) => core_1.types.identifier('$' + name), }; case 'preact': case 'reactNative': case 'react': case 'solid': return { getter: (name) => core_1.types.identifier(name), setter: (name) => core_1.types.identifier('set' + (0, capitalize_1.capitalize)(name)), }; default: // default case: strip the `.value` accessor return { getter: (name) => core_1.types.identifier(name), }; } }; /** * Processes `mySignal.value` accessors for props, context, and state. */ const getSignalAccessPlugin = ({ target }) => () => ({ json: { pre: (x) => { return (0, process_code_1.createCodeProcessorPlugin)((_codeType, json) => (code) => { var _a; const mapSignal = getSignalMapperForTarget(target); const nodeMaps = []; for (const propName in json.props) { if (json.props[propName].propertyType === 'reactive') { const getter = core_1.types.memberExpression(core_1.types.identifier('props'), mapSignal.getter(propName)); const setter = mapSignal.setter ? core_1.types.memberExpression(core_1.types.identifier('props'), mapSignal.setter(propName)) : undefined; nodeMaps.push({ from: core_1.types.memberExpression(core_1.types.memberExpression(core_1.types.identifier('props'), core_1.types.identifier(propName)), core_1.types.identifier('value')), to: getter, setTo: setter, }); nodeMaps.push({ from: core_1.types.optionalMemberExpression(core_1.types.memberExpression(core_1.types.identifier('props'), core_1.types.identifier(propName)), core_1.types.identifier('value'), false, true), to: getter, setTo: setter, }); } } for (const propName in json.context.get) { if (json.context.get[propName].type === 'reactive') { nodeMaps.push({ from: core_1.types.memberExpression(core_1.types.identifier(propName), core_1.types.identifier('value')), to: mapSignal.getter(propName), setTo: mapSignal.setter ? mapSignal.setter(propName) : undefined, }); } } for (const propName in json.state) { if (((_a = json.state[propName]) === null || _a === void 0 ? void 0 : _a.propertyType) === 'reactive') { const to = core_1.types.memberExpression(core_1.types.identifier('state'), mapSignal.getter(propName)); const setTO = mapSignal.setter ? mapSignal.setter(propName) : undefined; nodeMaps.push({ from: core_1.types.memberExpression(core_1.types.memberExpression(core_1.types.identifier('state'), core_1.types.identifier(propName)), core_1.types.identifier('value')), to: to, setTo: setTO, }); nodeMaps.push({ from: core_1.types.optionalMemberExpression(core_1.types.memberExpression(core_1.types.identifier('state'), core_1.types.identifier(propName)), core_1.types.identifier('value'), false, true), to: to, setTo: setTO, }); } } const filteredNodeMaps = nodeMaps.filter((x) => (0, nullable_1.checkIsDefined)(x.setTo)); // we run state-setter replacement first, because otherwise the other one will catch it. if (filteredNodeMaps.length) { code = (0, exports.replaceSignalSetters)({ code, nodeMaps: filteredNodeMaps }); } if (nodeMaps.length) { code = (0, replace_identifiers_1.replaceNodes)({ code, nodeMaps }); } return code; })(x); }, }, }); exports.getSignalAccessPlugin = getSignalAccessPlugin;