diff --git a/packages/@angular/cli/custom-typings.d.ts b/packages/@angular/cli/custom-typings.d.ts index 91c843700857..ccd697a97ffa 100644 --- a/packages/@angular/cli/custom-typings.d.ts +++ b/packages/@angular/cli/custom-typings.d.ts @@ -15,7 +15,7 @@ interface IWebpackDevServerConfigurationOptions { }; publicPath?: string; headers?: { [key: string]: string }; - stats?: { [key: string]: boolean }; + stats?: { [key: string]: boolean } | string; inline: boolean; https?: boolean; key?: string; diff --git a/packages/@angular/cli/tasks/build.ts b/packages/@angular/cli/tasks/build.ts index 96655a2b4d58..cd767b8ec4f0 100644 --- a/packages/@angular/cli/tasks/build.ts +++ b/packages/@angular/cli/tasks/build.ts @@ -7,6 +7,7 @@ import { BuildTaskOptions } from '../commands/build'; import { NgCliWebpackConfig } from '../models/webpack-config'; import { getWebpackStatsConfig } from '../models/webpack-configs/utils'; import { CliConfig } from '../models/config'; +import {statsToString} from '../utilities/stats'; const Task = require('../ember-cli/lib/models/task'); const SilentError = require('silent-error'); @@ -39,18 +40,19 @@ export default Task.extend({ return reject(err); } - this.ui.writeLine(stats.toString(statsConfig)); + const json = stats.toJson('verbose'); + if (runTaskOptions.verbose) { + this.ui.writeLine(stats.toString(statsConfig)); + } else { + this.ui.writeLine(statsToString(json, statsConfig)); + } if (runTaskOptions.watch) { return; - } - - if (!runTaskOptions.watch && runTaskOptions.statsJson) { - const jsonStats = stats.toJson('verbose'); - + } else if (runTaskOptions.statsJson) { fs.writeFileSync( path.resolve(this.project.root, outputPath, 'stats.json'), - JSON.stringify(jsonStats, null, 2) + JSON.stringify(json, null, 2) ); } diff --git a/packages/@angular/cli/tasks/serve.ts b/packages/@angular/cli/tasks/serve.ts index 618ea14419ba..7e36cc3360ad 100644 --- a/packages/@angular/cli/tasks/serve.ts +++ b/packages/@angular/cli/tasks/serve.ts @@ -9,6 +9,7 @@ import { NgCliWebpackConfig } from '../models/webpack-config'; import { ServeTaskOptions } from '../commands/serve'; import { CliConfig } from '../models/config'; import { getAppFromConfig } from '../utilities/app-utils'; +import {statsToString} from '../utilities/stats'; const WebpackDevServer = require('webpack-dev-server'); const Task = require('../ember-cli/lib/models/task'); @@ -162,7 +163,7 @@ export default Task.extend({ disableDotRule: true, htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'] }, - stats: statsConfig, + stats: serveTaskOptions.verbose ? statsConfig : 'none', inline: true, proxy: proxyConfig, compress: serveTaskOptions.target === 'production', @@ -212,6 +213,19 @@ export default Task.extend({ `)); const server = new WebpackDevServer(webpackCompiler, webpackDevServerConfiguration); + if (!serveTaskOptions.verbose) { + webpackCompiler.plugin('done', (stats: any) => { + const str = statsToString(stats.toJson(), statsConfig); + if (stats.hasErrors()) { + this.ui.writeError(str); + } else if (stats.hasWarnings()) { + this.ui.writeWarnLine(str); + } else { + this.ui.writeLine(str); + } + }); + } + return new Promise((_resolve, reject) => { server.listen(serveTaskOptions.port, serveTaskOptions.host, (err: any, _stats: any) => { if (err) { diff --git a/packages/@angular/cli/utilities/stats.ts b/packages/@angular/cli/utilities/stats.ts new file mode 100644 index 000000000000..9c1213665bc6 --- /dev/null +++ b/packages/@angular/cli/utilities/stats.ts @@ -0,0 +1,45 @@ +import { bold, green, reset, white, yellow } from 'chalk'; +import { stripIndents } from 'common-tags'; + + +function _formatSize(size: number): string { + if (size <= 0) { + return '0 bytes'; + } + + const abbreviations = ['bytes', 'kB', 'MB', 'GB']; + const index = Math.floor(Math.log(size) / Math.log(1000)); + + return `${+(size / Math.pow(1000, index)).toPrecision(3)} ${abbreviations[index]}`; +} + + +export function statsToString(json: any, statsConfig: any) { + const colors = statsConfig.colors; + const r = (x: string) => colors ? reset(x) : x; + const w = (x: string) => colors ? bold(white(x)) : x; + const g = (x: string) => colors ? bold(green(x)) : x; + const y = (x: string) => colors ? bold(yellow(x)) : x; + + return r(stripIndents` + Date: ${w(new Date().toISOString())} + Hash: ${w(json.hash)} + Time: ${w('' + json.time)}ms + ${json.chunks.map((chunk: any) => { + const asset = json.assets.filter((x: any) => x.name == chunk.files[0])[0]; + const size = asset ? ` ${_formatSize(asset.size)}` : ''; + const files = chunk.files.join(', '); + const names = chunk.names ? ` (${chunk.names.join(', ')})` : ''; + const parents = chunk.parents.map((id: string) => ` {${y(id)}}`).join(''); + const initial = y(chunk.entry ? '[entry]' : chunk.initial ? '[initial]' : ''); + const flags = ['rendered', 'recorded'] + .map(f => f && chunk[f] ? g(` [${f}]`) : '') + .join(''); + + return `chunk {${y(chunk.id)}} ${g(files)}${names}${size}${parents} ${initial}${flags}`; + }).join('\n')} + + ${json.warnings.map((warning: any) => y(`WARNING in ${warning}`)).join('\n\n')} + ${json.errors.map((error: any) => r(`ERROR in ${error}`)).join('\n')} + `); +}