NPM 包发布与管理完全指南

2025/12/06 npmnodepackage.json

随着前端项目复杂度的提升,模块化开发已成为标配。NPM(Node Package Manager)作为 JavaScript 生态系统中最大的包管理工具,不仅让我们能够便捷地使用他人的代码,也为我们提供了分享自己代码的平台。

本文将从零开始,循序渐进地介绍如何发布和管理 npm 包,涵盖从入门到进阶的完整知识体系,让初学者能够快速上手,也让有经验的开发者能够查漏补缺。

# 一、快速开始:发布你的第一个 npm 包

# 1.1 前置准备

在发布 npm 包之前,你需要完成以下准备工作:

# 注册 npm 账号

访问 npmjs.com (opens new window) 注册一个账号。注册完成后,在本地登录:

npm login

按提示输入用户名、密码和邮箱。登录成功后,可以通过以下命令确认:

npm whoami

# 初始化项目

使用 npm init 创建一个新项目:

mkdir my-awesome-package
cd my-awesome-package
npm init

npm init 会以交互式方式引导你创建 package.json 文件。如果想跳过交互,使用 -y 参数:

npm init -y

这会使用默认值创建 package.json

# 1.2 最小化配置

要发布一个 npm 包,package.json 中只有两个字段是必需的:

{
  "name": "my-awesome-package",
  "version": "1.0.0"
}

# name - 包名

包名需要遵循以下规则:

  • 唯一性:在 npm registry 中不能与现有包重名
  • 长度限制:不超过 214 个字符
  • 命名规范
    • 只能包含小写字母、数字、连字符 - 和下划线 _
    • 不能以 ._ 开头
    • 不能包含非 URL 安全字符
    • 不能与 Node.js 核心模块同名

检查包名是否可用:

npm view <package-name>

如果显示 404,说明该名称可用。

作用域包(Scoped Packages)

为了避免命名冲突,可以使用作用域包:

{
  "name": "@your-username/my-awesome-package"
}

作用域包默认是私有的。要发布公开的作用域包,需要使用:

npm publish --access public

# version - 版本号

版本号遵循 Semantic Versioning (opens new window)(语义化版本)规范,格式为 X.Y.Z

  • X(主版本号 Major):不兼容的 API 修改
  • Y(次版本号 Minor):向下兼容的功能性新增
  • Z(修订号 Patch):向下兼容的问题修正

示例:

  • 1.0.01.0.1:修复了一个 bug
  • 1.0.01.1.0:新增了一个功能
  • 1.0.02.0.0:有破坏性的改动

# 1.3 发布流程

# 创建入口文件

创建一个 index.js 文件作为包的入口:

// index.js
function sayHello(name) {
  return `Hello, ${name}!`;
}

module.exports = { sayHello };

# 测试发布

在正式发布前,建议先测试一下:

# 查看将要发布的文件列表
npm pack --dry-run

# 或者实际打包查看
npm pack

# 发布到 npm

npm publish

首次发布作用域包时:

npm publish --access public

发布成功后,可以在 npmjs.com (opens new window) 上搜索到你的包。

# 快速示例

让我们看一个完整的最小化示例:

项目结构:

my-hello-package/
├── index.js
├── package.json
└── README.md

package.json:

{
  "name": "@username/my-hello-package",
  "version": "1.0.0",
  "description": "A simple hello package",
  "main": "index.js",
  "keywords": ["hello", "demo"],
  "author": "Your Name",
  "license": "MIT"
}

index.js:

exports.sayHello = (name = 'World') => `Hello, ${name}!`;

README.md:

# My Hello Package

A simple package that says hello.

## Installation
\`\`\`bash
npm install @username/my-hello-package
\`\`\`

## Usage
\`\`\`javascript
const { sayHello } = require('@username/my-hello-package');
console.log(sayHello('NPM'));
\`\`\`

发布命令:

npm publish --access public

# 二、进阶配置:让你的包更专业

# 2.1 版本管理策略

手动修改版本号容易出错,使用 npm 提供的版本管理命令更安全:

# 修订号 +1 (1.0.0 -> 1.0.1)
npm version patch

# 次版本号 +1 (1.0.0 -> 1.1.0)
npm version minor

# 主版本号 +1 (1.0.0 -> 2.0.0)
npm version major

这些命令会:

  1. 修改 package.json 中的版本号
  2. 自动创建 git commit
  3. 打上 git tag

完整的发布流程:

# 1. 更新版本
npm version patch

# 2. 推送代码和标签
git push && git push --tags

# 3. 发布到 npm
npm publish

# 预发布版本

对于测试版本,使用预发布标识:

# 发布 alpha 版本 (1.0.0-alpha.0)
npm version prerelease --preid=alpha

# 发布 beta 版本 (1.0.0-beta.0)
npm version prerelease --preid=beta

# 发布 rc 版本 (1.0.0-rc.0)
npm version prerelease --preid=rc

用户可以通过标签安装特定版本:

# 安装最新稳定版
npm install my-package

# 安装 beta 版本
npm install my-package@beta

# 安装特定版本
npm install my-package@1.0.0-beta.0

发布预发布版本时使用标签:

npm publish --tag beta

# 2.2 文件管理

默认情况下,npm 会发布项目中的所有文件(除了某些内置忽略的文件)。为了控制发布内容,有三种方式:

# 方式一:使用 files 字段(推荐)

package.json 中指定要发布的文件:

{
  "files": [
    "dist",
    "lib",
    "src",
    "README.md",
    "LICENSE"
  ]
}

这是白名单机制,只有列出的文件和目录会被发布。

注意:以下文件会始终被包含,无需配置:

  • package.json
  • README.md
  • LICENSE
  • main 字段指定的文件

# 方式二:使用 .npmignore

类似 .gitignore,指定不发布的文件:

# .npmignore
node_modules
src
tests
*.test.js
.eslintrc

这是黑名单机制。

优先级

  • 如果存在 files 字段,.npmignore 会被忽略
  • 如果没有 files 字段但有 .npmignore,则使用 .npmignore
  • 如果都没有,则使用 .gitignore

# 方式三:使用 .gitignore

如果既没有 files 也没有 .npmignore,npm 会使用 .gitignore 的规则。

# 最佳实践

  • 优先使用 files 字段:更明确,不易出错
  • 查看发布内容:发布前使用 npm pack --dry-run 检查
  • 精简发布:只发布必要文件,减小包体积
# 查看将被发布的文件
npm pack --dry-run

# 实际打包到 .tgz 文件
npm pack

# 查看打包内容
tar -tzf your-package-1.0.0.tgz

# 2.3 入口文件配置

入口文件配置决定了用户如何引入你的包,这对包的使用体验至关重要。

# main - 主入口(必需)

main 字段指定包的主入口文件,默认是 index.js

{
  "main": "dist/index.js"
}

当用户使用 require('your-package') 时,会加载这个文件。

# module - ES Module 入口

module 字段指定 ES Module 格式的入口文件:

{
  "main": "dist/index.js",      // CommonJS 入口
  "module": "dist/index.esm.js" // ES Module 入口
}

支持 ES Module 的打包工具(如 Webpack、Rollup)会优先使用 module 字段,以便进行 Tree Shaking 优化。

# browser - 浏览器入口

为浏览器环境指定特殊的入口:

{
  "main": "dist/index.js",
  "browser": "dist/index.browser.js"
}

或者针对不同文件做映射:

{
  "browser": {
    "./lib/server.js": "./lib/browser.js",
    "fs": false
  }
}

# exports - 现代化的入口配置(推荐)

exports 是最现代的入口配置方式,优先级最高:

基础用法:

{
  "exports": {
    "require": "./dist/index.cjs",
    "import": "./dist/index.mjs"
  }
}

等同于:

{
  "exports": {
    ".": {
      "require": "./dist/index.cjs",
      "import": "./dist/index.mjs"
    }
  }
}

多环境支持:

{
  "exports": {
    ".": {
      "node": {
        "require": "./dist/index.cjs",
        "import": "./dist/index.mjs"
      },
      "browser": {
        "require": "./dist/browser.cjs",
        "import": "./dist/browser.mjs"
      },
      "default": "./dist/index.js"
    }
  }
}

子路径导出:

{
  "exports": {
    ".": "./dist/index.js",
    "./utils": "./dist/utils.js",
    "./style.css": "./dist/style.css",
    "./package.json": "./package.json"
  }
}

用户可以这样使用:

import pkg from 'your-package';           // 主入口
import { format } from 'your-package/utils'; // 子路径
import 'your-package/style.css';          // 样式文件

封装性exports 未定义的路径无法被外部访问:

// ❌ 报错:不在 exports 中定义
import internal from 'your-package/dist/internal/secret';

条件导出的执行顺序:

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",      // TypeScript 类型
      "node": "./dist/index.node.js",    // Node.js 环境
      "browser": "./dist/index.browser.js", // 浏览器环境
      "import": "./dist/index.mjs",      // ES Module
      "require": "./dist/index.cjs",     // CommonJS
      "default": "./dist/index.js"       // 兜底
    }
  }
}

优先级顺序(从高到低):

  1. exports
  2. module / browser(取决于环境)
  3. main

# 完整示例

一个支持多种环境的现代包配置:

{
  "name": "my-universal-package",
  "version": "1.0.0",
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "node": {
        "import": "./dist/index.mjs",
        "require": "./dist/index.cjs"
      },
      "browser": {
        "import": "./dist/browser.mjs",
        "require": "./dist/browser.cjs"
      },
      "default": "./dist/index.js"
    },
    "./utils": {
      "types": "./dist/utils.d.ts",
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.cjs"
    },
    "./style.css": "./dist/style.css",
    "./package.json": "./package.json"
  },
  "files": ["dist"]
}

# 2.4 类型定义配置

# types / typings

为 TypeScript 用户提供类型定义:

{
  "types": "dist/index.d.ts",
  "main": "dist/index.js"
}

或者发布到 @types 组织:

# 用户安装你的包和对应的类型定义
npm install your-package
npm install --save-dev @types/your-package

# 在 exports 中配置类型

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

# 2.5 可执行脚本配置

# bin - 命令行工具

如果你的包提供命令行工具,使用 bin 字段:

{
  "name": "my-cli-tool",
  "bin": {
    "mycli": "./bin/cli.js"
  }
}

或者包名和命令名相同时:

{
  "name": "my-cli-tool",
  "bin": "./bin/cli.js"
}

cli.js 文件格式:

#!/usr/bin/env node

console.log('Hello from CLI!');

必须#!/usr/bin/env node 开头,并确保文件有执行权限:

chmod +x bin/cli.js

用户全局安装后即可使用:

npm install -g my-cli-tool
mycli

本地安装时通过 npx 使用:

npm install my-cli-tool
npx mycli

# 2.6 脚本配置

# scripts

定义项目的脚本命令:

{
  "scripts": {
    "build": "rollup -c",
    "test": "jest",
    "lint": "eslint src",
    "format": "prettier --write \"src/**/*.js\"",
    "prepublishOnly": "npm run build && npm test"
  }
}

生命周期脚本:

npm 在特定时机会自动执行某些脚本:

{
  "scripts": {
    "preinstall": "echo 'Installing...'",
    "install": "node-gyp rebuild",
    "postinstall": "echo 'Installed!'",

    "prepublish": "npm run build",      // npm v7+ 已废弃
    "prepublishOnly": "npm run build",  // 推荐使用
    "prepack": "npm run build",
    "postpack": "echo 'Packed!'",

    "preversion": "npm test",
    "version": "npm run format && git add -A",
    "postversion": "git push && git push --tags"
  }
}

最佳实践脚本:

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",

    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage",

    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix",

    "format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
    "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",

    "type-check": "tsc --noEmit",

    "prepublishOnly": "npm run lint && npm run type-check && npm run test && npm run build",
    "release": "npm version patch && npm publish",
    "release:minor": "npm version minor && npm publish",
    "release:major": "npm version major && npm publish"
  }
}

# 2.7 依赖配置

# dependencies

生产环境依赖,用户安装你的包时会一起安装:

{
  "dependencies": {
    "lodash": "^4.17.21",
    "axios": "~1.6.0"
  }
}

版本范围说明:

  • ^1.2.3:兼容 1.x.x,即 >=1.2.3 <2.0.0
  • ~1.2.3:兼容 1.2.x,即 >=1.2.3 <1.3.0
  • 1.2.3:精确版本
  • *latest:最新版本
  • >1.2.3>=1.2.3<1.2.3<=1.2.3:范围
  • 1.2.3 - 1.5.0:范围

# devDependencies

开发环境依赖,不会安装给用户:

{
  "devDependencies": {
    "typescript": "^5.3.0",
    "jest": "^29.7.0",
    "eslint": "^8.56.0",
    "@types/node": "^20.10.0"
  }
}

# peerDependencies

声明宿主环境需要的依赖:

{
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  }
}

使用场景:

  • React 组件库需要 React
  • Vue 插件需要 Vue
  • Webpack 插件需要 Webpack

npm 7+ 行为变化:

  • npm 7+ 会自动安装 peerDependencies
  • npm 3-6 只会发出警告

# peerDependenciesMeta

标记可选的 peer dependencies:

{
  "peerDependencies": {
    "react": ">=16.8.0",
    "vue": ">=3.0.0"
  },
  "peerDependenciesMeta": {
    "vue": {
      "optional": true
    }
  }
}

表示包可以同时支持 React 和 Vue,但 Vue 是可选的。

# optionalDependencies

可选依赖,安装失败不影响包的使用:

{
  "optionalDependencies": {
    "fsevents": "^2.3.2"
  }
}

代码中需要做兼容处理:

let fsevents;
try {
  fsevents = require('fsevents');
} catch (err) {
  // 不存在时的降级方案
}

# bundledDependencies

打包依赖,发布时会包含在 tarball 中:

{
  "bundledDependencies": [
    "my-private-package"
  ]
}

# overrides(npm 8.3+)

强制覆盖间接依赖的版本:

{
  "overrides": {
    "lodash": "^4.17.21",
    "foo": {
      "bar": "1.0.0"
    }
  }
}

# 2.8 发布配置

# private

防止意外发布私有包:

{
  "private": true
}

设置为 true 后,npm publish 会失败。

# publishConfig

发布时的配置:

{
  "publishConfig": {
    "access": "public",
    "registry": "https://registry.npmjs.org/",
    "tag": "latest"
  }
}

常用配置:

  • accesspublicrestricted(作用域包默认)
  • registry:发布到的仓库地址
  • tag:发布标签(默认 latest

# 2.9 系统配置

# engines

指定运行环境的版本要求:

{
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=9.0.0"
  }
}

注意:这只是建议,不会强制执行。要强制执行,需要配置 .npmrc

engine-strict=true

# os

指定操作系统:

{
  "os": ["darwin", "linux"],  // 允许
  "os": ["!win32"]            // 禁止
}

# cpu

指定 CPU 架构:

{
  "cpu": ["x64", "arm64"],
  "cpu": ["!arm", "!mips"]
}

# 2.10 第三方工具配置

# sideEffects(Webpack)

标记是否有副作用,用于 Tree Shaking:

{
  "sideEffects": false
}

或指定有副作用的文件:

{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

# unpkg / jsdelivr

指定 CDN 入口:

{
  "unpkg": "dist/index.umd.js",
  "jsdelivr": "dist/index.umd.js"
}

用户可以通过 CDN 使用:

<script src="https://unpkg.com/your-package"></script>
<script src="https://cdn.jsdelivr.net/npm/your-package"></script>

# browserslist

指定浏览器兼容范围:

{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

# 三、不同类型包的最佳实践

# 3.1 纯 JavaScript 库

适用于通用工具库,如日期处理、字符串处理等。

项目结构:

my-js-library/
├── src/
│   ├── index.js
│   └── utils.js
├── dist/
│   ├── index.js        # CommonJS
│   ├── index.mjs       # ES Module
│   └── index.umd.js    # UMD (浏览器)
├── test/
│   └── index.test.js
├── .npmignore
├── package.json
└── README.md

package.json:

{
  "name": "my-js-library",
  "version": "1.0.0",
  "description": "A useful JavaScript library",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "unpkg": "dist/index.umd.js",
  "jsdelivr": "dist/index.umd.js",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "browser": "./dist/index.umd.js"
    }
  },
  "files": ["dist"],
  "scripts": {
    "build": "rollup -c",
    "test": "jest",
    "prepublishOnly": "npm test && npm run build"
  },
  "keywords": ["utility", "helper"],
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "rollup": "^4.0.0",
    "@rollup/plugin-node-resolve": "^15.0.0",
    "jest": "^29.0.0"
  }
}

rollup.config.js:

export default [
  {
    input: 'src/index.js',
    output: [
      { file: 'dist/index.js', format: 'cjs' },
      { file: 'dist/index.mjs', format: 'esm' },
      { file: 'dist/index.umd.js', format: 'umd', name: 'MyLibrary' }
    ]
  }
];

# 3.2 TypeScript 库

现代包推荐使用 TypeScript 开发,提供更好的类型支持。

项目结构:

my-ts-library/
├── src/
│   ├── index.ts
│   ├── types.ts
│   └── utils.ts
├── dist/
│   ├── index.js
│   ├── index.mjs
│   ├── index.d.ts
│   └── types.d.ts
├── test/
│   └── index.test.ts
├── tsconfig.json
├── package.json
└── README.md

package.json:

{
  "name": "my-ts-library",
  "version": "1.0.0",
  "description": "A TypeScript library",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    }
  },
  "files": ["dist"],
  "scripts": {
    "build": "npm run build:types && npm run build:js",
    "build:types": "tsc --emitDeclarationOnly",
    "build:js": "rollup -c",
    "test": "jest",
    "type-check": "tsc --noEmit",
    "prepublishOnly": "npm run type-check && npm test && npm run build"
  },
  "keywords": ["typescript", "library"],
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "typescript": "^5.3.0",
    "rollup": "^4.0.0",
    "@rollup/plugin-typescript": "^11.0.0",
    "jest": "^29.0.0",
    "@types/jest": "^29.0.0",
    "ts-jest": "^29.0.0"
  }
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020"],
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "test"]
}

rollup.config.js:

import typescript from '@rollup/plugin-typescript';

export default [
  {
    input: 'src/index.ts',
    output: [
      { file: 'dist/index.js', format: 'cjs' },
      { file: 'dist/index.mjs', format: 'esm' }
    ],
    plugins: [
      typescript({
        tsconfig: './tsconfig.json',
        declaration: false  // 类型声明由 tsc 单独生成
      })
    ]
  }
];

# 3.3 CLI 工具

命令行工具需要特殊的配置和结构。

项目结构:

my-cli-tool/
├── bin/
│   └── cli.js
├── src/
│   ├── commands/
│   │   ├── init.js
│   │   └── build.js
│   ├── utils/
│   │   └── logger.js
│   └── index.js
├── templates/
│   └── default/
├── package.json
└── README.md

package.json:

{
  "name": "my-cli-tool",
  "version": "1.0.0",
  "description": "A powerful CLI tool",
  "bin": {
    "mycli": "./bin/cli.js"
  },
  "files": ["bin", "src", "templates"],
  "scripts": {
    "test": "jest",
    "prepublishOnly": "npm test"
  },
  "keywords": ["cli", "tool"],
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "commander": "^11.0.0",
    "chalk": "^5.3.0",
    "inquirer": "^9.2.0",
    "ora": "^7.0.0"
  },
  "devDependencies": {
    "jest": "^29.0.0"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}

bin/cli.js:

#!/usr/bin/env node

const { program } = require('commander');
const chalk = require('chalk');
const pkg = require('../package.json');

program
  .name('mycli')
  .description('A powerful CLI tool')
  .version(pkg.version);

program
  .command('init <project-name>')
  .description('Initialize a new project')
  .option('-t, --template <template>', 'Template to use')
  .action((projectName, options) => {
    console.log(chalk.green(`Creating project: ${projectName}`));
    // 实现初始化逻辑
  });

program
  .command('build')
  .description('Build the project')
  .option('-w, --watch', 'Watch mode')
  .action((options) => {
    console.log(chalk.blue('Building...'));
    // 实现构建逻辑
  });

program.parse();

测试 CLI:

# 本地链接
npm link

# 测试命令
mycli --version
mycli init my-project
mycli build --watch

# 取消链接
npm unlink

# 3.4 React 组件库

项目结构:

my-react-components/
├── src/
│   ├── components/
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   ├── Button.module.css
│   │   │   └── index.ts
│   │   └── Input/
│   ├── styles/
│   │   └── global.css
│   └── index.ts
├── dist/
├── package.json
└── README.md

package.json:

{
  "name": "@myorg/react-components",
  "version": "1.0.0",
  "description": "React component library",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    },
    "./styles": "./dist/styles.css"
  },
  "files": ["dist"],
  "scripts": {
    "build": "vite build && tsc --emitDeclarationOnly",
    "dev": "vite",
    "prepublishOnly": "npm run build"
  },
  "peerDependencies": {
    "react": ">=18.0.0",
    "react-dom": ">=18.0.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^5.3.0",
    "vite": "^5.0.0",
    "vite-plugin-dts": "^3.0.0"
  },
  "keywords": ["react", "components", "ui"],
  "license": "MIT"
}

使用示例:

// 用户代码
import { Button, Input } from '@myorg/react-components';
import '@myorg/react-components/styles';

function App() {
  return (
    <div>
      <Button>Click me</Button>
      <Input placeholder="Enter text" />
    </div>
  );
}

# 3.5 Monorepo 包管理

使用 workspaces 管理多个相关包。

项目结构:

my-monorepo/
├── packages/
│   ├── core/
│   │   ├── src/
│   │   └── package.json
│   ├── utils/
│   │   ├── src/
│   │   └── package.json
│   └── cli/
│       ├── bin/
│       ├── src/
│       └── package.json
├── package.json
└── README.md

根 package.json:

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces",
    "test": "npm run test --workspaces",
    "publish:all": "npm publish --workspaces --access public"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "jest": "^29.0.0"
  }
}

packages/core/package.json:

{
  "name": "@myorg/core",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc && rollup -c"
  }
}

packages/cli/package.json:

{
  "name": "@myorg/cli",
  "version": "1.0.0",
  "bin": {
    "myorg": "./bin/cli.js"
  },
  "dependencies": {
    "@myorg/core": "^1.0.0",
    "@myorg/utils": "^1.0.0"
  }
}

使用 workspaces 命令:

# 安装所有依赖
npm install

# 为某个包添加依赖
npm install lodash --workspace=@myorg/core

# 运行所有包的脚本
npm run build --workspaces

# 发布所有包
npm publish --workspaces --access public

# 3.6 全栈包(Node + Browser)

同时支持 Node.js 和浏览器环境的包。

package.json:

{
  "name": "my-universal-package",
  "version": "1.0.0",
  "description": "Works in Node.js and Browser",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "browser": "dist/browser.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "node": {
        "import": "./dist/node.mjs",
        "require": "./dist/node.js"
      },
      "browser": {
        "import": "./dist/browser.mjs",
        "require": "./dist/browser.js"
      },
      "default": "./dist/index.js"
    }
  },
  "files": ["dist"],
  "sideEffects": false
}

src/index.ts:

// 平台检测
export const isNode = typeof process !== 'undefined'
  && process.versions != null
  && process.versions.node != null;

export const isBrowser = typeof window !== 'undefined'
  && typeof window.document !== 'undefined';

// 条件导出
export function getStorage() {
  if (isNode) {
    return require('./storage.node');
  }
  return require('./storage.browser');
}

# 四、包的维护与管理

# 4.1 版本更新策略

# 语义化版本的实践

根据改动类型选择正确的版本号:

Patch 版本(1.0.0 → 1.0.1)

  • 修复 bug
  • 性能优化
  • 文档更新
  • 内部重构(不影响 API)
npm version patch

Minor 版本(1.0.0 → 1.1.0)

  • 新增功能
  • 标记某功能为废弃(但保留)
  • 内部大量重构
npm version minor

Major 版本(1.0.0 → 2.0.0)

  • 删除已有 API
  • 修改已有 API 的行为
  • 需要用户修改代码的改动
npm version major

# 预发布版本策略

# 第一个 alpha 版本
npm version premajor --preid=alpha  # 1.0.0 -> 2.0.0-alpha.0
npm version preminor --preid=alpha  # 1.0.0 -> 1.1.0-alpha.0
npm version prepatch --preid=alpha  # 1.0.0 -> 1.0.1-alpha.0

# 增加 prerelease 版本号
npm version prerelease  # 1.0.0-alpha.0 -> 1.0.0-alpha.1

# 发布 beta 版本
npm version prerelease --preid=beta  # 1.0.0-alpha.1 -> 1.0.0-beta.0

# 发布 rc (release candidate)
npm version prerelease --preid=rc    # 1.0.0-beta.0 -> 1.0.0-rc.0

# 发布正式版本
npm version patch  # 1.0.0-rc.0 -> 1.0.0

发布预发布版本到特定标签:

npm publish --tag alpha
npm publish --tag beta
npm publish --tag next

用户安装:

npm install my-package@alpha
npm install my-package@beta
npm install my-package@next

查看所有发布的版本:

npm view my-package versions
npm view my-package dist-tags

# 废弃旧版本

# 废弃某个版本
npm deprecate my-package@1.0.0 "This version has security issues, please upgrade to 1.0.1"

# 废弃某个范围的版本
npm deprecate my-package@"< 1.0.0" "Please upgrade to the latest version"

# 取消废弃
npm deprecate my-package@1.0.0 ""

# 4.2 npm scripts 最佳实践

# 命名约定

{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js",

    "build": "tsc && vite build",
    "build:dev": "vite build --mode development",
    "build:prod": "vite build --mode production",

    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "test:e2e": "playwright test",

    "lint": "eslint src",
    "lint:fix": "eslint src --fix",

    "format": "prettier --write \"src/**/*.ts\"",
    "format:check": "prettier --check \"src/**/*.ts\"",

    "type-check": "tsc --noEmit",

    "clean": "rm -rf dist",
    "clean:all": "rm -rf dist node_modules"
  }
}

# 组合脚本

使用 &&||& 组合命令:

{
  "scripts": {
    "prebuild": "npm run clean",
    "build": "npm run build:types && npm run build:js",
    "build:types": "tsc --emitDeclarationOnly",
    "build:js": "rollup -c",

    "validate": "npm run lint && npm run type-check && npm run test",

    "dev:all": "npm run dev:server & npm run dev:client",

    "prepare": "npm run build || echo 'Build failed, using existing build'"
  }
}

# 使用 npm-run-all

安装:

npm install --save-dev npm-run-all
{
  "scripts": {
    "clean": "rm -rf dist",
    "build:types": "tsc --emitDeclarationOnly",
    "build:js": "rollup -c",
    "build": "npm-run-all clean --parallel build:*",

    "lint:js": "eslint src",
    "lint:css": "stylelint src",
    "lint": "npm-run-all --parallel lint:*",

    "test:unit": "jest",
    "test:e2e": "playwright test",
    "test": "npm-run-all --serial test:*"
  }
}

# 环境变量

{
  "scripts": {
    "build": "NODE_ENV=production webpack",
    "dev": "NODE_ENV=development webpack-dev-server"
  }
}

跨平台支持,使用 cross-env

npm install --save-dev cross-env
{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack",
    "dev": "cross-env NODE_ENV=development webpack-dev-server"
  }
}

# 4.3 发布前检查清单

# 自动化检查脚本

{
  "scripts": {
    "prepublishOnly": "npm run check",
    "check": "npm-run-all --serial check:*",
    "check:git": "node scripts/check-git.js",
    "check:lint": "npm run lint",
    "check:type": "npm run type-check",
    "check:test": "npm run test",
    "check:build": "npm run build",
    "check:size": "size-limit"
  }
}

scripts/check-git.js:

const { execSync } = require('child_process');

// 检查是否有未提交的改动
try {
  const status = execSync('git status --porcelain').toString();
  if (status) {
    console.error('❌ Git working directory is not clean');
    console.error('Please commit or stash your changes before publishing');
    process.exit(1);
  }
  console.log('✓ Git working directory is clean');
} catch (error) {
  console.error('❌ Git check failed');
  process.exit(1);
}

// 检查是否在主分支
try {
  const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
  if (branch !== 'main' && branch !== 'master') {
    console.warn(`⚠️  Warning: Publishing from branch "${branch}"`);
  } else {
    console.log(`✓ On ${branch} branch`);
  }
} catch (error) {
  console.error('❌ Branch check failed');
  process.exit(1);
}

# 包大小检查

使用 size-limit (opens new window)

npm install --save-dev size-limit @size-limit/preset-small-lib

package.json:

{
  "size-limit": [
    {
      "path": "dist/index.js",
      "limit": "10 KB"
    },
    {
      "path": "dist/index.mjs",
      "limit": "10 KB"
    }
  ]
}

# 发布检查清单

创建 PUBLISH_CHECKLIST.md

# 发布检查清单

## 发布前
- [ ] 所有测试通过
- [ ] 文档已更新
- [ ] CHANGELOG.md 已更新
- [ ] package.json 版本号已更新
- [ ] 所有改动已提交到 git
- [ ] 在主分支上

## 发布
- [ ] `npm run build` 成功
- [ ] `npm pack` 检查发布文件
- [ ] `npm publish --dry-run` 测试
- [ ] `npm publish` 正式发布

## 发布后
- [ ] 推送代码到远程仓库
- [ ] 推送 git tag
- [ ] 创建 GitHub Release
- [ ] 更新文档网站
- [ ] 发布公告

# 4.4 维护 CHANGELOG

使用 conventional-changelog (opens new window)

npm install --save-dev conventional-changelog-cli
{
  "scripts": {
    "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
  }
}

CHANGELOG.md 示例:

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2025-01-15

### Added
- New `formatDate` function for date formatting
- Support for custom locales

### Changed
- Improved performance of `parseDate` by 20%

### Deprecated
- `oldFormat` function will be removed in v2.0.0

### Fixed
- Fixed timezone handling in `convertDate`

## [1.1.0] - 2024-12-01

### Added
- Initial release

# 4.5 自动化发布

# 使用 GitHub Actions

.github/workflows/publish.yml:

name: Publish to NPM

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - run: npm ci

      - run: npm test

      - run: npm run build

      - run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: See CHANGELOG.md for details

发布流程:

# 1. 更新版本并创建 tag
npm version patch  # 或 minor/major

# 2. 推送代码和 tag
git push && git push --tags

# GitHub Actions 会自动发布

# 使用 np

np (opens new window) 是一个更好的 npm publish 工具:

npm install --global np
{
  "scripts": {
    "release": "np"
  }
}

运行:

npm run release

np 会自动:

  1. 运行测试
  2. 检查 git 状态
  3. 更新版本号
  4. 发布到 npm
  5. 推送到 git
  6. 创建 GitHub Release

# 4.6 包的废弃与删除

# 废弃包

# 废弃整个包
npm deprecate my-package "This package is deprecated. Please use new-package instead."

# 删除包

注意:只能删除发布后 72 小时内的包!

# 删除特定版本
npm unpublish my-package@1.0.0

# 删除整个包(慎用!)
npm unpublish my-package --force

最佳实践

  • 不要删除已被广泛使用的包
  • 优先使用 deprecate 而不是 unpublish
  • 如果必须删除,提前通知用户

# 五、npm 配置与工具

# 5.1 .npmrc 配置

项目级配置文件 .npmrc

# 设置源
registry=https://registry.npmjs.org/

# 作用域包使用不同的源
@myorg:registry=https://npm.myorg.com/

# 保存依赖时使用精确版本
save-exact=true

# 使用 package-lock.json
package-lock=true

# 引擎严格模式
engine-strict=true

# 自动安装 peer dependencies (npm 7+)
legacy-peer-deps=false

# 5.2 镜像源管理

使用 nrm (opens new window)

# 安装
npm install -g nrm

# 查看可用源
nrm ls

# 切换源
nrm use taobao
nrm use npm

# 测试速度
nrm test

# 添加自定义源
nrm add myorg https://npm.myorg.com/

或使用 npm 命令:

# 查看当前源
npm config get registry

# 设置源
npm config set registry https://registry.npmmirror.com

# 恢复官方源
npm config set registry https://registry.npmjs.org

# 5.3 私有 npm 仓库

# Verdaccio(推荐)

Verdaccio (opens new window) 是轻量级的私有 npm 仓库:

# 安装
npm install -g verdaccio

# 启动
verdaccio

配置 ~/.config/verdaccio/config.yaml

storage: ./storage
auth:
  htpasswd:
    file: ./htpasswd
uplinks:
  npmjs:
    url: https://registry.npmjs.org/
packages:
  '@myorg/*':
    access: $authenticated
    publish: $authenticated
  '**':
    access: $all
    publish: $authenticated
    proxy: npmjs

使用:

# 添加用户
npm adduser --registry http://localhost:4873

# 发布
npm publish --registry http://localhost:4873

# 安装
npm install @myorg/my-package --registry http://localhost:4873

或配置 .npmrc

@myorg:registry=http://localhost:4873

# GitHub Packages

发布到 GitHub Packages:

# 登录
npm login --registry=https://npm.pkg.github.com

# 发布
npm publish --registry=https://npm.pkg.github.com

package.json:

{
  "name": "@username/my-package",
  "publishConfig": {
    "registry": "https://npm.pkg.github.com"
  }
}

# 5.4 依赖安全

# 检查漏洞

# 检查依赖中的安全漏洞
npm audit

# 自动修复
npm audit fix

# 查看详细报告
npm audit --json

# 使用 Snyk

Snyk (opens new window) 提供更全面的安全检查:

# 安装
npm install -g snyk

# 认证
snyk auth

# 测试
snyk test

# 持续监控
snyk monitor

# 六、最佳实践总结

# 6.1 项目初始化

# 使用 npm init 初始化新项目
npm init

# 或使用脚手架
npm create vite@latest
npm create next-app@latest

# 6.2 配置管理

  • ✅ 统一团队 npm 配置:固化到 .npmrc 文件中
  • ✅ 统一运行环境:在 package.json 中配置 engines
  • ✅ 提交 package-lock.json:保证依赖版本一致

# 6.3 依赖管理

  • ✅ 使用语义化版本:^ 用于次版本更新,~ 用于补丁更新
  • ✅ 区分依赖类型:正确使用 dependencies、devDependencies、peerDependencies
  • ✅ 定期更新依赖:使用 npm outdated 检查,npm update 更新
  • ✅ 检查安全漏洞:定期运行 npm audit

# 6.4 脚本管理

  • ✅ 使用 npm scripts:管理应用相关脚本
  • ✅ 使用 npx:运行本地安装的命令(npm >= 5.2)
  • ✅ 命名规范:使用 : 分隔命名空间,如 build:devtest:watch

# 6.5 发布管理

  • ✅ 语义化版本:严格遵循 semver 规范
  • ✅ 发布前检查:lint、test、build 全部通过
  • ✅ 控制发布文件:使用 files 字段明确指定
  • ✅ 使用 prepublishOnly:自动化发布前检查
  • ✅ 维护 CHANGELOG:记录每个版本的改动

# 6.6 文档

  • ✅ 完善的 README:安装、使用、示例、API 文档
  • ✅ TypeScript 类型定义:提供 .d.ts 文件
  • ✅ 示例代码:提供可运行的示例
  • ✅ 变更日志:维护 CHANGELOG.md

# 6.7 质量保证

  • ✅ 单元测试:覆盖核心功能
  • ✅ 类型检查:使用 TypeScript 或 JSDoc
  • ✅ 代码规范:使用 ESLint、Prettier
  • ✅ 持续集成:使用 GitHub Actions 自动化测试和发布

# 七、附录:package.json 字段完全解读

# 7.1 描述信息

# name - 包名

项目的名称,如果要发布包,这是必需字段。

规则:

  • 长度:≤ 214 字符
  • 命名:
    • 必须全部小写
    • 可以包含连字符 - 和下划线 _
    • 不能以 ._ 开头
    • 不能包含大写字母
    • 不能包含非 URL 安全字符
  • 不能与 Node.js 核心模块重名
  • 在 npm registry 中必须唯一

作用域包:

{
  "name": "@myorg/my-package"
}

# version - 版本号

遵循 Semantic Versioning (opens new window) 规范,格式为 X.Y.Z

  • X(Major):主版本号,不兼容的 API 修改
  • Y(Minor):次版本号,向下兼容的功能新增
  • Z(Patch):修订号,向下兼容的问题修正

预发布版本:

  • 1.0.0-alpha.0:alpha 版本
  • 1.0.0-beta.0:beta 版本
  • 1.0.0-rc.0:release candidate
{
  "version": "1.2.3"
}

# description - 描述

简短描述包的功能,显示在 npm 搜索结果中:

{
  "description": "A utility library for date formatting"
}

# keywords - 关键词

关键词数组,帮助用户在 npm 上找到你的包:

{
  "keywords": [
    "date",
    "time",
    "format",
    "utility"
  ]
}

# homepage - 主页

项目主页 URL:

{
  "homepage": "https://github.com/username/project#readme"
}

# repository - 代码仓库

指定代码仓库的位置:

{
  "repository": {
    "type": "git",
    "url": "https://github.com/username/project.git"
  }
}

简写形式:

{
  "repository": "github:username/project"
}

# bugs - 问题追踪

用户提交 bug 的地方:

{
  "bugs": {
    "url": "https://github.com/username/project/issues",
    "email": "bugs@example.com"
  }
}

或简写:

{
  "bugs": "https://github.com/username/project/issues"
}

# license - 许可证

指定包的许可证:

{
  "license": "MIT"
}

多个许可证:

{
  "license": "(MIT OR Apache-2.0)"
}

常见许可证:

  • MIT:宽松许可证
  • Apache-2.0:Apache 许可证
  • GPL-3.0:GNU 许可证
  • ISC:类似 MIT
  • BSD-3-Clause:BSD 许可证

# author - 作者

包的作者信息:

{
  "author": "Your Name <you@example.com> (https://yoursite.com)"
}

或对象形式:

{
  "author": {
    "name": "Your Name",
    "email": "you@example.com",
    "url": "https://yoursite.com"
  }
}

# contributors - 贡献者

贡献者列表:

{
  "contributors": [
    "Name1 <email1@example.com>",
    "Name2 <email2@example.com>"
  ]
}

# funding - 资助

资助信息,显示在 npm install 后:

{
  "funding": {
    "type": "github",
    "url": "https://github.com/sponsors/username"
  }
}

多个资助渠道:

{
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/username"
    },
    {
      "type": "patreon",
      "url": "https://patreon.com/username"
    }
  ]
}

# 7.2 文件配置

# files - 发布文件

指定发布到 npm 的文件列表(白名单):

{
  "files": [
    "dist",
    "lib",
    "bin",
    "README.md",
    "LICENSE"
  ]
}

始终包含的文件:

  • package.json
  • README.md / README
  • LICENSE / LICENCE
  • main 字段指定的文件

始终忽略的文件:

  • .git
  • node_modules
  • .npmrc
  • package-lock.json(可用 publishConfig.packages 包含)

# main - 主入口

CommonJS 格式的主入口文件:

{
  "main": "dist/index.js"
}

用户使用 require('your-package') 时加载此文件。

# module - ES Module 入口

ES Module 格式的入口文件:

{
  "module": "dist/index.esm.js"
}

打包工具会优先使用此字段进行 Tree Shaking。

# browser - 浏览器入口

浏览器环境的入口文件:

{
  "browser": "dist/browser.js"
}

或文件映射:

{
  "browser": {
    "./server.js": "./browser.js",
    "fs": false
  }
}

# exports - 现代入口配置

最现代的入口配置方式,优先级最高:

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "default": "./dist/index.js"
    },
    "./utils": "./dist/utils.js",
    "./package.json": "./package.json"
  }
}

特性:

  • 支持条件导出
  • 支持子路径导出
  • 封装性:未定义的路径无法访问

# type - 模块类型

指定 .js 文件的模块类型:

{
  "type": "module"
}
  • "module".js 文件为 ES Module
  • "commonjs".js 文件为 CommonJS(默认)

注意:

  • 设置为 "module" 后,需要用 .cjs 扩展名表示 CommonJS
  • 设置为 "commonjs" 后,需要用 .mjs 扩展名表示 ES Module

# types / typings - 类型定义

TypeScript 类型定义文件:

{
  "types": "dist/index.d.ts"
}

或:

{
  "typings": "dist/index.d.ts"
}

# man - 手册页

Linux/Unix man 命令的手册页:

{
  "man": "./man/doc.1"
}

或多个:

{
  "man": [
    "./man/doc.1",
    "./man/doc.2"
  ]
}

# directories - 目录结构

描述包的目录结构(较少使用):

{
  "directories": {
    "lib": "src",
    "bin": "bin",
    "man": "man",
    "doc": "docs",
    "example": "examples",
    "test": "test"
  }
}

# 7.3 脚本配置

# scripts - 脚本命令

定义可运行的脚本命令:

{
  "scripts": {
    "start": "node server.js",
    "dev": "vite",
    "build": "vite build",
    "test": "jest",
    "lint": "eslint src"
  }
}

生命周期钩子:

钩子 执行时机
preinstall install 之前
install / postinstall install 之后
prepack npm pack 之前
postpack npm pack 之后
prepublish npm publish 之前(已废弃)
prepublishOnly npm publish 之前(推荐)
preversion 修改版本号之前
version 修改版本号之后,提交之前
postversion 提交之后

自定义生命周期:

  • pre<script>:在脚本之前执行
  • post<script>:在脚本之后执行
{
  "scripts": {
    "prebuild": "rm -rf dist",
    "build": "tsc",
    "postbuild": "echo 'Build complete!'"
  }
}

# config - 配置参数

为脚本提供配置参数:

{
  "config": {
    "port": "8080"
  },
  "scripts": {
    "start": "node server.js $npm_package_config_port"
  }
}

在代码中访问:

console.log(process.env.npm_package_config_port); // "8080"

用户可以覆盖:

npm config set my-package:port 3000

# bin - 可执行文件

指定可执行脚本:

{
  "bin": {
    "mycli": "./bin/cli.js"
  }
}

包名和命令名相同时可简写:

{
  "name": "mycli",
  "bin": "./bin/cli.js"
}

注意:

  • 脚本文件必须以 #!/usr/bin/env node 开头
  • 文件需要有执行权限

# 7.4 依赖配置

# dependencies - 生产依赖

生产环境需要的依赖:

{
  "dependencies": {
    "react": "^18.2.0",
    "axios": "~1.6.0",
    "lodash": "4.17.21"
  }
}

版本规范:

  • ^1.2.3:兼容 1.x.x(>=1.2.3 <2.0.0
  • ~1.2.3:兼容 1.2.x(>=1.2.3 <1.3.0
  • 1.2.3:精确版本
  • >1.2.3>=1.2.3<1.2.3<=1.2.3:比较
  • 1.2.3 - 2.3.4:范围(等同于 >=1.2.3 <=2.3.4
  • *x:任意版本
  • latest:最新版本
  • http://...:URL
  • git://...:Git URL
  • github:user/repo:GitHub 简写
  • file:../path:本地路径

# devDependencies - 开发依赖

开发环境需要的依赖:

{
  "devDependencies": {
    "typescript": "^5.3.0",
    "eslint": "^8.56.0",
    "jest": "^29.7.0",
    "@types/node": "^20.10.0"
  }
}

安装方式:

npm install --save-dev typescript
# 或
npm install -D typescript

# peerDependencies - 对等依赖

声明包需要的宿主环境依赖:

{
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  }
}

使用场景:

  • 插件依赖宿主应用
  • 库依赖特定框架版本

npm 版本行为差异:

  • npm 1-2:自动安装 peerDependencies
  • npm 3-6:只警告,不自动安装
  • npm 7+:自动安装 peerDependencies

# peerDependenciesMeta - 对等依赖元数据

标记可选的 peer dependencies:

{
  "peerDependencies": {
    "react": ">=16.8.0",
    "vue": ">=3.0.0"
  },
  "peerDependenciesMeta": {
    "vue": {
      "optional": true
    }
  }
}

表示 Vue 是可选的对等依赖。

# optionalDependencies - 可选依赖

可选依赖,安装失败不影响包的使用:

{
  "optionalDependencies": {
    "fsevents": "^2.3.2"
  }
}

注意:

  • 会覆盖 dependencies 中的同名依赖
  • 代码中需要做降级处理
try {
  const fsevents = require('fsevents');
} catch (err) {
  // 降级方案
}

# bundledDependencies / bundleDependencies - 打包依赖

发布时打包进 tarball 的依赖:

{
  "bundledDependencies": [
    "private-package",
    "custom-build"
  ]
}

使用场景:

  • 私有包
  • 需要特殊构建的包
  • 离线安装

# overrides - 依赖覆盖(npm 8.3+)

强制覆盖依赖树中的版本:

{
  "overrides": {
    "lodash": "^4.17.21",
    "axios": {
      ".": "1.6.0",
      "follow-redirects": "^1.15.4"
    }
  }
}

类似功能:

  • Yarn:resolutions
  • pnpm:pnpm.overrides

# 7.5 发布配置

# private - 私有包

防止意外发布:

{
  "private": true
}

设置后 npm publish 会失败。

# publishConfig - 发布配置

发布时使用的配置:

{
  "publishConfig": {
    "access": "public",
    "registry": "https://registry.npmjs.org/",
    "tag": "latest"
  }
}

常用字段:

  • accesspublicrestricted
  • registry:发布到的 registry
  • tag:发布标签
  • directory:发布的目录

# 7.6 系统配置

# engines - 运行环境

指定运行环境的版本要求:

{
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=9.0.0",
    "yarn": ">=1.22.0"
  }
}

注意:

  • 默认只是警告
  • 要强制执行,需要设置 .npmrc
    engine-strict=true
    

# os - 操作系统

指定支持的操作系统:

{
  "os": ["darwin", "linux"]
}

黑名单:

{
  "os": ["!win32"]
}

# cpu - CPU 架构

指定支持的 CPU 架构:

{
  "cpu": ["x64", "arm64"]
}

黑名单:

{
  "cpu": ["!arm", "!mips"]
}

# 7.7 工作空间

# workspaces - Monorepo

定义工作空间,用于 monorepo 管理:

{
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

或排除某些目录:

{
  "workspaces": {
    "packages": [
      "packages/*"
    ],
    "nohoist": [
      "**/react-native",
      "**/react-native/**"
    ]
  }
}

特性:

  • 共享 node_modules
  • 统一安装依赖
  • 支持工作空间间的相互引用
  • 批量执行脚本

使用示例:

# 为特定工作空间添加依赖
npm install lodash --workspace=packages/utils

# 在所有工作空间运行脚本
npm run build --workspaces

# 发布所有工作空间
npm publish --workspaces --access public

# 7.8 第三方配置

这些配置用于特定工具,不是 npm 官方字段。

# sideEffects - 副作用(Webpack)

标记模块是否有副作用,用于 Tree Shaking:

{
  "sideEffects": false
}

标记特定文件有副作用:

{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

# unpkg / jsdelivr - CDN

指定 CDN 入口文件:

{
  "unpkg": "dist/index.umd.js",
  "jsdelivr": "dist/index.umd.js"
}

用户可以通过 CDN 访问:

<script src="https://unpkg.com/your-package"></script>
<script src="https://cdn.jsdelivr.net/npm/your-package"></script>

# browserslist - 浏览器兼容

指定浏览器兼容范围(用于 Babel、Autoprefixer 等):

{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead",
    "not ie 11"
  ]
}

或分环境:

{
  "browserslist": {
    "production": [
      "> 1%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

# lint-staged - Git Hooks

配合 husky 使用,对暂存文件执行命令:

{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }
}

# eslintConfig - ESLint

内联 ESLint 配置(不推荐,建议使用独立文件):

{
  "eslintConfig": {
    "extends": ["eslint:recommended"],
    "env": {
      "node": true,
      "es6": true
    }
  }
}

# prettier - Prettier

内联 Prettier 配置(不推荐,建议使用独立文件):

{
  "prettier": {
    "semi": false,
    "singleQuote": true,
    "tabWidth": 2
  }
}

# jest - Jest

内联 Jest 配置(不推荐,建议使用独立文件):

{
  "jest": {
    "testEnvironment": "node",
    "coverageDirectory": "coverage"
  }
}

# 参考资料

# 官方文档

# 深入学习

# 工具与生态


# 总结

本文系统地介绍了 npm 包的发布与管理,从快速入门到高级实践,涵盖了:

  1. 快速开始:最小化配置,发布第一个包
  2. 进阶配置:版本管理、文件管理、入口配置、依赖管理
  3. 最佳实践:不同类型包的完整案例
  4. 维护管理:版本更新、自动化发布、质量保证
  5. 完整参考:package.json 所有字段详解

希望这份指南能够帮助你更好地理解和使用 npm,创建高质量的 npm 包!

如果你觉得本文有帮助,欢迎分享给更多的开发者。如有疑问或建议,欢迎在评论区讨论。

上次更新: 2025/12/12 03:32:11