Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): display accurate sizes for downle…
Browse files Browse the repository at this point in the history
…velled files

Fixes #15425
  • Loading branch information
clydin authored and vikerman committed Sep 26, 2019
1 parent 1930bd5 commit d3e4dfa
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 31 deletions.
Expand Up @@ -27,11 +27,13 @@ export function generateEntryPoints(appConfig: {

const entryPoints = [
'polyfills-nomodule-es5',
'runtime',
'polyfills-es5',
'polyfills',
'sw-register',
...extraEntryPoints(appConfig.styles, 'styles'),
...extraEntryPoints(appConfig.scripts, 'scripts'),
'vendor',
'main',
];

Expand Down
Expand Up @@ -8,6 +8,7 @@
// tslint:disable
// TODO: cleanup this file, it's copied as is from Angular CLI.
import { tags, terminal } from '@angular-devkit/core';
import * as path from 'path';


const { bold, green, red, reset, white, yellow } = terminal;
Expand All @@ -23,27 +24,47 @@ export function formatSize(size: number): string {
return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${abbreviations[index]}`;
}

export function generateBundleStats(
info: {
id: string | number;
size?: number;
files: string[];
names?: string[];
entry: boolean;
initial: boolean;
rendered?: boolean;
},
colors: boolean,
): string {
const g = (x: string) => (colors ? bold(green(x)) : x);
const y = (x: string) => (colors ? bold(yellow(x)) : x);

const size = typeof info.size === 'number' ? ` ${formatSize(info.size)}` : '';
const files = info.files.map(f => path.basename(f)).join(', ');
const names = info.names ? ` (${info.names.join(', ')})` : '';
const initial = y(info.entry ? '[entry]' : info.initial ? '[initial]' : '');
const flags = ['rendered', 'recorded']
.map(f => (f && (info as any)[f] ? g(` [${f}]`) : ''))
.join('');

return `chunk {${y(info.id.toString())}} ${g(files)}${names}${size} ${initial}${flags}`;
}

export function generateBuildStats(hash: string, time: number, colors: boolean): string {
const w = (x: string) => colors ? bold(white(x)) : x;
return `Date: ${w(new Date().toISOString())} - Hash: ${w(hash)} - Time: ${w('' + time)}ms`
}

export function statsToString(json: any, statsConfig: any) {
const colors = statsConfig.colors;
const rs = (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;

const changedChunksStats = json.chunks
.filter((chunk: any) => chunk.rendered)
.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 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} ${initial}${flags}`;
return generateBundleStats({ ...chunk, size: asset && asset.size }, colors);
});

const unchangedChunkNumber = json.chunks.length - changedChunksStats.length;
Expand Down
115 changes: 106 additions & 9 deletions packages/angular_devkit/build_angular/src/browser/index.ts
Expand Up @@ -51,6 +51,8 @@ import {
import { readTsconfig } from '../angular-cli-files/utilities/read-tsconfig';
import { augmentAppWithServiceWorker } from '../angular-cli-files/utilities/service-worker';
import {
generateBuildStats,
generateBundleStats,
statsErrorsToString,
statsToString,
statsWarningsToString,
Expand All @@ -64,7 +66,12 @@ import {
normalizeSourceMaps,
} from '../utils';
import { manglingDisabled } from '../utils/mangle-options';
import { CacheKey, ProcessBundleOptions, ProcessBundleResult } from '../utils/process-bundle';
import {
CacheKey,
ProcessBundleFile,
ProcessBundleOptions,
ProcessBundleResult,
} from '../utils/process-bundle';
import { assertCompatibleAngularVersion } from '../utils/version';
import {
generateBrowserWebpackConfigFromContext,
Expand Down Expand Up @@ -202,9 +209,6 @@ export function buildWebpackBrowser(
// Check Angular version.
assertCompatibleAngularVersion(context.workspaceRoot, context.logger);

const loggingFn =
transforms.logging || createBrowserLoggingCallback(!!options.verbose, context.logger);

return from(initialize(options, context, host, transforms.webpackConfiguration)).pipe(
// tslint:disable-next-line: no-big-function
switchMap(({ config: configs, projectRoot }) => {
Expand All @@ -222,14 +226,24 @@ export function buildWebpackBrowser(
`);
}

const useBundleDownleveling =
isDifferentialLoadingNeeded && !(fullDifferential || options.watch);
const startTime = Date.now();

return from(configs).pipe(
// the concurrency parameter (3rd parameter of mergeScan) is deliberately
// set to 1 to make sure the build steps are executed in sequence.
mergeScan(
(lastResult, config) => {
// Make sure to only run the 2nd build step, if 1st one succeeded
if (lastResult.success) {
return runWebpack(config, context, { logging: loggingFn });
return runWebpack(config, context, {
logging:
transforms.logging ||
(useBundleDownleveling
? () => {}
: createBrowserLoggingCallback(!!options.verbose, context.logger)),
});
} else {
return of();
}
Expand All @@ -242,7 +256,19 @@ export function buildWebpackBrowser(
switchMap(async buildEvents => {
configs.length = 0;
const success = buildEvents.every(r => r.success);
if (success) {
if (!success && useBundleDownleveling) {
// If using bundle downleveling then there is only one build
// If it fails show any diagnostic messages and bail
const webpackStats = buildEvents[0].webpackStats;
if (webpackStats && webpackStats.warnings.length > 0) {
context.logger.warn(statsWarningsToString(webpackStats, { colors: true }));
}
if (webpackStats && webpackStats.errors.length > 0) {
context.logger.error(statsErrorsToString(webpackStats, { colors: true }));
}

return { success };
} else if (success) {
let noModuleFiles: EmittedFiles[] | undefined;
let moduleFiles: EmittedFiles[] | undefined;
let files: EmittedFiles[] | undefined;
Expand All @@ -263,7 +289,7 @@ export function buildWebpackBrowser(
noModuleFiles = secondBuild.emittedFiles;
}
} else if (isDifferentialLoadingNeeded && !fullDifferential) {
const { emittedFiles = [] } = firstBuild;
const { emittedFiles = [], webpackStats } = firstBuild;
moduleFiles = [];
noModuleFiles = [];

Expand Down Expand Up @@ -342,7 +368,9 @@ export function buildWebpackBrowser(
filename,
code,
map,
name: file.name,
// id is always present for non-assets
// tslint:disable-next-line: no-non-null-assertion
name: file.id!,
optimizeOnly: true,
});

Expand All @@ -356,7 +384,9 @@ export function buildWebpackBrowser(
filename,
code,
map,
name: file.name,
// id is always present for non-assets
// tslint:disable-next-line: no-non-null-assertion
name: file.id!,
runtime: file.file.startsWith('runtime'),
ignoreOriginal: es5Polyfills,
});
Expand Down Expand Up @@ -600,6 +630,73 @@ export function buildWebpackBrowser(
}

context.logger.info('ES5 bundle generation complete.');

type ArrayElement<A> = A extends ReadonlyArray<infer T> ? T : never;
function generateBundleInfoStats(
id: string | number,
bundle: ProcessBundleFile,
chunk: ArrayElement<webpack.Stats.ToJsonOutput['chunks']> | undefined,
): string {
return generateBundleStats(
{
id,
size: bundle.size,
files: bundle.map ? [bundle.filename, bundle.map.filename] : [bundle.filename],
names: chunk && chunk.names,
entry: !!chunk && chunk.names.includes('runtime'),
initial: !!chunk && chunk.initial,
rendered: true,
},
true,
);
}

let bundleInfoText = '';
const processedNames = new Set<string>();
for (const result of processResults) {
processedNames.add(result.name);

const chunk =
webpackStats &&
webpackStats.chunks &&
webpackStats.chunks.find(c => result.name === c.id.toString());
if (result.original) {
bundleInfoText +=
'\n' + generateBundleInfoStats(result.name, result.original, chunk);
}
if (result.downlevel) {
bundleInfoText +=
'\n' + generateBundleInfoStats(result.name, result.downlevel, chunk);
}
}

if (webpackStats && webpackStats.chunks) {
for (const chunk of webpackStats.chunks) {
if (processedNames.has(chunk.id.toString())) {
continue;
}

const asset =
webpackStats.assets && webpackStats.assets.find(a => a.name === chunk.files[0]);
bundleInfoText +=
'\n' + generateBundleStats({ ...chunk, size: asset && asset.size }, true);
}
}

bundleInfoText +=
'\n' +
generateBuildStats(
(webpackStats && webpackStats.hash) || '<unknown>',
Date.now() - startTime,
true,
);
context.logger.info(bundleInfoText);
if (webpackStats && webpackStats.warnings.length > 0) {
context.logger.warn(statsWarningsToString(webpackStats, { colors: true }));
}
if (webpackStats && webpackStats.errors.length > 0) {
context.logger.error(statsErrorsToString(webpackStats, { colors: true }));
}
} else {
const { emittedFiles = [] } = firstBuild;
files = emittedFiles.filter(x => x.name !== 'polyfills-es5');
Expand Down
Expand Up @@ -19,7 +19,7 @@ export interface ProcessBundleOptions {
filename: string;
code: string;
map?: string;
name?: string;
name: string;
sourceMaps?: boolean;
hiddenSourceMaps?: boolean;
vendorSourceMaps?: boolean;
Expand All @@ -34,7 +34,7 @@ export interface ProcessBundleOptions {
}

export interface ProcessBundleResult {
name?: string;
name: string;
integrity?: string;
original?: ProcessBundleFile;
downlevel?: ProcessBundleFile;
Expand Down
6 changes: 3 additions & 3 deletions tests/legacy-cli/e2e/tests/basic/scripts-array.ts
Expand Up @@ -73,15 +73,15 @@ export default async function () {
await expectFileToMatch(
'dist/test-project/index.html',
oneLineTrim`
<script src="runtime-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="polyfills-es5.js" nomodule defer></script>
<script src="polyfills-es2015.js" type="module"></script>
<script src="scripts.js" defer></script>
<script src="renamed-script.js" defer></script>
<script src="runtime-es2015.js" type="module"></script>
<script src="vendor-es2015.js" type="module"></script>
<script src="main-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="vendor-es5.js" nomodule defer></script>
<script src="main-es2015.js" type="module"></script>
<script src="main-es5.js" nomodule defer></script>
`,
);
Expand Down
6 changes: 3 additions & 3 deletions tests/legacy-cli/e2e/tests/basic/styles-array.ts
Expand Up @@ -61,13 +61,13 @@ export default async function() {
await expectFileToMatch(
'dist/test-project/index.html',
oneLineTrim`
<script src="runtime-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="polyfills-es5.js" nomodule defer></script>
<script src="polyfills-es2015.js" type="module"></script>
<script src="runtime-es2015.js" type="module"></script>
<script src="vendor-es2015.js" type="module"></script>
<script src="main-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="vendor-es5.js" nomodule defer></script>
<script src="main-es2015.js" type="module"></script>
<script src="main-es5.js" nomodule defer></script>
`,
);
Expand Down
6 changes: 3 additions & 3 deletions tests/legacy-cli/e2e/tests/misc/support-safari-10.1.ts
Expand Up @@ -44,15 +44,15 @@ export default async function () {
} else {
await expectFileToMatch('dist/test-project/index.html', oneLineTrim`
<script src="polyfills-nomodule-es5.js" nomodule></script>
<script src="runtime-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="polyfills-es5.js" nomodule defer></script>
<script src="polyfills-es2015.js" type="module"></script>
<script src="styles-es2015.js" type="module"></script>
<script src="styles-es5.js" nomodule defer></script>
<script src="runtime-es2015.js" type="module"></script>
<script src="vendor-es2015.js" type="module"></script>
<script src="main-es2015.js" type="module"></script>
<script src="runtime-es5.js" nomodule defer></script>
<script src="vendor-es5.js" nomodule defer></script>
<script src="main-es2015.js" type="module"></script>
<script src="main-es5.js" nomodule defer></script>
`);
}
Expand Down

0 comments on commit d3e4dfa

Please sign in to comment.