main
js 208 lines 6.45 KB
Raw
1 'use strict';
2
3 // Do this as the first thing so that any code reading it knows the right env.
4 process.env.BABEL_ENV = 'production';
5 process.env.NODE_ENV = 'production';
6
7 // Makes the script crash on unhandled rejections instead of silently
8 // ignoring them. In the future, promise rejections that are not handled will
9 // terminate the Node.js process with a non-zero exit code.
10 process.on('unhandledRejection', err => {
11 throw err;
12 });
13
14 // Ensure environment variables are read.
15 require('../config/env');
16
17 const path = require('path');
18 const chalk = require('chalk');
19 const fs = require('fs-extra');
20 const webpack = require('webpack');
21 const configFactory = require('../config/webpack.config');
22 const paths = require('../config/paths');
23 const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
24 const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
25 const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
26 const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
27 const printBuildError = require('react-dev-utils/printBuildError');
28
29 const measureFileSizesBeforeBuild =
30 FileSizeReporter.measureFileSizesBeforeBuild;
31 const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
32 const useYarn = fs.existsSync(paths.yarnLockFile);
33
34 // These sizes are pretty large. We'll warn for bundles exceeding them.
35 const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
36 const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
37
38 const isInteractive = process.stdout.isTTY;
39
40 // Warn and crash if required files are missing
41 if (!checkRequiredFiles([paths.appIndexJs])) {
42 process.exit(1);
43 }
44
45 const argv = process.argv.slice(2);
46 const writeStatsJson = argv.indexOf('--stats') !== -1;
47
48 // Generate configuration
49 const config = configFactory('production');
50
51 // We require that you explicitly set browsers and do not fall back to
52 // browserslist defaults.
53 const {checkBrowsers} = require('react-dev-utils/browsersHelper');
54 checkBrowsers(paths.appPath, isInteractive)
55 .then(() => {
56 // First, read the current file sizes in build directory.
57 // This lets us display how much they changed later.
58 return measureFileSizesBeforeBuild(paths.appBuild);
59 })
60 .then(previousFileSizes => {
61 // Remove all content but keep the directory so that
62 // if you're in it, you don't end up in Trash
63 fs.emptyDirSync(paths.appBuild);
64 // Merge with the public folder
65 copyPublicFolder();
66 // Start the webpack build
67 return build(previousFileSizes);
68 })
69 .then(
70 ({stats, previousFileSizes, warnings}) => {
71 if (warnings.length) {
72 console.log(chalk.yellow('Compiled with warnings.\n'));
73 console.log(warnings.join('\n\n'));
74 console.log(
75 '\nSearch for the ' +
76 chalk.underline(chalk.yellow('keywords')) +
77 ' to learn more about each warning.'
78 );
79 console.log(
80 'To ignore, add ' +
81 chalk.cyan('// eslint-disable-next-line') +
82 ' to the line before.\n'
83 );
84 } else {
85 console.log(chalk.green('Compiled successfully.\n'));
86 }
87
88 console.log('File sizes after gzip:\n');
89 printFileSizesAfterBuild(
90 stats,
91 previousFileSizes,
92 paths.appBuild,
93 WARN_AFTER_BUNDLE_GZIP_SIZE,
94 WARN_AFTER_CHUNK_GZIP_SIZE
95 );
96 console.log();
97
98 const appPackage = require(paths.appPackageJson);
99 const publicUrl = paths.publicUrlOrPath;
100 const publicPath = config.output.publicPath;
101 const buildFolder = path.relative(process.cwd(), paths.appBuild);
102 printHostingInstructions(
103 appPackage,
104 publicUrl,
105 publicPath,
106 buildFolder,
107 useYarn
108 );
109 },
110 err => {
111 const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
112 if (tscCompileOnError) {
113 console.log(
114 chalk.yellow(
115 'Compiled with the following type errors (you may want to check these before deploying your app):\n'
116 )
117 );
118 printBuildError(err);
119 } else {
120 console.log(chalk.red('Failed to compile.\n'));
121 printBuildError(err);
122 process.exit(1);
123 }
124 }
125 )
126 .catch(err => {
127 if (err && err.message) {
128 console.log(err.message);
129 }
130 process.exit(1);
131 });
132
133 // Create the production build and print the deployment instructions.
134 function build(previousFileSizes) {
135 console.log('Creating an optimized production build...');
136
137 const compiler = webpack(config);
138 return new Promise((resolve, reject) => {
139 compiler.run((err, stats) => {
140 let messages;
141 if (err) {
142 if (!err.message) {
143 return reject(err);
144 }
145
146 let errMessage = err.message;
147
148 // Add additional information for postcss errors
149 if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
150 errMessage +=
151 '\nCompileError: Begins at CSS selector ' +
152 err['postcssNode'].selector;
153 }
154
155 messages = formatWebpackMessages({
156 errors: [errMessage],
157 warnings: [],
158 });
159 } else {
160 messages = formatWebpackMessages(
161 stats.toJson({all: false, warnings: true, errors: true})
162 );
163 }
164 if (messages.errors.length) {
165 // Only keep the first error. Others are often indicative
166 // of the same problem, but confuse the reader with noise.
167 if (messages.errors.length > 1) {
168 messages.errors.length = 1;
169 }
170 return reject(new Error(messages.errors.join('\n\n')));
171 }
172 if (
173 process.env.CI &&
174 (typeof process.env.CI !== 'string' ||
175 process.env.CI.toLowerCase() !== 'false') &&
176 messages.warnings.length
177 ) {
178 // Ignore sourcemap warnings in CI builds. See #8227 for more info.
179 const filteredWarnings = messages.warnings.filter(
180 w => !/Failed to parse source map/.test(w)
181 );
182 if (filteredWarnings.length) {
183 console.log(
184 chalk.yellow(
185 '\nTreating warnings as errors because process.env.CI = true.\n' +
186 'Most CI servers set it automatically.\n'
187 )
188 );
189 return reject(new Error(filteredWarnings.join('\n\n')));
190 }
191 }
192
193 const resolveArgs = {
194 stats,
195 previousFileSizes,
196 warnings: messages.warnings,
197 };
198
199 return resolve(resolveArgs);
200 });
201 });
202 }
203
204 function copyPublicFolder() {
205 fs.copySync('public', 'build', {
206 dereference: true,
207 });
208 }