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-runChecking 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.8Resolution 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 ./bundlesChecking 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 lodashSpecs 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.mdbundle-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=falseFor 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.