UNPKG

5.28 kBJavaScriptView Raw
1'use strict'
2
3// 确保在文件首部设置环境变量
4process.env.BABEL_ENV = 'production'
5process.env.NODE_ENV = 'production'
6
7process.on('unhandledRejection', err => {
8 throw err
9})
10
11const fs = require('fs-extra')
12const path = require('path')
13const chalk = require('chalk')
14const ora = require('ora')
15const glob = require('glob')
16const webpack = require('webpack')
17const { getViews } = require('../lib/utils')
18const config = require('../config')
19const { GLOB } = require('../config/const')
20const getContext = require('../config/context')
21const paths = config.paths
22const getWebpackProdConf = require('../webpack/webpack.prod.conf')
23const getWebpackLibConf = require('../webpack/webpack.lib.conf')
24const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages')
25const printBuildError = require('../lib/printBuildError')
26const skeleton = require('../lib/skeleton')
27const {
28 getLastBuildSize,
29 printBuildAssets,
30 getBuildSizeOfFileMap
31} = require('../lib/buildReporter')
32const prehandleConfig = require('../lib/prehandleConfig')
33const { Stopwatch } = require('@mara/devkit')
34let shouldBuildDemos = false
35
36const spinner = ora()
37
38const 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
57async function build(options) {
58 // @TODO 多配置应用 prehandleConfig
59 // const webpackConfig = prehandleConfig('lib', webpackConfig);
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 // Only keep the first error. Others are often indicative
96 // of the same problem, but confuse the reader with noise.
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
119function 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
127function 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
173function error(err) {
174 spinner.stop()
175
176 console.log(chalk.red('Failed to compile.\n'))
177 printBuildError(err)
178 process.exit(1)
179}
180
181async 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
203module.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}