use .pnpmfile.cjs
to hoist deps/devDeps to project like
Nuxt Layer
✅ 10.x, 9.12+ Downloads(7-Days > 500K)
pnpm public-hoist-pattern only affects to the top-project (virtual store), not the sub-projects, therefore,
- relative path issues may occur
- copy same deps/devDeps from here to there
- config too many hoist-pattern
how to auto add the dep's deps/devDeps to my project? i.e. give your deps/devDeps to me when i deps on you.
e.g. mobile->common
, common->@nuxt
, after hoist layer,
├── common
│ ├── node_modules
│ │ ├── @nuxt -> ../.pnpm/...
└── mobile
├── node_modules
│ ├── @fessional
│ │ └── razor-common -> ../.pnpm/...
+ │ ├── @nuxt -> ../.pnpm/... // ✅ hoist as layer
When using catalog
in pnpm-workspace.yaml
, it is not easy to manage
the dependencies via pnpm add
, the recommended practice is to manually
edit the catalog
and the package.json
, and then run pnpm i
to
install the update, at this point, the following happens to pnpm-hoist-layer.
- make a temporary directory(tmpDir), and write the
.pnpmfile.cjs
hook. - start the sub-process,
pnpm -r i --resolution-only --lockfile-dir=tmpDir
- sub-process quickly resolves packages related to hoistLayer
- top-process parse stdout of sub-process as hoistLayer metadata
- top-process merges hoistLayer metadata via hooks
the hoistLayer metadata is 📝 hoist-layer.json
in the console,
[
{
"name": "hoist1",
"dependencies": {
"date-fns": "catalog:h1",
"lodash-es": "catalog:h1"
},
"devDependencies": {}
},
{
"name": "hoist2",
"dependencies": {
"date-fns": "catalog:h2",
"hoist1": "workspace:*",
"lodash-es": "catalog:h1"
},
"devDependencies": {},
"hoistLayer": [
"hoist1"
]
}
]
(1) add layer
to the package.json
-
hoistLayer
- to define which is the layer -
*dependencies
- for package resolution
"devDependencies": {
+ "@fessional/razor-common": "file:../common",
},
+ "hoistLayer": [
+ "@fessional/razor-common",
+ ]
(2) write .pnpmfile.cjs
to hook
## 💾 opt-1: project install and require
pnpm add -D pnpm-hoist-layer
cat > .pnpmfile.cjs << 'EOF'
const pnpmfile = {};
try {
pnpmfile.hooks = require('pnpm-hoist-layer').hooks;
} catch {
console.warn('⚠️ "pnpm-hoist-layer" not found, retry after installing.');
}
module.exports = pnpmfile;
EOF
## 📦 opt-2: write the content to .pnpmfile.cjs
curl -o .pnpmfile.cjs https://raw.githubusercontent.com/trydofor\
/pnpm-hoist-layer/main/index.js
the deps tree are resolved from top to bottom, and hoist from bottom to top, it's a reverse process.
- ✅ shared-workspace-lockfile=false, may 🐞 peers
- ✅ monorepo + shared-workspace-lockfile=false, but 🐞 default=true
- ✅ pnpm cli at top-dir, but 🐞 sub-dir (
packages/*
) - ✅
--resolution-only
resolvedevDependencies
, but ❗pnpm i
NOT. - ❗ do NOT use
link:
, it do NOT hook - ❗ do NOT deps indirectly , 2+ level deps NOT resolved
- ❗ this is a bad practice
manages nodejs
version by asdf
and pnpm
by corepack
git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.15.0
## config zsh
cat >> ~/.zshrc << 'EOF'
export ASDF_NODEJS_AUTO_ENABLE_COREPACK=true
export ASDF_NODEJS_LEGACY_FILE_DYNAMIC_STRATEGY=latest_installed
source "$HOME/.asdf/asdf.sh"
EOF
## support .nvmrc or .node-version
cat >> ~/.asdfrc << 'EOF'
legacy_version_file=yes
EOF
## install nodejs plugin
asdf plugin add nodejs
## install nodejs and corepack enable
asdf install nodejs
## by package.json and corepack
pnpm -v
## Corepack is about to download
## init workspace top-project first
pnpm -w i --ignore-pnpmfile
## init workspace sub-project
pnpm -r i
## to debug with env DEBUG != null
DEBUG=1 pnpm i
## ignore if error
pnpm i --ignore-pnpmfile --ignore-scripts
Glossary
├── multi pkg, one git (often called "monorepo")
│ ├── with workspace (termed as "mono")
│ └── without workspace (termed as "poly")
└── one pkg, one git (termed as "solo")
node -v #v20.16.0
pnpm -v #9.12.1
pnpm test
# ✅ Success mono1, npmrc={}
# ✅ Success mono1, npmrc={"shared-workspace-lockfile":false}
# ✅ Success mono2, npmrc={}
# ✅ Success mono2, npmrc={"shared-workspace-lockfile":false}
# ✅ Success poly1, npmrc={}
# ✅ Success poly2, npmrc={}
# ✅ Success hoist, npmrc={}
- hoist - hoist auto/manual testing
- mono1 - multi-pkg + workspace, sub
hoistLayer
- mono2 - multi-pkg + workspace, top
hoistLayer
- poly1 - multi-pkg, sub
hoistLayer
- poly2 - multi-pkg, top
hoistLayer
- solo - single pkg as deps for test
diff mono
from pnpm -r i --ignore-pnpmfile
to pnpm -r i
like this,
## pnpm -r list
Legend: production dependency, optional only, dev only
mono-test-0@1.0.0 pnpm-hoist-layer/test/mono/packages/pkg0
+ dependencies:
+ solo-prd-dep link:../../solo/prd
= devDependencies:
= mono-test-1 link:../pkg1
+ mono-test-2 link:../pkg2
+ solo-dev-dep link:../../solo/dev
mono-test-1@1.0.0 pnpm-hoist-layer/test/mono/packages/pkg1
+ dependencies:
+ solo-prd-dep link:../../solo/prd
= devDependencies:
= mono-test-2 link:../pkg2
+ solo-dev-dep link:../../solo/dev
mono-test-2@1.0.0 pnpm-hoist-layer/test/mono/packages/pkg2
= dependencies:
= solo-prd-dep link:../../solo/prd
= devDependencies:
= solo-dev-dep link:../../solo/dev
## tree -L 4
✅ mono
= ├── node_modules
= ├── package.json
= ├── packages
= │ ├── pkg0
= │ │ ├── node_modules
= │ │ │ ├── mono-test-1 -> ../../../node_modules/.pnpm/
+ │ │ │ └── mono-test-2 -> ../../pkg2
+ │ │ │ ├── solo-dev-dep -> ../../../solo/dev
+ │ │ │ ├── solo-prd-dep -> ../../../solo/prd
= │ │ └── package.json
= │ ├── pkg1
= │ │ ├── node_modules
= │ │ │ └── mono-test-2 -> ../../pkg2
+ │ │ │ ├── solo-dev-dep -> ../../../solo/dev
+ │ │ │ ├── solo-prd-dep -> ../../../solo/prd
= │ │ └── package.json
= │ └── pkg2
= │ ├── node_modules
= │ │ └── solo-dev-dep -> ../../../solo/dev
= │ │ ├── solo-prd-dep -> ../../../solo/prd
= │ └── package.json
= ├── pnpm-lock.yaml
= └── pnpm-workspace.yaml
diff poly
from pnpm -r i --ignore-pnpmfile
to pnpm -r i
like this,
## pnpm -r list
Legend: production dependency, optional only, dev only
poly-test-0@1.0.0 pnpm-hoist-layer/test/poly/packages/pkg0
+ dependencies:
+ solo-prd-dep link:../../solo/prd
= devDependencies:
= poly-test-1 file:../pkg1
+ poly-test-2 file:../pkg2
+ solo-dev-dep link:../../solo/dev
poly-test-1@1.0.0 pnpm-hoist-layer/test/poly/packages/pkg1
+ dependencies:
+ solo-prd-dep link:../../solo/prd
= devDependencies:
= poly-test-2 file:../pkg2
+ solo-dev-dep link:../../solo/dev
poly-test-2@1.0.0 pnpm-hoist-layer/test/poly/packages/pkg2
= dependencies:
= solo-prd-dep link:../../solo/prd
= devDependencies:
= solo-dev-dep link:../../solo/dev
## tree -L 4
✅ poly
= ├── package.json
= ├── packages
= │ ├── pkg0
= │ │ ├── node_modules
= │ │ │ ├── poly-test-1 -> .pnpm/
+ │ │ │ └── poly-test-2 -> .pnpm/
+ │ │ │ ├── solo-dev-dep -> ../../../solo/dev
+ │ │ │ ├── solo-prd-dep -> ../../../solo/prd
= │ │ ├── package.json
= │ │ └── pnpm-lock.yaml
= │ ├── pkg1
= │ │ ├── node_modules
= │ │ │ └── poly-test-2 -> .pnpm/
+ │ │ │ ├── solo-dev-dep -> ../../../solo/dev
+ │ │ │ ├── solo-prd-dep -> ../../../solo/prd
= │ │ ├── package.json
= │ │ └── pnpm-lock.yaml
= │ └── pkg2
= │ ├── node_modules
= │ │ ├── solo-dev-dep -> ../../../solo/dev
= │ │ └── solo-prd-dep -> ../../../solo/prd
= │ ├── package.json
= │ └── pnpm-lock.yaml
= └── pnpm-lock.yaml