From f0477e164aa9b5a21b36ad73672265a22c94dad4 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Fri, 10 Apr 2015 13:28:35 -0700 Subject: [PATCH] chore(build): add typescript to the cjs build. Refactor the file extension logic in traceur plugin to simplify --- Brocfile-js_cjs.js | 42 ++++++++++++-------- Brocfile-js_dev.js | 9 +---- Brocfile-js_prod.js | 9 +---- gulpfile.js | 4 -- tools/broccoli/traceur/index.js | 15 +++----- tools/broccoli/traceur/index.ts | 18 ++++----- tools/broccoli/typescript/index.js | 61 ++++++++++++++++++++++++++++++ tools/broccoli/typescript/index.ts | 51 +++++++++++++++++++++++++ 8 files changed, 154 insertions(+), 55 deletions(-) create mode 100644 tools/broccoli/typescript/index.js create mode 100644 tools/broccoli/typescript/index.ts diff --git a/Brocfile-js_cjs.js b/Brocfile-js_cjs.js index 1c641f9437..c4c12edab2 100644 --- a/Brocfile-js_cjs.js +++ b/Brocfile-js_cjs.js @@ -2,12 +2,14 @@ var Funnel = require('broccoli-funnel'); var mergeTrees = require('broccoli-merge-trees'); var stew = require('broccoli-stew'); var TraceurCompiler = require('./tools/broccoli/traceur'); +var TypescriptCompiler = require('./tools/broccoli/typescript'); var renderLodashTemplate = require('broccoli-lodash'); -var modulesTree = new Funnel('modules', {include: ['**/**'], exclude: ['angular2/src/core/zone/vm_turn_zone.es6']}); +var modulesTree = new Funnel( + 'modules', {include: ['**/**'], exclude: ['angular2/src/core/zone/vm_turn_zone.es6']}); -// Use Traceur to transpile original sources to ES6 -var cjsTree = new TraceurCompiler(modulesTree, '.js', { +// Use Traceur to transpile original sources to ES5 +var traceurOpts = { sourceMaps: true, annotations: true, // parse annotations types: true, // parse types @@ -17,18 +19,13 @@ var cjsTree = new TraceurCompiler(modulesTree, '.js', { // Don't use type assertions since this is partly transpiled by typescript typeAssertions: false, modules: 'commonjs' -}, true); +}; +var cjsTree = new TraceurCompiler(modulesTree, '.js', '.map', traceurOpts); -// Munge the filenames since we use an '.es6' extension -cjsTree = stew.rename(cjsTree, function(relativePath) { - return relativePath.replace(/\.(js|es6)\.map$/, '.map').replace(/\.es6$/, '.js'); -}); - -// Now we add a few more files to the es6 tree that Traceur should not see +// Add the LICENSE file in each module ['angular2', 'benchmarks', 'benchmarks_external', 'benchpress', 'examples', 'rtts_assert'].forEach( function(destDir) { - var extras = new Funnel('.', {files: ['LICENSE'], destDir: destDir}); - cjsTree = mergeTrees([cjsTree, extras]); + cjsTree = mergeTrees([cjsTree, new Funnel('.', {files: ['LICENSE'], destDir: destDir})]); }); extras = new Funnel(modulesTree, {include: ['**/*.md', '**/*.png'], exclude: ['**/*.dart.md']}); @@ -53,12 +50,25 @@ var COMMON_PACKAGE_JSON = { }; // Add a .template extension since renderLodashTemplate strips one extension -var packageJsons = stew.rename(new Funnel(modulesTree, {include: ['**/package.json']}), '.json', '.json.template'); -packageJsons = renderLodashTemplate(packageJsons, { - files: ["**/**"], - context: { 'packageJson': COMMON_PACKAGE_JSON } +var packageJsons = + stew.rename(new Funnel(modulesTree, {include: ['**/package.json']}), '.json', '.json.template'); +packageJsons = renderLodashTemplate( + packageJsons, {files: ["**/**"], context: {'packageJson': COMMON_PACKAGE_JSON}}); + +var typescriptTree = new TypescriptCompiler(modulesTree, { + target: 'ES5', + sourceMap: true, + mapRoot: '', /* force sourcemaps to use relative path */ + module: /*system.js*/'commonjs', + allowNonTsExtensions: false, + typescript: require('typescript'), + //declarationFiles: true, + noEmitOnError: true, + outDir: 'angular2' }); +// For now, we just overwrite the Traceur-compiled files with their Typescript equivalent +cjsTree = mergeTrees([cjsTree, typescriptTree], { overwrite: true }); cjsTree = mergeTrees([cjsTree, extras, packageJsons]); module.exports = stew.mv(cjsTree, 'js/cjs'); diff --git a/Brocfile-js_dev.js b/Brocfile-js_dev.js index 96457eb2d2..848c27185f 100644 --- a/Brocfile-js_dev.js +++ b/Brocfile-js_dev.js @@ -10,7 +10,7 @@ var path = require('path'); var modulesTree = new Funnel('modules', {include: ['**/**'], exclude: ['**/*.cjs'], destDir: '/'}); // Use Traceur to transpile original sources to ES6 -var es6DevTree = new TraceurCompiler(modulesTree, '.es6', { +var es6DevTree = new TraceurCompiler(modulesTree, '.es6', '.map', { sourceMaps: true, annotations: true, // parse annotations types: true, // parse types @@ -21,14 +21,9 @@ var es6DevTree = new TraceurCompiler(modulesTree, '.es6', { typeAssertions: true, outputLanguage: 'es6' }); -// Munge the filenames since we use an '.es6' extension -es6DevTree = stew.rename(es6DevTree, function(relativePath) { - return relativePath.replace(/\.(js|es6)\.map$/, '.map').replace(/\.js$/, '.es6'); -}); // Call Traceur again to lower the ES6 build tree to ES5 -var es5DevTree = new TraceurCompiler(es6DevTree, '.js', {modules: 'instantiate', sourceMaps: true}); -es5DevTree = stew.rename(es5DevTree, '.es6.map', '.js.map'); +var es5DevTree = new TraceurCompiler(es6DevTree, '.js', '.js.map', {modules: 'instantiate', sourceMaps: true}); // Now we add a few more files to the es6 tree that Traceur should not see ['angular2', 'benchmarks', 'benchmarks_external', 'benchpress', 'examples', 'rtts_assert'].forEach( diff --git a/Brocfile-js_prod.js b/Brocfile-js_prod.js index 90a9652ee3..3b89cbeac0 100644 --- a/Brocfile-js_prod.js +++ b/Brocfile-js_prod.js @@ -10,7 +10,7 @@ var path = require('path'); var modulesTree = new Funnel('modules', {include: ['**/**'], destDir: '/'}); // Use Traceur to transpile original sources to ES6 -var es6ProdTree = new TraceurCompiler(modulesTree, '.es6', { +var es6ProdTree = new TraceurCompiler(modulesTree, '.es6', '.map', { sourceMaps: true, annotations: true, // parse annotations types: true, // parse types @@ -21,14 +21,9 @@ var es6ProdTree = new TraceurCompiler(modulesTree, '.es6', { typeAssertions: false, outputLanguage: 'es6' }); -// Munge the filenames since we use an '.es6' extension -es6ProdTree = stew.rename(es6ProdTree, function(relativePath) { - return relativePath.replace(/\.(js|es6)\.map$/, '.map').replace(/\.js$/, '.es6'); -}); // Call Traceur again to lower the ES6 build tree to ES5 -var es5ProdTree = new TraceurCompiler(es6ProdTree, '.js', {modules: 'instantiate', sourceMaps: true}); -es5ProdTree = stew.rename(es5ProdTree, '.es6.map', '.js.map'); +var es5ProdTree = new TraceurCompiler(es6ProdTree, '.js', '.js.map', {modules: 'instantiate', sourceMaps: true}); // Now we add a few more files to the es6 tree that Traceur should not see ['angular2', 'benchmarks', 'benchmarks_external', 'benchpress', 'examples', 'rtts_assert'].forEach( diff --git a/gulpfile.js b/gulpfile.js index 133f457524..10a40a1e17 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -761,10 +761,6 @@ gulp.task('broccoli.js.cjs', function() { gulp.task('build.js.cjs', function(done) { runSequence( 'broccoli.js.cjs', - //['build/transpile.js.cjs', 'build/copy.js.cjs', 'build/multicopy.js.cjs'], - // Overwrite the .js.cjs transpilation with typescript outputs - // We still need traceur outputs everywhere else, for now. - 'build/transpile.ts.cjs', ['build/linknodemodules.js.cjs'], 'build/transformCJSTests', done diff --git a/tools/broccoli/traceur/index.js b/tools/broccoli/traceur/index.js index dc1dc47b29..f90359bdaf 100644 --- a/tools/broccoli/traceur/index.js +++ b/tools/broccoli/traceur/index.js @@ -13,14 +13,12 @@ var Writer = require('broccoli-writer'); var xtend = require('xtend'); var TraceurFilter = (function (_super) { __extends(TraceurFilter, _super); - function TraceurFilter(inputTree, destExtension, options, hackSourceMapExtension) { - if (destExtension === void 0) { destExtension = '.js'; } + function TraceurFilter(inputTree, destExtension, destSourceMapExtension, options) { if (options === void 0) { options = {}; } - if (hackSourceMapExtension === void 0) { hackSourceMapExtension = false; } this.inputTree = inputTree; this.destExtension = destExtension; + this.destSourceMapExtension = destSourceMapExtension; this.options = options; - this.hackSourceMapExtension = hackSourceMapExtension; } TraceurFilter.prototype.write = function (readTree, destDir) { var _this = this; @@ -39,16 +37,13 @@ var TraceurFilter = (function (_super) { var result = traceur.compile(options, filepath, sourcecode); // TODO: we should fix the sourceMappingURL written by Traceur instead of overriding // (but we might switch to typescript first) - var url = path.basename(filepath).replace(/\.es6$/, '') + (_this.destExtension === '.js' ? '.js.map' : '.map'); - if (_this.hackSourceMapExtension) { - url = path.basename(filepath).replace(/\.\w+$/, '') + '.map'; - } - result.js = result.js + ("\n//# sourceMappingURL=./" + url); + var mapFilepath = filepath.replace(/\.\w+$/, '') + _this.destSourceMapExtension; + result.js = result.js + ("\n//# sourceMappingURL=./" + path.basename(mapFilepath)); var destFilepath = filepath.replace(/\.\w+$/, _this.destExtension); var destFile = path.join(destDir, destFilepath); fse.mkdirsSync(path.dirname(destFile)); - var destMap = path.join(destDir, destFilepath + '.map'); fs.writeFileSync(destFile, result.js, fsOpts); + var destMap = path.join(destDir, mapFilepath); result.sourceMap.file = destFilepath; fs.writeFileSync(destMap, JSON.stringify(result.sourceMap), fsOpts); }); diff --git a/tools/broccoli/traceur/index.ts b/tools/broccoli/traceur/index.ts index 14f4f2c6ef..b0c3acc496 100644 --- a/tools/broccoli/traceur/index.ts +++ b/tools/broccoli/traceur/index.ts @@ -7,9 +7,11 @@ var Writer = require('broccoli-writer'); var xtend = require('xtend'); class TraceurFilter extends Writer { - constructor(private inputTree, private destExtension: string = '.js', - private options = {}, private hackSourceMapExtension: boolean = false) {} static RUNTIME_PATH = traceur.RUNTIME_PATH; + + constructor(private inputTree, private destExtension: string, + private destSourceMapExtension: string, private options = {}) {} + write(readTree, destDir) { return readTree(this.inputTree) .then(srcDir => { @@ -29,21 +31,15 @@ class TraceurFilter extends Writer { // TODO: we should fix the sourceMappingURL written by Traceur instead of overriding // (but we might switch to typescript first) - var url = path.basename(filepath).replace(/\.es6$/, '') + - (this.destExtension === '.js' ? '.js.map' : '.map'); - if (this.hackSourceMapExtension) { - url = path.basename(filepath).replace(/\.\w+$/, '') + '.map'; - } - result.js = result.js + `\n//# sourceMappingURL=./${url}`; + var mapFilepath = filepath.replace(/\.\w+$/, '') + this.destSourceMapExtension; + result.js = result.js + `\n//# sourceMappingURL=./${path.basename(mapFilepath)}`; var destFilepath = filepath.replace(/\.\w+$/, this.destExtension); var destFile = path.join(destDir, destFilepath); fse.mkdirsSync(path.dirname(destFile)); - var destMap = path.join(destDir, destFilepath + '.map'); - - fs.writeFileSync(destFile, result.js, fsOpts); + var destMap = path.join(destDir, mapFilepath); result.sourceMap.file = destFilepath; fs.writeFileSync(destMap, JSON.stringify(result.sourceMap), fsOpts); }); diff --git a/tools/broccoli/typescript/index.js b/tools/broccoli/typescript/index.js new file mode 100644 index 0000000000..1a61710a7d --- /dev/null +++ b/tools/broccoli/typescript/index.js @@ -0,0 +1,61 @@ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var fs = require('fs'); +var path = require('path'); +var ts = require('typescript'); +var walkSync = require('walk-sync'); +var Writer = require('broccoli-writer'); +var xtend = require('xtend'); +var TSCompiler = (function (_super) { + __extends(TSCompiler, _super); + function TSCompiler(inputTree, options) { + if (options === void 0) { options = {}; } + _super.call(this, inputTree, options); + this.inputTree = inputTree; + this.options = options; + } + TSCompiler.prototype.write = function (readTree, destDir) { + var options = xtend({ + outDir: destDir + }, this.options); + if (this.options.outDir) { + options.outDir = path.resolve(destDir, options.outDir); + } + if (options.out) { + options.out = path.resolve(destDir, options.out); + } + options.target = ts.ScriptTarget[options.target]; + return readTree(this.inputTree).then(function (srcDir) { + var files = walkSync(srcDir).filter(function (filepath) { + return path.extname(filepath).toLowerCase() === '.ts'; + }).map(function (filepath) { + return path.resolve(srcDir, filepath); + }); + if (files.length > 0) { + var program = ts.createProgram(files, options); + var emitResult = program.emit(); + var allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + var errMsg = ''; + allDiagnostics.forEach(function (diagnostic) { + var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + if (!diagnostic.file) { + errMsg += "\n" + message; + return; + } + var _a = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start), line = _a.line, character = _a.character; + errMsg += "\n" + diagnostic.file.fileName + " (" + (line + 1) + "," + (character + 1) + "): " + message; + }); + if (emitResult.emitSkipped) { + throw new Error(errMsg); + } + } + }); + }; + return TSCompiler; +})(Writer); +module.exports = TSCompiler; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/tools/broccoli/typescript/index.ts b/tools/broccoli/typescript/index.ts new file mode 100644 index 0000000000..04153814c5 --- /dev/null +++ b/tools/broccoli/typescript/index.ts @@ -0,0 +1,51 @@ +var fs = require('fs'); +var path = require('path'); +var ts = require('typescript'); +var walkSync = require('walk-sync'); +var Writer = require('broccoli-writer'); +var xtend = require('xtend'); + +class TSCompiler extends Writer { + constructor(private inputTree, private options = {}) { super(inputTree, options); } + + write(readTree, destDir) { + var options: ts.CompilerOptions = xtend({outDir: destDir}, this.options); + if (this.options.outDir) { + options.outDir = path.resolve(destDir, options.outDir); + } + if (options.out) { + options.out = path.resolve(destDir, options.out); + } + options.target = ts.ScriptTarget[options.target]; + return readTree(this.inputTree) + .then(srcDir => { + var files = walkSync(srcDir) + .filter(filepath => path.extname(filepath).toLowerCase() === '.ts') + .map(filepath => path.resolve(srcDir, filepath)); + + if (files.length > 0) { + var program = ts.createProgram(files, options); + var emitResult = program.emit(); + + var allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + + var errMsg = ''; + allDiagnostics.forEach(diagnostic => { + var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + if (!diagnostic.file) { + errMsg += `\n${message}`; + return; + } + var {line, character} = + diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + errMsg += `\n${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`; + }); + + if (emitResult.emitSkipped) { + throw new Error(errMsg); + } + } + }); + } +} +module.exports = TSCompiler;