From 8a5b2656ced81e84c96bac300b140179f473e2a2 Mon Sep 17 00:00:00 2001 From: Hans Larsen Date: Tue, 4 Oct 2016 18:19:31 -0700 Subject: [PATCH] feat(aot): adding README and type checking. Close #2527 --- .../models/webpack-build-typescript.ts | 10 +- packages/webpack/README.md | 39 +++ packages/webpack/src/loader.ts | 67 +++-- packages/webpack/src/plugin.ts | 272 ++++++++++++------ .../app/feature/lazy-feature.module.ts | 23 ++ .../webpack/test-app/app/lazy.module.ts | 3 +- .../assets/webpack/test-app/webpack.config.js | 8 +- tests/e2e_runner.js | 42 ++- 8 files changed, 311 insertions(+), 153 deletions(-) create mode 100644 packages/webpack/README.md create mode 100644 tests/e2e/assets/webpack/test-app/app/feature/lazy-feature.module.ts diff --git a/packages/angular-cli/models/webpack-build-typescript.ts b/packages/angular-cli/models/webpack-build-typescript.ts index b574f56bc9bc..0d9aaf810139 100644 --- a/packages/angular-cli/models/webpack-build-typescript.ts +++ b/packages/angular-cli/models/webpack-build-typescript.ts @@ -1,7 +1,7 @@ import * as path from 'path'; import * as webpack from 'webpack'; import {findLazyModules} from './find-lazy-modules'; -import {NgcWebpackPlugin} from '@ngtools/webpack'; +import {AotPlugin} from '@ngtools/webpack'; const atl = require('awesome-typescript-loader'); @@ -59,11 +59,9 @@ export const getWebpackAotConfigPartial = function(projectRoot: string, appConfi ] }, plugins: [ - new NgcWebpackPlugin({ - project: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig), - baseDir: path.resolve(projectRoot, appConfig.root), - main: path.join(projectRoot, appConfig.root, appConfig.main), - genDir: path.resolve(projectRoot, appConfig.root) + new AotPlugin({ + tsConfigPath: path.resolve(projectRoot, appConfig.root, appConfig.tsconfig), + mainPath: path.join(projectRoot, appConfig.root, appConfig.main) }), ] }; diff --git a/packages/webpack/README.md b/packages/webpack/README.md new file mode 100644 index 000000000000..e04251f4f7bc --- /dev/null +++ b/packages/webpack/README.md @@ -0,0 +1,39 @@ +# Angular Ahead-of-Time Webpack Plugin + +Webpack plugin that AoT compiles your Angular components and modules. + +## Usage +In your webpack config, add the following plugin and loader: + +```typescript +import {AotPlugin} from '@ngtools/webpack' + +exports = { /* ... */ + module: { + rules: [ + { + test: /\.ts$/, + loader: '@ngtools/webpack', + } + ] + }, + + plugins: [ + new AotPlugin({ + tsConfigPath: 'path/to/tsconfig.json', + entryModule: 'path/to/app.module#AppModule' + }) + ] +} +``` + +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. + +## Options + +* `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`. +* `basePath`. Optional. The root to use by the compiler to resolve file paths. By default, use the `tsConfigPath` root. +* `entryModule`. Optional if specified in `angularCompilerOptions`. The path and classname of the main application module. This follows the format `path/to/file#ClassName`. +* `mainPath`. Optional if `entryModule` is specified. The `main.ts` file containing the bootstrap code. The plugin will use AST to determine the `entryModule`. +* `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`. +* `typeChecking`. Optional, defaults to true. Enable type checking through your application. This will slow down compilation, but show syntactic and semantic errors in webpack. \ No newline at end of file diff --git a/packages/webpack/src/loader.ts b/packages/webpack/src/loader.ts index ba162a5d18fd..2a85fbfb7beb 100644 --- a/packages/webpack/src/loader.ts +++ b/packages/webpack/src/loader.ts @@ -1,6 +1,6 @@ import * as path from 'path'; import * as ts from 'typescript'; -import {NgcWebpackPlugin} from './plugin'; +import {AotPlugin} from './plugin'; import {MultiChange, ReplaceChange, insertImport} from '@angular-cli/ast-tools'; // TODO: move all this to ast-tools. @@ -31,7 +31,7 @@ function _removeDecorators(fileName: string, source: string): string { function _replaceBootstrap(fileName: string, source: string, - plugin: NgcWebpackPlugin): Promise { + plugin: AotPlugin): Promise { // If bootstrapModule can't be found, bail out early. if (!source.match(/\bbootstrapModule\b/)) { return Promise.resolve(source); @@ -40,11 +40,11 @@ function _replaceBootstrap(fileName: string, let changes = new MultiChange(); // Calculate the base path. - const basePath = path.normalize(plugin.angularCompilerOptions.basePath); + const basePath = path.normalize(plugin.basePath); const genDir = path.normalize(plugin.genDir); const dirName = path.normalize(path.dirname(fileName)); - const [entryModulePath, entryModuleName] = plugin.entryModule.split('#'); - const entryModuleFileName = path.normalize(entryModulePath + '.ngfactory'); + const entryModule = plugin.entryModule; + const entryModuleFileName = path.normalize(entryModule.path + '.ngfactory'); const relativeEntryModulePath = path.relative(basePath, entryModuleFileName); const fullEntryModulePath = path.resolve(genDir, relativeEntryModulePath); const relativeNgFactoryPath = path.relative(dirName, fullEntryModulePath); @@ -82,7 +82,7 @@ function _replaceBootstrap(fileName: string, .filter(call => bootstraps.some(bs => bs == call.expression)) .forEach((call: ts.CallExpression) => { changes.appendChange(new ReplaceChange(fileName, call.arguments[0].getStart(sourceFile), - entryModuleName, entryModuleName + 'NgFactory')); + entryModule.className, entryModule.className + 'NgFactory')); }); calls @@ -98,7 +98,7 @@ function _replaceBootstrap(fileName: string, 'bootstrapModule', 'bootstrapModuleFactory')); }); changes.appendChange(insertImport(fileName, 'platformBrowser', '@angular/platform-browser')); - changes.appendChange(insertImport(fileName, entryModuleName + 'NgFactory', ngFactoryPath)); + changes.appendChange(insertImport(fileName, entryModule.className + 'NgFactory', ngFactoryPath)); let sourceText = source; return changes.apply({ @@ -107,35 +107,52 @@ function _replaceBootstrap(fileName: string, }).then(() => sourceText); } +function _transpile(plugin: AotPlugin, filePath: string, sourceText: string) { + const program = plugin.program; + if (plugin.typeCheck) { + const sourceFile = program.getSourceFile(filePath); + const diagnostics = program.getSyntacticDiagnostics(sourceFile) + .concat(program.getSemanticDiagnostics(sourceFile)) + .concat(program.getDeclarationDiagnostics(sourceFile)); + + if (diagnostics.length > 0) { + const message = diagnostics + .map(diagnostic => { + const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + return `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message})`; + }) + .join('\n'); + throw new Error(message); + } + } + + const result = ts.transpileModule(sourceText, { + compilerOptions: plugin.compilerOptions, + fileName: filePath + }); + + return { + outputText: result.outputText, + sourceMap: JSON.parse(result.sourceMapText) + }; +} // Super simple TS transpiler loader for testing / isolated usage. does not type check! export function ngcLoader(source: string) { this.cacheable(); - const plugin = this._compilation._ngToolsWebpackPluginInstance as NgcWebpackPlugin; - if (plugin && plugin instanceof NgcWebpackPlugin) { + const plugin = this._compilation._ngToolsWebpackPluginInstance as AotPlugin; + // We must verify that AotPlugin is an instance of the right class. + if (plugin && plugin instanceof AotPlugin) { const cb: any = this.async(); plugin.done .then(() => _removeDecorators(this.resource, source)) .then(sourceText => _replaceBootstrap(this.resource, sourceText, plugin)) .then(sourceText => { - const result = ts.transpileModule(sourceText, { - compilerOptions: { - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.ES2015, - } - }); - - if (result.diagnostics && result.diagnostics.length) { - let message = ''; - result.diagnostics.forEach(d => { - message += d.messageText + '\n'; - }); - cb(new Error(message)); - } - - cb(null, result.outputText, result.sourceMapText ? JSON.parse(result.sourceMapText) : null); + const result = _transpile(plugin, this.resource, sourceText); + cb(null, result.outputText, result.sourceMap); }) .catch(err => cb(err)); } else { diff --git a/packages/webpack/src/plugin.ts b/packages/webpack/src/plugin.ts index 1877d11b06d4..f01ae0fe7952 100644 --- a/packages/webpack/src/plugin.ts +++ b/packages/webpack/src/plugin.ts @@ -1,5 +1,6 @@ -import * as ts from 'typescript'; +import * as fs from 'fs'; import * as path from 'path'; +import * as ts from 'typescript'; import {NgModule} from '@angular/core'; import * as ngCompiler from '@angular/compiler-cli'; @@ -8,7 +9,6 @@ import {tsc} from '@angular/tsc-wrapped/src/tsc'; import {patchReflectorHost} from './reflector_host'; import {WebpackResourceLoader} from './resource_loader'; import {createResolveDependenciesFromContextMap} from './utils'; -import { AngularCompilerOptions } from '@angular/tsc-wrapped'; import {WebpackCompilerHost} from './compiler_host'; import {resolveEntryModuleFromMain} from './entry_resolver'; @@ -16,73 +16,122 @@ import {resolveEntryModuleFromMain} from './entry_resolver'; /** * Option Constants */ -export interface AngularWebpackPluginOptions { - tsconfigPath?: string; - providers?: any[]; - entryModule?: string; - project: string; - baseDir: string; +export interface AotPluginOptions { + tsConfigPath: string; basePath?: string; + entryModule?: string; genDir?: string; - main?: string; + mainPath?: string; + typeChecking?: boolean; +} + + +export class ModuleRoute { + constructor(public readonly path: string, public readonly className: string = null) {} + + toString() { + return `${this.path}#${this.className}`; + } + + static fromString(entry: string): ModuleRoute { + const split = entry.split('#'); + return new ModuleRoute(split[0], split[1]); + } } -export class NgcWebpackPlugin { - projectPath: string; - rootModule: string; - rootModuleName: string; - reflector: ngCompiler.StaticReflector; - reflectorHost: ngCompiler.ReflectorHost; - program: ts.Program; - compilerHost: WebpackCompilerHost; - compilerOptions: ts.CompilerOptions; - angularCompilerOptions: AngularCompilerOptions; - files: any[]; - lazyRoutes: any; - loader: any; - genDir: string; - entryModule: string; - - done: Promise; - - nmf: any = null; - cmf: any = null; - compiler: any = null; - compilation: any = null; - - constructor(public options: AngularWebpackPluginOptions) { - const tsConfig = tsc.readConfiguration(options.project, options.baseDir); - this.compilerOptions = tsConfig.parsed.options; - this.files = tsConfig.parsed.fileNames; - this.angularCompilerOptions = Object.assign({}, tsConfig.ngOptions, options); - - this.angularCompilerOptions.basePath = options.baseDir || process.cwd(); - this.genDir = this.options.genDir - || path.resolve(process.cwd(), this.angularCompilerOptions.genDir + '/app'); - this.entryModule = options.entryModule || (this.angularCompilerOptions as any).entryModule; - if (!options.entryModule && options.main) { - this.entryModule = resolveEntryModuleFromMain(options.main); +export class AotPlugin { + private _entryModule: ModuleRoute; + private _compilerOptions: ts.CompilerOptions; + private _angularCompilerOptions: ngCompiler.AngularCompilerOptions; + private _program: ts.Program; + private _reflector: ngCompiler.StaticReflector; + private _reflectorHost: ngCompiler.ReflectorHost; + private _rootFilePath: string[]; + private _compilerHost: WebpackCompilerHost; + private _resourceLoader: WebpackResourceLoader; + private _lazyRoutes: { [route: string]: string }; + + private _donePromise: Promise; + private _compiler: any = null; + private _compilation: any = null; + + private _typeCheck: boolean = true; + + + constructor(options: AotPluginOptions) { + this._setupOptions(options); + } + + get basePath() { return this._angularCompilerOptions.basePath; } + get compilation() { return this._compilation; } + get compilerOptions() { return this._compilerOptions; } + get done() { return this._donePromise; } + get entryModule() { return this._entryModule; } + get genDir() { return this._angularCompilerOptions.genDir; } + get program() { return this._program; } + get typeCheck() { return this._typeCheck; } + + private _setupOptions(options: AotPluginOptions) { + // Fill in the missing options. + if (!options.hasOwnProperty('tsConfigPath')) { + throw new Error('Must specify "tsConfigPath" in the configuration of @ngtools/webpack.'); +} + + // Check the base path. + let basePath = path.resolve(process.cwd(), path.dirname(options.tsConfigPath)); + if (fs.statSync(options.tsConfigPath).isDirectory()) { + basePath = options.tsConfigPath; + } + if (options.hasOwnProperty('basePath')) { + basePath = options.basePath; + } + + const tsConfig = tsc.readConfiguration(options.tsConfigPath, basePath); + this._rootFilePath = tsConfig.parsed.fileNames; + + // Check the genDir. + let genDir = basePath; + if (options.hasOwnProperty('genDir')) { + genDir = options.genDir; + } else if (tsConfig.ngOptions.hasOwnProperty('genDir')) { + genDir = tsConfig.ngOptions.genDir; } - const entryModule = this.entryModule; - const [rootModule, rootNgModule] = entryModule.split('#'); - this.projectPath = options.project; - this.rootModule = rootModule; - this.rootModuleName = rootNgModule; - this.compilerHost = new WebpackCompilerHost(this.compilerOptions); - this.program = ts.createProgram(this.files, this.compilerOptions, this.compilerHost); - this.reflectorHost = new ngCompiler.ReflectorHost( - this.program, this.compilerHost, this.angularCompilerOptions); - this.reflector = new ngCompiler.StaticReflector(this.reflectorHost); + this._compilerOptions = tsConfig.parsed.options; + + if (options.entryModule) { + this._entryModule = ModuleRoute.fromString(options.entryModule); + } else { + if (options.mainPath) { + this._entryModule = ModuleRoute.fromString(resolveEntryModuleFromMain(options.mainPath)); + } else { + this._entryModule = ModuleRoute.fromString((tsConfig.ngOptions as any).entryModule); + } + } + this._angularCompilerOptions = Object.assign({}, tsConfig.ngOptions, { + basePath, + entryModule: this._entryModule.toString(), + genDir + }); + + if (options.hasOwnProperty('typeChecking')) { + this._typeCheck = options.typeChecking; + } + + this._compilerHost = new WebpackCompilerHost(this._compilerOptions); + this._program = ts.createProgram( + this._rootFilePath, this._compilerOptions, this._compilerHost); + this._reflectorHost = new ngCompiler.ReflectorHost( + this._program, this._compilerHost, this._angularCompilerOptions); + this._reflector = new ngCompiler.StaticReflector(this._reflectorHost); } // registration hook for webpack plugin apply(compiler: any) { - this.compiler = compiler; - compiler.plugin('normal-module-factory', (nmf: any) => this.nmf = nmf); + this._compiler = compiler; + compiler.plugin('context-module-factory', (cmf: any) => { - this.cmf = cmf; cmf.plugin('before-resolve', (request: any, callback: (err?: any, request?: any) => void) => { if (!request) { return callback(); @@ -103,7 +152,7 @@ export class NgcWebpackPlugin { result.recursive = true; result.dependencies.forEach((d: any) => d.critical = false); result.resolveDependencies = createResolveDependenciesFromContextMap( - (_: any, cb: any) => cb(null, this.lazyRoutes)); + (_: any, cb: any) => cb(null, this._lazyRoutes)); return callback(null, result); }); @@ -112,8 +161,8 @@ export class NgcWebpackPlugin { compiler.plugin('make', (compilation: any, cb: any) => this._make(compilation, cb)); compiler.plugin('after-emit', (compilation: any, cb: any) => { - this.done = null; - this.compilation = null; + this._donePromise = null; + this._compilation = null; compilation._ngToolsWebpackPluginInstance = null; cb(); }); @@ -121,7 +170,7 @@ export class NgcWebpackPlugin { // Virtual file system. compiler.resolvers.normal.plugin('resolve', (request: any, cb?: () => void) => { // Populate the file system cache with the virtual module. - this.compilerHost.populateWebpackResolver(compiler.resolvers.normal); + this._compilerHost.populateWebpackResolver(compiler.resolvers.normal); if (cb) { cb(); } @@ -129,68 +178,103 @@ export class NgcWebpackPlugin { } private _make(compilation: any, cb: (err?: any, request?: any) => void) { - const rootModulePath = path.normalize(this.rootModule + '.ts'); - const rootModuleName = this.rootModuleName; - this.compilation = compilation; + this._compilation = compilation; - if (this.compilation._ngToolsWebpackPluginInstance) { - cb(new Error('A ngtools/webpack plugin already exist for this compilation.')); + if (this._compilation._ngToolsWebpackPluginInstance) { + cb(new Error('An @ngtools/webpack plugin already exist for this compilation.')); } - this.compilation._ngToolsWebpackPluginInstance = this; + this._compilation._ngToolsWebpackPluginInstance = this; - this.loader = new WebpackResourceLoader(compilation); + this._resourceLoader = new WebpackResourceLoader(compilation); - const i18nOptions: any = { + const i18nOptions: ngCompiler.NgcCliOptions = { i18nFile: undefined, i18nFormat: undefined, locale: undefined, - basePath: this.options.baseDir + basePath: this.basePath }; // Create the Code Generator. const codeGenerator = ngCompiler.CodeGenerator.create( - this.angularCompilerOptions, + this._angularCompilerOptions, i18nOptions, - this.program, - this.compilerHost, - new ngCompiler.NodeReflectorHostContext(this.compilerHost), - this.loader + this._program, + this._compilerHost, + new ngCompiler.NodeReflectorHostContext(this._compilerHost), + this._resourceLoader ); // We need to temporarily patch the CodeGenerator until either it's patched or allows us // to pass in our own ReflectorHost. patchReflectorHost(codeGenerator); - this.done = codeGenerator.codegen() + this._donePromise = codeGenerator.codegen() + .then(() => { + // Create a new Program, based on the old one. This will trigger a resolution of all + // transitive modules, which include files that might just have been generated. + this._program = ts.createProgram( + this._rootFilePath, this._compilerOptions, this._compilerHost, this._program); + + const diagnostics = this._program.getGlobalDiagnostics(); + if (diagnostics.length > 0) { + const message = diagnostics + .map(diagnostic => { + const {line, character} = diagnostic.file.getLineAndCharacterOfPosition( + diagnostic.start); + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + return `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message})`; + }) + .join('\n'); + + throw new Error(message); + } + }) .then(() => { - // process the lazy routes - const lazyModules = this._processNgModule(rootModulePath, rootModuleName, rootModulePath) - .map(moduleKey => moduleKey.split('#')[0]); - this.lazyRoutes = lazyModules.reduce((lazyRoutes: any, lazyModule: any) => { - const genDir = this.genDir; - lazyRoutes[`${lazyModule}.ngfactory`] = path.join(genDir, lazyModule + '.ngfactory.ts'); + // Process the lazy routes + this._lazyRoutes = + this._processNgModule(this._entryModule, null) + .map(module => ModuleRoute.fromString(module)) + .reduce((lazyRoutes: any, module: ModuleRoute) => { + lazyRoutes[`${module.path}.ngfactory`] = path.join( + this.genDir, module.path + '.ngfactory.ts'); return lazyRoutes; }, {}); }) .then(() => cb(), (err) => cb(err)); } - private _processNgModule(mod: string, ngModuleName: string, containingFile: string): string[] { - const staticSymbol = this.reflectorHost.findDeclaration(mod, ngModuleName, containingFile); - const entryNgModuleMetadata = this.getNgModuleMetadata(staticSymbol); - const loadChildren = this.extractLoadChildren(entryNgModuleMetadata); + private _resolveModule(module: ModuleRoute, containingFile: string) { + if (module.path.startsWith('.')) { + return path.join(path.dirname(containingFile), module.path); + } + return module.path; + } - return loadChildren.reduce((res, lc) => { - const [childModule, childNgModule] = lc.split('#'); + private _processNgModule(module: ModuleRoute, containingFile: string | null): string[] { + const modulePath = containingFile ? module.path : ('./' + path.basename(module.path)); + if (containingFile === null) { + containingFile = module.path + '.ts'; + } - // TODO calculate a different containingFile for relative paths + const resolvedModulePath = this._resolveModule(module, containingFile); + const staticSymbol = this._reflectorHost + .findDeclaration(modulePath, module.className, containingFile); + const entryNgModuleMetadata = this.getNgModuleMetadata(staticSymbol); + const loadChildren = this.extractLoadChildren(entryNgModuleMetadata); + const result = loadChildren.map(route => { + return this._resolveModule(new ModuleRoute(route), resolvedModulePath); + }); - const children = this._processNgModule(childModule, childNgModule, containingFile); - return res.concat(children); - }, loadChildren); + // Also concatenate every child of child modules. + for (const route of loadChildren) { + const childModule = ModuleRoute.fromString(route); + const children = this._processNgModule(childModule, resolvedModulePath + '.ts'); + result.push(...children); + } + return result; } private getNgModuleMetadata(staticSymbol: ngCompiler.StaticSymbol) { - const ngModules = this.reflector.annotations(staticSymbol).filter(s => s instanceof NgModule); + const ngModules = this._reflector.annotations(staticSymbol).filter(s => s instanceof NgModule); if (ngModules.length === 0) { throw new Error(`${staticSymbol.name} is not an NgModule`); } @@ -208,7 +292,7 @@ export class NgcWebpackPlugin { if (!providers) { return []; } - const ROUTES = this.reflectorHost.findDeclaration( + const ROUTES = this._reflectorHost.findDeclaration( '@angular/router/src/router_config_loader', 'ROUTES', undefined); return providers.reduce((m, p) => { diff --git a/tests/e2e/assets/webpack/test-app/app/feature/lazy-feature.module.ts b/tests/e2e/assets/webpack/test-app/app/feature/lazy-feature.module.ts new file mode 100644 index 000000000000..8fafca158b24 --- /dev/null +++ b/tests/e2e/assets/webpack/test-app/app/feature/lazy-feature.module.ts @@ -0,0 +1,23 @@ +import {NgModule, Component} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {HttpModule, Http} from '@angular/http'; + +@Component({ + selector: 'lazy-feature-comp', + template: 'lazy feature!' +}) +export class LazyFeatureComponent {} + +@NgModule({ + imports: [ + RouterModule.forChild([ + {path: '', component: LazyFeatureComponent, pathMatch: 'full'}, + {path: 'feature', loadChildren: './feature.module#FeatureModule'} + ]), + HttpModule + ], + declarations: [LazyFeatureComponent] +}) +export class LazyFeatureModule { + constructor(http: Http) {} +} diff --git a/tests/e2e/assets/webpack/test-app/app/lazy.module.ts b/tests/e2e/assets/webpack/test-app/app/lazy.module.ts index 29dab5d11d35..96da4de7515b 100644 --- a/tests/e2e/assets/webpack/test-app/app/lazy.module.ts +++ b/tests/e2e/assets/webpack/test-app/app/lazy.module.ts @@ -12,7 +12,8 @@ export class LazyComponent {} imports: [ RouterModule.forChild([ {path: '', component: LazyComponent, pathMatch: 'full'}, - {path: 'feature', loadChildren: './feature/feature.module#FeatureModule'} + {path: 'feature', loadChildren: './feature/feature.module#FeatureModule'}, + {path: 'lazy-feature', loadChildren: './feature/lazy-feature.module#LazyFeatureModule'} ]), HttpModule ], diff --git a/tests/e2e/assets/webpack/test-app/webpack.config.js b/tests/e2e/assets/webpack/test-app/webpack.config.js index cfb6ea75f284..9f4cd36678c6 100644 --- a/tests/e2e/assets/webpack/test-app/webpack.config.js +++ b/tests/e2e/assets/webpack/test-app/webpack.config.js @@ -1,5 +1,4 @@ -const NgcWebpackPlugin = require('@ngtools/webpack').NgcWebpackPlugin; -const path = require('path'); +const ngToolsWebpack = require('@ngtools/webpack'); module.exports = { resolve: { @@ -12,9 +11,8 @@ module.exports = { filename: 'app.main.js' }, plugins: [ - new NgcWebpackPlugin({ - project: './tsconfig.json', - baseDir: path.resolve(__dirname, '') + new ngToolsWebpack.AotPlugin({ + tsConfigPath: './tsconfig.json' }) ], module: { diff --git a/tests/e2e_runner.js b/tests/e2e_runner.js index bc96d6f0f535..cf3523b67884 100644 --- a/tests/e2e_runner.js +++ b/tests/e2e_runner.js @@ -108,30 +108,28 @@ testsToRun.reduce((previous, relativeName) => { }); }); }, Promise.resolve()) -.then( - () => { - console.log(green('Done.')); - process.exit(0); - }, - (err) => { - console.log('\n'); - console.error(red(`Test "${currentFileName}" failed...`)); - console.error(red(err.message)); - console.error(red(err.stack)); - - if (argv.debug) { - console.log(`Current Directory: ${process.cwd()}`); - console.log('Will loop forever while you debug... CTRL-C to quit.'); - - /* eslint-disable no-constant-condition */ - while (1) { - // That's right! - } +.then(() => { + console.log(green('Done.')); + process.exit(0); +}, +(err) => { + console.log('\n'); + console.error(red(`Test "${currentFileName}" failed...`)); + console.error(red(err.message)); + console.error(red(err.stack)); + + if (argv.debug) { + console.log(`Current Directory: ${process.cwd()}`); + console.log('Will loop forever while you debug... CTRL-C to quit.'); + + /* eslint-disable no-constant-condition */ + while (1) { + // That's right! } - - process.exit(1); } -); + + process.exit(1); +}); function encode(str) {