@builder.io/mitosis 0.10.0 → 0.11.1
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.
- package/dist/src/generators/angular/helpers/index.d.ts +1 -1
- package/dist/src/generators/angular/helpers/index.js +6 -3
- package/dist/src/generators/angular/signals/blocks.js +141 -6
- package/dist/src/generators/angular/signals/component.js +109 -23
- package/dist/src/generators/angular/signals/helpers/get-computed.d.ts +6 -0
- package/dist/src/generators/angular/signals/helpers/get-computed.js +79 -0
- package/dist/src/generators/angular/signals/helpers/get-dynamic-template-refs.d.ts +2 -0
- package/dist/src/generators/angular/signals/helpers/get-dynamic-template-refs.js +25 -0
- package/dist/src/generators/angular/signals/helpers/get-inputs.js +1 -1
- package/dist/src/generators/angular/signals/helpers/index.d.ts +6 -1
- package/dist/src/generators/angular/signals/helpers/index.js +9 -4
- package/dist/src/generators/angular/signals/plugins/get-code-processor-plugins.js +364 -11
- package/dist/src/generators/angular/types.d.ts +6 -1
- package/dist/src/generators/angular/types.js +1 -0
- package/dist/src/generators/mitosis/generator.js +8 -1
- package/dist/src/generators/react/generator.js +2 -2
- package/dist/src/generators/react/helpers/state.d.ts +1 -1
- package/dist/src/generators/react/helpers/state.js +44 -2
- package/dist/src/helpers/event-handlers.js +1 -1
- package/dist/src/helpers/is-hook-empty.d.ts +2 -0
- package/dist/src/helpers/is-hook-empty.js +14 -0
- package/dist/src/types/config.d.ts +4 -3
- package/package.json +1 -1
|
@@ -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('.')
|
|
162
|
+
if ((0, is_mitosis_node_1.isMitosisNode)(item) && item.name.includes('.')) {
|
|
162
163
|
const children = item.children
|
|
163
|
-
.map((child) =>
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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: ((
|
|
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}
|
|
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
|
-
${((
|
|
216
|
-
?
|
|
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
|
-
${
|
|
319
|
+
${(0, is_hook_empty_1.isHookEmpty)(json.hooks.onInit)
|
|
241
320
|
? ''
|
|
242
321
|
: `ngOnInit() {
|
|
243
|
-
${!((
|
|
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
|
-
((
|
|
250
|
-
? Object.entries((
|
|
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
|
-
${((
|
|
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,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;
|