Skip to content

package.json

配置

基础

json
{
  "name": "project", // 项目名称、包含组织名(Scope),如 @myorg/project
  "version": "1.0.0", // 项目版本
  "description": "", // 项目描述
  "keywords": ["vue", "react", "typescript"], // 项目关键词
  "main": "index.js", // 项目入口文件
  "scripts": { // 指定运行脚本命令的 npm 命令行缩写
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",// 作者
  "license": "ISC", // 许可证
  "contributors": [ // 贡献者
    "name <email>",
    "name2 <email2>"
  ],
  "files": [  // 必包含,不需要制定字段  package.json、README、LICENSE 或 LICENCE、main 字段指定的文件、bin 字段指定的文件
    "dist"// 指定发布到npm的文件
  ],
  "homepage": "", // 项目主页
  "bugs": "", // 项目bugs
  "repository": "", // 项目仓库
  "private": true, // 是否私有
  "workspaces": [ // 工作区
    "packages/*"
  ],
  "engines": { // 指定项目运行所需的环境,包括 node, npm 等。
    "node": ">=10.0.0", // node 是项目运行所需的 Node.js 版本。
    "npm": ">=6.0.0" // npm 是项目运行所需的 npm 版本。
  },
  "os": [ // 指定操作系统
    "darwin", // macOS(苹果电脑使用的Unix系统)
    "linux", // Linux 系统
    "win32" // Windows 系统,(无论 32 位还是 64 位都用这个名字)
  ],
  "cpu": [ // 指定 CPU 架构
    "x64", // 64 位
    "!arm" // 非 arm 架构
  ],
  "workspaces": [ // 工作区 
    "packages/*"
  ],
  "publishConfig":{
    "registry":"https://registry.npmjs.org/" // 设置发布地址(私有npm)
  },
  "bin": {
    "my-package": "./bin/my-package.js"
  },
  // 非标准
  // 打包工具(Webpack,rollup 等)扩展的字段,类似于 main 字段,只不过用于指定项目的 ES Module 入口文件。
  "module": "index.mjs",
  // Webpack 在构建时有一个 target 配置项,默认为 web。如果使用 import 语法引入模块,优先级 browser > module > main;如果使用 require 语法引入模块,优先级 main > module > browser。
  "browser": {
    "./index.js": "./dist/my-lib.js"
  },
  // 由 CDN 服务商扩展的字段,用于指定项目在 CDN 上的入口文件。
  "unpkg": "dist/vue.global.js",
  "jsdelivr": "dist/vue.global.js",
  "type":"commonjs", // node字段 模块格式 缺少“类型”字段或 commonjs 则将.js文件视为commonjs,需要使用 ES Module,可以使用 .mjs 后缀。
  "type": "module",  // node字段 模块格式 ES module,所有 .js 文件都会被当作 ES Module 处理。如果需要使用 CommonJS,可以使用 .cjs 后缀。
  "imports": {
    "#utils/*": "./src/utils/*",
    "#config": "./src/config.js"
  },
  "exports": {
    ".": {
      "import": "./index.js",
      "require": "./index.cjs",
      "types": "./index.d.ts"
      "default": "./index.js"
    },
    "./module": {
      "import": "./module.js",
      "require": "./module.cjs",
      "types": "./module.d.ts"
      "default": "./module.js"
    }
  },
  "types": "index.d.ts", // 由 TypeScript 扩展的字段,用于指定项目的类型定义文件
  "sideEffects": false // 是否开启副作用
}

参考:

version 版本

  • 版本阶段
  • alpha 预览版:大量bug、内测、验证核心功能 v1.0.0-alpha
  • beta 测试版:性能问题(次要bug)、公测、收集用户反馈 v1.0.0-beta
  • rc 候选发布版:功能完善(不再新增特性)、少量bug(仅修复关键缺陷)、没有严重问题则转为正式版 v1.0.0-rc1
  • GA/stable(稳定版)/LTS(长期支持版) 正式发布版:功能完善、少量bug v1.0.0
  • 版本对比:
    • 1.0.0 主版本号.次版本号.补丁版本号
    • 主版本 >=1
      • ~1.1.2 匹配范围:>=1.1.2 <1.2.0(1.1.x)
      • ^1.1.2 匹配范围:>=1.1.2 <2.0.0(1.x.x)
      • 行为不一致
    • 主版本 = 0
      • ^0.1.2 匹配范围:>=0.1.2 <0.2.0(1.1.x)
      • ~0.1.2 匹配范围:>=0.1.2 <0.2.0(1.1.x)
      • 行为一致
    • 参考:语义化版本 2.0.0
  • 同时安装不同版本
    bash
    npm install vue2@npm:vue@^2.6.14
    npm install vue3@npm:vue@^3.2.37
    bash
    pnpm add @antv/x6-v2@npm:@antv/x6@^2.18.1 -D
    # 别名 @antv/x6-v2
    # 连接 @npm
    # 找对应包 @antv/x6@^2.18.1
    json
    {
      "devDependencies":{
        "@antv/x6-v2": "npm:@antv/x6@^2.18.1",
      }
    }

bin

  • pnpm dev 或者 yarn start
  • 启动脚本运行前会先自动新建一个命令行环境,然后把当前目录下的node_modules/.bin加入系统环境变量中,
  • 接着执行scripts配置节指定的脚本的内容,执行完成后再把node_modules/.bin从系统环境变量中删除。
  • 所以,当前目录下的node_modules/.bin子目录里面的所有脚本,都可以直接用脚本名调用,不必加上路径

workspaces

  • 从单个顶部级别的root软件包中管理多个软件包的支持。
  • 删除了手动使用NPM链接以将引用添加到应链接到当前Node_modules文件夹中的软件包中的需要。
  • 将文件夹软件包/*链接到当前工作dir的node_modules文件夹
  • 嵌套工作空间
json
{
  "workspaces": ["packages/a"]
}
bash
npm init -w ./packages/a # 在packages/a目录下初始化package.json 确保package.json中配置了workspaces
npm install abbrev -w a #给工作区a安装abbrev依赖
npm run test --workspace=a #特定工作区的上下文中运行给定命令 相当于 cd packages/a && npm run test
npm run test --workspace=packages
js
// ./packages/a/index.js
module.exports = 'a'
js
const moduleA = require('a')
console.log(moduleA) // -> a

imports

仅适用于软件包本身内部导入指定符的私有映射 “导入”字段中的条目必须始终以#开始,以确保它们与外部软件包指定符的歧义。

json
{
  "imports": {
    "#utils/*": "./src/utils/*",
    "#config": "./src/config.js",
    "#dep": { //“导入”字段允许映射到外部软件包。dep-node-native
      "node": "node-fetch-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "node-fetch-native": "^1.6.6"
  }
}
js
// 引入imports
const moduleB = require('#utils/index.js')
console.log(moduleB) // -> utils index

const moduleC = require('#config')
console.log(moduleC) // -> config

const dep = require('#dep')
console.log(dep) // -> fetch
// 引入exports
const moduleD = require('@play/npm/config')
console.log(moduleD) // -> config

exports

"exports" 提供了 "main" 的现代替代方案

  • 允许定义多个入口点
  • 支持不同环境

确保导出每个先前支持的入口点

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

支持导出全部文件

json
{
  "name": "es-module-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./features/private-internal/*": null, // 排除private-internal,不导出
    "./package.json": "./package.json"
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Throws: ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js

Subpath exports

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}
js
import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js
import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED

Subpath patterns#

json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}
js
// 项目自身,既可以引入imports,也可以引入exports
import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js
import featureY from 'es-module-package/features/y/y.js';
// Loads ./node_modules/es-module-package/src/features/y/y.js
import internalZ from '#internal/z.js';
// Loads ./node_modules/es-module-package/src/internal/z.js

有条件 exports

json
{
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.umd.cjs"
    }
  }
}

overrides

固定版本覆盖依赖版本

json
{
  "overrides": {
    "vxe-table": {
      "xe-utils": "3.5.30"
    }
  },
  "pnpm": {
    "overrides": {
      "foo": "^1.0.0",
      "quux": "npm:@myorg/quux@^1.0.0",
      "bar@^2.1.0": "3.0.0",
      "qar@1>zoo": "2" // qar@1>zoo will only override the zoo dependency of qar@1, not for any other dependencies.
    }
  }
}

参考:

peerDependencies

限定关联依赖版本

json
{
  "name": "@npm/tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "@npm/tea": "2.x" // 与@npm/tea@2.x版本兼容 一起使用
  }
}

peerDependenciesMeta

如何使用你的peerDependencies(how your peer dependencies are to be used)

json
{
  "name": "@npm/tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "@npm/tea": "2.x",
    "@npm/soy-milk": "1.2"
  },
  "peerDependenciesMeta": {
    "@npm/soy-milk": {
      "optional": true // 是否可选(是)
    }
  }
}

optionalDependencies

可选依赖包

  • 这表示项目依赖于其他包,但是不是必须的,
  • 如果用户安装了这些包,项目会使用这些包,
  • 如果没有安装,项目会正常运行。

对比 peerDependencies

  • 区别是 optionalDependencies 会直接被引用,
  • 而 peerDependencies 不会被直接引用。

patchedDependencies

  • pnpm patch express 给软件包添加补丁
  • 将express提取到一个可以随意编辑的临时目录中 ./node_modules/.pnpm_patches/express@4.18.1
  • 进行更改
  • 运行 pnpm patch-commit ./node_modules/.pnpm_patches/express@4.18.1
  • 并且生成一个 patches/express@4.18.1.patch 文件
diff
diff --git a/index.js b/index.js
index d219b0c878dc6136eb2096cffa140bf6bf2b8e9c..eb51bf969926d4b82e53633c28389fc2a1121530 100644
--- a/index.js
+++ b/index.js
@@ -8,4 +8,6 @@
 
 'use strict';
 
+console.log('test express patch')
+
 module.exports = require('./lib/express');
  • package.json 中增加 patchedDependencies 配置
json
{
  "pnpm": {
    "patchedDependencies": {
      // 其中键值是软件包名称与精确版本号。 值应该是一个指向修补文件的相对路径。
      "express@4.18.1": "patches/express@4.18.1.patch"
    }
  }
}
  • pnpm-workspace.yaml中 增加 patchedDependencies
yaml
patchedDependencies:
  express: patches/express.patch

publishConfig

发布配置 PNPM 对 publishConfig 进行了扩展1

json
{
  "publishConfig": {
    "registry": "https://registry.npmjs.org/",
    // 你还可以使用 publishConfig.directory 字段来自定义相对于当前 package.json 的发布子目录。
    // 在这个例子中 "dist" 文件夹必须包含一个 package.json
    "directory": "dist",
    // 当设置为 true 时,项目将在本地开发期间从 publishConfig.directory 位置进行符号链接。
    "linkDirectory": true
  }
}

sideEffects

所谓副作用就是会影响tree-shaking的文件

json
{
  "sideEffects": false // 是否开启副作用
}

副作用情况:

  • 注册全局事件监听器、修改全局状态等
  • 样式文件
  • Polyfill 设置值
  • false 表示项目的所有文件都没有副作用,可以进行 tree-shaking。
  • true 表示项目的所有文件都有副作用,不可以进行 tree-shaking。
  • ["*.css"] 表示项目的所有 css 文件都有副作用,不可以进行 tree-shaking。 问题:
  • lib的
    • 根目录下没有.css,其他目录下有.css:a/xx.css b/xx.css
    • package.json中配置了如下,
    json
    {
      "sideEffects": [ // css是副作用,保留
        "*.css"
      ],
    }
  • build之后,lib根目录下有xx.css,
  • 项目引入lib下的xx.css,发现无法引入模块了,但是lib下确实存在xx.css
  • 尝试给vite 配置,就可以了
    js
    {
      resolve: {
        alias: [
          {find: 'packageName', replacement: '/node_modules/packageName'}
        ],
      },
    }
  • 初步分析:
    • 路径解析受到影响
  • 修改sideEffects,打包,问题就没有出现了,解决
    json
    {
      "sideEffects": [
        "**/*.css"
      ],
    }

分析: 1、打包时的package.json和引入时的package.json是同一个

  • 打包时:
    • "sideEffects": ["*.css"]根目录下的*.css是有副作用的,"sideEffects": ["**/*.css"]是其他目录下也是有副作用的;
    • 修改sideEffects是否真的影响打包结果的(测试结果:应该有,副作用会起作用)
  • 引入时:
    • 是打包结果影响的呢(好像打包结果也差不多)?(测试结果:可能是,但是都有这个css文件,按道理说不会影响引用)
    • 还是修改了package.json的sideEffects配置,"sideEffects": ["*.css"]"sideEffects": ["**/*.css"],对路径解析分别有影响呢?(测试结果:不是,修改lib 的package.json不起作用)
  • 再测试一次, 测试结果:
    • 打包时:sideEffects影响了输出结果
    • 引入时:路径解析受到影响,更改lib的package.json的sideEffects,不能解决路径问题
    • 但是(几个疑点):
    • 1、通过pnpm link 本地的打包结果,又不存在这个问题(之前的所有引用时npm发布的backage引用的)
    • 2、重新下载下来发布的包,放到本地引用,确实是有这个问题,这说明这两个包有不同的
  • 进行详细对比结果
    • 1、发现 "./*.css": "./*.cssc", css多了一个c,真相大白,不是sideEffects影响的,误操作导致的
      json
      {
        "exports": {
          "./*.css": "./*.cssc",
        },
      }
    • 2、更改后,不存在路径解析不到的问题
    • "sideEffects": ["*.css"]根目录下的*.css是有副作用的,"sideEffects": ["**/*.css"]是其他目录下也是有副作用的;
    • 这里应该产生了影响,但其实都没有影响,副作用不会影响文件引用
  • 引入时:
    • "sideEffects": ["*.css"]"sideEffects": ["**/*.css"] 对路径解析分别有影响呢?
    • 这里不受影响

2、sideEffects到底什么用?详细分析一下

js
// utils/a.js
export function a() {
  console.log('a');
}
js
// utils/b.js
console.log('======== b.js ==========');
export function b() {
  console.log('b');
}
js
// utils/index.js
export * from './a';
export * from './b';
js
// app.js
import { a } from './utils';
a();
  • 以上测试代码,b模块(模块的副作用:模块执行的时候除了导出成员,其他的如:console.log('======== b.js ==========');就是副作用)
  • 进行tree shaking:
  • appjs没有加载b模块,tree shaking只能移除没有用到的代码成员,export ...
  • 但是b.js中的副作用代码console.log('======== b.js ==========');被保留了;
js
// output
([
  function(e, t, r) {
    'use strict';
    r.r(t),
      console.log('======== b.js =========='),
      console.log('a');
  },
])
  • 当b模块被加载时,我们希望执行我们副作用代码;
  • 因为整个模块没有被使用到,所以副作用代码也没有必要保留了;
  • 没必要保留副作用代码console.log('======== b.js ==========');去除需要sideEffects;
  • 注意:
    • 对全局有影响的副作用代码不能移除;
    • 只是对模块有影响的副作用代码就可以移除;
  • sideEffects:false 代表所有代码都没有副作用,就会将有导出export,没有加载,这种情况中的,副作用代码去掉;
  • 有些副作用是需要保留的:"sideEffects": ["./src/**/*.css"] 代表这些个.css是有副作用的,进行保留;

lock-file

npm

pnpm

packages snapshots

pnpm vs npm

特性pnpm (pnpm-lock.yaml)npm (package-lock.json)
文件格式YAMLJSON
文件大小一般更小,结构更紧凑较大,尤其是大依赖树下文件会变大
依赖记录方式显示完整依赖包路径并使用版本与依赖的精确映射(区分peer doppelgangers)结构以依赖树形式存储,自动扁平化依赖
工作区支持支持多包工作区,记录 importers 字段,管理多个项目仅支持单项目的锁定,不原生支持多包工作区
依赖安装模式使用硬链接或符号链接,复用缓存提升性能复制安装,依赖包可能多份存储
可读性相对更易读,YAML 格式利于手动查看和排查JSON 格式,信息丰富但相对臃肿
版本解析一致性严格锁定版本,适合多环境确定性安装版本锁定一致,但可能依赖扁平化影响具体结构
多版本同包共存支持 peer doppelgangers,允许同版本多实例支持多版本,但处理方式不同

.npmrc

  • .npmrc 文件是 npm 包管理器的配置文件,主要用来设置 npm 在运行时的各种行为和参数。
  • 这个文件采用类似 INI 格式的键值对,支持配置注册表地址、认证信息、代理设置、缓存策略、包安装行为等。
  • 它帮助开发者统一和定制项目或全局范围内的 npm 行为,

npm

ini
@myorg:registry=https://somewhere-else.com/myorg
@another:registry=https://somewhere-else.com/another
//registry.npmjs.org/:_authToken=MYTOKEN
; would apply to both @myorg and @another
//somewhere-else.com/:_authToken=MYTOKEN
; would apply only to @myorg
//somewhere-else.com/myorg/:_authToken=MYTOKEN1
; would apply only to @another
//somewhere-else.com/another/:_authToken=MYTOKEN2
ini
registry = https://registry.npmmirror.com/
@scope:registry = https://npm.scpoe.com/
//npm.scpoe.com/:_authToken=xxxxxx # 配置token 登录私有npm

pnpm

ini
strict-peer-dependencies = false # 如果启用了此选项,那么在依赖树中存在缺失或无效的对等依赖关系时,命令将执行失败。
save-workspace-protocol = rolling # workspace:*|^|~

save-workspace-protocol

save-workspace-protocolsave-prefixspec
false''1.0.0
false'~'~1.0.0
false'^'^1.0.0
true''workspace:1.0.0
true'~'workspace:~1.0.0
true'^'workspace:^1.0.0
rolling''workspace:*
rolling'~'workspace:~
rolling'^'workspace:^

其他

ini
# node-canvas 项目基于 node-pre-gyp 的约定配置
# https://github.com/mapbox/node-pre-gyp#download-binary-files-from-a-mirror
canvas_binary_host_mirror=https://xxxxxx.com
git-checks = false # 公司的私有工具链或者某些项目中自定义的 npm 脚本或者配置名