Skip to content

Commit

Permalink
feat(compiler-cli): make ngc use the new transformer pipeline
Browse files Browse the repository at this point in the history
The source map does not currently work with the transformer pipepline.
It will be re-enabled after TypeScript 2.4 is made is min version.
  • Loading branch information
vicb committed Aug 2, 2017
1 parent 49cd851 commit 361b5f5
Show file tree
Hide file tree
Showing 9 changed files with 206 additions and 122 deletions.
2 changes: 1 addition & 1 deletion integration/hello_world__closure/package.json
Expand Up @@ -13,7 +13,7 @@
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
"google-closure-compiler": "20170409.0.0",
"rxjs": "5.3.1",
"typescript": "2.1.6",
"typescript": "~2.3.1",
"zone.js": "0.8.6"
},
"devDependencies": {
Expand Down
5 changes: 3 additions & 2 deletions packages/compiler-cli/package.json
Expand Up @@ -5,13 +5,14 @@
"main": "index.js",
"typings": "index.d.ts",
"bin": {
"ngc": "./src/main.js",
"ngc": "./src/main2.js",
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"@angular/tsc-wrapped": "5.0.0-beta.1",
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0"
"minimist": "^1.2.0",
"tsickle": "^0.23.4"
},
"peerDependencies": {
"typescript": "^2.0.2",
Expand Down
Expand Up @@ -56,7 +56,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
name: reference.name,
kind: 'reference',
type: type || info.query.getBuiltinType(BuiltinType.Any),
get definition() { return getDefintionOf(info, reference); }
get definition() { return getDefinitionOf(info, reference); }
});
}
}
Expand All @@ -77,7 +77,7 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
return result;
}

function getDefintionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
function getDefinitionOf(info: DiagnosticTemplateInfo, ast: TemplateAst): Definition|undefined {
if (info.fileName) {
const templateOffset = info.offset;
return [{
Expand Down Expand Up @@ -124,7 +124,7 @@ function getVarDeclarations(
}
result.push({
name,
kind: 'variable', type, get definition() { return getDefintionOf(info, variable); }
kind: 'variable', type, get definition() { return getDefinitionOf(info, variable); }
});
}
}
Expand Down
15 changes: 15 additions & 0 deletions packages/compiler-cli/src/main2.ts
@@ -0,0 +1,15 @@
#!/usr/bin/env node
/**
* @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 {main} from './ngc';

// CLI entry point
if (require.main === module) {
process.exit(main(process.argv.slice(2), s => console.error(s)));
}
133 changes: 49 additions & 84 deletions packages/compiler-cli/src/ngc.ts
Expand Up @@ -13,6 +13,7 @@ import {isSyntaxError, syntaxError} from '@angular/compiler';
import {MetadataBundler, createBundleIndexHost} from '@angular/tsc-wrapped';
import * as fs from 'fs';
import * as path from 'path';
import * as tsickle from 'tsickle';
import * as ts from 'typescript';
import * as api from './transformers/api';
import * as ng from './transformers/entry_points';
Expand Down Expand Up @@ -66,17 +67,6 @@ function check(cwd: string, ...args: Diagnostics[]) {
}
}

function syntheticError(message: string): ts.Diagnostic {
return {
file: null as any as ts.SourceFile,
start: 0,
length: 0,
messageText: message,
category: ts.DiagnosticCategory.Error,
code: 0
};
}

export function readConfiguration(
project: string, basePath: string, checkFunc: (cwd: string, ...args: any[]) => void = check,
existingOptions?: ts.CompilerOptions) {
Expand Down Expand Up @@ -106,85 +96,56 @@ export function readConfiguration(
return {parsed, ngOptions};
}

function getProjectDirectory(project: string): string {
let isFile: boolean;
try {
isFile = fs.lstatSync(project).isFile();
} catch (e) {
// Project doesn't exist. Assume it is a file has an extension. This case happens
// when the project file is passed to set basePath but no tsconfig.json file exists.
// It is used in tests to ensure that the options can be passed in without there being
// an actual config file.
isFile = path.extname(project) !== '';
}

// If project refers to a file, the project directory is the file's parent directory
// otherwise project is the project directory.
return isFile ? path.dirname(project) : project;
}

export function performCompilation(
basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any,
consoleError: (s: string) => void = console.error,
checkFunc: (cwd: string, ...args: any[]) => void = check, tsCompilerHost?: ts.CompilerHost) {
try {
ngOptions.basePath = basePath;
ngOptions.genDir = basePath;

let host = tsCompilerHost || ts.createCompilerHost(options, true);
host.realpath = p => p;

const rootFileNames = files.map(f => path.normalize(f));

const addGeneratedFileName = (fileName: string) => {
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
rootFileNames.push(fileName);
}
};

if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
const {host: bundleHost, indexName, errors} =
createBundleIndexHost(ngOptions, rootFileNames, host);
if (errors) checkFunc(basePath, errors);
if (indexName) addGeneratedFileName(indexName);
host = bundleHost;
checkFunc: (cwd: string, ...args: any[]) => void = check,
tsCompilerHost?: ts.CompilerHost): tsickle.EmitResult {
ngOptions.basePath = basePath;
ngOptions.genDir = basePath;

let host = tsCompilerHost || ts.createCompilerHost(options, true);
host.realpath = p => p;

const rootFileNames = files.map(f => path.normalize(f));

const addGeneratedFileName = (fileName: string) => {
if (fileName.startsWith(basePath) && TS_EXT.exec(fileName)) {
rootFileNames.push(fileName);
}
};

const ngHostOptions = {...options, ...ngOptions};
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
const {host: bundleHost, indexName, errors} =
createBundleIndexHost(ngOptions, rootFileNames, host);
if (errors) checkFunc(basePath, errors);
if (indexName) addGeneratedFileName(indexName);
host = bundleHost;
}

const ngProgram =
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});
const ngHostOptions = {...options, ...ngOptions};
const ngHost = ng.createHost({tsHost: host, options: ngHostOptions});

// Check parameter diagnostics
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());
const ngProgram =
ng.createProgram({rootNames: rootFileNames, host: ngHost, options: ngHostOptions});

// Check syntactic diagnostics
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());
// Check parameter diagnostics
checkFunc(basePath, ngProgram.getTsOptionDiagnostics(), ngProgram.getNgOptionDiagnostics());

// Check TypeScript semantic and Angular structure diagnostics
checkFunc(
basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());
// Check syntactic diagnostics
checkFunc(basePath, ngProgram.getTsSyntacticDiagnostics());

// Check Angular semantic diagnostics
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());
// Check TypeScript semantic and Angular structure diagnostics
checkFunc(basePath, ngProgram.getTsSemanticDiagnostics(), ngProgram.getNgStructuralDiagnostics());

ngProgram.emit({
emitFlags: api.EmitFlags.Default |
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
});
} catch (e) {
if (isSyntaxError(e)) {
console.error(e.message);
consoleError(e.message);
return 1;
}
}
// Check Angular semantic diagnostics
checkFunc(basePath, ngProgram.getNgSemanticDiagnostics());

return 0;
return ngProgram.emit({
emitFlags: api.EmitFlags.Default |
((ngOptions.skipMetadataEmit || ngOptions.flatModuleOutFile) ? 0 : api.EmitFlags.Metadata)
});
}


export function main(
args: string[], consoleError: (s: string) => void = console.error,
checkFunc: (cwd: string, ...args: any[]) => void = check): number {
Expand All @@ -197,16 +158,20 @@ export function main(
// file names in tsconfig are resolved relative to this absolute path
const basePath = path.resolve(process.cwd(), projectDir);
const {parsed, ngOptions} = readConfiguration(project, basePath, checkFunc);
return performCompilation(
basePath, parsed.fileNames, parsed.options, ngOptions, consoleError, checkFunc);

const result =
performCompilation(basePath, parsed.fileNames, parsed.options, ngOptions, checkFunc);

return result.diagnostics.length === 0 ? 0 : 1;

} catch (e) {
if (isSyntaxError(e)) {
consoleError(e.message);
return 1;
}

consoleError(e.stack);
consoleError('Compilation failed');
return 2;
}
}

// CLI entry point
if (require.main === module) {
process.exit(main(process.argv.slice(2), s => console.error(s)));
}
3 changes: 2 additions & 1 deletion packages/compiler-cli/src/transformers/api.ts
Expand Up @@ -7,6 +7,7 @@
*/

import {ParseSourceSpan} from '@angular/compiler';
import * as tsickle from 'tsickle';
import * as ts from 'typescript';

export enum DiagnosticCategory {
Expand Down Expand Up @@ -227,5 +228,5 @@ export interface Program {
emitFlags: EmitFlags,
// transformers?: CustomTransformers, // See TODO above
cancellationToken?: ts.CancellationToken,
}): void;
}): tsickle.EmitResult;
}
41 changes: 28 additions & 13 deletions packages/compiler-cli/src/transformers/program.ts
Expand Up @@ -10,6 +10,7 @@ import {AotCompiler, GeneratedFile, NgAnalyzedModules, createAotCompiler, getPar
import {MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
import {writeFileSync} from 'fs';
import * as path from 'path';
import * as tsickle from 'tsickle';
import * as ts from 'typescript';

import {CompilerHost as AotCompilerHost, CompilerHostContext} from '../compiler_host';
Expand Down Expand Up @@ -115,9 +116,26 @@ class AngularCompilerProgram implements Program {
getLazyRoutes(cancellationToken?: ts.CancellationToken): {[route: string]: string} { return {}; }

emit({emitFlags = EmitFlags.Default, cancellationToken}:
{emitFlags?: EmitFlags, cancellationToken?: ts.CancellationToken}): ts.EmitResult {
{emitFlags?: EmitFlags, cancellationToken?: ts.CancellationToken}): tsickle.EmitResult {
const emitMap = new Map<string, string>();
const result = this.programWithStubs.emit(

const tsickleCompilerHostOptions: tsickle.TransformerOptions = {
googmodule: false,
untyped: true,
convertIndexImportShorthand: true,
transformDecorators: this.options.annotationsAs === 'static fields',
transformTypesToClosure: this.options.annotateForClosureCompiler,
};

const tsickleHost: tsickle.TransformerHost = {
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName),
pathToModuleName: (context, importPath) => '',
shouldIgnoreWarningsForPath: (filePath) => false,
fileNameToModuleId: (fileName) => fileName,
};

const result = tsickle.emitWithTsickle(
this.programWithStubs, tsickleHost, tsickleCompilerHostOptions, this.host, this.options,
/* targetSourceFile */ undefined,
createWriteFileCallback(emitFlags, this.host, this.metadataCache, emitMap),
cancellationToken, (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
Expand All @@ -134,6 +152,7 @@ class AngularCompilerProgram implements Program {
this.host.writeFile(fileName, file.source, false, error => {});
}
});

return result;
}

Expand Down Expand Up @@ -183,19 +202,15 @@ class AngularCompilerProgram implements Program {
return this.generatedFiles && this._generatedFileDiagnostics !;
}

private calculateTransforms(): ts.CustomTransformers {
const before: ts.TransformerFactory<ts.SourceFile>[] = [];
const after: ts.TransformerFactory<ts.SourceFile>[] = [];
private calculateTransforms(): tsickle.EmitTransformers {
const beforeTs: ts.TransformerFactory<ts.SourceFile>[] = [];
if (!this.options.disableExpressionLowering) {
before.push(getExpressionLoweringTransformFactory(this.metadataCache));
beforeTs.push(getExpressionLoweringTransformFactory(this.metadataCache));
}
if (!this.options.skipTemplateCodegen) {
after.push(getAngularEmitterTransformFactory(this.generatedFiles));
beforeTs.push(getAngularEmitterTransformFactory(this.generatedFiles));
}
const result: ts.CustomTransformers = {};
if (before.length) result.before = before;
if (after.length) result.after = after;
return result;
return {beforeTs};
}

private catchAnalysisError(e: any): NgAnalyzedModules {
Expand Down Expand Up @@ -228,8 +243,8 @@ class AngularCompilerProgram implements Program {
private generateStubs() {
return this.options.skipTemplateCodegen ? [] :
this.options.generateCodeForLibraries === false ?
this.compiler.emitAllStubs(this.analyzedModules) :
this.compiler.emitPartialStubs(this.analyzedModules);
this.compiler.emitPartialStubs(this.analyzedModules) :
this.compiler.emitAllStubs(this.analyzedModules);
}

private generateFiles() {
Expand Down

0 comments on commit 361b5f5

Please sign in to comment.