diff --git a/gulpfile.js b/gulpfile.js index 3781f7378a..d1fafbbd42 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -46,10 +46,36 @@ var dartSdk = require('./tools/build/dart'); var browserProvidersConf = require('./browser-providers.conf.js'); var os = require('os'); -require('./tools/check-environment')({ - requiredNpmVersion: '>=2.14.7', - requiredNodeVersion: '>=4.2.1' -}); +require('./tools/check-environment')( + {requiredNpmVersion: '>=2.14.7', requiredNodeVersion: '>=4.2.1'}); + +var cliArgs = minimist(process.argv.slice(2)); + +if (cliArgs.projects) { + // normalize for analytics + cliArgs.projects.split(',').sort().join(','); +} + +// --projects=angular2,angular2_material => {angular2: true, angular2_material: true} +var allProjects = + 'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground'; +var cliArgsProjects = (cliArgs.projects || allProjects) + .split(',') + .reduce((map, projectName) => { + map[projectName] = true; + return map; + }, {}); + +function printModulesWarning() { + if (!cliArgs.projects && !process.env.CI) { + // if users didn't specify projects to build, tell them why and how they should + console.warn( + "Pro Tip: Did you know that you can speed up your build by specifying project name(s)?"); + console.warn(" It's like pressing the turbo button in the old days, but better!"); + console.warn(" Examples: --project=angular2 or --project=angular2,angular2_material"); + } +} + // Make it easy to quiet down portions of the build. // --logs=all -> log everything (This is the default) @@ -162,7 +188,7 @@ gulp.task('build/tree.dart', ['build/clean.dart', 'build.tools'], gulp.task('!build/tree.dart', - function() { return angularBuilder.rebuildDartTree(); }); + function() { return angularBuilder.rebuildDartTree(cliArgsProjects); }); // ------------ @@ -444,15 +470,19 @@ function getBrowsersFromCLI(provider) { } gulp.task('test.unit.js', ['build.js.dev'], function(done) { + printModulesWarning(); runSequence('!test.unit.js/karma-server', function() { watch('modules/**', {ignoreInitial: true}, ['!broccoli.js.dev', '!test.unit.js/karma-run']); }); }); -gulp.task('watch.js.dev', ['build.js.dev'], - function(done) { watch('modules/**', ['!broccoli.js.dev']); }); +gulp.task('watch.js.dev', ['build.js.dev'], function(done) { + printModulesWarning(); + watch('modules/**', ['!broccoli.js.dev']); +}); gulp.task('test.unit.js.sauce', ['build.js.dev'], function(done) { + printModulesWarning(); var browserConf = getBrowsersFromCLI('SL'); if (browserConf.isProvider) { launchKarmaWithExternalBrowsers(['dots'], browserConf.browsersToRun, done); @@ -462,6 +492,7 @@ gulp.task('test.unit.js.sauce', ['build.js.dev'], function(done) { }); gulp.task('test.unit.js.browserstack', ['build.js.dev'], function(done) { + printModulesWarning(); var browserConf = getBrowsersFromCLI('BS'); if (browserConf.isProvider) { launchKarmaWithExternalBrowsers(['dots'], browserConf.browsersToRun, done); @@ -535,6 +566,7 @@ gulp.task('!test.unit.router/karma-run', function(done) { gulp.task('buildRouter.dev', function() { buildRouter(); }); gulp.task('test.unit.dart', function(done) { + printModulesWarning(); runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart', '!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css', '!test.unit.dart/karma-server', '!test.unit.dart/karma-run', function(error) { @@ -632,12 +664,9 @@ gulp.task('test.unit.cjs/ci', function(done) { gulp.task('test.unit.cjs', ['build/clean.js', 'build.tools'], function(neverDone) { - + printModulesWarning(); treatTestErrorsAsFatal = false; - - var buildAndTest = ['!build.js.cjs', 'test.unit.cjs/ci']; - - watch('modules/**', buildAndTest); + watch('modules/**', ['!build.js.cjs', 'test.unit.cjs/ci']); }); // Use this target to continuously run dartvm unit-tests (such as transformer @@ -812,12 +841,11 @@ gulp.task('!build.tools', function() { gulp.task('broccoli.js.dev', ['build.tools'], function(done) { runSequence('!broccoli.js.dev', sequenceComplete(done)); }); -gulp.task('!broccoli.js.dev', () => { - return angularBuilder.rebuildBrowserDevTree(); -}); +gulp.task('!broccoli.js.dev', + () => { return angularBuilder.rebuildBrowserDevTree(cliArgsProjects); }); gulp.task('!broccoli.js.prod', - function() { return angularBuilder.rebuildBrowserProdTree(); }); + function() { return angularBuilder.rebuildBrowserProdTree(cliArgsProjects); }); gulp.task('build.js.dev', ['build/clean.js'], function(done) { runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done)); @@ -840,7 +868,7 @@ var firstBuildJsCjs = true; * private task */ gulp.task('!build.js.cjs', function() { - return angularBuilder.rebuildNodeTree() + return angularBuilder.rebuildNodeTree(cliArgsProjects) .then(function() { if (firstBuildJsCjs) { firstBuildJsCjs = false; diff --git a/modules/angular2/manual_typings/globals-es6.d.ts b/modules/angular2/manual_typings/globals-es6.d.ts index 02540be14c..43d437ad64 100644 --- a/modules/angular2/manual_typings/globals-es6.d.ts +++ b/modules/angular2/manual_typings/globals-es6.d.ts @@ -8,8 +8,14 @@ /// /// /// + +// TODO: ideally the node.d.ts reference should be scoped only for files that need and not to all +// the code including client code +/// + declare var assert: any; + interface BrowserNodeGlobal { Object: typeof Object; Array: typeof Array; diff --git a/tools/broccoli/angular_builder.ts b/tools/broccoli/angular_builder.ts index ae45e1ef57..699c3d6a02 100644 --- a/tools/broccoli/angular_builder.ts +++ b/tools/broccoli/angular_builder.ts @@ -7,6 +7,10 @@ var path = require('path'); var printSlowTrees = require('broccoli-slow-trees'); var Q = require('q'); +type ProjectMap = { + [key: string]: boolean +}; + /** * BroccoliBuilder facade for all of our build pipelines. */ @@ -21,26 +25,26 @@ export class AngularBuilder { constructor(public options: AngularBuilderOptions) { this.outputPath = options.outputPath; } - public rebuildBrowserDevTree(): Promise { - this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(); + public rebuildBrowserDevTree(projects: ProjectMap): Promise { + this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(projects); return this.rebuild(this.browserDevBuilder, 'js.dev'); } - public rebuildBrowserProdTree(): Promise { - this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(); + public rebuildBrowserProdTree(projects: ProjectMap): Promise { + this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(projects); return this.rebuild(this.browserProdBuilder, 'js.prod'); } - public rebuildNodeTree(): Promise { - this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(); + public rebuildNodeTree(projects: ProjectMap): Promise { + this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(projects); return this.rebuild(this.nodeBuilder, 'js.cjs'); } - public rebuildDartTree(): Promise { - this.dartBuilder = this.dartBuilder || this.makeDartBuilder(); + public rebuildDartTree(projects: ProjectMap): Promise { + this.dartBuilder = this.dartBuilder || this.makeDartBuilder(projects); return this.rebuild(this.dartBuilder, 'dart'); } @@ -54,31 +58,32 @@ export class AngularBuilder { } - private makeBrowserDevBuilder(): BroccoliBuilder { - let tree = makeBrowserTree({name: 'dev', typeAssertions: true}, + private makeBrowserDevBuilder(projects: ProjectMap): BroccoliBuilder { + let tree = makeBrowserTree({name: 'dev', typeAssertions: true, projects: projects}, path.join(this.outputPath, 'js', 'dev')); return new broccoli.Builder(tree); } - private makeBrowserProdBuilder(): BroccoliBuilder { - let tree = makeBrowserTree({name: 'prod', typeAssertions: false}, + private makeBrowserProdBuilder(projects: ProjectMap): BroccoliBuilder { + let tree = makeBrowserTree({name: 'prod', typeAssertions: false, projects: projects}, path.join(this.outputPath, 'js', 'prod')); return new broccoli.Builder(tree); } - private makeNodeBuilder(): BroccoliBuilder { - let tree = makeNodeTree(path.join(this.outputPath, 'js', 'cjs')); + private makeNodeBuilder(projects: ProjectMap): BroccoliBuilder { + let tree = makeNodeTree(projects, path.join(this.outputPath, 'js', 'cjs')); return new broccoli.Builder(tree); } - private makeDartBuilder(): BroccoliBuilder { + private makeDartBuilder(projects: ProjectMap): BroccoliBuilder { let options = { outputPath: path.join(this.outputPath, 'dart'), dartSDK: this.options.dartSDK, - logs: this.options.logs + logs: this.options.logs, + projects: projects }; let tree = makeDartTree(options); return new broccoli.Builder(tree); diff --git a/tools/broccoli/broccoli-typescript.ts b/tools/broccoli/broccoli-typescript.ts index 5156422e8f..c90db682a8 100644 --- a/tools/broccoli/broccoli-typescript.ts +++ b/tools/broccoli/broccoli-typescript.ts @@ -47,6 +47,12 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin { // in tsc 1.7.x this api was renamed to parseJsonConfigFileContent // the conversion is a bit awkward, see https://github.com/Microsoft/TypeScript/issues/5276 this.tsOpts = ts.parseConfigFile({compilerOptions: options, files: []}, null, null).options; + + // TODO: the above turns rootDir set to './' into an empty string - looks like a tsc bug + // check back when we upgrade to 1.7.x + if (this.tsOpts.rootDir === '') { + this.tsOpts.rootDir = './'; + } this.tsOpts.outDir = this.cachePath; this.tsServiceHost = new CustomLanguageServiceHost(this.tsOpts, this.rootFilePaths, diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts index ad02214e91..b736c25b63 100644 --- a/tools/broccoli/trees/browser_tree.ts +++ b/tools/broccoli/trees/browser_tree.ts @@ -70,26 +70,55 @@ const kServedPaths = [ module.exports = function makeBrowserTree(options, destinationPath) { - var modulesTree = new Funnel('modules', { - include: ['**/**'], - exclude: [ - '**/*.cjs', - 'benchmarks/e2e_test/**', - 'angular1_router/**', - // Exclude ES6 polyfill typings when tsc target=ES6 - 'angular2/typings/es6-*/**', - ], - destDir: '/' - }); + var modules = options.projects; + + if (modules.angular2) { + var angular2Tree = new Funnel('modules/angular2', { + include: ['**/**'], + exclude: [ + // Exclude ES6 polyfill typings when tsc target=ES6 + 'typings/es6-*/**', + ], + destDir: '/angular2/' + }); + } + + if (modules.angular2_material) { + var angular2MaterialTree = + new Funnel('modules/angular2_material', + {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/angular2_material/'}); + } + + if (modules.benchmarks) { + var benchmarksTree = + new Funnel('modules/benchmarks', + {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks/'}); + } + + if (modules.benchmarks_external) { + var benchmarksExternalTree = new Funnel( + 'modules/benchmarks_external', + {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks_external/'}); + } + + if (modules.playground) { + var playgroundTree = + new Funnel('modules/playground', + {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/playground/'}); + } + + var modulesTree = mergeTrees( + [angular2Tree, angular2MaterialTree, benchmarksTree, benchmarksExternalTree, playgroundTree]); var clientModules = new Funnel( 'node_modules', {include: ['@reactivex/**/**', 'parse5/**/**', 'css/**/**'], destDir: '/'}); - var es5ModulesTree = new Funnel('modules', { - include: ['**/**'], - exclude: ['**/*.cjs', 'angular1_router/**', 'benchmarks/e2e_test/**'], - destDir: '/' - }); + var es6PolyfillTypings = + new Funnel('modules', {include: ['angular2/typings/es6-*/**'], destDir: '/'}); + + var es5ModulesTree = mergeTrees([modulesTree, es6PolyfillTypings]); + + es5ModulesTree = stew.debug(es5ModulesTree, {name: 'debug-es5'}); var scriptPathPatternReplacement = { match: '@@PATH', @@ -119,7 +148,7 @@ module.exports = function makeBrowserTree(options, destinationPath) { experimentalDecorators: true, mapRoot: '', // force sourcemaps to use relative path noEmitOnError: false, - rootDir: '.', + rootDir: './', rootFilePaths: ['angular2/manual_typings/globals-es6.d.ts'], sourceMap: true, sourceRoot: '.', @@ -127,7 +156,7 @@ module.exports = function makeBrowserTree(options, destinationPath) { }); // Use TypeScript to transpile the *.ts files to ES5 - var typescriptOptions = { + var es5Tree = compileWithTypescript(es5ModulesTree, { declaration: false, emitDecoratorMetadata: true, experimentalDecorators: true, @@ -135,13 +164,12 @@ module.exports = function makeBrowserTree(options, destinationPath) { module: 'commonjs', moduleResolution: 'classic', noEmitOnError: true, - rootDir: '.', + rootDir: './', rootFilePaths: ['angular2/manual_typings/globals.d.ts'], sourceMap: true, sourceRoot: '.', target: 'es5' - }; - var es5Tree = compileWithTypescript(es5ModulesTree, typescriptOptions); + }); var vendorScriptsTree = flatten(new Funnel('.', { files: [ @@ -173,61 +201,80 @@ module.exports = function makeBrowserTree(options, destinationPath) { return funnels; } + + if (modules.angular2_material || modules.benchmarks || modules.benchmarks_external || + modules.playground) { + var assetsTree = new Funnel( + modulesTree, {include: ['**/*'], exclude: ['**/*.{html,ts,dart}'], destDir: '/'}); + } + var htmlTree = new Funnel( modulesTree, {include: ['*/src/**/*.html', '**/playground/**/*.html'], destDir: '/'}); - htmlTree = replace(htmlTree, { - files: ['playground*/**/*.html'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}, - scriptPathPatternReplacement, - scriptFilePatternReplacement - ] - }); + if (modules.benchmarks || modules.benchmarks_external || modules.playground) { + htmlTree = replace(htmlTree, { + files: ['playground*/**/*.html'], + patterns: [ + {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}, + scriptPathPatternReplacement, + scriptFilePatternReplacement + ] + }); + } - htmlTree = replace(htmlTree, { - files: ['benchmarks/**'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}, - scriptPathPatternReplacement, - scriptFilePatternReplacement - ] - }); + if (modules.benchmarks) { + htmlTree = replace(htmlTree, { + files: ['benchmarks/**'], + patterns: [ + {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}, + scriptPathPatternReplacement, + scriptFilePatternReplacement + ] + }); + } - htmlTree = replace(htmlTree, { - files: ['benchmarks_external/**'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}, - scriptPathPatternReplacement, - scriptFilePatternReplacement - ] - }); + if (modules.benchmarks_external) { + htmlTree = replace(htmlTree, { + files: ['benchmarks_external/**'], + patterns: [ + {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}, + scriptPathPatternReplacement, + scriptFilePatternReplacement + ] + }); + } - // We need to replace the regular angular bundle with the web-worker bundle - // for web-worker e2e tests. - htmlTree = replace(htmlTree, { - files: ['playground*/**/web_workers/**/*.html'], - patterns: [{match: "/bundle/angular2.dev.js", replacement: "/bundle/web_worker/ui.dev.js"}] - }); + if (modules.playground) { + // We need to replace the regular angular bundle with the web-worker bundle + // for web-worker e2e tests. + htmlTree = replace(htmlTree, { + files: ['playground*/**/web_workers/**/*.html'], + patterns: [{match: "/bundle/angular2.dev.js", replacement: "/bundle/web_worker/ui.dev.js"}] + }); + } - var assetsTree = - new Funnel(modulesTree, {include: ['**/*'], exclude: ['**/*.{html,ts,dart}'], destDir: '/'}); + if (modules.benchmarks || modules.benchmarks_external) { + var scripts = mergeTrees(servingTrees); + } - var scripts = mergeTrees(servingTrees); - var polymerFiles = new Funnel('.', { - files: [ - 'bower_components/polymer/polymer.html', - 'bower_components/polymer/polymer-micro.html', - 'bower_components/polymer/polymer-mini.html', - 'tools/build/snippets/url_params_to_form.js' - ] - }); - var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); + if (modules.benchmarks_external) { + var polymerFiles = new Funnel('.', { + files: [ + 'bower_components/polymer/polymer.html', + 'bower_components/polymer/polymer-micro.html', + 'bower_components/polymer/polymer-mini.html', + 'tools/build/snippets/url_params_to_form.js' + ] + }); + var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); - var reactFiles = new Funnel('.', {files: ['node_modules/react/dist/react.min.js']}); - var react = stew.mv(flatten(reactFiles), 'benchmarks_external/src/tree/react'); + var reactFiles = new Funnel('.', {files: ['node_modules/react/dist/react.min.js']}); + var react = stew.mv(flatten(reactFiles), 'benchmarks_external/src/tree/react'); + } - htmlTree = mergeTrees([htmlTree, scripts, polymer, react]); + if (modules.benchmarks || modules.benchmarks_external || modules.playground) { + htmlTree = mergeTrees([htmlTree, scripts, polymer, react]); + } es5Tree = mergeTrees([es5Tree, htmlTree, assetsTree, clientModules]); es6Tree = mergeTrees([es6Tree, htmlTree, assetsTree, clientModules]); diff --git a/tools/broccoli/trees/node_tree.ts b/tools/broccoli/trees/node_tree.ts index 3ff5f7c1f8..ef45433c5c 100644 --- a/tools/broccoli/trees/node_tree.ts +++ b/tools/broccoli/trees/node_tree.ts @@ -12,7 +12,7 @@ var stew = require('broccoli-stew'); var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..', '..')); -module.exports = function makeNodeTree(destinationPath) { +module.exports = function makeNodeTree(projects, destinationPath) { // list of npm packages that this build will create var outputPackages = ['angular2', 'benchpress'];