前言
平時大家在公司接手一個已有專案的時候,首先會看的是什麼呢?我的習慣是先看 README.md
和 package.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 包時name
和version
這兩個欄位是必須的,兩個共同構成唯一的專案標識,透過更改 version
來對你的 npm 包進行釋出更新。
"version": "1.1.0"
通常推薦使用 semver 版本控制策略,開源專案也通常遵循這個語義化規範
版本格式:主版本號.次版本號.修訂號,版本號遞增規則如下:
- 主版本號 Major,當你做了不相容的 API 修改,通常在涉及重大功能更新,產生了破壞性變更時會更新此版本號
- 次版本號 Minor,引入了新功能,但未產生破壞性變更,依然可以向下相容時會更新此版本號,可理解為 Feature 版本
- 修訂號 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 版本規範的:
# 檢視 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 "
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 的這個地方:
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 個階段:
- 檢查 scripts 物件中是否存在 preinstall 命令,如果有,先執行該命令;
- 檢查是否有 install 命令,有的話執行 install 命令,沒有的話報錯;
- 檢查是否存在 postinstall 命令,如果有,執行 postinstall 命令
npm 預設提供下面這些鉤子。
prepublish,postpublish
preinstall,postinstall
preuninstall,postuninstall
preversion,postversion
pretest,posttest
prestop,poststop
prestart,poststart
prerestart,postrestart
另外,我們自定義的指令碼命令也可以加上 pre 和 post 鉤子。
下面舉例我工作中使用到 npm 鉤子的場景:
- 所有專案都由 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
- 第三方庫有 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"
},
"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.usedExports
和 sideEffects
共同承擔
- 透過設定
usedExports
為 true,表示模組只匯出被使用的成員,配合 terser 刪除專案所有模組中未被引用的匯出變數
module.exports = {
optimization: {
usedExports: true, // 作用於程式碼語句層面,只匯出(export)有使用的變數/方法
},
}
當然這個 webpack 打包生產環境預設開啟的
- 透過
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"
使用 types
或typings
都可以,作用相同
main
main
、browser
、module
三個欄位都是用於 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 使用,比如 unpkg
及 jsdelivr
。
也可以直接透過宣告 unpkg
及 jsdelivr
欄位來配置入口檔案,下面再細說這兩個欄位。
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"
如果一個專案同時定義了 main
,browser
和 module
,Webpack 等構建工具打包的時候會根據環境以及不同的模組規範來進行不同的入口檔案查詢。
// Vue:
"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",
總結 npm 包 main
、module
、browser
:
- 匯出包只在 web 端使用,並且禁止在 server 端使用,使用
browser
- 匯出包只在 server 端使用,使用
main
- 匯出 ESM 規範的包,使用
module
- 匯出包在 web 端和 server 端都允許使用,使用
module
和main
還有其他具體情況這裡就不展開了
exports
如果打包工具支援 exports
欄位,則該欄位優先順序最高,會忽略 main/browser/module
的配置。
比如同時使用 require
和 import
欄位定義模組規範入口:
"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 xxx
或 npm 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
開發依賴,專案開發環境需要用到而線上執行時不需要的依賴,用於輔助開發。
比如 webpack
、eslint
、jest
、TS @types/xxx
型別檔案等等。
當打包上線時並不需要這些包,所以可以把這些依賴新增到 devDependencies 中,這些依賴依然會在本地指定 npm install 時被安裝和管理,但是不會被安裝到生產環境中。
使用 npm install xxx -D
或者 npm install xxx --save-dev
時,npm 包就會自動插入到該欄位下。
peerDependencies
同伴依賴,防止包避免重複安裝,一般元件庫比較常見。
如 Ant Design 的配置:表示我們專案使用 antd
npm 包還需安裝 react
和 react-dom
,且版本都要>=16.9.0
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
peerDependenciesMeta
可選的同伴依賴。
"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
欄位
更多可瞭解: 前端依賴版本重寫指南