@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
195 lines (194 loc) • 9.44 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCodeProcessorPlugins = void 0;
const babel_transform_1 = require("../../../../helpers/babel-transform");
const class_components_1 = require("../../../../helpers/class-components");
const event_handlers_1 = require("../../../../helpers/event-handlers");
const process_code_1 = require("../../../../helpers/plugins/process-code");
const mitosis_node_1 = require("../../../../types/mitosis-node");
const types_1 = require("@babel/types");
const isStateOrPropsExpression = (path) => {
return ((0, types_1.isMemberExpression)(path.node) &&
(0, types_1.isIdentifier)(path.node.object) &&
(0, types_1.isIdentifier)(path.node.property) &&
(path.node.object.name === 'props' || path.node.object.name === 'state'));
};
const handleAssignmentExpression = (path) => {
if ((0, types_1.isMemberExpression)(path.node.left) &&
(0, types_1.isIdentifier)(path.node.left.object) &&
(0, types_1.isIdentifier)(path.node.left.property) &&
path.node.left.object.name === 'state') {
const root = (0, types_1.memberExpression)(path.node.left, (0, types_1.identifier)('set'));
root.extra = { ...root.extra, updateExpression: true };
const call = (0, types_1.callExpression)(root, [path.node.right]);
path.replaceWith(call);
}
};
const handleMemberExpression = (path) => {
var _a, _b, _c, _d;
if (((_a = path.node.extra) === null || _a === void 0 ? void 0 : _a.makeCallExpressionDone) || ((_c = (_b = path.parentPath) === null || _b === void 0 ? void 0 : _b.node.extra) === null || _c === void 0 ? void 0 : _c.updateExpression)) {
// Don't add a function if we've done it already
return;
}
if ((0, types_1.isCallExpression)(path.parent) &&
(0, types_1.isMemberExpression)(path.parent.callee) &&
(0, types_1.isIdentifier)(path.parent.callee.object) &&
(path.parent.callee.object.name === 'props' || path.parent.callee.object.name === 'state') &&
!((_d = path.parent.callee.extra) === null || _d === void 0 ? void 0 : _d.updateExpression)) {
// Don't add a function if it is already
return;
}
if (isStateOrPropsExpression(path)) {
path.node.extra = { ...path.node.extra, makeCallExpressionDone: true };
path.replaceWith((0, types_1.callExpression)(path.node, []));
}
};
const handleHookAndStateOnEvents = (path, isHookDepArray) => {
if ((0, types_1.isIdentifier)(path.node.property) && (0, event_handlers_1.checkIsEvent)(path.node.property.name)) {
if ((0, types_1.isIfStatement)(path.parent)) {
// We don't do anything if the event is in an IfStatement
path.node.extra = { ...path.node.extra, updateExpression: true };
return true;
}
else if ((0, types_1.isCallExpression)(path.parent) &&
(0, types_1.isIdentifier)(path.node.object) &&
(0, types_1.isMemberExpression)(path.parent.callee)) {
// We add "emit" to events
const root = (0, types_1.memberExpression)(path.node, (0, types_1.identifier)('emit'));
root.extra = { ...root.extra, updateExpression: true };
path.replaceWith(root);
}
else if (isHookDepArray && (0, types_1.isIdentifier)(path.node.object)) {
const iden = (0, types_1.identifier)(`// "${path.node.object.name}.${path.node.property.name}" is an event skip it.`);
path.replaceWith(iden);
return true;
}
}
return false;
};
const transformHooksAndState = (code, isHookDepArray) => {
return (0, babel_transform_1.babelTransformExpression)(code, {
AssignmentExpression(path) {
handleAssignmentExpression(path);
},
UpdateExpression(path) {
/*
* If we have a function like this:
* `state._counter++;`
*
* We need to convert it and use the "update" example from https://angular.dev/guide/signals#writable-signals:
* `state._counter.update(_counter=>_counter++)`
*
*/
if ((0, types_1.isMemberExpression)(path.node.argument) &&
(0, types_1.isIdentifier)(path.node.argument.object) &&
path.node.argument.object.name === 'state' &&
(0, types_1.isIdentifier)(path.node.argument.property)) {
const root = (0, types_1.memberExpression)(path.node.argument, (0, types_1.identifier)('update'));
root.extra = { ...root.extra, updateExpression: true };
const argument = path.node.argument.property;
const block = (0, types_1.blockStatement)([
(0, types_1.expressionStatement)((0, types_1.updateExpression)(path.node.operator, argument)),
(0, types_1.returnStatement)(argument),
]);
const arrowFunction = (0, types_1.arrowFunctionExpression)([argument], block);
const call = (0, types_1.callExpression)(root, [arrowFunction]);
path.replaceWith(call);
}
},
MemberExpression(path) {
const skip = handleHookAndStateOnEvents(path, isHookDepArray);
if (skip) {
return;
}
handleMemberExpression(path);
},
});
};
const addToImportCall = (json, importName) => {
const isImportCall = json.imports.find((imp) => imp.imports[importName]);
const isExportCall = json.exports ? !!json.exports[importName] : false;
if (isImportCall || isExportCall) {
json.compileContext.angular.extra.importCalls.push(importName);
}
};
const transformBindings = (json, code) => {
return (0, babel_transform_1.babelTransformExpression)(code, {
BlockStatement() {
console.error(`
Component ${json.name} has a BlockStatement inside JSX'.
This will cause an error in Angular.
Please create and call a new function instead with this code:
${code}`);
},
CallExpression(path) {
// If we call a function from an import we need to add it to the Component as well
if ((0, types_1.isIdentifier)(path.node.callee)) {
addToImportCall(json, path.node.callee.name);
}
},
Identifier(path) {
// If we use a constant from an import we need to add it to the Component as well
if ((0, types_1.isIdentifier)(path.node)) {
addToImportCall(json, path.node.name);
}
},
StringLiteral(path) {
var _a;
// We cannot use " for string literal in template
if ((_a = path.node.extra) === null || _a === void 0 ? void 0 : _a.raw) {
path.node.extra.raw = path.node.extra.raw.replaceAll('"', "'");
}
},
AssignmentExpression(path) {
handleAssignmentExpression(path);
},
MemberExpression(path) {
handleMemberExpression(path);
},
});
};
const getCodeProcessorPlugins = (json, options, processBindingOptions) => {
return [
...(options.plugins || []),
(0, process_code_1.CODE_PROCESSOR_PLUGIN)((codeType) => {
switch (codeType) {
case 'bindings':
return (code, key, context) => {
var _a, _b, _c;
let replaceWith = '';
if (key === 'key') {
/**
* If we have a key attribute we need to check if it is inside a @for loop.
* We will create a new function for the key attribute.
* Therefore, we need to add "this" to state and props.
*/
const isForParent = ((_c = (_b = (_a = context === null || context === void 0 ? void 0 : context.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.node) === null || _c === void 0 ? void 0 : _c.name) === mitosis_node_1.ForNodeName;
if (isForParent) {
replaceWith = 'this.';
}
}
return (0, class_components_1.processClassComponentBinding)(json, transformBindings(json, code), {
...processBindingOptions,
replaceWith,
});
};
case 'hooks-deps-array':
case 'hooks':
case 'state':
return (code) => {
return (0, class_components_1.processClassComponentBinding)(json, transformHooksAndState(code, codeType === 'hooks-deps-array'), processBindingOptions);
};
case 'properties':
case 'hooks-deps':
case 'context-set':
case 'dynamic-jsx-elements':
case 'types':
return (code) => {
return (0, class_components_1.processClassComponentBinding)(json, code, processBindingOptions);
};
}
}),
];
};
exports.getCodeProcessorPlugins = getCodeProcessorPlugins;
;