@builder.io/mitosis 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,7 +19,7 @@ export declare const transformState: (json: MitosisComponent) => void;
19
19
  export declare const hasFirstChildKeyAttribute: (node: MitosisNode) => boolean;
20
20
  export declare const preprocessCssAsJson: (json: MitosisComponent) => void;
21
21
  export declare const generateNgModule: (content: string, name: string, componentsUsed: string[], component: MitosisComponent, bootstrapMapper: Function | null | undefined) => string;
22
- export declare const traverseToGetAllDynamicComponents: (json: MitosisComponent, options: ToAngularOptions, blockOptions: AngularBlockOptions) => {
22
+ export declare const traverseToGetAllDynamicComponents: (json: MitosisComponent, options: ToAngularOptions, blockOptions: AngularBlockOptions, api?: 'signals' | 'classic') => {
23
23
  components: Set<string>;
24
24
  dynamicTemplate: string;
25
25
  };
@@ -36,6 +36,7 @@ const helpers_1 = require("../../../helpers/styles/helpers");
36
36
  const babel = __importStar(require("@babel/core"));
37
37
  const function_1 = require("fp-ts/function");
38
38
  const legacy_1 = __importDefault(require("neotraverse/legacy"));
39
+ const blocks_2 = require("../signals/blocks");
39
40
  const HELPER_FUNCTIONS = (isTs) => ({
40
41
  useObjectWrapper: `useObjectWrapper(...args${isTs ? ': any[]' : ''}) {
41
42
  let obj = {}
@@ -154,13 +155,15 @@ ${content}
154
155
  export class ${name}Module {}`;
155
156
  };
156
157
  exports.generateNgModule = generateNgModule;
157
- const traverseToGetAllDynamicComponents = (json, options, blockOptions) => {
158
+ const traverseToGetAllDynamicComponents = (json, options, blockOptions, api = 'classic') => {
158
159
  const components = new Set();
159
160
  let dynamicTemplate = '';
160
161
  (0, legacy_1.default)(json).forEach((item) => {
161
- if ((0, is_mitosis_node_1.isMitosisNode)(item) && item.name.includes('.') && item.name.split('.').length === 2) {
162
+ if ((0, is_mitosis_node_1.isMitosisNode)(item) && item.name.includes('.')) {
162
163
  const children = item.children
163
- .map((child) => (0, blocks_1.blockToAngular)({ root: json, json: child, options, blockOptions }))
164
+ .map((child) => api === 'classic'
165
+ ? (0, blocks_1.blockToAngular)({ root: json, json: child, options, blockOptions })
166
+ : (0, blocks_2.blockToAngularSignals)({ root: json, json: child, options, blockOptions }))
164
167
  .join('\n');
165
168
  dynamicTemplate = `<ng-template #${item.name.split('.')[1].toLowerCase() + 'Template'}>${children}</ng-template>`;
166
169
  components.add(item.name);
@@ -8,13 +8,16 @@ const html_tags_1 = require("../../../constants/html_tags");
8
8
  const helpers_1 = require("../../../generators/angular/helpers");
9
9
  const parse_selector_1 = require("../../../generators/angular/helpers/parse-selector");
10
10
  const babel_transform_1 = require("../../../helpers/babel-transform");
11
+ const bindings_1 = require("../../../helpers/bindings");
11
12
  const event_handlers_1 = require("../../../helpers/event-handlers");
12
13
  const is_children_1 = __importDefault(require("../../../helpers/is-children"));
13
14
  const is_mitosis_node_1 = require("../../../helpers/is-mitosis-node");
14
15
  const slots_1 = require("../../../helpers/slots");
16
+ const symbol_processor_1 = require("../../../symbols/symbol-processor");
15
17
  const types_1 = require("@babel/types");
16
18
  const function_1 = require("fp-ts/function");
17
19
  const lodash_1 = require("lodash");
20
+ const hooks_1 = require("../helpers/hooks");
18
21
  const getChildren = (root, json, options, blockOptions) => {
19
22
  var _a;
20
23
  return (_a = json.children) === null || _a === void 0 ? void 0 : _a.map((item) => (0, exports.blockToAngularSignals)({ root, json: item, options, blockOptions })).join('\n');
@@ -59,7 +62,11 @@ ${children}
59
62
  const children = getChildren(root, json, options, blockOptions);
60
63
  const item = forName !== null && forName !== void 0 ? forName : '_';
61
64
  const of = (_c = forNode.bindings.each) === null || _c === void 0 ? void 0 : _c.code;
62
- const track = `track ${trackByFnName ? trackByFnName : indexName ? indexName : 'i'};`;
65
+ const track = `track ${trackByFnName
66
+ ? `${trackByFnName}(${indexName !== null && indexName !== void 0 ? indexName : 'i'}, ${forName})`
67
+ : indexName
68
+ ? indexName
69
+ : 'i'};`;
63
70
  const index = indexName ? `let ${indexName} = $index` : 'let i = $index';
64
71
  return `
65
72
  @for (${item} of ${of};${track}${index}) {
@@ -95,13 +102,115 @@ const BINDINGS_MAPPER = {
95
102
  innerHTML: 'innerHTML',
96
103
  style: 'ngStyle',
97
104
  };
98
- const stringifyBinding = (node, blockOptions) => ([key, binding]) => {
105
+ const handleDynamicComponentBindings = (node) => {
106
+ var _a;
107
+ let allProps = '';
108
+ for (const key in node.properties) {
109
+ if (key.startsWith('$') || key === 'key') {
110
+ continue;
111
+ }
112
+ const value = node.properties[key];
113
+ allProps += `${key}: '${value}', `;
114
+ }
115
+ for (const key in node.bindings) {
116
+ if (key.startsWith('"') || key.startsWith('$') || key === 'key') {
117
+ continue;
118
+ }
119
+ const { code } = node.bindings[key];
120
+ if (key === 'ref') {
121
+ allProps += `${key}: this.${code}(), `;
122
+ continue;
123
+ }
124
+ if (((_a = node.bindings[key]) === null || _a === void 0 ? void 0 : _a.type) === 'spread') {
125
+ allProps += `...${code}, `;
126
+ continue;
127
+ }
128
+ let keyToUse = key.includes('-') ? `'${key}'` : key;
129
+ keyToUse = keyToUse.replace('state.', '').replace('props.', '');
130
+ if ((0, event_handlers_1.checkIsEvent)(key)) {
131
+ const eventName = (0, event_handlers_1.getEventNameWithoutOn)(key);
132
+ allProps += `on${eventName.charAt(0).toUpperCase() + eventName.slice(1)}: ${code.replace(/\(.*?\)/g, '')}.bind(this), `;
133
+ }
134
+ else {
135
+ allProps += `${keyToUse}: ${code}, `;
136
+ }
137
+ }
138
+ if (allProps.endsWith(', ')) {
139
+ allProps = allProps.slice(0, -2);
140
+ }
141
+ if (allProps.startsWith(', ')) {
142
+ allProps = allProps.slice(2);
143
+ }
144
+ return allProps;
145
+ };
146
+ const codeSetAttributes = (refName, code) => {
147
+ return `this.setAttributes(this.${refName}()?.nativeElement, ${code});`;
148
+ };
149
+ const saveSpreadRef = (root, refName) => {
150
+ root.compileContext = root.compileContext || {};
151
+ root.compileContext.angular = root.compileContext.angular || { extra: {} };
152
+ root.compileContext.angular.extra = root.compileContext.angular.extra || {};
153
+ root.compileContext.angular.extra.spreadRefs = root.compileContext.angular.extra.spreadRefs || [];
154
+ root.compileContext.angular.extra.spreadRefs.push(refName);
155
+ };
156
+ const handleSpreadBinding = (node, binding, root) => {
157
+ if (html_tags_1.VALID_HTML_TAGS.includes(node.name.trim())) {
158
+ if (binding.code === 'this') {
159
+ // if its an arbitrary { ...props } spread then we skip because Angular needs a named prop to be defined
160
+ return;
161
+ }
162
+ let refName = '';
163
+ if (node.meta._spreadRefName) {
164
+ refName = node.meta._spreadRefName;
165
+ const shouldAddRef = !node.meta._spreadRefAdded;
166
+ node.meta._spreadRefAdded = true;
167
+ (0, hooks_1.addCodeToOnUpdate)(root, codeSetAttributes(refName, binding.code));
168
+ (0, hooks_1.addCodeNgAfterViewInit)(root, codeSetAttributes(refName, binding.code));
169
+ return shouldAddRef ? `#${refName} ` : '';
170
+ }
171
+ const spreadRefIndex = root.meta._spreadRefIndex || 0;
172
+ refName = `elRef${spreadRefIndex}`;
173
+ root.meta._spreadRefIndex = spreadRefIndex + 1;
174
+ node.meta._spreadRefName = refName;
175
+ node.meta._spreadRefAdded = true;
176
+ node.bindings['spreadRef'] = (0, bindings_1.createSingleBinding)({ code: refName });
177
+ if (!root.refs[refName]) {
178
+ root.refs[refName] = { argument: '' };
179
+ }
180
+ if (!root.state['_listenerFns']) {
181
+ root.state['_listenerFns'] = {
182
+ code: 'new Map()',
183
+ type: 'property',
184
+ };
185
+ }
186
+ (0, hooks_1.addCodeToOnUpdate)(root, codeSetAttributes(refName, binding.code));
187
+ (0, hooks_1.addCodeNgAfterViewInit)(root, codeSetAttributes(refName, binding.code));
188
+ if (!root.state['setAttributes']) {
189
+ root.state['setAttributes'] = {
190
+ code: (0, helpers_1.HELPER_FUNCTIONS)(true).setAttributes,
191
+ type: 'method',
192
+ };
193
+ }
194
+ if (!root.hooks.onUnMount) {
195
+ root.hooks.onUnMount = {
196
+ code: `
197
+ for (const fn of this._listenerFns.values()) {
198
+ fn();
199
+ }
200
+ `,
201
+ };
202
+ }
203
+ saveSpreadRef(root, refName);
204
+ return `#${refName} `;
205
+ }
206
+ };
207
+ const stringifyBinding = (node, blockOptions, root) => ([key, binding]) => {
99
208
  var _a, _b;
100
- if (key.startsWith('$') || key.startsWith('"') || key === 'key') {
209
+ if (key.startsWith('$') || key === 'key') {
101
210
  return;
102
211
  }
103
212
  if ((binding === null || binding === void 0 ? void 0 : binding.type) === 'spread') {
104
- return;
213
+ return handleSpreadBinding(node, binding, root);
105
214
  }
106
215
  const keyToUse = BINDINGS_MAPPER[key] || key;
107
216
  if (!binding)
@@ -198,7 +307,7 @@ const blockToAngularSignals = ({ root, json, options = {}, blockOptions = {
198
307
  nativeAttributes: [],
199
308
  nativeEvents: [],
200
309
  }, rootRef, }) => {
201
- var _a;
310
+ var _a, _b;
202
311
  if (MAPPERS[json.name]) {
203
312
  return MAPPERS[json.name](root, json, options, blockOptions);
204
313
  }
@@ -213,6 +322,32 @@ const blockToAngularSignals = ({ root, json, options = {}, blockOptions = {
213
322
  return `{{${textCode}}}`;
214
323
  }
215
324
  let str = '';
325
+ // Handle dynamic components, state.MyComponent / props.MyComponent
326
+ if (json.name.includes('.')) {
327
+ const elSelector = ((_b = blockOptions.childComponents) === null || _b === void 0 ? void 0 : _b.find((impName) => impName === json.name))
328
+ ? (0, lodash_1.kebabCase)(json.name)
329
+ : json.name;
330
+ const elSelectorProcessed = elSelector.replace('state.', '').replace('props.', '');
331
+ const dynamicComponentRef = elSelectorProcessed.replace(/^this\.([^.]+)/, '$1()');
332
+ let allProps = handleDynamicComponentBindings(json);
333
+ const computedName = `dynamicProps_${(0, symbol_processor_1.hashCodeAsString)(allProps)}`;
334
+ if (allProps) {
335
+ if (!root.state[computedName]) {
336
+ root.state[computedName] = {
337
+ code: `get ${computedName}() {
338
+ return { ${allProps} };
339
+ }`,
340
+ type: 'getter',
341
+ };
342
+ }
343
+ }
344
+ str += `<ng-container *ngComponentOutlet="
345
+ ${dynamicComponentRef};${allProps ? `\ninputs: ${computedName}();` : ''}
346
+ content: myContent();
347
+ "> `;
348
+ str += `</ng-container>`;
349
+ return str;
350
+ }
216
351
  const { element, additionalString } = getElementTag(json, blockOptions);
217
352
  str += `<${element} ${additionalString}`;
218
353
  for (const key in json.properties) {
@@ -220,7 +355,7 @@ const blockToAngularSignals = ({ root, json, options = {}, blockOptions = {
220
355
  str += ` ${key}="${value}" `;
221
356
  }
222
357
  const stringifiedBindings = Object.entries(json.bindings)
223
- .map(stringifyBinding(json, blockOptions))
358
+ .map(stringifyBinding(json, blockOptions, root))
224
359
  .join('');
225
360
  str += stringifiedBindings;
226
361
  if (rootRef && !str.includes(`#${rootRef}`)) {
@@ -8,6 +8,7 @@ const get_refs_1 = require("../../../generators/angular/helpers/get-refs");
8
8
  const get_styles_1 = require("../../../generators/angular/helpers/get-styles");
9
9
  const blocks_1 = require("../../../generators/angular/signals/blocks");
10
10
  const helpers_2 = require("../../../generators/angular/signals/helpers");
11
+ const get_computed_1 = require("../../../generators/angular/signals/helpers/get-computed");
11
12
  const get_inputs_1 = require("../../../generators/angular/signals/helpers/get-inputs");
12
13
  const get_code_processor_plugins_1 = require("../../../generators/angular/signals/plugins/get-code-processor-plugins");
13
14
  const types_1 = require("../../../generators/angular/types");
@@ -19,6 +20,7 @@ const get_child_components_1 = require("../../../helpers/get-child-components");
19
20
  const get_components_used_1 = require("../../../helpers/get-components-used");
20
21
  const get_props_1 = require("../../../helpers/get-props");
21
22
  const get_state_object_string_1 = require("../../../helpers/get-state-object-string");
23
+ const is_hook_empty_1 = require("../../../helpers/is-hook-empty");
22
24
  const is_upper_case_1 = require("../../../helpers/is-upper-case");
23
25
  const merge_options_1 = require("../../../helpers/merge-options");
24
26
  const render_imports_1 = require("../../../helpers/render-imports");
@@ -26,9 +28,10 @@ const strip_meta_properties_1 = require("../../../helpers/strip-meta-properties"
26
28
  const attribute_passing_1 = require("../../../helpers/web-components/attribute-passing");
27
29
  const plugins_1 = require("../../../modules/plugins");
28
30
  const lodash_1 = require("lodash");
31
+ const get_dynamic_template_refs_1 = require("./helpers/get-dynamic-template-refs");
29
32
  const componentToAngularSignals = (userOptions = {}) => {
30
33
  return ({ component }) => {
31
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
34
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10;
32
35
  // Make a copy we can safely mutate, similar to babel's toolchain
33
36
  let json = (0, fast_clone_1.fastClone)(component);
34
37
  // Init compileContext
@@ -38,6 +41,9 @@ const componentToAngularSignals = (userOptions = {}) => {
38
41
  ngAfterViewInit: {
39
42
  code: '',
40
43
  },
44
+ ngAfterContentInit: {
45
+ code: '',
46
+ },
41
47
  },
42
48
  extra: {
43
49
  importCalls: [],
@@ -55,7 +61,7 @@ const componentToAngularSignals = (userOptions = {}) => {
55
61
  if (options.plugins) {
56
62
  json = (0, plugins_1.runPreJsonPlugins)({ json, plugins: options.plugins });
57
63
  }
58
- const withAttributePassing = true; // We always want to pass attributes
64
+ const withAttributePassing = (0, attribute_passing_1.shouldAddAttributePassing)(json, options);
59
65
  const rootRef = (0, attribute_passing_1.getAddAttributePassingRef)(json, options);
60
66
  const domRefs = (0, get_refs_1.getDomRefs)({ json, options, rootRef, withAttributePassing });
61
67
  let props = Array.from((0, get_props_1.getProps)(json));
@@ -91,6 +97,15 @@ const componentToAngularSignals = (userOptions = {}) => {
91
97
  if (shouldUseSanitizer) {
92
98
  injectables.push('protected sanitizer: DomSanitizer');
93
99
  }
100
+ if (json.hooks.onMount.length > 0) {
101
+ json.compileContext.angular.hooks.ngAfterViewInit = {
102
+ code: `
103
+ if (typeof window !== 'undefined') {
104
+ ${(0, on_mount_1.stringifySingleScopeOnMount)(json)}
105
+ }
106
+ `,
107
+ };
108
+ }
94
109
  // HTML
95
110
  let template = json.children
96
111
  .map((item) => {
@@ -112,33 +127,70 @@ const componentToAngularSignals = (userOptions = {}) => {
112
127
  if (options.prettier !== false) {
113
128
  template = (0, format_1.tryFormat)(template, 'html');
114
129
  }
130
+ const { components: dynamicComponents, dynamicTemplate } = (0, helpers_1.traverseToGetAllDynamicComponents)(json, options, {
131
+ childComponents,
132
+ nativeAttributes: (_m = (_l = (_k = (_j = json.meta) === null || _j === void 0 ? void 0 : _j.useMetadata) === null || _k === void 0 ? void 0 : _k.angular) === null || _l === void 0 ? void 0 : _l.nativeAttributes) !== null && _m !== void 0 ? _m : [],
133
+ nativeEvents: (_r = (_q = (_p = (_o = json.meta) === null || _o === void 0 ? void 0 : _o.useMetadata) === null || _p === void 0 ? void 0 : _p.angular) === null || _q === void 0 ? void 0 : _q.nativeEvents) !== null && _r !== void 0 ? _r : [],
134
+ }, 'signals');
135
+ const hasDynamicComponents = dynamicComponents.size > 0;
136
+ if (hasDynamicComponents) {
137
+ injectables.push('private viewContainer: ViewContainerRef');
138
+ json.compileContext.angular.hooks.ngAfterContentInit.code =
139
+ `this._updateView();` + json.compileContext.angular.hooks.ngAfterContentInit.code;
140
+ }
115
141
  // Angular component settings
116
142
  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));
117
143
  const componentSettings = {
118
- selector: `'${(0, lodash_1.kebabCase)(json.name)}'`,
144
+ selector: ((_s = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _s === void 0 ? void 0 : _s.selector)
145
+ ? `'${(_t = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _t === void 0 ? void 0 : _t.selector}'`
146
+ : `'${(0, lodash_1.kebabCase)(json.name || 'my-component')}'`,
119
147
  standalone: 'true',
120
148
  imports: `[${['CommonModule', ...componentsUsed].join(', ')}]`,
121
- template: `\`${(0, helpers_1.getTemplateFormat)(template)}\``,
149
+ template: `\`${dynamicTemplate}${(0, helpers_1.getTemplateFormat)(template)}\``,
122
150
  };
123
151
  if (onPush) {
124
- componentSettings.changeDetection = `'ChangeDetectionStrategy.OnPush'`;
152
+ componentSettings.changeDetection = 'ChangeDetectionStrategy.OnPush';
125
153
  }
126
154
  if (styles) {
127
155
  componentSettings.styles = `\`${styles}\``;
128
156
  }
157
+ if ((_u = useMetadata === null || useMetadata === void 0 ? void 0 : useMetadata.angular) === null || _u === void 0 ? void 0 : _u.skipHydration) {
158
+ componentSettings.host = `{ ngSkipHydration: 'true' }`;
159
+ }
129
160
  (0, strip_meta_properties_1.stripMetaProperties)(json);
130
161
  const dataString = (0, get_state_object_string_1.getStateObjectStringFromComponent)(json, {
131
162
  format: 'class',
132
163
  data: true,
133
164
  functions: false,
134
165
  getters: false,
135
- valueMapper: (code, _, typeParameter) => {
166
+ valueMapper: (code, _, typeParameter, key) => {
167
+ var _a;
136
168
  if (typeParameter && !code.length) {
137
169
  console.error(`
138
170
  Component ${json.name} has state property without an initial value'.
139
171
  This will cause an error in Angular.
140
172
  Please add a initial value for every state property even if it's \`undefined\`.`);
141
173
  }
174
+ // Special case for _listenerFns - don't wrap in signal()
175
+ if (key === '_listenerFns') {
176
+ return code;
177
+ }
178
+ if (key) {
179
+ const propRefs = props.filter((prop) => code.includes(`this.${prop}()`));
180
+ if (propRefs.length > 0) {
181
+ if (!((_a = json.hooks.onInit) === null || _a === void 0 ? void 0 : _a.code)) {
182
+ json.hooks.onInit = {
183
+ code: `
184
+ this.${key}.set(${code});
185
+ `,
186
+ };
187
+ }
188
+ else {
189
+ json.hooks.onInit.code = `this.${key}.set(${code});`.concat(json.hooks.onInit.code);
190
+ }
191
+ return `signal${typeParameter ? `<${typeParameter}>` : ''}(undefined)`;
192
+ }
193
+ }
142
194
  return `signal${typeParameter ? `<${typeParameter}>` : ''}(${code})`;
143
195
  },
144
196
  });
@@ -146,21 +198,36 @@ Please add a initial value for every state property even if it's \`undefined\`.`
146
198
  format: 'class',
147
199
  data: false,
148
200
  functions: true,
149
- getters: true,
201
+ getters: false,
150
202
  onlyValueMapper: true,
151
203
  valueMapper: (code, type, _, key) => {
152
204
  return code.startsWith('function') ? code.replace('function', '').trim() : code;
153
205
  },
154
206
  });
207
+ // Handle getters as computed signals
208
+ const gettersString = (0, get_computed_1.getComputedGetters)({ json });
209
+ // Check if we need Renderer2 for spread attributes
210
+ const usesRenderer2 = !!json.state['_listenerFns'];
211
+ if (usesRenderer2) {
212
+ injectables.push('private renderer: Renderer2');
213
+ }
214
+ const importsViewChild = hasDynamicComponents ||
215
+ domRefs.size !== 0 ||
216
+ ((_y = (_x = (_w = (_v = json.compileContext) === null || _v === void 0 ? void 0 : _v.angular) === null || _w === void 0 ? void 0 : _w.extra) === null || _x === void 0 ? void 0 : _x.spreadRefs) === null || _y === void 0 ? void 0 : _y.length) > 0;
155
217
  // Imports
156
218
  const coreImports = (0, helpers_2.getAngularCoreImportsAsString)({
157
219
  refs: domRefs.size !== 0,
158
220
  input: props.length !== 0,
159
221
  output: events.length !== 0,
160
222
  model: writeableSignals.length !== 0,
161
- effect: ((_j = json.hooks.onUpdate) === null || _j === void 0 ? void 0 : _j.length) !== 0,
162
- signal: dataString.length !== 0,
223
+ effect: ((_z = json.hooks.onUpdate) === null || _z === void 0 ? void 0 : _z.length) !== 0,
224
+ signal: dataString.length !== 0 || hasDynamicComponents,
225
+ computed: gettersString.length !== 0,
163
226
  onPush,
227
+ viewChild: importsViewChild,
228
+ viewContainerRef: hasDynamicComponents,
229
+ templateRef: hasDynamicComponents,
230
+ renderer: usesRenderer2,
164
231
  });
165
232
  let str = (0, dedent_1.dedent) `
166
233
  import { ${coreImports} } from '@angular/core';
@@ -177,6 +244,8 @@ Please add a initial value for every state property even if it's \`undefined\`.`
177
244
  target: 'angular',
178
245
  preserveFileExtensions: options.preserveFileExtensions,
179
246
  importMapper: (_, theImport, importedValues) => {
247
+ if (options.defaultExportComponents)
248
+ return undefined;
180
249
  const { defaultImport } = importedValues;
181
250
  const { path } = theImport;
182
251
  if (defaultImport && componentsUsed.includes(defaultImport)) {
@@ -191,11 +260,12 @@ Please add a initial value for every state property even if it's \`undefined\`.`
191
260
  .map(([k, v]) => `${k}: ${v}`)
192
261
  .join(',')}
193
262
  })
194
- export class ${json.name} implements AfterViewInit {
263
+ export ${options.defaultExportComponents ? 'default ' : ''}class ${json.name} {
195
264
  ${(0, lodash_1.uniq)(json.compileContext.angular.extra.importCalls)
196
265
  .map((importCall) => `protected readonly ${importCall} = ${importCall};`)
197
266
  .join('\n')}
198
-
267
+
268
+ ${hasDynamicComponents ? (0, get_dynamic_template_refs_1.getDynamicTemplateRefs)(dynamicComponents) : ''}
199
269
  ${(0, get_inputs_1.getSignalInputs)({
200
270
  json,
201
271
  writeableSignals,
@@ -207,13 +277,22 @@ Please add a initial value for every state property even if it's \`undefined\`.`
207
277
  ${Array.from(domRefs)
208
278
  .map((refName) => `${refName} = viewChild<ElementRef>("${refName}")`)
209
279
  .join('\n')}
280
+ ${((_2 = (_1 = (_0 = json.compileContext) === null || _0 === void 0 ? void 0 : _0.angular) === null || _1 === void 0 ? void 0 : _1.extra) === null || _2 === void 0 ? void 0 : _2.spreadRefs)
281
+ ? Array.from(new Set(json.compileContext.angular.extra.spreadRefs))
282
+ .filter((refName) => !Array.from(domRefs).includes(refName))
283
+ .map((refName) => `${refName} = viewChild<ElementRef>("${refName}")`)
284
+ .join('\n')
285
+ : ''}
210
286
 
211
287
  ${dataString}
288
+ ${gettersString}
212
289
  ${methodsString}
213
290
 
214
- constructor(${injectables.join(',\n')}) {
215
- ${((_k = json.hooks.onUpdate) === null || _k === void 0 ? void 0 : _k.length)
216
- ? (_l = json.hooks.onUpdate) === null || _l === void 0 ? void 0 : _l.map(({ code, depsArray }) =>
291
+ constructor(${injectables.join(',\n')}) {
292
+ ${(0, is_hook_empty_1.isHookEmpty)(json.hooks.onUpdate)
293
+ ? ''
294
+ : `if (typeof window !== 'undefined') {
295
+ ${(_3 = json.hooks.onUpdate) === null || _3 === void 0 ? void 0 : _3.map(({ code, depsArray }) =>
217
296
  /**
218
297
  * We need allowSignalWrites only for Angular 17 https://angular.dev/api/core/CreateEffectOptions#allowSignalWrites
219
298
  * TODO: remove on 2025-05-15 https://angular.dev/reference/releases#actively-supported-versions
@@ -231,23 +310,30 @@ Please add a initial value for every state property even if it's \`undefined\`.`
231
310
  {
232
311
  allowSignalWrites: true, // Enable writing to signals inside effects
233
312
  }
234
- );`).join('\n')
235
- : ''}
236
- }
313
+ );`).join('\n')}
314
+ }
315
+ `}}
237
316
 
238
317
  ${withAttributePassing ? (0, attribute_passing_1.getAttributePassingString)(options.typescript) : ''}
239
318
 
240
- ${!json.hooks.onMount.length && !((_m = json.hooks.onInit) === null || _m === void 0 ? void 0 : _m.code)
319
+ ${(0, is_hook_empty_1.isHookEmpty)(json.hooks.onInit)
241
320
  ? ''
242
321
  : `ngOnInit() {
243
- ${!((_o = json.hooks) === null || _o === void 0 ? void 0 : _o.onInit) ? '' : (_p = json.hooks.onInit) === null || _p === void 0 ? void 0 : _p.code}
244
- ${json.hooks.onMount.length > 0 ? (0, on_mount_1.stringifySingleScopeOnMount)(json) : ''}
322
+ ${!((_4 = json.hooks) === null || _4 === void 0 ? void 0 : _4.onInit) ? '' : (_5 = json.hooks.onInit) === null || _5 === void 0 ? void 0 : _5.code}
245
323
  }`}
324
+
325
+ ${!hasDynamicComponents
326
+ ? ''
327
+ : `
328
+ _updateView() {
329
+ ${(0, get_dynamic_template_refs_1.getInitEmbedViewCode)(dynamicComponents)}
330
+ }`}
246
331
 
247
332
  ${
248
333
  // hooks specific to Angular
249
- ((_r = (_q = json.compileContext) === null || _q === void 0 ? void 0 : _q.angular) === null || _r === void 0 ? void 0 : _r.hooks)
250
- ? Object.entries((_t = (_s = json.compileContext) === null || _s === void 0 ? void 0 : _s.angular) === null || _t === void 0 ? void 0 : _t.hooks)
334
+ ((_7 = (_6 = json.compileContext) === null || _6 === void 0 ? void 0 : _6.angular) === null || _7 === void 0 ? void 0 : _7.hooks)
335
+ ? Object.entries((_9 = (_8 = json.compileContext) === null || _8 === void 0 ? void 0 : _8.angular) === null || _9 === void 0 ? void 0 : _9.hooks)
336
+ .filter(([_, value]) => !(0, is_hook_empty_1.isHookEmpty)(value))
251
337
  .map(([key, value]) => {
252
338
  return `${key}() {
253
339
  ${value.code}
@@ -258,7 +344,7 @@ Please add a initial value for every state property even if it's \`undefined\`.`
258
344
 
259
345
  ${json.hooks.onUnMount
260
346
  ? `ngOnDestroy() {
261
- ${((_u = json.hooks.onUnMount) === null || _u === void 0 ? void 0 : _u.code) || ''}
347
+ ${((_10 = json.hooks.onUnMount) === null || _10 === void 0 ? void 0 : _10.code) || ''}
262
348
  }`
263
349
  : ''}
264
350
 
@@ -0,0 +1,6 @@
1
+ import type { MitosisComponent } from '../../../../types/mitosis-component';
2
+ import type { Binding } from '../../../../types/mitosis-node';
3
+ export declare const getComputedGetters: ({ json }: {
4
+ json: MitosisComponent;
5
+ }) => string;
6
+ export declare const createObjectSpreadComputed: (json: MitosisComponent, binding: Binding, key: string, isForContext?: boolean, forName?: string, indexName?: string) => string;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createObjectSpreadComputed = exports.getComputedGetters = void 0;
4
+ const babel_transform_1 = require("../../../../helpers/babel-transform");
5
+ const symbol_processor_1 = require("../../../../symbols/symbol-processor");
6
+ const types_1 = require("@babel/types");
7
+ const lodash_1 = require("lodash");
8
+ const getComputedGetters = ({ json }) => {
9
+ const getterKeys = Object.keys((0, lodash_1.pickBy)(json.state, (i) => (i === null || i === void 0 ? void 0 : i.type) === 'getter'));
10
+ if (!getterKeys.length) {
11
+ return '';
12
+ }
13
+ return getterKeys
14
+ .map((key) => {
15
+ var _a, _b;
16
+ const code = (_b = (_a = json.state[key]) === null || _a === void 0 ? void 0 : _a.code) === null || _b === void 0 ? void 0 : _b.toString();
17
+ if (!code) {
18
+ return '';
19
+ }
20
+ // Transform `get foo() { return this.bar }` to `foo = computed(() => { return bar.value })`
21
+ const getterAsFunction = code
22
+ .replace('get', '')
23
+ .replace(key, '')
24
+ .trim()
25
+ .replace(/^\(\)/, '() =>');
26
+ return `${key} = computed(${getterAsFunction})`;
27
+ })
28
+ .filter(Boolean)
29
+ .join('\n');
30
+ };
31
+ exports.getComputedGetters = getComputedGetters;
32
+ const createObjectSpreadComputed = (json, binding, key, isForContext = false, forName = '', indexName = '') => {
33
+ const computedName = `objSpread_${key}_${(0, symbol_processor_1.hashCodeAsString)(binding.code)}`;
34
+ const transformedCode = (0, babel_transform_1.babelTransformExpression)(binding.code, {
35
+ MemberExpression(path) {
36
+ var _a;
37
+ if ((0, types_1.isMemberExpression)(path.node) &&
38
+ (0, types_1.isIdentifier)(path.node.object) &&
39
+ (0, types_1.isIdentifier)(path.node.property) &&
40
+ (path.node.object.name === 'props' || path.node.object.name === 'state') &&
41
+ !((_a = path.node.extra) === null || _a === void 0 ? void 0 : _a.processed)) {
42
+ path.node.object.name = 'this';
43
+ path.node.extra = { ...path.node.extra, processed: true };
44
+ const code = path.toString();
45
+ path.replaceWithSourceString(`${code}()`);
46
+ }
47
+ },
48
+ });
49
+ const finalCode = transformedCode
50
+ // Replace props.x.y with this.x().y
51
+ .replace(/\bprops\.(\w+)(\.\w+|\?\.\w+|\[.*?\])/g, (match, prop, rest) => {
52
+ return `this.${prop}()${rest}`;
53
+ })
54
+ // Replace state.x.y with this.x().y
55
+ .replace(/\bstate\.(\w+)(\.\w+|\?\.\w+|\[.*?\])/g, (match, prop, rest) => {
56
+ return `this.${prop}()${rest}`;
57
+ });
58
+ if (isForContext && (forName || indexName)) {
59
+ // Create a method that accepts the for loop context variables
60
+ const params = [];
61
+ if (forName)
62
+ params.push(forName);
63
+ if (indexName)
64
+ params.push(indexName);
65
+ json.state[computedName] = {
66
+ code: `${computedName}(${params.join(', ')}) { return ${finalCode} }`,
67
+ type: 'method',
68
+ };
69
+ }
70
+ else {
71
+ // Creates a getter that gets converted to Angular's computed
72
+ json.state[computedName] = {
73
+ code: `get ${computedName}() { return ${finalCode} }`,
74
+ type: 'getter',
75
+ };
76
+ }
77
+ return computedName;
78
+ };
79
+ exports.createObjectSpreadComputed = createObjectSpreadComputed;
@@ -0,0 +1,2 @@
1
+ export declare const getDynamicTemplateRefs: (dynamicComponents: Set<string>) => string;
2
+ export declare const getInitEmbedViewCode: (dynamicComponents: Set<string>) => string;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getInitEmbedViewCode = exports.getDynamicTemplateRefs = void 0;
4
+ const getViewContainerRefName = (component) => {
5
+ return component.split('.')[1].toLowerCase() + 'TemplateRef';
6
+ };
7
+ const getNgTemplateRefName = (component) => {
8
+ return component.split('.')[1].toLowerCase() + 'Template';
9
+ };
10
+ const getDynamicTemplateRefs = (dynamicComponents) => {
11
+ return `
12
+ myContent = signal<any[]>([]);
13
+ ${Array.from(dynamicComponents)
14
+ .map((component) => `${getViewContainerRefName(component)} = viewChild<TemplateRef<any>>('${getNgTemplateRefName(component)}');`)
15
+ .join('\n')}
16
+ `;
17
+ };
18
+ exports.getDynamicTemplateRefs = getDynamicTemplateRefs;
19
+ const getInitEmbedViewCode = (dynamicComponents) => {
20
+ return `
21
+ this.myContent.set([${Array.from(dynamicComponents)
22
+ .map((component) => `this.viewContainer.createEmbeddedView(this.${getViewContainerRefName(component)}()).rootNodes`)
23
+ .join(', ')}]);`;
24
+ };
25
+ exports.getInitEmbedViewCode = getInitEmbedViewCode;
@@ -8,7 +8,7 @@ const getSignalInputs = ({ props, json, writeableSignals, requiredSignals, }) =>
8
8
  const hasDefaultProp = json.defaultProps && json.defaultProps.hasOwnProperty(prop);
9
9
  const propType = propsTypeRef ? `${propsTypeRef}["${prop}"]` : 'any';
10
10
  const defaultProp = hasDefaultProp ? `defaultProps["${prop}"]` : '';
11
- return `${prop} = ${writeableSignals.includes(prop) ? 'model' : 'input'}${requiredSignals.includes(prop) ? '.required' : ''}<${propType}>(${defaultProp})`;
11
+ return `${prop}: ${writeableSignals.includes(prop) ? 'ModelSignal' : 'InputSignal'}<${propType}> = ${writeableSignals.includes(prop) ? 'model' : 'input'}${requiredSignals.includes(prop) ? '.required' : ''}<${propType}>(${defaultProp})`;
12
12
  })
13
13
  .join('\n');
14
14
  };
@@ -1,4 +1,4 @@
1
- export declare const getAngularCoreImportsAsString: ({ refs, output, input, model, onPush, effect, signal, }: {
1
+ export declare const getAngularCoreImportsAsString: ({ refs, output, input, model, onPush, effect, signal, computed, viewChild, viewContainerRef, templateRef, renderer, }: {
2
2
  refs: boolean;
3
3
  output: boolean;
4
4
  input: boolean;
@@ -6,4 +6,9 @@ export declare const getAngularCoreImportsAsString: ({ refs, output, input, mode
6
6
  onPush: boolean;
7
7
  effect: boolean;
8
8
  signal: boolean;
9
+ computed: boolean;
10
+ viewChild: boolean;
11
+ viewContainerRef: boolean;
12
+ templateRef: boolean;
13
+ renderer?: boolean | undefined;
9
14
  }) => string;