Skip to content

Commit 01e2424

Browse files
clydinalexeagle
authored andcommitted
fix(@schematics/angular): avoid tslint overhead for lazy module migration
By using a pure schematic based implementation the overhead of running tslint can be removed. This also has the benefit of allowing direct control over the files loaded and to modify files only within the schematic tree context. For a hello world application, there was a ~20% performance improvement for the CLI migration from 7.0 to 8.0 as well as a ~10% reduction in memory usage.
1 parent 537bf5e commit 01e2424

File tree

3 files changed

+75
-100
lines changed

3 files changed

+75
-100
lines changed

packages/schematics/angular/migrations/update-8/rules/noLazyModulePathsRule.ts

Lines changed: 0 additions & 63 deletions
This file was deleted.

packages/schematics/angular/migrations/update-8/update-lazy-module-paths.ts

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,72 @@
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 {
9-
Rule,
10-
SchematicContext,
11-
Tree,
12-
} from '@angular-devkit/schematics';
13-
import {
14-
TslintFixTask,
15-
} from '@angular-devkit/schematics/tasks';
16-
import * as path from 'path';
17-
18-
export const updateLazyModulePaths = (): Rule => {
19-
return (_: Tree, context: SchematicContext) => {
20-
context.addTask(new TslintFixTask({
21-
rulesDirectory: path.join(__dirname, 'rules'),
22-
rules: {
23-
'no-lazy-module-paths': [true],
24-
},
25-
}, {
26-
includes: '**/*.ts',
27-
silent: false,
28-
}));
8+
import { DirEntry, Rule, UpdateRecorder } from '@angular-devkit/schematics';
9+
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
10+
11+
function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
12+
for (const path of directory.subfiles) {
13+
if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
14+
const entry = directory.file(path);
15+
if (entry) {
16+
const content = entry.content;
17+
if (content.includes('loadChildren')) {
18+
const source = ts.createSourceFile(
19+
entry.path,
20+
content.toString(),
21+
ts.ScriptTarget.Latest,
22+
true,
23+
);
24+
25+
yield source;
26+
}
27+
}
28+
}
29+
}
30+
31+
for (const path of directory.subdirs) {
32+
if (path === 'node_modules') {
33+
continue;
34+
}
35+
36+
yield* visit(directory.dir(path));
37+
}
38+
}
39+
40+
export function updateLazyModulePaths(): Rule {
41+
return tree => {
42+
for (const sourceFile of visit(tree.root)) {
43+
let recorder: UpdateRecorder | undefined;
44+
45+
ts.forEachChild(sourceFile, function analyze(node) {
46+
if (ts.isPropertyAssignment(node) &&
47+
(ts.isIdentifier(node.name) || ts.isStringLiteral(node.name)) &&
48+
node.name.text === 'loadChildren' &&
49+
ts.isStringLiteral(node.initializer)) {
50+
const valueNode = node.initializer;
51+
const parts = valueNode.text.split('#');
52+
const path = parts[0];
53+
const moduleName = parts[1] || 'default';
54+
55+
const fix = `() => import('${path}').then(m => m.${moduleName})`;
56+
57+
if (!recorder) {
58+
recorder = tree.beginUpdate(sourceFile.fileName);
59+
}
60+
61+
const index = valueNode.getStart();
62+
const length = valueNode.getWidth();
63+
recorder
64+
.remove(index, length)
65+
.insertLeft(index, fix);
66+
}
67+
68+
ts.forEachChild(node, analyze);
69+
});
70+
71+
if (recorder) {
72+
tree.commitUpdate(recorder);
73+
}
74+
}
2975
};
30-
};
76+
}

packages/schematics/angular/migrations/update-8/update-lazy-module-paths_spec.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
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 { getSystemPath, normalize, virtualFs } from '@angular-devkit/core';
9-
import { TempScopedNodeJsSyncHost } from '@angular-devkit/core/node/testing';
10-
import { HostTree } from '@angular-devkit/schematics';
8+
import { normalize, virtualFs } from '@angular-devkit/core';
9+
import { EmptyTree } from '@angular-devkit/schematics';
1110
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
1211

1312
describe('Migration to version 8', () => {
@@ -17,7 +16,6 @@ describe('Migration to version 8', () => {
1716
);
1817

1918
let tree: UnitTestTree;
20-
let host: TempScopedNodeJsSyncHost;
2119

2220
const lazyRoutePath = normalize('src/lazy-route.ts');
2321
const lazyRoute = virtualFs.stringToFileBuffer(`
@@ -45,36 +43,30 @@ describe('Migration to version 8', () => {
4543

4644
describe('Migration to import() style lazy routes', () => {
4745
beforeEach(async () => {
48-
host = new TempScopedNodeJsSyncHost();
49-
tree = new UnitTestTree(new HostTree(host));
46+
tree = new UnitTestTree(new EmptyTree());
5047
tree.create('/package.json', JSON.stringify({}));
51-
process.chdir(getSystemPath(host.root));
5248
});
5349

5450
it('should replace the module path string', async () => {
55-
await host.write(lazyRoutePath, lazyRoute).toPromise();
51+
tree.create(lazyRoutePath, Buffer.from(lazyRoute));
5652

5753
schematicRunner.runSchematic('migration-08', {}, tree);
5854
await schematicRunner.engine.executePostTasks().toPromise();
5955

60-
const routes = await host.read(lazyRoutePath)
61-
.toPromise()
62-
.then(virtualFs.fileBufferToString);
56+
const routes = tree.readContent(lazyRoutePath);
6357

6458
expect(routes).not.toContain('./lazy/lazy.module#LazyModule');
6559
expect(routes).toContain(
6660
`loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)`);
6761
});
6862

6963
it('should replace the module path string in a child path', async () => {
70-
await host.write(lazyRoutePath, lazyChildRoute).toPromise();
64+
tree.create(lazyRoutePath, Buffer.from(lazyChildRoute));
7165

7266
schematicRunner.runSchematic('migration-08', {}, tree);
7367
await schematicRunner.engine.executePostTasks().toPromise();
7468

75-
const routes = await host.read(lazyRoutePath)
76-
.toPromise()
77-
.then(virtualFs.fileBufferToString);
69+
const routes = tree.readContent(lazyRoutePath);
7870

7971
expect(routes).not.toContain('./lazy/lazy.module#LazyModule');
8072

0 commit comments

Comments
 (0)