Skip to content

Commit

Permalink
fix(@angular-devkit/schematics): implement HostTree specific filtering
Browse files Browse the repository at this point in the history
  • Loading branch information
clydin committed Jul 3, 2018
1 parent 61d2181 commit 9582b84
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 52 deletions.
9 changes: 7 additions & 2 deletions packages/angular_devkit/schematics/src/rules/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
import { Observable, of as observableOf } from 'rxjs';
import { concatMap, last, map } from 'rxjs/operators';
import { FileOperator, Rule, SchematicContext, Source } from '../engine/interface';
import { FilterTree, FilteredTree } from '../tree/filtered';
import { SchematicsException } from '../exception/exception';
import { FilteredTree } from '../tree/filtered';
import { FilterHostTree, HostTree } from '../tree/host-tree';
import { FileEntry, FilePredicate, MergeStrategy, Tree } from '../tree/interface';
import {
branch,
Expand Down Expand Up @@ -93,10 +95,13 @@ export function noop(): Rule {

export function filter(predicate: FilePredicate<boolean>): Rule {
return ((tree: Tree) => {
// TODO: Remove VirtualTree usage in 7.0
if (tree instanceof VirtualTree) {
return new FilteredTree(tree, predicate);
} else if (tree instanceof HostTree) {
return new FilterHostTree(tree, predicate);
} else {
return new FilterTree(tree, predicate);
throw new SchematicsException('Tree type is not supported.');
}
});
}
Expand Down
15 changes: 1 addition & 14 deletions packages/angular_devkit/schematics/src/tree/filtered.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { DelegateTree } from './delegate';
import { FilePredicate, Tree } from './interface';
import { VirtualTree } from './virtual';


// TODO: Remove this along with VirtualTree in 7.0
export class FilteredTree extends VirtualTree {
constructor(tree: Tree, filter: FilePredicate<boolean> = () => true) {
super();
Expand Down Expand Up @@ -39,15 +38,3 @@ export class FilteredTree extends VirtualTree {
});
}
}

export class FilterTree extends DelegateTree {
constructor(tree: Tree, filter: FilePredicate<boolean> = () => true) {
super(tree.branch());

tree.visit(path => {
if (!filter(path)) {
this.delete(path);
}
});
}
}
30 changes: 1 addition & 29 deletions packages/angular_devkit/schematics/src/tree/filtered_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import { normalize } from '@angular-devkit/core';
import { FilterTree, FilteredTree } from './filtered';
import { HostTree } from './host-tree';
import { FilteredTree } from './filtered';
import { VirtualTree } from './virtual';


Expand All @@ -22,30 +21,3 @@ describe('FilteredTree', () => {
expect(filtered.files.sort()).toEqual(['/file1', '/file3'].map(normalize));
});
});

describe('FilterTree', () => {
it('works', () => {
const tree = new HostTree();
tree.create('/file1', '');
tree.create('/file2', '');
tree.create('/file3', '');

const filtered = new FilterTree(tree, p => p != '/file2');
const filteredFiles: string[] = [];
filtered.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/file1', '/file3'].map(normalize));
});

it('works with two filters', () => {
const tree = new HostTree();
tree.create('/file1', '');
tree.create('/file2', '');
tree.create('/file3', '');

const filtered = new FilterTree(tree, p => p != '/file2');
const filtered2 = new FilterTree(filtered, p => p != '/file3');
const filteredFiles: string[] = [];
filtered2.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/file1'].map(normalize));
});
});
68 changes: 67 additions & 1 deletion packages/angular_devkit/schematics/src/tree/host-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
normalize,
virtualFs,
} from '@angular-devkit/core';
import { Observable, of } from 'rxjs';
import { concatMap, map, mergeMap } from 'rxjs/operators';
import {
ContentHasMutatedException,
FileAlreadyExistException,
Expand All @@ -35,10 +37,12 @@ import { LazyFileEntry } from './entry';
import {
DirEntry,
FileEntry,
FilePredicate,
FileVisitor,
FileVisitorCancelToken,
MergeStrategy,
Tree, TreeSymbol,
Tree,
TreeSymbol,
UpdateRecorder,
} from './interface';
import { UpdateRecorderBase } from './recorder';
Expand Down Expand Up @@ -414,3 +418,65 @@ export class HostCreateTree extends HostTree {
});
}
}

export class FilterHostTree extends HostTree {
constructor(tree: HostTree, filter: FilePredicate<boolean> = () => true) {
const newBackend = new virtualFs.SimpleMemoryHost();
// cast to allow access
const originalBackend = (tree as FilterHostTree)._backend;

const recurse: (base: Path) => Observable<void> = base => {
return originalBackend.list(base)
.pipe(
mergeMap(x => x),
map(path => join(base, path)),
concatMap(path => {
let isDirectory = false;
originalBackend.isDirectory(path).subscribe(val => isDirectory = val);
if (isDirectory) {
return recurse(path);
}

let isFile = false;
originalBackend.isFile(path).subscribe(val => isFile = val);
if (!isFile || !filter(path)) {
return of();
}

let content: ArrayBuffer | null = null;
originalBackend.read(path).subscribe(val => content = val);
if (!content) {
return of();
}

return newBackend.write(path, content as {} as virtualFs.FileBuffer);
}),
);
};

recurse(normalize('/')).subscribe();

super(newBackend);

for (const action of tree.actions) {
if (!filter(action.path)) {
continue;
}

switch (action.kind) {
case 'c':
this.create(action.path, action.content);
break;
case 'd':
this.delete(action.path);
break;
case 'o':
this.overwrite(action.path, action.content);
break;
case 'r':
this.rename(action.path, action.to);
break;
}
}
}
}
77 changes: 76 additions & 1 deletion packages/angular_devkit/schematics/src/tree/host-tree_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// tslint:disable:no-any
import { normalize, virtualFs } from '@angular-devkit/core';
import { Action } from './action';
import { HostTree } from './host-tree';
import { FilterHostTree, HostTree } from './host-tree';
import { VirtualTree } from './virtual';

describe('HostTree', () => {
Expand Down Expand Up @@ -36,3 +36,78 @@ describe('HostTree', () => {
] as any);
});
});

describe('FilterHostTree', () => {
it('works', () => {
const tree = new HostTree();
tree.create('/file1', '');
tree.create('/file2', '');
tree.create('/file3', '');

const filtered = new FilterHostTree(tree, p => p != '/file2');
const filteredFiles: string[] = [];
filtered.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/file1', '/file3'].map(normalize));
expect(filtered.actions.length).toEqual(2);
});

it('works with two filters', () => {
const tree = new HostTree();
tree.create('/file1', '');
tree.create('/file2', '');
tree.create('/file3', '');

const filtered = new FilterHostTree(tree, p => p != '/file2');
const filtered2 = new FilterHostTree(filtered, p => p != '/file3');
const filteredFiles: string[] = [];
filtered2.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/file1'].map(normalize));
expect(filtered2.actions.map(a => a.kind)).toEqual(['c']);
});

it('works with underlying files', () => {
const fs = new virtualFs.test.TestHost({
'/file1': '',
});
const tree = new HostTree(fs);
tree.create('/file2', '');
tree.create('/file3', '');

const filtered = new FilterHostTree(tree, p => p != '/file2');
const filtered2 = new FilterHostTree(filtered, p => p != '/file3');
const filteredFiles: string[] = [];
filtered2.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/file1'].map(normalize));
expect(filtered2.actions.map(a => a.kind)).toEqual([]);
});

it('works with created paths and files', () => {
const tree = new HostTree();
tree.create('/dir1/file1', '');
tree.create('/dir2/file2', '');
tree.create('/file3', '');

const filtered = new FilterHostTree(tree, p => p != '/dir2/file2');
const filtered2 = new FilterHostTree(filtered, p => p != '/file3');
const filteredFiles: string[] = [];
filtered2.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/dir1/file1'].map(normalize));
expect(filtered2.actions.map(a => a.kind)).toEqual(['c']);
});

it('works with underlying paths and files', () => {
const fs = new virtualFs.test.TestHost({
'/dir1/file1': '',
'/dir2/file2': '',
});
const tree = new HostTree(fs);
tree.create('/file3', '');

const filtered = new FilterHostTree(tree, p => p != '/dir2/file2');
const filtered2 = new FilterHostTree(filtered, p => p != '/file3');
const filteredFiles: string[] = [];
filtered2.visit(path => filteredFiles.push(path));
expect(filteredFiles.sort()).toEqual(['/dir1/file1'].map(normalize));
expect(filtered2.actions.map(a => a.kind)).toEqual([]);
});
});
13 changes: 8 additions & 5 deletions packages/angular_devkit/schematics/src/tree/static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { FilterTree, FilteredTree } from './filtered';
import { HostTree } from './host-tree';
import { SchematicsException } from '../exception/exception';
import { FilteredTree } from './filtered';
import { FilterHostTree, HostTree } from './host-tree';
import { FilePredicate, MergeStrategy, Tree } from './interface';
import { VirtualTree } from './virtual';

Expand Down Expand Up @@ -42,11 +43,13 @@ export function partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree,
new FilteredTree(tree, predicate),
new FilteredTree(tree, (path, entry) => !predicate(path, entry)),
];
} else {
} else if (tree instanceof HostTree) {
return [
new FilterTree(tree, predicate),
new FilterTree(tree, (path, entry) => !predicate(path, entry)),
new FilterHostTree(tree, predicate),
new FilterHostTree(tree, (path, entry) => !predicate(path, entry)),
];
} else {
throw new SchematicsException('Tree type is not supported.');
}
}

Expand Down

0 comments on commit 9582b84

Please sign in to comment.