{T}

私有包发布

概述

本文档详细介绍 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     │
└──────────────────────────────┘

私有仓库方案对比

方案对比表

特性VerdaccioNexusArtifactoryGitHub PackagesGitLab 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 publish

Docker 生产部署

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 仓库

  1. 创建 npm (hosted) 仓库:存储私有包

    • Repository → Repositories → Create repository
    • 选择 npm (hosted)
    • Name: npm-private
    • Blob store: default
  2. 创建 npm (proxy) 仓库:代理公共包

    • 选择 npm (proxy)
    • Name: npm-proxy
    • Proxy → Remote storage: https://registry.npmjs.org/
  3. 创建 npm (group) 仓库:组合仓库

    • 选择 npm (group)
    • Name: npm-group
    • Member repositories: 添加 npm-privatenpm-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-components

CI/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 publish

Scoped 包详解

什么是 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.com

Monorepo 场景

使用 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.json
json
// 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 publish

CI/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_TOKEN

Jenkins 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-tags

3. CHANGELOG 管理

markdown
# CHANGELOG.md

## [1.2.0] - 2024-01-15

### Added
- 新增深色模式支持
- 添加国际化功能

### Changed
- 优化组件性能
- 更新依赖版本

### Fixed
- 修复按钮点击无响应问题
- 修复移动端样式错乱

### Breaking Changes
- 移除废弃的 API 方法
- 升级最低 Node.js 版本要求至 16
bash
# 使用 standard-version 自动生成
npm install -g standard-version
standard-version

4. 文档规范

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(贡献指南)

## 许可证

MIT

5. 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.com

2. 发布失败:需要付费账户

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.com

3. 发布后包内容缺失

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.com

5. 安装失败:认证失败

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_token

6. 安装速度慢

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 latest

9. 包体积过大

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 create

2. .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.example
bash
# .npmrc.example(提交到 Git)
@mycompany:registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=${NPM_TOKEN}

# 团队成员复制并配置
cp .npmrc.example .npmrc
# 设置环境变量
export NPM_TOKEN=your_token

3. 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: 10

5. 访问控制

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-team

6. 审计日志

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月
维护团队:前端架构组