Skip to content

Commit ac6d0e8

Browse files
committed
refactor: remove files property
I added it as a property of UnitTestTree so it can be used for testing where its actually useful. Otherwsie the visit() function acts in a similar fashion.
1 parent 2099030 commit ac6d0e8

File tree

10 files changed

+137
-28
lines changed

10 files changed

+137
-28
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ const collection = {
4242
} as CollectionDescription<CollectionT>;
4343

4444

45+
function files(tree: Tree) {
46+
const files: string[] = [];
47+
tree.visit(x => files.push(x));
48+
49+
return files;
50+
}
51+
52+
4553
describe('Schematic', () => {
4654
it('works with a rule', done => {
4755
let inner: Tree | null = null;
@@ -62,8 +70,8 @@ describe('Schematic', () => {
6270
schematic.call({}, Observable.of(empty()))
6371
.toPromise()
6472
.then(x => {
65-
expect(inner !.files).toEqual([]);
66-
expect(x.files).toEqual(['/a/b/c']);
73+
expect(files(inner !)).toEqual([]);
74+
expect(files(x)).toEqual(['/a/b/c']);
6775
})
6876
.then(done, done.fail);
6977
});
@@ -87,8 +95,8 @@ describe('Schematic', () => {
8795
schematic.call({}, Observable.of(empty()))
8896
.toPromise()
8997
.then(x => {
90-
expect(inner !.files).toEqual([]);
91-
expect(x.files).toEqual([]);
98+
expect(files(inner !)).toEqual([]);
99+
expect(files(x)).toEqual([]);
92100
expect(inner).not.toBe(x);
93101
})
94102
.then(done, done.fail);

packages/angular_devkit/schematics/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export * from './rules/random';
2424
export * from './rules/schematic';
2525
export * from './rules/template';
2626
export * from './rules/url';
27+
export * from './tree/delegate';
2728
export * from './tree/empty';
2829
export * from './tree/filesystem';
2930
export * from './tree/memory-host';

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ export function partitionApplyMerge(
133133

134134
export function forEach(operator: FileOperator): Rule {
135135
return (tree: Tree) => {
136-
tree.files.forEach(path => {
137-
const entry = tree.get(path);
136+
tree.visit((path, entry) => {
138137
if (!entry) {
139138
return;
140139
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { Action } from './action';
9+
import {
10+
DirEntry,
11+
FileEntry,
12+
FileVisitor,
13+
MergeStrategy,
14+
Tree,
15+
UpdateRecorder,
16+
} from './interface';
17+
18+
export class DelegateTree implements Tree {
19+
constructor(protected _other: Tree) {}
20+
21+
branch(): Tree { return this._other.branch(); }
22+
merge(other: Tree, strategy?: MergeStrategy): void { this._other.merge(other, strategy); }
23+
24+
get root(): DirEntry { return this._other.root; }
25+
26+
// Readonly.
27+
read(path: string): Buffer | null { return this._other.read(path); }
28+
exists(path: string): boolean { return this._other.exists(path); }
29+
get(path: string): FileEntry | null { return this._other.get(path); }
30+
getDir(path: string): DirEntry { return this._other.getDir(path); }
31+
visit(visitor: FileVisitor): void { return this._other.visit(visitor); }
32+
33+
// Change content of host files.
34+
overwrite(path: string, content: Buffer | string): void {
35+
return this._other.overwrite(path, content);
36+
}
37+
beginUpdate(path: string): UpdateRecorder { return this._other.beginUpdate(path); }
38+
commitUpdate(record: UpdateRecorder): void { return this._other.commitUpdate(record); }
39+
40+
// Structural methods.
41+
create(path: string, content: Buffer | string): void {
42+
return this._other.create(path, content);
43+
}
44+
delete(path: string): void { return this._other.delete(path); }
45+
rename(from: string, to: string): void { return this._other.rename(from, to); }
46+
47+
apply(action: Action, strategy?: MergeStrategy): void {
48+
return this._other.apply(action, strategy);
49+
}
50+
get actions(): Action[] { return this._other.actions; }
51+
}

packages/angular_devkit/schematics/src/tree/interface.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,22 @@ export interface FilePredicate<T> {
5151
(path: Path, entry?: Readonly<FileEntry> | null): T;
5252
}
5353

54+
export const FileVisitorCancelToken = Symbol();
55+
export type FileVisitor = FilePredicate<void>;
56+
5457

5558
export interface Tree {
59+
branch(): Tree;
60+
merge(other: Tree, strategy?: MergeStrategy): void;
61+
5662
readonly root: DirEntry;
5763

5864
// Readonly.
59-
readonly files: string[];
60-
exists(path: string): boolean;
61-
62-
// Content access.
6365
read(path: string): Buffer | null;
66+
exists(path: string): boolean;
6467
get(path: string): FileEntry | null;
6568
getDir(path: string): DirEntry;
69+
visit(visitor: FileVisitor): void;
6670

6771
// Change content of host files.
6872
overwrite(path: string, content: Buffer | string): void;

packages/angular_devkit/schematics/src/tree/null.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,19 @@ export class NullTreeDirEntry implements DirEntry {
4242

4343

4444
export class NullTree implements Tree {
45+
branch(): Tree {
46+
return new NullTree();
47+
}
48+
merge(_other: Tree, _strategy?: MergeStrategy): void {}
49+
4550
readonly root: DirEntry = new NullTreeDirEntry(normalize('/'));
4651

4752
// Simple readonly file system operations.
4853
exists(_path: string) { return false; }
4954
read(_path: string) { return null; }
5055
get(_path: string) { return null; }
5156
getDir(path: string) { return new NullTreeDirEntry(normalize('/' + path)); }
52-
get files(): string[] { return []; }
57+
visit() {}
5358

5459
// Change content of host files.
5560
beginUpdate(path: string): never {

packages/angular_devkit/schematics/src/tree/virtual.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ import {
2323
} from '../exception/exception';
2424
import { Action, ActionList, UnknownActionException } from './action';
2525
import { SimpleFileEntry } from './entry';
26-
import { DirEntry, FileEntry, MergeStrategy, Tree, UpdateRecorder } from './interface';
26+
import {
27+
DirEntry,
28+
FileEntry,
29+
FileVisitor,
30+
FileVisitorCancelToken,
31+
MergeStrategy,
32+
Tree,
33+
UpdateRecorder,
34+
} from './interface';
2735
import { UpdateRecorderBase } from './recorder';
2836

2937

@@ -126,6 +134,16 @@ export class VirtualTree implements Tree {
126134
return dir;
127135
}
128136

137+
visit(visitor: FileVisitor) {
138+
try {
139+
this.files.forEach(path => visitor(path, this.get(path)));
140+
} catch (e) {
141+
if (e !== FileVisitorCancelToken) {
142+
throw e;
143+
}
144+
}
145+
}
146+
129147
beginUpdate(path: string): UpdateRecorder {
130148
const entry = this.get(path);
131149
if (!entry) {
@@ -308,12 +326,12 @@ export class VirtualTree implements Tree {
308326
}
309327

310328
static branch(tree: Tree) {
311-
return (tree as VirtualTree).branch();
329+
return tree.branch();
312330
}
313331

314332
static merge(tree: Tree, other: Tree, strategy: MergeStrategy = MergeStrategy.Default): Tree {
315-
const newTree = (tree as VirtualTree).branch() as VirtualTree;
316-
newTree.merge((other as VirtualTree), strategy);
333+
const newTree = tree.branch();
334+
newTree.merge(other, strategy);
317335

318336
return newTree;
319337
}

packages/angular_devkit/schematics/src/tree/virtual_spec.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@
99
import { normalize } from '@angular-devkit/core';
1010
import { FileAlreadyExistException, FileDoesNotExistException } from '../exception/exception';
1111
import { FileSystemTree } from './filesystem';
12-
import { FileEntry, MergeStrategy } from './interface';
12+
import { FileEntry, MergeStrategy, Tree } from './interface';
1313
import { InMemoryFileSystemTreeHost } from './memory-host';
1414
import { merge, partition } from './static';
1515
import { VirtualTree } from './virtual';
1616

1717

18+
function files(tree: Tree) {
19+
const treeFiles: string[] = [];
20+
tree.visit(x => treeFiles.push(x));
21+
22+
return treeFiles;
23+
}
24+
25+
1826
describe('VirtualTree', () => {
1927
describe('exists()', () => {
2028
it('works', () => {
@@ -39,7 +47,7 @@ describe('VirtualTree', () => {
3947
tree.create('/some/other-file', 'some _content');
4048
tree.create('/some/other-file2', 'some _content');
4149

42-
expect(tree.files).toEqual([
50+
expect(files(tree)).toEqual([
4351
'/some/file', '/some/other-file', '/some/other-file2',
4452
].map(normalize));
4553
});
@@ -91,7 +99,7 @@ describe('VirtualTree', () => {
9199
expect(tree2.exists('file3')).toBe(true);
92100

93101
const tree3 = merge(tree1, tree2, MergeStrategy.Error);
94-
expect(tree3.files.sort()).toEqual(tree.files.sort());
102+
expect(files(tree3).sort()).toEqual(files(tree).sort());
95103
});
96104
});
97105

@@ -127,8 +135,8 @@ describe('VirtualTree', () => {
127135
tree.overwrite('/hello', 'world');
128136
tree.overwrite('/test', 'test 3');
129137

130-
const files = ['/hello', '/sub/directory/file2', '/sub/file1', '/test'];
131-
expect(tree.files).toEqual(files.map(normalize));
138+
const expected = ['/hello', '/sub/directory/file2', '/sub/file1', '/test'];
139+
expect(files(tree)).toEqual(expected.map(normalize));
132140

133141
const tree2 = tree.branch() as VirtualTree;
134142
expect(tree.actions.length).toBe(4);

packages/angular_devkit/schematics/testing/schematic-test-runner.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { Logger, schema } from '@angular-devkit/core';
99
import {
1010
Collection,
11+
DelegateTree,
1112
SchematicEngine,
1213
Tree,
1314
VirtualTree,
@@ -21,6 +22,16 @@ import { Observable } from 'rxjs/Observable';
2122

2223
export interface SchematicSchemaT {}
2324

25+
26+
export class UnitTestTree extends DelegateTree {
27+
get files() {
28+
const result: string[] = [];
29+
this.visit(path => result.push(path));
30+
31+
return result;
32+
}
33+
}
34+
2435
export class SchematicTestRunner {
2536
private _engineHost = new NodeModulesTestEngineHost();
2637
private _engine: SchematicEngine<{}, {}> = new SchematicEngine(this._engineHost);
@@ -57,21 +68,26 @@ export class SchematicTestRunner {
5768

5869
get logger() { return this._logger; }
5970

60-
runSchematicAsync(schematicName: string, opts?: SchematicSchemaT, tree?: Tree): Observable<Tree> {
71+
runSchematicAsync(
72+
schematicName: string,
73+
opts?: SchematicSchemaT,
74+
tree?: Tree,
75+
): Observable<UnitTestTree> {
6176
const schematic = this._collection.createSchematic(schematicName);
6277
const host = Observable.of(tree || new VirtualTree);
6378

64-
return schematic.call(opts || {}, host, { logger: this._logger });
79+
return schematic.call(opts || {}, host, { logger: this._logger })
80+
.map(tree => new UnitTestTree(tree));
6581
}
6682

67-
runSchematic(schematicName: string, opts?: SchematicSchemaT, tree?: Tree): Tree {
83+
runSchematic(schematicName: string, opts?: SchematicSchemaT, tree?: Tree): UnitTestTree {
6884
const schematic = this._collection.createSchematic(schematicName);
6985

70-
let result: Tree | null = null;
86+
let result: UnitTestTree | null = null;
7187
const host = Observable.of(tree || new VirtualTree);
7288

7389
schematic.call(opts || {}, host, { logger: this._logger })
74-
.subscribe(t => result = t);
90+
.subscribe(t => result = new UnitTestTree(t));
7591

7692
if (result === null) {
7793
throw new Error('Schematic is async, please use runSchematicAsync');

packages/schematics/angular/application/index_spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { Tree } from '@angular-devkit/schematics';
9-
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
8+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
109
import * as path from 'path';
1110
import { getFileContent } from '../utility/test';
1211
import { Schema as ApplicationOptions } from './schema';
@@ -72,7 +71,7 @@ describe('Application Schematic', () => {
7271
it('should handle a different sourceDir', () => {
7372
const options = { ...defaultOptions, sourceDir: 'some/custom/path' };
7473

75-
let tree: Tree | null = null;
74+
let tree: UnitTestTree | null = null;
7675
expect(() => tree = schematicRunner.runSchematic('application', options))
7776
.not.toThrow();
7877

0 commit comments

Comments
 (0)