| 1 | /**
|
|---|
| 2 | * Copyright (c) 2015-present, Facebook, Inc.
|
|---|
| 3 | *
|
|---|
| 4 | * This source code is licensed under the MIT license found in the
|
|---|
| 5 | * LICENSE file in the root directory of this source tree.
|
|---|
| 6 | */
|
|---|
| 7 |
|
|---|
| 8 | 'use strict';
|
|---|
| 9 |
|
|---|
| 10 | var fs = require('fs');
|
|---|
| 11 | var path = require('path');
|
|---|
| 12 | var chalk = require('chalk');
|
|---|
| 13 | var filesize = require('filesize');
|
|---|
| 14 | var recursive = require('recursive-readdir');
|
|---|
| 15 | var stripAnsi = require('strip-ansi');
|
|---|
| 16 | var gzipSize = require('gzip-size').sync;
|
|---|
| 17 |
|
|---|
| 18 | function canReadAsset(asset) {
|
|---|
| 19 | return (
|
|---|
| 20 | /\.(js|css)$/.test(asset) &&
|
|---|
| 21 | !/service-worker\.js/.test(asset) &&
|
|---|
| 22 | !/precache-manifest\.[0-9a-f]+\.js/.test(asset)
|
|---|
| 23 | );
|
|---|
| 24 | }
|
|---|
| 25 |
|
|---|
| 26 | // Prints a detailed summary of build files.
|
|---|
| 27 | function printFileSizesAfterBuild(
|
|---|
| 28 | webpackStats,
|
|---|
| 29 | previousSizeMap,
|
|---|
| 30 | buildFolder,
|
|---|
| 31 | maxBundleGzipSize,
|
|---|
| 32 | maxChunkGzipSize
|
|---|
| 33 | ) {
|
|---|
| 34 | var root = previousSizeMap.root;
|
|---|
| 35 | var sizes = previousSizeMap.sizes;
|
|---|
| 36 | var assets = (webpackStats.stats || [webpackStats])
|
|---|
| 37 | .map(stats =>
|
|---|
| 38 | stats
|
|---|
| 39 | .toJson({ all: false, assets: true })
|
|---|
| 40 | .assets.filter(asset => canReadAsset(asset.name))
|
|---|
| 41 | .map(asset => {
|
|---|
| 42 | var fileContents = fs.readFileSync(path.join(root, asset.name));
|
|---|
| 43 | var size = gzipSize(fileContents);
|
|---|
| 44 | var previousSize = sizes[removeFileNameHash(root, asset.name)];
|
|---|
| 45 | var difference = getDifferenceLabel(size, previousSize);
|
|---|
| 46 | return {
|
|---|
| 47 | folder: path.join(
|
|---|
| 48 | path.basename(buildFolder),
|
|---|
| 49 | path.dirname(asset.name)
|
|---|
| 50 | ),
|
|---|
| 51 | name: path.basename(asset.name),
|
|---|
| 52 | size: size,
|
|---|
| 53 | sizeLabel:
|
|---|
| 54 | filesize(size) + (difference ? ' (' + difference + ')' : ''),
|
|---|
| 55 | };
|
|---|
| 56 | })
|
|---|
| 57 | )
|
|---|
| 58 | .reduce((single, all) => all.concat(single), []);
|
|---|
| 59 | assets.sort((a, b) => b.size - a.size);
|
|---|
| 60 | var longestSizeLabelLength = Math.max.apply(
|
|---|
| 61 | null,
|
|---|
| 62 | assets.map(a => stripAnsi(a.sizeLabel).length)
|
|---|
| 63 | );
|
|---|
| 64 | var suggestBundleSplitting = false;
|
|---|
| 65 | assets.forEach(asset => {
|
|---|
| 66 | var sizeLabel = asset.sizeLabel;
|
|---|
| 67 | var sizeLength = stripAnsi(sizeLabel).length;
|
|---|
| 68 | if (sizeLength < longestSizeLabelLength) {
|
|---|
| 69 | var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength);
|
|---|
| 70 | sizeLabel += rightPadding;
|
|---|
| 71 | }
|
|---|
| 72 | var isMainBundle = asset.name.indexOf('main.') === 0;
|
|---|
| 73 | var maxRecommendedSize = isMainBundle
|
|---|
| 74 | ? maxBundleGzipSize
|
|---|
| 75 | : maxChunkGzipSize;
|
|---|
| 76 | var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize;
|
|---|
| 77 | if (isLarge && path.extname(asset.name) === '.js') {
|
|---|
| 78 | suggestBundleSplitting = true;
|
|---|
| 79 | }
|
|---|
| 80 | console.log(
|
|---|
| 81 | ' ' +
|
|---|
| 82 | (isLarge ? chalk.yellow(sizeLabel) : sizeLabel) +
|
|---|
| 83 | ' ' +
|
|---|
| 84 | chalk.dim(asset.folder + path.sep) +
|
|---|
| 85 | chalk.cyan(asset.name)
|
|---|
| 86 | );
|
|---|
| 87 | });
|
|---|
| 88 | if (suggestBundleSplitting) {
|
|---|
| 89 | console.log();
|
|---|
| 90 | console.log(
|
|---|
| 91 | chalk.yellow('The bundle size is significantly larger than recommended.')
|
|---|
| 92 | );
|
|---|
| 93 | console.log(
|
|---|
| 94 | chalk.yellow(
|
|---|
| 95 | 'Consider reducing it with code splitting: https://goo.gl/9VhYWB'
|
|---|
| 96 | )
|
|---|
| 97 | );
|
|---|
| 98 | console.log(
|
|---|
| 99 | chalk.yellow(
|
|---|
| 100 | 'You can also analyze the project dependencies: https://goo.gl/LeUzfb'
|
|---|
| 101 | )
|
|---|
| 102 | );
|
|---|
| 103 | }
|
|---|
| 104 | }
|
|---|
| 105 |
|
|---|
| 106 | function removeFileNameHash(buildFolder, fileName) {
|
|---|
| 107 | return fileName
|
|---|
| 108 | .replace(buildFolder, '')
|
|---|
| 109 | .replace(/\\/g, '/')
|
|---|
| 110 | .replace(
|
|---|
| 111 | /\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/,
|
|---|
| 112 | (match, p1, p2, p3, p4) => p1 + p4
|
|---|
| 113 | );
|
|---|
| 114 | }
|
|---|
| 115 |
|
|---|
| 116 | // Input: 1024, 2048
|
|---|
| 117 | // Output: "(+1 KB)"
|
|---|
| 118 | function getDifferenceLabel(currentSize, previousSize) {
|
|---|
| 119 | var FIFTY_KILOBYTES = 1024 * 50;
|
|---|
| 120 | var difference = currentSize - previousSize;
|
|---|
| 121 | var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
|
|---|
| 122 | if (difference >= FIFTY_KILOBYTES) {
|
|---|
| 123 | return chalk.red('+' + fileSize);
|
|---|
| 124 | } else if (difference < FIFTY_KILOBYTES && difference > 0) {
|
|---|
| 125 | return chalk.yellow('+' + fileSize);
|
|---|
| 126 | } else if (difference < 0) {
|
|---|
| 127 | return chalk.green(fileSize);
|
|---|
| 128 | } else {
|
|---|
| 129 | return '';
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | function measureFileSizesBeforeBuild(buildFolder) {
|
|---|
| 134 | return new Promise(resolve => {
|
|---|
| 135 | recursive(buildFolder, (err, fileNames) => {
|
|---|
| 136 | var sizes;
|
|---|
| 137 | if (!err && fileNames) {
|
|---|
| 138 | sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
|
|---|
| 139 | var contents = fs.readFileSync(fileName);
|
|---|
| 140 | var key = removeFileNameHash(buildFolder, fileName);
|
|---|
| 141 | memo[key] = gzipSize(contents);
|
|---|
| 142 | return memo;
|
|---|
| 143 | }, {});
|
|---|
| 144 | }
|
|---|
| 145 | resolve({
|
|---|
| 146 | root: buildFolder,
|
|---|
| 147 | sizes: sizes || {},
|
|---|
| 148 | });
|
|---|
| 149 | });
|
|---|
| 150 | });
|
|---|
| 151 | }
|
|---|
| 152 |
|
|---|
| 153 | module.exports = {
|
|---|
| 154 | measureFileSizesBeforeBuild: measureFileSizesBeforeBuild,
|
|---|
| 155 | printFileSizesAfterBuild: printFileSizesAfterBuild,
|
|---|
| 156 | };
|
|---|