pnpm-hoist-layer v1.2.0
šŖ pnpm-hoist-layer
use .pnpmfile.cjs to hoist deps/devDeps to project like
Nuxt Layer
Purpose
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 layerMechanism
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.cjshook. - 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"
]
}
]Usage
(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'
module.exports = require('pnpm-hoist-layer');
EOF
## š¦ opt-2: write content to .pnpmfile.cjs
curl -o .pnpmfile.cjs https://raw.githubusercontent.com/trydofor\
/pnpm-hoist-layer/main/index.js
## š opt-3: global install and require
pnpm add -g pnpm-hoist-layer
cat > .pnpmfile.cjs << 'EOF'
module.exports = (() => {
try {
return require('pnpm-hoist-layer');
}
catch {
const gr = require('child_process').execSync('pnpm root -g').toString().trim();
return require(require('path').join(gr, 'pnpm-hoist-layer'));
}
})();
EOFKnown Issues
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-onlyresolvedevDependencies, but āpnpm iNOT. - ā do NOT use
link:, it do NOT hook - ā do NOT deps indirectly , 2+ level deps NOT resolved
- ā this is a bad practice
Useful Commands
## 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
## asdf nodejs+pnpm, should disable corepack
export PNPM_HOME="$(asdf where pnpm)"
pnpm -g add pnpm-hoist-layer
## only corepack
corepack enable pnpm
corepack use pnpm@latestTest and Diff
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
Mono before and after
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.yamlPoly before and after
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