Skip to content

Commit

Permalink
feat(@angular-devkit/schematics): allow chain rule to accept iterab…
Browse files Browse the repository at this point in the history
…les of rules

Previously, the `chain` base rule only accepted an `Array` of schematics rules.
In addition to still allowing an `Array`, `chain` now can accept either an `Iterable<Rule>`
or `AsyncIterable<Rule>`. This provides support for sync and async generator functions
with the `chain` rule.
  • Loading branch information
clydin authored and dgp1130 committed May 24, 2022
1 parent 4325995 commit 526cdb2
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 6 deletions.
2 changes: 1 addition & 1 deletion goldens/public-api/angular_devkit/schematics/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function callRule(rule: Rule, input: Tree_2 | Observable<Tree_2>, context
export function callSource(source: Source, context: SchematicContext): Observable<Tree_2>;

// @public
export function chain(rules: Rule[]): Rule;
export function chain(rules: Iterable<Rule> | AsyncIterable<Rule>): Rule;

// @public (undocumented)
export class CircularCollectionException extends BaseException {
Expand Down
11 changes: 8 additions & 3 deletions packages/angular_devkit/schematics/src/rules/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ export function empty(): Source {
/**
* Chain multiple rules into a single rule.
*/
export function chain(rules: Rule[]): Rule {
return (tree, context) => {
return rules.reduce<Tree | Observable<Tree>>((acc, curr) => callRule(curr, acc, context), tree);
export function chain(rules: Iterable<Rule> | AsyncIterable<Rule>): Rule {
return async (initialTree, context) => {
let intermediateTree: Observable<Tree> | undefined;
for await (const rule of rules) {
intermediateTree = callRule(rule, intermediateTree ?? initialTree, context);
}

return () => intermediateTree;
};
}

Expand Down
58 changes: 56 additions & 2 deletions packages/angular_devkit/schematics/src/rules/base_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { apply, applyToSubtree, chain } from './base';
import { callRule, callSource } from './call';
import { move } from './move';

const context: SchematicContext = ({
const context: SchematicContext = {
engine: null,
debug: false,
strategy: MergeStrategy.Default,
} as {}) as SchematicContext;
} as {} as SchematicContext;

describe('chain', () => {
it('works with simple rules', (done) => {
Expand All @@ -48,6 +48,60 @@ describe('chain', () => {
.then(done, done.fail);
});

it('works with a sync generator of rules', async () => {
const rulesCalled: Tree[] = [];

const tree0 = empty();
const tree1 = empty();
const tree2 = empty();
const tree3 = empty();

const rule0: Rule = (tree: Tree) => ((rulesCalled[0] = tree), tree1);
const rule1: Rule = (tree: Tree) => ((rulesCalled[1] = tree), tree2);
const rule2: Rule = (tree: Tree) => ((rulesCalled[2] = tree), tree3);

function* generateRules() {
yield rule0;
yield rule1;
yield rule2;
}

const result = await callRule(chain(generateRules()), tree0, context).toPromise();

expect(result).not.toBe(tree0);
expect(rulesCalled[0]).toBe(tree0);
expect(rulesCalled[1]).toBe(tree1);
expect(rulesCalled[2]).toBe(tree2);
expect(result).toBe(tree3);
});

it('works with an async generator of rules', async () => {
const rulesCalled: Tree[] = [];

const tree0 = empty();
const tree1 = empty();
const tree2 = empty();
const tree3 = empty();

const rule0: Rule = (tree: Tree) => ((rulesCalled[0] = tree), tree1);
const rule1: Rule = (tree: Tree) => ((rulesCalled[1] = tree), tree2);
const rule2: Rule = (tree: Tree) => ((rulesCalled[2] = tree), tree3);

async function* generateRules() {
yield rule0;
yield rule1;
yield rule2;
}

const result = await callRule(chain(generateRules()), tree0, context).toPromise();

expect(result).not.toBe(tree0);
expect(rulesCalled[0]).toBe(tree0);
expect(rulesCalled[1]).toBe(tree1);
expect(rulesCalled[2]).toBe(tree2);
expect(result).toBe(tree3);
});

it('works with observable rules', (done) => {
const rulesCalled: Tree[] = [];

Expand Down

0 comments on commit 526cdb2

Please sign in to comment.