Skip to content

Commit 398d66a

Browse files
committed
refactor(@angular-devkit/schematics): reduce RxJS usage in callRule/callSource functions
The amount of additional RxJS operators and chaining has been reduced within the `callRule` and `callSource` functions. This also reduces the complexity of the code and removes several repetitive code segments.
1 parent 18396f6 commit 398d66a

File tree

2 files changed

+46
-60
lines changed

2 files changed

+46
-60
lines changed

packages/angular_devkit/schematics/src/engine/schematic.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import { BaseException } from '@angular-devkit/core';
10-
import { Observable, of as observableOf } from 'rxjs';
10+
import { Observable } from 'rxjs';
1111
import { concatMap, first, map } from 'rxjs/operators';
1212
import { callRule } from '../rules/call';
1313
import { Tree } from '../tree/interface';
@@ -29,7 +29,8 @@ export class InvalidSchematicsNameException extends BaseException {
2929
}
3030

3131
export class SchematicImpl<CollectionT extends object, SchematicT extends object>
32-
implements Schematic<CollectionT, SchematicT> {
32+
implements Schematic<CollectionT, SchematicT>
33+
{
3334
constructor(
3435
private _description: SchematicDescription<CollectionT, SchematicT>,
3536
private _factory: RuleFactory<{}>,
@@ -73,7 +74,7 @@ export class SchematicImpl<CollectionT extends object, SchematicT extends object
7374
input = tree;
7475
}
7576

76-
return callRule(this._factory(transformedOptions), observableOf(input), context).pipe(
77+
return callRule(this._factory(transformedOptions), input, context).pipe(
7778
map((output) => {
7879
if (output === input) {
7980
return tree;

packages/angular_devkit/schematics/src/rules/call.ts

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { BaseException, isPromise } from '@angular-devkit/core';
10-
import { Observable, from, isObservable, of as observableOf, throwError } from 'rxjs';
11-
import { defaultIfEmpty, last, mergeMap, tap } from 'rxjs/operators';
9+
import { BaseException } from '@angular-devkit/core';
10+
import { Observable, defer, isObservable } from 'rxjs';
11+
import { defaultIfEmpty, mergeMap } from 'rxjs/operators';
1212
import { Rule, SchematicContext, Source } from '../engine/interface';
1313
import { Tree, TreeSymbol } from '../tree/interface';
1414

@@ -48,67 +48,52 @@ export class InvalidSourceResultException extends BaseException {
4848
}
4949

5050
export function callSource(source: Source, context: SchematicContext): Observable<Tree> {
51-
const result = source(context);
51+
return defer(async () => {
52+
let result = source(context);
5253

53-
if (isObservable(result)) {
54-
// Only return the last Tree, and make sure it's a Tree.
55-
return result.pipe(
56-
defaultIfEmpty(),
57-
last(),
58-
tap((inner) => {
59-
if (!inner || !(TreeSymbol in inner)) {
60-
throw new InvalidSourceResultException(inner);
61-
}
62-
}),
63-
);
64-
} else if (result && TreeSymbol in result) {
65-
return observableOf(result);
66-
} else {
67-
return throwError(new InvalidSourceResultException(result));
68-
}
54+
if (isObservable(result)) {
55+
result = await result.pipe(defaultIfEmpty()).toPromise();
56+
}
57+
58+
if (result && TreeSymbol in result) {
59+
return result as Tree;
60+
}
61+
62+
throw new InvalidSourceResultException(result);
63+
});
6964
}
7065

7166
export function callRule(
7267
rule: Rule,
7368
input: Tree | Observable<Tree>,
7469
context: SchematicContext,
7570
): Observable<Tree> {
76-
return (isObservable(input) ? input : observableOf(input)).pipe(
77-
mergeMap((inputTree) => {
78-
const result = rule(inputTree, context);
71+
if (isObservable(input)) {
72+
return input.pipe(mergeMap((inputTree) => callRuleAsync(rule, inputTree, context)));
73+
} else {
74+
return defer(() => callRuleAsync(rule, input, context));
75+
}
76+
}
77+
78+
async function callRuleAsync(rule: Rule, tree: Tree, context: SchematicContext): Promise<Tree> {
79+
let result = await rule(tree, context);
80+
81+
while (typeof result === 'function') {
82+
// This is considered a Rule, chain the rule and return its output.
83+
result = await result(tree, context);
84+
}
85+
86+
if (typeof result === 'undefined') {
87+
return tree;
88+
}
89+
90+
if (isObservable(result)) {
91+
result = await result.pipe(defaultIfEmpty(tree)).toPromise();
92+
}
93+
94+
if (TreeSymbol in result) {
95+
return result as Tree;
96+
}
7997

80-
if (!result) {
81-
return observableOf(inputTree);
82-
} else if (typeof result == 'function') {
83-
// This is considered a Rule, chain the rule and return its output.
84-
return callRule(result, inputTree, context);
85-
} else if (isObservable(result)) {
86-
// Only return the last Tree, and make sure it's a Tree.
87-
return result.pipe(
88-
defaultIfEmpty(),
89-
last(),
90-
tap((inner) => {
91-
if (!inner || !(TreeSymbol in inner)) {
92-
throw new InvalidRuleResultException(inner);
93-
}
94-
}),
95-
);
96-
} else if (isPromise(result)) {
97-
return from(result).pipe(
98-
mergeMap((inner) => {
99-
if (typeof inner === 'function') {
100-
// This is considered a Rule, chain the rule and return its output.
101-
return callRule(inner, inputTree, context);
102-
} else {
103-
return observableOf(inputTree);
104-
}
105-
}),
106-
);
107-
} else if (TreeSymbol in result) {
108-
return observableOf(result);
109-
} else {
110-
return throwError(new InvalidRuleResultException(result));
111-
}
112-
}),
113-
);
98+
throw new InvalidRuleResultException(result);
11499
}

0 commit comments

Comments
 (0)