网络异常,图片无法展示
|
前言
还是为了解决之前的问题;
公司用CNPM作为内部私有仓,没有开启全量实时同步;
所以有些包会相对落后,所以常用同步上游就显得很重要了;
我想了想,每次都要手动去执行个别的包或者少量包的查询,操作太多了;
原理还是遵循CNPM更新机制,可以看看上篇帖子哈~
考虑的点
- 设置一个根路径,会自动检索下所有项目的packaeg.json(不包含node_modules)
- 包括所有git subtree或者monorepo的package.json
- 支持延时执行,一瞬间太多要同步的,会把内部搭建cnpm搞崩;
- 同步过,再下一个执行同步的会自动过滤.也就是同步过同名包不会再发同步请求
使用成本极低,一个Node环境装几个常用的npm包;
环境
- Node 14.16.1
效果图
网络异常,图片无法展示
|
源码
const globby = require('globby'); const fs = require('fs'); const path = require('path'); const axios = require('axios'); const chalk = require('chalk'); const isPlainObject = require('lodash/isPlainObject'); const options = { baseRootPath: '/Users/linqunhe/Code/ONES', // 检索的根路径 ignorePackage: ['@ones-ai', '@ones'], // 忽略的包名,就是不管有木有缓存都不同步 delayTime: 10, // 每一次执行延时的时间,随着执行次数会递增 , 2000 = 2s maxRetry: 3, // 整个逻辑,中间有错误重试机制最大次数 privateHostName: 'xxxxx', // 内网部署CNPM的访问域名 } let cachePkgList = []; let retryCount = 0; const baseDep = ['npm', 'pnpm', 'yarn','recoil','typescript','mobx','mobx-react','react','redux','vite']; function onesNpmSyncUpdate(pkgList, isArray = false) { const syncReq = (pkgName) => { return axios.put(`${options.privateHostName}/sync/${pkgName}?sync_upstream=true`).then(res => { if (res && res.data && res.data.ok) { const data = [ { '执行时间': new Date().toISOString(), 'NPM包名': pkgName, '同步状态': res.data.ok } ] console.dir(data); } }).catch(err => { if (err) console.log('🍑 NPM包名', chalk.red(`${pkgName}`.padEnd(60)), '👀 同步状态: ', chalk.green('false')); }) } if (isArray) { pkgList.forEach(pkgName => { syncReq(pkgName) }) }else{ syncReq(pkgList); } } function arrayTypeData(array) { let decoratorsArr = [] let normalArr = [] for (let item of array) { if (item && typeof item === 'string') { if (item.startsWith('@') && item.includes('/')) { decoratorsArr.push(item) } else { normalArr.push(item) } } } return { decoratorsArr, normalArr } } function getPackageJsonDepKey(json = { dependencies: {}, devDependencies: {} }, ignore = []) { const { dependencies, devDependencies, peerDependencies } = json; let dependenciesKey = []; let devDependenciesKey = []; let peerDependenciesKey = []; if (dependencies && isPlainObject(dependencies)) { dependenciesKey = Object.keys(dependencies); } if (devDependencies && isPlainObject(devDependencies)) { devDependenciesKey = Object.keys(devDependencies); } if (peerDependencies && isPlainObject(peerDependencies)) { peerDependenciesKey = Object.keys(peerDependencies); } const allDepKey = [...new Set([...dependenciesKey, ...devDependenciesKey, ...peerDependenciesKey])] return allDepKey.filter(item => { for (const iterator of ignore) { if (item.indexOf(iterator) !== -1) { return false; } } return true }) } function readPackageJson(path) { try { const data = fs.readFileSync(path, { encoding: 'utf8' }); if (data && typeof data === 'string') { return JSON.parse(data) } } catch (error) { console.log('%c 🍦 error: ', 'font-size:20px;background-color: #EA7E5C;color:#fff;', path, error); } } function getUpdatePkgList(depKeyArr) { if (Array.isArray(depKeyArr) && depKeyArr.length <= 0) return []; let newUpdatePkgList = []; let uniDepKeyArr = [...new Set(depKeyArr)]; if (Array.isArray(cachePkgList)) { if (cachePkgList.length <= 0) { cachePkgList = uniDepKeyArr; newUpdatePkgList = cachePkgList; } else { newUpdatePkgList = uniDepKeyArr.filter(item => !cachePkgList.includes(item)) cachePkgList = [...new Set(cachePkgList.concat(uniDepKeyArr))] } } return newUpdatePkgList } function updatePkgList(depKeyArr, index) { const { decoratorsArr, normalArr } = arrayTypeData(depKeyArr); if (Array.isArray(normalArr) && normalArr.length > 0) { onesNpmSyncUpdate(normalArr, true) } if (Array.isArray(decoratorsArr) && decoratorsArr.length > 0) { decoratorsArr.forEach(item => { onesNpmSyncUpdate(item) }) } } const sleep = (time) => new Promise((resolve) => { console.log(`🎳🎳🎳 ${chalk.green(`${time / 1000} s`)} 后执行更新操作!`); setTimeout(resolve, time); }) const getExecFileBaseInfo = (abPath) => { const { base, dir, ext } = path.parse(abPath); const data = [{ '执行时间': new Date().toISOString(), '所在目录': dir, '执行文件': base, '文件类型': ext, }] console.table(data); } const runScript = async (options) => { const pkgGlob = `${options.baseRootPath}/**/**/package.json`; let index = 1; let execTime = 1000; let depKeyArr = [...baseDep]; try { for await (const path of globby.stream(pkgGlob, { ignore: ['**/node_modules'] })) { const packageJson = readPackageJson(path); if (packageJson && isPlainObject(packageJson)) { const packageDepKey = getPackageJsonDepKey(packageJson, options.ignorePackage); if (Array.isArray(packageDepKey) && packageDepKey.length > 0) { depKeyArr = [...depKeyArr, ...packageDepKey] } } const newUpdatePkgList = getUpdatePkgList(depKeyArr); if (newUpdatePkgList.length <= 0) { continue } else { getExecFileBaseInfo(path); if (index <= 1) { updatePkgList(newUpdatePkgList, index); } else { await sleep(execTime * index) updatePkgList(newUpdatePkgList, index); } index = ++index; } } } catch (error) { if (error) { if (retryCount < options.maxRetry) { console.log('%c 🍞 error: ', 'font-size:20px;background-color: #B03734;color:#fff;', error, '准备重试'); runScript(options); retryCount = ++retryCount; } } } } runScript(options);