結合實踐解讀 package.json

JackySummer發表於2022-11-23

前言

平時大家在公司接手一個已有專案的時候,首先會看的是什麼呢?我的習慣是先看 README.mdpackage.json

透過 README 瞭解專案是做什麼和注意點,透過package.json瞭解專案涉及的技術棧和 npm 庫等等。

今天就來深入瞭解下package.json這個檔案,不僅是解釋詳細欄位含義與運用(忽略部分第三方欄位本文就不介紹了),更重要的是想借此擴充套件總結下涉及工作中與社群知名庫的一些實踐,對我們自己做開源專案也有一定幫助。

本文同步於我的 Github 個人部落格

常見配置欄位

name

專案的名稱,該欄位決定了你釋出的包在 npm 的名字,也就是平時安裝庫的包名了 npm i 包名

該欄位也是有命名規範的,如下:

  • 名稱的長度必須小於或等於 214 個字元,不能以._開頭。不要使用空格<>[]{}|\^% 等。
  • 不能使用大寫字母命名
  • 如果要釋出到 npm 上,名稱不能和社群已有的重複,可以使用npm view命令查詢,或直接上npm查。比如隨機想一個包名,jacky-summer-utils
npm view jacky-summer-utils

# 報 404,證明該名稱可用
npm ERR! code E404

除了常規命名,我們還會見到社群有@開頭的命名,格式如:@[scope]/[name]

例子:@ant-design/icons@babel/preset-env,這代表一個組織下的庫。

如果你也想使用這樣的結構,比如用自己名字@開頭的話,需要在自己 npm 上建立組織(Add Organization),不這麼做的話包名帶@則釋出時會不透過。

比如我建立了個組織 @summer-toolkit,包名是config,則我的這個包和name值就命名為 @summer-toolkit/config

"name": "@summer-toolkit/config"

version

專案版本號,當釋出 npm 包時nameversion這兩個欄位是必須的,兩個共同構成唯一的專案標識,透過更改 version 來對你的 npm 包進行釋出更新。

"version": "1.1.0"

通常推薦使用 semver 版本控制策略,開源專案也通常遵循這個語義化規範

版本格式:主版本號.次版本號.修訂號,版本號遞增規則如下:
  1. 主版本號 Major,當你做了不相容的 API 修改,通常在涉及重大功能更新,產生了破壞性變更時會更新此版本號
  2. 次版本號 Minor,引入了新功能,但未產生破壞性變更,依然可以向下相容時會更新此版本號,可理解為 Feature 版本
  3. 修訂號 Patch,修復了一些 bug,也未產生破壞性變更時會更新此版本號,可理解為 Bug Fix

當要釋出大版本或者核心的功能時,無法確定該版本功能穩定,可能無法滿足預期的相容性需求,這個時候就需要透過釋出先行版本。先行版本透過會加在版本號的後面,透過-號連線以.分隔的識別符號和版本編譯資訊:

  • 內部版本(alpha):通常該版本軟體的 Bug 較多,需要持續修改
  • 公測版本(beta):相對於 alpha 版已有很大改進,會繼續加新特性,修復了較嚴重的錯誤,但依然存在一些缺陷
  • 候選版本(rc,即 release candiate):正式版本的候選版本,該版本一般可以說錯誤較少了,基本是修復而不是再加新特性了
react:16.3.0-alpha.0
vue:3.2.34-beta.1、3.0.0-rc.13

比如想檢視 vue 的歷史 Tag 版本,可看到 vue 是比較嚴格遵循 semver 版本規範的:

image.png

# 檢視 vue 歷史版本
npm view vue versions
# 檢視 vue 最新版本
npm view vue version

一些問題解答:

在庫初始開發階段,該如何進行版本控制?

最簡單的做法是以 0.1.0 作為你的初始化開發版本,並在後續的每次發行時遞增次版本號。當最終完成用於正式線上環境了,這時就可以改為 1.0.0,之後就都按照 Semver 規範走了

公司一些較小的公共庫版本,怎麼維護?

這個可視乎庫規模和使用人數多少決定,像一般公司大部分私庫測試環境都可直接釋出 beta 版本,等 UAT/線上就是直接走的正式版本

description

專案的描述,資訊會直接展示在 npm 官網,透過它能讓別人能快速你的專案。

dayjs庫的:

"description": "2KB immutable date time library alternative to Moment.js with the same modern API "

image.png

Webpack 的描述就相對長一些了:

"description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",

keywords

專案關鍵詞。關鍵詞填得準,可方便在 npm 上更好地檢索

如 Ant Design 的 keywords

"keywords": [
    "ant",
    "component",
    "components",
    "design",
    "framework",
    "frontend",
    "react",
    "react-component",
    "ui"
],

Redux 的:

"keywords": [
    "redux",
    "reducer",
    "state",
    "predictable",
    "functional",
    "immutable",
    "hot",
    "live",
    "replay",
    "flux",
    "elm"
],

repository

專案的倉庫地址以及版本控制資訊,可透過該欄位找到程式碼託管地址

// Ant Design
"repository": {
    "type": "git",
    "url": "https://github.com/ant-design/ant-design"
}

license

專案的開源許可證,說明你的庫使用哪個許可證,使用者保護你自己和貢獻者。

沒有 License 的內容是預設會被版權保護,如果你允許社群內開發者可基於你的專案二次改造等,需要選擇合適的許可證才能賦予任何人放心使用(改造、分享等)。

目前 Github 我們熟知的大部分專案都是 MIT 許可證

 "license": "MIT"
MIT 基本就是沒有任何限制,表示任何人都可以售賣我的軟體,甚至可以用作者的名字促銷。
專案的版權擁有人可以使用開源許可證來限制原始碼的使用、複製、修改和再發布等行為,

通常開源專案都會在根目錄下新建 LICENSE 檔案,並將許可證的文字複製到這裡。如看下 Vue

對於公司不開源的專案,這個配置項一般可忽略。

瞭解詳細可閱讀:

private

設定為私有防止意外發布。如果你不希望把專案釋出到 npm 倉庫上,可以將 private 設為 true。

"private": true

當設定為 true 時,執行npm publish會報錯,npm 會拒絕釋出這個專案。

像公司的非開源專案就可以設定為 true,防止被無意間釋出出去

publishConfig

npm 包釋出使用的配置,通常publishConfig會配合private使用;如果只想讓模組釋出到特定 npm 倉庫,就可以這樣來配置

"publishConfig": {
    "registry": "https://registry.npmjs.org", // 私源地址配置
    "tag": "beta", // 如果沒有指定tag,預設是 latest
    "access": "public"
}
  • registry:npm 私源地址
  • tag:指定當前版本對應的 tag
  • access:包的訪問級別。如果是 scoped 包(如 @babel/xxx,@ant-design/xxx),一定需要設定為 public(除非加入付費計劃)

bugs

bug 反饋地址,透過該連結向你的倉庫反饋 bug,github 上的一般都是 github issue 頁的連結

"bugs": "https://github.com/webpack/webpack/issues",

homepage

專案主頁地址,如:倉庫 Github 連結、官網地址、文件地址等

// Ant Design:
"homepage": "https://ant.design"

// Redux:
"homepage": "http://redux.js.org"

homepage欄位會展示在 npm 的這個地方:

image.png

homepage 還可設定應用的根路徑,詳細可見【小技巧】package.json 中 homepage 屬性的作用

author

專案的作者(資訊)

  • 字串形式
"author": "JackySummer"
  • 物件形式:包含必選的 name 屬性和可選的 url、email 屬性
"author": {
    "name": "JackySummer",
    "email": "xxxxx@qq.com",
    "url": "https://jackylin.vercel.app"
}

也可以用下面的字串簡寫,npm 會幫我們解析:

"author": "JackySummer <xxxxx@qq.com> (https://jackylin.vercel.app)"

contributors

貢獻者資訊。物件格式陣列,物件的內容和author一樣

"contributors": [{
    "name": "JackySummer",
    "email": "xxxxx@qq.com",
    "url": "https://jackylin.vercel.app"
}]

通常對應的,Github 開源專案通常會有 /.github/CONTRIBUTING.md貢獻指南這個檔案,裡面也可能有列出一些貢獻者簡要資訊等,如 Vue 的 CONTRIBUTING.md

scripts

專案內建的指令碼命令,這一欄位下的東西是我比較感興趣的,通常可以看到專案的啟動方式、prettier/eslint 執行指令碼,是否用了單元測試等等...

找到專案的/node_modules/.bin目錄,專案 npm run xx的底層呼叫命令在這裡都能找到

npm 指令碼的原理:每當執行 npm run,就會自動新建一個 Shell,在這個 Shell 裡面執行指定的指令碼命令。因此,只要是 Shell(一般是 Bash)可以執行的命令,就可以寫在 npm 指令碼里面

比如我們安裝了 eslint,scripts裡面配置:

"eslint": "eslint --fix --ext .ts,.tsx ."

而不必寫成路徑的方式

"eslint": "./node_modules/.bin/eslint --fix --ext .ts,.tsx .",

npm scripts 鉤子

npm 指令碼有 pre 和 post 兩個鉤子,如 執行 npm run install 的時候,分 3 個階段:

  1. 檢查 scripts 物件中是否存在 preinstall 命令,如果有,先執行該命令;
  2. 檢查是否有 install 命令,有的話執行 install 命令,沒有的話報錯;
  3. 檢查是否存在 postinstall 命令,如果有,執行 postinstall 命令

npm 預設提供下面這些鉤子。

prepublish,postpublish
preinstall,postinstall
preuninstall,postuninstall
preversion,postversion
pretest,posttest
prestop,poststop
prestart,poststart
prerestart,postrestart

另外,我們自定義的指令碼命令也可以加上 pre 和 post 鉤子。

下面舉例我工作中使用到 npm 鉤子的場景:

  1. 所有專案都由 npm 遷移 pnpm,但有的人沒注意或忘了,拿到專案還是使用 npm 安裝,也產生了 package-lock.json 檔案

解決方案:在 npm install 之前限制住只能使用 pnpm 安裝。.gitignore 新增 npm 和 yarn 的 lock 檔案

"scripts": {
    "preinstall": "npx only-allow pnpm"
}

.gitignore

yarn.lock
package-lock.json
  1. 第三方庫有 Bug,如何臨時緊急修復

這個以前遇過,年代久遠了舉不出例子來了,找了別人的文章可以瞭解下:

patch-package 和 postinstall :【 非常實用】如何優雅地解決 npm 依賴 bug

瞭解更多 npm scripts 可參考學習:npm scripts 使用指南


建議 script 有約定俗成的規範指令碼命令,提高可讀性與降低維護成本

來看下一些開源專案的 scripts 配置:

Vue

 "scripts": {
    "dev": "rollup -w -c scripts/config.js --environment TARGET:full-dev",
    "dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:runtime-cjs-dev",
    "dev:esm": "rollup -w -c scripts/config.js --environment TARGET:runtime-esm",
    "dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:server-renderer",
    "dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:compiler ",
    "build": "node scripts/build.js",
    "build:ssr": "npm run build -- runtime-cjs,server-renderer",
    "build:types": "rimraf temp && tsc --declaration --emitDeclarationOnly --outDir temp && api-extractor run && api-extractor run -c packages/compiler-sfc/api-extractor.json",
    "test": "npm run ts-check && npm run test:types && npm run test:unit && npm run test:e2e && npm run test:ssr && npm run test:sfc",
    "test:unit": "vitest run test/unit",
    "test:ssr": "npm run build:ssr && vitest run server-renderer",
    "test:sfc": "vitest run compiler-sfc",
    "test:e2e": "npm run build -- full-prod,server-renderer-basic && vitest run test/e2e",
    "test:transition": "karma start test/transition/karma.conf.js",
    "test:types": "npm run build:types && tsc -p ./types/tsconfig.json",
    "format": "prettier --write --parser typescript \"(src|test|packages|types)/**/*.ts\"",
    "ts-check": "tsc -p tsconfig.json --noEmit",
    "ts-check:test": "tsc -p test/tsconfig.json --noEmit",
    "bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
    "release": "node scripts/release.js",
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
  },

dayjs

 "scripts": {
    "test": "TZ=Pacific/Auckland npm run test-tz && TZ=Europe/London npm run test-tz && TZ=America/Whitehorse npm run test-tz && npm run test-tz && jest",
    "test-tz": "date && jest test/timezone.test --coverage=false",
    "lint": "./node_modules/.bin/eslint src/* test/* build/*",
    "prettier": "prettier --write \"docs/**/*.md\"",
    "babel": "cross-env BABEL_ENV=build babel src --out-dir esm --copy-files && node build/esm",
    "build": "cross-env BABEL_ENV=build node build && npm run size",
    "sauce": "npx karma start karma.sauce.conf.js",
    "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2  && npm run sauce -- 3",
    "size": "size-limit && gzip-size dayjs.min.js"
  },

config

用於新增命令列的環境變數,即我們指令碼在執行時的引數。

"config" : { "port" : "8080" },
"scripts" : { "start" : "node server.js" }

在 server.js 指令碼就可以引用 config 欄位的值

http
  .createServer(...)
  .listen(process.env.npm_package_config_port) // 8080

執行 npm run start 命令時,這個指令碼就可以得到值。

使用者可以改變這個值

npm config set foo:port 80

這個欄位沒用過,實戰中這些配置一般不會直接寫在 package.json 檔案裡面

例子引用自 config 欄位

engines

宣告專案需要在怎樣的 node 環境下執行,曾遇過比較老舊的專案,甚至可能要降級 node 版本才能跑起來。這時可以給專案新增欄位,說明哪個版本下執行能跑起來專案,當然你現在正維護開發的專案也推薦補充下。

"engines": {
    "node": ">=14.16.0",
    "pnpm": ">=6 <7"
},

engines只是起一個說明的作用,即使版本不符合要求也絲毫不影響安裝使用。

但如果真的遇上對 node 版本要求比較嚴格的專案,就可以在 .npmrc檔案設定

engine-strict = true

這時本地 node 版本不匹配的話就會報錯 Unsupported engine,要求你必須切換engines配置的對應版本才能正常安裝

workspaces

monorepo 專案的工作區配置,用於在本地的根目錄下管理多個子專案,可以自動地在 npm install 時將 workspaces 下面的包,軟鏈到根目錄的 node_modules 中,而不用手動執行 npm link 操作

workspaces 欄位接收一個陣列,陣列裡可以是資料夾名稱或者萬用字元,通常子專案都會管理在 packages 目錄下。比如:

  "workspaces": [
    "packages/*"
  ]

Babel 的配置:

  "workspaces": [
    "codemods/*",
    "eslint/*",
    "packages/*",
    "test/esm",
    "test/runtime-integration/*",
    "benchmark"
  ],

files

專案釋出時包含的檔案,配置格式為陣列,可以指定單獨的檔案或整個資料夾。

如 Ant Design:

"files": [
    "dist",
    "lib",
    "es"
]

宣告files欄位後,當該 npm 包被安裝時,安裝的是files欄位指定的內容,從而做到精準控制釋出內容,也控制了 npm 包大小。

如果有不想提交的檔案,可以在專案根目錄中新建一個.npmignore檔案進行配置,宣告不需要提交的檔案;寫在這裡的檔案即使 files 宣告瞭也不會被提交,這是為了防止垃圾無用檔案被推到 npm 上

.vscode

sideEffects

宣告設定哪些模組具有副作用,讓打包工具知道你的模組是否是“純”的以此更好的 Tree Shaking。

首先要知道什麼會讓一個模組有副作用?

例如修改一個全域性變數,傳送 API 請求,或匯出 CSS,是否重寫了原生物件方法,總結起來就是函式可能對外部產生影響的行為

window.foo = 'global foo'

Tree Shaking 怎麼最佳化的

Webpack 的 Tree Shaking 機制由 optimization.usedExportssideEffects 共同承擔

  1. 透過設定 usedExports 為 true,表示模組只匯出被使用的成員,配合 terser 刪除專案所有模組中未被引用的匯出變數
module.exports = {
  optimization: {
    usedExports: true, // 作用於程式碼語句層面,只匯出(export)有使用的變數/方法
  },
}

當然這個 webpack 打包生產環境預設開啟的

  1. 透過 package.json 配置的 sideEffects,用於標記整個模組的副作用。

sideEffects 設為 false,表示沒有任何模組具有副作用,所有模組都是"純"的,即打包的時候不管它是否有沒有副作用,只要它沒有被引用,整個模組/包都會被完整的移除

"sideEffects": false

也可以使用字串陣列來列出哪些檔案具有副作用

如 Ant Design 的sideEffects宣告如下 ,告知這些檔案有副作用,引入後不能被刪除

"sideEffects": [
    "dist/*",
    "es/**/style/*",
    "lib/**/style/*",
    "*.less"
],

type

定義 package.json 檔案和及其所在目錄根目錄中.js檔案和沒有擴充名檔案的處理方式

預設值為 commonjs

"type": "commonjs"

比如我們在專案根目錄下新建兩個檔案:

// test.js
export const foo = 10

// index.js
import { foo } from './test.js'

console.log(foo)

然後在控制檯執行node index.js,這時會報錯

SyntaxError: Cannot use import statement outside a module

因為 Node 的模組化方案採用的是 CommonJS,而程式碼是 ES 語法,所以報錯;

而 Node 在 v13.2.0 已開始正式支援 ES Modules 特性,我們可在 package.json 設定:

"type": "module"

這樣子 Node 就會用 ES 規範進行解析,重新執行就不會報錯了

需要注意的是,無論package.json中的type欄位設定為何值,只要檔案字尾是 .mjs的檔案都依然按照 ES 模組規範來處理, .cjs的檔案都按照 CommonJS 模組規範來處理,不會受 type影響

types/typings

對外暴露相關的型別,指定 TypeScript 的型別定義的入口檔案,前提是專案是用 TypeScript 寫的,用於方便 IDE 識別與智慧提示。

"typings": "types/index.d.ts"

使用 typestypings都可以,作用相同

main

mainbrowsermodule三個欄位都是用於 npm 包的,如果專案不是作為 npm 包釋出,這三個欄位不需要寫。

main 欄位指定載入的入口檔案,指向一個相容 CommonJS 格式的產出,這個檔名是專案作為 npm 包被打包時配置的,在 browser 和 node 環境中都可以使用。如使用require的方式匯入該 npm 包時,返回的就是main欄位指定檔案的module.exports屬性。

當不設定該欄位時,預設值是根目錄下的index.js 檔案

"main": "./index.js"
// Vue
"main": "dist/vue.runtime.common.js"

browser

指向支援 UMD 模組的入口檔案,這個欄位也會被一些公共 CDN 使用,比如 unpkgjsdelivr
也可以直接透過宣告 unpkgjsdelivr 欄位來配置入口檔案,下面再細說這兩個欄位。

UMD:相容 CommonJS 和 AMD 的模組,既可以在 node 環境中被 require 引用,也可以在瀏覽器中直接用 CDN 被script標籤 引入

unpkg 和 jsDelivr 是開源 CDN 服務

main 欄位裡指定的入口檔案在 browser 和 node 環境中都可以使用。如果只想在 web 端使用,禁止在 server 端使用,可以透過 browser 欄位指定入口。

"browser": "./browser/index.js"

module

  • module 指向支援 ESM 模組的入口檔案,browser 環境和 node 環境均可使用
"module": "./index.mjs"

如果一個專案同時定義了 mainbrowsermodule,Webpack 等構建工具打包的時候會根據環境以及不同的模組規範來進行不同的入口檔案查詢。

// Vue:
"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",

總結 npm 包 mainmodulebrowser

  • 匯出包只在 web 端使用,並且禁止在 server 端使用,使用 browser
  • 匯出包只在 server 端使用,使用 main
  • 匯出 ESM 規範的包,使用 module
  • 匯出包在 web 端和 server 端都允許使用,使用 modulemain

還有其他具體情況這裡就不展開了

exports

如果打包工具支援 exports 欄位,則該欄位優先順序最高,會忽略 main/browser/module的配置。

比如同時使用 requireimport 欄位定義模組規範入口:

"exports": {
  "require": "./index.js",
  "import": "./index.mjs"
 }
}

// 上面寫法還等同於
"exports": {
  // 這裡路徑宣告在根目錄下,因為還支援配置包的子路徑
  ".": {
    "require": "./index.js",
    "import": "./index.mjs"
  }
 }
}

更多 exports 文件可看 NodeJS 文件說明

// Vue 的配置:
"exports": {
    ".": {
      "import": {
        "node": "./dist/vue.runtime.mjs",
        "default": "./dist/vue.runtime.esm.js"
      },
      "require": "./dist/vue.runtime.common.js",
      "types": "./types/index.d.ts"
    },
    "./compiler-sfc": {
      "import": "./compiler-sfc/index.mjs",
      "require": "./compiler-sfc/index.js"
    },
    "./dist/*": "./dist/*",
    "./types/*": "./types/*",
    "./package.json": "./package.json"
},

unpkg/jsdelivr

讓 npm 上所有的檔案都開啟 cdn 服務。注意檔案需要是 UMD 模組規範格式

"unpkg": "dist/redux.js",

使用unpkg/jsdelivr欄位可以讓 npm 上所有的檔案都開啟 cdn 服務,比如當我們從 CDN 訪問使用 Redux時( https://unpkg.com/redux),

會重定向到 https://unpkg.com/redux@4.2.0/dist/redux.js (取最新Redux版本)獲取檔案

dependencies

執行依賴,專案線上環境下需要用到的依賴。
使用 npm install xxxnpm install xxx --save 時,npm 包就會自動插入到該欄位下。

"dependencies": {
  "react": "18.2.0",
  "react-dom": "18.2.0",
  "@ant-design/icons": "4.7.0",
}

在安裝之前就可以判斷 npm 包是否需要線上上執行,如不需要則不要放到該欄位下。

在我經歷過的來看,建議一些重要且穩定的庫鎖死版本,防止意外升級導致生產 bug,因為升級庫的次版本,也有機率出現較嚴重 Bug。

"antd": "4.21.0",

找了篇文章可以看下:如何管理 npm 版本號:語義化版本策略 SemVer

devDependencies

開發依賴,專案開發環境需要用到而線上執行時不需要的依賴,用於輔助開發。

比如 webpackeslintjest、TS @types/xxx型別檔案等等。

當打包上線時並不需要這些包,所以可以把這些依賴新增到 devDependencies 中,這些依賴依然會在本地指定 npm install 時被安裝和管理,但是不會被安裝到生產環境中。

使用 npm install xxx -D 或者 npm install xxx --save-dev 時,npm 包就會自動插入到該欄位下。

peerDependencies

同伴依賴,防止包避免重複安裝,一般元件庫比較常見。

如 Ant Design 的配置:表示我們專案使用 antd npm 包還需安裝 reactreact-dom,且版本都要>=16.9.0

"peerDependencies": {
    "react": ">=16.9.0",
    "react-dom": ">=16.9.0"
},

peerDependenciesMeta

可選的同伴依賴

舉例:react-redux package.json

 "peerDependencies": {
    "@types/react": "^16.8 || ^17.0 || ^18.0",
    "@types/react-dom": "^16.8 || ^17.0 || ^18.0",
    "react": "^16.8 || ^17.0 || ^18.0",
    "react-dom": "^16.8 || ^17.0 || ^18.0",
    "react-native": ">=0.59",
    "redux": "^4"
  },
  "peerDependenciesMeta": {
    "@types/react": {
      "optional": true
    },
    "@types/react-dom": {
      "optional": true
    },
    "react-dom": {
      "optional": true
    },
    "react-native": {
      "optional": true
    },
    "redux": {
      "optional": true
    }
  },

上述指定了 5 個 npm 包在peerDependenciesMeta中,表示都為可選項,所以只安裝任意幾個可能不會報錯,當然也不需要全部安裝,因為這裡明顯看出這是區分 native 和 web 兩個環境的,使用的話就任選只在一種環境下。

bin

指定各個內部命令對應的可執行檔案的位置,指定了 bin 欄位的 npm 包,如果被全域性安裝,就會被載入到全域性環境中,可以透過別名來執行該檔案。如帶有工具性質的 npm 包。

// webpack
"bin": {
    "webpack": "bin/webpack.js"
},

// eslint
"bin": {
    "eslint": "./bin/eslint.js"
},
node_modules/.bin/目錄下的命令,都可以用 npm run [命令] 的格式執行

拿 eslint 舉例,eslint 命令對應的可執行檔案為 bin 子目錄下的 eslint.js。npm 會在node_modules/.bin/目錄下建立符號連結,由於node_modules/.bin/目錄會在執行時加入系統的 PATH 變數,所以在執行 npm 時,就可以不帶路徑,直接透過命令來呼叫這些指令碼。

"eslint": "./node_modules/bin/eslint.js --fix --ext .ts,.tsx"

// 簡寫為
"eslint": "eslint --fix --ext .ts,.tsx"

overrides

重寫專案依賴的依賴,及其依賴樹下某個依賴的版本號,進行包的替換,支援任意深度的巢狀。。

這個拿我遇過的問題來解釋,比如ts-ebml庫,我們在專案中已經鎖死了版本,但是有次發現 lock 檔案更新了,當然它還是保持原來版本不變,但是它的子依賴matroska被升級了小版本導致 bug。

由於matroska這個庫的場景在我們專案只有一處地方,為了解決這個問題,就需要把matroska版本鎖死,恢復到之前沒有 bug 的版本,所以可以用該欄位

"overrides": {
  "matroska": "2.2.3"
}

此時查詢 lock 檔案依賴樹,會發現 lock 檔案全部被安裝為我們指定的版本/matroska/2.2.3,當然你也可以針對單獨的庫進行版本重寫,如下:

"overrides": {
  "ts-ebml": {
    "matroska": "2.2.3"
  }
}
  • pnpm 需要使用欄位:

    "pnpm": {
      "overrides": {
      }
    },
  • yarn 需要使用 resolution 欄位

更多可瞭解: 前端依賴版本重寫指南

參考

相關文章