1. 'use strict';
    
  2. 
    
  3. const Table = require('cli-table');
    
  4. const filesize = require('filesize');
    
  5. const chalk = require('chalk');
    
  6. const join = require('path').join;
    
  7. const fs = require('fs');
    
  8. const mkdirp = require('mkdirp');
    
  9. 
    
  10. const BUNDLE_SIZES_FILE_NAME = join(__dirname, '../../build/bundle-sizes.json');
    
  11. const prevBuildResults = fs.existsSync(BUNDLE_SIZES_FILE_NAME)
    
  12.   ? require(BUNDLE_SIZES_FILE_NAME)
    
  13.   : {bundleSizes: []};
    
  14. 
    
  15. const currentBuildResults = {
    
  16.   // Mutated inside build.js during a build run.
    
  17.   bundleSizes: [],
    
  18. };
    
  19. 
    
  20. function saveResults() {
    
  21.   if (process.env.CIRCLE_NODE_TOTAL) {
    
  22.     // In CI, write the bundle sizes to a subdirectory and append the node index
    
  23.     // to the filename. A downstream job will consolidate these into a
    
  24.     // single file.
    
  25.     const nodeIndex = process.env.CIRCLE_NODE_INDEX;
    
  26.     mkdirp.sync('build/sizes');
    
  27.     fs.writeFileSync(
    
  28.       join('build', 'sizes', `bundle-sizes-${nodeIndex}.json`),
    
  29.       JSON.stringify(currentBuildResults, null, 2)
    
  30.     );
    
  31.   } else {
    
  32.     // Write all the bundle sizes to a single JSON file.
    
  33.     fs.writeFileSync(
    
  34.       BUNDLE_SIZES_FILE_NAME,
    
  35.       JSON.stringify(currentBuildResults, null, 2)
    
  36.     );
    
  37.   }
    
  38. }
    
  39. 
    
  40. function fractionalChange(prev, current) {
    
  41.   return (current - prev) / prev;
    
  42. }
    
  43. 
    
  44. function percentChangeString(change) {
    
  45.   if (!isFinite(change)) {
    
  46.     // When a new package is created
    
  47.     return 'n/a';
    
  48.   }
    
  49.   const formatted = (change * 100).toFixed(1);
    
  50.   if (/^-|^0(?:\.0+)$/.test(formatted)) {
    
  51.     return `${formatted}%`;
    
  52.   } else {
    
  53.     return `+${formatted}%`;
    
  54.   }
    
  55. }
    
  56. 
    
  57. const resultsHeaders = [
    
  58.   'Bundle',
    
  59.   'Prev Size',
    
  60.   'Current Size',
    
  61.   'Diff',
    
  62.   'Prev Gzip',
    
  63.   'Current Gzip',
    
  64.   'Diff',
    
  65. ];
    
  66. 
    
  67. function generateResultsArray(current, prevResults) {
    
  68.   return current.bundleSizes
    
  69.     .map(result => {
    
  70.       const prev = prevResults.bundleSizes.filter(
    
  71.         res =>
    
  72.           res.filename === result.filename &&
    
  73.           res.bundleType === result.bundleType
    
  74.       )[0];
    
  75.       if (result === prev) {
    
  76.         // We didn't rebuild this bundle.
    
  77.         return;
    
  78.       }
    
  79. 
    
  80.       const size = result.size;
    
  81.       const gzip = result.gzip;
    
  82.       let prevSize = prev ? prev.size : 0;
    
  83.       let prevGzip = prev ? prev.gzip : 0;
    
  84. 
    
  85.       return {
    
  86.         filename: result.filename,
    
  87.         bundleType: result.bundleType,
    
  88.         packageName: result.packageName,
    
  89.         prevSize: filesize(prevSize),
    
  90.         prevFileSize: filesize(size),
    
  91.         prevFileSizeChange: fractionalChange(prevSize, size),
    
  92.         prevFileSizeAbsoluteChange: size - prevSize,
    
  93.         prevGzip: filesize(prevGzip),
    
  94.         prevGzipSize: filesize(gzip),
    
  95.         prevGzipSizeChange: fractionalChange(prevGzip, gzip),
    
  96.         prevGzipSizeAbsoluteChange: gzip - prevGzip,
    
  97.       };
    
  98.       // Strip any nulls
    
  99.     })
    
  100.     .filter(f => f);
    
  101. }
    
  102. 
    
  103. function printResults() {
    
  104.   const table = new Table({
    
  105.     head: resultsHeaders.map(label => chalk.gray.yellow(label)),
    
  106.   });
    
  107. 
    
  108.   const results = generateResultsArray(currentBuildResults, prevBuildResults);
    
  109.   results.forEach(result => {
    
  110.     table.push([
    
  111.       chalk.white.bold(`${result.filename}  (${result.bundleType})`),
    
  112.       chalk.gray.bold(result.prevSize),
    
  113.       chalk.white.bold(result.prevFileSize),
    
  114.       percentChangeString(result.prevFileSizeChange),
    
  115.       chalk.gray.bold(result.prevGzip),
    
  116.       chalk.white.bold(result.prevGzipSize),
    
  117.       percentChangeString(result.prevGzipSizeChange),
    
  118.     ]);
    
  119.   });
    
  120. 
    
  121.   return table.toString();
    
  122. }
    
  123. 
    
  124. module.exports = {
    
  125.   currentBuildResults,
    
  126.   generateResultsArray,
    
  127.   printResults,
    
  128.   saveResults,
    
  129.   resultsHeaders,
    
  130. };