@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
265 lines (259 loc) • 12.9 kB
JavaScript
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.componentToSolid = void 0;
const bindings_1 = require("../../helpers/bindings");
const create_mitosis_node_1 = require("../../helpers/create-mitosis-node");
const dedent_1 = require("../../helpers/dedent");
const fast_clone_1 = require("../../helpers/fast-clone");
const filter_empty_text_nodes_1 = require("../../helpers/filter-empty-text-nodes");
const get_components_used_1 = require("../../helpers/get-components-used");
const get_refs_1 = require("../../helpers/get-refs");
const get_state_object_string_1 = require("../../helpers/get-state-object-string");
const is_mitosis_node_1 = require("../../helpers/is-mitosis-node");
const is_root_text_node_1 = require("../../helpers/is-root-text-node");
const merge_options_1 = require("../../helpers/merge-options");
const nullable_1 = require("../../helpers/nullable");
const on_event_1 = require("../../helpers/on-event");
const process_code_1 = require("../../helpers/plugins/process-code");
const render_imports_1 = require("../../helpers/render-imports");
const strip_meta_properties_1 = require("../../helpers/strip-meta-properties");
const collect_css_1 = require("../../helpers/styles/collect-css");
const helpers_1 = require("../../helpers/styles/helpers");
const Array_1 = require("fp-ts/lib/Array");
const S = __importStar(require("fp-ts/string"));
const hash_sum_1 = __importDefault(require("hash-sum"));
const legacy_1 = __importDefault(require("neotraverse/legacy"));
const standalone_1 = require("prettier/standalone");
const plugins_1 = require("../../modules/plugins");
const context_1 = require("../helpers/context");
const blocks_1 = require("./blocks");
const state_1 = require("./state");
const helpers_2 = require("./state/helpers");
// Transform <foo.bar key={value} /> to <Dynamic compnent={foo.bar} key={value} />
function processDynamicComponents(json, options) {
let found = false;
(0, legacy_1.default)(json).forEach((node) => {
if ((0, is_mitosis_node_1.isMitosisNode)(node)) {
if (node.name.includes('.') && !node.name.endsWith('.Provider')) {
node.bindings.component = (0, bindings_1.createSingleBinding)({ code: node.name });
node.name = 'Dynamic';
found = true;
}
}
});
return found;
}
function getContextString(component, options) {
let str = '';
for (const key in component.context.get) {
str += `
const ${key} = useContext(${component.context.get[key].name});
`;
}
return str;
}
const getRefsString = (json, options) => Array.from((0, get_refs_1.getRefs)(json))
.map((ref) => {
var _a;
// fix prettier issue when encounter `let props.ref`
// Prettier playground: https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AEAsgJ4DCEAtgA7QIwAUVAThFQM5ICGUxAlPsAA6UfPgA2cGPmas2AOhxQq6GACU4qANzDRTSeiYj6O0fgA8i5VPx7UAXmAz2CpSvWoAhAF9RAegB8JqK8wl7CcAAeNExSACYanOhiUiTk1LSwmiAANCCsuNBsyKCcTCwA7gAKpQhFKJxi5ZzERbkARkycYADWkgDKnBRwADKKcMioDWxw7Z09-VRdigDmyDBM6DMg0xQ4axtbkVRwTDhDsA0AKidQpThwdZNi07lsKxIAiugQ8BNTWwArNgRPrvOBfH7jJBPF4gACO33glRY7GQIE4bAAtFA4HB4rEciB1pwcGIVmkKJw0Q0xIS3lBlhIAIIwdY4NoqOCVE6jHF-Z5bAAWMAoYgA6oKcPA2IswHA+rUpTgAG5S4hosBsVogZWbACSUHisD6YFOVBgTMNfRgxAk-NhMmmYs6VDRzAeJ2V41yimmMWRnGWlPtW0WTD9aJtxzYppw5sJzEUMDFOFiMEFyAAHAAGXJ6BE4PQBoNU6H-XIwThtFNpjNIABMuXQ00uVce5ZAcAobTx+OG3GW6EDcAAYhAmJTWStqSoICAvF4gA
if (ref.includes('.'))
return '';
const typeParameter = (options.typescript && ((_a = json['refs'][ref]) === null || _a === void 0 ? void 0 : _a.typeParameter)) || '';
return `let ${ref}${typeParameter ? ': ' + typeParameter : ''};`;
})
.join('\n');
function addProviderComponents(json, options) {
for (const key in json.context.set) {
const { name, value, ref } = json.context.set[key];
const bindingValue = value
? (0, bindings_1.createSingleBinding)({ code: (0, get_state_object_string_1.stringifyContextValue)(value) })
: ref
? (0, bindings_1.createSingleBinding)({ code: ref })
: undefined;
json.children = [
(0, create_mitosis_node_1.createMitosisNode)({
name: `${name}.Provider`,
children: json.children,
...(bindingValue && { bindings: { value: bindingValue } }),
}),
];
}
}
const DEFAULT_OPTIONS = {
state: 'signals',
stylesType: 'style-tag',
};
const componentToSolid = (passedOptions) => ({ component }) => {
var _a, _b, _c, _d, _e, _f, _g;
let json = (0, fast_clone_1.fastClone)(component);
const options = (0, merge_options_1.initializeOptions)({
target: 'solid',
component,
defaults: DEFAULT_OPTIONS,
userOptions: passedOptions,
});
options.plugins = [
...(options.plugins || []),
(0, on_event_1.processOnEventHooksPlugin)(),
(0, process_code_1.CODE_PROCESSOR_PLUGIN)((codeType) => {
switch (codeType) {
case 'state':
case 'context-set':
case 'dynamic-jsx-elements':
case 'types':
return (c) => c;
case 'bindings':
case 'hooks':
case 'hooks-deps':
case 'hooks-deps-array':
case 'properties':
return (0, helpers_2.updateStateCode)({
component: json,
options,
updateSetters: codeType === 'properties' ? false : true,
});
}
}),
];
if (options.plugins) {
json = (0, plugins_1.runPreJsonPlugins)({ json, plugins: options.plugins });
}
addProviderComponents(json, options);
const componentHasStyles = (0, helpers_1.hasCss)(json);
const hasCustomStyles = !!((_a = json.style) === null || _a === void 0 ? void 0 : _a.length);
const shouldInjectCustomStyles = hasCustomStyles && options.stylesType === 'styled-components';
const addWrapper = json.children.filter(filter_empty_text_nodes_1.filterEmptyTextNodes).length !== 1 ||
options.stylesType === 'style-tag' ||
shouldInjectCustomStyles ||
(0, is_root_text_node_1.isRootTextNode)(json);
// we need to run this before we run the code processor plugin, so the dynamic component variables are transformed
const foundDynamicComponents = processDynamicComponents(json, options);
if (options.plugins) {
json = (0, plugins_1.runPostJsonPlugins)({ json, plugins: options.plugins });
}
(0, strip_meta_properties_1.stripMetaProperties)(json);
const css = options.stylesType === 'style-tag' && (0, collect_css_1.collectCss)(json, { prefix: (0, hash_sum_1.default)(json) });
const state = (0, state_1.getState)({ json, options });
const componentsUsed = (0, get_components_used_1.getComponentsUsed)(json);
const hasShowComponent = componentsUsed.has('Show');
const hasForComponent = componentsUsed.has('For');
const solidJSImports = (0, Array_1.uniq)(S.Eq)([
(0, context_1.hasGetContext)(json) ? 'useContext' : undefined,
hasShowComponent ? 'Show' : undefined,
hasForComponent ? 'For' : undefined,
json.hooks.onMount.length ? 'onMount' : undefined,
...(((_b = json.hooks.onUpdate) === null || _b === void 0 ? void 0 : _b.length) ? ['on', 'createEffect', 'createMemo'] : []),
...((_c = state === null || state === void 0 ? void 0 : state.import.solidjs) !== null && _c !== void 0 ? _c : []),
].filter(nullable_1.checkIsDefined));
const storeImports = (_d = state === null || state === void 0 ? void 0 : state.import.store) !== null && _d !== void 0 ? _d : [];
const propType = json.propsTypeRef || 'any';
const propsArgs = `props${options.typescript ? `:${propType}` : ''}`;
let str = (0, dedent_1.dedent) `
${solidJSImports.length > 0 ? `import { ${solidJSImports.join(', ')} } from 'solid-js';` : ''}
${!foundDynamicComponents ? '' : `import { Dynamic } from 'solid-js/web';`}
${storeImports.length > 0 ? `import { ${storeImports.join(', ')} } from 'solid-js/store';` : ''}
${componentHasStyles && options.stylesType === 'styled-components'
? 'import { css } from "solid-styled-components";'
: ``}
${json.types && options.typescript ? json.types.join('\n') : ''}
${(0, render_imports_1.renderPreComponent)({
explicitImportFileExtension: options.explicitImportFileExtension,
component: json,
target: 'solid',
})}
function ${json.name}(${propsArgs}) {
${(_e = state === null || state === void 0 ? void 0 : state.str) !== null && _e !== void 0 ? _e : ''}
${getRefsString(json, options)}
${getContextString(json, options)}
${(_g = (_f = json.hooks.onInit) === null || _f === void 0 ? void 0 : _f.code) !== null && _g !== void 0 ? _g : ''}
${json.hooks.onMount.map((hook) => `onMount(() => { ${hook.code} })`).join('\n')}
${json.hooks.onUpdate
? json.hooks.onUpdate
.map((hook, index) => {
// TO-DO: support `onUpdate` without `deps`
if (!hook.deps)
return '';
const hookName = `onUpdateFn_${index}`;
const depsArray = hook.deps
.slice(1, hook.deps.length - 1)
.split(',')
.map((x) => x.trim());
const getReactiveDepName = (dep) => {
const newLocal = dep.replace(/(\.|\?|\(|\)|\[|\])/g, '_');
return `${hookName}_${newLocal}`;
};
const needsMemo = (dep) => true;
const reactiveDepsWorkaround = depsArray
.filter(needsMemo)
.map((dep) => `const ${getReactiveDepName(dep)} = createMemo(() => ${dep});`)
.join('\n');
const depsArrayStr = depsArray
.map((x) => (needsMemo(x) ? `${getReactiveDepName(x)}()` : x))
.join(', ');
return `
${reactiveDepsWorkaround}
function ${hookName}() { ${hook.code} };
createEffect(on(() => [${depsArrayStr}], ${hookName}));
`;
})
.join('\n')
: ''}
return (${addWrapper ? '<>' : ''}
${json.children
.filter(filter_empty_text_nodes_1.filterEmptyTextNodes)
.map((item) => (0, blocks_1.blockToSolid)(item, component, options, addWrapper))
.join('\n')}
${options.stylesType === 'style-tag' && css && css.trim().length > 4
? `<style>{\`${css}\`}</style>`
: ''}
${shouldInjectCustomStyles ? `<style>{\`${json.style}\`}</style>` : ''}
${addWrapper ? '</>' : ''})
}
export default ${json.name};
`;
if (options.plugins) {
str = (0, plugins_1.runPreCodePlugins)({ json, code: str, plugins: options.plugins });
}
if (options.prettier !== false) {
str = (0, standalone_1.format)(str, {
parser: 'typescript',
plugins: [require('prettier/parser-typescript'), require('prettier/parser-postcss')],
});
}
if (options.plugins) {
str = (0, plugins_1.runPostCodePlugins)({ json, code: str, plugins: options.plugins });
}
return str;
};
exports.componentToSolid = componentToSolid;
__exportStar(require("./types"), exports);
;