DEV Community

Cover image for Building a Tiny, npm-Compatible Package Installer with Zero Dependencies
Daijiro Wachi
Daijiro Wachi

Posted on

Building a Tiny, npm-Compatible Package Installer with Zero Dependencies

I have been working on dep, a tiny dependency installer for Node.js.

It is not meant to replace npm, pnpm, or Yarn. Those tools are mature, powerful, and solve many real-world problems. dep is intentionally much smaller.

The goal is to explore a simple question:

How small can a useful Node.js package installer be?

This question is not new for dep.

Back in 2017, I wrote about the original philosophy behind the project in dep: a little Node.js dependency installer. At the time, I described the idea of a “module end-user”: someone who builds software with packages, but does not necessarily publish packages themselves.

For that user, the minimum useful feature set was:

  • Install dependencies defined in package.json
  • Lock dependencies for repeatable installs
  • Run scripts defined in package.json

In short: install, lock, and run.

That philosophy has not really changed. What has changed is the implementation around it.

The current answer is a tool that can:

  • Install packages and resolve nested dependencies
  • Write an npm-compatible package-lock.json v3
  • Run package scripts with dep run <script>
  • Support npm-style workspaces
  • Use zero runtime dependencies, built purely on the Node.js standard library

GitHub: github.com/depjs/dep


Why build another installer?

The interesting part of dep is not that it is another package manager.

Package installation is usually treated as something magical, huge, and difficult to understand. But at its core, a useful installer only needs to do a few things:

  1. Read package.json
  2. Resolve dependency versions
  3. Download package tarballs from the registry
  4. Extract them into node_modules
  5. Write a deterministic lockfile
  6. Run package scripts
  7. Link workspace packages

That is still not trivial, but it is small enough to reason about.

dep is an attempt to make that process readable and educational.


What dep supports today

dep currently focuses on a small, predictable subset of package-manager behavior:

dep install
dep lock
dep run test
Enter fullscreen mode Exit fullscreen mode

It also supports npm-style workspaces:

{
  "workspaces": ["packages/*"]
}
Enter fullscreen mode Exit fullscreen mode

The lockfile format is compatible with npm's package-lock.json v3.

This means dep does not invent a new ecosystem format. It stays close to the existing npm ecosystem while keeping the implementation minimal.


Installing packages under the hood

At a high level, dep install orchestrates the following flow:

package.json
    ↓
Dependency metadata fetching
    ↓
Version resolution
    ↓
Tarball download and verification
    ↓
node_modules layout
    ↓
package-lock.json v3 generation
Enter fullscreen mode Exit fullscreen mode

The challenge is not that each individual step is impossible.

The challenge is ensuring that all of them work together predictably without hiding the process behind too many abstractions.

For dep, the design goal is to keep each step explicit and understandable.


Writing package-lock.json v3

One of the most important decisions in dep is to write an npm-compatible lockfile.

A lockfile is not just an implementation detail. It is the contract between environments.

If two installs produce different dependency trees, debugging becomes painful very quickly.

By targeting package-lock.json v3, dep aims to provide repeatability and CI-friendliness without inventing a new format.

This also makes it easier to try dep in existing Node.js projects.


Zero runtime dependencies: the trade-offs

dep has zero runtime dependencies.

It relies entirely on built-in Node.js APIs such as fs, path, crypto, child_process, and others.

This is technically interesting for a few reasons.

Fewer moving parts

A dependency installer is the thing that installs dependencies.

So depending on a large dependency tree to install another dependency tree can feel recursive and strange.

With dep, there is no runtime dependency tree behind the installer itself.

Auditability

There is no hidden stack of packages behind the tool.

If something behaves unexpectedly, the code path is easier to inspect.

This matters especially for tooling that touches dependency installation, lockfiles, and project scripts.

Educational value

If you have ever wondered how package resolution, tarball extraction, lockfile generation, or workspace linking works, dep is intended to be small enough to read and experiment with.

Of course, zero dependencies does not mean zero complexity.

It often means implementing things directly that external libraries usually abstract away.

That is the core trade-off of the project.


What dep is not

dep is not trying to be a universal replacement for npm, pnpm, or Yarn.

You should keep using those tools for large production applications that depend on their full, optimized feature sets.

dep is ideal when you want:

  • A minimal dependency installer
  • An npm-compatible lockfile
  • A readable codebase for learning how JavaScript tooling works
  • A tool for small apps, experiments, or constrained environments
  • A package installer that uses only the Node.js standard library at runtime

In that sense, dep is closer to a tiny npm-compatible installer than a full package manager.


Try it out

You can check out the source, open issues, or contribute here:

github.com/depjs/dep

dep install
dep run test
Enter fullscreen mode Exit fullscreen mode

If you are interested in Node.js tooling, lockfile internals, workspace behavior, or dependency resolution edge cases, I would love to hear your thoughts.

What is your favorite or least favorite part of how modern package managers handle dependencies?

Top comments (0)