verdaccio如何发布本地包与已下载好的node_modules依赖包?批量发布

vue3框架开发过程中,当项目达到一定数量,开始有搭建依赖包私服的需求了,毫无疑问,目前基本都在使用verdaccio,简单易用,跟npmjs库的发布方式基本一致,不废话,说说verdaccio的基本安装使用:

安装使用

npm i verdaccio -g

全局安装,这个时候会在nvm的安装目录,当前使用版本文件夹下的node_modules下有一个verdaccio文件夹,如果你也是用nvm进行node版本管理,那么路径一般大概都在这里:

C:\Users\Administrator\AppData\Roaming\nvm\v20.10.0\node_modules\verdaccio

多找找应该都能找到,然后verdaccio目录下有个conf文件夹,里面有几个yaml文件:
0659e3adeb8a40a0bd2948b6baa4057e.png

然后修改default.yaml里面的配置即可,一些基本配置如下:

# path to a directory with all packages
storage: H:/verdaccio/storage #发布的包的目录
# path to a directory with plugins to include
plugins: H:/verdaccio/plugins

# https://verdaccio.org/docs/webui
web:
  title: Verdaccio本地库
  showSearch: true #搜索生效


# https://verdaccio.org/docs/configuration#max-body-size
max_body_size: 500mb

listen:
# - localhost:4873            # default value
# - http://localhost:4873     # same thing
- 0.0.0.0:4873 #这样其他电脑才能访问你本地的verdaccio仓库

基本需要修改的配置就这些,注意:yaml文件的缩进只能用空格,不要用tab键;
然后打开cmd或者Powershell窗口直接运行verdaccio:

verdaccio

verdaccio库就会跑起来,浏览器输入http://localhost:4873 访问,就能看到verdaccio界面;

有个特殊的坑提前说下:max_body_size: 500mb,这个参数可能改了不生效,不改连element-plus这种大点的库都发布不了,报request entity too large,要去这个目录下:

C:\Users\Administrator\AppData\Roaming\verdaccio

删掉config.yaml文件,再重新启动verdaccio,这个时候verdaccio会自动将上面的default.yaml复制一份过来,直接手动改config.yaml文件可能会直接无法启动verdaccio

发布第三方包到verdaccio

所谓第三方包,就是本地项目已经下载过的node_modules目录依赖包,因为需要私服的一般都是内网,将其他人发布过的包重新发布一份到verdaccio,其实也很简单,因为那些依赖都是打包过的,可以直接发布;

要发布依赖包先要创建verdaccio用户:

npm adduser --registry http://localhost:4873/

接着会让你输入用户名、密码、邮箱,照填就行;

登录:

npm login --registry http://localhost:4873/

输入用户名、密码、邮箱即可,登录成功!

接下来就可以进入发布步骤了:

我们只需要通过CMD命令进入到要发布的包的根目录下:

cd 项目目录/node_modules/element-plus

执行:

npm version patch
npm publish --registry http://localhost:4873/

可能有同学不懂npm version patch是什么,其实是因为发布包之前一定要改package.json文件的版本号,这里是用命令直接改了,当然也可以手动修改package.json里面的version版本号,才能发布成功,每次发布都要改一次,另外很重要的一点,就是要把package.json里的scripts属性置为空才行,不然可能会失败,因为里面也有publish命令;

用命令执行是为后面的批量发布准备,目录里几百个依赖包,不可能每个依赖都手动改一次版本号,改一次scripts命令,然后再发布吧,因此批量发布时,最好是拷一份依赖到指定目录去执行,保证不污染原依赖;

不出意外,这样就可以发布成功啦
成功会打印一行:+ ………

注意:发布后需要重启verdaccio才能搜索到;

发布自己开发的组件包到verdaccio

会发布第三方包以后,那如何自己开发这种依赖包呢?

简单的做法是创建一个目录,然后在目录里创建一个index.js文件,然后在这个目录下执行:

npm init -y

会生成一个默认的package.json文件,里面的main属性默认就是上面的index.js,如果是普通的方法,直接在index.js里开发然后export导出即可,这种不用编译,相对简单;

复杂的就要用rollupvite等依赖一起开发来编译过后才能发布;

其实也是跟平时开发一样,开发完也要npm run build打包到dist目录然后在dist目录下发布,下面小编举一个自己发布过的组件的做法吧,这是一个实现瀑布流布局的composable函数组件:

最终文件目录预览:
e524153fd58d4374945c7e4246d65b00.png

因为要用到vue+ts,所以在目录下分别创建src/index.tsrollup.config.jstsconfig.json
然后在目录下执行:

npm init -y

生成package.json文件,然后在src/index.ts里开发composable组合式方法:

import { ref } from 'vue'
import type { Ref } from 'vue'

export function useWaterFallLayout(options: Object) {
    ......
}

rollup.config.js文件:

import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import vue from 'rollup-plugin-vue'
import { terser } from 'rollup-plugin-terser'

export default {
    input: 'src/index.ts',//包入口
    output: {
        file: 'dist/index.js',//输出路径
        format: 'esm', // 输出为ES模块
        exports: 'named'
    },
    plugins: [
        resolve(),
        commonjs(),
        vue(),
        typescript(),
        terser() // 用于压缩代码
    ],
    external: ['vue'] // 假设vue是外部依赖,不包含在打包文件中
}

tsconfig.json文件:

{
    "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "moduleResolution": "node",
        "declaration": true,
        "outDir": "./dist",
        "strict": true,
        "jsx": "preserve",
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    }
}

package.json文件:

{
    "name": "usewaterfalllayout",
    "version": "1.0.5",
    "description": "一个vue3+ts的composable瀑布流布局组件",
    "main": "index.js",
    "scripts": {
        "build": "rollup -c",
        "test": "echo "Error: no test specified" && exit 1"
    },
    "keywords": [
        "waterfalllayout"
    ],
    "author": "wengyd",
    "license": "ISC",
    "devDependencies": {
        "@rollup/plugin-commonjs": "^25.0.7",
        "@rollup/plugin-node-resolve": "^15.2.3",
        "@types/node": "^20.11.24",
        "rollup": "^2.79.1",
        "rollup-plugin-commonjs": "^10.1.0",
        "rollup-plugin-node-resolve": "^5.2.0",
        "rollup-plugin-terser": "^7.0.2",
        "rollup-plugin-typescript2": "^0.36.0",
        "typescript": "^5.3.3"
    },
    "dependencies": {
        "rollup-plugin-vue": "^6.0.0",
        "vue": "^3.4.21"
    }
}

开发完以后npm run build打包,生成dist目录下的:

59439af77614421cba87c0665913bab7.png

然后将根目录的package.json文件拷贝过来dist目录,执行:

npm publish --registry http://localhost:4873/

进行发布即可;

批量发布依赖

新建一个publish.mjs文件,用mjs是为了能使用ES语法,直接上代码吧:

import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import process from 'process'
import child_process from 'child_process'

const exec = child_process.execSync
const __filename = fileURLToPath(import.meta.url)

const rootPath = path.dirname(__filename)
const consoleLogPath = path.join(rootPath, `publishLog_${format(getNowTime())}`)
const modulePath = path.join(rootPath, 'npmjs/node_modules') //要发布的包目录
const res = fs.readdirSync(modulePath)

let total = 0,
    packtotal = 0,
    successCount = 0,
    failedCount = 0,
    failedList = [],
    beginTime = getNowTime()

function getNowTime() {
    const d = new Date()
    return `${d.getFullYear()}-${
        d.getMonth() + 1
    }-${d.getDate()}:${d.getHours()}:${d.getMinutes()}:${d.getSeconds()}.${d.getMilliseconds()}`
}

function format(timeStr) {
    return timeStr.replace(/[\-\.\:]/g, '_')
}

batchPublish(res, consoleLogPath, modulePath)

function batchPublish(res, consoleLogPath, modulePath, isSubDir) {
    let subPacktotal = 0,
        subSuccessCount = 0,
        subFailCount = 0

    const subBeginTime = getNowTime()

    const isPublish = true

    console.log(
        `\n\n 【-- ${isSubDir ? getNowTime() : beginTime} --${isSubDir ? '子' : '总'}任务开始...】`
    )

    res.forEach((item, index) => {
        const dPath = path.join(modulePath, item)
        const stat = fs.statSync(dPath)

        total++
        if (stat.isDirectory()) {
            process.chdir(dPath)
            console.log(`-->进入目录:${dPath}`)

            const packageJsonPath = path.join(dPath, 'package.json')

            if (fs.existsSync(packageJsonPath)) {
                console.log(`\n已找到package.json文件,启动发布程序...\n`)
                subPacktotal++
                packtotal++
                const packageJsonContentString = fs.readFileSync(packageJsonPath, 'utf-8')
                const parsePackageJson = JSON.parse(packageJsonContentString)
                if (
                    parsePackageJson.scripts &&
                    Object.keys(parsePackageJson.scripts).length !== 0
                ) {
                    parsePackageJson.scripts = {}
                    fs.writeFileSync(packageJsonPath, JSON.stringify(parsePackageJson))
                }

                try {
                    console.log(`包 名 称:${parsePackageJson.name}`)
                    console.log(`文件位置:${dPath}`)
                    console.log(`开始时间:${getNowTime()}\n`)

                    if (isPublish) {
                        console.log(`正在发布中...\n`)

                        exec('npm version patch && npm publish --registry http://localhost:4873/')

                        const newPackageJsonString = fs.readFileSync(packageJsonPath, 'utf-8')
                        const newPackageJson = JSON.parse(newPackageJsonString)

                        console.log(`\n结束时间:${getNowTime()}`)
                        console.log(`原版本号:${parsePackageJson.version}`)
                        console.log(`新版本号:${newPackageJson.version}`)
                        console.log(`处理结果:发布成功!`)
                    } else {
                        console.log(`正在删除中...\n`)

                        exec(
                            `npm unpublish ${parsePackageJson.name} --force --registry http://localhost:4873/`
                        )

                        console.log(`\n结束时间:${getNowTime()}`)
                        console.log(`处理结果:删除成功!`)
                    }
                    successCount++
                    subSuccessCount++
                    fs.appendFileSync(
                        consoleLogPath,
                        `[${index + 1}/${res.length}]${
                            isPublish ? '成功发布包' : '成功删除包'
                        }->${dPath}:${parsePackageJson.name}@${
                            parsePackageJson.version
                        } @${getNowTime()}\n`
                    )
                } catch (e) {
                    console.log(e)
                    console.log(`\n结束时间:${getNowTime()}`)

                    if (isPublish) {
                        console.log(`处理结果:已发布过/发布失败!`)
                    } else {
                        console.log(`处理结果:不存在/删除失败!`)
                    }
                    failedCount++
                    subFailCount++
                    failedList.push(
                        `${dPath}:${parsePackageJson.name}@${
                            parsePackageJson.version
                        } @${getNowTime()}\n`
                    )
                }
            }
            const subRes = fs.readdirSync(dPath)
            batchPublish(subRes, consoleLogPath, dPath, true)
            process.chdir(modulePath)
            console.log(`<--回到目录:${modulePath}`)
        }
    })

    if (isSubDir) {
        console.log(`\n【---${getNowTime()}--子任务结束--exit-】\n`)

        console.log(`\n【子任务统计】:`)
        console.log(`总文件数:${res.length}`)
        console.log(`有 效 包:${subPacktotal}`)
        console.log(`处理成功:${subSuccessCount}`)
        console.log(`失败/不处理:${subFailCount}`)
        console.log(`耗    时:${subBeginTime} --> ${getNowTime()}`)
        console.log(`成 功 率:${(subSuccessCount / subPacktotal) * 100}%\n`)
    } else {
        console.log(`\n总任务已处理完毕,失败/不处理的包日志正在写入...\n`)

        if (failedList.length !== 0) {
            fs.appendFileSync(consoleLogPath, `失败日志列表:\n`)
            failedList.forEach((f, i) => {
                fs.appendFileSync(
                    consoleLogPath,
                    `${isPublish ? '发布失败' : '删除失败'}${i + 1}.${f}`
                )
            })
        }

        console.log(`【总任务统计】:`)
        console.log(`累计文件数:${total}`)
        console.log(`有 效 包:${packtotal}`)
        console.log(`处理成功:${successCount}`)
        console.log(`失败/不处理:${failedCount}`)
        console.log(`耗    时:${beginTime} --> ${getNowTime()}`)
        console.log(`成 功 率:${(successCount / packtotal) * 100}%`)

        console.log(`\n本次总任务已结束,退出-- over --------------------\n`)
    }
}

目录如下:
21370eb88ae54cdf9393b2aa75789072.png
f5d376a1c088435c94d1d5944e10d242.png

然后在publish.mjs文件目录下执行:

node publish.mjs

这样node_modules文件夹下的依赖就会被批量发布到verdaccio,等待执行完毕即可:

到此,关于verdaccio的相关操作都已经说啦,快去试试吧

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿Owen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值