私有包发布
概述
本文档详细介绍 Node.js 私有包的发布、管理和最佳实践。涵盖以下内容:
- 私有仓库搭建:Verdaccio、Nexus、GitHub Packages、GitLab Registry
- 包发布流程:从账号注册到自动发布的完整流程
- 权限管理:用户认证、访问控制、Token 管理
- CI/CD 集成:自动化发布和依赖安装
- 安全与性能:安全审计、缓存优化、镜像配置
目标读者:企业前端团队负责人、DevOps 工程师、前端开发工程师
前置知识:熟悉 npm 基本操作、了解 Node.js 模块系统
为什么需要私有包
核心价值
- 企业内部代码复用:共享业务组件、工具函数、开发规范
- 敏感代码不便开源:包含业务逻辑、专有算法的代码
- 统一管理公共组件:UI 组件库、脚手架模板、配置规范
- 提高开发效率:减少重复开发,保证代码质量一致性
- 版本控制:统一管理依赖版本,避免版本冲突
- 安全审计:可控的依赖来源,降低供应链攻击风险
适用场景
code
场景一:企业组件库
┌─────────────┐
│ 设计系统 │
├─────────────┤
│ UI 组件 │───┐
│ 工具函数 │ │
│ 业务组件 │ │
└─────────────┘ │
│
┌─────────┴──────────┐
│ 私有 npm 仓库 │
└─────────┬──────────┘
┌──────┴───────┬──────────┐
▼ ▼ ▼
项目 A 项目 B 项目 C
场景二:微前端共享模块
┌──────────────────────────────┐
│ 主应用 (Main App) │
├──────────────────────────────┤
│ ┌────────┐ ┌────────┐ │
│ │ 子应用1 │ │ 子应用2 │ │
│ └───┬────┘ └───┬────┘ │
│ │ │ │
│ └─────┬─────┘ │
│ ▼ │
│ @company/shared-utils │
│ @company/shared-types │
│ @company/shared-config │
└──────────────────────────────┘私有仓库方案对比
方案对比表
| 特性 | Verdaccio | Nexus | Artifactory | GitHub Packages | GitLab Registry |
|---|---|---|---|---|---|
| 部署难度 | ⭐ 简单 | ⭐⭐ 中等 | ⭐⭐⭐ 复杂 | 无需部署 | 无需部署 |
| 功能丰富度 | 基础 | 丰富 | 企业级 | 基础 | 基础 |
| 成本 | 免费 | 免费/付费 | 付费 | 免费/付费 | 免费/付费 |
| 性能 | 中等 | 高 | 高 | 取决于 GitHub | 取决于 GitLab |
| 缓存代理 | ✅ | ✅ | ✅ | ❌ | ❌ |
| 多语言支持 | ❌ | ✅ | ✅ | ❌ | ❌ |
| 权限管理 | 基础 | 完善 | 完善 | 依赖 GitHub | 依赖 GitLab |
| 适用规模 | 小型团队 | 中大型 | 企业级 | 任何规模 | 任何规模 |
方案选择建议
code
选择决策树:
团队规模?
├─ 1-10 人
│ └─ 已有 GitHub/GitLab?
│ ├─ 是 → 使用 GitHub Packages / GitLab Registry
│ └─ 否 → 使用 Verdaccio
├─ 10-50 人
│ └─ 需要多语言支持?
│ ├─ 是 → 使用 Nexus
│ └─ 否 → 使用 Verdaccio + 反向代理
└─ 50+ 人
└─ 预算充足?
├─ 是 → 使用 Artifactory / Nexus Pro
└─ 否 → 使用 Nexus OSS发布到 npm 官方仓库
方案说明
npm 官方仓库支持发布私有包,但需要付费订阅:
- Pro 计划:$7/月,支持无限私有包
- Team 计划:$7/用户/月,支持团队协作
- Enterprise 计划:定制价格,企业级功能
注册 npm 账号
bash
# 方式一:命令行注册
npm adduser
# 按提示输入用户名、密码、邮箱
# 方式二:网页注册
# 访问 https://www.npmjs.com/signup
# 登录验证
npm login
# 验证当前登录用户
npm whoami
# 输出:your-username
# 查看当前登录的 registry
npm config get registry
# 输出:https://registry.npmjs.org/准备发布
包结构示例
code
my-package/
├── src/
│ ├── index.js # 入口文件
│ ├── utils.js # 工具函数
│ └── components/ # 组件目录
├── tests/
│ └── index.test.js # 测试文件
├── docs/
│ └── API.md # 文档
├── package.json # 包配置
├── README.md # 说明文档
├── LICENSE # 许可证
└── .npmignore # 发布时忽略的文件package.json 配置
json
{
"name": "@mycompany/my-package",
"version": "1.0.0",
"description": "企业内部工具库",
"main": "src/index.js",
"module": "src/index.esm.js",
"types": "types/index.d.ts",
"files": [
"src",
"types"
],
"scripts": {
"test": "jest",
"build": "rollup -c",
"prepublishOnly": "npm test && npm run build"
},
"keywords": [
"utils",
"tools",
"mycompany"
],
"author": "Your Name <your.email@company.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/mycompany/my-package.git"
},
"bugs": {
"url": "https://github.com/mycompany/my-package/issues"
},
"homepage": "https://github.com/mycompany/my-package#readme",
"publishConfig": {
"access": "restricted",
"registry": "https://registry.npmjs.org/"
},
"private": false
}.npmignore 配置
code
# 测试相关
tests/
*.test.js
*.spec.js
__mocks__/
coverage/
# 开发配置
.travis.yml
.gitignore
.eslintrc
.prettierrc
.editorconfig
# 系统文件
.DS_Store
Thumbs.db
# 文档源文件
docs/
*.md
!README.md
# 构建配置
webpack.config.js
rollup.config.js
babel.config.js
tsconfig.json发布流程
bash
# 1. 检查包名是否可用
npm search @mycompany/my-package
# 或
npm info @mycompany/my-package
# 如果返回 404,说明包名可用
# 2. 本地测试
npm link
# 在其他项目中使用
npm link @mycompany/my-package
# 3. 发布前的最终检查
npm pack --dry-run
# 查看将要发布的文件列表
# 4. 发布
npm publish
# 5. 验证发布成功
npm info @mycompany/my-package发布不同类型
bash
# 发布私有 scoped 包(需付费)
npm publish
# package.json 中 publishConfig.access: "restricted"
# 发布公开 scoped 包(免费)
npm publish --access public
# 或在 package.json 中设置 publishConfig.access: "public"
# 发布 beta 版本
npm publish --tag beta
# 发布 next 版本
npm publish --tag next使用私有仓库
Verdaccio 方案
架构说明
code
┌─────────────────────────────────────┐
│ Verdaccio 代理架构 │
├─────────────────────────────────────┤
│ │
│ 开发者 ──┐ │
│ │ │
│ CI/CD ──┼──▶ Verdaccio ◀── npmjs │
│ │ │ Registry │
│ 其他服务 ─┘ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ 本地存储 │ │
│ │ - 私有包 │ │
│ │ - 缓存公共包 │ │
│ └──────────────┘ │
└─────────────────────────────────────┘
优势:
✅ 缓存公共依赖,加速安装
✅ 统一配置,简化管理
✅ 离线环境可用
✅ 免费、开源安装和启动
bash
# 全局安装
npm install -g verdaccio
# 启动服务(前台运行)
verdaccio
# 默认访问:http://localhost:4873
# 后台运行(使用 pm2)
npm install -g pm2
pm2 start verdaccio --name npm-registry
# 使用 Docker 运行
docker run -d -it --rm --name verdaccio \
-p 4873:4873 \
-v /path/to/storage:/verdaccio/storage \
verdaccio/verdaccio
# 使用 Docker Compose
# docker-compose.yml
version: '3'
services:
verdaccio:
image: verdaccio/verdaccio
container_name: verdaccio
ports:
- '4873:4873'
volumes:
- ./storage:/verdaccio/storage
- ./config:/verdaccio/conf
environment:
- VERDACCIO_PROTOCOL=http配置文件详解
yaml
# ~/.config/verdaccio/config.yaml
# 存储路径
storage: ./storage
# 插件目录
plugins: ./plugins
# 认证配置
auth:
htpasswd:
file: ./htpasswd
max_users: 100 # 最大用户数
# 算法:bcrypt(默认)、md5、sha1
# algorithm: bcrypt
# rounds: 10
# 包权限配置
packages:
# @scope/name 格式的包
'@mycompany/*':
access: $authenticated # 认证用户可访问
publish: $authenticated # 认证用户可发布
unpublish: $authenticated
# proxy: npmjs # 如果本地没有,不从上游获取
# 其他 scoped 包(代理到 npmjs)
'@*/*':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
# 普通包(代理到 npmjs)
'**':
access: $all
publish: $authenticated
unpublish: $authenticated
proxy: npmjs
# 上游仓库配置(代理)
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true
# 请求超时
timeout: 30s
# 请求失败重试
maxage: 2m
# 代理配置(可选)
# agent_options:
# keepAlive: true
# maxSockets: 50
# 添加淘宝镜像作为备用
taobao:
url: https://registry.npmmirror.com/
# 日志配置
logs:
- { type: stdout, format: pretty, level: warn }
# 文件日志
# - { type: file, path: ./verdaccio.log, level: info }
# 监听配置
listen:
- localhost:4873 # 本地访问
# - 0.0.0.0:4873 # 允许外部访问
# Web UI 配置
web:
title: MyCompany NPM Registry
logo: logo.png
scope: '@mycompany'
# 安全配置
security:
api:
legacy: true
jwt:
sign:
expiresIn: 7d
notBefore: 0
web:
sign:
expiresIn: 7d
# 中间件(高级功能)
middlewares:
audit:
enabled: true
# 通知配置
notify:
method: POST
headers: [{ "Content-Type": "application/json" }]
endpoint: https://webhook.company.com/npm-notify
content: '{"name": "{{name}}", "version": "{{version}}"}'用户管理
bash
# 添加用户
npm adduser --registry http://localhost:4873
# 按提示输入用户名、密码、邮箱
# 切换用户
npm logout --registry http://localhost:4873
npm adduser --registry http://localhost:4873
# 查看当前用户
npm whoami --registry http://localhost:4873
# 直接添加用户(使用 htpasswd 工具)
# 安装 htpasswd 工具
npm install -g htpasswd
# 创建用户(需要管理员权限)
htpasswd -c ~/.config/verdaccio/htpasswd username发布和使用
bash
# 方式一:指定 registry
npm publish --registry http://localhost:4873
# 安装私有包
npm install @mycompany/utils --registry http://localhost:4873
# 方式二:使用 .npmrc 配置
# 项目根目录创建 .npmrc
@mycompany:registry=http://localhost:4873
//localhost:4873/:_authToken="${NPM_TOKEN}"
# 方式三:使用 scope 关联
npm config set @mycompany:registry http://localhost:4873
npm publishDocker 生产部署
yaml
# docker-compose.prod.yml
version: '3.8'
services:
verdaccio:
image: verdaccio/verdaccio:5
container_name: verdaccio-prod
restart: always
ports:
- "4873:4873"
volumes:
- ./storage:/verdaccio/storage
- ./config:/verdaccio/conf
- ./plugins:/verdaccio/plugins
environment:
- VERDACCIO_PROTOCOL=http
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:4873"]
interval: 30s
timeout: 10s
retries: 3
# Nginx 反向代理(HTTPS)
nginx:
image: nginx:alpine
container_name: verdaccio-nginx
restart: always
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- verdaccio
# 备份服务
backup:
image: alpine:latest
container_name: verdaccio-backup
restart: always
volumes:
- ./storage:/storage
- ./backups:/backups
command: sh -c "while true; do tar -czf /backups/backup-$$(date +%Y%m%d-%H%M%S).tar.gz /storage && sleep 86400; done"nginx
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream verdaccio {
server verdaccio:4873;
}
server {
listen 443 ssl;
server_name npm.company.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
client_max_body_size 50M;
location / {
proxy_pass http://verdaccio;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}Nexus 方案
架构说明
code
┌──────────────────────────────────────┐
│ Nexus Repository │
├──────────────────────────────────────┤
│ │
│ ┌────────────────────────────┐ │
│ │ npm (proxy) │ │
│ │ 代理 npmjs、淘宝镜像 │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ npm (hosted) │ │
│ │ 存储私有包 │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ npm (group) │ │
│ │ 组合多个仓库 │ │
│ └────────────────────────────┘ │
│ │
│ 支持:npm、maven、docker、pypi... │
└──────────────────────────────────────┘安装和启动
bash
# 下载 Nexus
wget https://download.sonatype.com/nexus/3/latest-unix.tar.gz
# 解压
tar -zxvf latest-unix.tar.gz
# 启动
cd nexus-3.x.x/bin
./nexus start
# 查看状态
./nexus status
# 访问:http://localhost:8081
# 默认账号:admin
# 默认密码:admin123 或查看 admin.password 文件bash
# Docker 运行
docker run -d -p 8081:8081 --name nexus \
-v nexus-data:/nexus-data \
sonatype/nexus3
# Docker Compose
version: '3'
services:
nexus:
image: sonatype/nexus3
container_name: nexus
ports:
- "8081:8081"
volumes:
- nexus-data:/nexus-data
environment:
- INSTALL4J_ADD_VM_PARAMS=-Xms2g -Xmx2g -XX:MaxDirectMemorySize=2g创建 npm 仓库
-
创建 npm (hosted) 仓库:存储私有包
- Repository → Repositories → Create repository
- 选择
npm (hosted) - Name:
npm-private - Blob store:
default
-
创建 npm (proxy) 仓库:代理公共包
- 选择
npm (proxy) - Name:
npm-proxy - Proxy → Remote storage:
https://registry.npmjs.org/
- 选择
-
创建 npm (group) 仓库:组合仓库
- 选择
npm (group) - Name:
npm-group - Member repositories: 添加
npm-private和npm-proxy
- 选择
配置和使用
bash
# 设置 npm registry
npm config set registry http://localhost:8081/repository/npm-group/
# 或使用 .npmrc
# .npmrc
registry=http://localhost:8081/repository/npm-group/
@mycompany:registry=http://localhost:8081/repository/npm-private/
//localhost:8081/repository/npm-private/:_authToken=${NEXUS_TOKEN}
# 登录
npm login --registry=http://localhost:8081/repository/npm-private/
# 发布
npm publish --registry=http://localhost:8081/repository/npm-private/
# 或在 package.json 中配置
{
"publishConfig": {
"registry": "http://localhost:8081/repository/npm-private/"
}
}用户权限管理
code
Nexus 权限模型:
┌─────────────────┐
│ Role │
│ - npm-admin │──▶ 所有权限
│ - npm-developer│──▶ 发布/读取
│ - npm-readonly │──▶ 仅读取
└─────────────────┘
│
▼
┌─────────────────┐
│ User │
│ - developer1 │
│ - developer2 │
└─────────────────┘GitHub Packages
特点
- ✅ 与 GitHub 仓库集成
- ✅ 支持 scoped 包
- ✅ 使用 GitHub Token 认证
- ✅ 免费额度:公开包无限制,私有包有配额
- ❌ 无法代理公共包
完整配置流程
bash
# 1. 创建 GitHub Token
# Settings → Developer settings → Personal access tokens → Tokens (classic)
# 勾选权限:
# - write:packages
# - read:packages
# - repo (如果是私有仓库)
# 2. 配置 .npmrc(项目级)
# .npmrc
@username:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
# 3. 配置 package.json
{
"name": "@username/my-package",
"version": "1.0.0",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
},
"repository": {
"type": "git",
"url": "git+https://github.com/username/my-package.git"
}
}
# 4. 设置环境变量
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
# 5. 发布
npm publish组织包管理
bash
# 组织级别的包
# package.json
{
"name": "@myorg/ui-components",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
# .npmrc
@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
# 发布后访问:
# https://github.com/orgs/myorg/packages?repo_name=ui-componentsCI/CD 配置
yaml
# .github/workflows/publish.yml
name: Publish Package
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://npm.pkg.github.com'
scope: '@mycompany'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}GitLab Package Registry
配置流程
bash
# 1. 创建 GitLab Access Token
# Settings → Access Tokens
# 勾选权限:api、write_package_registry、read_package_registry
# 2. 配置 .npmrc
# 项目级
@mycompany:registry=https://gitlab.com/api/v4/projects/${PROJECT_ID}/packages/npm/
//gitlab.com/api/v4/projects/${PROJECT_ID}/packages/npm/:_authToken=${GITLAB_TOKEN}
# 或实例级
@mycompany:registry=https://gitlab.com/api/v4/packages/npm/
//gitlab.com/api/v4/packages/npm/:_authToken=${GITLAB_TOKEN}
# 3. 配置 package.json
{
"name": "@mycompany/utils",
"version": "1.0.0",
"publishConfig": {
"registry": "https://gitlab.com/api/v4/projects/${PROJECT_ID}/packages/npm/"
}
}
# 4. 设置环境变量
export GITLAB_TOKEN=glpat-xxxxxxxxxxxxx
export PROJECT_ID=12345
# 5. 发布
npm publishScoped 包详解
什么是 Scoped 包
Scoped 包是以 @scope/name 格式命名的包,提供命名空间隔离。
json
// 普通 package.json
{
"name": "my-utils", // 可能与 npm 上已存在的包冲突
"version": "1.0.0"
}
// Scoped package.json
{
"name": "@mycompany/utils", // 唯一命名空间
"version": "1.0.0"
}命名规范
code
命名规则:
✅ @scope/package-name (小写字母、数字、连字符)
✅ @my-company/utils (scope 可以包含连字符)
✅ @mycompany2024/common (可以包含数字)
❌ @MyCompany/Utils (不能有大写字母)
❌ @mycompany/utils_v1 (不能有下划线)
❌ @mycompany/utils.common (不能有点号)发布类型
json
{
"name": "@mycompany/utils",
"version": "1.0.0",
"publishConfig": {
// 私有包(需付费,或私有仓库)
"access": "restricted",
// 公开包(免费)
// "access": "public"
}
}bash
# 发布私有包
npm publish
# 发布公开 scoped 包(首次需要指定)
npm publish --access public
# 后续发布会记住 access 设置
npm publish多 scope 管理
bash
# .npmrc 配置多个 scope
@mycompany:registry=https://npm.mycompany.com
@myteam:registry=https://npm.myteam.com
@partner:registry=https://npm.partner.com
registry=https://registry.npmjs.org/
# 安装时自动识别
npm install @mycompany/utils # 从 mycompany registry 安装
npm install @myteam/common # 从 myteam registry 安装
npm install lodash # 从 npmjs 安装配置私有 Registry
配置层级
code
配置优先级(从高到低):
1. 命令行参数 --registry
2. 项目级 .npmrc (项目根目录)
3. 用户级 .npmrc (~/.npmrc)
4. 全局级 .npmrc ($PREFIX/etc/npmrc)
5. npm 内置默认配置项目级配置
bash
# 项目根目录 .npmrc
# 所有包使用私有 registry
registry=https://npm.mycompany.com
# 仅 @mycompany scope 使用私有 registry
@mycompany:registry=https://npm.mycompany.com
# 认证配置
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# 代理配置
proxy=http://proxy.company.com:8080
https-proxy=http://proxy.company.com:8080用户级配置
bash
# ~/.npmrc
# 默认 registry
registry=https://registry.npmjs.org/
# 企业 scope
@mycompany:registry=https://npm.mycompany.com
@myteam:registry=https://npm.myteam.com
# 认证(安全做法:使用环境变量)
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
//npm.myteam.com/:_authToken=${MYTEAM_TOKEN}
# 缓存配置
cache=/path/to/npm-cache
# 日志级别
loglevel=warn使用 nrm 管理源
bash
# 安装
npm install -g nrm
# 查看所有可用源
nrm ls
# 输出:
# npm -------- https://registry.npmjs.org/
# yarn ------- https://registry.yarnpkg.com/
# cnpm ------- https://r.cnpmjs.org/
# taobao ----- https://registry.npmmirror.com/
# 添加私有源
nrm add company https://npm.mycompany.com
# 切换源
nrm use company
# 测试源速度
nrm test company
# 删除源
nrm del company环境变量配置
bash
# .bashrc / .zshrc
export NPM_TOKEN=npm_xxxxxxxxxxxx
export NPM_CONFIG_REGISTRY=https://npm.mycompany.com
# 或使用 direnv
# .envrc
export NPM_TOKEN=npm_xxxxxxxxxxxx
export NPM_CONFIG_CACHE=/tmp/npm-cache
# CI 环境变量
# GitHub Actions
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# GitLab CI
variables:
NPM_TOKEN: $CI_JOB_TOKEN包生命周期管理
版本管理
bash
# 语义化版本:MAJOR.MINOR.PATCH
# MAJOR: 破坏性变更
# MINOR: 新功能,向后兼容
# PATCH: Bug 修复
# 更新版本
npm version patch # 1.0.0 → 1.0.1
npm version minor # 1.0.1 → 1.1.0
npm version major # 1.1.0 → 2.0.0
# 预发布版本
npm version prerelease # 1.0.0 → 1.0.1-0
npm version prepatch # 1.0.0 → 1.0.1-0
npm version preminor # 1.0.0 → 1.1.0-0
npm version premajor # 1.0.0 → 2.0.0-0
# 带标签的预发布
npm version 2.0.0-beta.1
# 自定义版本号
npm version 1.2.3发布管理
bash
# 发布到不同 tag
npm publish # 发布到 latest(默认)
npm publish --tag beta # 发布到 beta
npm publish --tag next # 发布到 next
npm publish --tag rc # 发布到 rc
# 安装指定 tag
npm install @mycompany/utils@beta
npm install @mycompany/utils@next
# 查看所有 tag
npm dist-tag ls @mycompany/utils
# 输出:
# beta: 2.0.0-beta.1
# latest: 1.5.0
# next: 2.0.0-next.2
# 修改 tag 指向
npm dist-tag add @mycompany/utils@2.0.0 latest
npm dist-tag rm @mycompany/utils beta弃用和下架
bash
# 弃用某个版本(仍然可安装,但会显示警告)
npm deprecate @mycompany/utils@1.0.0 "此版本有严重 bug,请升级到 1.0.1"
# 弃用整个包
npm deprecate @mycompany/utils "此包已废弃,请使用 @mycompany/new-utils"
# 取消弃用
npm deprecate @mycompany/utils@1.0.0 ""
# 下架包(仅限发布 24 小时内)
npm unpublish @mycompany/utils@1.0.0
# 下架整个包(需要确认)
npm unpublish @mycompany/utils --force
# 从私有仓库下架
npm unpublish @mycompany/utils --registry https://npm.mycompany.comMonorepo 场景
使用 Lerna
bash
# 安装 Lerna
npm install -g lerna
# 初始化项目
mkdir my-monorepo && cd my-monorepo
lerna init
# 项目结构
my-monorepo/
├── packages/
│ ├── utils/
│ │ ├── package.json
│ │ └── src/
│ ├── ui-components/
│ │ ├── package.json
│ │ └── src/
│ └── cli/
│ ├── package.json
│ └── src/
├── lerna.json
└── package.jsonjson
// lerna.json
{
"version": "1.0.0",
"npmClient": "npm",
"command": {
"publish": {
"registry": "https://npm.mycompany.com"
},
"bootstrap": {
"npmClientArgs": ["--no-package-lock"]
}
},
"packages": ["packages/*"]
}bash
# 发布所有包
lerna publish
# 发布特定包
lerna publish --scope=@mycompany/utils
# 独立版本模式
lerna publish -- independent
# 发布到私有 registry
lerna publish --registry https://npm.mycompany.com使用 pnpm workspaces
yaml
# pnpm-workspace.yaml
packages:
- 'packages/*'json
// package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "pnpm -r run build",
"test": "pnpm -r run test",
"publish": "pnpm -r publish"
}
}bash
# 发布所有包
pnpm -r publish --registry https://npm.mycompany.com
# 发布特定包
pnpm --filter @mycompany/utils publishCI/CD 集成
GitHub Actions 完整配置
yaml
# .github/workflows/publish.yml
name: Publish Package
on:
push:
tags:
- 'v*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
publish-npm:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
publish-github:
needs: test
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://npm.pkg.github.com'
scope: '@mycompany'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Publish to GitHub Packages
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}GitLab CI 完整配置
yaml
# .gitlab-ci.yml
stages:
- test
- build
- publish
variables:
NODE_VERSION: '20'
# 测试
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm test
coverage: '/Coverage.*?\s+(\d+%)}/'
# 构建
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
# 发布到 GitLab Registry
publish-gitlab:
stage: publish
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
- npm publish
only:
- tags
variables:
GITLAB_TOKEN: $CI_JOB_TOKEN
# 发布到私有 Nexus
publish-nexus:
stage: publish
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
- npm publish --registry https://nexus.company.com/repository/npm-private/
only:
- tags
variables:
NPM_TOKEN: $NEXUS_TOKENJenkins Pipeline
groovy
// Jenkinsfile
pipeline {
agent any
environment {
NPM_TOKEN = credentials('npm-token')
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
post {
always {
junit 'reports/*.xml'
}
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
stage('Publish') {
when {
tag pattern: 'v*', comparator: 'GLOB'
}
steps {
sh 'npm publish'
}
}
}
post {
success {
slackSend channel: '#releases',
color: 'good',
message: "Published ${env.JOB_NAME} ${env.BUILD_TAG}"
}
}
}依赖安全审计
安全扫描工具
bash
# npm audit
npm audit
# 查看详细漏洞
npm audit --json
# 自动修复
npm audit fix
# 强制修复(可能升级主版本)
npm audit fix --force
# 使用第三方工具
npx better-npm-audit audit
npx audit-ci --moderate.npmignore 安全配置
code
# .npmignore
# 敏感信息
.env
.env.local
*.key
*.pem
secrets/
# 配置文件
config/
.eslintrc
.prettierrc
tsconfig.json
# 源代码(只发布构建产物)
src/
tests/
docs/
# 开发工具
.github/
.vscode/
.idea/
# CI 配置
.travis.yml
.circleci/
Jenkinsfile包内容检查
bash
# 检查将要发布的文件
npm pack --dry-run
# 检查已发布包的内容
npm pack @mycompany/utils
tar -tzf mycompany-utils-1.0.0.tgz
# 使用 npm-packlist 查看
npx npm-packlist安全最佳实践
bash
# 1. 使用锁文件
npm install --package-lock-only
git add package-lock.json
# 2. 验证包签名(如果支持)
npm install --verify-signatures
# 3. 使用 npm ci 而非 npm install
npm ci
# 4. 定期更新依赖
npm outdated
npm update
# 5. 使用 Dependabot 自动更新
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"性能优化
缓存策略
bash
# 配置缓存路径
npm config set cache /path/to/npm-cache --global
# 查看缓存
npm cache ls
# 清理缓存
npm cache clean --force
# 验证缓存
npm cache verify镜像加速
bash
# 使用淘宝镜像
npm config set registry https://registry.npmmirror.com
# 使用 cnpm
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install
# 临时使用镜像
npm install --registry=https://registry.npmmirror.com私有仓库缓存优化
yaml
# Verdaccio 配置优化
uplinks:
npmjs:
url: https://registry.npmjs.org/
cache: true
maxage: 30m # 缓存 30 分钟
# Nexus 配置优化
# 修改 npm proxy 仓库配置
# Proxy → Content Discovery → Maximum Component Age: 1440 (分钟)最佳实践
1. 包命名规范
json
{
"name": "@company/product-feature",
"version": "1.0.0",
"description": "产品特性模块",
"keywords": ["company", "product", "feature"]
}命名约定:
- 使用
@company/前缀 - 采用
产品-功能结构 - 使用小写字母和连字符
- 包含描述性关键词
2. 版本发布流程
bash
# 完整发布流程
# 1. 更新代码
git checkout main
git pull origin main
# 2. 安装依赖
npm ci
# 3. 运行测试
npm test
# 4. 构建项目
npm run build
# 5. 更新版本
npm version minor -m "chore(release): %s"
# 6. 发布
npm publish
# 7. 推送标签
git push --follow-tags3. CHANGELOG 管理
markdown
# CHANGELOG.md
## [1.2.0] - 2024-01-15
### Added
- 新增深色模式支持
- 添加国际化功能
### Changed
- 优化组件性能
- 更新依赖版本
### Fixed
- 修复按钮点击无响应问题
- 修复移动端样式错乱
### Breaking Changes
- 移除废弃的 API 方法
- 升级最低 Node.js 版本要求至 16bash
# 使用 standard-version 自动生成
npm install -g standard-version
standard-version4. 文档规范
markdown
# README.md 模板
## @mycompany/utils
企业级工具函数库
## 安装
\`\`\`bash
npm install @mycompany/utils
\`\`\`
## 快速开始
\`\`\`javascript
import { formatDate, debounce } from '@mycompany/utils';
console.log(formatDate(new Date())); // 2024-01-15
\`\`\`
## API 文档
### formatDate(date, format)
格式化日期
**参数:**
- `date` (Date): 日期对象
- `format` (string): 格式化字符串,默认 'YYYY-MM-DD'
**返回:**
- (string): 格式化后的日期字符串
**示例:**
\`\`\`javascript
formatDate(new Date(), 'YYYY/MM/DD'); // 2024/01/15
\`\`\`
## 更新日志
查看 CHANGELOG.md(变更记录)
## 贡献指南
查看 CONTRIBUTING.md(贡献指南)
## 许可证
MIT5. TypeScript 支持
json
// package.json
{
"name": "@mycompany/utils",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}typescript
// src/index.ts
export function formatDate(date: Date, format: string = 'YYYY-MM-DD'): string {
// 实现
}
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
// 实现
}6. 测试覆盖
bash
# Jest 配置
npm install --save-dev jest @types/jest
# package.json
{
"scripts": {
"test": "jest",
"test:coverage": "jest --coverage"
}
}javascript
// jest.config.js
module.exports = {
testEnvironment: 'node',
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};常见问题
发布相关
1. 发布失败:403 Forbidden
bash
# 原因:权限不足或包名已被占用
# 解决方案 1:检查登录状态
npm whoami
npm whoami --registry https://npm.mycompany.com
# 解决方案 2:检查包是否存在
npm info @mycompany/utils
# 解决方案 3:检查 publishConfig
{
"publishConfig": {
"access": "restricted", // 私有包需要权限
"registry": "https://npm.mycompany.com"
}
}
# 解决方案 4:重新登录
npm logout
npm login --registry https://npm.mycompany.com2. 发布失败:需要付费账户
bash
# 原因:在 npmjs 发布私有包需要付费
# 解决方案 1:发布为公开包
npm publish --access public
# 解决方案 2:使用私有仓库
npm publish --registry https://npm.mycompany.com
# 解决方案 3:使用 GitHub Packages(免费私有包)
npm publish --registry https://npm.pkg.github.com3. 发布后包内容缺失
bash
# 原因:.npmignore 配置错误或 files 字段缺失
# 检查将要发布的文件
npm pack --dry-run
# 解决方案 1:检查 .npmignore
cat .npmignore
# 解决方案 2:使用 files 字段
{
"files": [
"dist",
"src"
]
}
# 解决方案 3:移除 .npmignore(使用 .gitignore)安装相关
4. 安装失败:404 Not Found
bash
# 原因:registry 配置错误或包不存在
# 检查 registry 配置
npm config get @mycompany:registry
npm config get registry
# 解决方案:正确配置 scope registry
npm config set @mycompany:registry https://npm.mycompany.com
# 或使用 .npmrc
@mycompany:registry=https://npm.mycompany.com5. 安装失败:认证失败
bash
# 原因:Token 无效或过期
# 检查 token 配置
cat ~/.npmrc | grep authToken
# 解决方案 1:重新登录
npm logout --registry https://npm.mycompany.com
npm login --registry https://npm.mycompany.com
# 解决方案 2:更新 token
# .npmrc
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# 设置环境变量
export NPM_TOKEN=your_new_token6. 安装速度慢
bash
# 原因:网络问题或 registry 性能差
# 解决方案 1:使用镜像
npm config set registry https://registry.npmmirror.com
# 解决方案 2:使用代理
npm config set proxy http://proxy.company.com:8080
npm config set https-proxy http://proxy.company.com:8080
# 解决方案 3:使用私有缓存(Verdaccio)
npm config set registry http://localhost:4873其他问题
7. Token 泄露处理
bash
# 立即撤销 token
# 访问 npmjs.com → Access Tokens → Delete
# 或使用命令行
npm token revoke <token-id>
# 生成新 token
npm token create --read-only # 只读 token
# 更新所有环境的 token
# 1. 更新 CI/CD secrets
# 2. 更新本地 .npmrc
# 3. 更新团队共享配置8. 版本回退
bash
# 方式 1:弃用新版本
npm deprecate @mycompany/utils@2.0.0 "请使用 1.5.0"
# 方式 2:发布修复版本
npm version 1.5.1
npm publish
# 方式 3:删除版本(仅 24 小时内)
npm unpublish @mycompany/utils@2.0.0
# 方式 4:修改 tag
npm dist-tag add @mycompany/utils@1.5.0 latest9. 包体积过大
bash
# 检查包体积
npm pack
ls -lh *.tgz
# 分析包内容
npx npm-packlist
# 优化方案 1:排除测试文件
# .npmignore
tests/
*.test.js
*.spec.js
__tests__/
# 优化方案 2:排除开发配置
.eslintrc
tsconfig.json
webpack.config.js
# 优化方案 3:只发布构建产物
{
"files": ["dist"]
}安全建议
1. Token 管理
bash
# ✅ 使用环境变量
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# ❌ 不要硬编码
//npm.mycompany.com/:_authToken=npm_xxxxxxxxxxxx
# ✅ 使用只读 token(CI 安装依赖)
npm token create --read-only
# ✅ 使用发布专用 token
npm token create --publish
# ✅ 定期轮换 token(建议 90 天)
npm token revoke <old-token-id>
npm token create2. .npmrc 安全
bash
# ✅ 项目级 .npmrc(不含敏感信息)
@mycompany:registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# ✅ 用户级 .npmrc(包含敏感信息)
# ~/.npmrc 权限设置为 600
chmod 600 ~/.npmrc
# ✅ .gitignore 忽略 .npmrc
.gitignore
.npmrc
# 或使用 .npmrc 模板
.npmrc.examplebash
# .npmrc.example(提交到 Git)
@mycompany:registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# 团队成员复制并配置
cp .npmrc.example .npmrc
# 设置环境变量
export NPM_TOKEN=your_token3. CI/CD 安全
yaml
# ✅ GitHub Actions 使用 secrets
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# ✅ GitLab CI 使用 CI variables
variables:
NPM_TOKEN: $CI_NPM_TOKEN
# ✅ 限制 token 权限
# 只读 token 用于依赖安装
# 发布 token 仅用于发布流程
# ✅ 使用 OIDC(如支持)
# GitHub Packages 可以使用 GITHUB_TOKEN
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}4. 依赖安全
bash
# ✅ 定期运行安全审计
npm audit
npm audit fix
# ✅ 使用 npm ci 而非 npm install
npm ci # 严格按照 package-lock.json 安装
# ✅ 锁定依赖版本
{
"dependencies": {
"lodash": "4.17.21" # 精确版本
}
}
# ✅ 使用 Dependabot 自动更新
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 105. 访问控制
code
权限分级建议:
管理员
├─ 创建/删除仓库
├─ 管理用户权限
└─ 配置仓库设置
发布者
├─ 发布包
├─ 弃用包
└─ 修改 tag
开发者
├─ 安装包
└─ 查看包信息
访客
└─ 查看公开包yaml
# Verdaccio 权限配置示例
packages:
'@mycompany/*':
access: $authenticated
publish: mycompany-team
unpublish: admin
'@mycompany/public/*':
access: $all
publish: $authenticated
'@mycompany/internal/*':
access: admin-team
publish: admin-team6. 审计日志
bash
# 查看发布历史
npm info @mycompany/utils time
# Nexus 审计日志
# Administration → System → Audit Log
# GitLab Package Registry
# Deploy → Package Registry → Package History
# GitHub Packages
# Packages → [package-name] → Activity附录
相关资源
常用命令速查
bash
# 用户管理
npm adduser # 注册/登录
npm login # 登录
npm logout # 登出
npm whoami # 查看当前用户
# 发布管理
npm publish # 发布
npm publish --tag beta # 发布到 beta tag
npm deprecate <pkg> # 弃用包
npm unpublish <pkg> # 下架包
# 版本管理
npm version patch # 更新版本
npm version minor
npm version major
npm dist-tag ls <pkg> # 查看 tag
npm dist-tag add <pkg>@<version> <tag> # 添加 tag
# 信息查询
npm info <pkg> # 查看包信息
npm view <pkg> versions # 查看所有版本
npm search <pkg> # 搜索包
# 配置管理
npm config list # 查看配置
npm config get registry # 查看 registry
npm config set registry <url> # 设置 registry
# 安全审计
npm audit # 安全检查
npm audit fix # 自动修复
# 调试工具
npm pack --dry-run # 查看将要发布的文件
npm cache verify # 验证缓存
npm doctor # 诊断环境问题文档版本:2.0.0
最后更新:2025年2月
维护团队:前端架构组