package.json
The manifest file of a package. It contains all the package's metadata, including dependencies, title, author, et cetera. This is a standard preserved across all major Node.JS package managers, including pnpm.
engines
You can specify the version of Node and pnpm that your software works on:
{
"engines": {
"node": ">=10",
"pnpm": ">=3"
}
}
During local development, pnpm will always fail with an error message
if its version does not match the one specified in the engines
field.
Unless the user has set the engine-strict
config flag (see .npmrc), this
field is advisory only and will only produce warnings when your package is
installed as a dependency.
dependenciesMeta
Additional meta information used for dependencies declared inside dependencies
, optionalDependencies
, and devDependencies
.
dependenciesMeta.*.injected
If this is set to true
for a local dependency, the package will be hard linked to the virtual store (node_modules/.pnpm
) and symlinked from the virtual store to the modules directory.
If this is set to false
or not set for a local dependency, the package will be symlinked directly from its location in the workspace to the module directory.
For instance, the following package.json
in a workspace will create a symlink to button
in the node_modules
directory of card
:
{
"name": "card",
"dependencies": {
"button": "workspace:1.0.0"
}
}
But what if button
has react
in its peer dependencies? If all projects in the monorepo use the same version of react
, then no problem. But what if button
is required by card
that uses react@16
and form
with react@17
? Without using inject
, you'd have to choose a single version of react
and install it as dev dependency of button
. But using the injected
field you can inject button
to a package, and button
will be installed with the react
version of that package.
So this will be the package.json
of card
:
{
"name": "card",
"dependencies": {
"button": "workspace:1.0.0",
"react": "16"
},
"dependenciesMeta": {
"button": {
"injected": true
}
}
}
button
will be hard linked into the dependencies of card
, and react@16
will be symlinked to the dependencies of card/node_modules/button
.
And this will be the package.json
of form
:
{
"name": "form",
"dependencies": {
"button": "workspace:1.0.0",
"react": "17"
},
"dependenciesMeta": {
"button": {
"injected": true
}
}
}
button
will be hard linked into the dependencies of form
, and react@17
will be symlinked to the dependencies of form/node_modules/button
.
In contrast to normal dependencies, injected ones are not symlinked to the destination folder, so they are not updated automatically, e.g. after running the build script. To update the hard linked folder contents to the latest state of the dependency package folder, call pnpm i
again.
Note that the button
package must have any lifecycle script that runs on install in order for pnpm
to detect the changes and update it. For example, the package can be rebuilt on install: "prepare": "pnpm run build"
. Any script would work, even a simple unrelated command without side effects, like this: "prepare": "pnpm root"
.