From 126dc39e5b7427a9efc2a57442e02b8ca43675bc Mon Sep 17 00:00:00 2001 From: Sean Perkins <13732623+sean-perkins@users.noreply.github.com> Date: Fri, 8 Sep 2023 11:01:36 -0400 Subject: [PATCH] fix(angular-output-target): rewrite nested generics for custom events (#372) --- .../generate-angular-component.spec.ts | 51 +++++++++++++++++++ .../src/generate-angular-component.ts | 18 ++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/packages/angular-output-target/__tests__/generate-angular-component.spec.ts b/packages/angular-output-target/__tests__/generate-angular-component.spec.ts index b760a066..b74c988e 100644 --- a/packages/angular-output-target/__tests__/generate-angular-component.spec.ts +++ b/packages/angular-output-target/__tests__/generate-angular-component.spec.ts @@ -310,6 +310,57 @@ export declare interface MyComponent extends Components.MyComponent { myDoclessEvent: EventEmitter>; 'my-kebab-event': EventEmitter>; +}` + ); + }); + + it('rewrites complex nested generic types within custom events', () => { + // Issue: https://github.com/ionic-team/stencil-ds-output-targets/issues/369 + const definition = createComponentTypeDefinition( + 'component', + 'MyComponent', + [ + { + name: 'myChange', + method: 'myChange', + bubbles: true, + cancelable: true, + composed: true, + docs: { + tags: [], + text: '', + }, + complexType: { + original: 'MyEvent', + resolved: 'MyEvent', + references: { + MyEvent: { + location: 'import', + path: '../../types/MyEvent', + // Stencil v4.0.3+ only + id: 'src/types/MyEvent.ts::MyEvent', + } as any, + Currency: { + location: 'import', + path: '../../types/Currency', + // Stencil v4.0.3+ only + id: 'src/types/Currency.ts::Currency', + } as any, + }, + }, + internal: false, + }, + ], + '@ionic/core' + ); + + expect(definition).toEqual( + `import type { MyEvent as IMyComponentMyEvent } from '@ionic/core'; +import type { Currency as IMyComponentCurrency } from '@ionic/core'; + +export declare interface MyComponent extends Components.MyComponent { + + myChange: EventEmitter>>; }` ); }); diff --git a/packages/angular-output-target/src/generate-angular-component.ts b/packages/angular-output-target/src/generate-angular-component.ts index e60f9371..98566d5a 100644 --- a/packages/angular-output-target/src/generate-angular-component.ts +++ b/packages/angular-output-target/src/generate-angular-component.ts @@ -95,6 +95,7 @@ export class ${tagNameAsPascal} { * @returns The sanitized event type as a string. */ const formatOutputType = (componentClassName: string, event: ComponentCompilerEvent) => { + const prefix = `I${componentClassName}`; /** * The original attribute contains the original type defined by the devs. * This regexp normalizes the reference, by removing linebreaks, @@ -104,12 +105,25 @@ const formatOutputType = (componentClassName: string, event: ComponentCompilerEv .filter(([_, refObject]) => refObject.location === 'local' || refObject.location === 'import') .reduce( (type, [src, dst]) => { - const renamedType = `I${componentClassName}${type}`; + let renamedType = type; + if (!type.startsWith(prefix)) { + renamedType = `I${componentClassName}${type}`; + } return ( renamedType .replace(new RegExp(`^${src}$`, 'g'), `${dst}`) // Capture all instances of the `src` field surrounded by non-word characters on each side and join them. - .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join('')) + .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => { + if (dst?.location === 'import') { + /** + * Replaces a complex type reference within a generic type. + * For example, remapping a type like `EventEmitter>>` to + * `EventEmitter>>`. + */ + return [p1, `I${componentClassName}${v.substring(1, v.length - 1)}`, p2].join(''); + } + return [p1, dst, p2].join(''); + }) ); }, event.complexType.original