2018-08-31 18:21:41 +00:00
|
|
|
import { resolve } from 'path'
|
|
|
|
import EventEmitter from 'events'
|
2018-09-01 20:44:57 +00:00
|
|
|
import { sync as spawnSync } from 'cross-spawn'
|
2018-08-31 18:21:41 +00:00
|
|
|
import consola from 'consola'
|
|
|
|
import { readFileSync, existsSync, readJSONSync, writeFileSync, copySync, removeSync } from 'fs-extra'
|
2018-09-20 20:34:08 +00:00
|
|
|
import _ from 'lodash'
|
2018-08-31 18:21:41 +00:00
|
|
|
import { builtinsMap } from './builtins'
|
|
|
|
|
|
|
|
const DEFAULTS = {
|
|
|
|
distDir: 'dist',
|
2018-08-31 19:56:21 +00:00
|
|
|
buildSuffix: process.env.BUILD_SUFFIX
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
|
2018-09-20 20:34:08 +00:00
|
|
|
const sortObjectKeys = obj => _(obj).toPairs().sortBy(0).fromPairs().value()
|
|
|
|
|
2018-08-31 18:21:41 +00:00
|
|
|
export default class Package extends EventEmitter {
|
|
|
|
constructor(options) {
|
|
|
|
super()
|
|
|
|
|
|
|
|
// Assign options
|
|
|
|
Object.assign(this, DEFAULTS, options)
|
|
|
|
|
|
|
|
this.rootDir = this.rootDir || process.cwd()
|
|
|
|
this.distDir = this.resolvePath(this.distDir)
|
2018-08-31 19:26:51 +00:00
|
|
|
this.packageJS = this.resolvePath('package.js')
|
|
|
|
this.packageJSON = this.resolvePath('package.json')
|
2018-08-31 18:21:41 +00:00
|
|
|
|
|
|
|
// Initialize
|
|
|
|
this.init()
|
|
|
|
}
|
|
|
|
|
|
|
|
init() {
|
|
|
|
// Try to read package.json if not provided
|
|
|
|
this._readPackage()
|
|
|
|
|
|
|
|
// Init logger
|
|
|
|
this.logger = consola.withScope(this.packageObj.name)
|
|
|
|
|
|
|
|
// Try to load package.js
|
|
|
|
this._loadPackageJS()
|
|
|
|
}
|
|
|
|
|
|
|
|
resolvePath(...args) {
|
|
|
|
return resolve(this.rootDir, ...args)
|
|
|
|
}
|
|
|
|
|
|
|
|
_readPackage() {
|
2018-08-31 19:26:51 +00:00
|
|
|
this.packageObj = readJSONSync(this.packageJSON)
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_loadPackageJS() {
|
2018-08-31 19:26:51 +00:00
|
|
|
if (existsSync(this.packageJS)) {
|
|
|
|
let fn = require(this.packageJS)
|
2018-08-31 18:21:41 +00:00
|
|
|
fn = fn.default || fn
|
|
|
|
if (typeof fn === 'function') {
|
|
|
|
fn(this, {
|
|
|
|
load: (relativeRootDir, opts) => new Package(Object.assign({
|
|
|
|
rootDir: resolve(this.rootDir, relativeRootDir)
|
|
|
|
}, opts))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
writePackage() {
|
2018-08-31 19:32:30 +00:00
|
|
|
this.logger.debug('Writing', this.packageJSON)
|
2018-08-31 19:26:51 +00:00
|
|
|
writeFileSync(this.packageJSON, JSON.stringify(this.packageObj, null, 2) + '\n')
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generateVersion() {
|
|
|
|
const date = Math.round(Date.now() / (1000 * 60))
|
|
|
|
const gitCommit = this.gitShortCommit()
|
|
|
|
const baseVersion = this.packageObj.version.split('-')[0]
|
|
|
|
this.packageObj.version = `${baseVersion}-${date}.${gitCommit}`
|
|
|
|
}
|
|
|
|
|
2018-08-31 19:48:33 +00:00
|
|
|
convertTo(suffix) {
|
2018-08-31 19:52:24 +00:00
|
|
|
this.logger.info(`Converting to ${suffix} package`)
|
2018-08-31 19:48:33 +00:00
|
|
|
this.addNameSuffix(`-${suffix}`)
|
2018-08-31 18:21:41 +00:00
|
|
|
this.generateVersion()
|
|
|
|
this.writePackage()
|
|
|
|
}
|
|
|
|
|
|
|
|
addNameSuffix(suffix) {
|
|
|
|
if (!this.packageObj.name.includes(suffix)) {
|
|
|
|
this.packageObj.name += suffix
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
build() {
|
|
|
|
this.emit('build:before')
|
|
|
|
|
2018-08-31 19:55:13 +00:00
|
|
|
if (this.buildSuffix) {
|
|
|
|
this.convertTo(this.buildSuffix)
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.logger.info('Cleaning up')
|
|
|
|
removeSync(this.distDir)
|
|
|
|
|
|
|
|
this.logger.info('Building')
|
|
|
|
this.exec('rollup', '-c')
|
|
|
|
|
|
|
|
this.emit('build:done')
|
|
|
|
}
|
|
|
|
|
|
|
|
publish(tag = 'latest') {
|
|
|
|
this.logger.info(`publishing ${this.packageObj.name}@${this.packageObj.version} with tag ${tag}`)
|
|
|
|
this.exec('npm', `publish --tag ${tag}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
copyFieldsFrom(source, fields = []) {
|
|
|
|
for (const field of fields) {
|
|
|
|
this.packageObj[field] = source.packageObj[field]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
copyFilesFrom(source, files) {
|
2018-08-31 19:48:33 +00:00
|
|
|
for (const file of files || source.packageObj.files || []) {
|
2018-08-31 18:21:41 +00:00
|
|
|
const src = resolve(source.rootDir, file)
|
|
|
|
const dst = resolve(this.rootDir, file)
|
|
|
|
copySync(src, dst)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-20 20:34:08 +00:00
|
|
|
sortDependencies() {
|
|
|
|
if (this.packageObj.dependencies) {
|
|
|
|
this.packageObj.dependencies = sortObjectKeys(this.packageObj.dependencies)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.packageObj.devDependencies) {
|
|
|
|
this.packageObj.devDependencies = sortObjectKeys(this.packageObj.devDependencies)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-31 18:21:41 +00:00
|
|
|
updateDependencies({ dist, sources = [], extras = [], exclude = [] }) {
|
|
|
|
const dependencies = {}
|
|
|
|
const requireRegex = /require\('([-@/\w]+)'\)/g
|
|
|
|
|
|
|
|
// Extras
|
|
|
|
for (const name of extras) {
|
|
|
|
dependencies[name] = null
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan require() calls inside dist
|
|
|
|
const distSource = readFileSync(resolve(this.rootDir, dist))
|
|
|
|
|
|
|
|
let match = requireRegex.exec(distSource)
|
|
|
|
while (match) {
|
|
|
|
const name = match[1]
|
|
|
|
dependencies[name] = null
|
|
|
|
match = requireRegex.exec(distSource)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exclude
|
|
|
|
for (const name of exclude) {
|
|
|
|
delete dependencies[name]
|
|
|
|
}
|
|
|
|
|
|
|
|
const builtins = builtinsMap()
|
|
|
|
// Resolve dependency versions
|
|
|
|
for (const name in dependencies) {
|
|
|
|
// Ignore builtin modules
|
|
|
|
if (builtins[name]) {
|
|
|
|
delete dependencies[name]
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Try sources
|
|
|
|
for (const source of sources) {
|
|
|
|
const sourceDeps = source.packageObj.dependencies
|
|
|
|
if (sourceDeps && sourceDeps[name]) {
|
|
|
|
dependencies[name] = sourceDeps[name]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Try to require package.json of dependency
|
|
|
|
if (dependencies[name] === null) {
|
|
|
|
try {
|
|
|
|
const _pkg = require(`${name}/package.json`)
|
|
|
|
if (!_pkg.version) {
|
|
|
|
throw Error('No version specified')
|
|
|
|
}
|
|
|
|
dependencies[name] = `^${_pkg.version}`
|
|
|
|
} catch (e) {
|
2018-08-31 19:32:30 +00:00
|
|
|
this.logger.warn(e)
|
2018-08-31 18:21:41 +00:00
|
|
|
delete dependencies[name]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.packageObj.dependencies = dependencies
|
|
|
|
}
|
|
|
|
|
|
|
|
exec(command, args, silent = false) {
|
|
|
|
const r = spawnSync(command, args.split(' '), { cwd: this.rootDir }, { env: process.env })
|
|
|
|
|
|
|
|
if (!silent) {
|
|
|
|
const fullCommand = command + ' ' + args
|
|
|
|
if (r.error) {
|
|
|
|
this.logger.error(fullCommand, r.error)
|
|
|
|
} else {
|
2018-08-31 18:38:29 +00:00
|
|
|
this.logger.success(fullCommand, r.output)
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
error: r.error,
|
|
|
|
pid: r.pid,
|
|
|
|
status: r.status,
|
|
|
|
signal: r.signal,
|
|
|
|
stdout: String(r.stdout).trim(),
|
2018-08-31 18:38:29 +00:00
|
|
|
stderr: String(r.stderr).trim(),
|
|
|
|
output: (r.output || [])
|
|
|
|
.map(l => String(l).trim())
|
|
|
|
.filter(l => l.length)
|
|
|
|
.join('\n')
|
2018-08-31 18:21:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gitShortCommit() {
|
|
|
|
return this.exec('git', 'rev-parse --short HEAD', true).stdout
|
|
|
|
}
|
|
|
|
|
|
|
|
gitBranch() {
|
|
|
|
return this.exec('git', 'rev-parse --abbrev-ref HEAD', true).stdout
|
|
|
|
}
|
|
|
|
}
|