Node脚本快速同步CNPM项目内用到的依赖

简介: 还是为了解决之前的问题;公司用CNPM作为内部私有仓,没有开启全量实时同步;所以有些包会相对落后,所以常用同步上游就显得很重要了;我想了想,每次都要手动去执行个别的包或者少量包的查询,操作太多了;原理还是遵循CNPM更新机制,可以看看上篇帖子哈~

网络异常,图片无法展示
|


前言


还是为了解决之前的问题;


公司用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);


目录
相关文章
|
4月前
|
数据采集 资源调度 JavaScript
Node.js 适合做什么项目?
【8月更文挑战第4天】Node.js 适合做什么项目?
225 5
|
5月前
|
开发框架 JavaScript 测试技术
nodejs使用eggjs创建项目,接入influxdb完成单表增删改查
nodejs使用eggjs创建项目,接入influxdb完成单表增删改查
83 0
|
2月前
|
SQL JavaScript 关系型数据库
node博客小项目:接口开发、连接mysql数据库
【10月更文挑战第14天】node博客小项目:接口开发、连接mysql数据库
|
2月前
|
JavaScript Linux 网络安全
VS Code远程调试Nodejs项目
VS Code远程调试Nodejs项目
|
3月前
|
JavaScript
Nodejs的cnpm包管理器快速入门
介绍Node.js的cnpm包管理器,包括cnpm的实现原理、如何安装cnpm、使用cnpm安装软件包,以及Node.js搜索包的流程。
111 2
Nodejs的cnpm包管理器快速入门
|
3月前
|
JavaScript 应用服务中间件 Linux
宝塔面板部署Vue项目、服务端Node___配置域名
本文介绍了如何使用宝塔面板在阿里云服务器上部署Vue项目和Node服务端项目,并配置域名。文章详细解释了安装宝塔面板、上传项目文件、使用pm2启动Node项目、Vue项目打包上传、以及通过Nginx配置域名和反向代理的步骤。
565 0
宝塔面板部署Vue项目、服务端Node___配置域名
|
4月前
|
缓存 JavaScript 安全
2022年最新最详细的安装Node.js以及cnpm(详细图解过程、绝对成功)
这篇文章提供了2022年最新最详细的Node.js和cnpm安装教程,包括步骤图解、全局配置路径、cnpm安装命令、nrm的安装与使用,以及如何管理npm源和测试速度。
2022年最新最详细的安装Node.js以及cnpm(详细图解过程、绝对成功)
|
3月前
|
JavaScript Linux 开发工具
如何将nodejs项目程序部署到阿里云服务器上
该文章详细描述了将Node.js项目部署到阿里云服务器的步骤,包括服务器环境配置、项目上传及使用PM2进行服务管理的过程。
|
4月前
|
JavaScript
成功解决node、node-sass和sass-loader版本冲突问题、不需要降低node版本。如何在vue项目中安装node-sass,以及安装node-sass可能遇到的版本冲突问题
这篇文章介绍了在Vue项目中安装node-sass和sass-loader时遇到的版本冲突问题,并提供了解决这些问题的方法,包括在不降低node版本的情况下成功安装node-sass。
成功解决node、node-sass和sass-loader版本冲突问题、不需要降低node版本。如何在vue项目中安装node-sass,以及安装node-sass可能遇到的版本冲突问题
|
4月前
|
数据采集 资源调度 JavaScript
Node.js 适合做高并发、I/O密集型项目、轻量级实时应用、前端构建工具、命令行工具以及网络爬虫和数据处理等项目
【8月更文挑战第4天】Node.js 适合做高并发、I/O密集型项目、轻量级实时应用、前端构建工具、命令行工具以及网络爬虫和数据处理等项目
61 5