build.js 6.32 KB
Newer Older
zhanghaozhe committed
1
"use strict"
2 3

// Do this as the first thing so that any code reading it knows the right env.
zhanghaozhe committed
4 5 6
process.env.BABEL_ENV = "production"
process.env.NODE_ENV = "production"
process.env.REACT_APP_BUILD_ENV = "production"
7 8 9 10

// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
zhanghaozhe committed
11 12 13
process.on("unhandledRejection", (err) => {
  throw err
})
14 15

// Ensure environment variables are read.
zhanghaozhe committed
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
require("../config/env")

const path = require("path")
const chalk = require("react-dev-utils/chalk")
const fs = require("fs-extra")
const webpack = require("webpack")
const bfj = require("bfj")
const configFactory = require("../config/webpack.config")
const paths = require("../config/paths")
const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles")
const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages")
const printHostingInstructions = require("react-dev-utils/printHostingInstructions")
const FileSizeReporter = require("react-dev-utils/FileSizeReporter")
const printBuildError = require("react-dev-utils/printBuildError")
const childProcess = require("child_process")
const deploymentBranches = ["master", "pre", "dev"]
const version = childProcess.execSync("git symbolic-ref --short -q HEAD", {
  encoding: "utf8",
})
if (!deploymentBranches.includes(version.trim())) {
  console.log(
    chalk.yellow(
      `当前不在上线分支,请切换至上线分支(${deploymentBranches.join(
        "/"
      )})打包\n`
    )
  )
xuzhenghua committed
43 44 45
  return
}

zhanghaozhe committed
46 47 48
const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild
const useYarn = fs.existsSync(paths.yarnLockFile)
49 50

// These sizes are pretty large. We'll warn for bundles exceeding them.
zhanghaozhe committed
51 52
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024
53

zhanghaozhe committed
54
const isInteractive = process.stdout.isTTY
55 56 57

// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
zhanghaozhe committed
58
  process.exit(1)
59 60 61
}

// Process CLI arguments
zhanghaozhe committed
62 63
const argv = process.argv.slice(2)
const writeStatsJson = argv.indexOf("--stats") !== -1
64 65

// Generate configuration
zhanghaozhe committed
66
const config = configFactory("production")
67 68 69

// We require that you explicitly set browsers and do not fall back to
// browserslist defaults.
zhanghaozhe committed
70
const { checkBrowsers } = require("react-dev-utils/browsersHelper")
71 72 73 74
checkBrowsers(paths.appPath, isInteractive)
  .then(() => {
    // First, read the current file sizes in build directory.
    // This lets us display how much they changed later.
zhanghaozhe committed
75
    return measureFileSizesBeforeBuild(paths.appBuild)
76
  })
zhanghaozhe committed
77
  .then((previousFileSizes) => {
78 79
    // Remove all content but keep the directory so that
    // if you're in it, you don't end up in Trash
zhanghaozhe committed
80
    fs.emptyDirSync(paths.appBuild)
81
    // Merge with the public folder
zhanghaozhe committed
82
    copyPublicFolder()
83
    // Start the webpack build
zhanghaozhe committed
84
    return build(previousFileSizes)
85 86 87 88
  })
  .then(
    ({ stats, previousFileSizes, warnings }) => {
      if (warnings.length) {
zhanghaozhe committed
89 90
        console.log(chalk.yellow("Compiled with warnings.\n"))
        console.log(warnings.join("\n\n"))
91
        console.log(
zhanghaozhe committed
92 93 94 95
          "\nSearch for the " +
            chalk.underline(chalk.yellow("keywords")) +
            " to learn more about each warning."
        )
96
        console.log(
zhanghaozhe committed
97 98 99 100
          "To ignore, add " +
            chalk.cyan("// eslint-disable-next-line") +
            " to the line before.\n"
        )
101
      } else {
zhanghaozhe committed
102
        console.log(chalk.green("Compiled successfully.\n"))
103 104
      }

zhanghaozhe committed
105
      console.log("File sizes after gzip:\n")
106 107 108 109 110 111
      printFileSizesAfterBuild(
        stats,
        previousFileSizes,
        paths.appBuild,
        WARN_AFTER_BUNDLE_GZIP_SIZE,
        WARN_AFTER_CHUNK_GZIP_SIZE
zhanghaozhe committed
112 113
      )
      console.log()
114

zhanghaozhe committed
115 116 117 118
      const appPackage = require(paths.appPackageJson)
      const publicUrl = paths.publicUrl
      const publicPath = config.output.publicPath
      const buildFolder = path.relative(process.cwd(), paths.appBuild)
119 120 121 122 123 124
      printHostingInstructions(
        appPackage,
        publicUrl,
        publicPath,
        buildFolder,
        useYarn
zhanghaozhe committed
125
      )
126
    },
zhanghaozhe committed
127 128 129 130
    (err) => {
      console.log(chalk.red("Failed to compile.\n"))
      printBuildError(err)
      process.exit(1)
131 132
    }
  )
zhanghaozhe committed
133
  .catch((err) => {
134
    if (err && err.message) {
zhanghaozhe committed
135
      console.log(err.message)
136
    }
zhanghaozhe committed
137 138
    process.exit(1)
  })
139 140 141

// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
zhanghaozhe committed
142
  console.log("Creating an optimized production build...")
143

zhanghaozhe committed
144
  let compiler = webpack(config)
145 146
  return new Promise((resolve, reject) => {
    compiler.run((err, stats) => {
zhanghaozhe committed
147
      let messages
148 149
      if (err) {
        if (!err.message) {
zhanghaozhe committed
150
          return reject(err)
151 152 153 154
        }
        messages = formatWebpackMessages({
          errors: [err.message],
          warnings: [],
zhanghaozhe committed
155
        })
156 157 158
      } else {
        messages = formatWebpackMessages(
          stats.toJson({ all: false, warnings: true, errors: true })
zhanghaozhe committed
159
        )
160 161 162 163 164
      }
      if (messages.errors.length) {
        // Only keep the first error. Others are often indicative
        // of the same problem, but confuse the reader with noise.
        if (messages.errors.length > 1) {
zhanghaozhe committed
165
          messages.errors.length = 1
166
        }
zhanghaozhe committed
167
        return reject(new Error(messages.errors.join("\n\n")))
168 169 170
      }
      if (
        process.env.CI &&
zhanghaozhe committed
171 172
        (typeof process.env.CI !== "string" ||
          process.env.CI.toLowerCase() !== "false") &&
173 174 175 176
        messages.warnings.length
      ) {
        console.log(
          chalk.yellow(
zhanghaozhe committed
177 178
            "\nTreating warnings as errors because process.env.CI = true.\n" +
              "Most CI servers set it automatically.\n"
179
          )
zhanghaozhe committed
180 181
        )
        return reject(new Error(messages.warnings.join("\n\n")))
182 183 184 185 186 187
      }

      const resolveArgs = {
        stats,
        previousFileSizes,
        warnings: messages.warnings,
zhanghaozhe committed
188
      }
189 190
      if (writeStatsJson) {
        return bfj
zhanghaozhe committed
191
          .write(paths.appBuild + "/bundle-stats.json", stats.toJson())
192
          .then(() => resolve(resolveArgs))
zhanghaozhe committed
193
          .catch((error) => reject(new Error(error)))
194 195
      }

zhanghaozhe committed
196 197 198
      return resolve(resolveArgs)
    })
  })
199 200 201 202 203
}

function copyPublicFolder() {
  fs.copySync(paths.appPublic, paths.appBuild, {
    dereference: true,
zhanghaozhe committed
204 205
    filter: (file) => file !== paths.appHtml,
  })
206
}