Skip to content

package.json

通常包目录应该包含如下这些文件。

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: 包维护者列表。每个维护者由 nameemailweb 这 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

nameversion 的组合为库每次迭代创建一个唯一标识。

设置 scripts 字段

scripts 主要被包管理器用来安装、编译、测试和卸载包。

在 npm 中,可以通过配置 package.json 文件中的 scripts 字段来指定在执行 npm install 命令时要执行的脚本钩子。

常用的 npm install 执行的钩子脚本包括:

  • preinstall:在安装包之前运行的脚本。
  • postinstall: 在安装包之后运行的脚本。
  • preuninstall:在卸载包之前运行的脚本。
  • postuninstall:在卸载包之后运行的脚本。

例如,以下是一个 package.json 文件示例,其中定义了 preinstallpostinstall 钩子脚本:

json
{
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "echo Running preinstall script",
    "postinstall": "echo Running postinstall script"
  }
}

当运行 npm install 命令时,preinstallpostinstall 脚本将会被自动执行。

设置 version 字段

通过更改 version 来对你的库发布更新

版本号。一个语义化的版本号,这在 semver 上有详细定义,通常为major.minor.revision 格式。该版本号十分重要,常常用于一些版本控制的场合。

正如 name 部分所说,nameversion 的组合为你的库在 npm 上创建一个唯一标识。当你更新库中的代码时,你可以更新 version 字段并发布以允许开发者获取该新代码。

推荐使用 semver 版本控制策略,但要注意的是有些库选择 calver 或使用他们自己特有的版本控制策略。无论你选择使用哪种策略,都应该记录下来,以便开发者了解你的库是如何进行版本控制的。

你还应该在 changelog 中记录你的更改。

列出要发布的 files

files 决定 npm CLI 在打包库时哪些文件和目录包含到最终的 NPM 包中。

例如,如果你将代码从 TypeScript 编译为 JavaScript,你可能就不想在 NPM 包中包含 TypeScript 的源代码。(相反,你应该包含 sourcemap)。

files 可以接受一个字符串数组(如果需要,这些字符串可以包含类似 glob 的语法),例如:

json
{
  "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,表示没有任何模块具有副作用,也可以设置为字符串数组来列出哪些文件具有副作用。例如:

jsonc
{
  // 所有模块都是“纯”的
  "sideEffects": false
}

jsonc
{
  // 除了 "module.js",所有模块都是“纯”的
  "sideEffects": ["module.js"]
}

所以,什么让一个模块具有副作用?例如修改一个全局变量,发送 API 请求,或导出 CSS,而且开发人员不需要做任何事情这些动作就会被执行。例如:

js
// 具有副作用的模块

export const myVar = 'hello'

window.example = 'testing'

导入 myVar 时,你的模块自动设置 window.example

例如:

js
import { myVar } from 'library'

console.log(window.example)
// 打印 "testing"

在某些情况下,如 polyfill,这种行为是有意的。然而,如果我们想让这个模块是“纯”的,我们可以将对 window.example 的赋值移动到一个函数中。例如:

js
// 一个“纯”模块

export const myVar = 'hello'

export function setExample() {
  window.example = 'testing'
}

现在这是一个“纯”模块。注意,从开发者的角度来看会有不同:

js
import { myVar, setExample } from 'library'

console.log(window.example)
// 打印 "undefined"

setExample()

console.log(window.example)
// 打印 "testing"

通过这篇文章来了解更多。

设置给 CDN 使用的附加字段

支持 CDN,例如 unpkgjsdelivr

为让你的库在 CDN 上“以默认的方式正常工作”,如 unpkgjsdelivr,你可以设置它们的特定字段指向你的 umd 产出。例如:

json
{
  "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,这些打包工具会优先考虑这个字段,而不是其他字段,比如 modulemain

设置 types 字段

types 定义 TypeScript 类型

types 是一个当打包工具或运行时不支持 package.json#exports 时的兜底方案; 如果打包工具或运行时支持 package exports,则不会使用 types

types 应该指向你的 TypeScript 入口文件,例如 index.d.ts;它应该与 package exports 中的 types 字段指向同一个文件。

列出 peerDependencies

如果你依赖别的框架或库,将它设置为 peer dependency

你应该外置框架。然而,这样做后,你的库只有在开发人员自行安装你需要的框架后才能工作。设置 peerDependencies 让他们知道他们需要安装的框架。

例如,如果你在创建一个 React 库:

json
{
  "peerDependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

通过这篇文章来了解更多。

你应该以书面形式来体现这些依赖;例如,npm v3-v6 不安装 peer dependencies,而 npm v7+ 将自动安装 peer dependencies。

说明你的库使用哪个许可证

保护你自己和其他的贡献者

开源许可证用于保护贡献者和用户。没有这种保护,企业和有经验的开发者不会使用该项目。

上述引用自 Choose a License,这也是一篇很好的文章,帮助你来决定哪个许可证适合你的项目。

当你决定了许可证,关于许可证的 npm 文档中描述了许可证字段的格式。例如:

json
{
  "license": "MIT"
}

除此之外,你可以在项目的根目录下创建一个 LICENSE.txt 文件,并将许可证的文本复制到这里。

Reference