From 896a0457f804fff71412191f86371fe9005e53b0 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Mon, 13 Apr 2015 16:39:47 -0700 Subject: [PATCH] refactor(build): Better encapsulate the broccoli builder. --- gulpfile.js | 18 +- tools/broccoli/broccoli_builder.ts | 90 +++++++++ tools/broccoli/gulp/index.ts | 66 ------- tools/broccoli/make-broccoli-tree.ts | 275 --------------------------- tools/broccoli/trees/browser_tree.ts | 120 ++++++++++++ tools/broccoli/trees/dart_tree.ts | 47 +++++ tools/broccoli/trees/node_tree.ts | 109 +++++++++++ 7 files changed, 376 insertions(+), 349 deletions(-) create mode 100644 tools/broccoli/broccoli_builder.ts delete mode 100644 tools/broccoli/gulp/index.ts delete mode 100644 tools/broccoli/make-broccoli-tree.ts create mode 100644 tools/broccoli/trees/browser_tree.ts create mode 100644 tools/broccoli/trees/dart_tree.ts create mode 100644 tools/broccoli/trees/node_tree.ts diff --git a/gulpfile.js b/gulpfile.js index db28fcc0b6..f2fe664986 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,7 +36,7 @@ var insert = require('gulp-insert'); function missingDynamicBroccoli() { throw new Error('ERROR: build.broccoli.tools task should have been run before using broccoli'); } -var makeBroccoliTree = missingDynamicBroccoli, broccoliBuild = missingDynamicBroccoli; +var getBroccoli = missingDynamicBroccoli; // Note: when DART_SDK is not found, all gulp tasks ending with `.dart` will be skipped. @@ -295,7 +295,7 @@ gulp.task('build/clean.docs', clean(gulp, gulpPlugins, { // transpile gulp.task('build/transpile.dart', ['build.broccoli.tools'], function() { - return broccoliBuild(makeBroccoliTree('dart'), 'dart'); + return getBroccoli().forDartTree().buildOnce(); }); // ------------ @@ -609,7 +609,7 @@ gulp.task('test.transpiler.unittest', function() { return gulp.src('tools/transpiler/unittest/**/*.js') .pipe(jasmine({ includeStackTrace: true - })) + })); }); // ----------------- @@ -641,17 +641,19 @@ gulp.task('build.broccoli.tools', function() { .pipe(tsc({ target: 'ES5', module: 'commonjs' })); return tsResult.js.pipe(gulp.dest('dist/broccoli')) .on('end', function() { - makeBroccoliTree = require('./dist/broccoli/make-broccoli-tree'); - broccoliBuild = require('./dist/broccoli/gulp'); + var BroccoliBuilder = require('./dist/broccoli/broccoli_builder').BroccoliBuilder; + getBroccoli = function() { + return BroccoliBuilder; + }; }); }); gulp.task('broccoli.js.dev', ['build.broccoli.tools'], function() { - return broccoliBuild(makeBroccoliTree('dev'), path.join('js', 'dev')); + return getBroccoli().forDevTree().buildOnce(); }); gulp.task('broccoli.js.prod', ['build.broccoli.tools'], function() { - return broccoliBuild(makeBroccoliTree('prod'), path.join('js', 'prod')); + return getBroccoli().forProdTree().buildOnce(); }); gulp.task('build.js.dev', function(done) { @@ -666,7 +668,7 @@ gulp.task('build.js.dev', function(done) { gulp.task('build.js.prod', ['broccoli.js.prod']); gulp.task('broccoli.js.cjs', ['build.broccoli.tools'], function() { - return broccoliBuild(makeBroccoliTree('cjs'), path.join('js', 'cjs')); + return getBroccoli().forNodeTree().buildOnce(); }); gulp.task('build.js.cjs', function(done) { runSequence( diff --git a/tools/broccoli/broccoli_builder.ts b/tools/broccoli/broccoli_builder.ts new file mode 100644 index 0000000000..3d11899111 --- /dev/null +++ b/tools/broccoli/broccoli_builder.ts @@ -0,0 +1,90 @@ +var broccoli = require('broccoli'); +var destCopy = require('./broccoli-dest-copy'); +var fse = require('fs-extra'); +var makeBrowserTree = require('./trees/browser_tree'); +var makeNodeTree = require('./trees/node_tree'); +var makeDartTree = require('./trees/dart_tree'); +var path = require('path'); +var printSlowTrees = require('broccoli-slow-trees'); + +/** + * Helper for building with broccoli. + */ +export class BroccoliBuilder { + private builder; + private broccoliExecuted: {[s: string]: boolean} = {}; + + // Named constructors + static forDevTree(): BroccoliBuilder { + return new BroccoliBuilder(makeBrowserTree({name: 'dev', typeAssertions: true}), + path.join('js', 'dev')); + } + static forNodeTree(): BroccoliBuilder { + return new BroccoliBuilder(makeNodeTree(), path.join('js', 'cjs')); + } + static forProdTree(): BroccoliBuilder { + return new BroccoliBuilder(makeBrowserTree({name: 'prod', typeAssertions: false}), + path.join('js', 'prod')); + } + static forDartTree(): BroccoliBuilder { return new BroccoliBuilder(makeDartTree(), 'dart'); } + + constructor(tree, private outputRoot: string) { + if (this.broccoliExecuted[outputRoot]) { + throw new Error('The broccoli task can be called only once for outputRoot ' + outputRoot); + } + this.broccoliExecuted[outputRoot] = true; + + var distPath = path.join('dist', outputRoot); + + // We do a clean build every time to avoid stale outputs. + // Broccoli's cache folders allow it to remain incremental without reading this dir. + fse.removeSync(distPath); + + tree = destCopy(tree, 'dist'); + + this.builder = new broccoli.Builder(tree); + } + + doBuild(): Promise { + return this.builder.build() + .then(hash => + { + console.log('=== Stats for %s (total: %ds) ===', this.outputRoot, + Math.round(hash.totalTime / 1000000) / 1000); + printSlowTrees(hash.graph); + }) + .catch(err => { + // Should show file and line/col if present + if (err.file) { + console.error('File: ' + err.file); + } + if (err.stack) { + console.error(err.stack); + } else { + console.error(err); + } + throw err; + }); + } + + buildOnce(): Promise { + // es6-promise doesn't have finally() + return (this.doBuild()) + .finally(() => + { + this.time('Build cleanup', () => this.builder.cleanup()); + console.log('=== Done building %s ===', this.outputRoot); + }) + .catch(err => { + console.error('\nBuild failed'); + process.exit(1); + }); + } + + time(label, work) { + var start = Date.now(); + work(); + var duration = Date.now() - start; + console.log("%s: %dms", label, duration); + } +} diff --git a/tools/broccoli/gulp/index.ts b/tools/broccoli/gulp/index.ts deleted file mode 100644 index b9b5f35e4f..0000000000 --- a/tools/broccoli/gulp/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -var broccoli = require('broccoli'); -var destCopy = require('../broccoli-dest-copy'); -var fse = require('fs-extra'); -var path = require('path'); -var printSlowTrees = require('broccoli-slow-trees'); - - -module.exports = broccoliBuild; -var broccoliExecuted = {}; - -/** - * Integration point to run a broccoli build pipeline under gulp. - * This is mostly derived from the broccoli-cli - * @param tree a broccoli tree, obtained by calling `require` on the Brocfile - * @param outputRoot the path under 'dist' owned exclusively by this Brocfile - * @returns the promise to return back to gulp - */ -function broccoliBuild(tree, outputRoot) { - if (broccoliExecuted.hasOwnProperty(outputRoot)) { - throw new Error('The broccoli task can be called only once for outputRoot ' + outputRoot); - } - broccoliExecuted[outputRoot] = true; - - var distPath = path.join('dist', outputRoot); - - // We do a clean build every time to avoid stale outputs. - // Broccoli's cache folders allow it to remain incremental without reading this dir. - fse.removeSync(distPath); - - tree = destCopy(tree, 'dist'); - - var builder = new broccoli.Builder(tree); - return builder.build() - .then(hash => - { - console.log('=== Stats for %s (total: %ds) ===', outputRoot, - Math.round(hash.totalTime / 1000000) / 1000); - printSlowTrees(hash.graph); - }) - .finally(() => - { - time('Build cleanup', () => builder.cleanup()); - console.log('=== Done building %s ===', outputRoot); - }) - .catch(err => { - // Should show file and line/col if present - if (err.file) { - console.error('File: ' + err.file); - } - if (err.stack) { - console.error(err.stack); - } else { - console.error(err); - } - console.error('\nBuild failed'); - process.exit(1); - }); -} - - -function time(label, work) { - var start = Date.now(); - work(); - var duration = Date.now() - start; - console.log("%s: %dms", label, duration); -} diff --git a/tools/broccoli/make-broccoli-tree.ts b/tools/broccoli/make-broccoli-tree.ts deleted file mode 100644 index 00f6907217..0000000000 --- a/tools/broccoli/make-broccoli-tree.ts +++ /dev/null @@ -1,275 +0,0 @@ -'use strict'; - -var Funnel = require('broccoli-funnel'); -var flatten = require('broccoli-flatten'); -var htmlReplace = require('./html-replace'); -var mergeTrees = require('broccoli-merge-trees'); -var path = require('path'); -var renderLodashTemplate = require('broccoli-lodash'); -var replace = require('broccoli-replace'); -var stew = require('broccoli-stew'); -var ts2dart = require('./broccoli-ts2dart'); -var TraceurCompiler = require('./traceur'); -var TypescriptCompiler = require('./typescript'); - -var projectRootDir = path.normalize(path.join(__dirname, '..', '..')); - - -module.exports = function makeBroccoliTree(name) { - switch (name) { - case 'dev': - return makeBrowserTree({name: name, typeAssertions: true}); - case 'prod': - return makeBrowserTree({name: name, typeAssertions: false}); - case 'cjs': - return makeCjsTree(); - case 'dart': - return makeDartTree(); - default: - throw new Error('Unknown build type: ' + name); - } -}; - - -function makeBrowserTree(options) { - var modulesTree = new Funnel( - 'modules', - {include: ['**/**'], exclude: ['**/*.cjs', 'benchmarks/e2e_test/**'], destDir: '/'}); - - // Use Traceur to transpile original sources to ES6 - var es6Tree = new TraceurCompiler(modulesTree, '.es6', '.map', { - sourceMaps: true, - annotations: true, // parse annotations - types: true, // parse types - script: false, // parse as a module - memberVariables: true, // parse class fields - modules: 'instantiate', - typeAssertionModule: 'rtts_assert/rtts_assert', - typeAssertions: options.typeAssertions, - outputLanguage: 'es6' - }); - - - // Call Traceur again to lower the ES6 build tree to ES5 - var es5Tree = - new TraceurCompiler(es6Tree, '.js', '.js.map', {modules: 'instantiate', sourceMaps: true}); - - // Now we add a few more files to the es6 tree that Traceur should not see - ['angular2', 'rtts_assert'].forEach(function(destDir) { - var extras = new Funnel('tools/build', {files: ['es5build.js'], destDir: destDir}); - es6Tree = mergeTrees([es6Tree, extras]); - }); - - - var vendorScriptsTree = flatten(new Funnel('.', { - files: [ - 'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js', - 'node_modules/zone.js/zone.js', - 'node_modules/zone.js/long-stack-trace-zone.js', - 'node_modules/systemjs/dist/system.src.js', - 'node_modules/systemjs/lib/extension-register.js', - 'node_modules/systemjs/lib/extension-cjs.js', - 'node_modules/rx/dist/rx.all.js', - 'tools/build/snippets/runtime_paths.js', - path.relative(projectRootDir, TraceurCompiler.RUNTIME_PATH) - ] - })); - var vendorScripts_benchmark = - new Funnel('tools/build/snippets', {files: ['url_params_to_form.js'], destDir: '/'}); - var vendorScripts_benchmarks_external = - new Funnel('node_modules/angular', {files: ['angular.js'], destDir: '/'}); - - var servingTrees = []; - - function copyVendorScriptsTo(destDir) { - servingTrees.push(new Funnel(vendorScriptsTree, {srcDir: '/', destDir: destDir})); - if (destDir.indexOf('benchmarks') > -1) { - servingTrees.push(new Funnel(vendorScripts_benchmark, {srcDir: '/', destDir: destDir})); - } - if (destDir.indexOf('benchmarks_external') > -1) { - servingTrees.push( - new Funnel(vendorScripts_benchmarks_external, {srcDir: '/', destDir: destDir})); - } - } - - function writeScriptsForPath(relativePath, result) { - copyVendorScriptsTo(path.dirname(relativePath)); - return result.replace('@@FILENAME_NO_EXT', relativePath.replace(/\.\w+$/, '')); - } - - var htmlTree = new Funnel(modulesTree, {include: ['*/src/**/*.html'], destDir: '/'}); - htmlTree = replace(htmlTree, { - files: ['examples*/**'], - patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}], - replaceWithPath: writeScriptsForPath - }); - htmlTree = replace(htmlTree, { - files: ['benchmarks/**'], - patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}], - replaceWithPath: writeScriptsForPath - }); - htmlTree = replace(htmlTree, { - files: ['benchmarks_external/**'], - patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}], - replaceWithPath: writeScriptsForPath - }); - - // Copy all vendor scripts into all examples and benchmarks - ['benchmarks/src', 'benchmarks_external/src', 'examples/src/benchpress'].forEach( - copyVendorScriptsTo); - - var scripts = mergeTrees(servingTrees, {overwrite: true}); - var css = new Funnel(modulesTree, {include: ["**/*.css"]}); - var polymerFiles = new Funnel('.', { - files: [ - 'bower_components/polymer/lib/polymer.html', - 'tools/build/snippets/url_params_to_form.js' - ] - }); - var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); - htmlTree = mergeTrees([htmlTree, scripts, polymer, css]); - - es5Tree = mergeTrees([es5Tree, htmlTree]); - - return mergeTrees([ - stew.mv(es6Tree, 'js/' + options.name + '/es6'), - stew.mv(es5Tree, 'js/' + options.name + '/es5') - ]); -} - - -function makeCjsTree() { - // list of npm packages that this build will create - var outputPackages = ['angular2', 'benchpress', 'rtts_assert']; - - var modulesTree = new Funnel('modules', { - include: ['angular2/**', 'benchpress/**', 'rtts_assert/**', '**/e2e_test/**'], - exclude: ['angular2/src/core/zone/vm_turn_zone.es6'] - }); - - var cjsTree = new TraceurCompiler(modulesTree, '.js', '.map', { - sourceMaps: true, - annotations: true, // parse annotations - types: true, // parse types - script: false, // parse as a module - memberVariables: true, // parse class fields - typeAssertionModule: 'rtts_assert/rtts_assert', - // Don't use type assertions since this is partly transpiled by typescript - typeAssertions: false, - modules: 'commonjs' - }); - - // Transform all tests to make them runnable in node - cjsTree = replace(cjsTree, { - files: ['**/test/**/*_spec.js'], - patterns: [ - { - // Override the default DOM adapter with Parse5 for all tests - match: /"use strict";/, - replacement: - "'use strict'; var parse5Adapter = require('angular2/src/dom/parse5_adapter'); " + - "parse5Adapter.Parse5DomAdapter.makeCurrent();" - }, - { - // Append main() to all tests since all of our tests are wrapped in exported main fn - match: /$/g, - replacement: "\r\n main();" - } - ] - }); - - // Now we add the LICENSE file into all the folders that will become npm packages - outputPackages.forEach(function(destDir) { - var license = new Funnel('.', {files: ['LICENSE'], destDir: destDir}); - cjsTree = mergeTrees([cjsTree, license]); - }); - - // Get all docs and related assets and prepare them for js build - var docs = new Funnel(modulesTree, {include: ['**/*.md', '**/*.png'], exclude: ['**/*.dart.md']}); - docs = stew.rename(docs, 'README.js.md', 'README.md'); - - // Generate shared package.json info - var BASE_PACKAGE_JSON = require(path.join(projectRootDir, 'package.json')); - var COMMON_PACKAGE_JSON = { - version: BASE_PACKAGE_JSON.version, - homepage: BASE_PACKAGE_JSON.homepage, - bugs: BASE_PACKAGE_JSON.bugs, - license: BASE_PACKAGE_JSON.license, - contributors: BASE_PACKAGE_JSON.contributors, - dependencies: BASE_PACKAGE_JSON.dependencies, - devDependencies: { - "yargs": BASE_PACKAGE_JSON.devDependencies['yargs'], - "gulp-sourcemaps": BASE_PACKAGE_JSON.devDependencies['gulp-sourcemaps'], - "gulp-traceur": BASE_PACKAGE_JSON.devDependencies['gulp-traceur'], - "gulp": BASE_PACKAGE_JSON.devDependencies['gulp'], - "gulp-rename": BASE_PACKAGE_JSON.devDependencies['gulp-rename'], - "through2": BASE_PACKAGE_JSON.devDependencies['through2'] - } - }; - - // 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 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, docs, packageJsons]); - - return stew.mv(cjsTree, 'js/cjs'); -} - - -function makeDartTree() { - // Transpile everything in 'modules'... - var modulesTree = new Funnel('modules', { - include: ['**/*.js', '**/*.ts', '**/*.dart'], // .dart file available means don't translate. - exclude: ['rtts_assert/**/*'], // ... except for the rtts_asserts (don't apply to Dart). - destDir: '/' // Remove the 'modules' prefix. - }); - - // Transpile to dart. - var dartTree = ts2dart.transpile(modulesTree); - - // Move around files to match Dart's layout expectations. - dartTree = stew.rename(dartTree, function(relativePath) { - // If a file matches the `pattern`, insert the given `insertion` as the second path part. - var replacements = [ - {pattern: /^benchmarks\/test\//, insertion: ''}, - {pattern: /^benchmarks\//, insertion: 'web'}, - {pattern: /^benchmarks_external\/test\//, insertion: ''}, - {pattern: /^benchmarks_external\//, insertion: 'web'}, - {pattern: /^example.?\//, insertion: 'web/'}, - {pattern: /^example.?\/test\//, insertion: ''}, - {pattern: /^[^\/]*\/test\//, insertion: ''}, - {pattern: /^./, insertion: 'lib'}, // catch all. - ]; - - for (var i = 0; i < replacements.length; i++) { - var repl = replacements[i]; - if (relativePath.match(repl.pattern)) { - var parts = relativePath.split('/'); - parts.splice(1, 0, repl.insertion); - return path.join.apply(path, parts); - } - } - throw new Error('Failed to match any path: ' + relativePath); - }); - - // Move the tree under the 'dart' folder. - return stew.mv(dartTree, 'dart'); -} diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts new file mode 100644 index 0000000000..6f8caa7512 --- /dev/null +++ b/tools/broccoli/trees/browser_tree.ts @@ -0,0 +1,120 @@ +'use strict'; + +var Funnel = require('broccoli-funnel'); +var flatten = require('broccoli-flatten'); +var htmlReplace = require('../html-replace'); +var mergeTrees = require('broccoli-merge-trees'); +var path = require('path'); +var replace = require('broccoli-replace'); +var stew = require('broccoli-stew'); +var ts2dart = require('../broccoli-ts2dart'); +var TraceurCompiler = require('../traceur'); + +var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..')); + + +module.exports = function makeBrowserTree(options) { + var modulesTree = new Funnel( + 'modules', + {include: ['**/**'], exclude: ['**/*.cjs', 'benchmarks/e2e_test/**'], destDir: '/'}); + + // Use Traceur to transpile original sources to ES6 + var es6Tree = new TraceurCompiler(modulesTree, '.es6', '.map', { + sourceMaps: true, + annotations: true, // parse annotations + types: true, // parse types + script: false, // parse as a module + memberVariables: true, // parse class fields + modules: 'instantiate', + typeAssertionModule: 'rtts_assert/rtts_assert', + typeAssertions: options.typeAssertions, + outputLanguage: 'es6' + }); + + + // Call Traceur again to lower the ES6 build tree to ES5 + var es5Tree = + new TraceurCompiler(es6Tree, '.js', '.js.map', {modules: 'instantiate', sourceMaps: true}); + + // Now we add a few more files to the es6 tree that Traceur should not see + ['angular2', 'rtts_assert'].forEach(function(destDir) { + var extras = new Funnel('tools/build', {files: ['es5build.js'], destDir: destDir}); + es6Tree = mergeTrees([es6Tree, extras]); + }); + + + var vendorScriptsTree = flatten(new Funnel('.', { + files: [ + 'node_modules/es6-module-loader/dist/es6-module-loader-sans-promises.src.js', + 'node_modules/zone.js/zone.js', + 'node_modules/zone.js/long-stack-trace-zone.js', + 'node_modules/systemjs/dist/system.src.js', + 'node_modules/systemjs/lib/extension-register.js', + 'node_modules/systemjs/lib/extension-cjs.js', + 'node_modules/rx/dist/rx.all.js', + 'tools/build/snippets/runtime_paths.js', + path.relative(projectRootDir, TraceurCompiler.RUNTIME_PATH) + ] + })); + var vendorScripts_benchmark = + new Funnel('tools/build/snippets', {files: ['url_params_to_form.js'], destDir: '/'}); + var vendorScripts_benchmarks_external = + new Funnel('node_modules/angular', {files: ['angular.js'], destDir: '/'}); + + var servingTrees = []; + + function copyVendorScriptsTo(destDir) { + servingTrees.push(new Funnel(vendorScriptsTree, {srcDir: '/', destDir: destDir})); + if (destDir.indexOf('benchmarks') > -1) { + servingTrees.push(new Funnel(vendorScripts_benchmark, {srcDir: '/', destDir: destDir})); + } + if (destDir.indexOf('benchmarks_external') > -1) { + servingTrees.push( + new Funnel(vendorScripts_benchmarks_external, {srcDir: '/', destDir: destDir})); + } + } + + function writeScriptsForPath(relativePath, result) { + copyVendorScriptsTo(path.dirname(relativePath)); + return result.replace('@@FILENAME_NO_EXT', relativePath.replace(/\.\w+$/, '')); + } + + var htmlTree = new Funnel(modulesTree, {include: ['*/src/**/*.html'], destDir: '/'}); + htmlTree = replace(htmlTree, { + files: ['examples*/**'], + patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}], + replaceWithPath: writeScriptsForPath + }); + htmlTree = replace(htmlTree, { + files: ['benchmarks/**'], + patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}], + replaceWithPath: writeScriptsForPath + }); + htmlTree = replace(htmlTree, { + files: ['benchmarks_external/**'], + patterns: [{match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}], + replaceWithPath: writeScriptsForPath + }); + + // Copy all vendor scripts into all examples and benchmarks + ['benchmarks/src', 'benchmarks_external/src', 'examples/src/benchpress'].forEach( + copyVendorScriptsTo); + + var scripts = mergeTrees(servingTrees, {overwrite: true}); + var css = new Funnel(modulesTree, {include: ["**/*.css"]}); + var polymerFiles = new Funnel('.', { + files: [ + 'bower_components/polymer/lib/polymer.html', + 'tools/build/snippets/url_params_to_form.js' + ] + }); + var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); + htmlTree = mergeTrees([htmlTree, scripts, polymer, css]); + + es5Tree = mergeTrees([es5Tree, htmlTree]); + + return mergeTrees([ + stew.mv(es6Tree, 'js/' + options.name + '/es6'), + stew.mv(es5Tree, 'js/' + options.name + '/es5') + ]); +}; diff --git a/tools/broccoli/trees/dart_tree.ts b/tools/broccoli/trees/dart_tree.ts new file mode 100644 index 0000000000..ed69b2d049 --- /dev/null +++ b/tools/broccoli/trees/dart_tree.ts @@ -0,0 +1,47 @@ +'use strict'; + +var Funnel = require('broccoli-funnel'); +var path = require('path'); +var replace = require('broccoli-replace'); +var stew = require('broccoli-stew'); +var ts2dart = require('../broccoli-ts2dart'); + +module.exports = function makeDartTree() { + // Transpile everything in 'modules'... + var modulesTree = new Funnel('modules', { + include: ['**/*.js', '**/*.ts', '**/*.dart'], // .dart file available means don't translate. + exclude: ['rtts_assert/**/*'], // ... except for the rtts_asserts (don't apply to Dart). + destDir: '/' // Remove the 'modules' prefix. + }); + + // Transpile to dart. + var dartTree = ts2dart.transpile(modulesTree); + + // Move around files to match Dart's layout expectations. + dartTree = stew.rename(dartTree, function(relativePath) { + // If a file matches the `pattern`, insert the given `insertion` as the second path part. + var replacements = [ + {pattern: /^benchmarks\/test\//, insertion: ''}, + {pattern: /^benchmarks\//, insertion: 'web'}, + {pattern: /^benchmarks_external\/test\//, insertion: ''}, + {pattern: /^benchmarks_external\//, insertion: 'web'}, + {pattern: /^example.?\//, insertion: 'web/'}, + {pattern: /^example.?\/test\//, insertion: ''}, + {pattern: /^[^\/]*\/test\//, insertion: ''}, + {pattern: /^./, insertion: 'lib'}, // catch all. + ]; + + for (var i = 0; i < replacements.length; i++) { + var repl = replacements[i]; + if (relativePath.match(repl.pattern)) { + var parts = relativePath.split('/'); + parts.splice(1, 0, repl.insertion); + return path.join.apply(path, parts); + } + } + throw new Error('Failed to match any path: ' + relativePath); + }); + + // Move the tree under the 'dart' folder. + return stew.mv(dartTree, 'dart'); +}; diff --git a/tools/broccoli/trees/node_tree.ts b/tools/broccoli/trees/node_tree.ts new file mode 100644 index 0000000000..b00bd8120e --- /dev/null +++ b/tools/broccoli/trees/node_tree.ts @@ -0,0 +1,109 @@ +'use strict'; + +var Funnel = require('broccoli-funnel'); +var mergeTrees = require('broccoli-merge-trees'); +var path = require('path'); +var renderLodashTemplate = require('broccoli-lodash'); +var replace = require('broccoli-replace'); +var stew = require('broccoli-stew'); +var ts2dart = require('../broccoli-ts2dart'); +var TraceurCompiler = require('../traceur'); +var TypescriptCompiler = require('../typescript'); + +var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..')); + + +module.exports = function makeNodeTree() { + // list of npm packages that this build will create + var outputPackages = ['angular2', 'benchpress', 'rtts_assert']; + + var modulesTree = new Funnel('modules', { + include: ['angular2/**', 'benchpress/**', 'rtts_assert/**', '**/e2e_test/**'], + exclude: ['angular2/src/core/zone/vm_turn_zone.es6'] + }); + + var nodeTree = new TraceurCompiler(modulesTree, '.js', '.map', { + sourceMaps: true, + annotations: true, // parse annotations + types: true, // parse types + script: false, // parse as a module + memberVariables: true, // parse class fields + typeAssertionModule: 'rtts_assert/rtts_assert', + // Don't use type assertions since this is partly transpiled by typescript + typeAssertions: false, + modules: 'commonjs' + }); + + // Transform all tests to make them runnable in node + nodeTree = replace(nodeTree, { + files: ['**/test/**/*_spec.js'], + patterns: [ + { + // Override the default DOM adapter with Parse5 for all tests + match: /"use strict";/, + replacement: + "'use strict'; var parse5Adapter = require('angular2/src/dom/parse5_adapter'); " + + "parse5Adapter.Parse5DomAdapter.makeCurrent();" + }, + { + // Append main() to all tests since all of our tests are wrapped in exported main fn + match: /$/g, + replacement: "\r\n main();" + } + ] + }); + + // Now we add the LICENSE file into all the folders that will become npm packages + outputPackages.forEach(function(destDir) { + var license = new Funnel('.', {files: ['LICENSE'], destDir: destDir}); + nodeTree = mergeTrees([nodeTree, license]); + }); + + // Get all docs and related assets and prepare them for js build + var docs = new Funnel(modulesTree, {include: ['**/*.md', '**/*.png'], exclude: ['**/*.dart.md']}); + docs = stew.rename(docs, 'README.js.md', 'README.md'); + + // Generate shared package.json info + var BASE_PACKAGE_JSON = require(path.join(projectRootDir, 'package.json')); + var COMMON_PACKAGE_JSON = { + version: BASE_PACKAGE_JSON.version, + homepage: BASE_PACKAGE_JSON.homepage, + bugs: BASE_PACKAGE_JSON.bugs, + license: BASE_PACKAGE_JSON.license, + contributors: BASE_PACKAGE_JSON.contributors, + dependencies: BASE_PACKAGE_JSON.dependencies, + devDependencies: { + "yargs": BASE_PACKAGE_JSON.devDependencies['yargs'], + "gulp-sourcemaps": BASE_PACKAGE_JSON.devDependencies['gulp-sourcemaps'], + "gulp-traceur": BASE_PACKAGE_JSON.devDependencies['gulp-traceur'], + "gulp": BASE_PACKAGE_JSON.devDependencies['gulp'], + "gulp-rename": BASE_PACKAGE_JSON.devDependencies['gulp-rename'], + "through2": BASE_PACKAGE_JSON.devDependencies['through2'] + } + }; + + // 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 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 + nodeTree = mergeTrees([nodeTree, typescriptTree], { overwrite: true }); + nodeTree = mergeTrees([nodeTree, docs, packageJsons]); + + return stew.mv(nodeTree, 'js/cjs'); +};