Skip to content

Commit

Permalink
fix(@ngtools/webpack): remove compiler workarounds
Browse files Browse the repository at this point in the history
These issues were fixed in Angular 5.0.0-rc.7.

Fix #8228
  • Loading branch information
filipesilva committed Oct 28, 2017
1 parent d2e22b2 commit 6570983
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 140 deletions.
153 changes: 61 additions & 92 deletions packages/@ngtools/webpack/src/angular_compiler_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
createProgram,
createCompilerHost,
formatDiagnostics,
readConfiguration,
} from './ngtools_api';
import { findAstNodes } from './transformers/ast_helpers';

Expand Down Expand Up @@ -86,13 +87,11 @@ export class AngularCompilerPlugin implements Tapable {
private _options: AngularCompilerPluginOptions;

// TS compilation.
private _compilerOptions: ts.CompilerOptions;
private _angularCompilerOptions: CompilerOptions;
private _tsFilenames: string[];
private _compilerOptions: CompilerOptions;
private _rootNames: string[];
private _program: (ts.Program | Program);
private _compilerHost: WebpackCompilerHost;
private _compilerHost: WebpackCompilerHost & CompilerHost;
private _moduleResolutionCache: ts.ModuleResolutionCache;
private _angularCompilerHost: WebpackCompilerHost & CompilerHost;
private _resourceLoader: WebpackResourceLoader;
// Contains `moduleImportPath#exportName` => `fullModulePath`.
private _lazyRoutes: LazyRouteMap = Object.create(null);
Expand Down Expand Up @@ -214,11 +213,10 @@ export class AngularCompilerPlugin implements Tapable {
}

// Parse the tsconfig contents.
const tsConfig = ts.parseJsonConfigFileContent(
tsConfigJson, ts.sys, basePath, undefined, this._tsConfigPath);
const config = readConfiguration(this._tsConfigPath, tsConfigJson);

this._tsFilenames = tsConfig.fileNames;
this._compilerOptions = tsConfig.options;
this._rootNames = config.rootNames;
this._compilerOptions = config.options;

// Overwrite outDir so we can find generated files next to their .ts origin in compilerHost.
this._compilerOptions.outDir = '';
Expand Down Expand Up @@ -250,55 +248,54 @@ export class AngularCompilerPlugin implements Tapable {
// to the webpack dependency tree and rebuilds triggered by file edits.
this._compilerOptions.noEmitOnError = false;

// Compose Angular Compiler Options.
this._angularCompilerOptions = Object.assign(
this._compilerOptions,
tsConfig.raw['angularCompilerOptions'],
{ basePath }
);

// Set JIT (no code generation) or AOT mode.
if (options.skipCodeGeneration !== undefined) {
this._JitMode = options.skipCodeGeneration;
}

// Process i18n options.
if (options.hasOwnProperty('i18nInFile')) {
this._angularCompilerOptions.i18nInFile = options.i18nInFile;
this._compilerOptions.i18nInFile = options.i18nInFile;
}
if (options.hasOwnProperty('i18nInFormat')) {
this._angularCompilerOptions.i18nInFormat = options.i18nInFormat;
this._compilerOptions.i18nInFormat = options.i18nInFormat;
}
if (options.hasOwnProperty('i18nOutFile')) {
this._angularCompilerOptions.i18nOutFile = options.i18nOutFile;
this._compilerOptions.i18nOutFile = options.i18nOutFile;
}
if (options.hasOwnProperty('i18nOutFormat')) {
this._angularCompilerOptions.i18nOutFormat = options.i18nOutFormat;
this._compilerOptions.i18nOutFormat = options.i18nOutFormat;
}
if (options.hasOwnProperty('locale') && options.locale) {
this._angularCompilerOptions.i18nInLocale = this._validateLocale(options.locale);
this._compilerOptions.i18nInLocale = this._validateLocale(options.locale);
}
if (options.hasOwnProperty('missingTranslation')) {
this._angularCompilerOptions.i18nInMissingTranslations =
this._compilerOptions.i18nInMissingTranslations =
options.missingTranslation as 'error' | 'warning' | 'ignore';
}

// Use entryModule if available in options, otherwise resolve it from mainPath after program
// creation.
if (this._options.entryModule) {
this._entryModule = this._options.entryModule;
} else if (this._angularCompilerOptions.entryModule) {
} else if (this._compilerOptions.entryModule) {
this._entryModule = path.resolve(this._basePath,
this._angularCompilerOptions.entryModule);
this._compilerOptions.entryModule);
}

// Create the webpack compiler host.
this._compilerHost = new WebpackCompilerHost(this._compilerOptions, this._basePath);
this._compilerHost.enableCaching();
const webpackCompilerHost = new WebpackCompilerHost(this._compilerOptions, this._basePath);
webpackCompilerHost.enableCaching();

// Create and set a new WebpackResourceLoader.
this._resourceLoader = new WebpackResourceLoader();
this._compilerHost.setResourceLoader(this._resourceLoader);
webpackCompilerHost.setResourceLoader(this._resourceLoader);

// Use the WebpackCompilerHost with a resource loader to create an AngularCompilerHost.
this._compilerHost = createCompilerHost({
options: this._compilerOptions,
tsHost: webpackCompilerHost
}) as CompilerHost & WebpackCompilerHost;

// Override some files in the FileSystem.
if (this._options.hostOverrideFileSystem) {
Expand Down Expand Up @@ -342,28 +339,24 @@ export class AngularCompilerPlugin implements Tapable {
private _createOrUpdateProgram() {
return Promise.resolve()
.then(() => {
const changedTsFiles = this._getChangedTsFiles();

changedTsFiles.forEach((file) => {
if (!this._tsFilenames.includes(file)) {
// TODO: figure out if action is needed for files that were removed from the
// compilation.
this._tsFilenames.push(file);
}
});
// Get the root files from the ts config.
// When a new root name (like a lazy route) is added, it won't be available from
// following imports on the existing files, so we need to get the new list of root files.
this._rootNames = readConfiguration(this._tsConfigPath).rootNames;

// Update the forked type checker.
// Update the forked type checker with all changed compilation files.
// This includes templates, that also need to be reloaded on the type checker.
if (this._forkTypeChecker && !this._firstRun) {
this._updateForkedTypeChecker(changedTsFiles);
this._updateForkedTypeChecker(this._rootNames, this._getChangedCompilationFiles());
}

if (this._JitMode) {
// Create the TypeScript program.
time('AngularCompilerPlugin._createOrUpdateProgram.ts.createProgram');
this._program = ts.createProgram(
this._tsFilenames,
this._angularCompilerOptions,
this._angularCompilerHost,
this._rootNames,
this._compilerOptions,
this._compilerHost,
this._program as ts.Program
);
timeEnd('AngularCompilerPlugin._createOrUpdateProgram.ts.createProgram');
Expand All @@ -372,26 +365,19 @@ export class AngularCompilerPlugin implements Tapable {
} else {
time('AngularCompilerPlugin._createOrUpdateProgram.ng.createProgram');
// Create the Angular program.
try {
this._program = createProgram({
rootNames: this._tsFilenames,
options: this._angularCompilerOptions,
host: this._angularCompilerHost,
oldProgram: this._program as Program
this._program = createProgram({
rootNames: this._rootNames,
options: this._compilerOptions,
host: this._compilerHost,
oldProgram: this._program as Program
});
timeEnd('AngularCompilerPlugin._createOrUpdateProgram.ng.createProgram');

time('AngularCompilerPlugin._createOrUpdateProgram.ng.loadNgStructureAsync');
return this._program.loadNgStructureAsync()
.then(() => {
timeEnd('AngularCompilerPlugin._createOrUpdateProgram.ng.loadNgStructureAsync');
});
timeEnd('AngularCompilerPlugin._createOrUpdateProgram.ng.createProgram');

time('AngularCompilerPlugin._createOrUpdateProgram.ng.loadNgStructureAsync');
return this._program.loadNgStructureAsync()
.then(() => {
timeEnd('AngularCompilerPlugin._createOrUpdateProgram.ng.loadNgStructureAsync');
});
} catch (e) {
// TODO: remove this when the issue is addressed.
// Temporary workaround for https://github.com/angular/angular/issues/19951
this._program = undefined;
throw e;
}
}
})
.then(() => {
Expand All @@ -412,7 +398,7 @@ export class AngularCompilerPlugin implements Tapable {
const result = __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
program: this._getTsProgram(),
host: this._compilerHost,
angularCompilerOptions: Object.assign({}, this._angularCompilerOptions, {
angularCompilerOptions: Object.assign({}, this._compilerOptions, {
// genDir seems to still be needed in @angular\compiler-cli\src\compiler_host.js:226.
genDir: ''
}),
Expand Down Expand Up @@ -508,8 +494,8 @@ export class AngularCompilerPlugin implements Tapable {
} else {
// Found a new route, add it to the map and read it into the compiler host.
this._lazyRoutes[moduleKey] = modulePath;
this._angularCompilerHost.readFile(lazyRouteTSFile);
this._angularCompilerHost.invalidate(lazyRouteTSFile);
this._compilerHost.readFile(lazyRouteTSFile);
this._compilerHost.invalidate(lazyRouteTSFile);
}
});
}
Expand Down Expand Up @@ -545,7 +531,7 @@ export class AngularCompilerPlugin implements Tapable {

this._typeCheckerProcess = fork(path.resolve(__dirname, typeCheckerFile), [], forkOptions);
this._typeCheckerProcess.send(new InitMessage(this._compilerOptions, this._basePath,
this._JitMode, this._tsFilenames));
this._JitMode, this._rootNames));

// Cleanup.
const killTypeCheckerProcess = () => {
Expand All @@ -557,8 +543,8 @@ export class AngularCompilerPlugin implements Tapable {
process.once('uncaughtException', killTypeCheckerProcess);
}

private _updateForkedTypeChecker(changedTsFiles: string[]) {
this._typeCheckerProcess.send(new UpdateMessage(changedTsFiles));
private _updateForkedTypeChecker(rootNames: string[], changedCompilationFiles: string[]) {
this._typeCheckerProcess.send(new UpdateMessage(rootNames, changedCompilationFiles));
}


Expand Down Expand Up @@ -682,22 +668,12 @@ export class AngularCompilerPlugin implements Tapable {
// Update the resource loader with the new webpack compilation.
this._resourceLoader.update(compilation);

// Create a new process for the type checker on the second build if there isn't one yet.
if (this._forkTypeChecker && !this._firstRun && !this._typeCheckerProcess) {
this._createForkedTypeChecker();
}

this._donePromise = Promise.resolve()
.then(() => {
// Create a new process for the type checker.
if (this._forkTypeChecker && !this._firstRun && !this._typeCheckerProcess) {
this._createForkedTypeChecker();
}
})
.then(() => {
if (this._firstRun) {
// Use the WebpackResourceLoader with a resource loader to create an AngularCompilerHost.
this._angularCompilerHost = createCompilerHost({
options: this._angularCompilerOptions,
tsHost: this._compilerHost
}) as CompilerHost & WebpackCompilerHost;
}
})
.then(() => this._update())
.then(() => {
timeEnd('AngularCompilerPlugin._make');
Expand Down Expand Up @@ -731,10 +707,6 @@ export class AngularCompilerPlugin implements Tapable {
const changedTsFiles = this._getChangedTsFiles();
if (this._ngCompilerSupportsNewApi) {
this._processLazyRoutes(this._listLazyRoutesFromProgram());
// TODO: remove this when the issue is addressed.
// Fix for a bug in compiler where the program needs to be updated after
// _listLazyRoutesFromProgram is called.
return this._createOrUpdateProgram();
} else if (this._firstRun) {
this._processLazyRoutes(this._getLazyRoutesFromNgtools());
} else if (changedTsFiles.length > 0) {
Expand Down Expand Up @@ -772,11 +744,11 @@ export class AngularCompilerPlugin implements Tapable {
}

// If we have a locale, auto import the locale data file.
if (this._angularCompilerOptions.i18nInLocale) {
if (this._compilerOptions.i18nInLocale) {
transformOps.push(...registerLocaleData(
sf,
this.entryModule,
this._angularCompilerOptions.i18nInLocale
this._compilerOptions.i18nInLocale
));
}
} else if (this._platform === PLATFORM.Server) {
Expand Down Expand Up @@ -846,7 +818,7 @@ export class AngularCompilerPlugin implements Tapable {
}

// Write the extracted messages to disk.
const i18nOutFilePath = path.resolve(this._basePath, this._angularCompilerOptions.i18nOutFile);
const i18nOutFilePath = path.resolve(this._basePath, this._compilerOptions.i18nOutFile);
const i18nOutFileContent = this._compilerHost.readFile(i18nOutFilePath);
if (i18nOutFileContent) {
_recursiveMkDir(path.dirname(i18nOutFilePath))
Expand Down Expand Up @@ -994,17 +966,14 @@ export class AngularCompilerPlugin implements Tapable {

if (!hasErrors(allDiagnostics)) {
time('AngularCompilerPlugin._emit.ng.emit');
const extractI18n = !!this._angularCompilerOptions.i18nOutFile;
const extractI18n = !!this._compilerOptions.i18nOutFile;
const emitFlags = extractI18n ? EmitFlags.I18nBundle : EmitFlags.Default;
emitResult = angularProgram.emit({ emitFlags, customTransformers });
allDiagnostics.push(...emitResult.diagnostics);
if (extractI18n) {
this.writeI18nOutFile();
}
timeEnd('AngularCompilerPlugin._emit.ng.emit');
} else {
// Throw away the old program if there was an error.
this._program = undefined;
}
}
} catch (e) {
Expand Down
11 changes: 11 additions & 0 deletions packages/@ngtools/webpack/src/ngtools_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ export interface CreateCompilerHostInterface {
export interface FormatDiagnosticsInterface {
(diags: Diagnostics): string;
}
export interface ParsedConfiguration {
project: string;
options: CompilerOptions;
rootNames: string[];
emitFlags: any;
errors: Diagnostics;
}
export interface ReadConfigurationInterface {
(project: string, existingOptions?: ts.CompilerOptions): ParsedConfiguration;
}

// Manually check for Compiler CLI availability and supported version.
// This is needed because @ngtools/webpack does not depend directly on @angular/compiler-cli, since
Expand Down Expand Up @@ -173,4 +183,5 @@ try {
export const createProgram: CreateProgramInterface = ngtools2.createProgram;
export const createCompilerHost: CreateCompilerHostInterface = ngtools2.createCompilerHost;
export const formatDiagnostics: FormatDiagnosticsInterface = ngtools2.formatDiagnostics;
export const readConfiguration: ReadConfigurationInterface = compilerCli.readConfiguration;
export const EmitFlags = ngtools2.EmitFlags;
Loading

0 comments on commit 6570983

Please sign in to comment.