To find what is making a Node project’s installation large on macOS, measure the installed packages, then trace the largest candidates back through npm’s dependency tree. In Terminal, start from the project directory: du shows disk usage, npm ls lists the installed tree, and npm explain shows why a package is present. These answer different questions: size points to what occupies space; the dependency chain points to what brought it in.

Measure the installed packages on macOS

Open Terminal, change to the project folder, and check that it contains node_modules before measuring. The following commands work in macOS’s default zsh and avoid relying on shell wildcard behavior:

cd "/path/to/your/project"
ls -ld node_modules
find node_modules -mindepth 1 -maxdepth 1 -type d -prune -exec du -sh {} + | sort -h

du -sh reports a human-readable total for each immediate directory, and sort -h orders those totals from smaller to larger. Read from the bottom to see the largest entries. The find command measures each top-level directory once: for example, it reports node_modules/@example as one combined scope directory, not each scoped package separately. This is a useful first pass, not a report of each dependency’s individually attributable cost.

If the command says node_modules does not exist, the project may not have an installed dependency tree yet, or you may be in the wrong directory. Check the project’s package manager and lockfile before installing anything just to measure it. npm’s lockfile describes a resolved dependency tree, while the installed files are what du measures. npm’s package-lock documentation

Trace a large directory to its dependency chain

Use npm ls to inspect npm’s logical dependency tree. Its default output focuses on top-level packages; --all expands the listing to transitive dependencies. The npm ls documentation

npm ls --all

For a large project, the full listing can be unwieldy. Once you have a candidate package name from the tree, ask npm why it is installed:

npm explain package-name

Replace package-name with the actual name, such as some-library. The output follows the dependency chain toward the root project. If npm reports multiple copies or versions, identify the relevant installed location in the listing and pass that path to npm explain, for example:

npm explain node_modules/parent-package/node_modules/package-name

npm documents npm explain for tracing the chain that causes a package to be installed, including a particular folder when a dependency is nested. npm explain documentation

Inspect scoped packages and nested folders

The first measurement groups a scope such as @example into one directory. To inspect its immediate package directories, run:

find node_modules/@example -mindepth 1 -maxdepth 1 -type d -prune -exec du -sh {} + | sort -h

Replace @example with the scope you saw in the first measurement. Quote a scope path if you use a variable or a path containing spaces. For an unscoped package, measure its folder directly, for example du -sh "node_modules/some-library". A package’s folder may include its own nested dependencies; measuring both a parent and a nested directory counts some files twice if you add the totals together. Use these figures to locate candidates, not to calculate an exact contribution for each branch.

Next, run npm explain on a large package or on a nested package path that appears inside the measured directory. If the explanation points through a direct dependency, that top-level package is the branch to investigate. A large transitive package can also be shared by several branches, so its presence under one path does not necessarily mean only that direct dependency uses it. npm describes npm ls as a logical tree, which can differ from the physical layout of node_modules; use the explanation output to understand the relationship instead of treating folder nesting as the whole graph.

Separate development dependencies from production dependencies

A large development tool may be unnecessary in a production installation, while a large runtime dependency may be required by the app. Inspect the chain and whether it leads from a development or production dependency in npm’s output. If you want to compare a production-only npm install, npm ls --omit=dev omits development dependencies from the on-disk installation view; it does not remove them from package.json or the lockfile. Confirm npm’s behavior and version before using omission options in automation. npm’s install documentation

Treat a large size as a lead, not proof that a package is unnecessary. Check what your application imports, build scripts, and deployment process require before removing or replacing a dependency. If a lockfile is present, keep it in view when comparing installs: it records the resolved tree and helps explain why another install may differ from the current folder.

A practical stopping point

You have a useful answer when you can name the largest measured directory, identify the package or packages within it, and use npm explain to trace them to the root dependency that introduces them. That gives you a concrete branch to review. Disk usage alone cannot decide whether the branch is safe to remove, and npm’s dependency commands do not calculate a package’s exclusive share when files or dependencies are shared.

Sources