构建介绍
- packages/* 的包构建
依赖第三方库
- minimist (opens in a new tab) 功能:命令行参数解析
- execa (opens in a new tab) 功能:流程执行
- pico (opens in a new tab) 功能:控制台颜色输出
- pretty-bytes (opens in a new tab) 功能:针对字节长度 转换 可读性更好的
执行流程
run -> buildAll -> runParallel -> build -> checkAllSizes -> checkSize -> checkFileSize
run
async function run() {
if (writeSize) await fs.mkdir(sizeDir, { recursive: true })
const removeCache = scanEnums()
try {
const resolvedTargets = targets.length
? fuzzyMatchTarget(targets, buildAllMatching)
: allTargets;
// resolvedTargets = ['compiler-core', 'compiler-sfc', xxx] // packages/* 所有文件目录名称
await buildAll(resolvedTargets)
await checkAllSizes(resolvedTargets)
if (buildTypes) {
await execa(
'pnpm',
[
'run',
'build-dts',
...(targets.length
? ['--environment', `TARGETS:${resolvedTargets.join(',')}`]
: [])
],
{
stdio: 'inherit'
}
)
}
} finally {
removeCache()
}
}
buildAll
async function buildAll(targets) {
await runParallel(cpus().length, targets, build)
}
runParallel
- 并发控制, 最底下有这段代码详细介绍,和测试代码
async function runParallel(maxConcurrency, source, iteratorFn) {
const ret = []
const executing = []
for (const item of source) {
const p = Promise.resolve().then(() => iteratorFn(item))
ret.push(p)
if (maxConcurrency <= source.length) {
const e = p.then(() => executing.splice(executing.indexOf(e), 1))
executing.push(e)
if (executing.length >= maxConcurrency) {
await Promise.race(executing)
}
}
}
return Promise.all(ret)
}
build
async function build(target) {
const pkgDir = path.resolve(`packages/${target}`)
const pkg = require(`${pkgDir}/package.json`)
// if this is a full build (no specific targets), ignore private packages
if ((isRelease || !targets.length) && pkg.private) {
return
}
// if building a specific format, do not remove dist.
if (!formats && existsSync(`${pkgDir}/dist`)) {
await fs.rm(`${pkgDir}/dist`, { recursive: true })
}
const env =
(pkg.buildOptions && pkg.buildOptions.env) ||
(devOnly ? 'development' : 'production')
await execa(
'rollup',
[
'-c',
'--environment',
[
`COMMIT:${commit}`,
`NODE_ENV:${env}`,
`TARGET:${target}`,
formats ? `FORMATS:${formats}` : ``,
prodOnly ? `PROD_ONLY:true` : ``,
sourceMap ? `SOURCE_MAP:true` : ``
]
.filter(Boolean)
.join(',')
],
{ stdio: 'inherit' }
)
}
checkAllSizes
async function checkAllSizes(targets) {
if (devOnly || (formats && !formats.includes('global'))) {
return
}
console.log()
for (const target of targets) {
await checkSize(target)
}
console.log()
}
checkSize
async function checkSize(target) {
const pkgDir = path.resolve(`packages/${target}`)
await checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`)
if (!formats || formats.includes('global-runtime')) {
await checkFileSize(`${pkgDir}/dist/${target}.runtime.global.prod.js`)
}
}
checkFileSize
async function checkFileSize(filePath) {
if (!existsSync(filePath)) {
return
}
const file = await fs.readFile(filePath)
const fileName = path.basename(filePath)
const gzipped = gzipSync(file)
const brotli = brotliCompressSync(file)
console.log(
`${pico.gray(pico.bold(fileName))} min:${prettyBytes(
file.length
)} / gzip:${prettyBytes(gzipped.length)} / brotli:${prettyBytes(
brotli.length
)}`
)
if (writeSize)
await fs.writeFile(
path.resolve(sizeDir, `${fileName}.json`),
JSON.stringify({
file: fileName,
size: file.length,
gzip: gzipped.length,
brotli: brotli.length
}),
'utf-8'
)
}
测试 并发不得超过cpu数量
async function awaitPromise() {
const timer = ((Math.random() * 10 + 1) | 0) * 500;
return new Promise((resolve) => {
setTimeout(resolve, timer);
console.log(timer)
})
}
async function build(name) {
console.log('开始执行:', name)
await awaitPromise();
console.log('执行完毕:', name);
}
const testArr = [
'compiler-core',
'compiler-dom',
'compiler-sfc',
'compiler-ssr',
'reactivity',
'reactivity-transform',
'runtime-core',
'runtime-dom',
'server-renderer',
'shared',
'template-explorer',
'vue',
'vue-compat'
]
const CPU_LENGTH = 5;
/* 控制并发量 */
async function runParallel(maxConcurrency, source, iteratorFn) {
const ret = []
// 任务队列
const executing = [];
for (const item of source) {
const p = Promise.resolve().then(() => iteratorFn(item));
ret.push(p);
// 如果 可并行执行的任务的数量 小于 任务数 则进入判断
if (maxConcurrency <= source.length) {
// 等待任务执行
const e = p.then(() => {
// 任务执行完一个 移除出队列
executing.splice(executing.indexOf(e), 1)
})
// 下一个任务加入队列
executing.push(e);
// 如果任务队列 数量 大于 可执行数量, 则进入判断, 否则进入下一次循环
if (executing.length >= maxConcurrency) {
// 整个进程停止 等待任务队列中的任务完成后,在进行下一次循环
await Promise.race(executing)
}
}
}
return Promise.all(ret)
}
runParallel(CPU_LENGTH, testArr, build).then(() => {
console.log('--------------- 全部执行完毕 ---------------');
});