Netlify, Cloudflare Pages and GitHub Pages do not serve symlinks, so there symlink: true is a local-development affordance, not a deploy strategy.
Vercel serves only symlinks that point inside the output directory, and AWS Amplify serves them wherever they point.
Configuration
Most options are available either as a config file key, or a command line option (e.g. foo would be --foo), though their CLI version may support a more limited syntax.
Some command line options also allow for a shorthand one letter syntax (e.g. -d foo instead of --dir=foo).
The config file defaults to nudeps.js in your project root, and should export an object literal:
export default {
dir: "./vendor",
prune: true,
};
Priority (weakest to strongest): hard defaults → defaults from a programmatic caller → built-in mode presets → config file values → matching override rules → CLI args.
Unknown or invalid options fail loudly: typos get a “did you mean” suggestion, invalid values report which layer supplied them, and options from previous versions point at their replacement. Inside override rules only keys are checked, so an invalid value there passes silently.
Top-level values are global; the overrides option scopes any package-scoped option (marked below) to specific packages, versions, or modes.
At a glance
| Option | Default | CLI | Per-package | What it does |
|---|---|---|---|---|
dir |
./client_modules |
--dir, -d |
✅ | Where packages are materialized |
map |
importmap.js |
--map, -o |
Import map injection script path | |
root |
Workspace root | --root |
Directory the host serves as / |
|
module |
false |
--module |
Load the map script as type="module" |
|
terse |
false |
--terse |
Minify the map script | |
prune |
false |
--prune |
Keep only specifiers the entry points use | |
include |
— | ✅ (only) | Direct-install membership: "force" | true | false |
|
ignore |
Dotfiles, lockfiles | --ignore |
✅ | File globs to skip when copying |
imports |
— | ✅ | Import map entries merged into the generated map | |
cjs |
true |
--cjs |
✅ | Shim CommonJS packages |
subpaths |
"split" |
--subpaths |
Collapse subpath mappings: "split" | "combined" | "both" |
|
symlink |
External deps only | --symlink |
✅ | Symlink packages instead of copying |
preserveSymlinks |
false |
--preserveSymlinks |
✅ | Keep symlinks inside copied packages |
alias |
true |
--alias |
✅ | Unversioned stable paths to packages |
wireLocalDeps |
— | ✅ | Add the notify hook to local deps’ package.json |
|
overrides |
— | Conditional rules: per package, mode, version | ||
host |
Auto-detected | --host |
Deploy host adapter | |
mode |
— | --mode, -m |
Active mode, tested by rules | |
hooks |
— | Lifecycle hook callbacks | ||
config |
nudeps.js |
--config, -c |
Config file path | |
init |
false |
--init |
Full re-initialization: clear caches, regenerate |
“Per-package” = settable from a package-matched override rule; an empty CLI cell means config file (or programmatic) only.
Output
dir
--dir, -d · Default: ./client_modules · Package-scoped
Directory to copy deployed dependencies to, relative to project root. It will be created if it does not exist. It is assumed that Nudeps owns this directory, do not use a directory path that you use for other things. Overriding it per package via a rule materializes just that package elsewhere (the import map follows).
map
--map, -o · Default: importmap.js
File path for import map injection script, relative to project root. Nudeps needs to be able to own this file, do not input a file you use for other things too.
root
--root · Default: workspace root
The directory your host serves as /.
Hosts without symlink support (Netlify, Cloudflare) express aliases as redirect rules, which need URLs rather than file paths — so set this when dir lives inside a build output directory (e.g. an SSG’s dist/), or the rules will be written outside the deployed site and will not match.
Defaults to the npm workspace root, which is typically the deploy root; in a single-package project that is the project root.
module
--module · Default: false
Set to true if the import map script will be loaded as <script type="module">.
Please note that this will reduce browser support, as certain browsers do not support injecting import maps after any module has started loading.
terse
--terse · Default: false
Terser import map injection script (compact JSON, no error checks, reduced whitespace).
Enabled by default in prod mode.
What gets included
prune
--prune · Default: false
Whether to subset only to specifiers used by the package entry points (true), or include all direct dependencies anyway.
Packages with include: "force" survive pruning.
See Pruning.
include
Rule-only (inside overrides)
One setting for direct-install membership, replacing separate add/force/exclude lists:
true installs a package like a dependency even if unlisted (prunable), "force" also survives prune, false removes a package from direct installs, and undefined restores standard behavior.
export default {
overrides: {
"canvas-confetti": { include: true },
"my-design-system": { include: "force" },
"@netlify/blobs": { include: false }, // server-side dep
},
};
Note that include: false does not guarantee absence from the map: a package actively imported by your code still gets mapped.
ignore
--ignore · Package-scoped
Any files to exclude from being copied to the target directory. See Deployed files.
On the command line each --ignore takes a single glob, and the flag may be repeated to add more.
Commas do not separate globs: --ignore="dist/**,docs/**" is one glob that matches nothing.
The { copy } and { ignore } object forms are config file only.
Resolution
imports
Config file only · Package-scoped
Import map entries deep-merged into the generated map, using the platform’s own imports shape: keys are specifiers, values are paths relative to the map file (or full URLs).
Set a value to undefined to remove an entry from the map.
export default {
imports: {
lodash: "./vendor/lodash.js",
},
};
Inside a package rule, values are package-relative paths instead, resolved against the package’s versioned directory — no node_modules paths needed:
export default {
overrides: {
"colorjs.io": {
imports: {
"colorjs.io/fn": "./src/index-fn.js",
"colorjs.io/src/": "./src/",
},
},
},
};
cjs
--cjs · Default: true · Package-scoped
Whether to add a CommonJS shim to the import map if any CJS packages are detected.
Setting to false will omit both the shim and these packages from the import map.
Override per package for misdetected packages.
See How are CJS packages handled?
subpaths
--subpaths · Default: "split"
Whether to collapse multiple subpath entries for the same package into a single trailing-slash prefix (e.g. "pkg/" instead of individual "pkg/a", "pkg/b" entries):
"split"keeps every used subpath explicit"combined"collapses within scopes"both"also collapses top-level imports
Combining subpaths can produce significantly smaller import maps, but is a lossy process, as it can expose specifiers that would not have resolved to anything in the original package.
Corresponds to the combineSubpaths option of @jspm/generator.
Linking
symlink
--symlink · Default: symlink local dependencies, copy the rest · Package-scoped
Whether to symlink a package into dir instead of copying it.
Symlinking means edits to a local dependency are visible immediately, with no re-copy.
dev mode sets it to true, prod mode to false.
preserveSymlinks
--preserveSymlinks · Default: false · Package-scoped
Whether to keep symlinks found inside a package as-is when copying it, rather than dereferencing them to real files. Scope to specific packages via override rules.
alias
--alias · Default: true · Package-scoped
Create unversioned symlinks pointing to versioned directories. Useful for stable URLs to package assets (CSS, images, etc.). See Aliases.
wireLocalDeps
Config file only · Package-scoped
Let Nudeps add "dependencies": "npx nudeps dependents" to the package.json of every local dependency in the chain, so each one notifies its dependents when its own dependencies change.
- Unset: Nudeps never edits another repo’s
package.json. It warns instead, and you can add the hook yourself. false: the same, without the warning.true: your run adds the hook to your local dependencies and to theirs.
A package rule decides for that package and for the local dependencies it pulls in:
export default {
wireLocalDeps: true,
overrides: {
"other-repo": { wireLocalDeps: false },
},
};
Environment
host
--host · Default: auto-detected
Deploy host adapter: netlify, vercel, amplify, cloudflare, or gitHubPages.
Set it to force one.
Vercel runs npm install, and so Nudeps, only with a build step.
So for Vercel projects, nudeps install adds "build": "nudeps" when there is no build script.
It detects Vercel from vercel.json or .vercel/project.json (created by vercel link).
Otherwise, run npx nudeps install --host vercel.
Other runs never edit your package.json: they warn when the build script is missing and point you to npx nudeps install.
mode
--mode, -m
The active mode.
Built-in presets: dev, prod; define your own with rules that match on mode.
See Overrides & modes.
overrides
Config file only
Conditional config rules: override options per package, per mode, per version — or unconditionally. See Overrides & modes.
hooks
Config file only
Lifecycle hook callbacks: prepare-start, create-aliases-start, create-aliases-after-external, create-aliases-end.
See blissful-hooks.
config
--config, -c · Default: nudeps.js
File path for nudeps configuration, relative to project root. It should export an object literal with the configuration options as keys.