Node.js 函数打包为 npm 包的规范流程
Node.js 函数打包为 npm 包的规范流程
概述
本教程提供从零开始创建和发布 npm 包的标准化流程,遵循最佳实践和行业规范。
第一步:项目初始化
1. 创建项目目录结构
1 2
| mkdir my-package cd my-package
|
2. 初始化 package.json
3. 标准项目结构
1 2 3 4 5 6 7 8 9 10 11
| my-package/ ├── src/ # 源代码目录 │ └── index.ts # 主入口文件 ├── static/ # 静态资源 ├── test/ # 测试文件 ├── package.json # 包配置 ├── tsconfig.json # TypeScript 配置 ├── .gitignore # Git 忽略规则 ├── .npmignore # NPM 发布忽略规则 ├── README.md # 文档 └── LICENSE # 许可证
|
第二步:开发环境配置
1. 安装开发依赖
方案一:使用 TypeScript 编译器 (tsc)
1 2 3
| npm install -D typescript @types/node npm install -D rimraf npm install -D jest @types/jest
|
方案二:使用 tsup 构建工具(推荐)
1 2 3 4
| npm install -D tsup npm install -D @types/node npm install -D rimraf npm install -D jest @types/jest
|
tsup 优势:
- 零配置,开箱即用
- 支持 ES Modules 和 CommonJS 双格式输出
- 自动生成类型声明文件
- 更快的构建速度
- 更好的 Tree Shaking
2. 配置 TypeScript (tsconfig.json)
使用 tsc 的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "compilerOptions": { "target": "ES2020", "module": "CommonJS", "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "declaration": true, "declarationMap": true, "sourceMap": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "test"] }
|
使用 tsup 的简化配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| { "compilerOptions": { "target": "ES2020", "lib": ["ES2020"], "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "test"] }
|
3. 配置构建脚本 (package.json)
使用 tsc 的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsc", "dev": "tsc -w", "test": "jest", "prepublishOnly": "npm run build", "lint": "eslint src/**/*.ts" }, "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ "dist", "static", "README.md", "LICENSE" ] }
|
使用 tsup 的配置(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| { "scripts": { "clean": "rimraf dist", "build": "npm run clean && tsup", "dev": "tsup --watch", "test": "jest", "prepublishOnly": "npm run build", "lint": "eslint src/**/*.ts" }, "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.js", "types": "./dist/index.d.ts" } }, "files": [ "dist", "static", "README.md", "LICENSE" ] }
|
4. 配置 tsup (tsup.config.ts 或 package.json)
方式一:在 package.json 中配置
1 2 3 4 5 6 7 8 9 10 11
| { "tsup": { "entry": ["src/index.ts"], "format": ["cjs", "esm"], "dts": true, "clean": true, "sourcemap": true, "splitting": false, "minify": false } }
|
方式二:创建 tsup.config.ts 文件
1 2 3 4 5 6 7 8 9 10 11
| import { defineConfig } from 'tsup'
export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], dts: true, clean: true, sourcemap: true, splitting: false, minify: false })
|
方式三:命令行参数(package.json scripts)
1 2 3 4 5
| { "scripts": { "build": "tsup src/index.ts --format cjs,esm --dts --clean --sourcemap" } }
|
第三步:代码开发规范
1. 源代码编写规范
src/index.ts - 主入口文件示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
export class MyLibrary { private config: any;
constructor(config?: any) { this.config = config || {}; }
serveExpress() { return (req: any, res: any, next: any) => { res.json({ message: 'Hello from Express' }); }; }
serveKoa() { return async (ctx: any, next: any) => { ctx.body = { message: 'Hello from Koa' }; await next(); }; } }
export default MyLibrary;
if (typeof module !== 'undefined' && module.exports) { module.exports = MyLibrary; }
|
2. 类型定义规范
创建类型定义文件 src/types/index.ts:
1 2 3 4 5 6 7 8 9 10
| export interface Config { prefix?: string; debug?: boolean; }
export interface ApiResponse { success: boolean; data?: any; error?: string; }
|
第四步:构建和测试
1. 构建流程
使用 tsc 的构建流程
1 2 3 4 5 6 7 8
| npm run build
npm run dev
npm test
|
使用 tsup 的构建流程(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| npm run build
npm run dev
npm test
npx tsup src/index.ts --format cjs --dts
npx tsup src/index.ts --format esm --dts
npx tsup src/index.ts --format cjs,esm --dts --minify
|
2. 验证构建结果
tsc 构建结果
构建完成后检查 dist 目录:
1 2 3 4 5 6
| dist/ ├── index.js # 编译后的 JavaScript ├── index.d.ts # TypeScript 类型声明 ├── index.js.map # 源码映射文件 └── types/ └── index.d.ts # 类型定义
|
tsup 构建结果
构建完成后检查 dist 目录:
1 2 3 4 5 6 7
| dist/ ├── index.js # CommonJS 格式输出 ├── index.mjs # ES Modules 格式输出 ├── index.d.ts # TypeScript 类型声明 ├── index.d.mts # ES Modules 类型声明 ├── index.js.map # CommonJS 源码映射 └── index.mjs.map # ES Modules 源码映射
|
3. tsup 构建配置详解
常用配置选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| npx tsup src/index.ts
npx tsup src/index.ts --format cjs,esm
npx tsup src/index.ts --dts
npx tsup src/index.ts --clean
npx tsup src/index.ts --sourcemap
npx tsup src/index.ts --splitting
npx tsup src/index.ts --minify
npx tsup src/index.ts --external react,vue
npx tsup src/index.ts --platform node npx tsup src/index.ts --platform browser
npx tsup src/index.ts --target node16 npx tsup src/index.ts --target es2020
|
高级配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| npx tsup src/index.ts src/cli.ts --format cjs,esm --dts
npx tsup src/index.ts --out-dir build
npx tsup src/index.ts --env.NODE_ENV production
npx tsup src/index.ts --watch
npx tsup src/index.ts --format cjs --platform node --target node16
npx tsup src/index.ts --format esm --platform browser --target es2020
|
3. 本地包开发和测试(npm link)
npm link 的作用
- 在本地开发和测试包,无需发布到 npm
- 模拟真实的包安装和使用场景
- 支持实时修改和测试
在包项目中创建全局链接
在测试项目中使用链接的包
1 2 3 4 5 6 7 8 9 10
| mkdir test-project cd test-project npm init -y
npm link your-package-name
npm link /path/to/your/package
|
验证链接是否成功
1 2 3 4 5 6 7 8
| npm ls your-package-name
npm ls -g --link
node -e "console.log(require('your-package-name'))"
|
实时开发和测试
取消链接
1 2 3 4 5 6 7 8
| npm unlink your-package-name
npm uninstall your-package-name
npm unlink
|
npm link 的高级用法
多个包之间的链接
1 2 3 4 5 6 7 8 9 10 11
|
cd package-a npm link
cd package-b npm link package-a
|
使用相对路径链接
1 2
| npm link ../my-other-package
|
查看所有全局链接
1 2 3 4 5
| npm ls -g --depth=0 --link
npm ls -g your-package-name
|
npm link 的常见问题解决
链接不生效
1 2 3 4 5 6 7
| npm ls your-package-name
npm unlink your-package-name && npm link your-package-name
|
权限问题
1 2 3 4 5
| sudo npm link
sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}
|
Windows 系统注意事项
1 2 3 4 5 6 7
|
npm config get prefix
|
npm link 的最佳实践
开发流程
1 2 3 4 5 6 7
| npm run dev
npm link your-package
|
版本管理
1 2 3 4 5
| npm unlink
npm install your-package@latest
|
团队协作
1 2
| npm link ../team-member-package
|
4. 测试配置(可选)
package.json 添加测试配置:
1 2 3 4 5 6 7
| { "scripts": { "test": "jest", "test:watch": "jest --watch", "test:link": "npm run build && cd test-project && npm test" } }
|
jest.config.js:
1 2 3 4 5
| module.exports = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/test/**/*.test.ts'] };
|
第五步:发布前配置
1. 忽略文件配置
.gitignore - Git 版本控制忽略规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # 依赖目录 node_modules/
# 构建输出 dist/ build/
# 日志文件 *.log npm-debug.log*
# 环境变量 .env .env.local .env.production
# 系统文件 .DS_Store Thumbs.db
# IDE 配置 .vscode/ .idea/
|
.npmignore - NPM 发布忽略规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| # 源代码目录 src/
# 配置文件 tsconfig.json jest.config.js .eslintrc.js
# 开发依赖 node_modules/
# 测试文件 test/ *.test.ts *.spec.ts
# Git 相关 .git/ .gitignore
# 开发工具配置 .eslintignore .prettierrc
# 临时文件 *.tmp *.temp
|
2. 文档和许可证
README.md - 项目文档模板:
1 2 3 4 5 6 7
| # My Package
功能描述和使用说明
## 安装 ```bash npm install my-package
|
使用
1 2 3
| import MyLibrary from 'my-package';
const lib = new MyLibrary();
|
API 文档
serveExpress() - Express 中间件
serveKoa() - Koa 中间件
许可证
MIT
1
| **LICENSE** - MIT 许可证模板:
|
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted…
1 2 3 4 5 6 7 8 9 10 11
| ## 第六步:发布流程
### 1. 版本管理 ```bash # 查看当前版本 npm version
# 升级版本(遵循语义化版本) npm version patch # 修复 bug npm version minor # 新增功能 npm version major # 不兼容变更
|
2. 发布前检查
1 2 3 4 5 6 7 8
| npm run build
npm test
npm pack
|
3. 发布到 NPM
1 2 3 4 5 6 7 8 9 10 11
| npm login
npm publish
npm publish --tag beta
npm unpublish my-package@1.0.0
|
4. 发布后验证
1 2 3 4 5 6 7 8
| mkdir test-project cd test-project npm init -y npm install my-package
node -e "console.log(require('my-package'))"
|
第七步:配置参数详解
package.json 关键字段说明
| 字段 |
作用 |
配置时机 |
示例值 |
name |
包名,全局唯一 |
项目初始化 |
"my-package" |
version |
版本号,语义化版本 |
每次发布 |
"1.0.0" |
description |
包的功能描述 |
项目初始化 |
"A useful library" |
main |
CommonJS 入口文件 |
构建配置后 |
"dist/index.js" |
types |
TypeScript 类型文件 |
添加 TS 后 |
"dist/index.d.ts" |
files |
发布到 npm 的文件 |
发布前确定 |
["dist", "static"] |
scripts |
自定义脚本命令 |
开发过程中 |
{"build": "tsc"} |
keywords |
搜索关键词 |
项目初始化 |
["library", "tool"] |
repository |
代码仓库地址 |
项目初始化 |
{"type": "git", "url": "..."} |
tsconfig.json 配置项详解
| 配置项 |
作用 |
推荐值 |
说明 |
target |
编译目标版本 |
ES2020 |
现代 JavaScript 特性 |
module |
模块系统 |
CommonJS |
Node.js 兼容性 |
outDir |
输出目录 |
"./dist" |
编译后文件位置 |
rootDir |
源代码目录 |
"./src" |
源代码组织 |
declaration |
生成类型文件 |
true |
TypeScript 类型支持 |
sourceMap |
生成源码映射 |
true |
调试支持 |
strict |
严格模式 |
true |
类型安全检查 |
esModuleInterop |
ES 模块互操作 |
true |
兼容性优化 |
第八步:构建机制解析
TypeScript 编译器 (tsc) 构建过程
- 源代码扫描: 根据
include 模式匹配 .ts 文件
- 类型检查: 使用
strict 配置进行严格类型验证
- 代码转换: 将 TypeScript 转换为指定
target 的 JavaScript
- 文件输出: 保持目录结构输出到
outDir 目录
- 附加文件: 生成
.d.ts 类型声明和 .map 源码映射
tsup 构建过程(基于 esbuild)
- 入口分析: 解析指定的入口文件及其依赖
- 依赖打包: 使用 esbuild 将依赖打包到单个文件中
- 格式转换: 根据配置生成 CommonJS 和 ES Modules 格式
- 类型生成: 使用 TypeScript 编译器 API 生成类型声明文件
- 源码映射: 生成源码映射文件用于调试
- 输出优化: 应用 Tree Shaking 和代码压缩(可选)
tsc 文件映射关系
1 2 3 4 5 6 7 8
| src/ dist/ ├── index.ts ├── index.js ├── utils/ ├── index.d.ts │ └── helper.ts ├── index.js.map └── types/ └── utils/ └── api.ts └── helper.js └── helper.d.ts └── helper.js.map
|
tsup 文件映射关系
1 2 3 4 5 6 7 8
| src/ dist/ ├── index.ts ├── index.js # CommonJS 格式 ├── utils/ ├── index.mjs # ES Modules 格式 │ └── helper.ts ├── index.d.ts # 类型声明 └── types/ ├── index.d.mts # ES Modules 类型声明 └── api.ts ├── index.js.map # CommonJS 源码映射 ├── index.mjs.map # ES Modules 源码映射 └── (依赖被打包到输出文件中)
|
tsup 与 tsc 对比
| 特性 |
tsc |
tsup |
| 构建速度 |
较慢 |
极快(基于 esbuild) |
| 配置复杂度 |
中等 |
简单(零配置) |
| 多格式支持 |
需要多次构建 |
单次构建支持多格式 |
| Tree Shaking |
有限 |
优秀 |
| 依赖打包 |
不支持 |
支持 |
| 源码映射 |
支持 |
支持 |
| 类型生成 |
支持 |
支持 |
| 代码分割 |
不支持 |
支持 |
tsup 构建流程详解
1. 入口解析阶段
- 分析入口文件的导入依赖
- 构建完整的依赖图
- 排除外部依赖(通过 –external 配置)
2. 打包阶段
- 使用 esbuild 进行快速打包
- 应用 Tree Shaking 移除未使用代码
- 处理模块解析和路径转换
3. 格式转换阶段
- 根据 –format 配置生成不同格式
- CommonJS: 使用 module.exports 和 require
- ES Modules: 使用 import/export 语法
4. 类型生成阶段
- 使用 tsc API 生成 .d.ts 文件
- 为不同格式生成对应的类型声明
- 保持类型定义的准确性
5. 输出优化阶段
- 生成源码映射文件
- 应用代码压缩(–minify)
- 清理输出目录(–clean)
第九步:常见问题解决
1. 类型声明不生效
- 原因:
declaration 配置为 false 或 types 字段缺失
- 解决: 确保
tsconfig.json 中 declaration: true,package.json 中正确配置 types 字段
2. 模块导入错误
- 原因: 模块导出配置不正确
- 解决: 同时支持 ES6 和 CommonJS 导出方式
3. 静态资源路径问题
- 原因: 发布后路径变化
- 解决: 使用
__dirname 和 path.join() 构建绝对路径
4. tsup 构建常见问题
问题:CommonJS 导入时出现 “TypeError: X is not a constructor”
- 原因: tsup 将 ES Modules 默认导出转换为
module.exports.default
- 解决:
- 使用命名导入:
const { Knife4jDoc } = require('package-name')
- 使用默认导入:
const Knife4jDoc = require('package-name').default
- 在 package.json 中配置正确的 exports 字段
问题:构建后文件过大
- 原因: 依赖被打包到输出文件中
- 解决:
- 使用
--external 排除外部依赖
- 配置
--splitting 启用代码分割
- 使用
--minify 压缩代码
问题:类型声明文件不完整
- 原因: tsup 的类型生成可能不完整
- 解决:
- 确保源代码有完整的类型定义
- 使用
--dts 标志生成类型声明
- 检查
tsconfig.json 中的类型配置
问题:ES Modules 和 CommonJS 导入不一致
- 原因: exports 字段配置不正确
- 解决:
- 在 package.json 中配置完整的 exports 字段
- 确保 import 和 require 指向正确的文件
- 测试两种导入方式是否正常工作
问题:构建速度慢
- 原因: 配置不当或依赖过多
- 解决:
- 使用
--external 排除不必要的依赖
- 启用
--minify 减少文件大小
- 考虑使用缓存或增量构建
5. tsup 配置优化建议
性能优化
1 2 3 4 5 6 7 8
| npx tsup src/index.ts --external react,vue,lodash
npx tsup src/index.ts --splitting
npx tsup src/index.ts --minify
|
兼容性优化
1 2 3 4 5 6 7 8
| npx tsup src/index.ts --target node16
npx tsup src/index.ts --platform node
npx tsup src/index.ts --cjs-interop
|
开发体验优化
1 2 3 4 5 6 7 8
| npx tsup src/index.ts --watch
npx tsup src/index.ts --sourcemap
npx tsup src/index.ts --clean
|
第十步:最佳实践总结
开发规范
- 代码组织: 使用
src/ 目录组织源代码
- 类型安全: 充分利用 TypeScript 类型系统
- 文档完善: 提供完整的 API 文档和使用示例
- 测试覆盖: 添加单元测试确保功能稳定
发布规范
- 版本管理: 严格遵循语义化版本规范
- 文件控制: 精确控制发布内容,避免泄露源代码
- 质量保证: 发布前进行完整测试和验证
- 持续集成: 考虑添加自动化测试和发布流程
维护规范
- 更新日志: 维护 CHANGELOG.md 记录变更
- 问题跟踪: 使用 GitHub Issues 管理问题
- 社区支持: 提供清晰的贡献指南
第十一步:发布到 npm 官网的完整流程
1. 账号准备阶段
检查是否已有 npm 账号
注册 npm 账号(如果没有)
- 访问官网: https://www.npmjs.com/signup
- 填写信息:
- 用户名(username): 全局唯一,将作为包名前缀
- 邮箱地址: 用于验证和通知
- 密码: 至少8个字符
- 邮箱验证: 检查邮箱并点击验证链接
- 双重验证(推荐): 启用 2FA 增强安全性
账号验证准备
- 确保邮箱可以正常接收邮件
- 准备手机用于 2FA(如果启用)
- 记录用户名和密码到安全位置
2. 本地环境配置
检查 npm 版本
1 2 3 4 5
| npm -v
npm install -g npm@latest
|
配置 npm 镜像(如果需要)
1 2 3 4 5 6 7 8 9 10 11
| npm config get registry
npm config set registry https://registry.npmjs.org/
npm config set registry https://registry.npmmirror.com/
npm config set registry https://registry.npmjs.org/
|
可以使用nrm切换npm镜像
3. 登录 npm
命令行登录
1 2 3 4 5 6 7 8 9 10
| npm login
Username: your-username Password: ******** Email: your-email@example.com
Enter one-time password: 123456
|
登录验证
1 2 3 4 5 6
| npm whoami
npm profile get
|
登录问题排查
- 认证失败: 检查用户名/密码,或重置密码
- 网络问题: 检查网络连接和防火墙设置
- 2FA 问题: 确保手机时间同步,重新生成验证码
4. 发布前最终检查
包名检查
1 2 3 4 5
| npm search your-package-name
npm publish --dry-run
|
版本号检查
1 2 3 4 5 6 7
| npm version
npm version patch npm version minor npm version major
|
文件内容检查
1 2 3 4 5 6 7 8
| npm pack
tar -tzf your-package-1.0.0.tgz
|
5. 正式发布流程
第一次发布
1 2 3 4 5
| npm publish
+ your-package@1.0.0
|
发布后验证
1 2 3 4 5 6 7 8 9 10 11
| npm info your-package
mkdir test-package cd test-package npm init -y npm install your-package
node -e "console.log(require('your-package'))"
|
发布到特定标签
1 2 3 4 5 6 7 8
| npm publish --tag beta
npm install your-package@beta
npm dist-tag add your-package@1.0.0 latest
|
6. 发布后管理
版本管理
1 2 3 4 5 6 7 8 9 10 11
| npm view your-package versions
npm dist-tag ls your-package
npm unpublish your-package@1.0.0
npm unpublish your-package --force
|
包信息更新
1 2 3 4 5 6 7 8 9
| npm pkg set description="New description"
npm pkg set keywords='["new", "keywords"]'
npm pkg set repository.type=git npm pkg set repository.url=https://github.com/your/repo
|
维护更新
1 2 3 4 5 6
| npm version patch && npm publish
npm publish
|
7. 常见发布问题解决
包名冲突
1 2 3 4
|
npm init --scope=your-username
|
权限问题
1 2 3 4
|
npm whoami npm owner ls your-package
|
网络超时
2FA 要求
1 2 3
|
npm profile enable-2fa auth-only
|
8. 最佳实践建议
安全性
- 启用 2FA 保护账号
- 使用强密码并定期更换
- 不要在公共代码中泄露令牌
版本管理
- 严格遵循语义化版本规范
- 为重大变更提供迁移指南
- 维护详细的更新日志
质量控制
- 发布前运行完整测试套件
- 使用 CI/CD 自动化测试和发布
- 提供清晰的文档和示例
社区维护
- 及时回复 issue 和 PR
- 定期更新依赖项
- 考虑添加贡献指南
完整流程总结
通过以上标准化流程,您可以:
- 从零开始创建专业的 npm 包
- 确保代码质量和类型安全
- 支持多种使用场景和框架
- 规范发布和维护流程
- 提供良好的开发者体验
这个流程适用于大多数 Node.js 库的开发,可以根据具体需求进行调整和扩展。