2023-02-03 18:24:03 +08:00
|
|
|
// @ts-check
|
|
|
|
|
2018-09-20 00:21:00 +08:00
|
|
|
/*
|
2019-10-05 22:23:05 +08:00
|
|
|
Produces production builds and stitches together d.ts files.
|
2018-09-20 00:21:00 +08:00
|
|
|
|
2019-10-05 22:23:05 +08:00
|
|
|
To specify the package to build, simply pass its name and the desired build
|
2018-09-20 00:21:00 +08:00
|
|
|
formats to output (defaults to `buildOptions.formats` specified in that package,
|
|
|
|
or "esm,cjs"):
|
|
|
|
|
|
|
|
```
|
|
|
|
# name supports fuzzy match. will build all packages with name containing "dom":
|
2021-10-09 02:23:30 +08:00
|
|
|
nr build dom
|
2018-09-20 00:21:00 +08:00
|
|
|
|
|
|
|
# specify the format to output
|
2021-10-09 02:23:30 +08:00
|
|
|
nr build core --formats cjs
|
2018-09-20 00:21:00 +08:00
|
|
|
```
|
|
|
|
*/
|
|
|
|
|
2023-01-26 14:24:49 +08:00
|
|
|
import fs from 'node:fs/promises'
|
2023-10-20 11:25:27 +08:00
|
|
|
import { existsSync } from 'node:fs'
|
2023-01-26 14:24:49 +08:00
|
|
|
import path from 'node:path'
|
|
|
|
import minimist from 'minimist'
|
2023-04-10 14:06:28 +08:00
|
|
|
import { gzipSync, brotliCompressSync } from 'node:zlib'
|
2023-10-20 10:23:18 +08:00
|
|
|
import pico from 'picocolors'
|
2023-09-21 20:25:32 +08:00
|
|
|
import { execa, execaSync } from 'execa'
|
2023-01-26 14:24:49 +08:00
|
|
|
import { cpus } from 'node:os'
|
|
|
|
import { createRequire } from 'node:module'
|
2023-02-03 18:12:56 +08:00
|
|
|
import { targets as allTargets, fuzzyMatchTarget } from './utils.js'
|
2023-02-06 09:35:08 +08:00
|
|
|
import { scanEnums } from './const-enum.js'
|
2023-08-21 11:33:51 +08:00
|
|
|
import prettyBytes from 'pretty-bytes'
|
2023-01-26 14:24:49 +08:00
|
|
|
|
|
|
|
const require = createRequire(import.meta.url)
|
|
|
|
const args = minimist(process.argv.slice(2))
|
2019-10-02 23:19:30 +08:00
|
|
|
const targets = args._
|
2018-09-20 00:21:00 +08:00
|
|
|
const formats = args.formats || args.f
|
2019-10-02 23:19:30 +08:00
|
|
|
const devOnly = args.devOnly || args.d
|
|
|
|
const prodOnly = !devOnly && (args.prodOnly || args.p)
|
2023-04-18 11:39:21 +08:00
|
|
|
const buildTypes = args.withTypes || args.t
|
2020-01-28 23:28:40 +08:00
|
|
|
const sourceMap = args.sourcemap || args.s
|
2019-12-11 11:14:02 +08:00
|
|
|
const isRelease = args.release
|
2018-10-23 23:58:37 +08:00
|
|
|
const buildAllMatching = args.all || args.a
|
2023-08-21 11:33:51 +08:00
|
|
|
const writeSize = args.size
|
2023-10-20 17:31:50 +08:00
|
|
|
const commit = execaSync('git', ['rev-parse', '--short=7', 'HEAD']).stdout
|
2019-10-14 13:08:00 +08:00
|
|
|
|
2023-08-21 11:33:51 +08:00
|
|
|
const sizeDir = path.resolve('temp/size')
|
|
|
|
|
2019-10-14 13:08:00 +08:00
|
|
|
run()
|
|
|
|
|
|
|
|
async function run() {
|
2023-08-21 11:33:51 +08:00
|
|
|
if (writeSize) await fs.mkdir(sizeDir, { recursive: true })
|
2023-02-06 09:35:08 +08:00
|
|
|
const removeCache = scanEnums()
|
|
|
|
try {
|
2023-04-18 11:39:21 +08:00
|
|
|
const resolvedTargets = targets.length
|
|
|
|
? fuzzyMatchTarget(targets, buildAllMatching)
|
|
|
|
: allTargets
|
|
|
|
await buildAll(resolvedTargets)
|
2023-08-21 11:33:51 +08:00
|
|
|
await checkAllSizes(resolvedTargets)
|
2023-04-18 11:39:21 +08:00
|
|
|
if (buildTypes) {
|
|
|
|
await execa(
|
|
|
|
'pnpm',
|
|
|
|
[
|
|
|
|
'run',
|
|
|
|
'build-dts',
|
|
|
|
...(targets.length
|
|
|
|
? ['--environment', `TARGETS:${resolvedTargets.join(',')}`]
|
|
|
|
: [])
|
|
|
|
],
|
|
|
|
{
|
|
|
|
stdio: 'inherit'
|
|
|
|
}
|
|
|
|
)
|
2023-02-06 09:35:08 +08:00
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
removeCache()
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
2019-10-14 13:08:00 +08:00
|
|
|
}
|
2018-09-19 23:35:38 +08:00
|
|
|
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Builds all the targets in parallel.
|
|
|
|
* @param {Array<string>} targets - An array of targets to build.
|
|
|
|
* @returns {Promise<void>} - A promise representing the build process.
|
|
|
|
*/
|
2018-09-20 00:21:00 +08:00
|
|
|
async function buildAll(targets) {
|
2023-01-26 14:24:49 +08:00
|
|
|
await runParallel(cpus().length, targets, build)
|
2020-12-04 02:22:28 +08:00
|
|
|
}
|
|
|
|
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Runs iterator function in parallel.
|
|
|
|
* @template T - The type of items in the data source
|
|
|
|
* @param {number} maxConcurrency - The maximum concurrency.
|
|
|
|
* @param {Array<T>} source - The data source
|
|
|
|
* @param {(item: T) => Promise<void>} iteratorFn - The iteratorFn
|
|
|
|
* @returns {Promise<void[]>} - A Promise array containing all iteration results.
|
|
|
|
*/
|
2020-12-04 02:22:28 +08:00
|
|
|
async function runParallel(maxConcurrency, source, iteratorFn) {
|
2023-11-10 13:41:25 +08:00
|
|
|
/**@type {Promise<void>[]} */
|
2020-12-04 02:22:28 +08:00
|
|
|
const ret = []
|
2023-11-10 13:41:25 +08:00
|
|
|
/**@type {Promise<void>[]} */
|
2020-12-04 02:22:28 +08:00
|
|
|
const executing = []
|
|
|
|
for (const item of source) {
|
2023-11-10 13:41:25 +08:00
|
|
|
const p = Promise.resolve().then(() => iteratorFn(item))
|
2020-12-04 02:22:28 +08:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
2020-12-04 02:22:28 +08:00
|
|
|
return Promise.all(ret)
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Builds the target.
|
|
|
|
* @param {string} target - The target to build.
|
|
|
|
* @returns {Promise<void>} - A promise representing the build process.
|
|
|
|
*/
|
2018-09-20 00:21:00 +08:00
|
|
|
async function build(target) {
|
2018-09-19 23:35:38 +08:00
|
|
|
const pkgDir = path.resolve(`packages/${target}`)
|
2018-10-17 05:41:59 +08:00
|
|
|
const pkg = require(`${pkgDir}/package.json`)
|
2018-09-19 23:35:38 +08:00
|
|
|
|
2021-07-20 21:29:45 +08:00
|
|
|
// if this is a full build (no specific targets), ignore private packages
|
|
|
|
if ((isRelease || !targets.length) && pkg.private) {
|
2019-12-11 11:14:02 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-11-07 04:23:50 +08:00
|
|
|
// if building a specific format, do not remove dist.
|
2023-01-26 16:11:24 +08:00
|
|
|
if (!formats && existsSync(`${pkgDir}/dist`)) {
|
2023-01-26 14:24:49 +08:00
|
|
|
await fs.rm(`${pkgDir}/dist`, { recursive: true })
|
2019-11-07 04:23:50 +08:00
|
|
|
}
|
2018-09-19 23:35:38 +08:00
|
|
|
|
2019-10-05 01:08:06 +08:00
|
|
|
const env =
|
|
|
|
(pkg.buildOptions && pkg.buildOptions.env) ||
|
|
|
|
(devOnly ? 'development' : 'production')
|
2018-09-20 00:21:00 +08:00
|
|
|
await execa(
|
|
|
|
'rollup',
|
|
|
|
[
|
|
|
|
'-c',
|
|
|
|
'--environment',
|
2019-10-05 10:40:54 +08:00
|
|
|
[
|
|
|
|
`COMMIT:${commit}`,
|
|
|
|
`NODE_ENV:${env}`,
|
|
|
|
`TARGET:${target}`,
|
|
|
|
formats ? `FORMATS:${formats}` : ``,
|
2019-10-14 13:08:00 +08:00
|
|
|
prodOnly ? `PROD_ONLY:true` : ``,
|
2020-01-28 23:28:40 +08:00
|
|
|
sourceMap ? `SOURCE_MAP:true` : ``
|
2019-10-05 10:40:54 +08:00
|
|
|
]
|
2019-10-12 22:55:35 +08:00
|
|
|
.filter(Boolean)
|
2019-10-05 10:40:54 +08:00
|
|
|
.join(',')
|
2018-09-20 00:21:00 +08:00
|
|
|
],
|
|
|
|
{ stdio: 'inherit' }
|
|
|
|
)
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
|
|
|
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Checks the sizes of all targets.
|
|
|
|
* @param {string[]} targets - The targets to check sizes for.
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
2023-08-21 11:33:51 +08:00
|
|
|
async function checkAllSizes(targets) {
|
2021-09-24 02:46:16 +08:00
|
|
|
if (devOnly || (formats && !formats.includes('global'))) {
|
2019-12-03 07:18:02 +08:00
|
|
|
return
|
|
|
|
}
|
2018-09-19 23:35:38 +08:00
|
|
|
console.log()
|
|
|
|
for (const target of targets) {
|
2023-08-21 11:33:51 +08:00
|
|
|
await checkSize(target)
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
|
|
|
console.log()
|
|
|
|
}
|
|
|
|
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Checks the size of a target.
|
|
|
|
* @param {string} target - The target to check the size for.
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
2023-08-21 11:33:51 +08:00
|
|
|
async function checkSize(target) {
|
2018-09-19 23:35:38 +08:00
|
|
|
const pkgDir = path.resolve(`packages/${target}`)
|
2023-08-21 11:33:51 +08:00
|
|
|
await checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`)
|
2021-09-24 02:46:16 +08:00
|
|
|
if (!formats || formats.includes('global-runtime')) {
|
2023-08-21 11:33:51 +08:00
|
|
|
await checkFileSize(`${pkgDir}/dist/${target}.runtime.global.prod.js`)
|
2021-09-24 02:46:16 +08:00
|
|
|
}
|
2020-03-24 03:09:29 +08:00
|
|
|
}
|
|
|
|
|
2023-11-10 13:41:25 +08:00
|
|
|
/**
|
|
|
|
* Checks the file size.
|
|
|
|
* @param {string} filePath - The path of the file to check the size for.
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
2023-08-21 11:33:51 +08:00
|
|
|
async function checkFileSize(filePath) {
|
2023-01-26 14:24:49 +08:00
|
|
|
if (!existsSync(filePath)) {
|
2020-03-24 03:09:29 +08:00
|
|
|
return
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|
2023-08-21 11:33:51 +08:00
|
|
|
const file = await fs.readFile(filePath)
|
|
|
|
const fileName = path.basename(filePath)
|
|
|
|
|
2020-03-24 03:09:29 +08:00
|
|
|
const gzipped = gzipSync(file)
|
2023-08-21 11:33:51 +08:00
|
|
|
const brotli = brotliCompressSync(file)
|
|
|
|
|
2020-03-24 03:09:29 +08:00
|
|
|
console.log(
|
2023-10-20 10:23:18 +08:00
|
|
|
`${pico.gray(pico.bold(fileName))} min:${prettyBytes(
|
2023-08-21 11:33:51 +08:00
|
|
|
file.length
|
|
|
|
)} / gzip:${prettyBytes(gzipped.length)} / brotli:${prettyBytes(
|
|
|
|
brotli.length
|
|
|
|
)}`
|
2020-03-24 03:09:29 +08:00
|
|
|
)
|
2023-08-21 11:33:51 +08:00
|
|
|
|
|
|
|
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'
|
|
|
|
)
|
2018-09-19 23:35:38 +08:00
|
|
|
}
|