node_modules
directory is found in the working directory or higher, Bun will abandon Node.js-style module resolution in favor of the Bun module resolution algorithm.
Under Bun-style module resolution, all imported packages are auto-installed on the fly into a global module cache during execution (the same cache used by bun install
).
"foo"
and cache it. The next time you run the script, it will use the cached version.
Version resolution
To determine which version to install, Bun follows the following algorithm:- Check for a
bun.lock
file in the project root. If it exists, use the version specified in the lockfile. - Otherwise, scan up the tree for a
package.json
that includes"foo"
as a dependency. If found, use the specified semver version or version range. - Otherwise, use
latest
.
Cache behavior
Once a version or version range has been determined, Bun will:- Check the module cache for a compatible version. If one exists, use it.
- When resolving
latest
, Bun will check ifpackage@latest
has been downloaded and cached in the last 24 hours. If so, use it. - Otherwise, download and install the appropriate version from the
npm
registry.
Installation
Packages are installed and cached into<cache>/<pkg>@<version>
, so multiple versions of the same package can be cached at once. Additionally, a symlink is created under <cache>/<pkg>/<version>
to make it faster to look up all versions of a package that exist in the cache.
Version specifiers
This entire resolution algorithm can be short-circuited by specifying a version or version range directly in your import statement.Benefits
This auto-installation approach is useful for a few reasons:- Space efficiency — Each version of a dependency only exists in one place on disk. This is a huge space and time savings compared to redundant per-project installations.
- Portability — To share simple scripts and gists, your source file is self-contained. No need to
zip
together a directory containing your code and config files. With version specifiers inimport
statements, even apackage.json
isn’t necessary. - Convenience — There’s no need to run
npm install
orbun install
before running a file or script. Justbun run
it. - Backwards compatibility — Because Bun still respects the versions specified in
package.json
if one exists, you can switch to Bun-style resolution with a single command:rm -rf node_modules
.
Limitations
- No Intellisense. TypeScript auto-completion in IDEs relies on the existence of type declaration files inside
node_modules
. We are investigating various solutions to this. - No patch-package support
FAQ
How is this different from what pnpm does?
How is this different from what pnpm does?
With pnpm, you have to run
pnpm install
, which creates a node_modules
folder of symlinks for the runtime to resolve. By contrast, Bun resolves dependencies on the fly when you run a file; there’s no need to run any install
command ahead of time. Bun also doesn’t create a node_modules
folder.How is this different from Yarn Plug'N'Play does?
How is this different from Yarn Plug'N'Play does?
With Yarn, you must run
yarn install
before you run a script. By contrast, Bun resolves dependencies on the fly when you run a file; there’s no need to run any install
command ahead of time.Yarn Plug’N’Play also uses zip files to store dependencies. This makes dependency loading slower at runtime, as random access reads on zip files tend to be slower than the equivalent disk lookup.How is this different from what Deno does?
How is this different from what Deno does?
Deno requires an
npm:
specifier before each npm import
, lacks support for import maps via compilerOptions.paths
in tsconfig.json
, and has incomplete support for package.json
settings. Unlike Deno, Bun does not currently support URL imports.