Skip to content

Commit 0fffc6d

Browse files
committed
fix(@angular-devkit/schematics): support delegating trees in the filter rule
`filter()` only accepted a `HostTree` and threw "Tree type is not supported." for any other tree. A `UnitTestTree` is a `DelegateTree` around a `HostTree`, so a schematic that uses `filter()` failed in unit tests when run on the tree returned by a previous `runSchematic` call. Unwrap delegating trees and filter the `HostTree` they wrap. Closes #26000
1 parent 0be52a8 commit 0fffc6d

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

‎packages/angular_devkit/schematics/src/rules/base.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { concat, lastValueFrom, map, mapTo, toArray } from 'rxjs';
1010
import { FileOperator, Rule, Source } from '../engine/interface';
1111
import { SchematicsException } from '../exception/exception';
12+
import { DelegateTree } from '../tree/delegate';
1213
import { FilterHostTree, HostTree } from '../tree/host-tree';
1314
import { FileEntry, FilePredicate, MergeStrategy, Tree } from '../tree/interface';
1415
import { ScopedTree } from '../tree/scoped';
@@ -74,8 +75,15 @@ export function noop(): Rule {
7475

7576
export function filter(predicate: FilePredicate<boolean>): Rule {
7677
return (tree: Tree) => {
77-
if (HostTree.isHostTree(tree)) {
78-
return new FilterHostTree(tree, predicate);
78+
// Trees such as `UnitTestTree` delegate to a `HostTree`, so filter the tree they wrap.
79+
let hostTree = tree;
80+
while (hostTree instanceof DelegateTree) {
81+
// cast to allow access
82+
hostTree = (hostTree as unknown as { _other: Tree })._other;
83+
}
84+
85+
if (HostTree.isHostTree(hostTree)) {
86+
return new FilterHostTree(hostTree, predicate);
7987
} else {
8088
throw new SchematicsException('Tree type is not supported.');
8189
}

‎packages/angular_devkit/schematics/src/rules/base_spec.ts‎

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88

99
import { Path, virtualFs } from '@angular-devkit/core';
1010
import { lastValueFrom, of as observableOf } from 'rxjs';
11+
import { UnitTestTree } from '../../testing';
1112
import { Rule, SchematicContext, Source } from '../engine/interface';
1213
import { HostTree } from '../tree/host-tree';
1314
import { MergeStrategy, Tree } from '../tree/interface';
1415
import { empty } from '../tree/static';
15-
import { apply, applyToSubtree, chain, partitionApplyMerge } from './base';
16+
import { apply, applyToSubtree, chain, filter, partitionApplyMerge } from './base';
1617
import { callRule, callSource } from './call';
1718
import { move } from './move';
1819

@@ -242,3 +243,37 @@ describe('applyToSubtree', () => {
242243
expect(result.exists('a/c/file3')).toBe(true);
243244
});
244245
});
246+
247+
describe('filter', () => {
248+
it('works with a HostTree', async () => {
249+
const tree = new HostTree();
250+
tree.create('/file1.ts', '');
251+
tree.create('/file2.js', '');
252+
253+
const result = await lastValueFrom(
254+
callRule(
255+
filter((path) => path.endsWith('.ts')),
256+
observableOf(tree),
257+
context,
258+
),
259+
);
260+
expect(result.exists('/file1.ts')).toBe(true);
261+
expect(result.exists('/file2.js')).toBe(false);
262+
});
263+
264+
it('works with a UnitTestTree', async () => {
265+
const tree = new HostTree();
266+
tree.create('/file1.ts', '');
267+
tree.create('/file2.js', '');
268+
269+
const result = await lastValueFrom(
270+
callRule(
271+
filter((path) => path.endsWith('.ts')),
272+
observableOf(new UnitTestTree(new UnitTestTree(tree))),
273+
context,
274+
),
275+
);
276+
expect(result.exists('/file1.ts')).toBe(true);
277+
expect(result.exists('/file2.js')).toBe(false);
278+
});
279+
});

0 commit comments

Comments
 (0)