Why Smuggle?
Testing local packages in a real consumer project usually means npm link, pnpm link, yalc, or file: references. Each one leaves a trail.
Compare against:
beforenpm link
node_modules/my-pkg
-real folder
+→ ../../my-pkg (symlink)
Bundlers and resolvers behave differently
package.json
"my-pkg": "^1.0.0"
Range stays — but resolves to a symlink, not a release
pnpm-lock.yaml / package-lock.json
(usually unchanged)
Easy to commit a symlinked install by accident
aftersmuggle install
node_modules/my-pkg
✓real folder, real files
Bundlers, Vite, Node — all see a normal install
package.json
"my-pkg": "^1.0.0"
Untouched. Same diff as if you ran nothing.
pnpm-lock.yaml / package-lock.json
(unchanged)
No churn. Nothing to revert before committing.
Smuggle packs your local package the same way
npm publishwould, then directly replaces the installed copy innode_modules. Your lockfile andpackage.jsonstay untouched.
The trade-offs of the alternatives
npm link/pnpm link— creates symlinks that confuse bundlers, break with pnpm's content-addressable store, and behave differently from a real install.file:references — pollute your lockfile andpackage.jsonwith local paths that break for other contributors.yalc— closer, but still modifies your lockfile and requires manual cleanup.
Design principles
No symlinks
Packages are real files in node_modules — bundlers, Vite, and Node treat them like any other install.
No lockfile changes
pnpm-lock.yaml, package-lock.json, yarn.lock — never touched. Nothing to revert.
No .npmrc tweaks
No registry overrides, no scoped resolution rules.
No package.json edits
Version ranges are preserved. (smuggle add is the exception — it adds the dep on purpose.)
Automatic cleanup
Originals are restored on exit, even on Ctrl-C.
Hash-based change detection
Cache busts and Vite restarts only when the packed output actually changes.
Workspace aware
Detects pnpm workspaces and scans all member packages for matches.