From db95fd6ca9006a5ee3969705a362ec9cb772fe39 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Fri, 29 Apr 2016 10:35:36 -0700 Subject: [PATCH] build(offline compiler): package the compiler-cli for users Closes #8341 --- gulpfile.js | 11 ++++- tools/compiler_cli/README.md | 27 ++++++++++- tools/compiler_cli/package.json | 37 +++++++++++++++ tools/compiler_cli/src/codegen.ts | 2 +- tools/compiler_cli/src/index.ts | 58 ++---------------------- tools/compiler_cli/src/main.ts | 56 +++++++++++++++++++++++ tools/compiler_cli/src/reflector_host.ts | 4 +- 7 files changed, 133 insertions(+), 62 deletions(-) create mode 100644 tools/compiler_cli/package.json create mode 100644 tools/compiler_cli/src/main.ts diff --git a/gulpfile.js b/gulpfile.js index cd85d84a79..6ddde03900 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -300,7 +300,14 @@ function doCheckFormat() { var clangFormat = require('clang-format'); var gulpFormat = require('gulp-clang-format'); - return gulp.src(['modules/**/*.ts', 'tools/**/*.ts', '!**/typings/**/*.d.ts', 'gulpfile.js']) + return gulp.src([ + 'modules/**/*.ts', + 'tools/**/*.ts', + '!**/typings/**/*.d.ts', + // workaround https://github.com/angular/clang-format/issues/28 + '!tools/compiler_cli/src/main.ts', + 'gulpfile.js' + ]) .pipe(gulpFormat.checkFormat('file', clangFormat)); } @@ -1051,7 +1058,7 @@ gulp.task('!build.compiler_cli', ['build.js.cjs'], gulp.task('!test.compiler_cli.codegen', function(done) { try { - require('./dist/js/cjs/compiler_cli') + require('./dist/js/cjs/compiler_cli/main') .main("tools/compiler_cli/test") .then(function() { runTsc('tools/compiler_cli/test', done); }) .catch(function(rej) { done(new Error(rej)); }); diff --git a/tools/compiler_cli/README.md b/tools/compiler_cli/README.md index 1479367baf..e0a1ef0441 100644 --- a/tools/compiler_cli/README.md +++ b/tools/compiler_cli/README.md @@ -10,16 +10,31 @@ requires that the compiler be included in the code downloaded to the client. You can produce smaller, faster applications by running Angular's compiler as a build step, and then downloading only the executable JS to the client. +## Install and use + +``` +$ npm install angular2-template-compiler typescript rxjs +# Optional sanity check, make sure TypeScript can compile +$ ./node_modules/.bin/tsc -p path/to/project +$ ./node_modules/.bin/ng2tc -p path/to/project +``` + +In order to write a `bootstrap` that imports the generated code, you should first write your +top-level component, and run `ng2tc` once to produce a generated `.ngfactory.ts` file. +Then you can add an import statement in the `bootstrap` allowing you to bootstrap off the +generated code. + ## Configuration -The `tsconfig.json` file is expected to contain an additional configuration block: +The `tsconfig.json` file may contain an additional configuration block: ``` "angularCompilerOptions": { "genDir": "." } ``` the `genDir` option controls the path (relative to `tsconfig.json`) where the generated file tree -will be written. More options may be added as we implement more features. +will be written. If `genDir` is not set, then the code will be generated in the source tree, next +to your original sources. More options may be added as we implement more features. We recommend you avoid checking generated files into version control. This permits a state where the generated files in the repository were created from sources that were never checked in, @@ -70,3 +85,11 @@ gulp build.js.cjs # Run it on the test project node ./dist/js/cjs/compiler_cli -p tools/compiler_cli/test ``` + +Release: +``` +$ gulp test.compiler_cli +$ cp tools/compiler_cli/README.md tools/compiler_cli/package.json dist/js/cjs/compiler_cli +# npm login as angularcore +$ npm publish dist/js/cjs/compiler_cli +``` diff --git a/tools/compiler_cli/package.json b/tools/compiler_cli/package.json new file mode 100644 index 0000000000..3bc9094520 --- /dev/null +++ b/tools/compiler_cli/package.json @@ -0,0 +1,37 @@ +{ + "name": "angular2-template-compiler", + "version": "0.1.3", + "description": "Execute angular2 template compiler in nodejs.", + "main": "index.js", + "typings": "index.d.ts", + "bin": { + "ng2tc": "./main.js" + }, + "dependencies": { + "angular2": "angular/angular#2.0.0-build.cacdead.js", + "ts-metadata-collector": "^0.1.0", + "tsickle": "^0.1.0", + "reflect-metadata": "^0.1.2", + "parse5": "1.3.2" + }, + "peerDependencies": { + "typescript": "^1.8.0 || ^1.9.0-dev" + }, + "repository": { + "type": "git", + "url": "https://github.com/angular/angular.git" + }, + "keywords": [ + "angular", + "compiler" + ], + "contributors": [ + "Tobias Bosch (https://angular.io/)", + "Alex Eagle (https://angular.io/)" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/angular/angular/issues" + }, + "homepage": "https://github.com/angular/angular/tree/master/tools/compiler_cli" +} diff --git a/tools/compiler_cli/src/codegen.ts b/tools/compiler_cli/src/codegen.ts index 4f80b6ddca..809a1f0496 100644 --- a/tools/compiler_cli/src/codegen.ts +++ b/tools/compiler_cli/src/codegen.ts @@ -79,7 +79,7 @@ export class CodeGenerator { return result; } - codegen() { + codegen(): Promise { Parse5DomAdapter.makeCurrent(); const generateOneFile = (absSourcePath: string) => Promise.all(this.readComponents(absSourcePath)) diff --git a/tools/compiler_cli/src/index.ts b/tools/compiler_cli/src/index.ts index acc0f9c948..b2c5fb2aa5 100644 --- a/tools/compiler_cli/src/index.ts +++ b/tools/compiler_cli/src/index.ts @@ -1,55 +1,3 @@ -// TODO(alexeagle): use --lib=node when available; remove this reference -// https://github.com/Microsoft/TypeScript/pull/7757#issuecomment-205644657 -/// - -// Must be imported first, because angular2 decorators throws on load. -import 'reflect-metadata'; - -import * as fs from 'fs'; -import * as path from 'path'; -import * as ts from 'typescript'; -import {tsc, check} from './tsc'; - -import {CodeGenerator} from './codegen'; - -const DEBUG = false; - -function debug(msg: string, ...o: any[]) { - if (DEBUG) console.log(msg, ...o); -} - -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); - - // read the configuration options from wherever you store them - const {parsed, ngOptions} = tsc.readConfiguration(project, basePath); - - const host = ts.createCompilerHost(parsed.options, true); - const {errors, generator} = CodeGenerator.create(ngOptions, parsed, basePath, host); - check(errors); - - 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); - }); -} - -// 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); - } -} +export {CodeGenerator} from './codegen'; +export {NodeReflectorHost} from './reflector_host'; +export {wrapCompilerHost, CodeGeneratorHost} from './compiler_host'; diff --git a/tools/compiler_cli/src/main.ts b/tools/compiler_cli/src/main.ts new file mode 100644 index 0000000000..1f4238d83f --- /dev/null +++ b/tools/compiler_cli/src/main.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env node +// TODO(alexeagle): use --lib=node when available; remove this reference +// https://github.com/Microsoft/TypeScript/pull/7757#issuecomment-205644657 +/// + +// Must be imported first, because angular2 decorators throws on load. +import 'reflect-metadata'; + +import * as fs from 'fs'; +import * as path from 'path'; +import * as ts from 'typescript'; +import {tsc, check} from './tsc'; + +import {CodeGenerator} from './codegen'; + +const DEBUG = false; + +function debug(msg: string, ...o: any[]) { + if (DEBUG) console.log(msg, ...o); +} + +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); + + // read the configuration options from wherever you store them + const {parsed, ngOptions} = tsc.readConfiguration(project, basePath); + + const host = ts.createCompilerHost(parsed.options, true); + const {errors, generator} = CodeGenerator.create(ngOptions, parsed, basePath, host); + check(errors); + + 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); + }); +} + +// 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); + } +} diff --git a/tools/compiler_cli/src/reflector_host.ts b/tools/compiler_cli/src/reflector_host.ts index 5e9168983c..32bea8d650 100644 --- a/tools/compiler_cli/src/reflector_host.ts +++ b/tools/compiler_cli/src/reflector_host.ts @@ -24,7 +24,7 @@ export class NodeReflectorHost implements StaticReflectorHost { * they are resolvable by the moduleResolution strategy from the CompilerHost. */ private getModuleId(declarationFile: string, containingFile: string) { - const parts = declarationFile.replace(EXT, '').split(path.sep); + const parts = declarationFile.replace(EXT, '').split(path.sep).filter(p => !!p); for (let index = parts.length - 1; index >= 0; index--) { let candidate = parts.slice(index, parts.length).join(path.sep); @@ -51,7 +51,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 = 'index.ts'; + containingFile = path.join(this.compilerHost.getCurrentDirectory(), 'index.ts'); } try {