Skip to content

Commit 774076c

Browse files
FrozenPandazvikerman
authored andcommitted
refactor(@angular-devkit/schematics): improve performance of move() (#12857)
1 parent ebd2453 commit 774076c

File tree

4 files changed

+72
-29
lines changed

4 files changed

+72
-29
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,17 @@ export function move(from: string, to?: string): Rule {
2323
return noop;
2424
}
2525

26-
return tree => tree.visit(path => {
27-
if (path.startsWith(fromPath)) {
28-
tree.rename(path, toPath + '/' + path.substr(fromPath.length));
26+
return tree => {
27+
if (tree.exists(fromPath)) {
28+
// fromPath is a file
29+
tree.rename(fromPath, toPath);
30+
} else {
31+
// fromPath is a directory
32+
tree.getDir(fromPath).visit(path => {
33+
tree.rename(path, toPath + '/' + path.substr(fromPath.length));
34+
});
2935
}
30-
});
36+
37+
return tree;
38+
};
3139
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,40 @@ describe('move', () => {
4949
.then(done, done.fail);
5050
});
5151

52+
it('works on moving a directory into a subdirectory of itself', done => {
53+
const tree = new HostTree();
54+
tree.create('a/b/file1', 'hello world');
55+
tree.create('a/b/file2', 'hello world');
56+
tree.create('a/c/file3', 'hello world');
57+
58+
callRule(move('a/b', 'a/b/c'), observableOf(tree), context)
59+
.toPromise()
60+
.then(result => {
61+
expect(result.exists('a/b/c/file1')).toBe(true);
62+
expect(result.exists('a/b/c/file2')).toBe(true);
63+
expect(result.exists('a/c/file3')).toBe(true);
64+
})
65+
.then(done, done.fail);
66+
});
67+
68+
it('works on moving a directory into a parent of itself', done => {
69+
const tree = new HostTree();
70+
tree.create('a/b/file1', 'hello world');
71+
tree.create('a/b/file2', 'hello world');
72+
tree.create('a/c/file3', 'hello world');
73+
74+
callRule(move('a/b', 'a'), observableOf(tree), context)
75+
.toPromise()
76+
.then(result => {
77+
expect(result.exists('file1')).toBe(false);
78+
expect(result.exists('file2')).toBe(false);
79+
expect(result.exists('a/file1')).toBe(true);
80+
expect(result.exists('a/file2')).toBe(true);
81+
expect(result.exists('a/c/file3')).toBe(true);
82+
})
83+
.then(done, done.fail);
84+
});
85+
5286
it('becomes a noop with identical from and to', done => {
5387
const tree = new HostTree();
5488
tree.create('a/b/file1', 'hello world');

packages/angular_devkit/schematics/src/tree/host-tree.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,25 @@ export class HostDirEntry implements DirEntry {
7676
}
7777

7878
visit(visitor: FileVisitor): void {
79-
function _recurse(entry: DirEntry) {
80-
entry.subfiles.forEach(path => {
81-
visitor(join(entry.path, path), entry.file(path));
82-
});
83-
entry.subdirs.forEach(path => {
84-
_recurse(entry.dir(path));
85-
});
86-
}
87-
8879
try {
89-
_recurse(this);
80+
this.getSubfilesRecursively().forEach(file => visitor(file.path, file));
9081
} catch (e) {
9182
if (e !== FileVisitorCancelToken) {
9283
throw e;
9384
}
9485
}
9586
}
87+
88+
private getSubfilesRecursively() {
89+
function _recurse(entry: DirEntry): FileEntry[] {
90+
return entry.subdirs.reduce((files, subdir) => [
91+
...files,
92+
..._recurse(entry.dir(subdir)),
93+
], entry.subfiles.map(subfile => entry.file(subfile) as FileEntry));
94+
}
95+
96+
return _recurse(this);
97+
}
9698
}
9799

98100

@@ -315,12 +317,9 @@ export class HostTree implements Tree {
315317
return maybeCache;
316318
}
317319
visit(visitor: FileVisitor): void {
318-
const allFiles: [Path, FileEntry | null | undefined][] = [];
319320
this.root.visit((path, entry) => {
320-
allFiles.push([path, entry]);
321+
visitor(path, entry);
321322
});
322-
323-
allFiles.forEach(([path, entry]) => visitor(path, entry));
324323
}
325324

326325
// Change content of host files.

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,26 @@ export class VirtualDirEntry implements DirEntry {
8484
return this._tree.get(join(this._path, name));
8585
}
8686

87-
visit(visitor: FileVisitor) {
88-
function _recurse(entry: VirtualDirEntry) {
89-
entry.subfiles.forEach(path => {
90-
visitor(join(entry._path, path), entry.file(path));
91-
});
92-
entry.subdirs.forEach(path => {
93-
_recurse(entry.dir(path) as VirtualDirEntry);
94-
});
95-
}
96-
87+
visit(visitor: FileVisitor): void {
9788
try {
98-
_recurse(this);
89+
this.getSubfilesRecursively().forEach(file => visitor(file.path, file));
9990
} catch (e) {
10091
if (e !== FileVisitorCancelToken) {
10192
throw e;
10293
}
10394
}
10495
}
96+
97+
private getSubfilesRecursively() {
98+
function _recurse(entry: DirEntry): FileEntry[] {
99+
return entry.subdirs.reduce((files, subdir) => [
100+
...files,
101+
..._recurse(entry.dir(subdir)),
102+
], entry.subfiles.map(subfile => entry.file(subfile) as FileEntry));
103+
}
104+
105+
return _recurse(this);
106+
}
105107
}
106108

107109
/**

0 commit comments

Comments
 (0)