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