@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
225 lines (224 loc) • 12.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseElement = void 0;
const astring_1 = require("astring");
const lodash_1 = require("lodash");
const children_1 = require("../helpers/children");
const mitosis_node_1 = require("../helpers/mitosis-node");
const string_1 = require("../helpers/string");
const actions_1 = require("./actions");
const bindings_1 = require("../../../helpers/bindings");
const SPECIAL_ELEMENTS = new Set(['svelte:component', 'svelte:element']);
function parseElement(json, node) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
const mitosisNode = (0, mitosis_node_1.createMitosisNode)();
mitosisNode.name = node.name;
const useReference = () => {
const nodeReference = (0, string_1.uniqueName)(Object.keys(json.refs), node.name);
if (!Object.keys(json.refs).includes(nodeReference)) {
json.refs[nodeReference] = { argument: '', typeParameter: '' };
mitosisNode.bindings.ref = (0, bindings_1.createSingleBinding)({ code: nodeReference });
}
return nodeReference;
};
/*
Parse special elements such as svelte:component and svelte:element
*/
if (SPECIAL_ELEMENTS.has(node.name)) {
const expression = (0, astring_1.generate)(node.expression || node.tag);
let prefix = 'state';
if (json.props[expression]) {
prefix = 'props';
}
mitosisNode.name = `${prefix}.${expression}`;
}
if ((_a = node.attributes) === null || _a === void 0 ? void 0 : _a.length) {
for (const attribute of node.attributes) {
switch (attribute.type) {
case 'Attribute': {
switch ((_b = attribute.value[0]) === null || _b === void 0 ? void 0 : _b.type) {
case 'Text': {
const value = attribute.value[0];
// if there are already conditional class declarations
// add class names defined here to the bindings code as well
if (attribute.name === 'class' && ((_d = (_c = mitosisNode.bindings.class) === null || _c === void 0 ? void 0 : _c.code) === null || _d === void 0 ? void 0 : _d.length)) {
mitosisNode.bindings.class.code = (0, string_1.insertAt)(mitosisNode.bindings.class.code, ` ${value.data} `, 1);
}
else {
mitosisNode.properties[attribute.name] = value.data;
}
break;
}
case 'MustacheTag': {
const value = attribute.value[0];
const expression = value.expression;
let code = (0, astring_1.generate)(expression);
if (attribute.name === 'class') {
code = ((_f = (_e = mitosisNode.bindings.class) === null || _e === void 0 ? void 0 : _e.code) === null || _f === void 0 ? void 0 : _f.length)
? (0, string_1.insertAt)(mitosisNode.bindings.class.code, ' ${' + code + '}', mitosisNode.bindings.class.code.length - 1)
: '`${' + code + '}`';
}
mitosisNode.bindings[attribute.name] = (0, bindings_1.createSingleBinding)({ code });
break;
}
case 'AttributeShorthand': {
// e.g. <input {value}/>
const value = attribute.value[0];
const code = value.expression.name;
mitosisNode.bindings[code] = (0, bindings_1.createSingleBinding)({ code });
break;
}
default: {
const name = attribute.name;
mitosisNode.bindings[name] = (0, bindings_1.createSingleBinding)({
code: attribute.value.toString(),
});
}
}
break;
}
case 'Spread': {
const expression = attribute.expression;
mitosisNode.bindings[expression.name] = {
code: expression.name,
type: 'spread',
spreadType: 'normal',
};
break;
}
case 'EventHandler': {
let object = {
code: '',
arguments: [],
};
if (((_g = attribute.expression) === null || _g === void 0 ? void 0 : _g.type) === 'ArrowTypeFunction') {
const expression = attribute.expression;
let code = (0, astring_1.generate)(expression.body);
object = {
code,
arguments: (_j = (_h = expression.body) === null || _h === void 0 ? void 0 : _h.arguments) === null || _j === void 0 ? void 0 : _j.map((a) => { var _a; return (_a = a.name) !== null && _a !== void 0 ? _a : []; }),
};
}
else if (attribute.expression) {
let code = (0, astring_1.generate)(attribute.expression);
if (((_k = attribute.expression.body) === null || _k === void 0 ? void 0 : _k.type) === 'CallExpression') {
code = (0, astring_1.generate)(attribute.expression.body);
}
if (!code.startsWith(')') && !code.endsWith(')')) {
code += '()';
}
if (!((_l = attribute.expression.arguments) === null || _l === void 0 ? void 0 : _l.length) &&
!((_o = (_m = attribute.expression.body) === null || _m === void 0 ? void 0 : _m.arguments) === null || _o === void 0 ? void 0 : _o.length)) {
code = code.replace(/\(\)/g, '(event)');
}
let args = undefined;
if (attribute.expression.type === 'ArrowFunctionExpression') {
args = (_q = (_p = attribute.expression.params) === null || _p === void 0 ? void 0 : _p.map((arg) => (0, astring_1.generate)(arg))) !== null && _q !== void 0 ? _q : [];
}
else if (attribute.expression.type === 'CallExpression' &&
attribute.expression.arguments.length) {
args = [];
}
object = {
code,
arguments: args,
};
}
else {
object = {
code: `props.on${(0, lodash_1.upperFirst)(attribute.name)}(event)`,
arguments: ['event'],
};
}
mitosisNode.bindings[`on${(0, lodash_1.upperFirst)(attribute.name)}`] = (0, bindings_1.createSingleBinding)(object);
// add event handlers as props (e.g. props.onClick)
json.props = {
...json.props,
[`on${(0, lodash_1.upperFirst)(attribute.name)}`]: { default: () => ({}) },
};
break;
}
case 'Binding': {
/*
adding onChange handlers for bind:group and bind:property is done during post processing
same goes for replacing the group binding with checked
see helpers/post-process.ts
*/
const expression = attribute.expression;
const binding = expression.name;
let name = attribute.name;
// template ref
if (attribute.name === 'this') {
name = 'ref';
json.refs[binding] = {
argument: 'null',
typeParameter: 'any',
};
if (Object.prototype.hasOwnProperty.call(json.state, binding)) {
delete json.state[binding];
}
}
if (name !== 'ref' && name !== 'group' && name !== 'this') {
const onChangeCode = `${binding} = event.target.value`;
mitosisNode.bindings['onChange'] = (0, bindings_1.createSingleBinding)({
code: onChangeCode,
arguments: ['event'],
});
}
mitosisNode.bindings[name] = (0, bindings_1.createSingleBinding)({
code: binding,
});
break;
}
case 'Class': {
const expression = attribute.expression;
// conditional classes (e.g. class:disabled or class:disabled={disabled})
const binding = `${(0, astring_1.generate)(expression)} ? '${attribute.name}' : ''`;
let code = '';
// if there are existing class declarations
// add them here instead and remove them from properties
// to avoid duplicate class declarations in certain frameworks
if ((_s = (_r = mitosisNode.properties) === null || _r === void 0 ? void 0 : _r.class) === null || _s === void 0 ? void 0 : _s.length) {
code = `${mitosisNode.properties.class} `;
delete mitosisNode.properties.class;
}
// if class code is already defined (meaning there is more than 1 conditional class declaration)
// append it to the string instead of assigning it
if (mitosisNode.bindings.class &&
Object.prototype.hasOwnProperty.call(mitosisNode.bindings.class, 'code') &&
((_t = mitosisNode.bindings.class) === null || _t === void 0 ? void 0 : _t.code.length)) {
code = (0, string_1.insertAt)(mitosisNode.bindings.class.code, ' ${' + binding + '}', mitosisNode.bindings.class.code.length - 1);
mitosisNode.bindings.class = (0, bindings_1.createSingleBinding)({ code });
}
else {
// otherwise just assign
code = '`' + code + '${' + binding + '}`';
mitosisNode.bindings.class = (0, bindings_1.createSingleBinding)({ code });
}
break;
}
case 'Action': {
(0, actions_1.parseAction)(json, useReference(), attribute);
break;
}
// No default
}
}
}
let filteredChildren = [];
if (node.children) {
filteredChildren = (0, children_1.filterChildren)(node.children);
}
if (filteredChildren.length === 1 && filteredChildren[0].type === 'RawMustacheTag') {
const child = filteredChildren[0];
mitosisNode.children = [];
mitosisNode.bindings.innerHTML = (0, bindings_1.createSingleBinding)({
code: (0, astring_1.generate)(child.expression),
});
}
else {
mitosisNode.children = (0, children_1.parseChildren)(json, node);
}
return mitosisNode;
}
exports.parseElement = parseElement;
;