@builder.io/mitosis
Version:
Write components once, run everywhere. Compiles to Vue, React, Solid, and Liquid. Import code from Figma and Builder.io
357 lines (340 loc) • 19.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.componentToAngularClassic = void 0;
const blocks_1 = require("../../../generators/angular/classic/blocks");
const get_class_properties_plugin_1 = require("../../../generators/angular/classic/plugins/get-class-properties-plugin");
const get_code_processor_plugins_1 = require("../../../generators/angular/classic/plugins/get-code-processor-plugins");
const helpers_1 = require("../../../generators/angular/helpers");
const get_inputs_1 = require("../../../generators/angular/helpers/get-inputs");
const get_outputs_1 = require("../../../generators/angular/helpers/get-outputs");
const get_refs_1 = require("../../../generators/angular/helpers/get-refs");
const get_styles_1 = require("../../../generators/angular/helpers/get-styles");
const types_1 = require("../../../generators/angular/types");
const on_mount_1 = require("../../../generators/helpers/on-mount");
const dedent_1 = require("../../../helpers/dedent");
const fast_clone_1 = require("../../../helpers/fast-clone");
const get_child_components_1 = require("../../../helpers/get-child-components");
const get_components_used_1 = require("../../../helpers/get-components-used");
const get_custom_imports_1 = require("../../../helpers/get-custom-imports");
const get_prop_functions_1 = require("../../../helpers/get-prop-functions");
const get_props_1 = require("../../../helpers/get-props");
const get_props_ref_1 = require("../../../helpers/get-props-ref");
const get_refs_2 = require("../../../helpers/get-refs");
const get_state_object_string_1 = require("../../../helpers/get-state-object-string");
const get_typed_function_1 = require("../../../helpers/get-typed-function");
const is_upper_case_1 = require("../../../helpers/is-upper-case");
const merge_options_1 = require("../../../helpers/merge-options");
const render_imports_1 = require("../../../helpers/render-imports");
const strip_meta_properties_1 = require("../../../helpers/strip-meta-properties");
const attribute_passing_1 = require("../../../helpers/web-components/attribute-passing");
const plugins_1 = require("../../../modules/plugins");
const lodash_1 = require("lodash");
const format_1 = require("../helpers/format");
const componentToAngularClassic = (userOptions = {}) => {
return ({ component: _component }) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
// Make a copy we can safely mutate, similar to babel's toolchain
let json = (0, fast_clone_1.fastClone)(_component);
const useMetadata = (_a = json.meta) === null || _a === void 0 ? void 0 : _a.useMetadata;
const contextVars = Object.keys(((_b = json === null || json === void 0 ? void 0 : json.context) === null || _b === void 0 ? void 0 : _b.get) || {});
const metaOutputVars = (useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.outputs) || [];
const outputVars = (0, lodash_1.uniq)([...metaOutputVars, ...(0, get_prop_functions_1.getPropFunctions)(json)]);
const options = (0, merge_options_1.initializeOptions)({
target: 'angular',
component: _component,
defaults: types_1.DEFAULT_ANGULAR_OPTIONS,
userOptions,
});
options.plugins = (0, get_code_processor_plugins_1.getCodeProcessorPlugins)({ json, options, outputVars, contextVars });
if (options.state === 'class-properties') {
options.plugins.push((0, get_class_properties_plugin_1.getClassPropertiesPlugin)());
}
if (options.plugins) {
json = (0, plugins_1.runPreJsonPlugins)({ json, plugins: options.plugins });
}
// Prepare Props
const [forwardProp, hasPropRef] = (0, get_props_ref_1.getPropsRef)(json, true);
const props = (0, get_props_1.getProps)(json);
// prevent jsx props from showing up as @Input
if (hasPropRef) {
props.delete(forwardProp);
}
props.delete('children');
// remove props for outputs
for (const variableName of outputVars) {
props.delete(variableName);
}
const childComponents = (0, get_child_components_1.getChildComponents)(json);
const customImports = (0, get_custom_imports_1.getCustomImports)(json);
const { exports: localExports = {} } = json;
const localExportVars = Object.keys(localExports)
.filter((key) => localExports[key].usedInLocal)
.map((key) => `${key} = ${key};`);
// Context handling
const injectables = contextVars.map((variableName) => {
var _a, _b, _c, _d;
const variableType = (_a = json === null || json === void 0 ? void 0 : json.context) === null || _a === void 0 ? void 0 : _a.get[variableName].name;
if ((_b = options === null || options === void 0 ? void 0 : options.experimental) === null || _b === void 0 ? void 0 : _b.injectables) {
return (_c = options === null || options === void 0 ? void 0 : options.experimental) === null || _c === void 0 ? void 0 : _c.injectables(variableName, variableType);
}
if ((_d = options === null || options === void 0 ? void 0 : options.experimental) === null || _d === void 0 ? void 0 : _d.inject) {
return `@Inject(forwardRef(() => ${variableType})) public ${variableName}: ${variableType}`;
}
return `public ${variableName} : ${variableType}`;
});
// Handle refs
const withAttributePassing = (0, attribute_passing_1.shouldAddAttributePassing)(json, options);
const rootRef = (0, attribute_passing_1.getAddAttributePassingRef)(json, options);
const domRefs = (0, get_refs_1.getDomRefs)({ json, options, withAttributePassing, rootRef });
const jsRefs = Object.keys(json.refs).filter((ref) => !domRefs.has(ref));
if (options.plugins) {
json = (0, plugins_1.runPostJsonPlugins)({ json, plugins: options.plugins });
}
// CSS
const styles = (0, get_styles_1.getAngularStyles)({ json, options });
const helperFunctions = new Set();
const shouldUseSanitizer = !((_c = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _c === void 0 ? void 0 : _c.sanitizeInnerHTML) && (0, helpers_1.traverseAndCheckIfInnerHTMLIsUsed)(json);
const changeDetectionStrategy = (_d = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _d === void 0 ? void 0 : _d.changeDetection;
let template = json.children
.map((item) => {
var _a, _b, _c, _d;
const tmpl = (0, blocks_1.blockToAngular)({
root: json,
json: item,
options,
rootRef: withAttributePassing && rootRef === attribute_passing_1.ROOT_REF ? rootRef : undefined, // only pass rootRef if it's not the default
blockOptions: {
childComponents,
nativeAttributes: (_b = (_a = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _a === void 0 ? void 0 : _a.nativeAttributes) !== null && _b !== void 0 ? _b : [],
nativeEvents: (_d = (_c = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _c === void 0 ? void 0 : _c.nativeEvents) !== null && _d !== void 0 ? _d : [],
sanitizeInnerHTML: !shouldUseSanitizer,
},
});
if (options.state === 'inline-with-wrappers') {
(0, helpers_1.getAppropriateTemplateFunctionKeys)(tmpl).forEach((key) => helperFunctions.add((0, helpers_1.HELPER_FUNCTIONS)(options.typescript)[key]));
}
return tmpl;
})
.join('\n');
if (options.prettier !== false) {
template = (0, format_1.tryFormat)(template, 'html');
}
(0, strip_meta_properties_1.stripMetaProperties)(json);
const { components: dynamicComponents, dynamicTemplate } = (0, helpers_1.traverseToGetAllDynamicComponents)(json, options, {
childComponents,
nativeAttributes: (_f = (_e = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _e === void 0 ? void 0 : _e.nativeAttributes) !== null && _f !== void 0 ? _f : [],
nativeEvents: (_h = (_g = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _g === void 0 ? void 0 : _g.nativeEvents) !== null && _h !== void 0 ? _h : [],
});
(0, helpers_1.transformState)(json);
const dataString = (0, get_state_object_string_1.getStateObjectStringFromComponent)(json, {
format: 'class',
withType: options.typescript,
valueMapper: (code, type, typeParameter) => {
let value = code;
if (type !== 'data') {
value = (0, get_typed_function_1.getTypedFunction)(code, options.typescript, typeParameter);
}
return (0, helpers_1.processAngularCode)({
replaceWith: 'this',
contextVars,
outputVars,
domRefs: Array.from(domRefs),
})(value);
},
});
const refsForObjSpread = (0, get_refs_2.getRefs)(json, 'spreadRef');
// Preparing built in component metadata parameters
const componentsUsed = Array.from((0, get_components_used_1.getComponentsUsed)(json)).filter((item) => item.length && (0, is_upper_case_1.isUpperCase)(item[0]) && !types_1.BUILT_IN_COMPONENTS.has(item));
const componentMetadata = {
selector: ((_j = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _j === void 0 ? void 0 : _j.selector)
? `'${(_k = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _k === void 0 ? void 0 : _k.selector}'`
: `'${(0, lodash_1.kebabCase)(json.name || 'my-component')}'`,
template: `\`
${(0, helpers_1.getTemplateFormat)(dynamicTemplate)}
${(0, helpers_1.getTemplateFormat)(template)}
\``,
...(changeDetectionStrategy === 'OnPush'
? {
changeDetection: 'ChangeDetectionStrategy.OnPush',
}
: {}),
...(styles
? {
styles: `[\`${styles}\`]`,
}
: {}),
...(options.standalone
? // TODO: also add child component imports here as well
{
standalone: 'true',
imports: `[${['CommonModule', ...componentsUsed].join(', ')}]`,
}
: {}),
};
// Taking into consideration what user has passed in options and allowing them to override the default generated metadata
Object.entries(json.meta.angularConfig || {}).forEach(([key, value]) => {
componentMetadata[key] = value;
});
const hasConstructor = Boolean(injectables.length) ||
dynamicComponents.size ||
refsForObjSpread.size ||
shouldUseSanitizer;
const angularCoreImports = [
...(outputVars.length ? ['Output', 'EventEmitter'] : []),
...(((_l = options === null || options === void 0 ? void 0 : options.experimental) === null || _l === void 0 ? void 0 : _l.inject) ? ['Inject', 'forwardRef'] : []),
'Component',
...(domRefs.size || dynamicComponents.size || refsForObjSpread.size
? ['ViewChild', 'ElementRef']
: []),
...(refsForObjSpread.size ? ['Renderer2'] : []),
...(props.size ? ['Input'] : []),
...(dynamicComponents.size ? ['ViewContainerRef', 'TemplateRef'] : []),
...(((_m = json.hooks.onUpdate) === null || _m === void 0 ? void 0 : _m.length) && options.typescript ? ['SimpleChanges'] : []),
...(changeDetectionStrategy === 'OnPush' ? ['ChangeDetectionStrategy'] : []),
].join(', ');
const constructorInjectables = [
...injectables,
...(dynamicComponents.size
? [`private vcRef${options.typescript ? ': ViewContainerRef' : ''}`]
: []),
...(refsForObjSpread.size
? [`private renderer${options.typescript ? ': Renderer2' : ''}`]
: []),
...(shouldUseSanitizer
? [`protected sanitizer${options.typescript ? ': DomSanitizer' : ''}`]
: []),
].join(',\n');
let str = (0, dedent_1.dedent) `
import { ${angularCoreImports} } from '@angular/core';
${shouldUseSanitizer ? `import { DomSanitizer } from '@angular/platform-browser';` : ''}
${options.standalone ? `import { CommonModule } from '@angular/common';` : ''}
${json.types ? json.types.join('\n') : ''}
${(0, helpers_1.getDefaultProps)(json)}
${(0, render_imports_1.renderPreComponent)({
explicitImportFileExtension: options.explicitImportFileExtension,
component: json,
target: 'angular',
excludeMitosisComponents: !options.standalone && !options.preserveImports,
preserveFileExtensions: options.preserveFileExtensions,
componentsUsed,
importMapper: options === null || options === void 0 ? void 0 : options.importMapper,
})}
@Component({
${Object.entries(componentMetadata)
.map(([k, v]) => `${k}: ${v}`)
.join(',')}
})
export default class ${json.name} {
${localExportVars.join('\n')}
${customImports.map((name) => `${name} = ${name}`).join('\n')}
${(0, get_inputs_1.getInputs)({
json,
options,
props: Array.from(props),
})}
${(0, get_outputs_1.getOutputs)({ json, outputVars })}
${[...Array.from(domRefs), ...Array.from(refsForObjSpread)]
.map((refName) => `@ViewChild('${refName}') ${refName}${options.typescript ? '!: ElementRef' : ''}`)
.join('\n')}
${Array.from(dynamicComponents)
.map((component) => `@ViewChild('${component
.split('.')[1]
.toLowerCase()}Template', { static: true }) ${component
.split('.')[1]
.toLowerCase()}TemplateRef${options.typescript ? '!: TemplateRef<any>' : ''}`)
.join('\n')}
${dynamicComponents.size ? `myContent${options.typescript ? '?: any[][];' : ''}` : ''}
${refsForObjSpread.size
? `_listenerFns = new Map${options.typescript ? '<string, () => void>' : ''}()`
: ''}
${dataString}
${helperFunctions.size ? Array.from(helperFunctions).join('\n') : ''}
${jsRefs
.map((ref) => {
const argument = json.refs[ref].argument;
const typeParameter = json.refs[ref].typeParameter;
return `private _${ref}${typeParameter ? `: ${typeParameter}` : ''}${argument
? ` = ${(0, helpers_1.processAngularCode)({
replaceWith: 'this.',
contextVars,
outputVars,
domRefs: Array.from(domRefs),
})(argument)}`
: ''};`;
})
.join('\n')}
${!hasConstructor ? '' : `constructor(\n${constructorInjectables}) {}`}
${withAttributePassing ? (0, attribute_passing_1.getAttributePassingString)(options.typescript) : ''}
${!json.hooks.onMount.length && !dynamicComponents.size && !((_o = json.hooks.onInit) === null || _o === void 0 ? void 0 : _o.code)
? ''
: `ngOnInit() {
${!((_p = json.hooks) === null || _p === void 0 ? void 0 : _p.onInit)
? ''
: `
${(_q = json.hooks.onInit) === null || _q === void 0 ? void 0 : _q.code}
`}
${json.hooks.onMount.length > 0
? `
if (typeof window !== 'undefined') {
${(0, on_mount_1.stringifySingleScopeOnMount)(json)}
}
`
: ''}
${dynamicComponents.size
? `
this.myContent = [${Array.from(dynamicComponents)
.map((component) => `this.vcRef.createEmbeddedView(this.${component
.split('.')[1]
.toLowerCase()}TemplateRef).rootNodes`)
.join(', ')}];
`
: ''}
}`}
${
// hooks specific to Angular
((_s = (_r = json.compileContext) === null || _r === void 0 ? void 0 : _r.angular) === null || _s === void 0 ? void 0 : _s.hooks)
? Object.entries((_u = (_t = json.compileContext) === null || _t === void 0 ? void 0 : _t.angular) === null || _u === void 0 ? void 0 : _u.hooks)
.map(([key, value]) => {
return `${key}() {
${value.code}
}`;
})
.join('\n')
: ''}
${!((_v = json.hooks.onUpdate) === null || _v === void 0 ? void 0 : _v.length)
? ''
: `ngOnChanges(changes${options.typescript ? ': SimpleChanges' : ''}) {
if (typeof window !== 'undefined') {
${(_w = json.hooks.onUpdate) === null || _w === void 0 ? void 0 : _w.reduce((code, hook) => {
code += hook.code;
return code + '\n';
}, '')}
}
}
`}
${!json.hooks.onUnMount && !refsForObjSpread.size
? ''
: `ngOnDestroy() {
${((_x = json.hooks.onUnMount) === null || _x === void 0 ? void 0 : _x.code) || ''}
${refsForObjSpread.size
? `for (const fn of this._listenerFns.values()) { fn(); }`
: ''}
}`}
}
`;
if (options.standalone !== true) {
str = (0, helpers_1.generateNgModule)(str, json.name, componentsUsed, json, options.bootstrapMapper);
}
if (options.plugins) {
str = (0, plugins_1.runPreCodePlugins)({ json, code: str, plugins: options.plugins });
}
if (options.prettier !== false) {
str = (0, format_1.tryFormat)(str, 'typescript');
}
if (options.plugins) {
str = (0, plugins_1.runPostCodePlugins)({ json, code: str, plugins: options.plugins });
}
return str;
};
};
exports.componentToAngularClassic = componentToAngularClassic;
;