| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- // @ts-check
- /*
- Produces production builds and stitches together d.ts files.
- To specify the package to build, simply pass its name and the desired build
- formats to output (defaults to `buildOptions.formats` specified in that package,
- or ["esm-bundler", "cjs"]):
- ```
- # name supports fuzzy match. will build all packages with name containing "dom":
- nr build dom
- # specify the format to output
- nr build vue -f cjs
- # to specify multiple formats, separate with "+":
- nr build vue -f esm-bundler+esm-browser
- ```
- */
- import { rolldown } from 'rolldown'
- import {
- existsSync,
- mkdirSync,
- readFileSync,
- readdirSync,
- rmSync,
- writeFileSync,
- } from 'node:fs'
- import { parseArgs } from 'node:util'
- import path from 'node:path'
- import { brotliCompressSync, gzipSync } from 'node:zlib'
- import pico from 'picocolors'
- import { targets as allTargets, fuzzyMatchTarget } from './utils.js'
- import prettyBytes from 'pretty-bytes'
- import { spawnSync } from 'node:child_process'
- import { createConfigsForPackage } from './create-rolldown-config.js'
- import { scanEnums } from './inline-enums.js'
- import { fileURLToPath } from 'node:url'
- const __dirname = fileURLToPath(new URL('.', import.meta.url))
- const privatePackages = readdirSync('packages-private')
- const commit = spawnSync('git', ['rev-parse', '--short=7', 'HEAD'])
- .stdout.toString()
- .trim()
- const { values, positionals: targets } = parseArgs({
- allowPositionals: true,
- options: {
- formats: {
- type: 'string',
- short: 'f',
- },
- devOnly: {
- type: 'boolean',
- short: 'd',
- },
- prodOnly: {
- type: 'boolean',
- short: 'p',
- },
- withTypes: {
- type: 'boolean',
- short: 't',
- },
- sourceMap: {
- type: 'boolean',
- short: 's',
- },
- release: {
- type: 'boolean',
- },
- all: {
- type: 'boolean',
- short: 'a',
- },
- size: {
- type: 'boolean',
- },
- e2eTest: {
- type: 'boolean',
- short: 'e',
- },
- },
- })
- const {
- formats: rawFormats,
- all: buildAllMatching,
- devOnly,
- prodOnly,
- withTypes: buildTypes,
- sourceMap,
- release: isRelease,
- size,
- e2eTest,
- } = values
- /**
- * @type {string[] | undefined}
- */
- let formats
- let isNegation = false
- if (rawFormats) {
- isNegation = rawFormats.startsWith('~')
- formats = (isNegation ? rawFormats.slice(1) : rawFormats).split('+')
- }
- const sizeDir = path.resolve('temp/size')
- run()
- async function run() {
- if (size) mkdirSync(sizeDir, { recursive: true })
- const removeCache = scanEnums()
- try {
- const resolvedTargets = targets.length
- ? fuzzyMatchTarget(targets, buildAllMatching)
- : allTargets
- await buildAll(resolvedTargets)
- if (size) await checkAllSizes(resolvedTargets)
- if (buildTypes) {
- await import('./build-types.js')
- }
- } finally {
- removeCache()
- }
- }
- /**
- * 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.
- */
- async function buildAll(targets) {
- const start = performance.now()
- const all = []
- let count = 0
- for (const t of targets) {
- const configs = createConfigsForTarget(t)
- if (configs) {
- all.push(
- Promise.all(
- configs.map(c => {
- return rolldown(c).then(bundle => {
- // @ts-expect-error
- return bundle.write(c.output).then(() => {
- // @ts-expect-error
- return c.output.file
- })
- })
- }),
- ).then(files => {
- const from = process.cwd()
- files.forEach((/** @type {string} */ f) => {
- count++
- console.log(
- pico.gray('built: ') + pico.green(path.relative(from, f)),
- )
- })
- }),
- )
- }
- }
- await Promise.all(all)
- console.log(
- `\n${count} files built in ${(performance.now() - start).toFixed(2)}ms.`,
- )
- }
- /**
- * Builds the target.
- * @param {string} target - The target to build.
- * @returns {import('rolldown').RolldownOptions[] | void} - A promise representing the build process.
- */
- function createConfigsForTarget(target) {
- const pkgBase = privatePackages.includes(target)
- ? `packages-private`
- : `packages`
- const pkgDir = path.resolve(__dirname, `../${pkgBase}/${target}`)
- const pkg = JSON.parse(readFileSync(`${pkgDir}/package.json`, 'utf-8'))
- // if this is a full build (no specific targets), ignore private packages
- if ((isRelease || !targets.length) && pkg.private) {
- return
- }
- let resolvedFormats
- if (formats) {
- const pkgFormats = pkg.buildOptions?.formats
- if (pkgFormats) {
- if (isNegation) {
- resolvedFormats = pkgFormats.filter(
- (/** @type {string} */ f) => !formats.includes(f),
- )
- } else {
- resolvedFormats = formats.filter(f => pkgFormats.includes(f))
- }
- }
- if (!resolvedFormats.length) {
- return
- }
- }
- // if building a specific format, do not remove dist.
- if (!formats && existsSync(`${pkgDir}/dist`)) {
- rmSync(`${pkgDir}/dist`, { recursive: true })
- }
- return createConfigsForPackage({
- target,
- commit,
- formats: resolvedFormats,
- e2eTest,
- prodOnly,
- devOnly:
- (pkg.buildOptions && pkg.buildOptions.env === 'development') || devOnly,
- sourceMap,
- })
- }
- /**
- * Checks the sizes of all targets.
- * @param {string[]} targets - The targets to check sizes for.
- * @returns {Promise<void>}
- */
- async function checkAllSizes(targets) {
- if (devOnly || (formats && !formats.includes('global'))) {
- return
- }
- console.log()
- for (const target of targets) {
- await checkSize(target)
- }
- console.log()
- }
- /**
- * Checks the size of a target.
- * @param {string} target - The target to check the size for.
- * @returns {Promise<void>}
- */
- async function checkSize(target) {
- const pkgDir = path.resolve(__dirname, `../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`)
- }
- }
- /**
- * Checks the file size.
- * @param {string} filePath - The path of the file to check the size for.
- * @returns {Promise<void>}
- */
- async function checkFileSize(filePath) {
- if (!existsSync(filePath)) {
- return
- }
- const file = readFileSync(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 (size)
- writeFileSync(
- path.resolve(sizeDir, `${fileName}.json`),
- JSON.stringify({
- file: fileName,
- size: file.length,
- gzip: gzipped.length,
- brotli: brotli.length,
- }),
- 'utf-8',
- )
- }
|