1 | 'use strict'
|
2 |
|
3 |
|
4 | process.env.BABEL_ENV = 'production'
|
5 | process.env.NODE_ENV = 'production'
|
6 |
|
7 | process.on('unhandledRejection', err => {
|
8 | throw err
|
9 | })
|
10 |
|
11 | const fs = require('fs-extra')
|
12 | const chalk = require('chalk')
|
13 | const path = require('path')
|
14 | const ora = require('ora')
|
15 | const webpack = require('webpack')
|
16 | const getEntry = require('../libs/entry')
|
17 | const { uploadDir } = require('../libs/ftp')
|
18 | const config = require('../config')
|
19 | const paths = config.paths
|
20 | const getWebpackConfig = require('../webpack/webpack.prod.conf')
|
21 | const maraConf = require(paths.marauder)
|
22 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages')
|
23 | const { hybridDevPublish, hybridTestPublish } = require('../libs/hybrid')
|
24 | const printBuildError = require('../libs/printBuildError')
|
25 | const buildReporter = require('../libs/buildReporter')
|
26 | const prehandleConfig = require('../libs/prehandleConfig')
|
27 |
|
28 |
|
29 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024
|
30 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024
|
31 |
|
32 | const spinner = ora('building for production...')
|
33 |
|
34 | function build({ entryInput, dist }) {
|
35 | let webpackConfig = getWebpackConfig(entryInput)
|
36 | webpackConfig = prehandleConfig('build', webpackConfig)
|
37 | const compiler = webpack(webpackConfig)
|
38 |
|
39 | compiler.plugin('compilation', compilation => {
|
40 |
|
41 | genBuildJson(compilation)
|
42 | })
|
43 |
|
44 | return new Promise((resolve, reject) => {
|
45 | compiler.run((err, stats) => {
|
46 | let messages
|
47 |
|
48 | spinner.stop()
|
49 |
|
50 | if (err) {
|
51 | if (!err.message) {
|
52 | return reject(err)
|
53 | }
|
54 | messages = formatWebpackMessages({
|
55 | errors: [err.message],
|
56 | warnings: []
|
57 | })
|
58 | } else {
|
59 | messages = formatWebpackMessages(
|
60 | stats.toJson({ all: false, warnings: true, errors: true })
|
61 | )
|
62 | }
|
63 |
|
64 | if (messages.errors.length) {
|
65 |
|
66 |
|
67 | if (messages.errors.length > 1) {
|
68 | messages.errors.length = 1
|
69 | }
|
70 | return reject(new Error(messages.errors.join('\n\n')))
|
71 | }
|
72 |
|
73 | return resolve({
|
74 | entryInput,
|
75 | stats,
|
76 | publicPath: webpackConfig.output.publicPath,
|
77 | outputPath: webpackConfig.output.path,
|
78 | warnings: messages.warnings
|
79 | })
|
80 | })
|
81 | })
|
82 | }
|
83 |
|
84 | function clean(entryInput) {
|
85 | const dist = path.join(paths.dist, entryInput.entry)
|
86 |
|
87 | return fs.emptyDir(dist).then(() => ({
|
88 | entryInput,
|
89 | dist
|
90 | }))
|
91 | }
|
92 |
|
93 | function success({ entryInput, stats, publicPath, outputPath, warnings }) {
|
94 | const result = stats.toJson({
|
95 | hash: false,
|
96 | chunks: false,
|
97 | modules: false,
|
98 | chunkModules: false
|
99 | })
|
100 |
|
101 | if (warnings.length) {
|
102 | console.log(chalk.yellow('Compiled with warnings:\n'))
|
103 | console.log(warnings.join('\n\n'))
|
104 |
|
105 | console.log()
|
106 | }
|
107 |
|
108 | console.log(chalk.green(`Build complete in ${result.time}ms\n`))
|
109 | console.log('File sizes after gzip:\n')
|
110 |
|
111 | result.assets['__dist'] = outputPath
|
112 |
|
113 | buildReporter(
|
114 |
|
115 | { page: [result.assets] },
|
116 | WARN_AFTER_BUNDLE_GZIP_SIZE,
|
117 | WARN_AFTER_CHUNK_GZIP_SIZE
|
118 | )
|
119 |
|
120 | console.log()
|
121 | console.log(
|
122 | `The ${chalk.cyan(
|
123 | 'dist/' + entryInput.entry
|
124 | )} directory is ready to be deployed.\n`
|
125 | )
|
126 |
|
127 | if (publicPath === '/') {
|
128 | console.log(
|
129 | chalk.yellow(
|
130 | `The app is built assuming that it will be deployed at the root of a domain.`
|
131 | )
|
132 | )
|
133 | console.log(
|
134 | chalk.yellow(
|
135 | `If you intend to deploy it under a subpath, update the ${chalk.green(
|
136 | 'publicPath'
|
137 | )} option in your project config (${chalk.cyan(
|
138 | `marauder.config.js`
|
139 | )}).\n`
|
140 | )
|
141 | )
|
142 | }
|
143 |
|
144 | return entryInput
|
145 | }
|
146 |
|
147 | async function deploy({ entry, ftpBranch, entryArgs, remotePath }) {
|
148 | if (maraConf.hybrid && remotePath) {
|
149 | await hybridDevPublish(entry, remotePath)
|
150 | } else if (entryArgs.test !== null) {
|
151 | await hybridTestPublish(entry, entryArgs.test)
|
152 | }
|
153 | }
|
154 |
|
155 | function error(err) {
|
156 | console.log(chalk.red('\n🕳 Failed to compile.\n'))
|
157 | printBuildError(err)
|
158 | process.exit(1)
|
159 | }
|
160 |
|
161 | function genBuildJson(compilation) {
|
162 | const source = JSON.stringify({
|
163 | debug: maraConf.debug || process.env.MARA_compileModel == 'dev',
|
164 |
|
165 | target: process.env.jsbridgeBuildType === 'app' ? 'app' : 'web',
|
166 | marax: require(paths.ownPackageJson).version
|
167 | })
|
168 |
|
169 | compilation.assets['build.json'] = {
|
170 | source: () => source,
|
171 | size: () => source.length
|
172 | }
|
173 | }
|
174 |
|
175 | function ftp({ entry, entryArgs, ftpBranch }) {
|
176 | if (ftpBranch === null)
|
177 | return {
|
178 | entry,
|
179 | entryArgs,
|
180 | ftpBranch
|
181 | }
|
182 |
|
183 | return uploadDir(entry, ftpBranch).then(remotePath => ({
|
184 | entry,
|
185 | ftpBranch,
|
186 | entryArgs,
|
187 | remotePath
|
188 | }))
|
189 | }
|
190 |
|
191 | function setup(entryInput) {
|
192 | spinner.start()
|
193 |
|
194 | return entryInput
|
195 | }
|
196 |
|
197 |
|
198 | function done() {
|
199 | const date = new Date()
|
200 | const hour = date.getHours()
|
201 |
|
202 | hour > 21 && console.log(chalk.magenta('🚜 marauder loves you'))
|
203 | }
|
204 |
|
205 | module.exports = args => {
|
206 | return getEntry(args)
|
207 | .then(setup)
|
208 | .then(clean)
|
209 | .then(build)
|
210 | .then(success)
|
211 | .then(ftp)
|
212 | .then(deploy)
|
213 | .then(done)
|
214 | .catch(error)
|
215 | }
|