快速link monorepo仓库下的包

需求

业务项目需要快速link某一些包,这些包使用的pnpm的workspace去管理(monorepo)

index.js

#! node
import path from 'path'
import fs from 'fs'
import { program } from 'commander'
import yaml from 'yamljs'
import chalk from 'chalk'
import shell from 'shelljs'
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'))
 
program.version(packageJson.version, '-v, --version')
program
    .command('link')
    .description('使用指定库的全局link,如@vue')
    .argument('[namespace]', '请输入要link的库的namespace', (v) => v)
    .action((namespace) => {
        const workspaceConfig = getWorkspaceConfig()
        if (workspaceConfig) {
            console.log(chalk.red('当前目录是monorepo目录,请勿使用全局link,以避免bug'));
            shell.exit()
        }
        const ls = shell.ls()
        const packageJson = ls.find(v => v.endsWith('package.json'))
        if (!packageJson) {
            console.log(chalk.red('当前目录非npm项目,无package.json。已停止运行'));
            shell.exit()
        }
        console.log(chalk.blue(`当前工作区,正在从global link ${namespace}`));
 
        const globalDir = shell.exec('pnpm root -g').replace('\n', '')
        const packagePath = path.resolve(globalDir, namespace)
        const packages = shell.ls(packagePath)
        if (!packages.length) {
            console.log(chalk.red(`当前全局域内无${namespace}对应的link,请先前往${namespace}执行全局link`));
            shell.exit()
        }
        shell.exec(`pnpm link -g ${packages.map(v => `${namespace}/${v}`).join(' ')}`)
        console.log(chalk.green(`当前工作区,已使用${namespace}的全局link`));
    })
program
    .command('unlink')
    .description('unlink指定库,如@vue')
    .argument('[namespace]', '请输入要unlink的库的namespace', (v) => v)
    .action((namespace) => {
        const workspaceConfig = getWorkspaceConfig()
        if (workspaceConfig) {
            console.log(chalk.red('当前目录是monorepo目录,请勿使用全局link,以避免bug'));
            shell.exit()
        }
        const ls = shell.ls()
        const packageJson = ls.find(v => v.endsWith('package.json'))
        if (!packageJson) {
            console.log(chalk.red('当前目录非npm项目,无package.json。已停止运行'));
            shell.exit()
        }
        console.log(chalk.blue(`当前工作区,正在unlink ${namespace}`));
 
        const globalDir = shell.exec('pnpm root -g').replace('\n', '')
        const packagePath = path.resolve(globalDir, namespace)
        const packages = shell.ls(packagePath)
        shell.exec(`pnpm unlink ${packages.map(v => `${namespace}/${v}`).join(' ')}`)
        console.log(chalk.green(`当前工作区,已unlink ${namespace}`));
    })
program
    .command('global')
    .description('将当前库link到全局')
    .action(() => {
        console.log(chalk.blue(`当前工作区,正在link到global,请稍等...`));
        const workspaceDirs = getWordspaceDir()
        workspaceDirs.forEach(v => {
            shell.cd(v)
            shell.exec(`pnpm link -g`)
        })
        console.log(chalk.green(`当前工作区已设置全局link`));
    })
program
    .command('clear')
    .description('清除当前库全局link')
    .action(() => {
        console.log(chalk.blue(`当前工作区,正在从global清除link`));
        const workspaceDirs = getWordspaceDir()
        const globalDir = shell.exec('pnpm root -g').replace('\n', '')
        const existsDirs = []
        const emptyDirs = []
        workspaceDirs.forEach(v => {
            const json = JSON.parse(fs.readFileSync(path.resolve(v, 'package.json'), 'utf8'))
            // console.log(path.resolve(globalDir.toString(), json.name)); 
            if (fs.existsSync(path.resolve(globalDir, json.name))) {
                existsDirs.push(json.name)
            } else {
                emptyDirs.push(json.name)
            }
        })
        if (existsDirs.length) {
            shell.exec(`pnpm rm -g ${existsDirs.join(' ')}`)
            console.log(chalk.green(`已清除当前工作区${existsDirs.length}个全局link`));
        } else {
            console.log(chalk.yellow('当前工作区没有需要清除的全局link'));
        }
    })
program.parse(process.argv)
 
function getWorkspaceConfig() {
    const ls = shell.ls()
    return ls.find(v => v.endsWith('workspace.yaml'))
}
function getWordspaceDir() {
    const workspaceConfig = getWorkspaceConfig()
    if (!workspaceConfig) {
        console.log(chalk.red('当前目录非workspace目录,已停止操作'));
        shell.exit()
    }
    const yml = yaml.load(workspaceConfig)
    // console.log(yml)
    const workspaceList = yml.packages
    let workspaceDirs = []
    if (workspaceList.findIndex(v => v.includes('*')) > -1) {
        const packagePath = workspaceList.find(v => v.includes('*')).split('/*')[0]
        workspaceDirs = shell.ls(packagePath).map(v => path.resolve(`${packagePath}/${v}`))
    }
    return workspaceDirs
}

package.json

{
  "name": "qlink",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "bin": {
    "qlink": "index.js"
  },
  "keywords": [],
  "author": "liq245",
  "license": "ISC",
  "publishConfig": {
    "registry": "https://github.com/repository/xxx/",
    "access": "public"
  },
  "dependencies": {
    "chalk": "^5.0.1",
    "commander": "^9.3.0",
    "shelljs": "^0.8.5",
    "yamljs": "^0.3.0"
  }
}