scripts/watch.mjs (166 lines of code) (raw):
/*eslint-env node*/
import chokidar from 'chokidar'
import path from 'path'
import { execSync, spawn, exec, execFileSync } from 'child_process'
import { argv } from 'process'
import colors from 'colors/safe.js'
function yellow() {
console.log(colors.bgBlue.brightYellow.bold.underline(...arguments, ' '))
}
function green() {
console.log(colors.bgBlue.brightGreen.bold.underline(...arguments, ' '))
}
const FAST_MODE = argv.includes('--fast')
if (FAST_MODE) {
yellow('Enable fast mode. \nWill not build downstream packages.')
}
green('read local packages...')
const packagesJSON = execSync('npx lerna ls --json --all').toString()
const packageALL = JSON.parse(packagesJSON)
// console.log(packageALL)
import blist from './ignore.mjs'
const validPackages = packageALL.filter((pac) => {
let inBL = false
blist.forEach((rule) => {
inBL = inBL || pac.location.includes(rule)
})
return !inBL
})
// console.log('validPackages', validPackages)
function getLocalPackageByName(name) {
const pkg = validPackages.filter((p) => p.name === name)[0]
return pkg
}
function getLocalPackageByPath(path) {
const pkg = validPackages.filter((p) => path.includes(p.location))[0]
return pkg
}
green('read packages dependents...')
const dependentGraphJSON = execSync('npx lerna list --graph').toString()
const dependentGraph = JSON.parse(dependentGraphJSON)
// console.log(dependentGraph)
green('analyzing impact-graph...')
const impactGraph = {}
{
const keys = Object.keys(dependentGraph)
keys.forEach((key) => {
// 确定属于本地包
const pkg = getLocalPackageByName(key)
if (!pkg) {
console.log(colors.yellow(' - ', key, 'will be ignored.'))
return
}
const deps = dependentGraph[key]
deps.forEach((depName) => {
const pkg = getLocalPackageByName(depName)
if (!pkg) {
console.log(colors.yellow(' - ', depName, 'will be ignored.'))
return
}
if (!impactGraph[depName]) {
impactGraph[depName] = []
}
impactGraph[depName].push(key)
})
})
}
// console.log('impactGraph', impactGraph)
if (!FAST_MODE) {
for (const name of Object.keys(impactGraph)) {
console.log(
colors.cyan(
' - ',
name.slice(6),
'=> [',
impactGraph[name].map((a) => a.slice(6)).join(', '),
']'
)
)
}
}
function findAllDirtPkgs(pkg) {
const dirtList = new Set()
if (FAST_MODE) {
dirtList.add(pkg.name)
return dirtList
} else {
// eslint-disable-next-line no-inner-declarations
function dirtWalker(pkg) {
// 不要重复添加
// if (!dirtList.includes(pkg.name)) {
// dirtList.push(pkg.name)
// }
dirtList.add(pkg.name)
// 查找 impacts
const impacts = impactGraph[pkg.name] || []
for (const impactedPkgName of impacts) {
const impactedPkg = getLocalPackageByName(impactedPkgName)
if (impactedPkg) {
dirtWalker(impactedPkg)
}
}
}
dirtWalker(pkg)
return dirtList
}
}
/**
* 编译列表
*/
let waitlist = new Set()
let building = false
/**
*
* @param {Set<string>} dirtList
*/
async function rebuildDirt(dirtList) {
yellow('start building these packages', [...dirtList.values()].join(' '))
const command = `npx`
const args = ['lerna', 'run', '--no-private', '--stream', 'build']
dirtList.forEach((pkgName) => {
args.push(`--scope`, `${pkgName}`)
})
const sub = spawn(command, args, { stdio: 'inherit' })
sub.on('close', (code) => {
building = false
green('building done')
if (waitlist.size > 0) {
yellow('waitlist not empty. start another building...')
const newDirtlist = new Set(waitlist)
waitlist.clear()
building = true
rebuildDirt(newDirtlist)
}
})
}
function fireDirtList(dirtList) {
if (building) {
yellow('another building runing... join wait list')
dirtList.forEach((pkgName) => waitlist.add(pkgName))
} else {
yellow('builder available. start building...')
building = true
rebuildDirt(dirtList)
}
}
const watcher = chokidar.watch([path.resolve(process.env.PWD, './packages')], {
followSymlinks: false,
ignored: ['**/node_modules/**', '**/dist/**', '**/*.tsbuildinfo', '**/.cached-built-head'],
ignoreInitial: true,
atomic: 500,
})
green('started watching....')
watcher.on('all', (eventName, _path, stats) => {
const pkg = getLocalPackageByPath(_path)
// console.log(pkg)
if (!pkg) {
// '不是monorepo本地package,或者在ignore名单中,将忽略'
console.warn('detected change but ignoring...', pkg)
return
}
yellow('change detected. type:', eventName, pkg.name, _path)
// buildPkgRecursively(pkg)
const dirtList = findAllDirtPkgs(pkg)
// console.log(dirtList)
fireDirtList(dirtList)
})