[READ-ONLY] Mirror of https://github.com/danielroe/siroc. Zero-config build tooling for Node
bundle node package rollup typescript
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: build speed improvement and dx

Daniel Roe (Jun 7, 2020, 10:11 PM +0100) e036a9f0 0e322bb6

+417 -396
+5
src/utils.ts
··· 56 56 ) as ExcludeNullable<T> 57 57 58 58 export const includeIf = <T>(test: any, item: T) => (test ? [item] : []) 59 + 60 + export const runInParallel = async <T, R extends any>( 61 + items: T[], 62 + cb: (item: T) => Promise<R> 63 + ) => Promise.all(items.map(async item => cb(item)))
+12 -6
src/cli/index.ts
··· 1 - import 'v8-compile-cache' 2 - 3 1 import cac from 'cac' 4 2 import consola from 'consola' 5 3 6 4 import { version } from '../../package.json' 7 5 8 - import { build } from './commands/build' 6 + import { build, BuildOptions } from './commands/build' 9 7 import { changelog } from './commands/changelog' 10 8 11 9 const cli = cac('packager') 12 10 13 - const run = async (action: () => Promise<void>) => { 14 - await action().catch(err => { 11 + const run = async <A extends (...args: any[]) => Promise<void>>( 12 + action: A, 13 + ...args: Parameters<A> 14 + ) => { 15 + await action(...args).catch(err => { 15 16 consola.error(err) 16 17 process.exit(1) 17 18 }) 18 19 } 19 20 20 - cli.command('build', 'Bundle input files').action(() => run(build)) 21 + cli 22 + .command('build', 'Bundle input files') 23 + .option('--watch', 'Watch files in bundle and rebuild on changes', { 24 + default: false, 25 + }) 26 + .action((options: BuildOptions) => run(build, options)) 21 27 cli.command('changelog', 'Generate changelog').action(() => run(changelog)) 22 28 23 29 cli.version(version)
+2
src/cli/pkg.js
··· 1 + require('v8-compile-cache') 2 + 1 3 const _require = require('esm')(module) 2 4 module.exports = _require('./cli.js')
+1
src/config/rollup.ts
··· 141 141 file: resolve(rootDir, 'dist', 'index.d.ts'), 142 142 format: 'es' as const, 143 143 }, 144 + external, 144 145 plugins: [ 145 146 jsonPlugin(), 146 147 dts({
+5 -3
src/package/hooks.ts
··· 3 3 import type { RequireProperties } from '../utils' 4 4 import type { NuxtRollupOptions, NuxtRollupConfig } from '../config/rollup' 5 5 6 - export interface HookOptions { 6 + export interface PackageHookOptions { 7 7 'build:extend': { 8 8 config: RequireProperties<NuxtRollupOptions, 'alias' | 'replace'> 9 9 } ··· 17 17 (options: T): void | Array<(options: T) => void> 18 18 } 19 19 20 - export type PackageHooks = { 21 - [P in keyof HookOptions]?: Hook<HookOptions[P]> 20 + export type Hooks<T extends Record<string, any>> = { 21 + [P in keyof T]?: Hook<T[P]> 22 22 } 23 + 24 + export type PackageHooks = Hooks<PackageHookOptions>
+1 -373
src/package/index.ts
··· 1 - import { dirname, resolve } from 'path' 2 - import { existsSync, readJSONSync, writeFile, copy, remove } from 'fs-extra' 3 - 4 - import consola, { Consola } from 'consola' 5 - import execa from 'execa' 6 - import { rollup, watch, RollupOptions, RollupError } from 'rollup' 7 - import sortPackageJson from 'sort-package-json' 8 - 9 - import type { PackageJson } from '../config/package-json' 10 - import { 11 - rollupConfig, 12 - NuxtRollupOptions, 13 - NuxtRollupConfig, 14 - } from '../config/rollup' 15 - import { sortObjectKeys, tryRequire, RequireProperties, glob } from '../utils' 16 - import type { PackageHooks, HookOptions } from './hooks' 17 - 18 - export interface PackageOptions { 19 - rootDir: string 20 - pkgPath: string 21 - configPath: string 22 - distDir: string 23 - build: boolean 24 - suffix: string 25 - hooks: PackageHooks 26 - linkedDependencies?: string[] 27 - sortDependencies?: boolean 28 - rollup?: NuxtRollupOptions & RollupOptions 29 - } 30 - 31 - const DEFAULTS: PackageOptions = { 32 - rootDir: process.cwd(), 33 - pkgPath: 'package.json', 34 - configPath: 'package.js', 35 - distDir: 'dist', 36 - build: false, 37 - suffix: process.env.PACKAGE_SUFFIX ? `-${process.env.PACKAGE_SUFFIX}` : '', 38 - hooks: {}, 39 - } 40 - 41 - export class Package { 42 - options: PackageOptions 43 - logger: Consola 44 - pkg: RequireProperties<PackageJson, 'name' | 'version'> 45 - 46 - constructor(options: Partial<PackageOptions> = {}) { 47 - this.options = Object.assign({}, DEFAULTS, options) 48 - 49 - // Basic logger 50 - this.logger = consola 51 - 52 - this.pkg = this.readPackageJSON(this.options.pkgPath) 53 - 54 - // Use tagged logger 55 - this.logger = consola.withTag(this.pkg.name) 56 - 57 - this.loadConfig() 58 - } 59 - 60 - private readPackageJSON(packagePath: string): this['pkg'] { 61 - return readJSONSync(this.resolvePath(packagePath)) 62 - } 63 - 64 - private resolvePath(...pathSegments: string[]) { 65 - return resolve(this.options.rootDir, ...pathSegments) 66 - } 67 - 68 - loadConfig() { 69 - const configPath = this.resolvePath(this.options.configPath) 70 - const config = tryRequire<PackageOptions>(configPath) 71 - 72 - Object.assign(this.options, config) 73 - } 74 - 75 - async callHook<H extends keyof HookOptions>( 76 - name: H, 77 - options: HookOptions[H] 78 - ) { 79 - const fns = this.options.hooks[name] 80 - 81 - if (!fns) return 82 - 83 - const fnArray = Array.isArray(fns) ? fns : [fns] 84 - 85 - for (const fn of fnArray) { 86 - await fn(this, options) 87 - } 88 - } 89 - 90 - load(relativePath: string, opts?: PackageOptions) { 91 - return new Package( 92 - Object.assign( 93 - { 94 - rootDir: this.resolvePath(relativePath), 95 - }, 96 - opts 97 - ) 98 - ) 99 - } 100 - 101 - async writePackage() { 102 - if (this.options.sortDependencies) this.sortDependencies() 103 - 104 - const pkgPath = this.resolvePath(this.options.pkgPath) 105 - this.logger.debug('Writing', pkgPath) 106 - await writeFile(pkgPath, JSON.stringify(this.pkg, null, 2) + '\n') 107 - } 108 - 109 - generateVersion() { 110 - const date = Math.round(Date.now() / (1000 * 60)) 111 - const gitCommit = this.gitShortCommit() 112 - const baseVersion = this.pkg.version.split('-')[0] 113 - this.pkg.version = `${baseVersion}-${date}.${gitCommit}` 114 - } 115 - 116 - suffixAndVersion() { 117 - this.logger.info(`Adding suffix ${this.options.suffix}`) 118 - 119 - const oldPkgName = this.pkg.name 120 - 121 - // Add suffix to the package name 122 - if (!oldPkgName.includes(this.options.suffix)) { 123 - this.pkg.name += this.options.suffix 124 - } 125 - 126 - // Apply suffix to all linkedDependencies 127 - if (this.pkg.dependencies) { 128 - for (const oldName of this.options.linkedDependencies || []) { 129 - const name = oldName + this.options.suffix 130 - const version = 131 - this.pkg.dependencies[oldName] || this.pkg.dependencies[name] 132 - 133 - delete this.pkg.dependencies[oldName] 134 - this.pkg.dependencies[name] = version 135 - } 136 - } 137 - 138 - if (typeof this.pkg.bin === 'string') { 139 - const { bin } = this.pkg 140 - this.pkg.bin = { 141 - [oldPkgName]: bin, 142 - [this.pkg.name]: bin, 143 - } 144 - } 145 - 146 - this.generateVersion() 147 - } 148 - 149 - syncLinkedDependencies() { 150 - // Apply suffix to all linkedDependencies 151 - for (const _name of this.options.linkedDependencies || []) { 152 - const name = _name + (this.options.suffix || '') 153 - 154 - // Try to read pkg 155 - const pkg = 156 - tryRequire<PackageJson>(`${name}/package.json`) || 157 - tryRequire<PackageJson>(`${_name}/package.json`) 158 - 159 - // Skip if pkg or dependency not found 160 - if ( 161 - !pkg || 162 - !pkg.version || 163 - !this.pkg.dependencies || 164 - !this.pkg.dependencies[name] 165 - ) { 166 - continue 167 - } 168 - 169 - // Current version 170 - const currentVersion = this.pkg.dependencies[name] 171 - const caret = currentVersion[0] === '^' 172 - 173 - // Sync version 174 - this.pkg.dependencies[name] = caret ? `^${pkg.version}` : pkg.version 175 - } 176 - } 177 - 178 - async getWorkspacePackages() { 179 - const packages: Package[] = [] 180 - 181 - for (const workspace of this.pkg.workspaces || []) { 182 - const dirs = await glob(workspace) 183 - for (const dir of dirs) { 184 - if (existsSync(this.resolvePath(dir, 'package.json'))) { 185 - const pkg = new Package({ 186 - rootDir: this.resolvePath(dir), 187 - }) 188 - packages.push(pkg) 189 - } else { 190 - consola.warn('Invalid workspace package:', dir) 191 - } 192 - } 193 - } 194 - 195 - return packages 196 - } 197 - 198 - async removeBuildFolders(config: NuxtRollupConfig[]) { 199 - const directories = new Set<string>() 200 - config.forEach(conf => { 201 - directories.add(conf.output.dir || dirname(conf.output.file || '')) 202 - }) 203 - for (const dir of directories) { 204 - await remove(dir) 205 - } 206 - } 207 - 208 - async build(_watch = false) { 209 - // Prepare rollup config 210 - const config: RequireProperties<NuxtRollupOptions, 'alias' | 'replace'> = { 211 - rootDir: this.options.rootDir, 212 - alias: {}, 213 - replace: {}, 214 - ...this.options.rollup, 215 - } 216 - 217 - // Replace linkedDependencies with their suffixed version 218 - if (this.options.suffix && this.options.suffix.length) { 219 - for (const _name of this.options.linkedDependencies || []) { 220 - const name = _name + this.options.suffix 221 - config.replace[`'${_name}'`] = `'${name}'` 222 - config.alias[_name] = name 223 - } 224 - } 225 - 226 - // Allow extending config 227 - await this.callHook('build:extend', { config }) 228 - 229 - // Create rollup config 230 - const _rollupConfig = rollupConfig(config, this.pkg) 231 - 232 - // Allow extending rollup config 233 - await this.callHook('build:extendRollup', { 234 - rollupConfig: _rollupConfig, 235 - }) 236 - 237 - await this.removeBuildFolders(_rollupConfig) 238 - 239 - if (_watch) { 240 - // Watch 241 - const watcher = watch(_rollupConfig) 242 - watcher.on('event', event => { 243 - switch (event.code) { 244 - // The watcher is (re)starting 245 - case 'START': 246 - return this.logger.debug('Watching for changes') 247 - 248 - // Building an individual bundle 249 - case 'BUNDLE_START': 250 - return this.logger.debug('Building bundle') 251 - 252 - // Finished building a bundle 253 - case 'BUNDLE_END': 254 - return 255 - 256 - // Finished building all bundles 257 - case 'END': 258 - return this.logger.success('Bundle built') 259 - 260 - // Encountered an error while bundling 261 - case 'ERROR': 262 - this.formatError(event.error) 263 - return this.logger.error(event.error) 264 - 265 - // Unknown event 266 - default: 267 - return this.logger.info(JSON.stringify(event)) 268 - } 269 - }) 270 - } else { 271 - // Build 272 - this.logger.info('Building bundle') 273 - for (const config of _rollupConfig) { 274 - try { 275 - const bundle = await rollup(config) 276 - await bundle.write(config.output) 277 - 278 - this.logger.success('Bundle built') 279 - await this.callHook('build:done', { bundle }) 280 - } catch (err) { 281 - const formattedError = this.formatError(err) 282 - this.logger.error(formattedError) 283 - throw formattedError 284 - } 285 - } 286 - } 287 - } 288 - 289 - /** 290 - * Format rollup error 291 - */ 292 - private formatError(error: RollupError) { 293 - let loc = this.options.rootDir 294 - if (error.loc) { 295 - const { file, column, line } = error.loc 296 - loc = `${file}:${line}:${column}` 297 - } 298 - error.message = `[${error.code}] ${error.message}\nat ${loc}` 299 - return error 300 - } 301 - 302 - watch() { 303 - return this.build(true) 304 - } 305 - 306 - publish(tag = 'latest') { 307 - this.logger.info( 308 - `publishing ${this.pkg.name}@${this.pkg.version} with tag ${tag}` 309 - ) 310 - this.exec('npm', `publish --tag ${tag}`) 311 - } 312 - 313 - copyFieldsFrom(source: Package, fields: Array<keyof PackageJson> = []) { 314 - for (const field of fields) { 315 - ;(this.pkg[field] as any) = source.pkg[field] as any 316 - } 317 - } 318 - 319 - async copyFilesFrom(source: Package, files: string[]) { 320 - for (const file of files || source.pkg.files || []) { 321 - const src = resolve(source.options.rootDir, file) 322 - const dst = resolve(this.options.rootDir, file) 323 - await copy(src, dst) 324 - } 325 - } 326 - 327 - autoFix() { 328 - this.pkg = sortPackageJson(this.pkg) 329 - this.sortDependencies() 330 - } 331 - 332 - sortDependencies() { 333 - if (this.pkg.dependencies) { 334 - this.pkg.dependencies = sortObjectKeys(this.pkg.dependencies) 335 - } 336 - 337 - if (this.pkg.devDependencies) { 338 - this.pkg.devDependencies = sortObjectKeys(this.pkg.devDependencies) 339 - } 340 - } 341 - 342 - exec(command: string, args: string, silent = false) { 343 - const fullCommand = `${command} ${args}` 344 - const r = execa.commandSync(fullCommand, { 345 - cwd: this.options.rootDir, 346 - env: process.env, 347 - }) 348 - 349 - if (!silent) { 350 - if (r.failed) { 351 - this.logger.error(fullCommand, r.stderr.trim()) 352 - } else { 353 - this.logger.success(fullCommand, r.stdout.trim()) 354 - } 355 - } 356 - 357 - return { 358 - signal: r.signal, 359 - stdout: String(r.stdout).trim(), 360 - stderr: String(r.stderr).trim(), 361 - } 362 - } 363 - 364 - gitShortCommit() { 365 - const { stdout } = this.exec('git', 'rev-parse --short HEAD', true) 366 - return stdout 367 - } 368 - 369 - gitBranch() { 370 - const { stdout } = this.exec('git', 'rev-parse --abbrev-ref HEAD', true) 371 - return stdout 372 - } 373 - } 1 + export * from './package'
+382
src/package/package.ts
··· 1 + import { dirname, resolve } from 'path' 2 + import { readJSONSync, writeFile, copy, remove, existsSync } from 'fs-extra' 3 + 4 + import consola, { Consola } from 'consola' 5 + import chalk from 'chalk' 6 + import execa from 'execa' 7 + import { rollup, watch, RollupOptions, RollupError } from 'rollup' 8 + import sortPackageJson from 'sort-package-json' 9 + 10 + import type { PackageJson } from '../config/package-json' 11 + import { 12 + rollupConfig, 13 + NuxtRollupOptions, 14 + NuxtRollupConfig, 15 + } from '../config/rollup' 16 + import { sortObjectKeys, tryRequire, RequireProperties, glob } from '../utils' 17 + import type { PackageHooks, PackageHookOptions } from './hooks' 18 + 19 + export interface PackageOptions { 20 + rootDir: string 21 + pkgPath: string 22 + configPath: string 23 + distDir: string 24 + build: boolean 25 + suffix: string 26 + hooks: PackageHooks 27 + linkedDependencies?: string[] 28 + sortDependencies?: boolean 29 + rollup?: NuxtRollupOptions & RollupOptions 30 + } 31 + 32 + const DEFAULTS: PackageOptions = { 33 + rootDir: process.cwd(), 34 + pkgPath: 'package.json', 35 + configPath: 'package.js', 36 + distDir: 'dist', 37 + build: false, 38 + suffix: process.env.PACKAGE_SUFFIX ? `-${process.env.PACKAGE_SUFFIX}` : '', 39 + hooks: {}, 40 + } 41 + 42 + export class Package { 43 + options: PackageOptions 44 + logger: Consola 45 + pkg: RequireProperties<PackageJson, 'name' | 'version'> 46 + 47 + constructor(options: Partial<PackageOptions> = {}) { 48 + this.options = Object.assign({}, DEFAULTS, options) 49 + 50 + // Basic logger 51 + this.logger = consola 52 + 53 + this.pkg = this.readPackageJSON(this.options.pkgPath) 54 + 55 + // Use tagged logger 56 + this.logger = consola.withTag(this.pkg.name) 57 + 58 + this.loadConfig() 59 + } 60 + 61 + private readPackageJSON(packagePath: string): this['pkg'] { 62 + return readJSONSync(this.resolvePath(packagePath)) 63 + } 64 + 65 + resolvePath(...pathSegments: string[]) { 66 + return resolve(this.options.rootDir, ...pathSegments) 67 + } 68 + 69 + loadConfig() { 70 + const configPath = this.resolvePath(this.options.configPath) 71 + const config = tryRequire<PackageOptions>(configPath) 72 + 73 + Object.assign(this.options, config) 74 + } 75 + 76 + async callHook<H extends keyof PackageHookOptions>( 77 + name: H, 78 + options: PackageHookOptions[H] 79 + ) { 80 + const fns = this.options.hooks[name] 81 + 82 + if (!fns) return 83 + 84 + const fnArray = Array.isArray(fns) ? fns : [fns] 85 + 86 + for (const fn of fnArray) { 87 + await fn(this, options) 88 + } 89 + } 90 + 91 + load(relativePath: string, opts?: PackageOptions) { 92 + return new Package( 93 + Object.assign( 94 + { 95 + rootDir: this.resolvePath(relativePath), 96 + }, 97 + opts 98 + ) 99 + ) 100 + } 101 + 102 + async writePackage() { 103 + if (this.options.sortDependencies) this.sortDependencies() 104 + 105 + const pkgPath = this.resolvePath(this.options.pkgPath) 106 + this.logger.debug('Writing', pkgPath) 107 + await writeFile(pkgPath, JSON.stringify(this.pkg, null, 2) + '\n') 108 + } 109 + 110 + generateVersion() { 111 + const date = Math.round(Date.now() / (1000 * 60)) 112 + const gitCommit = this.gitShortCommit() 113 + const baseVersion = this.pkg.version.split('-')[0] 114 + this.pkg.version = `${baseVersion}-${date}.${gitCommit}` 115 + } 116 + 117 + suffixAndVersion() { 118 + this.logger.info(`Adding suffix ${this.options.suffix}`) 119 + 120 + const oldPkgName = this.pkg.name 121 + 122 + // Add suffix to the package name 123 + if (!oldPkgName.includes(this.options.suffix)) { 124 + this.pkg.name += this.options.suffix 125 + } 126 + 127 + // Apply suffix to all linkedDependencies 128 + if (this.pkg.dependencies) { 129 + for (const oldName of this.options.linkedDependencies || []) { 130 + const name = oldName + this.options.suffix 131 + const version = 132 + this.pkg.dependencies[oldName] || this.pkg.dependencies[name] 133 + 134 + delete this.pkg.dependencies[oldName] 135 + this.pkg.dependencies[name] = version 136 + } 137 + } 138 + 139 + if (typeof this.pkg.bin === 'string') { 140 + const { bin } = this.pkg 141 + this.pkg.bin = { 142 + [oldPkgName]: bin, 143 + [this.pkg.name]: bin, 144 + } 145 + } 146 + 147 + this.generateVersion() 148 + } 149 + 150 + syncLinkedDependencies() { 151 + // Apply suffix to all linkedDependencies 152 + for (const _name of this.options.linkedDependencies || []) { 153 + const name = _name + (this.options.suffix || '') 154 + 155 + // Try to read pkg 156 + const pkg = 157 + tryRequire<PackageJson>(`${name}/package.json`) || 158 + tryRequire<PackageJson>(`${_name}/package.json`) 159 + 160 + // Skip if pkg or dependency not found 161 + if ( 162 + !pkg || 163 + !pkg.version || 164 + !this.pkg.dependencies || 165 + !this.pkg.dependencies[name] 166 + ) { 167 + continue 168 + } 169 + 170 + // Current version 171 + const currentVersion = this.pkg.dependencies[name] 172 + const caret = currentVersion[0] === '^' 173 + 174 + // Sync version 175 + this.pkg.dependencies[name] = caret ? `^${pkg.version}` : pkg.version 176 + } 177 + } 178 + 179 + async removeBuildFolders(config: NuxtRollupConfig[]) { 180 + const directories = new Set<string>() 181 + config.forEach(conf => { 182 + directories.add(conf.output.dir || dirname(conf.output.file || '')) 183 + }) 184 + for (const dir of directories) { 185 + await remove(dir) 186 + } 187 + } 188 + 189 + async build(_watch = false) { 190 + // Prepare rollup config 191 + const config: RequireProperties<NuxtRollupOptions, 'alias' | 'replace'> = { 192 + rootDir: this.options.rootDir, 193 + alias: {}, 194 + replace: {}, 195 + ...this.options.rollup, 196 + } 197 + 198 + // Replace linkedDependencies with their suffixed version 199 + if (this.options.suffix && this.options.suffix.length) { 200 + for (const _name of this.options.linkedDependencies || []) { 201 + const name = _name + this.options.suffix 202 + config.replace[`'${_name}'`] = `'${name}'` 203 + config.alias[_name] = name 204 + } 205 + } 206 + 207 + // Allow extending config 208 + await this.callHook('build:extend', { config }) 209 + 210 + // Create rollup config 211 + const _rollupConfig = rollupConfig(config, this.pkg) 212 + 213 + // Allow extending rollup config 214 + await this.callHook('build:extendRollup', { 215 + rollupConfig: _rollupConfig, 216 + }) 217 + 218 + await this.removeBuildFolders(_rollupConfig) 219 + 220 + if (_watch) { 221 + // Watch 222 + const watcher = watch(_rollupConfig) 223 + watcher.on('event', event => { 224 + switch (event.code) { 225 + // The watcher is (re)starting 226 + case 'START': 227 + return this.logger.debug(`Watching ${this.pkg.name} for changes`) 228 + 229 + // Building an individual bundle 230 + case 'BUNDLE_START': 231 + return this.logger.debug(`Building ${this.pkg.name}`) 232 + 233 + // Finished building a bundle 234 + case 'BUNDLE_END': 235 + return 236 + 237 + // Finished building all bundles 238 + case 'END': 239 + return this.logger.success(`Built ${chalk.bold(this.pkg.name)}`) 240 + 241 + // Encountered an error while bundling 242 + case 'ERROR': 243 + this.formatError(event.error) 244 + return this.logger.error(event.error) 245 + 246 + // Unknown event 247 + default: 248 + return this.logger.info(JSON.stringify(event)) 249 + } 250 + }) 251 + } else { 252 + // Build 253 + this.logger.debug(`Building ${this.pkg.name}`) 254 + for (const config of _rollupConfig) { 255 + try { 256 + const bundle = await rollup(config) 257 + const { output } = await bundle.write(config.output) 258 + 259 + this.logger.success( 260 + `Built ${chalk.bold(this.pkg.name)} ${chalk.gray( 261 + output[0].fileName 262 + )}` 263 + ) 264 + await this.callHook('build:done', { bundle }) 265 + } catch (err) { 266 + const formattedError = this.formatError(err) 267 + this.logger.error(formattedError) 268 + throw formattedError 269 + } 270 + } 271 + } 272 + } 273 + 274 + /** 275 + * Format rollup error 276 + */ 277 + private formatError(error: RollupError) { 278 + let loc = this.options.rootDir 279 + if (error.loc) { 280 + const { file, column, line } = error.loc 281 + loc = `${file}:${line}:${column}` 282 + } 283 + error.message = `[${error.code}] ${error.message}\nat ${loc}` 284 + return error 285 + } 286 + 287 + watch() { 288 + return this.build(true) 289 + } 290 + 291 + publish(tag = 'latest') { 292 + this.logger.info( 293 + `publishing ${this.pkg.name}@${this.pkg.version} with tag ${tag}` 294 + ) 295 + this.exec('npm', `publish --tag ${tag}`) 296 + } 297 + 298 + copyFieldsFrom(source: Package, fields: Array<keyof PackageJson> = []) { 299 + for (const field of fields) { 300 + ;(this.pkg[field] as any) = source.pkg[field] as any 301 + } 302 + } 303 + 304 + async copyFilesFrom(source: Package, files: string[]) { 305 + for (const file of files || source.pkg.files || []) { 306 + const src = resolve(source.options.rootDir, file) 307 + const dst = resolve(this.options.rootDir, file) 308 + await copy(src, dst) 309 + } 310 + } 311 + 312 + autoFix() { 313 + this.pkg = sortPackageJson(this.pkg) 314 + this.sortDependencies() 315 + } 316 + 317 + sortDependencies() { 318 + if (this.pkg.dependencies) { 319 + this.pkg.dependencies = sortObjectKeys(this.pkg.dependencies) 320 + } 321 + 322 + if (this.pkg.devDependencies) { 323 + this.pkg.devDependencies = sortObjectKeys(this.pkg.devDependencies) 324 + } 325 + } 326 + 327 + exec(command: string, args: string, silent = false) { 328 + const fullCommand = `${command} ${args}` 329 + const r = execa.commandSync(fullCommand, { 330 + cwd: this.options.rootDir, 331 + env: process.env, 332 + }) 333 + 334 + if (!silent) { 335 + if (r.failed) { 336 + this.logger.error(fullCommand, r.stderr.trim()) 337 + } else { 338 + this.logger.success(fullCommand, r.stdout.trim()) 339 + } 340 + } 341 + 342 + return { 343 + signal: r.signal, 344 + stdout: String(r.stdout).trim(), 345 + stderr: String(r.stderr).trim(), 346 + } 347 + } 348 + 349 + async getWorkspacePackages() { 350 + const packages: Package[] = [] 351 + 352 + const dirs = new Set<string>() 353 + await Promise.all( 354 + (this.pkg.workspaces || []).map(async workspace => 355 + (await glob(workspace)).forEach(dir => dirs.add(dir)) 356 + ) 357 + ) 358 + 359 + for (const dir of dirs) { 360 + if (existsSync(this.resolvePath(dir, 'package.json'))) { 361 + const pkg = new Package({ 362 + rootDir: this.resolvePath(dir), 363 + }) 364 + packages.push(pkg) 365 + } else { 366 + this.logger.warn('Invalid workspace package:', dir) 367 + } 368 + } 369 + 370 + return packages 371 + } 372 + 373 + gitShortCommit() { 374 + const { stdout } = this.exec('git', 'rev-parse --short HEAD', true) 375 + return stdout 376 + } 377 + 378 + gitBranch() { 379 + const { stdout } = this.exec('git', 'rev-parse --abbrev-ref HEAD', true) 380 + return stdout 381 + } 382 + }
+9 -14
src/cli/commands/build.ts
··· 1 1 import consola from 'consola' 2 2 3 3 import { Package } from '../../package' 4 + import { runInParallel } from '../../utils' 4 5 5 6 export interface BuildOptions { 6 7 watch?: boolean ··· 12 13 const workspacePackages = await rootPackage.getWorkspacePackages() 13 14 14 15 const { watch } = options 15 - consola.info('starting') 16 - if (watch) consola.info('Watch mode') 16 + consola.info(`Beginning build${watch ? ' (watching)' : ''}`) 17 17 18 18 // Universal linkedDependencies based on workspace 19 19 const linkedDependencies = workspacePackages.map(p => 20 20 p.pkg.name.replace(p.options.suffix, '') 21 21 ) 22 22 23 - for (const pkg of workspacePackages) { 23 + await runInParallel(workspacePackages, async pkg => { 24 24 pkg.options.linkedDependencies = ( 25 25 pkg.options.linkedDependencies || [] 26 26 ).concat(linkedDependencies) 27 - } 28 27 29 - // Step 1: Apply suffixes 30 - for (const pkg of workspacePackages) { 28 + // Step 1: Apply suffixes 31 29 if (pkg.options.suffix && pkg.options.suffix.length) { 32 30 pkg.suffixAndVersion() 33 31 await pkg.writePackage() 34 32 } 35 - } 33 + }) 36 34 37 - // Step 2: Build packages 38 - for (const pkg of workspacePackages) { 35 + await runInParallel(workspacePackages, async pkg => { 36 + // Step 2: Build packages 39 37 if (pkg.options.build) { 40 38 if (watch) { 41 39 pkg.watch() ··· 43 41 await pkg.build() 44 42 } 45 43 } 46 - } 47 - 48 - // Step 3: Link dependencies and Fix packages 49 - for (const pkg of workspacePackages) { 44 + // Step 3: Link dependencies and Fix packages 50 45 pkg.syncLinkedDependencies() 51 46 pkg.autoFix() 52 47 pkg.writePackage() 53 - } 48 + }) 54 49 }