packall

Getting started

Bundle a package, look at what came out, and import it.

Check before you download

--dry-run resolves everything and reports what it found, without downloading a byte.

packall react@19.2.8 --dry-run
Checking registry…
Resolving 1 spec…
  resolved 1 package in 1s

Dry run — nothing downloaded. 1 package would be bundled:
  react → 19.2.8  (1 package in closure)

  Re-run without --dry-run to fetch them:

    packall react@19.2.8

Resolution is cheap and downloads are not, so this is the right first move against an unfamiliar dependency tree. A dry run always ends by printing the command that reproduces it for real — exploring your way to the right flags leaves you with something you can paste into a script.

Bundle it

Drop --dry-run and pick somewhere to write:

packall react@19.2.8 --out ./bundles
Checking registry…
Resolving 1 spec…
  resolved 1 package in 1s
Downloading 1 package…
  downloaded 1 package (25 KB) in 79ms
Packaging…
  react@19.2.8.tgz  26 KB

Done in 1s — 1 package, 25 KB downloaded.

  ./bundles/react@19.2.8.tgz  26 KB  (1 package)

  1 artifact in ./bundles (26 KB total)

Every tarball is checked against the checksum the registry advertised before it goes in. A mismatch fails the run rather than warning.

Bundle several at once

Pass as many specs as you like. They share one deduplicated download, so bundling ten packages that all depend on tslib fetches it once.

packall react@19.2.8 react-dom@19.2.8 lodash

Specs use the same syntax as npm install: a bare name takes the latest, and name@version, name@^7.2.0 and name@next all work. Quote anything with a ^ or a leading @ so your shell leaves it alone.

packall "@babel/core@^7" "typescript@next"

What is inside

A bundle is a tarball holding package tarballs, in exactly the layout every registry serves them from. Here is react-dom@19.2.8.tgz from the run above:

react-dom/-/react-dom-19.2.8.tgz
react/-/react-19.2.8.tgz
scheduler/-/scheduler-0.27.0.tgz
bundle-manifest.json
IMPORT.md

bundle-manifest.json lists every package with its version, integrity string, source URL, size and the reason it is there — root:react-dom@19.2.8, prod:scheduler@0.27.0. IMPORT.md is a short crib sheet for the import step, generated with the bundle.

Import it

Unpack, then upload the tree into a local npm repository. Nothing needs rewriting, because the paths already match what registries expect:

tar -xzf react-dom@19.2.8.tgz -C ./bundle
jf rt upload "bundle/(**)" "npm-local/{1}" --flat=false

For Verdaccio, Nexus, or any other npm registry, publish each tarball:

find ./bundle -name '*.tgz' -exec npm publish --registry <url> {} \;

See Importing a bundle for the details.

On this page