@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
155 lines (154 loc) • 7.1 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.angularToMitosisComponent = void 0;
const compiler_1 = require("@angular/compiler");
const core_1 = require("@babel/core");
const lodash_1 = require("lodash");
const typescript_1 = __importDefault(require("typescript"));
const babel_transform_1 = require("../helpers/babel-transform");
const bindings_1 = require("../helpers/bindings");
const capitalize_1 = require("../helpers/capitalize");
const create_mitosis_component_1 = require("../helpers/create-mitosis-component");
const create_mitosis_node_1 = require("../helpers/create-mitosis-node");
const getTsAST = (code) => {
return typescript_1.default.createSourceFile('code.ts', code, typescript_1.default.ScriptTarget.Latest, true);
};
const transformBinding = (binding, _options) => {
return (0, babel_transform_1.babelTransformCode)(binding, {
Identifier(path) {
const name = path.node.name;
if ((core_1.types.isObjectProperty(path.parent) && path.parent.key === path.node) ||
(core_1.types.isMemberExpression(path.parent) && path.parent.property === path.node)) {
return;
}
if (!(name.startsWith('state.') || name === 'event' || name === '$event')) {
path.replaceWith(core_1.types.identifier(`state.${name}`));
}
},
});
};
const isElement = (node) =>
// TODO: theres got to be a better way than this
Array.isArray(node.attributes);
const isTemplate = (node) =>
// TODO: theres got to be a better way than this
Array.isArray(node.templateAttrs);
const isText = (node) => typeof node.value === 'string';
const isBoundText = (node) => typeof node.value === 'object';
const angularTemplateNodeToMitosisNode = (node, options) => {
if (isTemplate(node)) {
const ngIf = node.templateAttrs.find((item) => item.name === 'ngIf');
if (ngIf) {
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'Show',
bindings: {
when: (0, bindings_1.createSingleBinding)({
code: transformBinding(ngIf.value.source, options),
}),
},
children: [angularTemplateNodeToMitosisNode((0, lodash_1.omit)(node, 'templateAttrs'), options)],
});
}
const ngFor = node.templateAttrs.find((item) => item.name === 'ngFor');
if (ngFor) {
const value = ngFor.value.source;
const split = value.split(/let\s|\sof\s/);
const [_let, itemName, _of, expression] = split;
return (0, create_mitosis_node_1.createMitosisNode)({
name: 'For',
bindings: {
each: (0, bindings_1.createSingleBinding)({ code: transformBinding(expression, options) }),
},
scope: {
forName: itemName,
},
children: [angularTemplateNodeToMitosisNode((0, lodash_1.omit)(node, 'templateAttrs'), options)],
});
}
}
if (isElement(node)) {
const properties = {};
const bindings = {};
for (const input of node.inputs) {
bindings[input.name] = (0, bindings_1.createSingleBinding)({
code: transformBinding(input.value.source, options),
});
}
for (const output of node.outputs) {
bindings['on' + (0, capitalize_1.capitalize)(output.name)] = (0, bindings_1.createSingleBinding)({
code: transformBinding(output.handler
.source // TODO: proper reference replace
.replace(/\$event/g, 'event'), options),
});
}
for (const attribute of node.attributes) {
properties[attribute.name] = attribute.value;
}
return (0, create_mitosis_node_1.createMitosisNode)({
name: node.name,
properties,
bindings: bindings,
children: node.children.map((node) => angularTemplateNodeToMitosisNode(node, options)),
});
}
if (isText(node)) {
return (0, create_mitosis_node_1.createMitosisNode)({
properties: {
_text: node.value,
},
});
}
if (isBoundText(node)) {
// TODO: handle the bindings
return (0, create_mitosis_node_1.createMitosisNode)({
properties: {
_text: node.value.source,
},
});
}
throw new Error(`Element node type {${node}} is not supported`);
};
const angularTemplateToMitosisNodes = (template, options) => {
const ast = (0, compiler_1.parseTemplate)(template, '.');
const blocks = ast.nodes.map((node) => angularTemplateNodeToMitosisNode(node, options));
return blocks;
};
const parseTypescript = (code, options) => {
const component = (0, create_mitosis_component_1.createMitosisComponent)();
const ast = getTsAST(code);
for (const statement of ast.statements) {
if (typescript_1.default.isClassDeclaration(statement)) {
const decorators = typescript_1.default.canHaveDecorators(statement) ? typescript_1.default.getDecorators(statement) : undefined;
if (decorators) {
for (const decorator of decorators) {
// TODO: proper reference tracing
if (typescript_1.default.isCallExpression(decorator.expression))
if (typescript_1.default.isIdentifier(decorator.expression.expression) &&
decorator.expression.expression.text === 'Component') {
const firstArg = decorator.expression.arguments[0];
if (typescript_1.default.isObjectLiteralExpression(firstArg)) {
firstArg.properties.find((item) => {
if (typescript_1.default.isPropertyAssignment(item)) {
if (typescript_1.default.isIdentifier(item.name) && item.name.text === 'template') {
if (typescript_1.default.isTemplateLiteral(item.initializer)) {
const template = item.initializer.getText().trim().slice(1, -1);
component.children = angularTemplateToMitosisNodes(template, options);
}
}
}
});
}
}
}
}
}
}
return component;
};
function angularToMitosisComponent(code, options = {}) {
return parseTypescript(code, options);
}
exports.angularToMitosisComponent = angularToMitosisComponent;
;