@stencil/angular-output-target 1.0.0 → 1.1.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/generate-angular-component.d.ts +4 -4
- package/dist/generate-angular-component.js +43 -12
- package/dist/index.cjs.js +56 -24
- package/dist/index.js +56 -24
- package/dist/output-angular.js +14 -14
- package/dist/types.d.ts +4 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +1 -0
- package/package.json +2 -2
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { ComponentCompilerEvent, ComponentCompilerProperty } from '@stencil/core/internal';
|
|
2
|
-
import type { OutputType } from './types';
|
|
2
|
+
import type { ComponentInputProperty, OutputType } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
5
5
|
*
|
|
6
6
|
* @param tagName The tag name of the component.
|
|
7
|
-
* @param inputs The inputs of the Stencil component (e.g. ['myInput']).
|
|
8
|
-
* @param outputs The outputs/events of the Stencil component. (e.g. ['myOutput']).
|
|
7
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
9
8
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
10
9
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
11
10
|
* @param standalone Whether to define the component as a standalone component.
|
|
12
11
|
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
12
|
+
* @param events The events of the Stencil component for generating outputs.
|
|
13
13
|
* @returns The component declaration as a string.
|
|
14
14
|
*/
|
|
15
|
-
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly
|
|
15
|
+
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly ComponentInputProperty[], methods: readonly string[], includeImportCustomElements?: boolean, standalone?: boolean, inlineComponentProps?: readonly ComponentCompilerProperty[], events?: readonly ComponentCompilerEvent[]) => string;
|
|
16
16
|
/**
|
|
17
17
|
* Creates the component interface type definition.
|
|
18
18
|
* @param outputType The output type.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createComponentEventTypeImports, dashToPascalCase, formatToQuotedList } from './utils';
|
|
1
|
+
import { createComponentEventTypeImports, dashToPascalCase, formatToQuotedList, mapPropName } from './utils';
|
|
2
2
|
/**
|
|
3
3
|
* Creates a property declaration.
|
|
4
4
|
*
|
|
@@ -24,25 +24,48 @@ function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
|
24
24
|
${eventName}: ${type};`;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates a formatted inputs text with required declaration.
|
|
29
|
+
*
|
|
30
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
31
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
32
|
+
* @returns The inputs list declaration as a string.
|
|
33
|
+
*/
|
|
34
|
+
function formatInputs(inputs) {
|
|
35
|
+
return inputs
|
|
36
|
+
.map((item) => {
|
|
37
|
+
if (item.required) {
|
|
38
|
+
return `{ name: '${item.name}', required: true }`;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return `'${item.name}'`;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.join(', ');
|
|
45
|
+
}
|
|
27
46
|
/**
|
|
28
47
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
29
48
|
*
|
|
30
49
|
* @param tagName The tag name of the component.
|
|
31
|
-
* @param inputs The inputs of the Stencil component (e.g. ['myInput']).
|
|
32
|
-
* @param outputs The outputs/events of the Stencil component. (e.g. ['myOutput']).
|
|
50
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
33
51
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
34
52
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
35
53
|
* @param standalone Whether to define the component as a standalone component.
|
|
36
54
|
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
55
|
+
* @param events The events of the Stencil component for generating outputs.
|
|
37
56
|
* @returns The component declaration as a string.
|
|
38
57
|
*/
|
|
39
|
-
export const createAngularComponentDefinition = (tagName, inputs,
|
|
58
|
+
export const createAngularComponentDefinition = (tagName, inputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = [], events = []) => {
|
|
40
59
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
60
|
+
const outputs = events.filter((event) => !event.internal).map((event) => event.name);
|
|
41
61
|
const hasInputs = inputs.length > 0;
|
|
42
62
|
const hasOutputs = outputs.length > 0;
|
|
43
63
|
const hasMethods = methods.length > 0;
|
|
44
64
|
// Formats the input strings into comma separated, single quoted values.
|
|
45
|
-
const
|
|
65
|
+
const proxyCmpFormattedInputs = formatToQuotedList(inputs.map(mapPropName));
|
|
66
|
+
// Formats the input strings into comma separated, single quoted values if optional.
|
|
67
|
+
// Formats the required input strings into comma separated {name, required} objects.
|
|
68
|
+
const formattedInputs = formatInputs(inputs);
|
|
46
69
|
// Formats the output strings into comma separated, single quoted values.
|
|
47
70
|
const formattedOutputs = formatToQuotedList(outputs);
|
|
48
71
|
// Formats the method strings into comma separated, single quoted values.
|
|
@@ -53,7 +76,7 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
53
76
|
proxyCmpOptions.push(`\n defineCustomElementFn: ${defineCustomElementFn}`);
|
|
54
77
|
}
|
|
55
78
|
if (hasInputs) {
|
|
56
|
-
proxyCmpOptions.push(`\n inputs: [${
|
|
79
|
+
proxyCmpOptions.push(`\n inputs: [${proxyCmpFormattedInputs}]`);
|
|
57
80
|
}
|
|
58
81
|
if (hasMethods) {
|
|
59
82
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
@@ -63,7 +86,18 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
63
86
|
standaloneOption = `\n standalone: false`;
|
|
64
87
|
}
|
|
65
88
|
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
66
|
-
const
|
|
89
|
+
const outputDeclarations = events
|
|
90
|
+
.filter((event) => !event.internal)
|
|
91
|
+
.map((event) => {
|
|
92
|
+
const camelCaseOutput = event.name.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
|
|
93
|
+
const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
94
|
+
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
95
|
+
});
|
|
96
|
+
const propertiesDeclarationText = [
|
|
97
|
+
`protected el: HTML${tagNameAsPascal}Element;`,
|
|
98
|
+
...propertyDeclarations,
|
|
99
|
+
...outputDeclarations,
|
|
100
|
+
].join('\n ');
|
|
67
101
|
/**
|
|
68
102
|
* Notes on the generated output:
|
|
69
103
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -77,16 +111,13 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
77
111
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
78
112
|
template: '<ng-content></ng-content>',
|
|
79
113
|
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
80
|
-
inputs: [${formattedInputs}],${standaloneOption}
|
|
114
|
+
inputs: [${formattedInputs}],${hasOutputs ? `\n outputs: [${formattedOutputs}],` : ''}${standaloneOption}
|
|
81
115
|
})
|
|
82
116
|
export class ${tagNameAsPascal} {
|
|
83
117
|
${propertiesDeclarationText}
|
|
84
118
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
85
119
|
c.detach();
|
|
86
|
-
this.el = r.nativeElement
|
|
87
|
-
? `
|
|
88
|
-
proxyOutputs(this, this.el, [${formattedOutputs}]);`
|
|
89
|
-
: ''}
|
|
120
|
+
this.el = r.nativeElement;
|
|
90
121
|
}
|
|
91
122
|
}`;
|
|
92
123
|
return output;
|
package/dist/index.cjs.js
CHANGED
|
@@ -15,6 +15,7 @@ const OutputTypes = {
|
|
|
15
15
|
Standalone: 'standalone',
|
|
16
16
|
};
|
|
17
17
|
const toLowerCase = (str) => str.toLowerCase();
|
|
18
|
+
const mapPropName = (prop) => prop.name;
|
|
18
19
|
const dashToPascalCase = (str) => toLowerCase(str)
|
|
19
20
|
.split('-')
|
|
20
21
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
@@ -169,25 +170,48 @@ function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
|
169
170
|
${eventName}: ${type};`;
|
|
170
171
|
}
|
|
171
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Creates a formatted inputs text with required declaration.
|
|
175
|
+
*
|
|
176
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
177
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
178
|
+
* @returns The inputs list declaration as a string.
|
|
179
|
+
*/
|
|
180
|
+
function formatInputs(inputs) {
|
|
181
|
+
return inputs
|
|
182
|
+
.map((item) => {
|
|
183
|
+
if (item.required) {
|
|
184
|
+
return `{ name: '${item.name}', required: true }`;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
return `'${item.name}'`;
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
.join(', ');
|
|
191
|
+
}
|
|
172
192
|
/**
|
|
173
193
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
174
194
|
*
|
|
175
195
|
* @param tagName The tag name of the component.
|
|
176
|
-
* @param inputs The inputs of the Stencil component (e.g. ['myInput']).
|
|
177
|
-
* @param outputs The outputs/events of the Stencil component. (e.g. ['myOutput']).
|
|
196
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
178
197
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
179
198
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
180
199
|
* @param standalone Whether to define the component as a standalone component.
|
|
181
200
|
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
201
|
+
* @param events The events of the Stencil component for generating outputs.
|
|
182
202
|
* @returns The component declaration as a string.
|
|
183
203
|
*/
|
|
184
|
-
const createAngularComponentDefinition = (tagName, inputs,
|
|
204
|
+
const createAngularComponentDefinition = (tagName, inputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = [], events = []) => {
|
|
185
205
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
206
|
+
const outputs = events.filter((event) => !event.internal).map((event) => event.name);
|
|
186
207
|
const hasInputs = inputs.length > 0;
|
|
187
208
|
const hasOutputs = outputs.length > 0;
|
|
188
209
|
const hasMethods = methods.length > 0;
|
|
189
210
|
// Formats the input strings into comma separated, single quoted values.
|
|
190
|
-
const
|
|
211
|
+
const proxyCmpFormattedInputs = formatToQuotedList(inputs.map(mapPropName));
|
|
212
|
+
// Formats the input strings into comma separated, single quoted values if optional.
|
|
213
|
+
// Formats the required input strings into comma separated {name, required} objects.
|
|
214
|
+
const formattedInputs = formatInputs(inputs);
|
|
191
215
|
// Formats the output strings into comma separated, single quoted values.
|
|
192
216
|
const formattedOutputs = formatToQuotedList(outputs);
|
|
193
217
|
// Formats the method strings into comma separated, single quoted values.
|
|
@@ -198,7 +222,7 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
198
222
|
proxyCmpOptions.push(`\n defineCustomElementFn: ${defineCustomElementFn}`);
|
|
199
223
|
}
|
|
200
224
|
if (hasInputs) {
|
|
201
|
-
proxyCmpOptions.push(`\n inputs: [${
|
|
225
|
+
proxyCmpOptions.push(`\n inputs: [${proxyCmpFormattedInputs}]`);
|
|
202
226
|
}
|
|
203
227
|
if (hasMethods) {
|
|
204
228
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
@@ -208,7 +232,18 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
208
232
|
standaloneOption = `\n standalone: false`;
|
|
209
233
|
}
|
|
210
234
|
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
211
|
-
const
|
|
235
|
+
const outputDeclarations = events
|
|
236
|
+
.filter((event) => !event.internal)
|
|
237
|
+
.map((event) => {
|
|
238
|
+
const camelCaseOutput = event.name.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
|
|
239
|
+
const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
240
|
+
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
241
|
+
});
|
|
242
|
+
const propertiesDeclarationText = [
|
|
243
|
+
`protected el: HTML${tagNameAsPascal}Element;`,
|
|
244
|
+
...propertyDeclarations,
|
|
245
|
+
...outputDeclarations,
|
|
246
|
+
].join('\n ');
|
|
212
247
|
/**
|
|
213
248
|
* Notes on the generated output:
|
|
214
249
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -222,16 +257,13 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
222
257
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
223
258
|
template: '<ng-content></ng-content>',
|
|
224
259
|
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
225
|
-
inputs: [${formattedInputs}],${standaloneOption}
|
|
260
|
+
inputs: [${formattedInputs}],${hasOutputs ? `\n outputs: [${formattedOutputs}],` : ''}${standaloneOption}
|
|
226
261
|
})
|
|
227
262
|
export class ${tagNameAsPascal} {
|
|
228
263
|
${propertiesDeclarationText}
|
|
229
264
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
230
265
|
c.detach();
|
|
231
|
-
this.el = r.nativeElement
|
|
232
|
-
? `
|
|
233
|
-
proxyOutputs(this, this.el, [${formattedOutputs}]);`
|
|
234
|
-
: ''}
|
|
266
|
+
this.el = r.nativeElement;
|
|
235
267
|
}
|
|
236
268
|
}`;
|
|
237
269
|
return output;
|
|
@@ -474,16 +506,13 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
474
506
|
*/
|
|
475
507
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef'];
|
|
476
508
|
if (includeOutputImports) {
|
|
477
|
-
angularCoreImports.push('EventEmitter');
|
|
509
|
+
angularCoreImports.push('EventEmitter', 'Output');
|
|
478
510
|
}
|
|
479
511
|
angularCoreImports.push('NgZone');
|
|
480
512
|
/**
|
|
481
513
|
* The collection of named imports from the angular-component-lib/utils.
|
|
482
514
|
*/
|
|
483
515
|
const componentLibImports = ['ProxyCmp'];
|
|
484
|
-
if (includeOutputImports) {
|
|
485
|
-
componentLibImports.push('proxyOutputs');
|
|
486
|
-
}
|
|
487
516
|
if (includeSingleComponentAngularModules) {
|
|
488
517
|
angularCoreImports.push('NgModule');
|
|
489
518
|
}
|
|
@@ -522,7 +551,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
522
551
|
}
|
|
523
552
|
const proxyFileOutput = [];
|
|
524
553
|
const filterInternalProps = (prop) => !prop.internal;
|
|
525
|
-
|
|
554
|
+
// Ensure that virtual properties has required as false.
|
|
555
|
+
const mapInputProp = (prop) => {
|
|
556
|
+
var _a;
|
|
557
|
+
return ({
|
|
558
|
+
name: prop.name,
|
|
559
|
+
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
560
|
+
});
|
|
561
|
+
};
|
|
526
562
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
527
563
|
for (let cmpMeta of components) {
|
|
528
564
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -530,15 +566,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
530
566
|
if (cmpMeta.properties) {
|
|
531
567
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
532
568
|
}
|
|
533
|
-
const inputs = internalProps.map(
|
|
569
|
+
const inputs = internalProps.map(mapInputProp);
|
|
534
570
|
if (cmpMeta.virtualProperties) {
|
|
535
|
-
inputs.push(...cmpMeta.virtualProperties.map(
|
|
536
|
-
}
|
|
537
|
-
inputs.sort();
|
|
538
|
-
const outputs = [];
|
|
539
|
-
if (cmpMeta.events) {
|
|
540
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps).map(mapPropName));
|
|
571
|
+
inputs.push(...cmpMeta.virtualProperties.map(mapInputProp));
|
|
541
572
|
}
|
|
573
|
+
const orderedInputs = sortBy(inputs, (cip) => cip.name);
|
|
542
574
|
const methods = [];
|
|
543
575
|
if (cmpMeta.methods) {
|
|
544
576
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
@@ -550,7 +582,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
550
582
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
551
583
|
* 3. The component interface (using declaration merging for types).
|
|
552
584
|
*/
|
|
553
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName,
|
|
585
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, orderedInputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps, cmpMeta.events || []);
|
|
554
586
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
555
587
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
556
588
|
proxyFileOutput.push(componentDefinition, '\n');
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const OutputTypes = {
|
|
|
7
7
|
Standalone: 'standalone',
|
|
8
8
|
};
|
|
9
9
|
const toLowerCase = (str) => str.toLowerCase();
|
|
10
|
+
const mapPropName = (prop) => prop.name;
|
|
10
11
|
const dashToPascalCase = (str) => toLowerCase(str)
|
|
11
12
|
.split('-')
|
|
12
13
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
|
@@ -161,25 +162,48 @@ function createPropertyDeclaration(prop, type, inlinePropertyAsSetter = false) {
|
|
|
161
162
|
${eventName}: ${type};`;
|
|
162
163
|
}
|
|
163
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Creates a formatted inputs text with required declaration.
|
|
167
|
+
*
|
|
168
|
+
* @param prop A ComponentCompilerEvent or ComponentCompilerProperty to turn into a property declaration.
|
|
169
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
170
|
+
* @returns The inputs list declaration as a string.
|
|
171
|
+
*/
|
|
172
|
+
function formatInputs(inputs) {
|
|
173
|
+
return inputs
|
|
174
|
+
.map((item) => {
|
|
175
|
+
if (item.required) {
|
|
176
|
+
return `{ name: '${item.name}', required: true }`;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
return `'${item.name}'`;
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
.join(', ');
|
|
183
|
+
}
|
|
164
184
|
/**
|
|
165
185
|
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
166
186
|
*
|
|
167
187
|
* @param tagName The tag name of the component.
|
|
168
|
-
* @param inputs The inputs of the Stencil component (e.g. ['myInput']).
|
|
169
|
-
* @param outputs The outputs/events of the Stencil component. (e.g. ['myOutput']).
|
|
188
|
+
* @param inputs The inputs of the Stencil component (e.g. [{name: 'myInput', required: true]).
|
|
170
189
|
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
171
190
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
172
191
|
* @param standalone Whether to define the component as a standalone component.
|
|
173
192
|
* @param inlineComponentProps List of properties that should be inlined into the component definition.
|
|
193
|
+
* @param events The events of the Stencil component for generating outputs.
|
|
174
194
|
* @returns The component declaration as a string.
|
|
175
195
|
*/
|
|
176
|
-
const createAngularComponentDefinition = (tagName, inputs,
|
|
196
|
+
const createAngularComponentDefinition = (tagName, inputs, methods, includeImportCustomElements = false, standalone = false, inlineComponentProps = [], events = []) => {
|
|
177
197
|
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
198
|
+
const outputs = events.filter((event) => !event.internal).map((event) => event.name);
|
|
178
199
|
const hasInputs = inputs.length > 0;
|
|
179
200
|
const hasOutputs = outputs.length > 0;
|
|
180
201
|
const hasMethods = methods.length > 0;
|
|
181
202
|
// Formats the input strings into comma separated, single quoted values.
|
|
182
|
-
const
|
|
203
|
+
const proxyCmpFormattedInputs = formatToQuotedList(inputs.map(mapPropName));
|
|
204
|
+
// Formats the input strings into comma separated, single quoted values if optional.
|
|
205
|
+
// Formats the required input strings into comma separated {name, required} objects.
|
|
206
|
+
const formattedInputs = formatInputs(inputs);
|
|
183
207
|
// Formats the output strings into comma separated, single quoted values.
|
|
184
208
|
const formattedOutputs = formatToQuotedList(outputs);
|
|
185
209
|
// Formats the method strings into comma separated, single quoted values.
|
|
@@ -190,7 +214,7 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
190
214
|
proxyCmpOptions.push(`\n defineCustomElementFn: ${defineCustomElementFn}`);
|
|
191
215
|
}
|
|
192
216
|
if (hasInputs) {
|
|
193
|
-
proxyCmpOptions.push(`\n inputs: [${
|
|
217
|
+
proxyCmpOptions.push(`\n inputs: [${proxyCmpFormattedInputs}]`);
|
|
194
218
|
}
|
|
195
219
|
if (hasMethods) {
|
|
196
220
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
@@ -200,7 +224,18 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
200
224
|
standaloneOption = `\n standalone: false`;
|
|
201
225
|
}
|
|
202
226
|
const propertyDeclarations = inlineComponentProps.map((m) => createPropertyDeclaration(m, `Components.${tagNameAsPascal}['${m.name}']`, true));
|
|
203
|
-
const
|
|
227
|
+
const outputDeclarations = events
|
|
228
|
+
.filter((event) => !event.internal)
|
|
229
|
+
.map((event) => {
|
|
230
|
+
const camelCaseOutput = event.name.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
|
|
231
|
+
const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
|
|
232
|
+
return `@Output() ${camelCaseOutput} = new ${outputType}();`;
|
|
233
|
+
});
|
|
234
|
+
const propertiesDeclarationText = [
|
|
235
|
+
`protected el: HTML${tagNameAsPascal}Element;`,
|
|
236
|
+
...propertyDeclarations,
|
|
237
|
+
...outputDeclarations,
|
|
238
|
+
].join('\n ');
|
|
204
239
|
/**
|
|
205
240
|
* Notes on the generated output:
|
|
206
241
|
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
@@ -214,16 +249,13 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
214
249
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
215
250
|
template: '<ng-content></ng-content>',
|
|
216
251
|
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
217
|
-
inputs: [${formattedInputs}],${standaloneOption}
|
|
252
|
+
inputs: [${formattedInputs}],${hasOutputs ? `\n outputs: [${formattedOutputs}],` : ''}${standaloneOption}
|
|
218
253
|
})
|
|
219
254
|
export class ${tagNameAsPascal} {
|
|
220
255
|
${propertiesDeclarationText}
|
|
221
256
|
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
222
257
|
c.detach();
|
|
223
|
-
this.el = r.nativeElement
|
|
224
|
-
? `
|
|
225
|
-
proxyOutputs(this, this.el, [${formattedOutputs}]);`
|
|
226
|
-
: ''}
|
|
258
|
+
this.el = r.nativeElement;
|
|
227
259
|
}
|
|
228
260
|
}`;
|
|
229
261
|
return output;
|
|
@@ -466,16 +498,13 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
466
498
|
*/
|
|
467
499
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef'];
|
|
468
500
|
if (includeOutputImports) {
|
|
469
|
-
angularCoreImports.push('EventEmitter');
|
|
501
|
+
angularCoreImports.push('EventEmitter', 'Output');
|
|
470
502
|
}
|
|
471
503
|
angularCoreImports.push('NgZone');
|
|
472
504
|
/**
|
|
473
505
|
* The collection of named imports from the angular-component-lib/utils.
|
|
474
506
|
*/
|
|
475
507
|
const componentLibImports = ['ProxyCmp'];
|
|
476
|
-
if (includeOutputImports) {
|
|
477
|
-
componentLibImports.push('proxyOutputs');
|
|
478
|
-
}
|
|
479
508
|
if (includeSingleComponentAngularModules) {
|
|
480
509
|
angularCoreImports.push('NgModule');
|
|
481
510
|
}
|
|
@@ -514,7 +543,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
514
543
|
}
|
|
515
544
|
const proxyFileOutput = [];
|
|
516
545
|
const filterInternalProps = (prop) => !prop.internal;
|
|
517
|
-
|
|
546
|
+
// Ensure that virtual properties has required as false.
|
|
547
|
+
const mapInputProp = (prop) => {
|
|
548
|
+
var _a;
|
|
549
|
+
return ({
|
|
550
|
+
name: prop.name,
|
|
551
|
+
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
552
|
+
});
|
|
553
|
+
};
|
|
518
554
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
519
555
|
for (let cmpMeta of components) {
|
|
520
556
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -522,15 +558,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
522
558
|
if (cmpMeta.properties) {
|
|
523
559
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
524
560
|
}
|
|
525
|
-
const inputs = internalProps.map(
|
|
561
|
+
const inputs = internalProps.map(mapInputProp);
|
|
526
562
|
if (cmpMeta.virtualProperties) {
|
|
527
|
-
inputs.push(...cmpMeta.virtualProperties.map(
|
|
528
|
-
}
|
|
529
|
-
inputs.sort();
|
|
530
|
-
const outputs = [];
|
|
531
|
-
if (cmpMeta.events) {
|
|
532
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps).map(mapPropName));
|
|
563
|
+
inputs.push(...cmpMeta.virtualProperties.map(mapInputProp));
|
|
533
564
|
}
|
|
565
|
+
const orderedInputs = sortBy(inputs, (cip) => cip.name);
|
|
534
566
|
const methods = [];
|
|
535
567
|
if (cmpMeta.methods) {
|
|
536
568
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
@@ -542,7 +574,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
542
574
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
543
575
|
* 3. The component interface (using declaration merging for types).
|
|
544
576
|
*/
|
|
545
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName,
|
|
577
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, orderedInputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps, cmpMeta.events || []);
|
|
546
578
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
547
579
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
548
580
|
proxyFileOutput.push(componentDefinition, '\n');
|
package/dist/output-angular.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCase, createImportStatement, isOutputTypeCustomElementsBuild, OutputTypes, } from './utils';
|
|
2
|
+
import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCase, createImportStatement, isOutputTypeCustomElementsBuild, OutputTypes, mapPropName, } from './utils';
|
|
3
3
|
import { createAngularComponentDefinition, createComponentTypeDefinition } from './generate-angular-component';
|
|
4
4
|
import { generateAngularDirectivesFile } from './generate-angular-directives-file';
|
|
5
5
|
import generateValueAccessors from './generate-value-accessors';
|
|
@@ -49,16 +49,13 @@ export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
49
49
|
*/
|
|
50
50
|
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef'];
|
|
51
51
|
if (includeOutputImports) {
|
|
52
|
-
angularCoreImports.push('EventEmitter');
|
|
52
|
+
angularCoreImports.push('EventEmitter', 'Output');
|
|
53
53
|
}
|
|
54
54
|
angularCoreImports.push('NgZone');
|
|
55
55
|
/**
|
|
56
56
|
* The collection of named imports from the angular-component-lib/utils.
|
|
57
57
|
*/
|
|
58
58
|
const componentLibImports = ['ProxyCmp'];
|
|
59
|
-
if (includeOutputImports) {
|
|
60
|
-
componentLibImports.push('proxyOutputs');
|
|
61
|
-
}
|
|
62
59
|
if (includeSingleComponentAngularModules) {
|
|
63
60
|
angularCoreImports.push('NgModule');
|
|
64
61
|
}
|
|
@@ -97,7 +94,14 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
97
94
|
}
|
|
98
95
|
const proxyFileOutput = [];
|
|
99
96
|
const filterInternalProps = (prop) => !prop.internal;
|
|
100
|
-
|
|
97
|
+
// Ensure that virtual properties has required as false.
|
|
98
|
+
const mapInputProp = (prop) => {
|
|
99
|
+
var _a;
|
|
100
|
+
return ({
|
|
101
|
+
name: prop.name,
|
|
102
|
+
required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
|
|
103
|
+
});
|
|
104
|
+
};
|
|
101
105
|
const { componentCorePackage, customElementsDir } = outputTarget;
|
|
102
106
|
for (let cmpMeta of components) {
|
|
103
107
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
@@ -105,15 +109,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
105
109
|
if (cmpMeta.properties) {
|
|
106
110
|
internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
|
|
107
111
|
}
|
|
108
|
-
const inputs = internalProps.map(
|
|
112
|
+
const inputs = internalProps.map(mapInputProp);
|
|
109
113
|
if (cmpMeta.virtualProperties) {
|
|
110
|
-
inputs.push(...cmpMeta.virtualProperties.map(
|
|
111
|
-
}
|
|
112
|
-
inputs.sort();
|
|
113
|
-
const outputs = [];
|
|
114
|
-
if (cmpMeta.events) {
|
|
115
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps).map(mapPropName));
|
|
114
|
+
inputs.push(...cmpMeta.virtualProperties.map(mapInputProp));
|
|
116
115
|
}
|
|
116
|
+
const orderedInputs = sortBy(inputs, (cip) => cip.name);
|
|
117
117
|
const methods = [];
|
|
118
118
|
if (cmpMeta.methods) {
|
|
119
119
|
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
@@ -125,7 +125,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
125
125
|
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
126
126
|
* 3. The component interface (using declaration merging for types).
|
|
127
127
|
*/
|
|
128
|
-
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName,
|
|
128
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, orderedInputs, methods, isCustomElementsBuild, isStandaloneBuild, inlineComponentProps, cmpMeta.events || []);
|
|
129
129
|
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
130
130
|
const componentTypeDefinition = createComponentTypeDefinition(outputType, tagNameAsPascal, cmpMeta.events, componentCorePackage, customElementsDir);
|
|
131
131
|
proxyFileOutput.push(componentDefinition, '\n');
|
package/dist/types.d.ts
CHANGED
package/dist/utils.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ export declare const OutputTypes: {
|
|
|
4
4
|
[key: string]: OutputType;
|
|
5
5
|
};
|
|
6
6
|
export declare const toLowerCase: (str: string) => string;
|
|
7
|
+
export declare const mapPropName: (prop: {
|
|
8
|
+
name: string;
|
|
9
|
+
}) => string;
|
|
7
10
|
export declare const dashToPascalCase: (str: string) => string;
|
|
8
11
|
export declare function sortBy<T>(array: T[], prop: (item: T) => string): T[];
|
|
9
12
|
export declare function normalizePath(str: string): string;
|
package/dist/utils.js
CHANGED
|
@@ -5,6 +5,7 @@ export const OutputTypes = {
|
|
|
5
5
|
Standalone: 'standalone',
|
|
6
6
|
};
|
|
7
7
|
export const toLowerCase = (str) => str.toLowerCase();
|
|
8
|
+
export const mapPropName = (prop) => prop.name;
|
|
8
9
|
export const dashToPascalCase = (str) => toLowerCase(str)
|
|
9
10
|
.split('-')
|
|
10
11
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stencil/angular-output-target",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Angular output target for @stencil/core components.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@angular/core": "8.2.14",
|
|
52
52
|
"@angular/forms": "8.2.14",
|
|
53
|
-
"@stencil/core": "4.
|
|
53
|
+
"@stencil/core": "4.35.1",
|
|
54
54
|
"@types/node": "^18.0.0",
|
|
55
55
|
"npm-run-all2": "^6.2.4",
|
|
56
56
|
"rimraf": "^5.0.0",
|