From 31f85f0297be9534abc67bcc3b080291a31f012e Mon Sep 17 00:00:00 2001 From: Tim Blasi Date: Thu, 19 Nov 2015 16:08:23 -0800 Subject: [PATCH] chore(analytics): Build hello_world, check constraints Create gulp targets to build `hello_world` and check its gzipped size against size constraints. See #5312, #5314 --- .gitignore | 3 + gulpfile.js | 36 ++++++++-- modules_dart/payload/hello_world/pubspec.yaml | 24 +++++++ .../payload/hello_world/web/index.dart | 68 +++++++++++++++++++ .../payload/hello_world/web/index.html | 12 ++++ tools/analytics/reportsize.js | 39 ++++++++--- tools/build/pubbuild.js | 39 ++++++++--- 7 files changed, 197 insertions(+), 24 deletions(-) create mode 100644 modules_dart/payload/hello_world/pubspec.yaml create mode 100644 modules_dart/payload/hello_world/web/index.dart create mode 100644 modules_dart/payload/hello_world/web/index.html diff --git a/.gitignore b/.gitignore index e2a9e85ddf..f6961d0181 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ npm-debug.log # build-analytics .build-analytics + +# built dart payload tests +/modules_dart/payload/**/build diff --git a/gulpfile.js b/gulpfile.js index 8aecc37e20..0e5ef94d1b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -252,9 +252,10 @@ gulp.task('build/check.apidocs.dart', // pubbuild // WARNING: this task is very slow (~15m as of July 2015) -gulp.task('build/pubbuild.dart', - pubbuild(gulp, gulpPlugins, - {src: CONFIG.dest.dart, dest: CONFIG.dest.js.dart2js, command: DART_SDK.PUB})); +gulp.task( + 'build/pubbuild.dart', + pubbuild.subdirs(gulp, gulpPlugins, + {src: CONFIG.dest.dart, dest: CONFIG.dest.js.dart2js, command: DART_SDK.PUB})); // ------------ // formatting @@ -584,6 +585,30 @@ gulp.task('test.unit.dart', function(done) { }); }); +// Dart Payload Size Test +// This test will fail if the size of our hello_world app goes beyond one of +// these values when compressed at the specified level. +// Measure in bytes. +var _DART_PAYLOAD_SIZE_LIMITS = {'uncompressed': 375 * 1024, 'gzip level=6': 105 * 1024}; +gulp.task('test.payload.dart/ci', function(done) { + runSequence('build/packages.dart', '!pubget.payload.dart', '!pubbuild.payload.dart', + '!checkAndReport.payload.dart', done); +}); + +gulp.task('!pubget.payload.dart', + pubget.dir(gulp, gulpPlugins, + {dir: 'modules_dart/payload/hello_world', command: DART_SDK.PUB})); + +gulp.task('!pubbuild.payload.dart', + pubbuild.single(gulp, gulpPlugins, + {command: DART_SDK.PUB, src: 'modules_dart/payload/hello_world'})); + +gulp.task('!checkAndReport.payload.dart', function() { + var reportSize = require('./tools/analytics/reportsize'); + return reportSize('modules_dart/payload/hello_world/build/web/*.dart.js', + {failConditions: _DART_PAYLOAD_SIZE_LIMITS, prefix: 'hello_world'}); +}); + gulp.task('watch.dart.dev', function(done) { runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart', '!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css', @@ -1053,10 +1078,9 @@ gulp.task('!bundle.copy', function() { gulp.src('dist/js/bundle/**').pipe(gulp.dest('dist/js/dev/es5/bundle'))); }); -gulp.task('!bundles.js.checksize', function() { +gulp.task('!bundles.js.checksize', function(done) { var reportSize = require('./tools/analytics/reportsize'); - return reportSize('dist/js/bundle/**', {printToConsole: false, - reportAnalytics: true}); + return reportSize('dist/js/bundle/**', {printToConsole: ['gzip level=2']}); }); gulp.task('bundles.js', diff --git a/modules_dart/payload/hello_world/pubspec.yaml b/modules_dart/payload/hello_world/pubspec.yaml new file mode 100644 index 0000000000..30ec384dea --- /dev/null +++ b/modules_dart/payload/hello_world/pubspec.yaml @@ -0,0 +1,24 @@ +name: hello_world +environment: + sdk: '>=1.10.0 <2.0.0' +dependencies: + observe: '^0.13.1' + angular2: any + browser: '^0.10.0' +dependency_overrides: + angular2: + path: ../../../dist/dart/angular2 +transformers: +- angular2: + platform_directives: 'package:angular2/src/common/directives.dart#CORE_DIRECTIVES' + entry_points: + - web/index.dart + +- $dart2js: + minify: true + commandLineOptions: + - --show-package-warnings + - --trust-type-annotations + - --trust-primitives + # Uncomment to generate summaries from dart2js + # - --dump-info diff --git a/modules_dart/payload/hello_world/web/index.dart b/modules_dart/payload/hello_world/web/index.dart new file mode 100644 index 0000000000..05a8fa6a8a --- /dev/null +++ b/modules_dart/payload/hello_world/web/index.dart @@ -0,0 +1,68 @@ +library hello_world.index; + +import "package:angular2/bootstrap.dart" show bootstrap; +import "package:angular2/core.dart" + show ElementRef, Component, Directive, Injectable; +import "package:angular2/render.dart" show Renderer; + +main() { + // Bootstrapping only requires specifying a root component. + + // The boundary between the Angular application and the rest of the page is + + // the shadowDom of this root component. + + // The selector of the component passed in is used to find where to insert the + + // application. + + // You can use the light dom of the tag as temporary content (for + + // example 'Loading...') before the application is ready. + bootstrap(HelloCmp); +} + +// A service available to the Injector, used by the HelloCmp component. +@Injectable() +class GreetingService { + String greeting = "hello"; +} +// Directives are light-weight. They don't allow new + +// expression contexts (use @Component for those needs). +@Directive(selector: "[red]") +class RedDec { + // ElementRef is always injectable and it wraps the element on which the + + // directive was found by the compiler. + RedDec(ElementRef el, Renderer renderer) { + renderer.setElementStyle(el, "color", "red"); + } +} +// Angular 2.0 supports 2 basic types of directives: + +// - Component - the basic building blocks of Angular 2.0 apps. Backed by + +// ShadowDom.(http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom/) + +// - Directive - add behavior to existing elements. + +// @Component is AtScript syntax to annotate the HelloCmp class as an Angular + +// 2.0 component. +@Component( + selector: "hello-app", + viewProviders: const [GreetingService], + template: + '''
{{greeting}} world!
+ ''', + directives: const [RedDec]) +class HelloCmp { + String greeting; + HelloCmp(GreetingService service) { + this.greeting = service.greeting; + } + void changeGreeting() { + this.greeting = "howdy"; + } +} diff --git a/modules_dart/payload/hello_world/web/index.html b/modules_dart/payload/hello_world/web/index.html new file mode 100644 index 0000000000..119f5ead0f --- /dev/null +++ b/modules_dart/payload/hello_world/web/index.html @@ -0,0 +1,12 @@ + + + Hello Angular 2.0 + + + Loading... + + + + + + diff --git a/tools/analytics/reportsize.js b/tools/analytics/reportsize.js index af44002726..b642c7bf50 100644 --- a/tools/analytics/reportsize.js +++ b/tools/analytics/reportsize.js @@ -22,11 +22,15 @@ const _gzipConfigs = { const _defaultOptions = { // @type {Object} - // - Key(s) must match keys of `_gzipConfigs`. + // - Key(s) must match one of `_gzipConfigs` keys. // - Values are the max size (in bytes) allowed for that configuration. failConditions: {}, prefix: '', - printToConsole: false, + // @type {Array|boolean} + // Entries must match one of `_gzipConfigs` keys. These values will be + // printed to the screen. + // If this is the boolean value `true`, will print all to screen. + printToConsole: ['gzip level=6'], reportAnalytics: true }; @@ -41,7 +45,7 @@ function reportSize(glob, options) { options[key] = _defaultOptions[key]; } } - var errStream = _checkFailConditionConfig(options.failConditions); + var errStream = _checkConfig(options); if (errStream) { return errStream; } @@ -83,27 +87,29 @@ function reportSize(glob, options) { if (options.reportAnalytics) { analytics.bundleSize(filePath, fileLen, compressionLevel); } - if (options.printToConsole) { + if (_shouldPrint(options, compressionLevel)) { console.log(` ${filePath} => ${fileLen} bytes (${compressionLevel})`) } if (options.failConditions.hasOwnProperty(compressionLevel)) { if (options.failConditions[compressionLevel] < fileLen) { errs.push(`Max size for "${compressionLevel}" is ` + `${options.failConditions[compressionLevel]}, but the size is now ${fileLen}.`); - if (options.printToConsole) { - console.log(` !!! ${errs[errs.length - 1]}`); - } } } } } } +function _shouldPrint(options, compressionLevel) { + const printAll = typeof options.printToConsole == 'boolean' && options.printToConsole; + return printAll || options.printToConsole.indexOf(compressionLevel) >= 0; +} + // Returns an error stream if the fail conditions are not provided property. // Returns `null` if everything is fine. -function _checkFailConditionConfig(failConditions) { - for (const key in failConditions) { - if (failConditions.hasOwnProperty(key)) { +function _checkConfig(config) { + for (const key in config.failConditions) { + if (config.failConditions.hasOwnProperty(key)) { if (!_gzipConfigs.hasOwnProperty(key)) { var stream = new Stream(); stream.emit( @@ -114,6 +120,19 @@ function _checkFailConditionConfig(failConditions) { } } } + if (typeof config.printToConsole != 'boolean') { + for (var i = 0; i < config.printToConsole.length; ++i) { + const key = config.printToConsole[i]; + if (!_gzipConfigs.hasOwnProperty(key)) { + var stream = new Stream(); + stream.emit( + 'error', + new Error(`Incorrect value "${key}" in printToConsole. Check _gzipConfigs.`)); + stream.emit('end'); + return stream; + } + } + } return null; } diff --git a/tools/build/pubbuild.js b/tools/build/pubbuild.js index 985878d1f9..c6c0434b56 100644 --- a/tools/build/pubbuild.js +++ b/tools/build/pubbuild.js @@ -6,7 +6,7 @@ var path = require('path'); var glob = require('glob'); var fs = require('fs'); -module.exports = function(gulp, plugins, config) { +function buildAllWebSubdirs(gulp, plugins, config) { return function() { var webFolders = [].slice.call(glob.sync(path.join(config.src, '*/web'))); return nextFolder(); @@ -17,20 +17,34 @@ module.exports = function(gulp, plugins, config) { } var folder = path.resolve(path.join(webFolders.shift(), '..')); var destFolder = path.resolve(path.join(config.dest, path.basename(folder))); - var pubMode = config.mode || 'release'; - var pubArgs = ['build', '--mode', pubMode, '-o', destFolder]; - return util.processToPromise(spawn(config.command, pubArgs, { - stdio: 'inherit', - cwd: folder - })).then(function() { + const nextConfig = { + command: config.command, + dest: destFolder, + mode: config.mode, + src: folder + }; + return single(nextConfig).then(function() { return replaceDartWithJsScripts(gulp, destFolder); }).then(function() { return removeWebFolder(gulp, destFolder); }).then(nextFolder); } }; -}; +} + +function single(config) { + var pubMode = config.mode || 'release'; + var pubArgs = ['build', '--mode', pubMode]; + if (config.dest) { + pubArgs = pubArgs.concat(['-o', config.dest]); + } + + return util.processToPromise(spawn(config.command, pubArgs, { + stdio: 'inherit', + cwd: config.src + })); +} function replaceDartWithJsScripts(gulp, folder) { return util.streamToPromise(gulp.src(path.join(folder, '**/*.html')) @@ -45,6 +59,10 @@ function replaceDartWithJsScripts(gulp, folder) { .pipe(gulp.dest(folder))); } +function singleWrapper(gulp, plugins, config) { + return function() { return single(config); }; +} + function removeWebFolder(gulp, folder) { var folders = [].slice.call(glob.sync(path.join(folder, 'web', '*'))); folders.forEach(function(subFolder) { @@ -53,3 +71,8 @@ function removeWebFolder(gulp, folder) { fs.rmdirSync(path.join(folder, 'web')); return Q.resolve(); } + +module.exports = { + single: singleWrapper, + subdirs: buildAllWebSubdirs +};