Skip to content

Commit

Permalink
fix(angular-output-target): rewrite nested generics for custom events (
Browse files Browse the repository at this point in the history
  • Loading branch information
sean-perkins authored Sep 8, 2023
1 parent d37ebd7 commit 126dc39
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,57 @@ export declare interface MyComponent extends Components.MyComponent {
myDoclessEvent: EventEmitter<CustomEvent<IMyComponentMyDoclessEvent>>;
'my-kebab-event': EventEmitter<CustomEvent<IMyComponentMyKebabEvent>>;
}`
);
});

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<Currency>',
resolved: 'MyEvent<Currency>',
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<CustomEvent<IMyComponentMyEvent<IMyComponentCurrency>>>;
}`
);
});
Expand Down
18 changes: 16 additions & 2 deletions packages/angular-output-target/src/generate-angular-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<CustomEvent<MyEvent<T>>>` to
* `EventEmitter<CustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
*/
return [p1, `I${componentClassName}${v.substring(1, v.length - 1)}`, p2].join('');
}
return [p1, dst, p2].join('');
})
);
},
event.complexType.original
Expand Down

0 comments on commit 126dc39

Please sign in to comment.