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!