packall
Guides

Bundle from a file

Point --file at a package.json, a lockfile, or a list of approved packages.

--file reads the specs from a file instead of the command line. It accepts three shapes, and works out which one it has from the content, not the extension — so --file deps.json, --file specs.txt and --file approved-packages all do the obvious thing.

A project's dependencies

packall --file package.json

Everything the project depends on, dev tooling included. If you are pointing this at a package.json you are almost certainly trying to make that project buildable behind the firewall, and a project without its dev dependencies does not build.

--prod excludes them:

packall --file package.json --prod

Lockfiles

A range resolves to whatever is newest at the moment you run the command. That is right for "make this project installable offline" and wrong for "bundle what CI actually installed" — between the two runs, ^1.2.0 quietly became 1.9.0.

So a lockfile beside your package.json is used by default, and the run says which one:

$ packall --file package.json

  pinned by ./pnpm-lock.yaml (pnpm, lockfileVersion 9.0)

package-lock.json (v1–v3), npm-shrinkwrap.json, pnpm-lock.yaml (v5, v6, v9) and bun.lock are all read.

--lockfileBehaviour
detect (default)Nearest lockfile at or above the package.json. Says which, or that it found none.
offNever look. Resolve every range fresh.
npm · pnpm · bunRequire that manager's lockfile; fail if it is not there.
a pathUse exactly that file.

A pinned run is pinned all the way down: every transitive edge comes from the lockfile too. Pinning only the top level and letting tslib@^2 resolve fresh underneath would put a version in the bundle that nobody installed.

Gaps are reported, never guessed at. A dependency the lockfile does not pin, or a pinned version the registry no longer serves, fails the run and names every affected package at once. Entries that cannot come from a registry — workspace:, file:, git dependencies — are warnings, and skipped.

In a monorepo

A workspace member usually has no lockfile of its own; the one that pins it sits at the repository root and pins every other member too. The search climbs the tree, and the parse then takes only that member's dependencies:

$ packall --file ./packages/core/package.json

  pinned by ./pnpm-lock.yaml (pnpm, lockfileVersion 9.0, workspace packages/core)

The nearest level wins, so a package with its own lockfile is never overruled by the root's.

A list of approved packages

The third shape is a plain list — one spec per line, using the syntax you would type as arguments:

approved-packages.txt
# What we are cleared to import this quarter
ms@2.1.3
semver@^7.6.0        // pinned by security review
@babel/code-frame@^7

typescript@next
packall --file approved-packages.txt --out ./bundles

# and // comments are allowed, full-line or trailing, and blank lines are ignored. A line that will not parse is reported with its line number.

For a restricted network this is often the better fit: the file is a reviewable record of what was approved, it lives in version control next to the change that introduced it, and re-running it reproduces the same bundle.

Unsupported inputs

bun.lockb — bun's pre-1.2 binary format — cannot be read; the error tells you to run bun install --save-text-lockfile. yarn lockfiles are not supported yet, and are named as such rather than failing as an unparseable spec list.

On this page