- Published on
如何创建并发布一个 NPM 包
- Authors
- Name
- Duck
什么是 NPM
npm 是 Node.js 的包管理工具,全称是 Node Package Manager。它主要用来管理 JavaScript 项目的依赖包。简单来说,npm 就像是一个 线上“应用商店”,可以帮你安装、更新和管理各种 JavaScript 库或工具。

网站(the website) 用于发现包、设置个人资料以及管理 npm 的其他功能。
命令行工具(CLI) 在终端中运行,是大多数开发者与 npm 交互的主要方式。
注册库(the registry) 是一个大型的公共数据库,存储 JavaScript 软件及其相关的元信息。
发布第一个包
本文用 typescript
创建一个摩斯电码解码包为例,记录一下包的创建发布流程。
注册 npm 账号
参考 官方文档
新建包并初始化 package.json
mkdir npm-package-start
cd ./npm-package-start
npm init
然后根据提示填写信息,最终在当前路径生成如下内容:
{
"name": "@duckbb/npm-package-start", //这里加上了 scope
"version": "1.0.0",
"main": "index.ts", //默认是 index.js,修改成 src/index.ts
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Duck",
"license": "ISC",
"description": "A simple npm package to get started with npm",
"keywords": [
"npm",
"package",
"start"
]
}
name
是包的名称,也是导入模块的名称,duckbb
是 scope,只能是用户名或组织名,加了 scope 默认是私有包version
版本号main
入口文件keywords
关键词,搜索关键词可以搜索到该包author
作者license
协议
再新建三个文件夹
src
包源码tests
存放单元测试代码e2e
端到端测试代码
创建入口文件
从入口文件导出两个模块
export const add = (a: number, b: number) => a + b
export const subtract = (a: number, b: number) => a - b
初始化 Git
创建 Git 忽略文件,dist 目录将用于存放编译后的代码,不需要同步到代码仓库
node_modules
dist
git init
git add -A
git commit -m "init"
就是这么简单,现在就可以直接发布包了。
发布包
npm login # 登录
npm publish --access public # 发布
因为包名加了 scope,默认是发布到私有包,所以加上 --access public
发布到公共包。
成功发布后,就可以在后台看到自己的包了。

编译代码
不过,目前发布的包是 typescript 代码,而所有主流的浏览器和 Node.js 运行时环境都只能直接执行 JavaScript 代码。所以,需要编译代码到 JS 再发布,否则无法正常使用。
一个标准的 TypeScript 包发布流程通常包含以下步骤:
编写 TypeScript 代码:在 src 目录下编写你的 .ts 文件。
配置 tsconfig.json:
{
"compilerOptions": {
"target": "ES2017", // 编译后的 JS 目标版本,ES2017 支持 async/await 等
"module": "ESNext", // 输出模块类型,ESNext 对应 ESM(import/export)
"declaration": true, // 生成类型声明文件 (.d.ts)
"outDir": "dist", // 编译输出目录
"strict": true, // 开启严格模式,启用所有严格类型检查选项
// "esModuleInterop": true, // 支持 CommonJS 模块默认导入,兼容 require() 的模块
// "skipLibCheck": true, // 跳过库文件类型检查,提高编译速度
"moduleResolution": "Node", // 模块解析策略,Node 风格(支持 node_modules)
"forceConsistentCasingInFileNames": true // 强制文件名大小写一致,避免跨平台问题
},
"include": ["src"] // 指定编译器要包含的文件或目录,这里只编译 src 文件夹
}
- 添加编译脚本:在 package.json 中添加一个 build 脚本来执行编译命令
npm install --save-dev typescript
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
},
- 编译代码。这会将你的 .ts 文件编译成 .js 文件和 .d.ts 文件,并将它们输出到指定的目录(dist)。
npm run build
- 配置 package.json。
{
"name": "@duckbb/npm-package-start",
"version": "1.0.0",
"main": "dist/index.js", // 入口 JS 文件,指向编译后的 dist 目录
"types": "dist/index.d.ts", // TypeScript 类型声明文件入口,供其他 TS 项目使用
"type": "module", // 指明这是 ESM 模块,使用 import/export
"files": [ // 发布到 npm 时包含的文件或目录
"dist" // 这里只发布 dist 文件夹,确保包体积小
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
},
"author": "Duck",
"license": "ISC",
"description": "A simple npm package to get started with npm",
"keywords": [
"npm",
"package",
"start"
],
"devDependencies": {
"typescript": "^5.9.2"
}
}
- 最后,运行
npm publish
。
测试
端到端测试
端到端测试(End-to-End Testing),简称 E2E 测试,是一种软件测试方法,它模拟用户从头到尾使用应用程序的整个流程,以验证整个系统作为一个整体是否能正常运行。最好在发布包之前,在本地进行测试。
npm link # 这个命令会在你的文件系统中创建一个符号链接(symbolic link),让你的项目可以像引用一个正常的 npm 包一样,引用你的本地包。
cd ./e2e # 在 e2e 中测试本地包
npm init -y # 初始化,视为一个独立的子项目
npm link @ducbbb/npm-package-start # 链接到本地包
创建测试文件index.mjs
以支持 ESM 语法:
import { add, subtract } from '@duckbb/npm-package-start'
console.log(add(1, 2))
console.log(subtract(1, 2))
执行,看看能否正常调用:
node index.mjs
3
-1
输出结果表明,成功调用本地包。
单元测试
单元测试是指针对代码中最小可测试单元进行验证的测试。它的核心在于测试对象。
测试对象:通常是一个函数、一个方法、一个类或一个组件。
测试范围:专注于测试该单元自身的逻辑,不涉及外部依赖(如数据库、网络请求、文件系统)。为了隔离外部依赖,通常会使用 mocking 或 stubbing 技术。
主要目的:确保代码的每个基本单元都按预期工作,是测试金字塔的基础。
流行的测试框架有 jest
和vitest
,经过对比,这里选择更简洁且对 ESM 支持更好的vitest
。
npm install -D vitest
创建两个测试文件
import { expect, test } from 'vitest'
import { add } from '../src/index'
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3)
})
import { expect, test } from 'vitest'
import { subtract } from '../src/index'
test('subtracts 1 - 2 to equal -1', () => {
expect(subtract(1, 2)).toBe(-1)
})
TIP
一般情况下,执行测试的文件名中必须包含 .test.
或 .spec.
。
"scripts": {
"test": "vitest"
}
最后,运行 npm run test
,vitest 将打印测试消息
✓ tests/add.test.ts (1 test) 2ms
✓ tests/subtract.test.ts (1 test) 2ms
Test Files 2 passed (2)
Tests 2 passed (2)
Start at 09:28:35
Duration 380ms (transform 85ms, setup 0ms, collect 107ms, tests 4ms, environment 0ms, prepare 243ms)
更新包
git add -A
git commit -m "add vitest"
npm version patch # 版本号+1 并commit ,比如 "1.2.0" => "1.2.1"
npm run build # 编译 ts
npm run e2e # 本地测试
npm run test # 单元测试
npm publish # 第一次已经指定了 --access public,后面就可以不用了
之后,就可以用这个做模板代码,快速开发自己的包了。完整模板代码可查看 https://github.com/duck-codes/npm-package-start
IMPORTANT
实际开发包时,可以使用流行的起步模板,配置会更加完善。建议使用 tsdown 快速开发。