UNPKG

12.6 kBJavaScriptView Raw
1'use strict'
2
3/**
4 * 文件说明:
5 * ----------------------------------------
6 * 创建用户: leo
7 * 创建日期 2019/1/3
8 */
9
10const {existsSync, readdirSync, statSync} = require('fs')
11const {join} = require('path')
12const rimraf = require('rimraf')
13const assert = require('assert')
14const {merge, get} = require('lodash')
15const signale = require('signale')
16const chalk = require('chalk')
17const inquirer = require('inquirer')
18const {fork} = require('child_process')
19const {babelBuild} = require('./babel')
20const rollup = require('./rollup')
21const {getExistFile, findPkgs, getConfig} = require('./utils')
22const getUserConfig = require('./get-user-config')
23const constants = require('./constants')
24
25function getBundleOpts(opts) {
26 const {cwd, buildArgs = {}} = opts
27 const entry = getExistFile({
28 cwd,
29 files: ['src/index.tsx', 'src/index.ts', 'src/index.jsx', 'src/index.js'],
30 returnRelative: true,
31 })
32 const userConfig = getUserConfig({cwd})
33 const userConfigs = Array.isArray(userConfig) ? userConfig : [userConfig]
34
35 return userConfigs.map((config) => {
36 const bundleOpts = merge(
37 {
38 entry,
39 },
40 buildArgs,
41 config,
42 )
43
44 if (typeof bundleOpts.esm === 'string') {
45 bundleOpts.esm = {type: bundleOpts.esm}
46 }
47 if (typeof bundleOpts.cjs === 'string') {
48 bundleOpts.cjs = {type: bundleOpts.cjs}
49 }
50
51 return bundleOpts
52 })
53}
54
55let workers = []
56
57function forkRollupBuild(option, pkg) {
58 return new Promise((resolve, reject) => {
59 try {
60 const child = fork(join(__dirname, './fork-rollup.js'))
61
62 child.on('exit', (code) => {
63 if (code === 1) {
64 reject(
65 new Error(`[forkRollupBuild: ${pkg} type: ${option.type}] exit: ${code}`),
66 )
67 } else {
68 signale.info(`[forkRollupBuild: ${pkg} type: ${option.type}] exit: ${code}`)
69 resolve()
70 }
71 })
72
73 child.on('error', (code) => {
74 reject(new Error(`[forkRollupBuild: ${pkg} type: ${option.type}] error: ${code}`))
75 })
76
77 child.on('close', (code) => {
78 if (code === 1) {
79 reject(
80 new Error(`[forkRollupBuild: ${pkg} type: ${option.type}] close: ${code}`),
81 )
82 } else {
83 signale.info(`[forkRollupBuild: ${pkg} type: ${option.type}] close: ${code}`)
84 resolve()
85 }
86 })
87
88 signale.info(`[forkRollupBuild: ${pkg} type: ${option.type}] forked`)
89
90 child.send(option)
91
92 workers.push(child)
93 } catch (e) {
94 signale.error(e)
95 process.exit()
96 }
97 })
98}
99
100process.on('SIGINT', () => {
101 process.exit()
102})
103
104process.on('exit', () => {
105 workers.forEach((worker) => {
106 try {
107 worker.kill()
108 } catch (e) {
109 signale.error('exit', e)
110 }
111 })
112
113 workers = null
114})
115
116process.on('error', () => {
117 workers.forEach((worker) => {
118 try {
119 worker.kill()
120 } catch (e) {
121 signale.error('error', e)
122 }
123 })
124
125 workers = null
126})
127
128function validateBundleOpts(bundleOpts, {cwd, rootPath}) {
129 if (bundleOpts.cjs && bundleOpts.cjs.lazy && bundleOpts.cjs.type === 'rollup') {
130 throw new Error(
131 `
132 cjs.lazy don't support rollup.
133 `.trim(),
134 )
135 }
136 if (!bundleOpts.esm && !bundleOpts.cjs && !bundleOpts.umd) {
137 throw new Error(
138 `
139 None format of ${chalk.cyan('cjs | esm | umd')} is configured
140 `.trim(),
141 )
142 }
143 if (bundleOpts.entry) {
144 const tsConfigPath = join(cwd, 'tsconfig.json')
145 const tsConfig =
146 existsSync(tsConfigPath) || (rootPath && existsSync(join(rootPath, 'tsconfig.json')))
147 if (
148 !tsConfig &&
149 ((Array.isArray(bundleOpts.entry) && bundleOpts.entry.some(isTypescriptFile)) ||
150 (!Array.isArray(bundleOpts.entry) && isTypescriptFile(bundleOpts.entry)))
151 ) {
152 signale.info(
153 `Project using ${chalk.cyan(
154 'typescript',
155 )} but tsconfig.json not exists. Use default config.`,
156 )
157 }
158 }
159}
160
161function isTypescriptFile(filePath) {
162 return filePath.endsWith('.ts') || filePath.endsWith('.tsx')
163}
164
165async function build(opts, extraOpts = {}) {
166 const {cwd, watch, rootPath, targetDir = 'lib/'} = opts
167 const {pkg} = extraOpts
168
169 function log(msg) {
170 signale.info(`${pkg ? `[${pkg}] ` : ''}${msg}`)
171 }
172
173 const bundleOptsArray = getBundleOpts(opts)
174 const buildArr = []
175
176 for (const bundleOpts of bundleOptsArray) {
177 validateBundleOpts(bundleOpts, {cwd, rootPath})
178
179 log(`Clean ${targetDir} directory`)
180 rimraf.sync(join(cwd, targetDir))
181
182 if (bundleOpts.umd) {
183 log('Build umd')
184 buildArr.push(
185 rollup({
186 cwd,
187 type: 'umd',
188 targetDir,
189 entry: bundleOpts.entry,
190 watch,
191 bundleOpts,
192 }),
193 )
194 }
195
196 if (bundleOpts.cjs) {
197 const {cjs} = bundleOpts
198 log(`Build cjs with ${cjs.type}`)
199 if (cjs.type === 'babel') {
200 buildArr.push(
201 babelBuild({
202 cwd,
203 watch,
204 rootPath,
205 type: 'cjs',
206 bundleOpts,
207 libTargetDir: bundleOpts.libTargetDir,
208 }),
209 )
210 } else {
211 const option = {
212 cwd,
213 type: 'cjs',
214 targetDir,
215 entry: bundleOpts.entry,
216 watch,
217 bundleOpts,
218 }
219
220 if (get(bundleOpts, 'cjs.forkRollupBuild')) {
221 buildArr.push(forkRollupBuild(option, pkg))
222 } else {
223 buildArr.push(rollup(option))
224 }
225 }
226 }
227
228 if (bundleOpts.esm) {
229 const {esm} = bundleOpts
230 log(`Build esm with ${esm.type}`)
231 if (esm && esm.type === 'babel') {
232 buildArr.push(
233 babelBuild({
234 cwd,
235 watch,
236 rootPath,
237 type: 'esm',
238 bundleOpts,
239 libTargetDir: bundleOpts.libTargetDir,
240 }),
241 )
242 } else {
243 const option = {
244 cwd,
245 type: 'esm',
246 targetDir,
247 entry: bundleOpts.entry,
248 watch,
249 bundleOpts,
250 }
251
252 if (get(bundleOpts, 'esm.forkRollupBuild')) {
253 buildArr.push(forkRollupBuild(option, pkg))
254 } else {
255 buildArr.push(rollup(option))
256 }
257 }
258 }
259 }
260
261 await Promise.all(buildArr)
262}
263
264async function buildForLerna(opts) {
265 const {cwd} = opts
266
267 const userConfig = getUserConfig({cwd})
268 const pkgs = readdirSync(join(opts.cwd, 'packages'))
269 const buildArr = []
270
271 for (const pkg of pkgs) {
272 if (process.env.PACKAGE && pkg !== process.env.PACKAGE) continue
273 const pkgPath = join(opts.cwd, 'packages', pkg)
274 if (!statSync(pkgPath).isDirectory()) continue
275 assert.ok(
276 existsSync(join(pkgPath, 'package.json')),
277 `package.json not found in packages/${pkg}`,
278 )
279 process.chdir(pkgPath)
280 buildArr.push(
281 build(
282 {
283 ...opts,
284 buildArgs: merge(opts.buildArgs, userConfig),
285 cwd: pkgPath,
286 rootPath: cwd,
287 },
288 {
289 pkg,
290 },
291 ),
292 )
293 }
294
295 await Promise.all(buildArr)
296}
297
298async function runForInquirer(opts) {
299 const {cwd, defaultCheck = true} = opts
300
301 const userConfig = getUserConfig({cwd})
302
303 const pkgs = findPkgs({
304 cwd,
305 packagesPatterns: userConfig.packagesPatterns,
306 cb: (pkg) => {
307 pkg.checked = defaultCheck
308 pkg.name = pkg.pkgJson.name
309 pkg.value = pkg.pkgJson.name
310
311 return pkg
312 },
313 })
314
315 if (!pkgs.length) {
316 signale.info('没有要编译的包')
317 process.exit(0)
318 }
319
320 const {checkPkgs = []} = await inquirer.prompt([
321 {
322 name: 'checkPkgs',
323 message: '选择要运行的包',
324 type: 'checkbox',
325 choices: pkgs,
326 },
327 ])
328
329 if (!checkPkgs.length) {
330 signale.error('没有要运行的包')
331 process.exit(1)
332 }
333
334 const buildArr = []
335
336 for (const checkPkgName of checkPkgs) {
337 const pkg = pkgs.find((item) => item.name === checkPkgName)
338 if (!statSync(pkg.pkgPath).isDirectory()) continue
339
340 process.chdir(pkg.pkgPath)
341
342 buildArr.push(
343 build(
344 {
345 ...opts,
346 buildArgs: merge(opts.buildArgs, userConfig),
347 cwd: pkg.pkgPath,
348 rootPath: cwd,
349 },
350 {
351 pkg: pkg.name,
352 },
353 ),
354 )
355 }
356
357 await Promise.all(buildArr)
358}
359
360exports.runForInquirer = runForInquirer
361
362async function runForLerna(opts) {
363 const useLerna = existsSync(join(opts.cwd, 'lerna.json'))
364 if (useLerna && process.env.LERNA !== 'none') {
365 await buildForLerna(opts)
366 } else {
367 await build(opts)
368 }
369}
370
371exports.runForLerna = runForLerna
372
373async function runForPkgs(opts) {
374 const {cwd, pkgs, rootUserConfig = getUserConfig({cwd})} = opts
375
376 const runPkgs = []
377
378 pkgs.forEach((pkg) => {
379 if (get(pkg, 'pkgJson.hzLib.noBuild', false) !== true) {
380 runPkgs.push(pkg)
381 }
382 })
383
384 if (!runPkgs.length) {
385 signale.info('没有要编译的包')
386
387 return
388 }
389
390 const buildArr = []
391
392 for (const pkg of runPkgs) {
393 if (!statSync(pkg.pkgPath).isDirectory()) continue
394 process.chdir(pkg.pkgPath)
395 buildArr.push(
396 build(
397 {
398 ...opts,
399 buildArgs: merge(opts.buildArgs, rootUserConfig),
400 cwd: pkg.pkgPath,
401 rootPath: cwd,
402 },
403 {
404 pkg: pkg.name,
405 },
406 ),
407 )
408 }
409
410 await Promise.all(buildArr)
411}
412
413exports.runForPkgs = runForPkgs
414
415async function run(opts) {
416 const {cwd} = opts
417
418 const {runLibs = []} =
419 getConfig({
420 cwd,
421 files: constants.localConfigFile,
422 returnRelative: false,
423 }) || {}
424
425 if (runLibs.length) {
426 const userConfig = getUserConfig({cwd})
427 const pkgs = findPkgs({
428 cwd,
429 packagesPatterns: userConfig.packagesPatterns,
430 cb: (pkg) => {
431 pkg.name = pkg.pkgJson.name
432
433 return pkg
434 },
435 })
436
437 if (!pkgs.length) {
438 signale.info('没有要编译的包')
439 process.exit(0)
440 }
441
442 const buildArr = []
443
444 for (const pkg of pkgs) {
445 const index = runLibs.indexOf(pkg.name)
446 if (index > -1 && statSync(pkg.pkgPath).isDirectory()) {
447 process.chdir(pkg.pkgPath)
448 buildArr.push(
449 build(
450 {
451 ...opts,
452 buildArgs: merge(opts.buildArgs, userConfig),
453 cwd: pkg.pkgPath,
454 rootPath: cwd,
455 },
456 {
457 pkg: pkg.name,
458 },
459 ),
460 )
461 }
462 }
463
464 await Promise.all(buildArr)
465 } else {
466 runForInquirer(opts)
467 }
468}
469
470exports.run = run