Skip to content

Commit

Permalink
feat(@schematics/angular): add lazy module path fixer
Browse files Browse the repository at this point in the history
  • Loading branch information
phenomnomnominal authored and alexeagle committed Apr 16, 2019
1 parent 5fc1f24 commit 77f99b5
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 1 deletion.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
]
},
"dependencies": {
"@phenomnomnominal/tsquery": "3.0.0",
"@types/debug": "^4.1.2",
"@types/node-fetch": "^2.1.6",
"@types/progress": "^2.0.3",
Expand Down
5 changes: 5 additions & 0 deletions packages/schematics/angular/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ ts_library(
"//packages/angular_devkit/schematics",
"//packages/angular_devkit/schematics:tasks",
"//packages/schematics/angular/third_party/github.com/Microsoft/TypeScript",
"@npm//@phenomnomnominal/tsquery",
"@npm//@types/node",
"@npm//rxjs",
"@npm//tslint",
],
)

Expand Down Expand Up @@ -83,12 +85,15 @@ ts_library(
deps = ALL_SCHEMA_DEPS + [
":angular",
"//packages/angular_devkit/core",
"//packages/angular_devkit/core:node_testing",
"//packages/angular_devkit/schematics",
"//packages/angular_devkit/schematics:testing",
"//packages/schematics/angular/third_party/github.com/Microsoft/TypeScript",
"@npm//@phenomnomnominal/tsquery",
"@npm//@types/node",
"@npm//@types/jasmine",
"@npm//rxjs",
"@npm//tslint",
],
testonly = True,
# @external_begin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
"version": "8.0.0-beta.12",
"factory": "./update-8",
"description": "Update an Angular CLI project to version 8."
},
"migration-08": {
"version": "8.0.0-beta.14",
"factory": "./update-8/#updateLazyModulePaths",
"description": "Update an Angular CLI project to version 8."
}
}
}
2 changes: 2 additions & 0 deletions packages/schematics/angular/migrations/update-8/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { updatePackageJson, updateTsLintConfig } from './codelyzer-5';
import { updateES5Projects } from './differential-loading';
import { dropES2015Polyfills } from './drop-es6-polyfills';

export { updateLazyModulePaths } from './update-lazy-module-paths';

export default function(): Rule {
return () => {
return chain([
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 { tsquery } from '@phenomnomnominal/tsquery';
import {
Replacement,
RuleFailure,
Rules,
} from 'tslint'; // tslint:disable-line:no-implicit-dependencies
import * as ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript';

// Constants:
const LOAD_CHILDREN_SPLIT = '#';
const NOT_CHILDREN_QUERY = `:not(:has(Identifier[name="children"]))`;
const HAS_LOAD_CHILDREN_QUERY = `:has(Identifier[name="loadChildren"])`;
const LAZY_VALUE_QUERY = `StringLiteral[value=/.*${LOAD_CHILDREN_SPLIT}.*/]`;
const LOAD_CHILDREN_ASSIGNMENT_QUERY =
`PropertyAssignment${NOT_CHILDREN_QUERY}${HAS_LOAD_CHILDREN_QUERY}:has(${LAZY_VALUE_QUERY})`;

const FAILURE_MESSAGE = 'Found magic `loadChildren` string. Use a function with `import` instead.';

export class Rule extends Rules.AbstractRule {
public apply (ast: ts.SourceFile): Array<RuleFailure> {
return tsquery(ast, LOAD_CHILDREN_ASSIGNMENT_QUERY).map(result => {
const [valueNode] = tsquery(result, LAZY_VALUE_QUERY);
let fix = this._promiseReplacement(valueNode.text);

// Try to fix indentation in replacement:
const { character } = ast.getLineAndCharacterOfPosition(result.getStart());
fix = fix.replace(/\n/g, `\n${' '.repeat(character)}`);

const replacement = new Replacement(valueNode.getStart(), valueNode.getWidth(), fix);
const start = result.getStart();
const end = result.getEnd();

return new RuleFailure(ast, start, end, FAILURE_MESSAGE, this.ruleName, replacement);
});
}

private _promiseReplacement (loadChildren: string): string {
const [path, moduleName] = this._getChunks(loadChildren);

return `() => import('${path}').then(m => m.${moduleName})`;
}

private _getChunks (loadChildren: string): Array<string> {
return loadChildren.split(LOAD_CHILDREN_SPLIT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {
Rule,
SchematicContext,
Tree,
} from '@angular-devkit/schematics';
import {
TslintFixTask,
} from '@angular-devkit/schematics/tasks';
import * as path from 'path';

export const updateLazyModulePaths = (): Rule => {
return (_: Tree, context: SchematicContext) => {
context.addTask(new TslintFixTask({
rulesDirectory: path.join(__dirname, 'rules'),
rules: {
'no-lazy-module-paths': [true],
},
}, {
includes: '**/*.ts',
silent: false,
}));
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 { getSystemPath, normalize, virtualFs } from '@angular-devkit/core';
import { TempScopedNodeJsSyncHost } from '@angular-devkit/core/node/testing';
import { HostTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';

describe('Migration to version 8', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);

let tree: UnitTestTree;
let host: TempScopedNodeJsSyncHost;

const lazyRoutePath = normalize('src/lazy-route.ts');
const lazyRoute = virtualFs.stringToFileBuffer(`
import { Route } from '@angular/router';
const routes: Array<Route> = [
{
path: '',
loadChildren: './lazy/lazy.module#LazyModule'
}
];
`);

const lazyChildRoute = virtualFs.stringToFileBuffer(`
import { Route } from '@angular/router';
const routes: Array<Route> = [
{
path: '',
children: [{
path: 'child',
loadChildren: './lazy/lazy.module#LazyModule'
}]
}
];
`);

describe('Migration to import() style lazy routes', () => {
beforeEach(async () => {
host = new TempScopedNodeJsSyncHost();
tree = new UnitTestTree(new HostTree(host));
tree.create('/package.json', JSON.stringify({}));
process.chdir(getSystemPath(host.root));
});

it('should replace the module path string', async () => {
await host.write(lazyRoutePath, lazyRoute).toPromise();

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

const routes = await host.read(lazyRoutePath)
.toPromise()
.then(virtualFs.fileBufferToString);

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

it('should replace the module path string in a child path', async () => {
await host.write(lazyRoutePath, lazyChildRoute).toPromise();

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

const routes = await host.read(lazyRoutePath)
.toPromise()
.then(virtualFs.fileBufferToString);

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

expect(routes).toContain(
`loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)`);
});
});
});
1 change: 1 addition & 0 deletions packages/schematics/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
],
"schematics": "./collection.json",
"dependencies": {
"@phenomnomnominal/tsquery": "3.0.0",
"@angular-devkit/core": "0.0.0",
"@angular-devkit/schematics": "0.0.0"
}
Expand Down
16 changes: 15 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@
resolved "https://registry.yarnpkg.com/@ngtools/json-schema/-/json-schema-1.1.0.tgz#c3a0c544d62392acc2813a42c8a0dc6f58f86922"
integrity sha1-w6DFRNYjkqzCgTpCyKDcb1j4aSI=

"@phenomnomnominal/[email protected]":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-3.0.0.tgz#6f2f4dbf6304ff52b12cc7a5b979f20c3794a22a"
integrity sha512-SW8lKitBHWJ9fAYkJ9kJivuctwNYCh3BUxLdH0+XiR1GPBiu+7qiZzh8p8jqlj1LgVC1TbvfNFroaEsmYlL8Iw==
dependencies:
esquery "^1.0.1"

"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
Expand Down Expand Up @@ -3341,6 +3348,13 @@ esprima@^4.0.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==

esquery@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==
dependencies:
estraverse "^4.0.0"

esrecurse@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
Expand All @@ -3353,7 +3367,7 @@ estraverse@^1.9.1:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=

estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
Expand Down

0 comments on commit 77f99b5

Please sign in to comment.