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
This commit is contained in:
Tim Blasi 2015-11-19 16:08:23 -08:00 committed by Timothy Blasi
parent 225b9d306b
commit 31f85f0297
7 changed files with 197 additions and 24 deletions

3
.gitignore vendored
View File

@ -45,3 +45,6 @@ npm-debug.log
# build-analytics
.build-analytics
# built dart payload tests
/modules_dart/payload/**/build

View File

@ -252,8 +252,9 @@ 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,
gulp.task(
'build/pubbuild.dart',
pubbuild.subdirs(gulp, gulpPlugins,
{src: CONFIG.dest.dart, dest: CONFIG.dest.js.dart2js, command: DART_SDK.PUB}));
// ------------
@ -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',

View File

@ -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

View File

@ -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 <hello-app> 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:
'''<div class="greeting">{{greeting}} <span red>world</span>!</div>
<button class="changeButton" (click)="changeGreeting()">change greeting</button>''',
directives: const [RedDec])
class HelloCmp {
String greeting;
HelloCmp(GreetingService service) {
this.greeting = service.greeting;
}
void changeGreeting() {
this.greeting = "howdy";
}
}

View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<title>Hello Angular 2.0</title>
<body>
<hello-app>
Loading...
</hello-app>
<script src="index.dart" type="application/dart"></script>
<script src="packages/browser/dart.js" type="text/javascript"></script>
</body>
</html>

View File

@ -22,11 +22,15 @@ const _gzipConfigs = {
const _defaultOptions = {
// @type {Object<string, number>}
// - 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<string>|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;
}

View File

@ -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
};