From 072446aed3bb511d16b1170361883b5a8bd488e5 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Sun, 1 May 2016 11:22:39 -0700 Subject: [PATCH] feat(offline compiler): add metadata emit Also add a configuration switch to disable the codegen, so we can still use the metadata emit and tsickle pre-processing in the build pipeline for angular itself. --- gulpfile.js | 18 ++++- tools/compiler_cli/README.md | 2 +- tools/compiler_cli/package.json | 8 +- tools/compiler_cli/src/basic_spec.ts | 28 +++++++ tools/compiler_cli/src/codegen.ts | 37 +++++---- tools/compiler_cli/src/compiler_host.ts | 51 +++++++++--- tools/compiler_cli/src/index.ts | 2 +- tools/compiler_cli/src/main.ts | 78 ++++++++++++------- tools/compiler_cli/src/reflector_host.ts | 31 ++++++-- tools/compiler_cli/src/static_reflector.ts | 17 ++-- .../compiler_cli/src/static_reflector_spec.ts | 8 ++ tools/compiler_cli/src/tsc.ts | 34 ++++---- tools/compiler_cli/test/tsconfig.json | 4 +- tools/metadata/package.json | 4 +- 14 files changed, 230 insertions(+), 92 deletions(-) create mode 100644 tools/compiler_cli/src/basic_spec.ts diff --git a/gulpfile.js b/gulpfile.js index 0119bc3c8b..44d97db350 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1056,11 +1056,19 @@ gulp.task('test.typings', ['build.js.cjs'], gulp.task('!build.compiler_cli', ['build.js.cjs'], function(done) { runTsc('tools/compiler_cli/src', done); }); +gulp.task('!clean.compiler_cli', function(done) { + fse.remove(path.join('dist', 'tools', 'compiler_cli', 'test'), + fse.remove(path.join('tools', 'compiler_cli', 'test', 'src', '*.ngfactory.ts'), + fse.remove(path.join('tools', 'compiler_cli', 'test', 'src', 'a', + '*.ngfactory.ts'), + done))); +}); + gulp.task('!test.compiler_cli.codegen', function(done) { try { require('./dist/js/cjs/compiler_cli/main') .main("tools/compiler_cli/test") - .then(function() { runTsc('tools/compiler_cli/test', done); }) + .then(done) .catch(function(rej) { done(new Error(rej)); }); } catch (err) { done(err); @@ -1070,11 +1078,17 @@ gulp.task('!test.compiler_cli.codegen', function(done) { gulp.task('!test.compiler_cli.unit', function(done) { runJasmineTests(['dist/js/cjs/compiler_cli/**/*_spec.js'], done) }); +// This task overwrites our careful tsickle-lowered Decorators with normal .js emit. +// So it should only be run after asserting on the .js file content. +gulp.task('!test.compiler_cli.verify_codegen', + function(done) { runTsc('tools/compiler_cli/test', done); }); + // End-to-end test for compiler CLI. // Calls the compiler using its command-line interface, then compiles the app with the codegen. // TODO(alexeagle): wire up the playground tests with offline compilation, similar to dart. gulp.task('test.compiler_cli', ['!build.compiler_cli'], function(done) { - runSequence('!test.compiler_cli.unit', '!test.compiler_cli.codegen', sequenceComplete(done)); + runSequence('!clean.compiler_cli', '!test.compiler_cli.codegen', '!test.compiler_cli.unit', + '!test.compiler_cli.verify_codegen', sequenceComplete(done)); }); // ----------------- diff --git a/tools/compiler_cli/README.md b/tools/compiler_cli/README.md index e0a1ef0441..df612aec43 100644 --- a/tools/compiler_cli/README.md +++ b/tools/compiler_cli/README.md @@ -13,7 +13,7 @@ and then downloading only the executable JS to the client. ## Install and use ``` -$ npm install angular2-template-compiler typescript rxjs +$ npm install angular2-template-compiler typescript@next rxjs @angular/compiler # Optional sanity check, make sure TypeScript can compile $ ./node_modules/.bin/tsc -p path/to/project $ ./node_modules/.bin/ng2tc -p path/to/project diff --git a/tools/compiler_cli/package.json b/tools/compiler_cli/package.json index 3bc9094520..7c7859cb20 100644 --- a/tools/compiler_cli/package.json +++ b/tools/compiler_cli/package.json @@ -8,14 +8,16 @@ "ng2tc": "./main.js" }, "dependencies": { - "angular2": "angular/angular#2.0.0-build.cacdead.js", "ts-metadata-collector": "^0.1.0", - "tsickle": "^0.1.0", + "tsickle": "^0.1.2", "reflect-metadata": "^0.1.2", "parse5": "1.3.2" }, "peerDependencies": { - "typescript": "^1.8.0 || ^1.9.0-dev" + "typescript": "^1.8.0 || ^1.9.0-dev", + "@angular/compiler": "0.0.0-5", + "@angular/platform-server": "0.0.0-5", + "@angular/core": "0.0.0-5" }, "repository": { "type": "git", diff --git a/tools/compiler_cli/src/basic_spec.ts b/tools/compiler_cli/src/basic_spec.ts new file mode 100644 index 0000000000..f577705cb0 --- /dev/null +++ b/tools/compiler_cli/src/basic_spec.ts @@ -0,0 +1,28 @@ +/// +/// +import * as fs from 'fs'; +import * as path from 'path'; + +describe("template codegen output", () => { + const outDir = path.join('dist', 'tools', 'compiler_cli', 'test', 'built'); + + it("should lower Decorators without reflect-metadata", () => { + const jsOutput = path.join(outDir, 'basic.js'); + expect(fs.existsSync(jsOutput)).toBeTruthy(); + expect(fs.readFileSync(jsOutput, {encoding: 'utf-8'})).not.toContain('Reflect.decorate'); + }); + + it("should produce metadata.json outputs", () => { + const metadataOutput = path.join(outDir, 'basic.metadata.json'); + expect(fs.existsSync(metadataOutput)).toBeTruthy(); + const output = fs.readFileSync(metadataOutput, {encoding: 'utf-8'}); + expect(output).toContain('"decorators":'); + expect(output).toContain('"name":"Component","module":"angular2/core"'); + }); + + it("should write .d.ts files", () => { + const dtsOutput = path.join(outDir, 'basic.d.ts'); + expect(fs.existsSync(dtsOutput)).toBeTruthy(); + expect(fs.readFileSync(dtsOutput, {encoding: 'utf-8'})).toContain('Basic'); + }); +}); \ No newline at end of file diff --git a/tools/compiler_cli/src/codegen.ts b/tools/compiler_cli/src/codegen.ts index bb148fa85e..d30e2864b4 100644 --- a/tools/compiler_cli/src/codegen.ts +++ b/tools/compiler_cli/src/codegen.ts @@ -21,7 +21,6 @@ import {Parse5DomAdapter} from '@angular/platform-server'; import {MetadataCollector} from 'ts-metadata-collector'; import {NodeReflectorHost} from './reflector_host'; -import {wrapCompilerHost, CodeGeneratorHost} from './compiler_host'; const SOURCE_EXTENSION = /\.[jt]s$/; const PREAMBLE = `/** @@ -33,11 +32,23 @@ const PREAMBLE = `/** export interface AngularCompilerOptions { // Absolute path to a directory where generated file structure is written genDir: string; + + // Path to the directory containing the tsconfig.json file. + basePath: string; + + // Don't do the template code generation + skipTemplateCodegen: boolean; + + // Don't produce .metadata.json files (they don't work for bundled emit with --out) + skipMetadataEmit: boolean; + + // Lookup angular's symbols using the old angular2/... npm namespace. + legacyPackageLayout: boolean; } export class CodeGenerator { constructor(private ngOptions: AngularCompilerOptions, private basePath: string, - public program: ts.Program, public host: CodeGeneratorHost, + private program: ts.Program, public host: ts.CompilerHost, private staticReflector: StaticReflector, private resolver: CompileMetadataResolver, private compiler: compiler.OfflineCompiler, private reflectorHost: NodeReflectorHost) {} @@ -105,20 +116,11 @@ export class CodeGenerator { .map(generateOneFile)); } - static create(ngOptions: AngularCompilerOptions, parsed: ts.ParsedCommandLine, basePath: string, - compilerHost: ts.CompilerHost): - {errors?: ts.Diagnostic[], generator?: CodeGenerator} { - const program = ts.createProgram(parsed.fileNames, parsed.options, compilerHost); - const errors = program.getOptionsDiagnostics(); - if (errors && errors.length) { - return {errors}; - } - - const metadataCollector = new MetadataCollector(); - const reflectorHost = - new NodeReflectorHost(program, metadataCollector, compilerHost, parsed.options); + static create(ngOptions: AngularCompilerOptions, program: ts.Program, options: ts.CompilerOptions, + compilerHost: ts.CompilerHost): CodeGenerator { const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))}; const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver(); + const reflectorHost = new NodeReflectorHost(program, compilerHost, options, ngOptions); const staticReflector = new StaticReflector(reflectorHost); const htmlParser = new HtmlParser(); const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser); @@ -132,10 +134,7 @@ export class CodeGenerator { new compiler.DirectiveResolver(staticReflector), new compiler.PipeResolver(staticReflector), new compiler.ViewResolver(staticReflector), null, null, staticReflector); - return { - generator: new CodeGenerator(ngOptions, basePath, program, - wrapCompilerHost(compilerHost, parsed.options), staticReflector, - resolver, offlineCompiler, reflectorHost) - }; + return new CodeGenerator(ngOptions, ngOptions.basePath, program, compilerHost, staticReflector, + resolver, offlineCompiler, reflectorHost); } } diff --git a/tools/compiler_cli/src/compiler_host.ts b/tools/compiler_cli/src/compiler_host.ts index 283bf37920..c86e496e1b 100644 --- a/tools/compiler_cli/src/compiler_host.ts +++ b/tools/compiler_cli/src/compiler_host.ts @@ -1,6 +1,8 @@ import * as ts from 'typescript'; import * as path from 'path'; import {convertDecorators} from 'tsickle'; +import {NodeReflectorHost} from './reflector_host'; +import {AngularCompilerOptions} from './codegen'; const DEBUG = false; function debug(msg: string, ...o: any[]) { @@ -31,14 +33,15 @@ export abstract class DelegatingHost implements ts.CompilerHost { trace = (s: string) => this.delegate.trace(s); directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName); } -const TSICKLE_SUPPORT = `interface DecoratorInvocation { - type: Function; - args?: any[]; -} -`; -export class CodeGeneratorHost extends DelegatingHost { + +export class TsickleHost extends DelegatingHost { // Additional diagnostics gathered by pre- and post-emit transformations. public diagnostics: ts.Diagnostic[] = []; + private TSICKLE_SUPPORT = `interface DecoratorInvocation { + type: Function; + args?: any[]; + } + `; constructor(delegate: ts.CompilerHost, private options: ts.CompilerOptions) { super(delegate); } getSourceFile = @@ -50,14 +53,40 @@ export class CodeGeneratorHost extends DelegatingHost { if (converted.diagnostics) { this.diagnostics.push(...converted.diagnostics); } - newContent = TSICKLE_SUPPORT + converted.output; + newContent = this.TSICKLE_SUPPORT + converted.output; debug(newContent); } return ts.createSourceFile(fileName, newContent, languageVersion, true); } } -export function wrapCompilerHost(delegate: ts.CompilerHost, - options: ts.CompilerOptions): CodeGeneratorHost { - return new CodeGeneratorHost(delegate, options); -} \ No newline at end of file +export class MetadataWriterHost extends DelegatingHost { + private reflectorHost: NodeReflectorHost; + constructor(delegate: ts.CompilerHost, program: ts.Program, options: ts.CompilerOptions, + ngOptions: AngularCompilerOptions) { + super(delegate); + this.reflectorHost = new NodeReflectorHost(program, this, options, ngOptions); + } + + writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, + onError?: (message: string) => void, + sourceFiles?: ts.SourceFile[]) => { + if (/\.d\.ts$/.test(fileName)) { + // Let the original file be written first; this takes care of creating parent directories + this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); + + // TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412 is + // released + return; + } + + if (!sourceFiles) { + throw new Error('Metadata emit requires the sourceFiles are passed to WriteFileCallback. ' + + 'Update to TypeScript ^1.9.0-dev'); + } + if (sourceFiles.length > 1) { + throw new Error('Bundled emit with --out is not supported'); + } + this.reflectorHost.writeMetadata(fileName, sourceFiles[0]); + } +} diff --git a/tools/compiler_cli/src/index.ts b/tools/compiler_cli/src/index.ts index b2c5fb2aa5..51bc86fb62 100644 --- a/tools/compiler_cli/src/index.ts +++ b/tools/compiler_cli/src/index.ts @@ -1,3 +1,3 @@ export {CodeGenerator} from './codegen'; export {NodeReflectorHost} from './reflector_host'; -export {wrapCompilerHost, CodeGeneratorHost} from './compiler_host'; +export {TsickleHost, MetadataWriterHost} from './compiler_host'; diff --git a/tools/compiler_cli/src/main.ts b/tools/compiler_cli/src/main.ts index 1f4238d83f..1934be926a 100644 --- a/tools/compiler_cli/src/main.ts +++ b/tools/compiler_cli/src/main.ts @@ -10,8 +10,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; import {tsc, check} from './tsc'; - +import {MetadataWriterHost, TsickleHost} from './compiler_host'; +import {NodeReflectorHost} from './reflector_host'; import {CodeGenerator} from './codegen'; +import {MetadataCollector, ModuleMetadata} from 'ts-metadata-collector'; const DEBUG = false; @@ -20,37 +22,61 @@ function debug(msg: string, ...o: any[]) { } export function main(project: string, basePath?: string): Promise { - // file names in tsconfig are resolved relative to this absolute path - basePath = path.join(process.cwd(), basePath || project); + try { + let projectDir = project; + if (fs.lstatSync(project).isFile()) { + projectDir = path.dirname(project); + } + // file names in tsconfig are resolved relative to this absolute path + basePath = path.join(process.cwd(), basePath || projectDir); - // read the configuration options from wherever you store them - const {parsed, ngOptions} = tsc.readConfiguration(project, basePath); + // read the configuration options from wherever you store them + const {parsed, ngOptions} = tsc.readConfiguration(project, basePath); + ngOptions.basePath = basePath; - const host = ts.createCompilerHost(parsed.options, true); - const {errors, generator} = CodeGenerator.create(ngOptions, parsed, basePath, host); - check(errors); + const host = ts.createCompilerHost(parsed.options, true); - return generator.codegen() - // use our compiler host, which wraps the built-in one from TypeScript - // This allows us to add features like --stripDesignTimeDecorators to optimize your - // application more. - .then(() => tsc.typeCheckAndEmit(generator.host, generator.program)) - .catch(rejected => { - console.error('Compile failed\n', rejected.message); - throw new Error(rejected); - }); + let codegenStep: Promise; + + const program = ts.createProgram(parsed.fileNames, parsed.options, host); + const errors = program.getOptionsDiagnostics(); + check(errors); + + const doCodegen = + ngOptions.skipTemplateCodegen ? + Promise.resolve(null) : + CodeGenerator.create(ngOptions, program, parsed.options, host).codegen(); + + return doCodegen.then(() => { + tsc.typeCheck(host, program); + + // Emit *.js with Decorators lowered to Annotations, and also *.js.map + const tsicklePreProcessor = new TsickleHost(host, parsed.options); + tsc.emit(tsicklePreProcessor, program); + + if (!ngOptions.skipMetadataEmit) { + // Emit *.metadata.json and *.d.ts + // Not in the same emit pass with above, because tsickle erases + // decorators which we want to read or document. + // Do this emit second since TypeScript will create missing directories for us + // in the standard emit. + const metadataWriter = new MetadataWriterHost(host, program, parsed.options, ngOptions); + tsc.emit(metadataWriter, program); + } + }); + } catch (e) { + return Promise.reject(e); + } } // CLI entry point if (require.main === module) { const args = require('minimist')(process.argv.slice(2)); - try { - main(args.p || args.project || '.', args.basePath) - .then(exitCode => process.exit(exitCode)) - .catch(r => { process.exit(1); }); - } catch (e) { - console.error(e.stack); - console.error("Compilation failed"); - process.exit(1); - } + main(args.p || args.project || '.', args.basePath) + .then(exitCode => process.exit(exitCode)) + .catch(e => { + console.error(e.stack); + console.error("Compilation failed"); + process.exit(1); + }); } diff --git a/tools/compiler_cli/src/reflector_host.ts b/tools/compiler_cli/src/reflector_host.ts index 32bea8d650..6a81b9db6d 100644 --- a/tools/compiler_cli/src/reflector_host.ts +++ b/tools/compiler_cli/src/reflector_host.ts @@ -3,14 +3,33 @@ import * as ts from 'typescript'; import {MetadataCollector, ModuleMetadata} from 'ts-metadata-collector'; import * as fs from 'fs'; import * as path from 'path'; +import {AngularCompilerOptions} from './codegen'; const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/; const DTS = /\.d\.ts$/; export class NodeReflectorHost implements StaticReflectorHost { - constructor(private program: ts.Program, private metadataCollector: MetadataCollector, - private compilerHost: ts.CompilerHost, private options: ts.CompilerOptions) {} + private metadataCollector = new MetadataCollector(); + constructor(private program: ts.Program, private compilerHost: ts.CompilerHost, + private options: ts.CompilerOptions, private ngOptions: AngularCompilerOptions) {} + angularImportLocations() { + if (this.ngOptions.legacyPackageLayout) { + return { + coreDecorators: 'angular2/src/core/metadata', + diDecorators: 'angular2/src/core/di/decorators', + diMetadata: 'angular2/src/core/di/metadata', + provider: 'angular2/src/core/di/provider' + }; + } else { + return { + coreDecorators: '@angular/core/src/metadata', + diDecorators: '@angular/core/src/di/decorators', + diMetadata: '@angular/core/src/di/metadata', + provider: '@angular/core/src/di/provider' + }; + } + } private resolve(m: string, containingFile: string) { const resolved = ts.resolveModuleName(m, containingFile, this.options, this.compilerHost).resolvedModule; @@ -51,7 +70,7 @@ export class NodeReflectorHost implements StaticReflectorHost { throw new Error("Resolution of relative paths requires a containing file."); } // Any containing file gives the same result for absolute imports - containingFile = path.join(this.compilerHost.getCurrentDirectory(), 'index.ts'); + containingFile = path.join(this.ngOptions.basePath, 'index.ts'); } try { @@ -134,8 +153,10 @@ export class NodeReflectorHost implements StaticReflectorHost { } writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) { - if (DTS.test(emitFilePath)) { - const path = emitFilePath.replace(DTS, '.metadata.json'); + // TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is + // released + if (/*DTS*/ /\.js$/.test(emitFilePath)) { + const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json'); const metadata = this.metadataCollector.getMetadata(sourceFile, this.program.getTypeChecker()); if (metadata && metadata.metadata) { diff --git a/tools/compiler_cli/src/static_reflector.ts b/tools/compiler_cli/src/static_reflector.ts index b5558dd613..2a89b93815 100644 --- a/tools/compiler_cli/src/static_reflector.ts +++ b/tools/compiler_cli/src/static_reflector.ts @@ -1,4 +1,4 @@ -import {StringMapWrapper, ListWrapper} from '@angular/facade/src/collection'; +import {StringMapWrapper, ListWrapper} from '@angular/core/src/facade/collection'; import { isArray, isPresent, @@ -6,7 +6,7 @@ import { isPrimitive, isStringMap, FunctionWrapper -} from '@angular/facade/src/lang'; +} from '@angular/core/src/facade/lang'; import { AttributeMetadata, DirectiveMetadata, @@ -58,6 +58,9 @@ export interface StaticReflectorHost { findDeclaration(modulePath: string, symbolName: string, containingFile?: string): StaticSymbol; getStaticSymbol(moduleId: string, declarationFile: string, name: string): StaticSymbol; + + angularImportLocations(): + {coreDecorators: string, diDecorators: string, diMetadata: string, provider: string}; } /** @@ -117,6 +120,9 @@ export class StaticReflector implements ReflectorReader { } public parameters(type: StaticSymbol): any[] { + if (!(type instanceof StaticSymbol)) { + throw new Error(`parameters recieved ${JSON.stringify(type)} which is not a StaticSymbol`); + } try { let parameters = this.parameterCache.get(type); if (!isPresent(parameters)) { @@ -148,7 +154,7 @@ export class StaticReflector implements ReflectorReader { } return parameters; } catch (e) { - console.log(`Failed on type ${type} with error ${e}`); + console.log(`Failed on type ${JSON.stringify(type)} with error ${e}`); throw e; } } @@ -170,10 +176,7 @@ export class StaticReflector implements ReflectorReader { } private initializeConversionMap(): void { - let coreDecorators = 'angular2/src/core/metadata'; - let diDecorators = 'angular2/src/core/di/decorators'; - let diMetadata = 'angular2/src/core/di/metadata'; - let provider = 'angular2/src/core/di/provider'; + const {coreDecorators, diDecorators, diMetadata, provider} = this.host.angularImportLocations(); this.registerDecoratorOrConstructor(this.host.findDeclaration(provider, 'Provider'), Provider); this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), diff --git a/tools/compiler_cli/src/static_reflector_spec.ts b/tools/compiler_cli/src/static_reflector_spec.ts index 7ddc2a6e56..691de4f9ca 100644 --- a/tools/compiler_cli/src/static_reflector_spec.ts +++ b/tools/compiler_cli/src/static_reflector_spec.ts @@ -241,6 +241,14 @@ describe('StaticReflector', () => { class MockReflectorHost implements StaticReflectorHost { private staticTypeCache = new Map(); + angularImportLocations() { + return { + coreDecorators: 'angular2/src/core/metadata', + diDecorators: 'angular2/src/core/di/decorators', + diMetadata: 'angular2/src/core/di/metadata', + provider: 'angular2/src/core/di/provider' + }; + } getStaticSymbol(moduleId: string, declarationFile: string, name: string): StaticSymbol { var cacheKey = `${declarationFile}:${name}`; var result = this.staticTypeCache.get(cacheKey); diff --git a/tools/compiler_cli/src/tsc.ts b/tools/compiler_cli/src/tsc.ts index 9179147470..c762f9f58f 100644 --- a/tools/compiler_cli/src/tsc.ts +++ b/tools/compiler_cli/src/tsc.ts @@ -3,7 +3,7 @@ import * as ts from 'typescript'; import {lstatSync} from 'fs'; import * as path from 'path'; import {AngularCompilerOptions} from './codegen'; -import {CodeGeneratorHost} from './compiler_host'; +import {TsickleHost} from './compiler_host'; /** * Our interface to the TypeScript standard compiler. @@ -14,7 +14,8 @@ export interface CompilerInterface { readConfiguration( project: string, basePath: string): {parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions}; - typeCheckAndEmit(compilerHost: CodeGeneratorHost, oldProgram?: ts.Program): number; + typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void; + emit(compilerHost: ts.CompilerHost, program: ts.Program): number; } const DEBUG = false; @@ -72,27 +73,32 @@ export class Tsc implements CompilerInterface { return {parsed: this.parsed, ngOptions: this.ngOptions}; } - typeCheckAndEmit(compilerHost: CodeGeneratorHost, oldProgram?: ts.Program): number { + typeCheck(compilerHost: ts.CompilerHost, oldProgram: ts.Program): void { + // Create a new program since codegen files were created after making the old program const program = ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost, oldProgram); debug("Checking global diagnostics..."); check(program.getGlobalDiagnostics()); + let diagnostics: ts.Diagnostic[] = []; debug("Type checking..."); - { - let diagnostics: ts.Diagnostic[] = []; - for (let sf of program.getSourceFiles()) { - diagnostics.push(...ts.getPreEmitDiagnostics(program, sf)); - } - check(diagnostics); + + for (let sf of program.getSourceFiles()) { + diagnostics.push(...ts.getPreEmitDiagnostics(program, sf)); } - - debug("Emitting outputs..."); - - const {diagnostics, emitSkipped} = program.emit(); check(diagnostics); + } + + emit(compilerHost: TsickleHost, oldProgram: ts.Program): number { + // Create a new program since the host may be different from the old program. + const program = ts.createProgram(this.parsed.fileNames, this.parsed.options, compilerHost); + debug("Emitting outputs..."); + const emitResult = program.emit(); + let diagnostics: ts.Diagnostic[] = []; + diagnostics.push(...emitResult.diagnostics); + check(compilerHost.diagnostics); - return emitSkipped ? 1 : 0; + return emitResult.emitSkipped ? 1 : 0; } } export var tsc: CompilerInterface = new Tsc(); diff --git a/tools/compiler_cli/test/tsconfig.json b/tools/compiler_cli/test/tsconfig.json index 804c9edac1..e6b1bd0771 100644 --- a/tools/compiler_cli/test/tsconfig.json +++ b/tools/compiler_cli/test/tsconfig.json @@ -2,7 +2,8 @@ "angularCompilerOptions": { // For TypeScript 1.8, we have to lay out generated files // in the same source directory with your code. - "genDir": "." + "genDir": ".", + "legacyPackageLayout": true }, "compilerOptions": { @@ -12,6 +13,7 @@ "moduleResolution": "node", "outDir": "../../../dist/tools/compiler_cli/test/built", "rootDir": "src", + "declaration": true, /** * These options are only needed because the test depends diff --git a/tools/metadata/package.json b/tools/metadata/package.json index 479049e779..04ed22aae2 100644 --- a/tools/metadata/package.json +++ b/tools/metadata/package.json @@ -1,6 +1,6 @@ { "name": "ts-metadata-collector", - "version": "0.1.0", + "version": "0.1.1", "description": "Collects static Decorator metadata from TypeScript sources", "homepage": "https://github.com/angular/angular/tree/master/tools/metadata", "bugs": "https://github.com/angular/angular/issues", @@ -11,6 +11,6 @@ "repository": {"type":"git","url":"https://github.com/angular/angular.git"}, "devDependencies": {}, "peerDependencies": { - "typescript": "^1.8.9 || ^1.9" + "typescript": "^1.8.9 || ^1.9.0-dev" } }