From ce47546188d682e22688793a7b337261f6591d44 Mon Sep 17 00:00:00 2001 From: Abhimanyu Deora Date: Thu, 13 Jul 2017 13:56:12 -0700 Subject: [PATCH] refactor(compiler-cli): add support for browser compiler bundle (#17979) PR Close #17979 --- .../compiler-cli/browser-rollup.config.js | 79 +++++++++++++++++++ packages/compiler-cli/src/ngc.ts | 48 +++++------ packages/compiler-cli/test/ngc_spec.ts | 17 ++-- 3 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 packages/compiler-cli/browser-rollup.config.js diff --git a/packages/compiler-cli/browser-rollup.config.js b/packages/compiler-cli/browser-rollup.config.js new file mode 100644 index 0000000000..1de437b635 --- /dev/null +++ b/packages/compiler-cli/browser-rollup.config.js @@ -0,0 +1,79 @@ +/** + * @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 commonjs from 'rollup-plugin-commonjs'; +import * as path from 'path'; + +import 'reflect-metadata'; + +var m = /^\@angular\/((\w|\-)+)(\/(\w|\d|\/|\-)+)?$/; +var location = normalize('../../dist/packages-dist') + '/'; +var rxjsLocation = normalize('../../node_modules/rxjs'); +var tslibLocation = normalize('../../node_modules/tslib'); +var esm = 'esm/'; + +var locations = { + 'tsc-wrapped': normalize('../../dist/tools/@angular') + '/', + 'compiler-cli': normalize('../../dist/packages') + '/' +}; + +var esm_suffixes = {}; + +function normalize(fileName) { + return path.resolve(__dirname, fileName); +} + +function resolve(id, from) { + // console.log('Resolve id:', id, 'from', from) + if (id == '@angular/tsc-wrapped') { + // Hack to restrict the import to not include the index of @angular/tsc-wrapped so we don't + // rollup tsickle. + return locations['tsc-wrapped'] + 'tsc-wrapped/src/collector.js'; + } + var match = m.exec(id); + if (match) { + var packageName = match[1]; + var esm_suffix = esm_suffixes[packageName] || ''; + var loc = locations[packageName] || location; + var r = loc !== location && (loc + esm_suffix + packageName + (match[3] || '/index') + '.js') || + loc + packageName + '/@angular/' + packageName + '.es5.js'; + // console.log('** ANGULAR MAPPED **: ', r); + return r; + } + if (id && id.startsWith('rxjs/')) { + const resolved = `${rxjsLocation}${id.replace('rxjs', '')}.js`; + return resolved; + } + if (id == 'tslib') { + return tslibLocation + '/tslib.es6.js'; + } +} + +// hack to get around issues with default exports +var banner = `ts['default'] = ts['default'] || ts; fs['default'] = fs['default'] || fs;`; + +export default { + entry: '../../dist/packages-dist/compiler-cli/src/ngc.js', + dest: './browser-bundle.umd.js', + format: 'umd', + moduleName: 'ng.compiler_cli_browser', + exports: 'named', + external: [ + 'fs', + 'path', + 'typescript', + 'reflect-metadata', + ], + globals: { + 'typescript': 'ts', + 'path': 'path', + 'fs': 'fs', + }, + banner: banner, + plugins: [{resolveId: resolve}, commonjs()] +} diff --git a/packages/compiler-cli/src/ngc.ts b/packages/compiler-cli/src/ngc.ts index 3089a9be74..0b71512894 100644 --- a/packages/compiler-cli/src/ngc.ts +++ b/packages/compiler-cli/src/ngc.ts @@ -122,30 +122,14 @@ function getProjectDirectory(project: string): string { return isFile ? path.dirname(project) : project; } -export function main( - args: string[], consoleError: (s: string) => void = console.error, files?: string[], - options?: ts.CompilerOptions, ngOptions?: any): number { +export function performCompilation( + basePath: string, files: string[], options: ts.CompilerOptions, ngOptions: any, + consoleError: (s: string) => void = console.error, tsCompilerHost?: ts.CompilerHost) { try { - const parsedArgs = require('minimist')(args); - const project = parsedArgs.p || parsedArgs.project || '.'; - - const projectDir = getProjectDirectory(project); - - // file names in tsconfig are resolved relative to this absolute path - const basePath = path.resolve(process.cwd(), projectDir); - - if (!files || !options || !ngOptions) { - const {parsed, ngOptions: readNgOptions} = readConfiguration(project, basePath); - if (!files) files = parsed.fileNames; - if (!options) options = parsed.options; - if (!ngOptions) ngOptions = readNgOptions; - } - - // Ignore what the tsconfig.json for baseDir and genDir ngOptions.basePath = basePath; ngOptions.genDir = basePath; - let host = ts.createCompilerHost(options, true); + let host = tsCompilerHost || ts.createCompilerHost(options, true); host.realpath = p => p; const rootFileNames = files.map(f => path.normalize(f)); @@ -189,18 +173,34 @@ export function main( }); } catch (e) { if (isSyntaxError(e)) { + console.error(e.message); consoleError(e.message); return 1; - } else { - consoleError(e.stack); - consoleError('Compilation failed'); - return 2; } } return 0; } + +export function main(args: string[], consoleError: (s: string) => void = console.error): number { + try { + const parsedArgs = require('minimist')(args); + const project = parsedArgs.p || parsedArgs.project || '.'; + + const projectDir = fs.lstatSync(project).isFile() ? path.dirname(project) : project; + + // file names in tsconfig are resolved relative to this absolute path + const basePath = path.resolve(process.cwd(), projectDir); + const {parsed, ngOptions} = readConfiguration(project, basePath); + return performCompilation(basePath, parsed.fileNames, parsed.options, ngOptions, consoleError); + } catch (e) { + 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))); diff --git a/packages/compiler-cli/test/ngc_spec.ts b/packages/compiler-cli/test/ngc_spec.ts index f5f3a24e03..2c272d0c96 100644 --- a/packages/compiler-cli/test/ngc_spec.ts +++ b/packages/compiler-cli/test/ngc_spec.ts @@ -11,7 +11,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import {main} from '../src/ngc'; +import {main, performCompilation} from '../src/ngc'; function getNgRootDir() { const moduleFilename = module.filename.replace(/\\/g, '/'); @@ -41,7 +41,7 @@ describe('ngc command-line', () => { write('tsconfig-base.json', `{ "compilerOptions": { "experimentalDecorators": true, - "skipLibCheck": true, + "skipLibCheck": true, "types": [], "outDir": "built", "declaration": true, @@ -81,8 +81,8 @@ describe('ngc command-line', () => { spyOn(mockConsole, 'error'); - const result = main( - ['-p', basePath], mockConsole.error, [path.join(basePath, 'test.ts')], { + const result = performCompilation( + basePath, [path.join(basePath, 'test.ts')], { experimentalDecorators: true, skipLibCheck: true, types: [], @@ -91,7 +91,7 @@ describe('ngc command-line', () => { module: ts.ModuleKind.ES2015, moduleResolution: ts.ModuleResolutionKind.NodeJs, }, - {}); + {}, mockConsole.error); expect(mockConsole.error).not.toHaveBeenCalled(); expect(result).toBe(0); }); @@ -419,9 +419,8 @@ describe('ngc command-line', () => { export class FlatModule { }`); - const exitCode = main( - ['-p', path.join(basePath, 'tsconfig.json')], undefined, - [path.join(basePath, 'public-api.ts')], { + const exitCode = performCompilation( + basePath, [path.join(basePath, 'public-api.ts')], { target: ts.ScriptTarget.ES5, experimentalDecorators: true, noImplicitAny: true, @@ -439,6 +438,8 @@ describe('ngc command-line', () => { flatModuleOutFile: 'index.js', skipTemplateCodegen: true }); + + expect(exitCode).toEqual(0); shouldExist('index.js'); shouldExist('index.metadata.json');