preface
Or to solve the previous problem; The company uses CNPM as internal private warehouse, and does not open full real-time synchronization; So some packages are lagging behind, so it’s important to sync upstream frequently; I thought about it, each time I have to manually perform individual packages or a small number of packages query, operation too much; The principle still follows CNPM update mechanism, can see the previous post ha ~
Considering the point
- Setting a root path will automatically retrieve packaeg.json for all projects (excluding node_modules)
- Include package.json for all Git subtrees or Monorepo
- Support delay execution, too much synchronization in a moment, will collapse the internal construction OF CNPM;
- If yes, the next one to perform synchronization will be automatically filtered. That is, packets with the same name that have been synchronized will not be synchronized again
Very low cost, a Node environment with several commonly used NPM packages;
The environment
- The Node 14.16.1
rendering
The source code
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'.// The root path to retrieve
ignorePackage: ['@ones-ai'.'@ones'].// The package name ignored is not synchronized with or without the cache
delayTime: 10.// The delay time of each execution increases with the number of executions, 2000 = 2s
maxRetry: 3.// The maximum number of error retries for the entire logic
privateHostName: 'xxxxx'.// Access domain name of the CNPM deployed on the Intranet
}
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 = [
{
'Execution time': new Date().toISOString(),
'NPM package name': pkgName,
'Sync status': res.data.ok
}
]
console.dir(data);
}
}).catch(err= > {
if (err) console.log('🍑 NPM package name ', chalk.red(`${pkgName}`.padEnd(60)), '👀 Synchronization status: ', 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(The '@') && 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 = [...newSet(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`)}After performing the update operation! `);
setTimeout(resolve, time);
})
const getExecFileBaseInfo = (abPath) = > {
const { base, dir, ext } = path.parse(abPath);
const data = [{
'Execution time': new Date().toISOString(),
'Location': dir,
'Execute file': base,
'File type': 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 {
awaitsleep(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, 'Ready to retry');
runScript(options);
retryCount = ++retryCount;
}
}
}
}
runScript(options);
Copy the code
conclusion
Now it’s very convenient. As my local projects get more and more. I can use it for a long time with regular updates; And also do not need full synchronization CNPM so exaggerated, only synchronization to use, and can follow up the upstream!! Have wrong place please leave a message, thank you for reading!