@stencil/angular-output-target 0.6.1-dev.11664502361.139bc7c5 → 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 +54 -29
- package/dist/generate-angular-directives-file.js +1 -1
- package/dist/generate-value-accessors.js +1 -1
- package/dist/index.cjs.js +65 -42
- package/dist/index.js +65 -42
- package/dist/output-angular.js +5 -4
- package/dist/plugin.js +4 -7
- package/dist/types.d.ts +3 -7
- package/package.json +2 -2
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, ' ')
|
|
@@ -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, ' ')
|
|
@@ -262,7 +287,7 @@ function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
|
262
287
|
if (!outputTarget.directivesArrayFile) {
|
|
263
288
|
return Promise.resolve();
|
|
264
289
|
}
|
|
265
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
290
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.directivesProxyFile, '.ts');
|
|
266
291
|
const directives = components
|
|
267
292
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
268
293
|
.map((className) => `d.${className}`)
|
|
@@ -281,7 +306,7 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
281
306
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
282
307
|
return;
|
|
283
308
|
}
|
|
284
|
-
const targetDir = path__default['default'].dirname(outputTarget.
|
|
309
|
+
const targetDir = path__default['default'].dirname(outputTarget.directivesProxyFile);
|
|
285
310
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
286
311
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
287
312
|
const type = va.type;
|
|
@@ -338,7 +363,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
338
363
|
const pkgData = await readPackageJson(config, rootDir);
|
|
339
364
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
340
365
|
await Promise.all([
|
|
341
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
366
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
342
367
|
copyResources$1(config, outputTarget),
|
|
343
368
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
344
369
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -352,7 +377,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
352
377
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
353
378
|
}
|
|
354
379
|
const srcDirectory = path__default['default'].join(__dirname, '..', 'angular-component-lib');
|
|
355
|
-
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');
|
|
356
381
|
return config.sys.copy([
|
|
357
382
|
{
|
|
358
383
|
src: srcDirectory,
|
|
@@ -365,7 +390,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
365
390
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
366
391
|
const distTypesDir = path__default['default'].dirname(pkgData.types);
|
|
367
392
|
const dtsFilePath = path__default['default'].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
368
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
393
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
369
394
|
/**
|
|
370
395
|
* The collection of named imports from @angular/core.
|
|
371
396
|
*/
|
|
@@ -376,6 +401,7 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
376
401
|
'ElementRef',
|
|
377
402
|
'EventEmitter',
|
|
378
403
|
'NgZone',
|
|
404
|
+
'Output',
|
|
379
405
|
];
|
|
380
406
|
/**
|
|
381
407
|
* The collection of named imports from the angular-component-lib/utils.
|
|
@@ -432,7 +458,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
432
458
|
inputs.sort();
|
|
433
459
|
const outputs = [];
|
|
434
460
|
if (cmpMeta.events) {
|
|
435
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
461
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
436
462
|
}
|
|
437
463
|
const methods = [];
|
|
438
464
|
if (cmpMeta.methods) {
|
|
@@ -471,14 +497,11 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
471
497
|
if (config.rootDir == null) {
|
|
472
498
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
473
499
|
}
|
|
474
|
-
if (outputTarget.directivesProxyFile
|
|
475
|
-
throw new Error('directivesProxyFile
|
|
476
|
-
}
|
|
477
|
-
if (outputTarget.proxyDeclarationFile == null) {
|
|
478
|
-
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.');
|
|
479
502
|
}
|
|
480
|
-
if (outputTarget.
|
|
481
|
-
results.
|
|
503
|
+
if (outputTarget.directivesProxyFile && !path__default['default'].isAbsolute(outputTarget.directivesProxyFile)) {
|
|
504
|
+
results.directivesProxyFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesProxyFile));
|
|
482
505
|
}
|
|
483
506
|
if (outputTarget.directivesArrayFile && !path__default['default'].isAbsolute(outputTarget.directivesArrayFile)) {
|
|
484
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, ' ')
|
|
@@ -254,7 +279,7 @@ function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
|
254
279
|
if (!outputTarget.directivesArrayFile) {
|
|
255
280
|
return Promise.resolve();
|
|
256
281
|
}
|
|
257
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
282
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.directivesProxyFile, '.ts');
|
|
258
283
|
const directives = components
|
|
259
284
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
260
285
|
.map((className) => `d.${className}`)
|
|
@@ -273,7 +298,7 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
|
|
|
273
298
|
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
274
299
|
return;
|
|
275
300
|
}
|
|
276
|
-
const targetDir = path.dirname(outputTarget.
|
|
301
|
+
const targetDir = path.dirname(outputTarget.directivesProxyFile);
|
|
277
302
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
278
303
|
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
279
304
|
const type = va.type;
|
|
@@ -330,7 +355,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
|
|
|
330
355
|
const pkgData = await readPackageJson(config, rootDir);
|
|
331
356
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
332
357
|
await Promise.all([
|
|
333
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
358
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
334
359
|
copyResources$1(config, outputTarget),
|
|
335
360
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
336
361
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -344,7 +369,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
344
369
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
345
370
|
}
|
|
346
371
|
const srcDirectory = path.join(__dirname, '..', 'angular-component-lib');
|
|
347
|
-
const destDirectory = path.join(path.dirname(outputTarget.
|
|
372
|
+
const destDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
348
373
|
return config.sys.copy([
|
|
349
374
|
{
|
|
350
375
|
src: srcDirectory,
|
|
@@ -357,7 +382,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
357
382
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
358
383
|
const distTypesDir = path.dirname(pkgData.types);
|
|
359
384
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
360
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
385
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
361
386
|
/**
|
|
362
387
|
* The collection of named imports from @angular/core.
|
|
363
388
|
*/
|
|
@@ -368,6 +393,7 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
368
393
|
'ElementRef',
|
|
369
394
|
'EventEmitter',
|
|
370
395
|
'NgZone',
|
|
396
|
+
'Output',
|
|
371
397
|
];
|
|
372
398
|
/**
|
|
373
399
|
* The collection of named imports from the angular-component-lib/utils.
|
|
@@ -424,7 +450,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
424
450
|
inputs.sort();
|
|
425
451
|
const outputs = [];
|
|
426
452
|
if (cmpMeta.events) {
|
|
427
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
453
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
428
454
|
}
|
|
429
455
|
const methods = [];
|
|
430
456
|
if (cmpMeta.methods) {
|
|
@@ -463,14 +489,11 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
463
489
|
if (config.rootDir == null) {
|
|
464
490
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
465
491
|
}
|
|
466
|
-
if (outputTarget.directivesProxyFile
|
|
467
|
-
throw new Error('directivesProxyFile
|
|
468
|
-
}
|
|
469
|
-
if (outputTarget.proxyDeclarationFile == null) {
|
|
470
|
-
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.');
|
|
471
494
|
}
|
|
472
|
-
if (outputTarget.
|
|
473
|
-
results.
|
|
495
|
+
if (outputTarget.directivesProxyFile && !path.isAbsolute(outputTarget.directivesProxyFile)) {
|
|
496
|
+
results.directivesProxyFile = normalizePath(path.join(config.rootDir, outputTarget.directivesProxyFile));
|
|
474
497
|
}
|
|
475
498
|
if (outputTarget.directivesArrayFile && !path.isAbsolute(outputTarget.directivesArrayFile)) {
|
|
476
499
|
results.directivesArrayFile = normalizePath(path.join(config.rootDir, outputTarget.directivesArrayFile));
|
package/dist/output-angular.js
CHANGED
|
@@ -9,7 +9,7 @@ export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, com
|
|
|
9
9
|
const pkgData = await readPackageJson(config, rootDir);
|
|
10
10
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
11
11
|
await Promise.all([
|
|
12
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
12
|
+
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
13
13
|
copyResources(config, outputTarget),
|
|
14
14
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
15
15
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -23,7 +23,7 @@ async function copyResources(config, outputTarget) {
|
|
|
23
23
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
24
24
|
}
|
|
25
25
|
const srcDirectory = path.join(__dirname, '..', 'angular-component-lib');
|
|
26
|
-
const destDirectory = path.join(path.dirname(outputTarget.
|
|
26
|
+
const destDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
|
|
27
27
|
return config.sys.copy([
|
|
28
28
|
{
|
|
29
29
|
src: srcDirectory,
|
|
@@ -36,7 +36,7 @@ async function copyResources(config, outputTarget) {
|
|
|
36
36
|
export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
37
37
|
const distTypesDir = path.dirname(pkgData.types);
|
|
38
38
|
const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
|
|
39
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
39
|
+
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
40
40
|
/**
|
|
41
41
|
* The collection of named imports from @angular/core.
|
|
42
42
|
*/
|
|
@@ -47,6 +47,7 @@ export function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
47
47
|
'ElementRef',
|
|
48
48
|
'EventEmitter',
|
|
49
49
|
'NgZone',
|
|
50
|
+
'Output',
|
|
50
51
|
];
|
|
51
52
|
/**
|
|
52
53
|
* The collection of named imports from the angular-component-lib/utils.
|
|
@@ -103,7 +104,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
|
|
|
103
104
|
inputs.sort();
|
|
104
105
|
const outputs = [];
|
|
105
106
|
if (cmpMeta.events) {
|
|
106
|
-
outputs.push(...cmpMeta.events.filter(filterInternalProps)
|
|
107
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps));
|
|
107
108
|
}
|
|
108
109
|
const methods = [];
|
|
109
110
|
if (cmpMeta.methods) {
|
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,17 +4,13 @@ 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;
|
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
|
}
|