一、背景
npm作为一种包管理工具,无论你是泛前端还是大前端都已经离不开它😺
目前已经有很多成熟的开源 npm 源可以使用,如npm、cnpm、taobao等等,但是出于稳定性、私密性、安全性考虑,公司发展到一定都会搭建自己公司的私有仓库
🧐
那么问题来了,私有包只能在公司内网访问,在访问不了公司内网时,比如交付场景下,需要交付代码且需在客户现场二次开发,私有源npm包是无法安装的😰
二、现状
私服安装逻辑👇
举个🌰:业务工程demo npm包依赖如下,其中@scope1作用域的包为私有包,即只在私有源上有相关包,在非公司内网,@scope1作用域的包是无法安装的🤯
// package.json
{
"name": "dmeo",
"version": "1.0.0",
"scripts": {
"start": "npm run serve",
},
"devDependencies": {
"@commitlint/cli": "11.0.0",
"@commitlint/config-conventional": "11.0.0",
"@scope1/cli-plugin-babel": "^1.0.14",
"@scope1/cli-plugin-vue": "^1.0.14",
"@scope1/cli-service": "^1.0.14",
"autoprefixer": "10.2.5",
"axios": "^0.21.1",
"babel-eslint": "10.1.0",
"babel-plugin-import": "^1.13.3",
"cross-env": "7.0.3",
"cssnano": "4.1.10",
"eslint": "7.21.0",
"eslint-config-prettier": "8.1.0",
"eslint-plugin-prettier": "3.1.4",
"eslint-plugin-vue": "7.7.0",
"globby": "^11.0.3",
"husky": "^4.3.8",
"lib-flexible": "^0.3.2",
"lint-staged": "10.5.4"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "2.6.12",
"vue-router": "3.5.1",
"vuex": "3.6.0",
"webpack-dev-server": "^3.11.2"
}
}
三、需求
-
在非公司a内网,实施现场b
有私服
,即公有包可走现场私服b下载,公司a私服的包无法下载,仅需处理a私服包- @scope1作用域的包(私有包)也是可以正常安装的👌
- 如果@scope1/package1包的依赖子包@scope1/subpackage1,也是可以正常安装的@scope1/subpackage1包的依赖子包...即
子包依赖存在@scope1都是可以正常安装的
👌 - @scope1/package1依赖子包@scope1/subpackage1@1.1.8, @scope1/package2依赖子包@scope1/subpackage1@2.0.4,@scope1/subpackage1
版本不一致
是可以正常安装的👌
-
在非公司a内网,实施现场b
无私服
,即所有的包都需要处理,这时候的处理方案是搭建私服
四、常见解决方案
4.1 有私服场景
大家这时候通常会想到以下解决方案🤢
- 将npm私有包发布到开源npm源
- 存在包的稳定性、私密性、安全性问题🙅♂️
- 将npm私有包发布到客户内部的私有npm源
- 需要将包源码交付到客户内部,存在泄露私密性问题🙅♂️
- 需要按照客户私服npm包发布流程发布包,沟通、成本高😷
- 将私有包拎出来放在一个仓库,仓库部署在客户系统,将依赖改为仓库地址,利用仓库共享npm包
- 需要手动将私包拷贝到git仓库,如果私有包的子包依赖中也有私有包,需要手动修改子包依赖的私有包npm install地址,依次递归;效率低且容易出错🙅♂️
- 客户现场需要多部署一个仓库,走客户的部署流程,成本高 😷
- 在客户现场搭建一个临时私服,将私有包放到私服上,通过私服安装项目依赖
- 搭建临时私服,需要占用机器资源、消耗人力,成本高😷
- 私服存在稳定性问题,机器受客户环境影响🙅♂️
4.2 无私服场景
用verdaccio搭建私服
五、实践方案
npm install <package>
,这里的第三个参数package通常就是我们所要安装的包名,默认配置下npm会从默认的源 (Registry
) 中查找该包名对应的包地址,并下载安装
但在 npm 的世界里,除了简单的指定包名, package 还可以是一个指向有效包名的文件夹路径
。本方案基于此实现,具体如下👇
本工具设计为cli
(命令行界面)
-
提供两种命令
privatify package <package> [scope]
、privatify scope <scope>
供具体工程处理私有包,实现私有源npm包只需执行一个命令即可自动处理成离线包,且处理了私有包子包依赖也存在私有包的场景,私有包子包依赖的私有包版本不一致处理,无需其他操作,以处理私服的场景 -
无私服的场景,提供
privatify register create
命令快速搭建私服,privatify register install
命令在业务工程将npm包缓存到私服仓库
npm-package-privatify
5.1 介绍
一个将私有源npm包处理为离线包的自动化工具
github地址:github.com/zxyue25/npm…
5.2 使用
安装
npm install -g npm-package-privatify
命令
1、 privatify package <package> [scope]
将所声明的npm包package处理为离线包,并查找离线包package子包依赖是否包含scope下的子包,如包含也处理为离线包
scope支持通配符, scope匹配规则:www.npmjs.com/package/min…
参数<package>
:npm包名
// 将packageName处理为私包
privatify package packageName
执行成功后,有两处变化:
a、本工程package.json
// 修改前
...
"dependencies|| devDependencies": {
"packageName": "^0.3.29",
},
// 修改后
...
"dependencies|| devDependencies": {
"packageName": "file:private/packageName-${version}.tar.gz",
},
b、路径private下新增文件packageName-${version}.tar.gz
参数[scope]
:查找包是否存在scope下的依赖,有则会处理子包私有包
privatify package @scope1/packageName @scope1
执行成功后,除了跟上述一样有两处变化,如果子包有依赖scope下的私有包,还有两处变化:
c、private/@scope1/packageName下的package.json文件
// 修改前
...
"dependencies|| devDependencies": {
"@scope1/subPackageName": "^0.3.29",
},
// 修改后
...
"dependencies|| devDependencies": {
"@scope1/subPackageName": "file:../../private/@scope1/packageName-${version}.tar.gz",
},
d、路径private/@scope1下新增文件subPackageName-${version}.tar.g
2、privatify scope <scope>
提供快捷操作,将所声明作用域下的包统一处理为离线包,查找包是否存在scope下的依赖,有则会处理子包私有包
scope支持通配符, scope匹配规则:www.npmjs.com/package/min…
参数1<scope>
:要查找的作用域
// 将在@scope1下的包处理为私包
privatify scope @scope1
3、privatify create <registry-name>
创建一个npm私服仓库
参数:
<registry-name>:私服仓库名称
5.3 实现原理图
npm包处理为离线包原理图:
搭建私服原理图: 待补充
六、总结
其实是业务中真实遇到私有源npm包需处理为离线包的场景,本方案从业务出发进行场景扩展,并抽离为通用方案~