@stencil/angular-output-target 0.6.1-dev.11662577182.1c45c8de → 0.6.1-dev.11667512184.13935f80
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/README.md +2 -2
- package/angular-component-lib/utils.ts +24 -0
- package/dist/generate-angular-component.d.ts +1 -1
- package/dist/generate-angular-component.js +60 -34
- package/dist/generate-angular-directives-file.js +1 -1
- package/dist/generate-value-accessors.js +1 -1
- package/dist/index.cjs.js +72 -81
- package/dist/index.js +72 -81
- package/dist/output-angular.js +6 -22
- package/dist/plugin.js +4 -7
- package/dist/types.d.ts +3 -11
- package/package.json +2 -2
- package/dist/generate-angular-modules.d.ts +0 -6
- package/dist/generate-angular-modules.js +0 -17
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ export const config: Config = {
|
|
|
27
27
|
outputTargets: [
|
|
28
28
|
angularOutputTarget({
|
|
29
29
|
componentCorePackage: 'component-library',
|
|
30
|
-
|
|
30
|
+
directivesProxyFile: '../component-library-angular/src/directives/proxies.ts',
|
|
31
31
|
directivesArrayFile: '../component-library-angular/src/directives/index.ts',
|
|
32
32
|
}),
|
|
33
33
|
{
|
|
@@ -43,7 +43,7 @@ export const config: Config = {
|
|
|
43
43
|
| Property | Description |
|
|
44
44
|
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
45
|
| `componentCorePackage` | The NPM package name of your Stencil component library. This package is used as a dependency for your Angular wrappers. |
|
|
46
|
-
| `
|
|
46
|
+
| `directivesProxyFile` | The output file of all the component wrappers generated by the output target. This file path should point to a location within your Angular library/project. |
|
|
47
47
|
| `directivesArrayFile` | The output file of a constant of all the generated component wrapper classes. Used for easily declaring and exporting the generated components from an `NgModule`. This file path should point to a location within your Angular library/project. |
|
|
48
48
|
| `valueAccessorConfigs` | The configuration object for how individual web components behave with Angular control value accessors. |
|
|
49
49
|
| `excludeComponents` | An array of tag names to exclude from generating component wrappers for. This is helpful when have a custom framework implementation of a specific component or need to extend the base component wrapper behavior. |
|
|
@@ -36,6 +36,28 @@ export const defineCustomElement = (tagName: string, customElement: any) => {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The Angular property name that contains the object of metadata properties
|
|
41
|
+
* for the component added by the Angular compiler.
|
|
42
|
+
*/
|
|
43
|
+
const NG_COMP_DEF = 'ɵcmp';
|
|
44
|
+
|
|
45
|
+
export const clearAngularOutputBindings = (cls: any) => {
|
|
46
|
+
if (typeof cls === 'function' && cls !== null) {
|
|
47
|
+
if (cls.prototype.constructor) {
|
|
48
|
+
const instance = cls.prototype.constructor;
|
|
49
|
+
if (instance[NG_COMP_DEF]) {
|
|
50
|
+
/**
|
|
51
|
+
* With the output targets generating @Output() proxies, we need to
|
|
52
|
+
* clear the metadata (ɵcmp.outputs) so that Angular does not add its own event listener
|
|
53
|
+
* and cause duplicate event emissions for the web component events.
|
|
54
|
+
*/
|
|
55
|
+
instance[NG_COMP_DEF].outputs = {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
39
61
|
// tslint:disable-next-line: only-arrow-functions
|
|
40
62
|
export function ProxyCmp(opts: { defineCustomElementFn?: () => void; inputs?: any; methods?: any }) {
|
|
41
63
|
const decorator = function (cls: any) {
|
|
@@ -45,6 +67,8 @@ export function ProxyCmp(opts: { defineCustomElementFn?: () => void; inputs?: an
|
|
|
45
67
|
defineCustomElementFn();
|
|
46
68
|
}
|
|
47
69
|
|
|
70
|
+
clearAngularOutputBindings(cls);
|
|
71
|
+
|
|
48
72
|
if (inputs) {
|
|
49
73
|
proxyInputs(cls, inputs);
|
|
50
74
|
}
|
|
@@ -9,7 +9,7 @@ import type { ComponentCompilerEvent } from '@stencil/core/internal';
|
|
|
9
9
|
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
10
10
|
* @returns The component declaration as a string.
|
|
11
11
|
*/
|
|
12
|
-
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly string[], outputs: readonly
|
|
12
|
+
export declare const createAngularComponentDefinition: (tagName: string, inputs: readonly string[], outputs: readonly ComponentCompilerEvent[], methods: readonly string[], includeImportCustomElements?: boolean) => string;
|
|
13
13
|
/**
|
|
14
14
|
* Creates the component interface type definition.
|
|
15
15
|
* @param tagNameAsPascal The tag name as PascalCase.
|
|
@@ -17,9 +17,11 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
17
17
|
// Formats the input strings into comma separated, single quoted values.
|
|
18
18
|
const formattedInputs = formatToQuotedList(inputs);
|
|
19
19
|
// Formats the output strings into comma separated, single quoted values.
|
|
20
|
-
const formattedOutputs = formatToQuotedList(outputs);
|
|
20
|
+
const formattedOutputs = formatToQuotedList(outputs.map((event) => event.name));
|
|
21
21
|
// Formats the method strings into comma separated, single quoted values.
|
|
22
22
|
const formattedMethods = formatToQuotedList(methods);
|
|
23
|
+
// The collection of @Output() decorators for the component.
|
|
24
|
+
const outputDecorators = outputs.map((event) => createAngularOutputDecorator(tagName, event));
|
|
23
25
|
const proxyCmpOptions = [];
|
|
24
26
|
if (includeImportCustomElements) {
|
|
25
27
|
const defineCustomElementFn = `define${tagNameAsPascal}`;
|
|
@@ -31,32 +33,54 @@ export const createAngularComponentDefinition = (tagName, inputs, outputs, metho
|
|
|
31
33
|
if (hasMethods) {
|
|
32
34
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
33
35
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
47
|
-
inputs: [${formattedInputs}]
|
|
48
|
-
})
|
|
49
|
-
export class ${tagNameAsPascal} {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
36
|
+
const output = [
|
|
37
|
+
`@ProxyCmp({${proxyCmpOptions.join(',')}\n})`,
|
|
38
|
+
`@Component({`,
|
|
39
|
+
` selector: '${tagName}',`,
|
|
40
|
+
` changeDetection: ChangeDetectionStrategy.OnPush,`,
|
|
41
|
+
` template: '<ng-content></ng-content>',`,
|
|
42
|
+
/**
|
|
43
|
+
* We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
44
|
+
* Angular does not complain about the inputs property. The output target
|
|
45
|
+
* uses the inputs property to define the inputs of the component instead of
|
|
46
|
+
* having to use the @Input decorator (and manually define the type and default value).
|
|
47
|
+
*/
|
|
48
|
+
` // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property`,
|
|
49
|
+
` inputs: [${formattedInputs}],`,
|
|
50
|
+
`})`,
|
|
51
|
+
`export class ${tagNameAsPascal} {`,
|
|
52
|
+
];
|
|
53
|
+
if (outputDecorators.length > 0) {
|
|
54
|
+
for (let outputDecorator of outputDecorators) {
|
|
55
|
+
output.push(` ${outputDecorator}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
output.push(` protected el: HTMLElement;`);
|
|
59
|
+
output.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {`);
|
|
60
|
+
output.push(` c.detach();`);
|
|
61
|
+
output.push(` this.el = r.nativeElement;`);
|
|
62
|
+
if (hasOutputs) {
|
|
63
|
+
output.push(` proxyOutputs(this, this.el, [${formattedOutputs}]);`);
|
|
64
|
+
}
|
|
65
|
+
output.push(` }`);
|
|
66
|
+
output.push(`}`);
|
|
67
|
+
return output.join('\n');
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Creates an `@Output()` decorator for a custom event on a Stencil component.
|
|
71
|
+
* @param tagName The tag name of the component.
|
|
72
|
+
* @param event The Stencil component event.
|
|
73
|
+
* @returns The `@Output()` decorator as a string.
|
|
74
|
+
*/
|
|
75
|
+
const createAngularOutputDecorator = (tagName, event) => {
|
|
76
|
+
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
77
|
+
// When updating to Stencil 3.0, the component custom event generic will be
|
|
78
|
+
// exported by default and can be referenced here with:
|
|
79
|
+
// const customEvent = `${tagNameAsPascal}CustomEvent`;
|
|
80
|
+
const customEventType = `CustomEvent`;
|
|
81
|
+
const eventType = formatOutputType(tagNameAsPascal, event);
|
|
82
|
+
const outputType = `${customEventType}<${eventType}>`;
|
|
83
|
+
return `@Output() ${event.name}: EventEmitter<${outputType}> = new EventEmitter();`;
|
|
60
84
|
};
|
|
61
85
|
/**
|
|
62
86
|
* Sanitizes and formats the component event type.
|
|
@@ -74,9 +98,10 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
74
98
|
.filter(([_, refObject]) => refObject.location === 'local' || refObject.location === 'import')
|
|
75
99
|
.reduce((type, [src, dst]) => {
|
|
76
100
|
const renamedType = `I${componentClassName}${type}`;
|
|
77
|
-
return renamedType
|
|
101
|
+
return (renamedType
|
|
78
102
|
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
79
|
-
|
|
103
|
+
// Capture all instances of the `src` field surrounded by non-word characters on each side and join them.
|
|
104
|
+
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join('')));
|
|
80
105
|
}, event.complexType.original
|
|
81
106
|
.replace(/\n/g, ' ')
|
|
82
107
|
.replace(/\s{2,}/g, ' ')
|
|
@@ -105,21 +130,22 @@ const createDocComment = (doc) => {
|
|
|
105
130
|
* @returns The component interface type definition as a string.
|
|
106
131
|
*/
|
|
107
132
|
export const createComponentTypeDefinition = (tagNameAsPascal, events, componentCorePackage, includeImportCustomElements = false, customElementsDir) => {
|
|
108
|
-
const publicEvents = events.filter(ev => !ev.internal);
|
|
133
|
+
const publicEvents = events.filter((ev) => !ev.internal);
|
|
109
134
|
const eventTypeImports = createComponentEventTypeImports(tagNameAsPascal, publicEvents, {
|
|
110
135
|
componentCorePackage,
|
|
111
136
|
includeImportCustomElements,
|
|
112
137
|
customElementsDir,
|
|
113
138
|
});
|
|
114
|
-
const eventTypes = publicEvents
|
|
115
|
-
.map((event) => {
|
|
139
|
+
const eventTypes = publicEvents.map((event) => {
|
|
116
140
|
const comment = createDocComment(event.docs);
|
|
117
141
|
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
118
142
|
${event.name}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
119
143
|
});
|
|
120
144
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
121
|
-
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '')
|
|
122
|
-
|
|
145
|
+
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
146
|
+
`${interfaceDeclaration}${eventTypes.length === 0
|
|
147
|
+
? '}'
|
|
148
|
+
: `
|
|
123
149
|
${eventTypes.join('\n')}
|
|
124
150
|
}`}`;
|
|
125
151
|
return typeDefinition;
|
|
@@ -4,7 +4,7 @@ export function generateAngularDirectivesFile(compilerCtx, components, outputTar
|
|
|
4
4
|
if (!outputTarget.directivesArrayFile) {
|
|
5
5
|
return Promise.resolve();
|
|
6
6
|
}
|
|
7
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
7
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.directivesProxyFile, '.ts');
|
|
8
8
|
const directives = components
|
|
9
9
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
10
10
|
.map((className) => `d.${className}`)
|
|
@@ -4,7 +4,7 @@ export default async function generateValueAccessors(compilerCtx, components, ou
|
|
|
4
4
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
7
|
-
const targetDir = path.dirname(outputTarget.
|
|
7
|
+
const targetDir = path.dirname(outputTarget.directivesProxyFile);
|
|
8
8
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
9
9
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
10
10
|
const type = va.type;
|
package/dist/index.cjs.js
CHANGED
|
@@ -148,9 +148,11 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
148
148
|
// Formats the input strings into comma separated, single quoted values.
|
|
149
149
|
const formattedInputs = formatToQuotedList(inputs);
|
|
150
150
|
// Formats the output strings into comma separated, single quoted values.
|
|
151
|
-
const formattedOutputs = formatToQuotedList(outputs);
|
|
151
|
+
const formattedOutputs = formatToQuotedList(outputs.map((event) => event.name));
|
|
152
152
|
// Formats the method strings into comma separated, single quoted values.
|
|
153
153
|
const formattedMethods = formatToQuotedList(methods);
|
|
154
|
+
// The collection of @Output() decorators for the component.
|
|
155
|
+
const outputDecorators = outputs.map((event) => createAngularOutputDecorator(tagName, event));
|
|
154
156
|
const proxyCmpOptions = [];
|
|
155
157
|
if (includeImportCustomElements) {
|
|
156
158
|
const defineCustomElementFn = `define${tagNameAsPascal}`;
|
|
@@ -162,32 +164,54 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
162
164
|
if (hasMethods) {
|
|
163
165
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
164
166
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
178
|
-
inputs: [${formattedInputs}]
|
|
179
|
-
})
|
|
180
|
-
export class ${tagNameAsPascal} {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
167
|
+
const output = [
|
|
168
|
+
`@ProxyCmp({${proxyCmpOptions.join(',')}\n})`,
|
|
169
|
+
`@Component({`,
|
|
170
|
+
` selector: '${tagName}',`,
|
|
171
|
+
` changeDetection: ChangeDetectionStrategy.OnPush,`,
|
|
172
|
+
` template: '<ng-content></ng-content>',`,
|
|
173
|
+
/**
|
|
174
|
+
* We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
175
|
+
* Angular does not complain about the inputs property. The output target
|
|
176
|
+
* uses the inputs property to define the inputs of the component instead of
|
|
177
|
+
* having to use the @Input decorator (and manually define the type and default value).
|
|
178
|
+
*/
|
|
179
|
+
` // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property`,
|
|
180
|
+
` inputs: [${formattedInputs}],`,
|
|
181
|
+
`})`,
|
|
182
|
+
`export class ${tagNameAsPascal} {`,
|
|
183
|
+
];
|
|
184
|
+
if (outputDecorators.length > 0) {
|
|
185
|
+
for (let outputDecorator of outputDecorators) {
|
|
186
|
+
output.push(` ${outputDecorator}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
output.push(` protected el: HTMLElement;`);
|
|
190
|
+
output.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {`);
|
|
191
|
+
output.push(` c.detach();`);
|
|
192
|
+
output.push(` this.el = r.nativeElement;`);
|
|
193
|
+
if (hasOutputs) {
|
|
194
|
+
output.push(` proxyOutputs(this, this.el, [${formattedOutputs}]);`);
|
|
195
|
+
}
|
|
196
|
+
output.push(` }`);
|
|
197
|
+
output.push(`}`);
|
|
198
|
+
return output.join('\n');
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Creates an `@Output()` decorator for a custom event on a Stencil component.
|
|
202
|
+
* @param tagName The tag name of the component.
|
|
203
|
+
* @param event The Stencil component event.
|
|
204
|
+
* @returns The `@Output()` decorator as a string.
|
|
205
|
+
*/
|
|
206
|
+
const createAngularOutputDecorator = (tagName, event) => {
|
|
207
|
+
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
208
|
+
// When updating to Stencil 3.0, the component custom event generic will be
|
|
209
|
+
// exported by default and can be referenced here with:
|
|
210
|
+
// const customEvent = `${tagNameAsPascal}CustomEvent`;
|
|
211
|
+
const customEventType = `CustomEvent`;
|
|
212
|
+
const eventType = formatOutputType(tagNameAsPascal, event);
|
|
213
|
+
const outputType = `${customEventType}<${eventType}>`;
|
|
214
|
+
return `@Output() ${event.name}: EventEmitter<${outputType}> = new EventEmitter();`;
|
|
191
215
|
};
|
|
192
216
|
/**
|
|
193
217
|
* Sanitizes and formats the component event type.
|
|
@@ -205,9 +229,10 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
205
229
|
.filter(([_, refObject]) => refObject.location === 'local' || refObject.location === 'import')
|
|
206
230
|
.reduce((type, [src, dst]) => {
|
|
207
231
|
const renamedType = `I${componentClassName}${type}`;
|
|
208
|
-
return renamedType
|
|
232
|
+
return (renamedType
|
|
209
233
|
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
210
|
-
|
|
234
|
+
// Capture all instances of the `src` field surrounded by non-word characters on each side and join them.
|
|
235
|
+
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join('')));
|
|
211
236
|
}, event.complexType.original
|
|
212
237
|
.replace(/\n/g, ' ')
|
|
213
238
|
.replace(/\s{2,}/g, ' ')
|
|
@@ -236,21 +261,22 @@ const createDocComment = (doc) => {
|
|
|
236
261
|
* @returns The component interface type definition as a string.
|
|
237
262
|
*/
|
|
238
263
|
const createComponentTypeDefinition = (tagNameAsPascal, events, componentCorePackage, includeImportCustomElements = false, customElementsDir) => {
|
|
239
|
-
const publicEvents = events.filter(ev => !ev.internal);
|
|
264
|
+
const publicEvents = events.filter((ev) => !ev.internal);
|
|
240
265
|
const eventTypeImports = createComponentEventTypeImports(tagNameAsPascal, publicEvents, {
|
|
241
266
|
componentCorePackage,
|
|
242
267
|
includeImportCustomElements,
|
|
243
268
|
customElementsDir,
|
|
244
269
|
});
|
|
245
|
-
const eventTypes = publicEvents
|
|
246
|
-
.map((event) => {
|
|
270
|
+
const eventTypes = publicEvents.map((event) => {
|
|
247
271
|
const comment = createDocComment(event.docs);
|
|
248
272
|
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
249
273
|
${event.name}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
250
274
|
});
|
|
251
275
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
252
|
-
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '')
|
|
253
|
-
|
|
276
|
+
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
277
|
+
`${interfaceDeclaration}${eventTypes.length === 0
|
|
278
|
+
? '}'
|
|
279
|
+
: `
|
|
254
280
|
${eventTypes.join('\n')}
|
|
255
281
|
}`}`;
|
|
256
282
|
return typeDefinition;
|
|
@@ -261,7 +287,7 @@ function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
|
261
287
|
if (!outputTarget.directivesArrayFile) {
|
|
262
288
|
return Promise.resolve();
|
|
263
289
|
}
|
|
264
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
290
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.directivesProxyFile, '.ts');
|
|
265
291
|
const directives = components
|
|
266
292
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
267
293
|
.map((className) => `d.${className}`)
|
|
@@ -280,7 +306,7 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
280
306
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
281
307
|
return;
|
|
282
308
|
}
|
|
283
|
-
const targetDir = path__default['default'].dirname(outputTarget.
|
|
309
|
+
const targetDir = path__default['default'].dirname(outputTarget.directivesProxyFile);
|
|
284
310
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
285
311
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
286
312
|
const type = va.type;
|
|
@@ -331,30 +357,13 @@ const VALUE_ACCESSOR_EVENT = `<VALUE_ACCESSOR_EVENT>`;
|
|
|
331
357
|
const VALUE_ACCESSOR_TARGETATTR = '<VALUE_ACCESSOR_TARGETATTR>';
|
|
332
358
|
const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChangeEvent($event.target.<VALUE_ACCESSOR_TARGETATTR>)'`;
|
|
333
359
|
|
|
334
|
-
/**
|
|
335
|
-
* Creates an Angular module declaration for a component wrapper.
|
|
336
|
-
* @param componentTagName The tag name of the Stencil component.
|
|
337
|
-
* @returns The Angular module declaration as a string.
|
|
338
|
-
*/
|
|
339
|
-
const generateAngularModuleForComponent = (componentTagName) => {
|
|
340
|
-
const tagNameAsPascal = dashToPascalCase(componentTagName);
|
|
341
|
-
const componentClassName = `${tagNameAsPascal}`;
|
|
342
|
-
const moduleClassName = `${tagNameAsPascal}Module`;
|
|
343
|
-
const moduleDefinition = `@NgModule({
|
|
344
|
-
declarations: [${componentClassName}],
|
|
345
|
-
exports: [${componentClassName}]
|
|
346
|
-
})
|
|
347
|
-
export class ${moduleClassName} { }`;
|
|
348
|
-
return moduleDefinition;
|
|
349
|
-
};
|
|
350
|
-
|
|
351
360
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
352
361
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
353
362
|
const rootDir = config.rootDir;
|
|
354
363
|
const pkgData = await readPackageJson(config, rootDir);
|
|
355
364
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
356
365
|
await Promise.all([
|
|
357
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
366
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
358
367
|
copyResources$1(config, outputTarget),
|
|
359
368
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
360
369
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -368,7 +377,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
368
377
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
369
378
|
}
|
|
370
379
|
const srcDirectory = path__default['default'].join(__dirname, '..', 'angular-component-lib');
|
|
371
|
-
const destDirectory = path__default['default'].join(path__default['default'].dirname(outputTarget.
|
|
380
|
+
const destDirectory = path__default['default'].join(path__default['default'].dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
372
381
|
return config.sys.copy([
|
|
373
382
|
{
|
|
374
383
|
src: srcDirectory,
|
|
@@ -379,11 +388,9 @@ async function copyResources$1(config, outputTarget) {
|
|
|
379
388
|
], srcDirectory);
|
|
380
389
|
}
|
|
381
390
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
382
|
-
var _a;
|
|
383
391
|
const distTypesDir = path__default['default'].dirname(pkgData.types);
|
|
384
392
|
const dtsFilePath = path__default['default'].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
385
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
386
|
-
const createSingleComponentAngularModules = (_a = outputTarget.createSingleComponentAngularModules) !== null && _a !== void 0 ? _a : false;
|
|
393
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
387
394
|
/**
|
|
388
395
|
* The collection of named imports from @angular/core.
|
|
389
396
|
*/
|
|
@@ -394,14 +401,12 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
394
401
|
'ElementRef',
|
|
395
402
|
'EventEmitter',
|
|
396
403
|
'NgZone',
|
|
404
|
+
'Output',
|
|
397
405
|
];
|
|
398
406
|
/**
|
|
399
407
|
* The collection of named imports from the angular-component-lib/utils.
|
|
400
408
|
*/
|
|
401
409
|
const componentLibImports = ['ProxyCmp', 'proxyOutputs'];
|
|
402
|
-
if (createSingleComponentAngularModules) {
|
|
403
|
-
angularCoreImports.push('NgModule');
|
|
404
|
-
}
|
|
405
410
|
const imports = `/* tslint:disable */
|
|
406
411
|
/* auto-generated angular directive proxies */
|
|
407
412
|
${createImportStatement(angularCoreImports, '@angular/core')}
|
|
@@ -437,12 +442,6 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
437
442
|
});
|
|
438
443
|
sourceImports = cmpImports.join('\n');
|
|
439
444
|
}
|
|
440
|
-
if (createSingleComponentAngularModules) {
|
|
441
|
-
// Generating Angular modules is only supported in the dist-custom-elements build
|
|
442
|
-
if (!outputTarget.includeImportCustomElements) {
|
|
443
|
-
throw new Error('Generating single component Angular modules requires the "includeImportCustomElements" option to be set to true.');
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
445
|
const proxyFileOutput = [];
|
|
447
446
|
const filterInternalProps = (prop) => !prop.internal;
|
|
448
447
|
const mapPropName = (prop) => prop.name;
|
|
@@ -459,7 +458,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
459
458
|
inputs.sort();
|
|
460
459
|
const outputs = [];
|
|
461
460
|
if (cmpMeta.events) {
|
|
462
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
461
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
463
462
|
}
|
|
464
463
|
const methods = [];
|
|
465
464
|
if (cmpMeta.methods) {
|
|
@@ -468,16 +467,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
468
467
|
/**
|
|
469
468
|
* For each component, we need to generate:
|
|
470
469
|
* 1. The @Component decorated class
|
|
471
|
-
* 2.
|
|
472
|
-
* 3. The component interface (using declaration merging for types).
|
|
470
|
+
* 2. The component interface (using declaration merging for types).
|
|
473
471
|
*/
|
|
474
472
|
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, includeImportCustomElements);
|
|
475
|
-
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
476
473
|
const componentTypeDefinition = createComponentTypeDefinition(tagNameAsPascal, cmpMeta.events, componentCorePackage, includeImportCustomElements, customElementsDir);
|
|
477
474
|
proxyFileOutput.push(componentDefinition, '\n');
|
|
478
|
-
if (createSingleComponentAngularModules) {
|
|
479
|
-
proxyFileOutput.push(moduleDefinition, '\n');
|
|
480
|
-
}
|
|
481
475
|
proxyFileOutput.push(componentTypeDefinition, '\n');
|
|
482
476
|
}
|
|
483
477
|
const final = [imports, typeImports, sourceImports, ...proxyFileOutput];
|
|
@@ -503,14 +497,11 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
503
497
|
if (config.rootDir == null) {
|
|
504
498
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
505
499
|
}
|
|
506
|
-
if (outputTarget.directivesProxyFile
|
|
507
|
-
throw new Error('directivesProxyFile
|
|
508
|
-
}
|
|
509
|
-
if (outputTarget.proxyDeclarationFile == null) {
|
|
510
|
-
throw new Error('proxyDeclarationFile is required. Please set it in the Stencil config.');
|
|
500
|
+
if (outputTarget.directivesProxyFile == null) {
|
|
501
|
+
throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
|
|
511
502
|
}
|
|
512
|
-
if (outputTarget.
|
|
513
|
-
results.
|
|
503
|
+
if (outputTarget.directivesProxyFile && !path__default['default'].isAbsolute(outputTarget.directivesProxyFile)) {
|
|
504
|
+
results.directivesProxyFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesProxyFile));
|
|
514
505
|
}
|
|
515
506
|
if (outputTarget.directivesArrayFile && !path__default['default'].isAbsolute(outputTarget.directivesArrayFile)) {
|
|
516
507
|
results.directivesArrayFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesArrayFile));
|
package/dist/index.js
CHANGED
|
@@ -140,9 +140,11 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
140
140
|
// Formats the input strings into comma separated, single quoted values.
|
|
141
141
|
const formattedInputs = formatToQuotedList(inputs);
|
|
142
142
|
// Formats the output strings into comma separated, single quoted values.
|
|
143
|
-
const formattedOutputs = formatToQuotedList(outputs);
|
|
143
|
+
const formattedOutputs = formatToQuotedList(outputs.map((event) => event.name));
|
|
144
144
|
// Formats the method strings into comma separated, single quoted values.
|
|
145
145
|
const formattedMethods = formatToQuotedList(methods);
|
|
146
|
+
// The collection of @Output() decorators for the component.
|
|
147
|
+
const outputDecorators = outputs.map((event) => createAngularOutputDecorator(tagName, event));
|
|
146
148
|
const proxyCmpOptions = [];
|
|
147
149
|
if (includeImportCustomElements) {
|
|
148
150
|
const defineCustomElementFn = `define${tagNameAsPascal}`;
|
|
@@ -154,32 +156,54 @@ const createAngularComponentDefinition = (tagName, inputs, outputs, methods, inc
|
|
|
154
156
|
if (hasMethods) {
|
|
155
157
|
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
156
158
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
170
|
-
inputs: [${formattedInputs}]
|
|
171
|
-
})
|
|
172
|
-
export class ${tagNameAsPascal} {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
159
|
+
const output = [
|
|
160
|
+
`@ProxyCmp({${proxyCmpOptions.join(',')}\n})`,
|
|
161
|
+
`@Component({`,
|
|
162
|
+
` selector: '${tagName}',`,
|
|
163
|
+
` changeDetection: ChangeDetectionStrategy.OnPush,`,
|
|
164
|
+
` template: '<ng-content></ng-content>',`,
|
|
165
|
+
/**
|
|
166
|
+
* We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
167
|
+
* Angular does not complain about the inputs property. The output target
|
|
168
|
+
* uses the inputs property to define the inputs of the component instead of
|
|
169
|
+
* having to use the @Input decorator (and manually define the type and default value).
|
|
170
|
+
*/
|
|
171
|
+
` // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property`,
|
|
172
|
+
` inputs: [${formattedInputs}],`,
|
|
173
|
+
`})`,
|
|
174
|
+
`export class ${tagNameAsPascal} {`,
|
|
175
|
+
];
|
|
176
|
+
if (outputDecorators.length > 0) {
|
|
177
|
+
for (let outputDecorator of outputDecorators) {
|
|
178
|
+
output.push(` ${outputDecorator}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
output.push(` protected el: HTMLElement;`);
|
|
182
|
+
output.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {`);
|
|
183
|
+
output.push(` c.detach();`);
|
|
184
|
+
output.push(` this.el = r.nativeElement;`);
|
|
185
|
+
if (hasOutputs) {
|
|
186
|
+
output.push(` proxyOutputs(this, this.el, [${formattedOutputs}]);`);
|
|
187
|
+
}
|
|
188
|
+
output.push(` }`);
|
|
189
|
+
output.push(`}`);
|
|
190
|
+
return output.join('\n');
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Creates an `@Output()` decorator for a custom event on a Stencil component.
|
|
194
|
+
* @param tagName The tag name of the component.
|
|
195
|
+
* @param event The Stencil component event.
|
|
196
|
+
* @returns The `@Output()` decorator as a string.
|
|
197
|
+
*/
|
|
198
|
+
const createAngularOutputDecorator = (tagName, event) => {
|
|
199
|
+
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
200
|
+
// When updating to Stencil 3.0, the component custom event generic will be
|
|
201
|
+
// exported by default and can be referenced here with:
|
|
202
|
+
// const customEvent = `${tagNameAsPascal}CustomEvent`;
|
|
203
|
+
const customEventType = `CustomEvent`;
|
|
204
|
+
const eventType = formatOutputType(tagNameAsPascal, event);
|
|
205
|
+
const outputType = `${customEventType}<${eventType}>`;
|
|
206
|
+
return `@Output() ${event.name}: EventEmitter<${outputType}> = new EventEmitter();`;
|
|
183
207
|
};
|
|
184
208
|
/**
|
|
185
209
|
* Sanitizes and formats the component event type.
|
|
@@ -197,9 +221,10 @@ const formatOutputType = (componentClassName, event) => {
|
|
|
197
221
|
.filter(([_, refObject]) => refObject.location === 'local' || refObject.location === 'import')
|
|
198
222
|
.reduce((type, [src, dst]) => {
|
|
199
223
|
const renamedType = `I${componentClassName}${type}`;
|
|
200
|
-
return renamedType
|
|
224
|
+
return (renamedType
|
|
201
225
|
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
202
|
-
|
|
226
|
+
// Capture all instances of the `src` field surrounded by non-word characters on each side and join them.
|
|
227
|
+
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join('')));
|
|
203
228
|
}, event.complexType.original
|
|
204
229
|
.replace(/\n/g, ' ')
|
|
205
230
|
.replace(/\s{2,}/g, ' ')
|
|
@@ -228,21 +253,22 @@ const createDocComment = (doc) => {
|
|
|
228
253
|
* @returns The component interface type definition as a string.
|
|
229
254
|
*/
|
|
230
255
|
const createComponentTypeDefinition = (tagNameAsPascal, events, componentCorePackage, includeImportCustomElements = false, customElementsDir) => {
|
|
231
|
-
const publicEvents = events.filter(ev => !ev.internal);
|
|
256
|
+
const publicEvents = events.filter((ev) => !ev.internal);
|
|
232
257
|
const eventTypeImports = createComponentEventTypeImports(tagNameAsPascal, publicEvents, {
|
|
233
258
|
componentCorePackage,
|
|
234
259
|
includeImportCustomElements,
|
|
235
260
|
customElementsDir,
|
|
236
261
|
});
|
|
237
|
-
const eventTypes = publicEvents
|
|
238
|
-
.map((event) => {
|
|
262
|
+
const eventTypes = publicEvents.map((event) => {
|
|
239
263
|
const comment = createDocComment(event.docs);
|
|
240
264
|
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
241
265
|
${event.name}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
242
266
|
});
|
|
243
267
|
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
244
|
-
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '')
|
|
245
|
-
|
|
268
|
+
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
269
|
+
`${interfaceDeclaration}${eventTypes.length === 0
|
|
270
|
+
? '}'
|
|
271
|
+
: `
|
|
246
272
|
${eventTypes.join('\n')}
|
|
247
273
|
}`}`;
|
|
248
274
|
return typeDefinition;
|
|
@@ -253,7 +279,7 @@ function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
|
253
279
|
if (!outputTarget.directivesArrayFile) {
|
|
254
280
|
return Promise.resolve();
|
|
255
281
|
}
|
|
256
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
282
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.directivesProxyFile, '.ts');
|
|
257
283
|
const directives = components
|
|
258
284
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
259
285
|
.map((className) => `d.${className}`)
|
|
@@ -272,7 +298,7 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
272
298
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
273
299
|
return;
|
|
274
300
|
}
|
|
275
|
-
const targetDir = path.dirname(outputTarget.
|
|
301
|
+
const targetDir = path.dirname(outputTarget.directivesProxyFile);
|
|
276
302
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
277
303
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
278
304
|
const type = va.type;
|
|
@@ -323,30 +349,13 @@ const VALUE_ACCESSOR_EVENT = `<VALUE_ACCESSOR_EVENT>`;
|
|
|
323
349
|
const VALUE_ACCESSOR_TARGETATTR = '<VALUE_ACCESSOR_TARGETATTR>';
|
|
324
350
|
const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChangeEvent($event.target.<VALUE_ACCESSOR_TARGETATTR>)'`;
|
|
325
351
|
|
|
326
|
-
/**
|
|
327
|
-
* Creates an Angular module declaration for a component wrapper.
|
|
328
|
-
* @param componentTagName The tag name of the Stencil component.
|
|
329
|
-
* @returns The Angular module declaration as a string.
|
|
330
|
-
*/
|
|
331
|
-
const generateAngularModuleForComponent = (componentTagName) => {
|
|
332
|
-
const tagNameAsPascal = dashToPascalCase(componentTagName);
|
|
333
|
-
const componentClassName = `${tagNameAsPascal}`;
|
|
334
|
-
const moduleClassName = `${tagNameAsPascal}Module`;
|
|
335
|
-
const moduleDefinition = `@NgModule({
|
|
336
|
-
declarations: [${componentClassName}],
|
|
337
|
-
exports: [${componentClassName}]
|
|
338
|
-
})
|
|
339
|
-
export class ${moduleClassName} { }`;
|
|
340
|
-
return moduleDefinition;
|
|
341
|
-
};
|
|
342
|
-
|
|
343
352
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
344
353
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
345
354
|
const rootDir = config.rootDir;
|
|
346
355
|
const pkgData = await readPackageJson(config, rootDir);
|
|
347
356
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
348
357
|
await Promise.all([
|
|
349
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
358
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
350
359
|
copyResources$1(config, outputTarget),
|
|
351
360
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
352
361
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -360,7 +369,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
360
369
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
361
370
|
}
|
|
362
371
|
const srcDirectory = path.join(__dirname, '..', 'angular-component-lib');
|
|
363
|
-
const destDirectory = path.join(path.dirname(outputTarget.
|
|
372
|
+
const destDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
364
373
|
return config.sys.copy([
|
|
365
374
|
{
|
|
366
375
|
src: srcDirectory,
|
|
@@ -371,11 +380,9 @@ async function copyResources$1(config, outputTarget) {
|
|
|
371
380
|
], srcDirectory);
|
|
372
381
|
}
|
|
373
382
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
374
|
-
var _a;
|
|
375
383
|
const distTypesDir = path.dirname(pkgData.types);
|
|
376
384
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
377
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
378
|
-
const createSingleComponentAngularModules = (_a = outputTarget.createSingleComponentAngularModules) !== null && _a !== void 0 ? _a : false;
|
|
385
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
379
386
|
/**
|
|
380
387
|
* The collection of named imports from @angular/core.
|
|
381
388
|
*/
|
|
@@ -386,14 +393,12 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
386
393
|
'ElementRef',
|
|
387
394
|
'EventEmitter',
|
|
388
395
|
'NgZone',
|
|
396
|
+
'Output',
|
|
389
397
|
];
|
|
390
398
|
/**
|
|
391
399
|
* The collection of named imports from the angular-component-lib/utils.
|
|
392
400
|
*/
|
|
393
401
|
const componentLibImports = ['ProxyCmp', 'proxyOutputs'];
|
|
394
|
-
if (createSingleComponentAngularModules) {
|
|
395
|
-
angularCoreImports.push('NgModule');
|
|
396
|
-
}
|
|
397
402
|
const imports = `/* tslint:disable */
|
|
398
403
|
/* auto-generated angular directive proxies */
|
|
399
404
|
${createImportStatement(angularCoreImports, '@angular/core')}
|
|
@@ -429,12 +434,6 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
429
434
|
});
|
|
430
435
|
sourceImports = cmpImports.join('\n');
|
|
431
436
|
}
|
|
432
|
-
if (createSingleComponentAngularModules) {
|
|
433
|
-
// Generating Angular modules is only supported in the dist-custom-elements build
|
|
434
|
-
if (!outputTarget.includeImportCustomElements) {
|
|
435
|
-
throw new Error('Generating single component Angular modules requires the "includeImportCustomElements" option to be set to true.');
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
437
|
const proxyFileOutput = [];
|
|
439
438
|
const filterInternalProps = (prop) => !prop.internal;
|
|
440
439
|
const mapPropName = (prop) => prop.name;
|
|
@@ -451,7 +450,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
451
450
|
inputs.sort();
|
|
452
451
|
const outputs = [];
|
|
453
452
|
if (cmpMeta.events) {
|
|
454
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
453
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
455
454
|
}
|
|
456
455
|
const methods = [];
|
|
457
456
|
if (cmpMeta.methods) {
|
|
@@ -460,16 +459,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
460
459
|
/**
|
|
461
460
|
* For each component, we need to generate:
|
|
462
461
|
* 1. The @Component decorated class
|
|
463
|
-
* 2.
|
|
464
|
-
* 3. The component interface (using declaration merging for types).
|
|
462
|
+
* 2. The component interface (using declaration merging for types).
|
|
465
463
|
*/
|
|
466
464
|
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, includeImportCustomElements);
|
|
467
|
-
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
468
465
|
const componentTypeDefinition = createComponentTypeDefinition(tagNameAsPascal, cmpMeta.events, componentCorePackage, includeImportCustomElements, customElementsDir);
|
|
469
466
|
proxyFileOutput.push(componentDefinition, '\n');
|
|
470
|
-
if (createSingleComponentAngularModules) {
|
|
471
|
-
proxyFileOutput.push(moduleDefinition, '\n');
|
|
472
|
-
}
|
|
473
467
|
proxyFileOutput.push(componentTypeDefinition, '\n');
|
|
474
468
|
}
|
|
475
469
|
const final = [imports, typeImports, sourceImports, ...proxyFileOutput];
|
|
@@ -495,14 +489,11 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
495
489
|
if (config.rootDir == null) {
|
|
496
490
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
497
491
|
}
|
|
498
|
-
if (outputTarget.directivesProxyFile
|
|
499
|
-
throw new Error('directivesProxyFile
|
|
500
|
-
}
|
|
501
|
-
if (outputTarget.proxyDeclarationFile == null) {
|
|
502
|
-
throw new Error('proxyDeclarationFile is required. Please set it in the Stencil config.');
|
|
492
|
+
if (outputTarget.directivesProxyFile == null) {
|
|
493
|
+
throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
|
|
503
494
|
}
|
|
504
|
-
if (outputTarget.
|
|
505
|
-
results.
|
|
495
|
+
if (outputTarget.directivesProxyFile && !path.isAbsolute(outputTarget.directivesProxyFile)) {
|
|
496
|
+
results.directivesProxyFile = normalizePath(path.join(config.rootDir, outputTarget.directivesProxyFile));
|
|
506
497
|
}
|
|
507
498
|
if (outputTarget.directivesArrayFile && !path.isAbsolute(outputTarget.directivesArrayFile)) {
|
|
508
499
|
results.directivesArrayFile = normalizePath(path.join(config.rootDir, outputTarget.directivesArrayFile));
|
package/dist/output-angular.js
CHANGED
|
@@ -3,14 +3,13 @@ import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCas
|
|
|
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';
|
|
6
|
-
import { generateAngularModuleForComponent } from './generate-angular-modules';
|
|
7
6
|
export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
8
7
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
9
8
|
const rootDir = config.rootDir;
|
|
10
9
|
const pkgData = await readPackageJson(config, rootDir);
|
|
11
10
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
12
11
|
await Promise.all([
|
|
13
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
12
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
14
13
|
copyResources(config, outputTarget),
|
|
15
14
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
16
15
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -24,7 +23,7 @@ async function copyResources(config, outputTarget) {
|
|
|
24
23
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
25
24
|
}
|
|
26
25
|
const srcDirectory = path.join(__dirname, '..', 'angular-component-lib');
|
|
27
|
-
const destDirectory = path.join(path.dirname(outputTarget.
|
|
26
|
+
const destDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
28
27
|
return config.sys.copy([
|
|
29
28
|
{
|
|
30
29
|
src: srcDirectory,
|
|
@@ -35,11 +34,9 @@ async function copyResources(config, outputTarget) {
|
|
|
35
34
|
], srcDirectory);
|
|
36
35
|
}
|
|
37
36
|
export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
38
|
-
var _a;
|
|
39
37
|
const distTypesDir = path.dirname(pkgData.types);
|
|
40
38
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
41
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
42
|
-
const createSingleComponentAngularModules = (_a = outputTarget.createSingleComponentAngularModules) !== null && _a !== void 0 ? _a : false;
|
|
39
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
43
40
|
/**
|
|
44
41
|
* The collection of named imports from @angular/core.
|
|
45
42
|
*/
|
|
@@ -50,14 +47,12 @@ export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
50
47
|
'ElementRef',
|
|
51
48
|
'EventEmitter',
|
|
52
49
|
'NgZone',
|
|
50
|
+
'Output',
|
|
53
51
|
];
|
|
54
52
|
/**
|
|
55
53
|
* The collection of named imports from the angular-component-lib/utils.
|
|
56
54
|
*/
|
|
57
55
|
const componentLibImports = ['ProxyCmp', 'proxyOutputs'];
|
|
58
|
-
if (createSingleComponentAngularModules) {
|
|
59
|
-
angularCoreImports.push('NgModule');
|
|
60
|
-
}
|
|
61
56
|
const imports = `/* tslint:disable */
|
|
62
57
|
/* auto-generated angular directive proxies */
|
|
63
58
|
${createImportStatement(angularCoreImports, '@angular/core')}
|
|
@@ -93,12 +88,6 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
93
88
|
});
|
|
94
89
|
sourceImports = cmpImports.join('\n');
|
|
95
90
|
}
|
|
96
|
-
if (createSingleComponentAngularModules) {
|
|
97
|
-
// Generating Angular modules is only supported in the dist-custom-elements build
|
|
98
|
-
if (!outputTarget.includeImportCustomElements) {
|
|
99
|
-
throw new Error('Generating single component Angular modules requires the "includeImportCustomElements" option to be set to true.');
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
91
|
const proxyFileOutput = [];
|
|
103
92
|
const filterInternalProps = (prop) => !prop.internal;
|
|
104
93
|
const mapPropName = (prop) => prop.name;
|
|
@@ -115,7 +104,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
115
104
|
inputs.sort();
|
|
116
105
|
const outputs = [];
|
|
117
106
|
if (cmpMeta.events) {
|
|
118
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
107
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
119
108
|
}
|
|
120
109
|
const methods = [];
|
|
121
110
|
if (cmpMeta.methods) {
|
|
@@ -124,16 +113,11 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
124
113
|
/**
|
|
125
114
|
* For each component, we need to generate:
|
|
126
115
|
* 1. The @Component decorated class
|
|
127
|
-
* 2.
|
|
128
|
-
* 3. The component interface (using declaration merging for types).
|
|
116
|
+
* 2. The component interface (using declaration merging for types).
|
|
129
117
|
*/
|
|
130
118
|
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, includeImportCustomElements);
|
|
131
|
-
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
132
119
|
const componentTypeDefinition = createComponentTypeDefinition(tagNameAsPascal, cmpMeta.events, componentCorePackage, includeImportCustomElements, customElementsDir);
|
|
133
120
|
proxyFileOutput.push(componentDefinition, '\n');
|
|
134
|
-
if (createSingleComponentAngularModules) {
|
|
135
|
-
proxyFileOutput.push(moduleDefinition, '\n');
|
|
136
|
-
}
|
|
137
121
|
proxyFileOutput.push(componentTypeDefinition, '\n');
|
|
138
122
|
}
|
|
139
123
|
const final = [imports, typeImports, sourceImports, ...proxyFileOutput];
|
package/dist/plugin.js
CHANGED
|
@@ -18,14 +18,11 @@ export function normalizeOutputTarget(config, outputTarget) {
|
|
|
18
18
|
if (config.rootDir == null) {
|
|
19
19
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
20
20
|
}
|
|
21
|
-
if (outputTarget.directivesProxyFile
|
|
22
|
-
throw new Error('directivesProxyFile
|
|
21
|
+
if (outputTarget.directivesProxyFile == null) {
|
|
22
|
+
throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
|
|
23
23
|
}
|
|
24
|
-
if (outputTarget.
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
if (outputTarget.proxyDeclarationFile && !path.isAbsolute(outputTarget.proxyDeclarationFile)) {
|
|
28
|
-
results.proxyDeclarationFile = normalizePath(path.join(config.rootDir, outputTarget.proxyDeclarationFile));
|
|
24
|
+
if (outputTarget.directivesProxyFile && !path.isAbsolute(outputTarget.directivesProxyFile)) {
|
|
25
|
+
results.directivesProxyFile = normalizePath(path.join(config.rootDir, outputTarget.directivesProxyFile));
|
|
29
26
|
}
|
|
30
27
|
if (outputTarget.directivesArrayFile && !path.isAbsolute(outputTarget.directivesArrayFile)) {
|
|
31
28
|
results.directivesArrayFile = normalizePath(path.join(config.rootDir, outputTarget.directivesArrayFile));
|
package/dist/types.d.ts
CHANGED
|
@@ -4,25 +4,17 @@ export interface OutputTargetAngular {
|
|
|
4
4
|
* This is used to generate the import statements.
|
|
5
5
|
*/
|
|
6
6
|
componentCorePackage: string;
|
|
7
|
-
/**
|
|
8
|
-
* @deprecated Use `proxyDeclarationFile` instead. This property has been replaced.
|
|
9
|
-
*/
|
|
10
|
-
directivesProxyFile?: string;
|
|
11
|
-
directivesArrayFile?: string;
|
|
12
|
-
directivesUtilsFile?: string;
|
|
13
7
|
/**
|
|
14
8
|
* The path to the proxy file that will be generated. This can be an absolute path
|
|
15
9
|
* or a relative path from the root directory of the Stencil library.
|
|
16
10
|
*/
|
|
17
|
-
|
|
11
|
+
directivesProxyFile: string;
|
|
12
|
+
directivesArrayFile?: string;
|
|
13
|
+
directivesUtilsFile?: string;
|
|
18
14
|
valueAccessorConfigs?: ValueAccessorConfig[];
|
|
19
15
|
excludeComponents?: string[];
|
|
20
16
|
includeImportCustomElements?: boolean;
|
|
21
17
|
customElementsDir?: string;
|
|
22
|
-
/**
|
|
23
|
-
* `true` to generate a single component Angular module for each component.
|
|
24
|
-
*/
|
|
25
|
-
createSingleComponentAngularModules?: boolean;
|
|
26
18
|
}
|
|
27
19
|
export declare type ValueAccessorTypes = 'text' | 'radio' | 'select' | 'number' | 'boolean';
|
|
28
20
|
export interface ValueAccessorConfig {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stencil/angular-output-target",
|
|
3
|
-
"version": "0.6.1-dev.
|
|
3
|
+
"version": "0.6.1-dev.11667512184.13935f80",
|
|
4
4
|
"description": "Angular output target for @stencil/core components.",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
],
|
|
59
59
|
"testURL": "http://localhost"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "3935f8069bb24eea156c4c5901c94357b302f1f5"
|
|
62
62
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates an Angular module declaration for a component wrapper.
|
|
3
|
-
* @param componentTagName The tag name of the Stencil component.
|
|
4
|
-
* @returns The Angular module declaration as a string.
|
|
5
|
-
*/
|
|
6
|
-
export declare const generateAngularModuleForComponent: (componentTagName: string) => string;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { dashToPascalCase } from './utils';
|
|
2
|
-
/**
|
|
3
|
-
* Creates an Angular module declaration for a component wrapper.
|
|
4
|
-
* @param componentTagName The tag name of the Stencil component.
|
|
5
|
-
* @returns The Angular module declaration as a string.
|
|
6
|
-
*/
|
|
7
|
-
export const generateAngularModuleForComponent = (componentTagName) => {
|
|
8
|
-
const tagNameAsPascal = dashToPascalCase(componentTagName);
|
|
9
|
-
const componentClassName = `${tagNameAsPascal}`;
|
|
10
|
-
const moduleClassName = `${tagNameAsPascal}Module`;
|
|
11
|
-
const moduleDefinition = `@NgModule({
|
|
12
|
-
declarations: [${componentClassName}],
|
|
13
|
-
exports: [${componentClassName}]
|
|
14
|
-
})
|
|
15
|
-
export class ${moduleClassName} { }`;
|
|
16
|
-
return moduleDefinition;
|
|
17
|
-
};
|