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.
This commit is contained in:
Charles Lyding 2022-08-11 15:38:59 -04:00 committed by Charles
parent 18396f6ee7
commit 398d66af4f
2 changed files with 47 additions and 61 deletions

View File

@ -7,7 +7,7 @@
*/ */
import { BaseException } from '@angular-devkit/core'; import { BaseException } from '@angular-devkit/core';
import { Observable, of as observableOf } from 'rxjs'; import { Observable } from 'rxjs';
import { concatMap, first, map } from 'rxjs/operators'; import { concatMap, first, map } from 'rxjs/operators';
import { callRule } from '../rules/call'; import { callRule } from '../rules/call';
import { Tree } from '../tree/interface'; import { Tree } from '../tree/interface';
@ -29,7 +29,8 @@ export class InvalidSchematicsNameException extends BaseException {
} }
export class SchematicImpl<CollectionT extends object, SchematicT extends object> export class SchematicImpl<CollectionT extends object, SchematicT extends object>
implements Schematic<CollectionT, SchematicT> { implements Schematic<CollectionT, SchematicT>
{
constructor( constructor(
private _description: SchematicDescription<CollectionT, SchematicT>, private _description: SchematicDescription<CollectionT, SchematicT>,
private _factory: RuleFactory<{}>, private _factory: RuleFactory<{}>,
@ -73,7 +74,7 @@ export class SchematicImpl<CollectionT extends object, SchematicT extends object
input = tree; input = tree;
} }
return callRule(this._factory(transformedOptions), observableOf(input), context).pipe( return callRule(this._factory(transformedOptions), input, context).pipe(
map((output) => { map((output) => {
if (output === input) { if (output === input) {
return tree; return tree;

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import { BaseException, isPromise } from '@angular-devkit/core'; import { BaseException } from '@angular-devkit/core';
import { Observable, from, isObservable, of as observableOf, throwError } from 'rxjs'; import { Observable, defer, isObservable } from 'rxjs';
import { defaultIfEmpty, last, mergeMap, tap } from 'rxjs/operators'; import { defaultIfEmpty, mergeMap } from 'rxjs/operators';
import { Rule, SchematicContext, Source } from '../engine/interface'; import { Rule, SchematicContext, Source } from '../engine/interface';
import { Tree, TreeSymbol } from '../tree/interface'; import { Tree, TreeSymbol } from '../tree/interface';
@ -48,24 +48,19 @@ export class InvalidSourceResultException extends BaseException {
} }
export function callSource(source: Source, context: SchematicContext): Observable<Tree> { export function callSource(source: Source, context: SchematicContext): Observable<Tree> {
const result = source(context); return defer(async () => {
let result = source(context);
if (isObservable(result)) { if (isObservable(result)) {
// Only return the last Tree, and make sure it's a Tree. result = await result.pipe(defaultIfEmpty()).toPromise();
return result.pipe( }
defaultIfEmpty(),
last(), if (result && TreeSymbol in result) {
tap((inner) => { return result as Tree;
if (!inner || !(TreeSymbol in inner)) { }
throw new InvalidSourceResultException(inner);
} throw new InvalidSourceResultException(result);
}), });
);
} else if (result && TreeSymbol in result) {
return observableOf(result);
} else {
return throwError(new InvalidSourceResultException(result));
}
} }
export function callRule( export function callRule(
@ -73,42 +68,32 @@ export function callRule(
input: Tree | Observable<Tree>, input: Tree | Observable<Tree>,
context: SchematicContext, context: SchematicContext,
): Observable<Tree> { ): Observable<Tree> {
return (isObservable(input) ? input : observableOf(input)).pipe( if (isObservable(input)) {
mergeMap((inputTree) => { return input.pipe(mergeMap((inputTree) => callRuleAsync(rule, inputTree, context)));
const result = rule(inputTree, context); } else {
return defer(() => callRuleAsync(rule, input, context));
if (!result) { }
return observableOf(inputTree); }
} else if (typeof result == 'function') {
// This is considered a Rule, chain the rule and return its output. async function callRuleAsync(rule: Rule, tree: Tree, context: SchematicContext): Promise<Tree> {
return callRule(result, inputTree, context); let result = await rule(tree, context);
} else if (isObservable(result)) {
// Only return the last Tree, and make sure it's a Tree. while (typeof result === 'function') {
return result.pipe( // This is considered a Rule, chain the rule and return its output.
defaultIfEmpty(), result = await result(tree, context);
last(), }
tap((inner) => {
if (!inner || !(TreeSymbol in inner)) { if (typeof result === 'undefined') {
throw new InvalidRuleResultException(inner); return tree;
} }
}),
); if (isObservable(result)) {
} else if (isPromise(result)) { result = await result.pipe(defaultIfEmpty(tree)).toPromise();
return from(result).pipe( }
mergeMap((inner) => {
if (typeof inner === 'function') { if (TreeSymbol in result) {
// This is considered a Rule, chain the rule and return its output. return result as Tree;
return callRule(inner, inputTree, context); }
} else {
return observableOf(inputTree); throw new InvalidRuleResultException(result);
}
}),
);
} else if (TreeSymbol in result) {
return observableOf(result);
} else {
return throwError(new InvalidRuleResultException(result));
}
}),
);
} }