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 path = require('path')
|
13 | const chalk = require('chalk')
|
14 | const ora = require('ora')
|
15 | const glob = require('glob')
|
16 | const webpack = require('webpack')
|
17 | const { getViews } = require('../lib/utils')
|
18 | const config = require('../config')
|
19 | const { GLOB } = require('../config/const')
|
20 | const getContext = require('../config/context')
|
21 | const paths = config.paths
|
22 | const getWebpackProdConf = require('../webpack/webpack.prod.conf')
|
23 | const getWebpackLibConf = require('../webpack/webpack.lib.conf')
|
24 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages')
|
25 | const printBuildError = require('../lib/printBuildError')
|
26 | const skeleton = require('../lib/skeleton')
|
27 | const {
|
28 | getLastBuildSize,
|
29 | printBuildAssets,
|
30 | getBuildSizeOfFileMap
|
31 | } = require('../lib/buildReporter')
|
32 | const prehandleConfig = require('../lib/prehandleConfig')
|
33 | const { Stopwatch } = require('@mara/devkit')
|
34 | let shouldBuildDemos = false
|
35 |
|
36 | const spinner = ora()
|
37 |
|
38 | const libs = [
|
39 | {
|
40 | format: 'commonjs2',
|
41 | mode: 'production',
|
42 | filename: 'index.cjs.js'
|
43 | },
|
44 | {
|
45 | format: 'umd',
|
46 | mode: 'production',
|
47 | filename: 'index.min.js',
|
48 | minify: true
|
49 | },
|
50 | {
|
51 | format: 'umd',
|
52 | mode: 'production',
|
53 | filename: 'index.js'
|
54 | }
|
55 | ]
|
56 |
|
57 | async function build(options) {
|
58 |
|
59 |
|
60 | const ticker = new Stopwatch()
|
61 | const demos = shouldBuildDemos ? getViews(config.paths.entryGlob) : []
|
62 |
|
63 | const baseCtx = await getContext({
|
64 | version: require(config.paths.packageJson).version
|
65 | })
|
66 |
|
67 | const webpackConfs = libs.concat(demos).map(target => {
|
68 | return typeof target === 'object'
|
69 | ? getWebpackLibConf(target, { ...baseCtx, entry: '__LIB__' })
|
70 | : getWebpackProdConf({ ...baseCtx, entry: target })
|
71 | })
|
72 | const compiler = webpack(webpackConfs)
|
73 |
|
74 | return new Promise((resolve, reject) => {
|
75 | ticker.start()
|
76 | compiler.run((err, stats) => {
|
77 | const buildTime = ticker.check()
|
78 | let messages
|
79 | spinner.stop()
|
80 |
|
81 | if (err) {
|
82 | if (!err.message) return reject(err)
|
83 |
|
84 | messages = formatWebpackMessages({
|
85 | errors: [err.message],
|
86 | warnings: []
|
87 | })
|
88 | } else {
|
89 | messages = formatWebpackMessages(
|
90 | stats.toJson({ all: false, warnings: true, errors: true })
|
91 | )
|
92 | }
|
93 |
|
94 | if (messages.errors.length) {
|
95 |
|
96 |
|
97 | messages.errors.length = 1
|
98 |
|
99 | return reject(new Error(messages.errors.join('\n\n')))
|
100 | }
|
101 |
|
102 | const tinifyOriginSizes = getBuildSizeOfFileMap(compiler._tinifySourceMap)
|
103 | options.preBuildSize.sizes = Object.assign(
|
104 | options.preBuildSize.sizes,
|
105 | tinifyOriginSizes
|
106 | )
|
107 |
|
108 | return resolve({
|
109 | stats,
|
110 | options,
|
111 | buildTime,
|
112 | demos,
|
113 | warnings: messages.warnings
|
114 | })
|
115 | })
|
116 | })
|
117 | }
|
118 |
|
119 | function clean(options) {
|
120 | const distArr = [options.libDir]
|
121 |
|
122 | if (shouldBuildDemos) distArr.push(options.distDir)
|
123 |
|
124 | return Promise.all(distArr.map(dir => fs.emptyDir(dir))).then(() => options)
|
125 | }
|
126 |
|
127 | function success({ warnings, buildTime, stats, demos, options }) {
|
128 | if (warnings.length) {
|
129 | console.log(chalk.yellow('Compiled with warnings:\n'))
|
130 | console.log(warnings.join('\n\n'))
|
131 | }
|
132 |
|
133 | if (buildTime < 1000) {
|
134 | buildTime += 'ms'
|
135 | } else {
|
136 | buildTime = buildTime / 1000 + 's'
|
137 | }
|
138 |
|
139 | console.log(chalk.green(`Compiled successfully in ${buildTime}\n`))
|
140 | console.log('File sizes after gzip:\n')
|
141 |
|
142 | const { children } = stats.toJson({
|
143 | hash: false,
|
144 | chunks: false,
|
145 | modules: false,
|
146 | chunkModules: false
|
147 | })
|
148 | const compAssets = {
|
149 | lib: children.slice(0, libs.length),
|
150 | demos: children.slice(libs.length)
|
151 | }
|
152 |
|
153 | compAssets.lib = compAssets.lib.map((stats, i) => {
|
154 | return stats.assets.map(a => {
|
155 | a['__dist'] = paths.lib
|
156 | a['__format'] = libs[i].format
|
157 | return a
|
158 | })
|
159 | })
|
160 |
|
161 | compAssets.demos = compAssets.demos.map((stats, i) => {
|
162 |
|
163 | stats.assets['__dist'] = path.join(paths.dist, demos[i])
|
164 | return stats.assets
|
165 | })
|
166 |
|
167 | printBuildAssets(compAssets, options.preBuildSize)
|
168 |
|
169 | console.log()
|
170 | console.log(`The ${chalk.cyan('lib')} folder is ready to be published.\n`)
|
171 | }
|
172 |
|
173 | function error(err) {
|
174 | spinner.stop()
|
175 |
|
176 | console.log(chalk.red('Failed to compile.\n'))
|
177 | printBuildError(err)
|
178 | process.exit(1)
|
179 | }
|
180 |
|
181 | async function setup(distDir, libDir) {
|
182 | if (!glob.sync(GLOB.LIB_ENTRY).length) {
|
183 | console.log(chalk.red('请按如下结构创建入口文件'))
|
184 | console.log(skeleton.library, '\n')
|
185 | process.exit(0)
|
186 | }
|
187 |
|
188 | spinner.start(
|
189 | shouldBuildDemos
|
190 | ? 'Building library & demos...'
|
191 | : 'Building library (commonjs + umd)...'
|
192 | )
|
193 |
|
194 | const preBuildSize = await getLastBuildSize(libDir)
|
195 |
|
196 | return {
|
197 | distDir,
|
198 | preBuildSize,
|
199 | libDir
|
200 | }
|
201 | }
|
202 |
|
203 | module.exports = args => {
|
204 | shouldBuildDemos = args.all
|
205 |
|
206 | return setup(paths.dist, paths.lib)
|
207 | .then(clean)
|
208 | .then(build)
|
209 | .then(success)
|
210 | .catch(error)
|
211 | }
|