Replit enforces a limit of account storage, so I’m investigating which Node.js package manager is most disk-efficient for a single project.

Disk usage is measured with gdu 5.13.2 on macOS 12.3, using the following command to prepare environment:

$ env HOME=$(mktemp -d) bash --init-file <(echo cd)
$ mkdir test && cd test

Contents

npm

$ npm -v
8.5.5
$ npm i -D vite solid-js vite-plugin-solid typescript

$ gdu -n
   **97.4 MiB /node_modules**
   88.0 KiB package-lock.json
    4.0 KiB package.json
$ gdu -n node_modules | head -n 10
   **62.4 MiB /typescript**
    8.7 MiB /esbuild
    6.3 MiB /rollup
    5.2 MiB /@babel
    4.6 MiB /vite
    3.3 MiB /caniuse-lite
    1.1 MiB /ts-toolbelt
  952.0 KiB /solid-js
  800.0 KiB /source-map
  644.0 KiB /resolve

# Global cache
$ gdu -ns ~/.npm/_cacache
  43.4 MiB _cacache

Yarn (nodeLinker: pnp)

<aside> ℹ️ Yarn also supports global hard links, but I won’t cover it here.

</aside>

$ npx yarn set version berry
$ npx yarn -v
3.2.0
$ npx yarn add -D vite solid-js vite-plugin-solid typescript

$ gdu -n
   **52.9 MiB /.yarn**
  624.0 KiB .pnp.cjs
   64.0 KiB yarn.lock
   12.0 KiB .pnp.loader.mjs
    4.0 KiB .yarnrc.yml
    4.0 KiB package.json
$ gdu -n .yarn
   31.9 MiB /cache
   18.9 MiB /unplugged
    2.1 MiB /releases
  152.0 KiB install-state.gz
$ gdu -n .yarn/cache | head -n 10
   **10.9 MiB typescript-patch-30b732d1e2-6bf45caf84.zip
   10.9 MiB typescript-npm-4.6.3-1493ebc82b-255bb26c8c.zip**
    3.3 MiB esbuild-darwin-64-npm-0.14.28-5a7ada9fb8-8.zip
    1.1 MiB rollup-npm-2.70.1-13154f7180-06c62933e6.zip
    1.0 MiB vite-npm-2.8.6-60b9dced78-4b02d13389.zip
  688.0 KiB caniuse-lite-npm-1.0.30001320-e77e9fa553-d1f52e9d8e.zip
  500.0 KiB node-gyp-npm-9.0.0-0eccfca4d1-4d8ef8860f.zip
  404.0 KiB @babel-parser-npm-7.17.8-667f8971e5-1771808491.zip
  196.0 KiB source-map-npm-0.5.7-7c3f035429-5dc2043b93.zip
  192.0 KiB iconv-lite-npm-0.6.3-24b8aae27e-3f60d47a5c.zip
$ gdu -n .yarn/unplugged
    8.2 MiB /esbuild-npm-0.14.28-c2ae07b619
    8.1 MiB /esbuild-darwin-64-npm-0.14.28-5a7ada9fb8
    2.3 MiB /node-gyp-npm-9.0.0-0eccfca4d1
  180.0 KiB /fsevents-patch-3340e2eb10

# Global cache
$ gdu -ns ~/.yarn/berry/cache
   31.9 MiB cache

pnpm

$ npx pnpm -v
6.32.3
$ npx pnpm i -D vite solid-js vite-plugin-solid typescript

$ gdu -n
   **97.7 MiB /node_modules**
   32.0 KiB pnpm-lock.yaml
    4.0 KiB package.json
$ gdu -n node_modules/.pnpm | head -n 10
    **62.5 MiB /typescript@4.6.3**
    8.2 MiB /esbuild@0.14.28
    6.4 MiB /rollup@2.70.1
    4.6 MiB /vite@2.8.6
    3.3 MiB /caniuse-lite@1.0.30001320
    1.7 MiB /@babel+parser@7.17.8
    1.4 MiB /@babel+types@7.17.0
    1.1 MiB /ts-toolbelt@9.6.0
  932.0 KiB /solid-js@1.3.13
  772.0 KiB /source-map@0.5.7

$ gdu -n ~
   96.5 MiB /.pnpm-store # Global store
   20.4 MiB /Library # Global cache (~/Library/Caches/pnpm)
    **2.9 MiB /test # node_modules sans hard links**

Conclusion

First of all, this only tests a common scenario (Vite + TypeScript + random framework). Results may vary wildly depending on the actual needs.

We see Yarn (PnP) is the clear winner here. Due to its usage of compressed modules, the biggest offender a.k.a typescript only consumes 1/3 space compared to npm or pnpm.

However, the symlink approach pnpm takes is certainly going to take off on your own machine, which typically hosts multiple projects at the same time.

One More Thing

That said, you will want to keep using npm on Replit: