pnpm fetch

Fetch packages from a lockfile into virtual store, package manifest is ignored.

Usage scenario

This command is specifically designed to improve building a docker image.

You may have read the official guide to writing a Dockerfile for a Node.js app, if you haven’t read it yet, you may want to read it first.

From that guide, we learn to write an optimized Dockerfile for projects using pnpm, which looks like

  1. FROM node:14
  2. RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
  3. # Files required by pnpm install
  4. COPY .npmrc package.json pnpm-lock.yaml .pnpmfile.cjs ./
  5. # If you patched any package, include patches before install too
  6. COPY patches patches
  7. RUN pnpm install --frozen-lockfile --prod
  8. # Bundle app source
  9. COPY . .
  10. EXPOSE 8080
  11. CMD [ "node", "server.js" ]

As long as there are no changes to .npmrc, package.json, pnpm-lock.yaml, .pnpmfile.cjs, docker build cache is still valid up to the layer of RUN pnpm install --frozen-lockfile --prod, which cost most of the time when building a docker image.

However, modification to package.json may happen much more frequently than we expect, because it does not only contain dependencies, but may also contain the version number, scripts, and arbitrary configuration for any other tool.

It’s also hard to maintain a Dockerfile that builds a monorepo project, it may look like

  1. FROM node:14
  2. RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
  3. # Files required by pnpm install
  4. COPY .npmrc package.json pnpm-lock.yaml .pnpmfile.cjs ./
  5. # If you patched any package, include patches before install too
  6. COPY patches patches
  7. # for each sub-package, we have to add one extra step to copy its manifest
  8. # to the right place, as docker have no way to filter out only package.json with
  9. # single instruction
  10. COPY packages/foo/package.json packages/foo/
  11. COPY packages/bar/package.json packages/bar/
  12. RUN pnpm install --frozen-lockfile --prod
  13. # Bundle app source
  14. COPY . .
  15. EXPOSE 8080
  16. CMD [ "node", "server.js" ]

As you can see, the Dockerfile has to be updated when you add or remove sub-packages.

pnpm fetch solves the above problem perfectly by providing the ability to load packages into the virtual store using only information from a lockfile.

  1. FROM node:14
  2. RUN curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
  3. # pnpm fetch does require only lockfile
  4. COPY pnpm-lock.yaml ./
  5. # If you patched any package, include patches before running pnpm fetch
  6. COPY patches patches
  7. RUN pnpm fetch --prod
  8. ADD . ./
  9. RUN pnpm install -r --offline --prod
  10. EXPOSE 8080
  11. CMD [ "node", "server.js" ]

It works for both simple and monorepo projects, --offline enforces pnpm not to communicate with the package registry as all needed packages are already present in the virtual store.

As long as the lockfile is not changed, the build cache is valid up to the layer, so RUN pnpm install -r --offline --prod, will save you much time.

Options

--dev

Only development packages will be fetched

--prod

Development packages will not be fetched