CodeSky 代码之空

随手记录自己的学习过程

npm package.json 字段梳理

2022-05-03 22:42分类: JavaScript评论: 0

好久没更新博客了拿着很久之前记的笔记凑个更新吧。

package.json | npm Docs

package.json,一个前端项目的归属——万万没想到的是,它有很多学问,因此现在我们就来完整的盘点一下。

  • name 包名,全世界唯一

  • version 版本号,怎么打请见「npm version」,做个人,请遵循语义化版本号

  • description 描述

  • keywords string[]和描述类似

  • homepage 包首页,可能是文档网站或者 GitHub 地址

  • bugs where is bug list,可以放 issue 地址, npm bug 可以打开地址(说实话我第一次知道这个指令)

  • licenses / license 许可协议,可以是多个:https://spdx.org/licenses/

  • author/contributors 作者信息

1{
2  "name" : "Barney Rubble",
3  "email" : "[email protected]",
4  "url" : "http://barnyrubble.tumblr.com/"
5}
6    
7// 或者合起来
8{
9  "author": "Barney Rubble <[email protected]> (http://barnyrubble.tumblr.com/)"
10}
11
  • funding 在哪里打钱给你

  • files 包发布后包含的文件,遵循 .gitignore 同款语法,默认会包含全部,这里复杂而日狗的是,npm 会使用三份内容:files 字段,.npmignore.gitignore ,以及背后的一些包含关系

    • .npmignore 在根目录时不会覆盖 files 字段;在子目录时会
    • 如果没有 .npmignore ,会用上 .gitignore
    • files 字段中的包含的内容无法被 .npmignore.gitignore 覆盖
    • 以下内容一定会包含
      • package.json
      • README
      • LICENSE / LICENCE
      • main 字段中的文件
    • 以下内容一定会被忽略
      • .git
      • CVS
      • .svn
      • .hg
      • .lock-wscript
      • .wafpickle-N
      • .*.swp
      • .DS_Store
      • ._*
      • npm-debug.log
      • .npmrc
      • node_modules
      • config.gypi
      • .orig
      • package-lock.json(如果你需要请使用 npm-shrinkwrap
  • main 入口文件,当你 require('module') 时,就会使用这个入口文件。默认会取 index.js ,所有路径都是相对于你的项目根目录而言的。

  • browser 如果你的包在浏览器环境下使用,应该使用这个字段表明浏览器里用哪个文件,他可以提示用户这个文件不能在 Node.js 下使用。(注意,这里依旧是 require 时使用,所以提供的不应该是 iife,而是 umd

  • bin 对于 cli 项目,你可能希望把文件安装进 bin 目录方便使用,这个时候就会用到这个字段,如果 bin: string ,那么表示指明 bin 文件应该是哪个;

    如果定义为:

1{
2  "bin": {
3    // 同样的,这个方法可以注册多个可执行文件
4    "myapp": "./cli.js"
5  }
6}
7

那么代表会在 bin 目录里增加一个叫 myapp 可执行文件,同时为了让 node 成功执行,记得在 bin 文件头上加 #!/usr/bin/env node

  • man

    文档地址,和 doc 不同的是,我们都知道 Linux 有个 man 命令,他其实是用于注册 man [myapp] 这样的 man 文件的配置项

  • directories

    表明包结构,主要有两个子配置,看文档

  • repository

    仓库信息,品种齐全,价格丰富

1{
2  "repository": "npm/npm",
3  "repository": "github:user/repo",
4  "repository": "gist:11081aaa281",
5  "repository": "bitbucket:user/repo",
6  "repository": "gitlab:user/repo"
7}
8
  • scripts

    这我只能说,你怎么这么熟练。

    scripts | npm Docs

    但其实需要注意的是,它其实是有生命周期钩子的:

1{
2  "scripts": {
3    "precompress": "{{ executes BEFORE the `compress` script }}",
4    "compress": "{{ run command to compress files }}",
5    "postcompress": "{{ executes AFTER `compress` script }}"
6  }
7}
8

此外,还提供了包全局的生命周期钩子:prepareprepublishprepublishOnlyprepackpostpack

  • config

    可能要试验下

  • dependencies

    语法很多很花哨,需要注意区分 devDependenciespeerDependencies

    • dependencies:使用这个包需要安装
    • devDependencies:开发这个包的时候安装,使用者不需要关心
    • peerDependencies:我可能不想集成进我自己的包,但需要你本机装了的包
      • peerDependenciesMeta****:****依赖说明,比如他可能并不非得要,可以标记成 optional
    • optionalDependencies:这个库可以有,但没有也能用
    • bundledDependencies:没怎么看懂,需要在研究一下
  • overrides

    日我依赖的依赖,直呼牛逼

  • engines

    我支持的 node / npm 版本,会在安装包时检查

  • os

    支持安装的平台

  • cpu

    支持的 CPU 类型

  • private

    不许发

  • publishConfig

    config | npm Docs

  • workspaces

    workspaces | npm Docs

    一个 Monorepo 概念

以上是 npm 中的说明,在 Node 中 package.json 还有一些扩充,它和 npm 的主要区别是,npm 列出的是 npm 用到的,而 node 列出的是由 Node 去控制使用的。

包模块 | Node.js API 文档

  • type

    表明包是 esm 还是 commonjs;如果引入方式是 import 且文件以 mjs 结尾;或者 package.json 中 type: module 时,JS 文件会被判断成 esm 使用的文件;否则会当成 commonjs 处理。

    注意:和 .mjs 一致,.cjs 永远作为 commonjs 处理

  • packageManager

    指定的包管理器,详情见文档

  • exports

    一个新增加的很强的东西,在 npm 中我们说明了 main 是入口文件,控制引入的默认文件;而 exports 则可以控制:

    1. 只允许引入后访问我指定的 exports list 入口点
    2. 支持在不同环境使用不同入口文件
1{
2  "name": "my-mod",
3// 只能用下面这些
4  "exports": {
5    ".": "./lib/index.js",
6    "./lib": "./lib/index.js",
7    "./lib/index": "./lib/index.js",
8    "./lib/index.js": "./lib/index.js",
9    "./feature": "./feature/index.js",
10    "./feature/index.js": "./feature/index.js",
11    "./package.json": "./package.json"
12  }
13}
14

exports 的优先级比 main 更高,此时 main 可以当做低版本 node 的降级策略去使用。

条件导出也就是之前说的不同环境,不同入口文件,这里几个简单的 case:

1"exports": {
2    "import": "./main-module.js",
3    "require": "./main-require.cjs"
4  }
5

分别指定了 esm 和 commonjs 的情况下使用的文件,支持以下参数:

  • "node" - 匹配任何 Node.js 环境。 可以是 CommonJS 或 ES 模块文件。 在大多数情况下,不需要明确调用 Node.js 平台。
  • "import" - 当包通过 import 或 import(),或者通过 ECMAScript 模块加载器的任何顶层导入或解析操作加载时匹配。 无论目标文件的模块格式如何,都适用。 始终与 "require" 互斥。
  • "require" - 当包通过 require() 加载时匹配。 引用的文件应该可以用 require() 加载,尽管无论目标文件的模块格式如何,条件都匹配。 预期的格式包括 CommonJS、JSON 和原生插件,但不包括 ES 模块,因为 require() 不支持它们。 始终与 "import" 互斥。
  • "default" - 始终匹配的通用后备。 可以是 CommonJS 或 ES 模块文件。 此条件应始终放在最后。

注意,node 字段也同样支持套娃:

1"node": {
2      "import": "./feature-node.mjs",
3      "require": "./feature-node.cjs"
4    }
5

当然,双包问题是另一个问题:

包模块 | Node.js API 文档

还没有仔细研究,大致讲的是由于不同引入方式引入了不同文件导致的不一致性甚至是跑不起来的情况,用隔离的 exports 可以最大程度避免这个问题。

最后,再来看看 TypeScript 怎么支持的 node esm ,以及他加了啥:

Documentation - Experimental Support for ECMAScript Modules in Node.js

  • types 定义了类型文件;exports 中也同样有了新的 types,根中的 types 作为一个 fallback 可。

这样我们就解释了「我拿到的包为什么不对」,以及「谁负责判断我用哪个包」,而「双包问题」,可能是下一次的 topic 了。

评论 (0)