| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- /*
- 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,cjs"):
- ```
- # name supports fuzzy match. will build all packages with name containing "dom":
- nr build dom
- # specify the format to output
- nr build core --formats cjs
- ```
- */
- const fs = require('fs-extra')
- const path = require('path')
- const chalk = require('chalk')
- const execa = require('execa')
- const { gzipSync } = require('zlib')
- const { compress } = require('brotli')
- const { targets: allTargets, fuzzyMatchTarget } = require('./utils')
- const args = require('minimist')(process.argv.slice(2))
- const targets = args._
- const formats = args.formats || args.f
- const devOnly = args.devOnly || args.d
- const prodOnly = !devOnly && (args.prodOnly || args.p)
- const sourceMap = args.sourcemap || args.s
- const isRelease = args.release
- const buildTypes = args.t || args.types || isRelease
- const buildAllMatching = args.all || args.a
- const commit = execa.sync('git', ['rev-parse', 'HEAD']).stdout.slice(0, 7)
- run()
- async function run() {
- if (isRelease) {
- // remove build cache for release builds to avoid outdated enum values
- await fs.remove(path.resolve(__dirname, '../node_modules/.rts2_cache'))
- }
- if (!targets.length) {
- await buildAll(allTargets)
- checkAllSizes(allTargets)
- } else {
- await buildAll(fuzzyMatchTarget(targets, buildAllMatching))
- checkAllSizes(fuzzyMatchTarget(targets, buildAllMatching))
- }
- }
- async function buildAll(targets) {
- await runParallel(require('os').cpus().length, targets, build)
- }
- async function runParallel(maxConcurrency, source, iteratorFn) {
- const ret = []
- const executing = []
- for (const item of source) {
- const p = Promise.resolve().then(() => iteratorFn(item, source))
- 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)
- }
- 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) {
- await fs.remove(`${pkgDir}/dist`)
- }
- 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}` : ``,
- buildTypes ? `TYPES:true` : ``,
- prodOnly ? `PROD_ONLY:true` : ``,
- sourceMap ? `SOURCE_MAP:true` : ``
- ]
- .filter(Boolean)
- .join(',')
- ],
- { stdio: 'inherit' }
- )
- if (buildTypes && pkg.types) {
- console.log()
- console.log(
- chalk.bold(chalk.yellow(`Rolling up type definitions for ${target}...`))
- )
- // build types
- const { Extractor, ExtractorConfig } = require('@microsoft/api-extractor')
- const extractorConfigPath = path.resolve(pkgDir, `api-extractor.json`)
- const extractorConfig =
- ExtractorConfig.loadFileAndPrepare(extractorConfigPath)
- const extractorResult = Extractor.invoke(extractorConfig, {
- localBuild: true,
- showVerboseMessages: true
- })
- if (extractorResult.succeeded) {
- // concat additional d.ts to rolled-up dts
- const typesDir = path.resolve(pkgDir, 'types')
- if (await fs.exists(typesDir)) {
- const dtsPath = path.resolve(pkgDir, pkg.types)
- const existing = await fs.readFile(dtsPath, 'utf-8')
- const typeFiles = await fs.readdir(typesDir)
- const toAdd = await Promise.all(
- typeFiles.map(file => {
- return fs.readFile(path.resolve(typesDir, file), 'utf-8')
- })
- )
- await fs.writeFile(dtsPath, existing + '\n' + toAdd.join('\n'))
- }
- console.log(
- chalk.bold(chalk.green(`API Extractor completed successfully.`))
- )
- } else {
- console.error(
- `API Extractor completed with ${extractorResult.errorCount} errors` +
- ` and ${extractorResult.warningCount} warnings`
- )
- process.exitCode = 1
- }
- await fs.remove(`${pkgDir}/dist/packages`)
- }
- }
- function checkAllSizes(targets) {
- if (devOnly || (formats && !formats.includes('global'))) {
- return
- }
- console.log()
- for (const target of targets) {
- checkSize(target)
- }
- console.log()
- }
- function checkSize(target) {
- const pkgDir = path.resolve(`packages/${target}`)
- checkFileSize(`${pkgDir}/dist/${target}.global.prod.js`)
- if (!formats || formats.includes('global-runtime')) {
- checkFileSize(`${pkgDir}/dist/${target}.runtime.global.prod.js`)
- }
- }
- function checkFileSize(filePath) {
- if (!fs.existsSync(filePath)) {
- return
- }
- const file = fs.readFileSync(filePath)
- const minSize = (file.length / 1024).toFixed(2) + 'kb'
- const gzipped = gzipSync(file)
- const gzippedSize = (gzipped.length / 1024).toFixed(2) + 'kb'
- const compressed = compress(file)
- const compressedSize = (compressed.length / 1024).toFixed(2) + 'kb'
- console.log(
- `${chalk.gray(
- chalk.bold(path.basename(filePath))
- )} min:${minSize} / gzip:${gzippedSize} / brotli:${compressedSize}`
- )
- }
|