Skip to content

Commit

Permalink
fix(plugin): support outputs without inline initialization
Browse files Browse the repository at this point in the history
Previously, this script would fail with a cryptic message (`cannot read property of undefined (reading includes)`) for input like:
```ts
@output() myOutput: EventEmitter<string>;
```

The changes support that use case and:
- Uses the initialization in the constructor to infer the type (and removes it)
- Creates an initialization (`= output()`) if it doesn't exist
  • Loading branch information
ShacharHarshuv committed Sep 13, 2024
1 parent 1d7bfd4 commit be08af7
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export class MyCmp {
withObservable = outputFromObservable(this.someObservable$);
aliasOutput = output<string>({ alias: 'withAlias' });
noInitializer = output<string>();
initializedInConstructor = output<string>();
constructor() {
}
ngOnInit() {
let imABoolean = false;
Expand Down
8 changes: 8 additions & 0 deletions libs/plugin/src/generators/convert-outputs/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ export class MyCmp {
@Output() withObservable = this.someObservable$;
@Output('withAlias') aliasOutput = new EventEmitter<string>();
@Output() noInitializer: EventEmitter<string>;
@Output() initializedInConstructor;
constructor() {
this.initializedInConstructor = new EventEmitter<string>();
}
ngOnInit() {
let imABoolean = false;
Expand Down
40 changes: 36 additions & 4 deletions libs/plugin/src/generators/convert-outputs/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,25 @@ function getOutputInitializer(
alias = decoratorArg.getText();
}

const initializerOrType =
initializer ?? (typeof currentType === 'string' ? currentType : undefined);

if (!initializerOrType) {
logger.error(
`[ngxtension] Unable to find initializer or type for "${propertyName}"`,
);
return exit(1);
}

// check if the initializer is not an EventEmitter -> means its an observable
if (!initializer.includes('EventEmitter')) {
if (!initializerOrType.includes('EventEmitter')) {
if (!initializer) {
logger.error(
`[ngxtension] Unable to find initializer for "${propertyName}"`,
);
return exit(1);
}

// if the initializer is a Subject or BehaviorSubject
if (
initializer.includes('Subject') ||
Expand Down Expand Up @@ -96,10 +113,11 @@ function getOutputInitializer(
}
} else {
let type = '';
if (initializer.includes('EventEmitter()')) {
if (initializerOrType.includes('EventEmitter()')) {
// there is no type
} else {
const genericTypeOnEmitter = initializer.match(/EventEmitter<(.+)>/);
const genericTypeOnEmitter =
initializerOrType.match(/EventEmitter<(.+)>/);
if (genericTypeOnEmitter?.length) {
type = genericTypeOnEmitter[1];
}
Expand Down Expand Up @@ -242,7 +260,7 @@ export async function convertOutputsGenerator(
if (Node.isPropertyDeclaration(node)) {
const outputDecorator = node.getDecorator('Output');
if (outputDecorator) {
const {
let {
name,
isReadonly,
docs,
Expand All @@ -252,6 +270,20 @@ export async function convertOutputsGenerator(
initializer,
} = node.getStructure();

if (!initializer) {
// look for constructor initializer
const constructor = targetClass.getConstructors()[0];
if (constructor) {
const constructorInitializer = constructor
.getStatements()
.find((stmt) => stmt.getText().includes(`${name} =`));
if (constructorInitializer) {
initializer = constructorInitializer.getText();
}
constructorInitializer?.remove();
}
}

const {
needsOutputFromObservableImport,
removeOnlyDecorator,
Expand Down

0 comments on commit be08af7

Please sign in to comment.