@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 CHANGED
@@ -27,7 +27,7 @@ export const config: Config = {
27
27
  outputTargets: [
28
28
  angularOutputTarget({
29
29
  componentCorePackage: 'component-library',
30
- proxyDeclarationFile: '../component-library-angular/src/directives/proxies.ts',
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
- | `proxyDeclarationFile` | 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. |
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 string[], methods: readonly string[], includeImportCustomElements?: boolean) => string;
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
- * Notes on the generated output:
36
- * - We disable @angular-eslint/no-inputs-metadata-property, so that
37
- * Angular does not complain about the inputs property. The output target
38
- * uses the inputs property to define the inputs of the component instead of
39
- * having to use the @Input decorator (and manually define the type and default value).
40
- */
41
- const output = `@ProxyCmp({${proxyCmpOptions.join(',')}\n})
42
- @Component({
43
- selector: '${tagName}',
44
- changeDetection: ChangeDetectionStrategy.OnPush,
45
- template: '<ng-content></ng-content>',
46
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
47
- inputs: [${formattedInputs}],
48
- })
49
- export class ${tagNameAsPascal} {
50
- protected el: HTMLElement;
51
- constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
52
- c.detach();
53
- this.el = r.nativeElement;${hasOutputs
54
- ? `
55
- proxyOutputs(this, this.el, [${formattedOutputs}]);`
56
- : ''}
57
- }
58
- }`;
59
- return output;
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
- .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
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.proxyDeclarationFile, '.ts');
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.proxyDeclarationFile);
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
- * Notes on the generated output:
167
- * - We disable @angular-eslint/no-inputs-metadata-property, so that
168
- * Angular does not complain about the inputs property. The output target
169
- * uses the inputs property to define the inputs of the component instead of
170
- * having to use the @Input decorator (and manually define the type and default value).
171
- */
172
- const output = `@ProxyCmp({${proxyCmpOptions.join(',')}\n})
173
- @Component({
174
- selector: '${tagName}',
175
- changeDetection: ChangeDetectionStrategy.OnPush,
176
- template: '<ng-content></ng-content>',
177
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
178
- inputs: [${formattedInputs}],
179
- })
180
- export class ${tagNameAsPascal} {
181
- protected el: HTMLElement;
182
- constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
183
- c.detach();
184
- this.el = r.nativeElement;${hasOutputs
185
- ? `
186
- proxyOutputs(this, this.el, [${formattedOutputs}]);`
187
- : ''}
188
- }
189
- }`;
190
- return output;
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
- .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
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.proxyDeclarationFile, '.ts');
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.proxyDeclarationFile);
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.proxyDeclarationFile, finalText),
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.proxyDeclarationFile), 'angular-component-lib');
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.proxyDeclarationFile, dtsFilePath, '.d.ts');
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).map(mapPropName));
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 !== undefined) {
475
- throw new Error('directivesProxyFile has been removed. Use proxyDeclarationFile instead.');
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.proxyDeclarationFile && !path__default['default'].isAbsolute(outputTarget.proxyDeclarationFile)) {
481
- results.proxyDeclarationFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.proxyDeclarationFile));
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
- * Notes on the generated output:
159
- * - We disable @angular-eslint/no-inputs-metadata-property, so that
160
- * Angular does not complain about the inputs property. The output target
161
- * uses the inputs property to define the inputs of the component instead of
162
- * having to use the @Input decorator (and manually define the type and default value).
163
- */
164
- const output = `@ProxyCmp({${proxyCmpOptions.join(',')}\n})
165
- @Component({
166
- selector: '${tagName}',
167
- changeDetection: ChangeDetectionStrategy.OnPush,
168
- template: '<ng-content></ng-content>',
169
- // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
170
- inputs: [${formattedInputs}],
171
- })
172
- export class ${tagNameAsPascal} {
173
- protected el: HTMLElement;
174
- constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
175
- c.detach();
176
- this.el = r.nativeElement;${hasOutputs
177
- ? `
178
- proxyOutputs(this, this.el, [${formattedOutputs}]);`
179
- : ''}
180
- }
181
- }`;
182
- return output;
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
- .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
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.proxyDeclarationFile, '.ts');
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.proxyDeclarationFile);
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.proxyDeclarationFile, finalText),
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.proxyDeclarationFile), 'angular-component-lib');
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.proxyDeclarationFile, dtsFilePath, '.d.ts');
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).map(mapPropName));
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 !== undefined) {
467
- throw new Error('directivesProxyFile has been removed. Use proxyDeclarationFile instead.');
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.proxyDeclarationFile && !path.isAbsolute(outputTarget.proxyDeclarationFile)) {
473
- results.proxyDeclarationFile = normalizePath(path.join(config.rootDir, outputTarget.proxyDeclarationFile));
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));
@@ -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.proxyDeclarationFile, finalText),
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.proxyDeclarationFile), 'angular-component-lib');
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.proxyDeclarationFile, dtsFilePath, '.d.ts');
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).map(mapPropName));
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 !== undefined) {
22
- throw new Error('directivesProxyFile has been removed. Use proxyDeclarationFile instead.');
21
+ if (outputTarget.directivesProxyFile == null) {
22
+ throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
23
23
  }
24
- if (outputTarget.proxyDeclarationFile == null) {
25
- throw new Error('proxyDeclarationFile is required. Please set it in the Stencil config.');
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
- proxyDeclarationFile: string;
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.11664502361.139bc7c5",
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": "39bc7c5aab00038b7b440f51d510010f506a1c54"
61
+ "gitHead": "3935f8069bb24eea156c4c5901c94357b302f1f5"
62
62
  }