build.js 6.48 KB
Newer Older
Christopher Chedeau's avatar
Christopher Chedeau committed
1
2
3
4
5
6
7
8
9
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

10
11
process.env.NODE_ENV = 'production';

12
var chalk = require('chalk');
13
var fs = require('fs');
14
var path = require('path');
15
var filesize = require('filesize');
16
var gzipSize = require('gzip-size').sync;
17
var rimrafSync = require('rimraf').sync;
18
var webpack = require('webpack');
19
var config = require('../config/webpack.config.prod');
20
var paths = require('../config/paths');
Elijah Manor's avatar
Elijah Manor committed
21
var recursive = require('recursive-readdir');
22
var stripAnsi = require('strip-ansi');
23

Elijah Manor's avatar
Elijah Manor committed
24
function removeFileNameHash(fileName) {
Dan Abramov's avatar
Dan Abramov committed
25
26
27
  return fileName
    .replace(paths.appBuild, '')
    .replace(/\/?(.*)(\.\w+)(\.js|\.css)/, (match, p1, p2, p3) => p1 + p3);
Elijah Manor's avatar
Elijah Manor committed
28
}
29

Dan Abramov's avatar
Dan Abramov committed
30
function getDifferenceLabel(currentSize, previousSize) {
31
  var FIFTY_KILOBYTES = 1024 * 50;
Elijah Manor's avatar
Elijah Manor committed
32
  var difference = currentSize - previousSize;
33
34
  var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
  if (difference >= FIFTY_KILOBYTES) {
Elijah Manor's avatar
Elijah Manor committed
35
    return chalk.red('+' + fileSize);
36
37
38
39
40
41
  } else if (difference < FIFTY_KILOBYTES && difference > 0) {
    return chalk.yellow('+' + fileSize);
  } else if (difference < 0) {
    return chalk.green(fileSize);
  } else {
    return '';
42
  }
Elijah Manor's avatar
Elijah Manor committed
43
}
44

Dan Abramov's avatar
Dan Abramov committed
45
46
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
Dan Abramov's avatar
Dan Abramov committed
47
recursive(paths.appBuild, (err, fileNames) => {
Dan Abramov's avatar
Dan Abramov committed
48
  var previousSizeMap = (fileNames || [])
Dan Abramov's avatar
Dan Abramov committed
49
    .filter(fileName => /\.(js|css)$/.test(fileName))
Elijah Manor's avatar
Elijah Manor committed
50
51
52
53
54
    .reduce((memo, fileName) => {
      var contents = fs.readFileSync(fileName);
      var key = removeFileNameHash(fileName);
      memo[key] = gzipSize(contents);
      return memo;
Dan Abramov's avatar
Dan Abramov committed
55
    }, {});
56

Elijah Manor's avatar
Elijah Manor committed
57
58
59
60
  // Remove all content but keep the directory so that
  // if you're in it, you don't end up in Trash
  rimrafSync(paths.appBuild + '/*');

Dan Abramov's avatar
Dan Abramov committed
61
  // Start the webpack build
Elijah Manor's avatar
Elijah Manor committed
62
63
  build(previousSizeMap);
});
64

Dan Abramov's avatar
Dan Abramov committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
function printFileSizes(stats, previousSizeMap) {
  var assets = stats.toJson().assets
    .filter(asset => /\.(js|css)$/.test(asset.name))
    .map(asset => {
      var fileContents = fs.readFileSync(paths.appBuild + '/' + asset.name);
      var size = gzipSize(fileContents);
      var previousSize = previousSizeMap[removeFileNameHash(asset.name)];
      var difference = getDifferenceLabel(size, previousSize);
      return {
        folder: path.join('build', path.dirname(asset.name)),
        name: path.basename(asset.name),
        size: size,
        sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : '')
      };
    });
  assets.sort((a, b) => b.size - a.size);

  var longestSizeLabelLength = Math.max.apply(null,
    assets.map(a => stripAnsi(a.sizeLabel).length)
  );
  assets.forEach(asset => {
    var sizeLabel = asset.sizeLabel;
    var sizeLength = stripAnsi(sizeLabel).length;
    if (sizeLength < longestSizeLabelLength) {
      var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength);
      sizeLabel += rightPadding;
    }
    console.log(
      '  ' + sizeLabel +
      '  ' + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name)
    );
  });
}

Elijah Manor's avatar
Elijah Manor committed
99
100
function build(previousSizeMap) {
  console.log('Creating an optimized production build...');
Dan Abramov's avatar
Dan Abramov committed
101
  webpack(config).run((err, stats) => {
Elijah Manor's avatar
Elijah Manor committed
102
103
104
105
    if (err) {
      console.error('Failed to create a production build. Reason:');
      console.error(err.message || err);
      process.exit(1);
106
    }
107

Elijah Manor's avatar
Elijah Manor committed
108
    console.log(chalk.green('Compiled successfully.'));
109
    console.log();
Elijah Manor's avatar
Elijah Manor committed
110
111

    console.log('File sizes after gzip:');
112
    console.log();
Dan Abramov's avatar
Dan Abramov committed
113
    printFileSizes(stats, previousSizeMap);
114
    console.log();
Elijah Manor's avatar
Elijah Manor committed
115
116
117

    var openCommand = process.platform === 'win32' ? 'start' : 'open';
    var homepagePath = require(paths.appPackageJson).homepage;
Dan Abramov's avatar
Dan Abramov committed
118
119
120
121
    var publicPath = config.output.publicPath;
    if (homepagePath && homepagePath.indexOf('.github.io/') !== -1) {
      // "homepage": "http://user.github.io/project"
      console.log('You can now deploy them to ' + chalk.green(homepagePath) + ':');
Elijah Manor's avatar
Elijah Manor committed
122
      console.log();
Dan Abramov's avatar
Dan Abramov committed
123
124
125
126
127
128
129
      console.log('  ' + chalk.blue('git') + chalk.cyan(' commit -am ') + chalk.yellow('"Save local changes"'));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' checkout -B gh-pages'));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' add -f build'));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' commit -am ' + chalk.yellow('"Rebuild website"')));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' filter-branch -f --prune-empty --subdirectory-filter build'));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' push -f origin gh-pages'));
      console.log('  ' + chalk.blue('git') + chalk.cyan(' checkout -'));
Elijah Manor's avatar
Elijah Manor committed
130
      console.log();
Dan Abramov's avatar
Dan Abramov committed
131
132
133
134
135
136
      console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.');
      console.log('You can control this with the ' + chalk.green('homepage') + ' field in your '  + chalk.cyan('package.json') + '.');
    } else if (publicPath !== '/') {
      // "homepage": "http://mywebsite.com/project"
      console.log('The project was built assuming it is hosted at ' + chalk.green(publicPath) + '.');
      console.log('You can control this with the ' + chalk.green('homepage') + ' field in your '  + chalk.cyan('package.json') + '.');
Elijah Manor's avatar
Elijah Manor committed
137
    } else {
Dan Abramov's avatar
Dan Abramov committed
138
139
      // no homepage or "homepage": "http://mywebsite.com"
      console.log('You can now deploy them or serve them with a static server:');
Elijah Manor's avatar
Elijah Manor committed
140
      console.log();
Dan Abramov's avatar
Dan Abramov committed
141
142
143
      console.log('  ' + chalk.blue('npm') +  chalk.cyan(' install -g pushstate-server'));
      console.log('  ' + chalk.blue('pushstate-server') + chalk.cyan(' build'));
      console.log('  ' + chalk.blue(openCommand) + chalk.cyan(' http://localhost:9000'));
Elijah Manor's avatar
Elijah Manor committed
144
      console.log();
Dan Abramov's avatar
Dan Abramov committed
145
146
147
148
149
150
151
152
153
154
155
      console.log('The project was built assuming it is hosted at the server root.');
      if (homepagePath) {
        // "homepage": "http://mywebsite.com"
        console.log('You can control this with the ' + chalk.green('homepage') + ' field in your '  + chalk.cyan('package.json') + '.');
      } else {
        // no homepage
        console.log('To override this, specify the ' + chalk.green('homepage') + ' in your '  + chalk.cyan('package.json') + '.');
        console.log('For example, add this to build it for GitHub Pages:')
        console.log();
        console.log('  ' + chalk.green('"homepage"') + chalk.cyan(': ') + chalk.green('"http://myname.github.io/myapp"') + chalk.cyan(','));
      }
Elijah Manor's avatar
Elijah Manor committed
156
    }
157
    console.log();
Elijah Manor's avatar
Elijah Manor committed
158
159
  });
}