package.json
通常包目录应该包含如下这些文件。
{
"name": "pkg-placeholder",
"type": "module",
"version": "0.0.0",
"packageManager": "pnpm@8.10.5",
"description": "_description_",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/antfu/pkg-placeholder#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/antfu/pkg-placeholder.git"
},
"bugs": "https://github.com/antfu/pkg-placeholder/issues",
"keywords": [],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./dist/index.d.ts"
]
}
},
"files": [
"dist"
],
"scripts": {
},
"devDependencies": {
}
}package.json:包描述文件。bin:用于存放可执行二进制文件的目录。lib:用于存放 JavaScript 代码的目录。doc:用于存放文档的目录。test:用于存放单元测试用例的代码。
package.json 中有许多重要的配置字段值得讨论;我在这里将着重讨论其中最为重要的一些,这还有很多额外的字段,你同样可以进行配置。
keywords: 关键词数组,NPM 中主要用来做分类搜索。一个好的关键词数组有利于用户快速找到你编写的包。maintainers: 包维护者列表。每个维护者由name、email和web这 3 个属性组成。示例如下: "maintainers": [{ "name": "Jackson Tian", "email": "shyvo1987@gmail.com", "web": "http://html5ify.com" }]NPM 通过该属性进行权限认证。contributors: 贡献者列表。在开源社区中,为开源项目提供代码是经常出现的事情,如 果名字能出现在知名项目的 contributors 列表中,是一件比较有荣誉感的事。列表中的第 一个贡献应当是包的作者本人。它的格式与维护者列表相同。bugs: 一个可以反馈 bug 的网页地址或邮件地址。licenses: 当前包所使用的许可证列表,表示这个包可以在哪些许可证下使用。repositories:托管源代码的位置列表,表明可以通过哪些方式和地址访问包的源代码。dependencies:使用当前包所需要依赖的包列表。这个属性十分重要,NPM 会通过这个属性帮助自动加载依赖的包。homepage:当前包的网站地址。author: 包作者。bin: 一些包作者希望包可以作为命令行工具使用。配置好 bin 字段后,通过npm install package_name -g命令可以将脚本添加到执行路径中,之后可以在命令行中直接执行。
设置 name 字段
由小写的字母和数字组成,可以包含.、_和-,但不允许出现空格。包名必须是唯一的,以免对外公布时产生重名冲突的误解。
name 字段将决定你的包在 npm 上的名字,开发者可以通过这个名字去安装并使用你的库。
注意,库的命名是有限制的,如果你的代码库属于某个组织,你还可以创建一个命名空间。更多细节可以参考 name docs on npm。
name 和 version 的组合为库每次迭代创建一个唯一标识。
设置 scripts 字段
scripts 主要被包管理器用来安装、编译、测试和卸载包。
在 npm 中,可以通过配置 package.json 文件中的 scripts 字段来指定在执行 npm install 命令时要执行的脚本钩子。
常用的 npm install 执行的钩子脚本包括:
preinstall:在安装包之前运行的脚本。postinstall: 在安装包之后运行的脚本。preuninstall:在卸载包之前运行的脚本。postuninstall:在卸载包之后运行的脚本。
例如,以下是一个 package.json 文件示例,其中定义了 preinstall 和 postinstall 钩子脚本:
{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"preinstall": "echo Running preinstall script",
"postinstall": "echo Running postinstall script"
}
}当运行 npm install 命令时,preinstall 和 postinstall 脚本将会被自动执行。
设置 version 字段
通过更改 version 来对你的库发布更新
版本号。一个语义化的版本号,这在 semver 上有详细定义,通常为major.minor.revision 格式。该版本号十分重要,常常用于一些版本控制的场合。
正如 name 部分所说,name 和 version 的组合为你的库在 npm 上创建一个唯一标识。当你更新库中的代码时,你可以更新 version 字段并发布以允许开发者获取该新代码。
推荐使用 semver 版本控制策略,但要注意的是有些库选择 calver 或使用他们自己特有的版本控制策略。无论你选择使用哪种策略,都应该记录下来,以便开发者了解你的库是如何进行版本控制的。
你还应该在 changelog 中记录你的更改。
列出要发布的 files
files 决定 npm CLI 在打包库时哪些文件和目录包含到最终的 NPM 包中。
例如,如果你将代码从 TypeScript 编译为 JavaScript,你可能就不想在 NPM 包中包含 TypeScript 的源代码。(相反,你应该包含 sourcemap)。
files 可以接受一个字符串数组(如果需要,这些字符串可以包含类似 glob 的语法),例如:
{
"files": ["dist"]
}TIP
注意,文件数组不接受相对路径表示;"files": ["./dist"] 将无法正常工作。
验证你已正确设置 files 的一种好方法是运行 npm publish --dry-run,它将根据此设置列出将会包含的文件。
为你的 JS 文件设置默认的模块 type
type 规定你的 .js 文件使用哪个模块系统
运行时和打包工具需要一种方法来确定你的 .js 文件采用哪种模块系统 —— ESM 还是 CommonJS。因为 CommonJS 首先出现,所以它被打包工具视为默认的 - 但你可以通过在你的 package.json 中添加 "type" 来控制这种行为。
你可以选择 "type":"module" 或 "type":"commonjs",也可以不添加该字段(默认为 CommonJS),但仍强烈建议你进行设置,显式地声明你正在使用哪一个。
请注意,你可以通过几个技巧在项目中混用模块类型:
.mjs文件总是 ESM 模块,即使你的package.json有"type": "commonjs"(或者没有type).cjs文件总是 CommonJS 模块,即使你的package.json有"type": "module"- 你可以在子目录下添加其他
package.json文件;运行时和打包工具将向上遍历文件目录,直到寻找到最近的package.json。这意味着你可以有两个不同的文件夹,都使用.js文件,但每个文件夹都有自己的package.json并设置为不同的type以获得基于 CommonJS 和 ESM 的文件夹。
参考优秀的 NodeJS 文档 这里 和 这里 了解更多信息。
列出哪些模块有 sideEffects
设置 sideEffects 来允许 treeshaking
创建一个“纯模块”带来的优点与创建一个纯函数十分类似;打包工具能够对你的库更好的进行 treeshaking。
通过设置 sideEffects 让打包工具知道你的模块是否是“纯”的。不设置这个字段,打包工具将不得不假设你所有的模块都是有副作用。
sideEffects 可以设为 false,表示没有任何模块具有副作用,也可以设置为字符串数组来列出哪些文件具有副作用。例如:
{
// 所有模块都是“纯”的
"sideEffects": false
}或
{
// 除了 "module.js",所有模块都是“纯”的
"sideEffects": ["module.js"]
}所以,什么让一个模块具有副作用?例如修改一个全局变量,发送 API 请求,或导出 CSS,而且开发人员不需要做任何事情这些动作就会被执行。例如:
// 具有副作用的模块
export const myVar = 'hello'
window.example = 'testing'导入 myVar 时,你的模块自动设置 window.example。
例如:
import { myVar } from 'library'
console.log(window.example)
// 打印 "testing"在某些情况下,如 polyfill,这种行为是有意的。然而,如果我们想让这个模块是“纯”的,我们可以将对 window.example 的赋值移动到一个函数中。例如:
// 一个“纯”模块
export const myVar = 'hello'
export function setExample() {
window.example = 'testing'
}现在这是一个“纯”模块。注意,从开发者的角度来看会有不同:
import { myVar, setExample } from 'library'
console.log(window.example)
// 打印 "undefined"
setExample()
console.log(window.example)
// 打印 "testing"通过这篇文章来了解更多。
设置给 CDN 使用的附加字段
支持 CDN,例如 unpkg 和 jsdelivr
为让你的库在 CDN 上“以默认的方式正常工作”,如 unpkg 和 jsdelivr,你可以设置它们的特定字段指向你的 umd 产出。例如:
{
"unpkg": "./dist/index.umd.js",
"jsdelivr": "./dist/index.umd.js"
}设置 browser 字段
browser 指向能在浏览器中工作的产出
browser 是一个当打包工具或运行时不支持 package.json#exports 时的兜底方案;如果打包工具或运行时支持 package exports, 则不会使用 browser。
browser 应该指向能在浏览器中工作的 esm 产出。但是,只有在为浏览器和服务器(等其他非浏览器环境)创建不同的产出时,才需要设置该字段。如果你没有为多个环境创建多个产出,或者你的产出是“纯 JavaScript”或“通用”的,可以在任何 JavaScript 环境中运行,那么你就不需要设置 browser 字段。
如果你确实需要设置该字段,这里有一个优秀的指南,介绍了配置它的不同方法。
注意,browser 字段不应该指向 umd 产出,因为那样的话,你的库就不会被打包工具(如 Webpack)进行 treeshaking,这些打包工具会优先考虑这个字段,而不是其他字段,比如 module 和 main。
设置 types 字段
types 定义 TypeScript 类型
types 是一个当打包工具或运行时不支持 package.json#exports 时的兜底方案; 如果打包工具或运行时支持 package exports,则不会使用 types。
types 应该指向你的 TypeScript 入口文件,例如 index.d.ts;它应该与 package exports 中的 types 字段指向同一个文件。
列出 peerDependencies
如果你依赖别的框架或库,将它设置为 peer dependency
你应该外置框架。然而,这样做后,你的库只有在开发人员自行安装你需要的框架后才能工作。设置 peerDependencies 让他们知道他们需要安装的框架。
例如,如果你在创建一个 React 库:
{
"peerDependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}通过这篇文章来了解更多。
你应该以书面形式来体现这些依赖;例如,npm v3-v6 不安装 peer dependencies,而 npm v7+ 将自动安装 peer dependencies。
说明你的库使用哪个许可证
保护你自己和其他的贡献者
开源许可证用于保护贡献者和用户。没有这种保护,企业和有经验的开发者不会使用该项目。
上述引用自 Choose a License,这也是一篇很好的文章,帮助你来决定哪个许可证适合你的项目。
当你决定了许可证,关于许可证的 npm 文档中描述了许可证字段的格式。例如:
{
"license": "MIT"
}除此之外,你可以在项目的根目录下创建一个 LICENSE.txt 文件,并将许可证的文本复制到这里。