Skip to content

Commit 3b226be

Browse files
authored
First implementation of get lazy modules entries. (#1632)
Fixes #1636.
1 parent 9d69748 commit 3b226be

File tree

4 files changed

+127
-22
lines changed

4 files changed

+127
-22
lines changed

addon/ng2/models/find-lazy-modules.ts

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as fs from 'fs';
2+
import * as glob from 'glob';
3+
import * as path from 'path';
4+
import * as ts from 'typescript';
5+
6+
import {Observable} from 'rxjs/Observable';
7+
import {getSource, findNodes, getContentOfKeyLiteral} from '../utilities/ast-utils';
8+
9+
10+
const loadChildrenRegex = /(\{[^{}]+?(loadChildren|['"]loadChildren['"])\s*:\s*)('[^']+'|"[^"]+")/gm;
11+
12+
13+
interface Array<T> {
14+
flatMap: <R>(mapFn: (item: T) => Array<R>) => Array<R>;
15+
}
16+
Array.prototype.flatMap = function<T, R>(mapFn: (item: T) => Array<R>): Array<R> {
17+
if (!mapFn) {
18+
return [];
19+
}
20+
21+
return this.reduce((arr, current) => {
22+
const result = mapFn.call(null, current);
23+
return result !== undefined ? arr.concat(result) : arr;
24+
}, []);
25+
};
26+
27+
28+
export function findLoadChildren(tsFilePath: string): string[] {
29+
const source = getSource(tsFilePath);
30+
const unique = {};
31+
32+
return findNodes(source, ts.SyntaxKind.ObjectLiteralExpression)
33+
.flatMap(node => findNodes(node, ts.SyntaxKind.PropertyAssignment))
34+
.filter((node: ts.PropertyAssignment) => {
35+
const key = getContentOfKeyLiteral(source, node.name);
36+
if (!key) {
37+
// key is an expression, can't do anything.
38+
return false;
39+
}
40+
return key == 'loadChildren'
41+
})
42+
// Remove initializers that are not files.
43+
.filter((node: ts.PropertyAssignment) => {
44+
return node.initializer.kind === ts.SyntaxKind.StringLiteral;
45+
})
46+
// Get the full text of the initiliazer.
47+
.map((node: ts.PropertyAssignment) => {
48+
return eval(node.initializer.getText(source));
49+
})
50+
.flatMap((value: string) => unique[value] ? undefined : unique[value] = value)
51+
.map((moduleName: string) => moduleName.split('#')[0]);
52+
}
53+
54+
55+
export function findLazyModules(projectRoot: any): string[] {
56+
const allTs = glob.sync(path.join(projectRoot, '/**/*.ts'));
57+
const result = {};
58+
allTs
59+
.flatMap(tsPath => {
60+
findLoadChildren(tsPath).forEach(moduleName => {
61+
const fileName = path.resolve(path.dirname(tsPath), moduleName) + '.ts';
62+
if (fs.existsSync(fileName)) {
63+
result[moduleName] = fileName;
64+
}
65+
});
66+
});
67+
return result;
68+
}

addon/ng2/models/webpack-build-common.ts

+17-14
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,26 @@ import * as path from 'path';
22
import * as CopyWebpackPlugin from 'copy-webpack-plugin';
33
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
44
import * as webpack from 'webpack';
5-
import { ForkCheckerPlugin } from 'awesome-typescript-loader';
6-
import { CliConfig } from './config';
5+
import * as atl from 'awesome-typescript-loader';
6+
7+
import {findLazyModules} from './find-lazy-modules';
78

8-
export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, outputDir: string) {
99

10-
let outputPath: string = path.resolve(projectRoot, outputDir);
10+
export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, outputDir: string) {
11+
const sourceRoot = path.resolve(projectRoot, sourceDir);
12+
const outputPath = path.resolve(projectRoot, outputDir);
13+
const lazyModules = findLazyModules(path.resolve(projectRoot, sourceDir));
1114

1215
return {
1316
devtool: 'source-map',
1417
resolve: {
1518
extensions: ['', '.ts', '.js'],
16-
root: path.resolve(projectRoot, `./${sourceDir}`)
19+
root: sourceRoot
1720
},
1821
context: path.resolve(__dirname, './'),
1922
entry: {
20-
main: [path.resolve(projectRoot, `./${sourceDir}/main.ts`)],
21-
polyfills: path.resolve(projectRoot, `./${sourceDir}/polyfills.ts`)
23+
main: [path.join(sourceRoot, 'main.ts')],
24+
polyfills: path.join(sourceRoot, 'polyfills.ts')
2225
},
2326
output: {
2427
path: outputPath,
@@ -42,10 +45,9 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, o
4245
loader: 'awesome-typescript-loader',
4346
query: {
4447
useForkChecker: true,
45-
tsconfig: path.resolve(projectRoot, `./${sourceDir}/tsconfig.json`)
48+
tsconfig: path.resolve(sourceRoot, 'tsconfig.json')
4649
}
47-
},
48-
{
50+
}, {
4951
loader: 'angular2-template-loader'
5052
}
5153
],
@@ -61,9 +63,10 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, o
6163
]
6264
},
6365
plugins: [
64-
new ForkCheckerPlugin(),
66+
new webpack.ContextReplacementPlugin(/.*/, sourceRoot, lazyModules),
67+
new atl.ForkCheckerPlugin(),
6568
new HtmlWebpackPlugin({
66-
template: path.resolve(projectRoot, `./${sourceDir}/index.html`),
69+
template: path.resolve(sourceRoot, 'index.html'),
6770
chunksSortMode: 'dependency'
6871
}),
6972
new webpack.optimize.CommonsChunkPlugin({
@@ -79,7 +82,7 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, o
7982
context: path.resolve(projectRoot, './public'),
8083
from: '**/*',
8184
to: outputPath
82-
}])
85+
}]),
8386
],
8487
node: {
8588
fs: 'empty',
@@ -90,4 +93,4 @@ export function getWebpackCommonConfig(projectRoot: string, sourceDir: string, o
9093
setImmediate: false
9194
}
9295
}
93-
};
96+
}

addon/ng2/utilities/ast-utils.ts

+40-8
Original file line numberDiff line numberDiff line change
@@ -57,21 +57,37 @@ export function getSourceNodes(sourceFile: ts.SourceFile): Observable<ts.Node> {
5757

5858

5959
/**
60-
* Find all nodes from the AST in the subtree of node of SyntaxKind kind.
61-
* @param node
62-
* @param kind
63-
* @return all nodes of kind, or [] if none is found
60+
* Find all nodes from the AST in the subtree of node of SyntaxKind kind.
61+
* @param node
62+
* @param kind
63+
* @param max The maximum number of items to return.
64+
* @return all nodes of kind, or [] if none is found
6465
*/
65-
export function findNodes(node: ts.Node, kind: ts.SyntaxKind): ts.Node[] {
66-
if (!node) {
66+
export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max: number = Infinity): ts.Node[] {
67+
if (!node || max == 0) {
6768
return [];
6869
}
70+
6971
let arr: ts.Node[] = [];
7072
if (node.kind === kind) {
7173
arr.push(node);
74+
max--;
75+
}
76+
if (max > 0) {
77+
for (const child of node.getChildren()) {
78+
findNodes(child, kind, max).forEach(node => {
79+
if (max > 0) {
80+
arr.push(node);
81+
}
82+
max--;
83+
});
84+
85+
if (max <= 0) {
86+
break;
87+
}
88+
}
7289
}
73-
return node.getChildren().reduce((foundNodes, child) =>
74-
foundNodes.concat(findNodes(child, kind)), arr);
90+
return arr;
7591
}
7692

7793

@@ -111,9 +127,25 @@ export function insertAfterLastOccurrence(nodes: ts.Node[], toInsert: string,
111127
}
112128

113129

130+
export function getContentOfKeyLiteral(source: ts.SourceFile, node: ts.Node): string {
131+
if (node.kind == ts.SyntaxKind.Identifier) {
132+
return (<ts.Identifier>node).text;
133+
} else if (node.kind == ts.SyntaxKind.StringLiteral) {
134+
try {
135+
return JSON.parse(node.getFullText(source))
136+
} catch (e) {
137+
return null;
138+
}
139+
} else {
140+
return null;
141+
}
142+
}
143+
144+
114145
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
115146
module: string): Observable<ts.Node> {
116147
const symbols = new Symbols(source);
148+
117149
return getSourceNodes(source)
118150
.filter(node => {
119151
return node.kind == ts.SyntaxKind.Decorator

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
},
3333
"homepage": "https://github.com/angular/angular-cli",
3434
"dependencies": {
35+
"@angular/core": "^2.0.0-rc.5",
3536
"@angular/tsc-wrapped": "^0.2.2",
3637
"@types/lodash": "^4.0.25-alpha",
3738
"@types/rimraf": "0.0.25-alpha",
@@ -81,6 +82,7 @@
8182
"silent-error": "^1.0.0",
8283
"source-map-loader": "^0.1.5",
8384
"sourcemap-istanbul-instrumenter-loader": "^0.2.0",
85+
"string-replace-loader": "^1.0.3",
8486
"style-loader": "^0.13.1",
8587
"stylus": "^0.54.5",
8688
"stylus-loader": "^2.1.0",

0 commit comments

Comments
 (0)