Files
create-react-app/packages/react-dev-utils/FileSizeReporter.js
Oskar Persson 9fd98447d8 Fix file size report after build (#5335)
Fixes #5333

printFileSizesAfterBuild calls canReadAsset with an object containing
the file name while measureFileSizesBeforeBuild calls it with just the
name. When canReadAsset only receives the name it returns false since
accessing the `name` property results in undefined.

This commit fixes that by having canReadAssset instead expect only the
file name and making printFileSizesAfterBuild just like
measureFileSizesBeforeBuild only provide the name as argument.
2018-10-06 18:27:30 -04:00

157 lines
4.5 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var fs = require('fs');
var path = require('path');
var chalk = require('chalk');
var filesize = require('filesize');
var recursive = require('recursive-readdir');
var stripAnsi = require('strip-ansi');
var gzipSize = require('gzip-size').sync;
function canReadAsset(asset) {
return (
/\.(js|css)$/.test(asset) &&
!/service-worker\.js/.test(asset) &&
!/precache-manifest\.[0-9a-f]+\.js/.test(asset)
);
}
// Prints a detailed summary of build files.
function printFileSizesAfterBuild(
webpackStats,
previousSizeMap,
buildFolder,
maxBundleGzipSize,
maxChunkGzipSize
) {
var root = previousSizeMap.root;
var sizes = previousSizeMap.sizes;
var assets = (webpackStats.stats || [webpackStats])
.map(stats =>
stats
.toJson({ all: false, assets: true })
.assets.filter(asset => canReadAsset(asset.name))
.map(asset => {
var fileContents = fs.readFileSync(path.join(root, asset.name));
var size = gzipSize(fileContents);
var previousSize = sizes[removeFileNameHash(root, asset.name)];
var difference = getDifferenceLabel(size, previousSize);
return {
folder: path.join(
path.basename(buildFolder),
path.dirname(asset.name)
),
name: path.basename(asset.name),
size: size,
sizeLabel:
filesize(size) + (difference ? ' (' + difference + ')' : ''),
};
})
)
.reduce((single, all) => all.concat(single), []);
assets.sort((a, b) => b.size - a.size);
var longestSizeLabelLength = Math.max.apply(
null,
assets.map(a => stripAnsi(a.sizeLabel).length)
);
var suggestBundleSplitting = false;
assets.forEach(asset => {
var sizeLabel = asset.sizeLabel;
var sizeLength = stripAnsi(sizeLabel).length;
if (sizeLength < longestSizeLabelLength) {
var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength);
sizeLabel += rightPadding;
}
var isMainBundle = asset.name.indexOf('main.') === 0;
var maxRecommendedSize = isMainBundle
? maxBundleGzipSize
: maxChunkGzipSize;
var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize;
if (isLarge && path.extname(asset.name) === '.js') {
suggestBundleSplitting = true;
}
console.log(
' ' +
(isLarge ? chalk.yellow(sizeLabel) : sizeLabel) +
' ' +
chalk.dim(asset.folder + path.sep) +
chalk.cyan(asset.name)
);
});
if (suggestBundleSplitting) {
console.log();
console.log(
chalk.yellow('The bundle size is significantly larger than recommended.')
);
console.log(
chalk.yellow(
'Consider reducing it with code splitting: https://goo.gl/9VhYWB'
)
);
console.log(
chalk.yellow(
'You can also analyze the project dependencies: https://goo.gl/LeUzfb'
)
);
}
}
function removeFileNameHash(buildFolder, fileName) {
return fileName
.replace(buildFolder, '')
.replace(/\\/g, '/')
.replace(
/\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/,
(match, p1, p2, p3, p4) => p1 + p4
);
}
// Input: 1024, 2048
// Output: "(+1 KB)"
function getDifferenceLabel(currentSize, previousSize) {
var FIFTY_KILOBYTES = 1024 * 50;
var difference = currentSize - previousSize;
var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
if (difference >= FIFTY_KILOBYTES) {
return chalk.red('+' + fileSize);
} else if (difference < FIFTY_KILOBYTES && difference > 0) {
return chalk.yellow('+' + fileSize);
} else if (difference < 0) {
return chalk.green(fileSize);
} else {
return '';
}
}
function measureFileSizesBeforeBuild(buildFolder) {
return new Promise(resolve => {
recursive(buildFolder, (err, fileNames) => {
var sizes;
if (!err && fileNames) {
sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
var contents = fs.readFileSync(fileName);
var key = removeFileNameHash(buildFolder, fileName);
memo[key] = gzipSize(contents);
return memo;
}, {});
}
resolve({
root: buildFolder,
sizes: sizes || {},
});
});
});
}
module.exports = {
measureFileSizesBeforeBuild: measureFileSizesBeforeBuild,
printFileSizesAfterBuild: printFileSizesAfterBuild,
};