Skip to content

Commit 8a5b265

Browse files
hanslfilipesilva
authored andcommitted
feat(aot): adding README and type checking.
Close #2527
1 parent afb36e8 commit 8a5b265

File tree

8 files changed

+311
-153
lines changed

8 files changed

+311
-153
lines changed

packages/angular-cli/models/webpack-build-typescript.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as path from 'path';
22
import * as webpack from 'webpack';
33
import {findLazyModules} from './find-lazy-modules';
4-
import {NgcWebpackPlugin} from '@ngtools/webpack';
4+
import {AotPlugin} from '@ngtools/webpack';
55

66
const atl = require('awesome-typescript-loader');
77

@@ -59,11 +59,9 @@ export const getWebpackAotConfigPartial = function(projectRoot: string, appConfi
5959
]
6060
},
6161
plugins: [
62-
new NgcWebpackPlugin({
63-
project: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
64-
baseDir: path.resolve(projectRoot, appConfig.root),
65-
main: path.join(projectRoot, appConfig.root, appConfig.main),
66-
genDir: path.resolve(projectRoot, appConfig.root)
62+
new AotPlugin({
63+
tsConfigPath: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig),
64+
mainPath: path.join(projectRoot, appConfig.root, appConfig.main)
6765
}),
6866
]
6967
};

packages/webpack/README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Angular Ahead-of-Time Webpack Plugin
2+
3+
Webpack plugin that AoT compiles your Angular components and modules.
4+
5+
## Usage
6+
In your webpack config, add the following plugin and loader:
7+
8+
```typescript
9+
import {AotPlugin} from '@ngtools/webpack'
10+
11+
exports = { /* ... */
12+
module: {
13+
rules: [
14+
{
15+
test: /\.ts$/,
16+
loader: '@ngtools/webpack',
17+
}
18+
]
19+
},
20+
21+
plugins: [
22+
new AotPlugin({
23+
tsConfigPath: 'path/to/tsconfig.json',
24+
entryModule: 'path/to/app.module#AppModule'
25+
})
26+
]
27+
}
28+
```
29+
30+
The loader works with the webpack plugin to compile your TypeScript. It's important to include both, and to not include any other TypeScript compiler loader.
31+
32+
## Options
33+
34+
* `tsConfigPath`. The path to the `tsconfig.json` file. This is required. In your `tsconfig.json`, you can pass options to the Angular Compiler with `angularCompilerOptions`.
35+
* `basePath`. Optional. The root to use by the compiler to resolve file paths. By default, use the `tsConfigPath` root.
36+
* `entryModule`. Optional if specified in `angularCompilerOptions`. The path and classname of the main application module. This follows the format `path/to/file#ClassName`.
37+
* `mainPath`. Optional if `entryModule` is specified. The `main.ts` file containing the bootstrap code. The plugin will use AST to determine the `entryModule`.
38+
* `genDir`. Optional. The output directory of the offline compiler. The files created by the offline compiler will be in a virtual file system, but the import paths might change. This can also be specified in `angularCompilerOptions`, and by default will be the same as `basePath`.
39+
* `typeChecking`. Optional, defaults to true. Enable type checking through your application. This will slow down compilation, but show syntactic and semantic errors in webpack.

packages/webpack/src/loader.ts

+42-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path';
22
import * as ts from 'typescript';
3-
import {NgcWebpackPlugin} from './plugin';
3+
import {AotPlugin} from './plugin';
44
import {MultiChange, ReplaceChange, insertImport} from '@angular-cli/ast-tools';
55

66
// TODO: move all this to ast-tools.
@@ -31,7 +31,7 @@ function _removeDecorators(fileName: string, source: string): string {
3131

3232
function _replaceBootstrap(fileName: string,
3333
source: string,
34-
plugin: NgcWebpackPlugin): Promise<string> {
34+
plugin: AotPlugin): Promise<string> {
3535
// If bootstrapModule can't be found, bail out early.
3636
if (!source.match(/\bbootstrapModule\b/)) {
3737
return Promise.resolve(source);
@@ -40,11 +40,11 @@ function _replaceBootstrap(fileName: string,
4040
let changes = new MultiChange();
4141

4242
// Calculate the base path.
43-
const basePath = path.normalize(plugin.angularCompilerOptions.basePath);
43+
const basePath = path.normalize(plugin.basePath);
4444
const genDir = path.normalize(plugin.genDir);
4545
const dirName = path.normalize(path.dirname(fileName));
46-
const [entryModulePath, entryModuleName] = plugin.entryModule.split('#');
47-
const entryModuleFileName = path.normalize(entryModulePath + '.ngfactory');
46+
const entryModule = plugin.entryModule;
47+
const entryModuleFileName = path.normalize(entryModule.path + '.ngfactory');
4848
const relativeEntryModulePath = path.relative(basePath, entryModuleFileName);
4949
const fullEntryModulePath = path.resolve(genDir, relativeEntryModulePath);
5050
const relativeNgFactoryPath = path.relative(dirName, fullEntryModulePath);
@@ -82,7 +82,7 @@ function _replaceBootstrap(fileName: string,
8282
.filter(call => bootstraps.some(bs => bs == call.expression))
8383
.forEach((call: ts.CallExpression) => {
8484
changes.appendChange(new ReplaceChange(fileName, call.arguments[0].getStart(sourceFile),
85-
entryModuleName, entryModuleName + 'NgFactory'));
85+
entryModule.className, entryModule.className + 'NgFactory'));
8686
});
8787

8888
calls
@@ -98,7 +98,7 @@ function _replaceBootstrap(fileName: string,
9898
'bootstrapModule', 'bootstrapModuleFactory'));
9999
});
100100
changes.appendChange(insertImport(fileName, 'platformBrowser', '@angular/platform-browser'));
101-
changes.appendChange(insertImport(fileName, entryModuleName + 'NgFactory', ngFactoryPath));
101+
changes.appendChange(insertImport(fileName, entryModule.className + 'NgFactory', ngFactoryPath));
102102

103103
let sourceText = source;
104104
return changes.apply({
@@ -107,35 +107,52 @@ function _replaceBootstrap(fileName: string,
107107
}).then(() => sourceText);
108108
}
109109

110+
function _transpile(plugin: AotPlugin, filePath: string, sourceText: string) {
111+
const program = plugin.program;
112+
if (plugin.typeCheck) {
113+
const sourceFile = program.getSourceFile(filePath);
114+
const diagnostics = program.getSyntacticDiagnostics(sourceFile)
115+
.concat(program.getSemanticDiagnostics(sourceFile))
116+
.concat(program.getDeclarationDiagnostics(sourceFile));
117+
118+
if (diagnostics.length > 0) {
119+
const message = diagnostics
120+
.map(diagnostic => {
121+
const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
122+
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
123+
return `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message})`;
124+
})
125+
.join('\n');
126+
throw new Error(message);
127+
}
128+
}
129+
130+
const result = ts.transpileModule(sourceText, {
131+
compilerOptions: plugin.compilerOptions,
132+
fileName: filePath
133+
});
134+
135+
return {
136+
outputText: result.outputText,
137+
sourceMap: JSON.parse(result.sourceMapText)
138+
};
139+
}
110140

111141
// Super simple TS transpiler loader for testing / isolated usage. does not type check!
112142
export function ngcLoader(source: string) {
113143
this.cacheable();
114144

115-
const plugin = this._compilation._ngToolsWebpackPluginInstance as NgcWebpackPlugin;
116-
if (plugin && plugin instanceof NgcWebpackPlugin) {
145+
const plugin = this._compilation._ngToolsWebpackPluginInstance as AotPlugin;
146+
// We must verify that AotPlugin is an instance of the right class.
147+
if (plugin && plugin instanceof AotPlugin) {
117148
const cb: any = this.async();
118149

119150
plugin.done
120151
.then(() => _removeDecorators(this.resource, source))
121152
.then(sourceText => _replaceBootstrap(this.resource, sourceText, plugin))
122153
.then(sourceText => {
123-
const result = ts.transpileModule(sourceText, {
124-
compilerOptions: {
125-
target: ts.ScriptTarget.ES5,
126-
module: ts.ModuleKind.ES2015,
127-
}
128-
});
129-
130-
if (result.diagnostics && result.diagnostics.length) {
131-
let message = '';
132-
result.diagnostics.forEach(d => {
133-
message += d.messageText + '\n';
134-
});
135-
cb(new Error(message));
136-
}
137-
138-
cb(null, result.outputText, result.sourceMapText ? JSON.parse(result.sourceMapText) : null);
154+
const result = _transpile(plugin, this.resource, sourceText);
155+
cb(null, result.outputText, result.sourceMap);
139156
})
140157
.catch(err => cb(err));
141158
} else {

0 commit comments

Comments
 (0)