跳至主要内容

[指令] npm cli & package.json

keywords: npm, cli, package.json

TL;DR

  • runtime 和 production 會用到這個套件 --> dependencies
  • 只有開發時會用到 --> devDependencies
  • 希望使用者自行安裝 --> peerDependencies
npm init -y                        # --yes, 接受所有預設值產生 package.json
npm init --scope=@pjchender -y # --scope, 專案屬於某個 scope 底下

npm ls # 列出該專案安裝的套件
npm ls | grep react # 只列出和 react 有關的套件
npm ls <package> # 可以檢視專案中有沒有使用到這個套件,以及版本號

npm install <package>@latest # latest stable release
npm install <package>@beta # latest beta release
npm install --verbose # 顯示安裝時的詳細資訊
npm install --production # 只安裝 dependencies,不會安裝 devDependencies
npm install --dev # 只會安裝 devDependencies,不會安裝 dependencies

npm view <package> # 檢視該套件有哪些版本
npm home <package> # 進到該套件的官網
npm repo <package> # 進到該套件的 github
npm docs <package> # 進到該套件的文件

npm run # 列出所有該 package 定義的指令
npm prune # 移除多餘沒用到的套件
npm doctor # 檢查 node, npm 版本,並檢驗有無足夠的權限安裝 dependencies
npm dedupe # npm ddp,移除重複的套件(duplicate packages)
npm outdated # 檢查版本過期的套件
npm outdated -l # --long
npm info "<pkg>@latest" peerDependencies # 列出該套件的所有 peerDependencies

npx <package> # 執行某個 package 內的指令
npx sort-package-json # 重新排序 package.json 的檔案(第三方)
npx install-peerdeps --dev <pkg> # 安裝某套件所需的所有 peerDependencies

預設的情況下 scope 的專案是 restricted,但這需要額外付費,因此可以透過設定把它改成 public,設定的方式可以參考這裡

npm init                        # 專案初始化(建立 package.json)

npm install # 根據 package.json 安裝所需要的檔案
npm install --save <packageName> # 套件會安裝在 dependencies 中(簡寫 -S)
npm install --save-dev <packageName> # 套件會安裝在 devDependencies 中(簡寫 -D)
npm install --global <packageName> # 套件會以全域的方式安裝(簡寫 -g)

npm install <packageName>@~1.14 # 安裝特定版本的模組
npm install <packageName>@latest # 安裝最新的穩定版
npm install <packageName>@next # 安裝最新的版本

npm uninstall <packageName> # 解除安裝〈簡寫 un〉

npm list # 列出所有已安裝的套件
npm update # 更新套件, -dd 顯示詳細資訊
npm prune # 根據 package.json 移除和專案無關的套件
npm outdated # 告訴你有哪些安裝的套件已經不是最新版了
sudo npm i -g npm # 更新 npm

npm -v # 查看 npm 版本
npm init # 初始化 package.json

使用 npm update 時,仍會依據 package.json 所定的版號作為依據,不會直接更新到最新版。

npm documentation @ npm

安裝模組參數說明

-g, --global 全局安裝(global)
-S, --save 安裝包信息將加入到dependencies(生產階段的依賴)
-D, --save-dev 安裝包信息將加入到devDependencies(開發階段的依賴),所以開發階段一般使用它
-O, --save-optional 安裝包信息將加入到optionalDependencies(可選階段的依賴)
-E, --save-exact 精確安裝指定模塊版本

開發套件相關

npm init                                # 建立 npm module (package)
npm ci # 在 CI 環境執行 npm install(確保相依套件是乾淨的)

npm adduser # 建立使用者
npm login # 登入使用者

npm config ls # 檢視本地端記錄了哪些註冊的使用者
npm publish # 上傳 package 到 npm(版本變動時上傳就是更新)
npm version <major | minor | patch> # 透過 cli 更新版本編號

npm deprecate <package-name> "<message>" # 不再維護某一專案
npm deprecate <package-name>@<version> "<message>" # 不再維護專案的某一版本
npm unpublish # 移除 npm 上的 package(需聯絡 npm)

# 將不繼續維護的的套件轉移給 npm 組織
npm owner add npm <package-name>
npm owner rm <user> <package-name>

# 將本地套件建立捷徑以方便測試
npm link # 進到要測試的套件中(例如 foo),執行後會在 global 產生一個捷徑
npm link foo # 進到要使用測試套件的專案中,執行 npm link <local_package>
npm ls -g depth 0 # 列出所有在 global 有被 link 的套件
npm rm <package-name> -g # 移除在 global 中被 link 的套件

npm ci 的作用和 npm install 類似,但它會

  • 依據 package-lock.json 中所定義的版號安裝套件
  • 移除 node_modules 以乾淨的環境重新安裝
  • 如果有錯誤的話,它不會改變 package.jsonpackage-lock.json 而是直接拋出錯誤

workspaces

npm 7 支援 workspaces 的功能,讓開發者可以從單一個上層的 root package 管理底下多個 packages。

定義 workspaces

只需要在 package.json 中使用 workspaces 這個欄位,裡面指的是 root packages 裡面的其他 packages:

// package.json
{
"name": "my-workspaces-powered-project",
"workspaces": ["workspace-a", "packages/*"]
}

workspaces 中套件安裝的位置

當我們的資料夾結構是:

.
├── packages
│   ├── bar
│   └── foo
└── workspace-a

. 執行 npm install 時,會在根目錄的 node_modules 內會多一個名為 workspace-afoobar 的捷徑,它會指向 ./workspace-apackages/foopackages/bar 的專案位置。像是這樣:

.
├── node_modules
│   ├── bar -> ../packages/bar
│   ├── foo -> ../packages/foo
│   └── workspace-a -> ../workspace-a
├── packages
│   ├── bar
│   └── foo
└── workspace-a

對於會重複在多個 packages 中使用到的套件,實際套件會安裝在根目錄的 ./node_modules 中。

直接把 workspaces 內的套件當成 package 匯入

由於會在根目錄的 node_modules 中建立捷徑,因此我們也可以直接把 workspaces 中定義好的套件 workspace-apackages/foopackages/bar 直接匯入。例如:

// ./workspace-a/index.js
module.exports = 'workspace-a';

// ./packages/foo/index.js
module.exports = 'foo';

// ./packages/bar/index.js
module.exports = 'bar';

匯入這些 workspaces 中的套件:

// ./index.js
const a = require('workspace-a'); // 'workspace-a'
const b = require('foo'); // 'foo'
const c = require('bar'); // 'bar'

// ./packages/foo/index.js
const bar = require('bar'); // 也可以直接匯入 'bar'

module.exports = 'foo';

在 root package 執行 npm 指令

在執行 npm 指令時,可以透過 --workspace="package-name" 這個 option 來執行 workspaces 內某 package 的指令;如果是想要一次執行 workspaces 內的所有 npm script,則可以使用 --workspaces(多 s)。例如,當執行:

# 只執行 workspace-a 裡的 npm script
$ npm run start --workspace=workspace-a
> [workspace-a] I am workspace-a

# 執行 workspace-a 和 bar 裡的 npm script
$ npm run start --workspace=workspace-a --workspace=bar
> [workspace-a] I am workspace-a
> [bar] I am bar

# 執行所有 workspaces,記得要加 s
$ npm start --workspaces

套件更新

How to Publish an Updated Version of an npm Package @ cloud 4

NPM Scripts and Package.json

# 使用 && 會依序執行各個指令
$ npm run build && npm run start

# 使用 & 會平行(parallel)執行多個指令
$ npm run build & npm run start

# 使用 -- 可以帶入參數
$ npm run foo -- "hello"
  • preinstall 會在所有套件安裝前執行
  • npm run env 列出 package 中所有環境變數,和在程式中使用 process.env.npm_package_name 類似
  • 在 package.json 中可以新增 config 欄位,來裡面定義的變數將可以透過 npm_package_config_foo 取得
// npm_package_config_foo=Hello World
"config": {
"foo": "Hello World"
},

npm init

keywords: npm create, npm innit

npm-init @ npmjs

npm init 一般可以用來初始化 npm 專案,最常見的應該是使用 npm init -y,也就是建立專案時,全部按 yes:

image-20220917170930351

但除此之外,還有一種方式是 npm init <initializer>,這麼做的話意思不太一樣,npm 會透過 npx(npm-exec) 來找到並執行名稱為 create-<initializer> 的專案,例如:

  • npm init foo 等同於 npx create-foo
  • npm init @usr/foo -> npx @usr/create-foo
  • npm init @usr -> npx @usr/create

透過這樣的邏輯,我們可以知道:

  • 除了使用 npx create-react-app my-app 外,也可以使用 npm init react-app my-app

npm exec

npm-exec @ npmjs

keywords: npx

npm exec 和 npx 的差別

當使用 npxnpm exec 來執行 binary 時,帶入參數的方式有些不同

  • 當使用 npx 時,可以直接在 binary 後帶入要給 binary 的參數
  • 當是用 npx exec 時,需要使用 -- 來分開那些是要給 npm 的參數,那些是要給 binary 的參數

具體來說:

# 如果我們希望執行的是
$ vite react-ts-vite --template react-ts

# 用 npx 要這樣寫
$ npx create-vite@latest react-ts-vite --template react-ts

# 用 npm exec 要這樣寫
$ npm exec create-vite@latest -- react-ts-vite --template react-ts

問題解決與錯誤處理

解決 package-lock.json 檔案內容異動的問題

$ rm -rf node_modules/
$ npm cache clean --force
(Revert the changes in your package-lock.json file) # 還原到原本的 package-lock.json
$ npm install

npm install downgrading resolved packages from https to http registry in package-lock.json

npm cache 位置

有些時候更新了 package.json 中的設定後(例如,browserslist),卻沒有立即產生對應的改變,這有可能是 babel-loader 沒有偵測到 package.json 中的變化,這時候可以刪除 node_modules/.cache 的檔案:

$ ls node_modules/.cache

package.json 範例

{
"name": "pkg-name",
"version": "0.0.1",
"scripts": {
"build": "webpack --optimize-minimize",
"start": "webpack-dev-server"
},

// https://nodejs.org/api/packages.html
// 除了用 main 也可以用 "exports" 來更精確描述不同 module system 要載入的檔案
// 但是一旦用了 exports 後,其他人就只能從有被定義 exports 的路徑載入檔案
"exports": {
".": {
// Entry-point for `import "my-package"` in ESM
"import": "./esm/index.js",
// Entry-point for `require("my-package") in CJS
"require": "./commonjs/index.cjs"
}

// 可以當作 export alias 使用,用的人只需 import "pkg-name/tailwind.config" 即可,不用進到 dist
"./tailwind.config": {
"import": "./dist/tailwind.config.js",
"require": "./dist/tailwind.config.cjs"
},
"./lib/ArrowTooltip": {
"import": "./dist/lib/ArrowTooltip/index.js",
"require": "./dist/lib/ArrowTooltip/index.cjs"
},

},
// 引用此 package 的進入點(CJS fall-back for older versions of Node.js)
"main": "./commonjs/index.cjs",

// 選擇 Node.js 要用 ES Module 或 CommonJS(預設)的 module system
"type": "module", // "module" 或 "commonjs"(預設)

// app 運作時會用到的 package
"dependencies": {
"jquery": "^3.1.0"
},

// 開發環境會用到的 package
"devDependencies": {
"webpack": "^1.13.1"
}
}

參考