Fast, reliable local npm package development tool — a better alternative to yalc.
- Blazing fast — Reflink/CoW + hard links for instant operations on large packages
- Smart linking — Automatically uses the fastest method: reflink → hardlink → parallel copy
- Monorepo support — Publish all workspace packages at once
- Cross-platform — Works on Linux, macOS, and Windows
- All package managers — npm, yarn, pnpm, and bun
- Full visibility — Know exactly what's linked where
curl -fsSL https://raw.githubusercontent.com/pedrosousa13/lnpm/main/install.sh | shirm https://raw.githubusercontent.com/pedrosousa13/lnpm/main/install.ps1 | iexInstalls to %LOCALAPPDATA%\lnpm. Override with $env:LNPM_INSTALL_DIR.
Note: On Windows, lnpm uses NTFS junction points for linking — no Developer Mode or admin privileges required.
go install github.com/pedrosousa13/lnpm/cmd/lnpm@latestgit clone https://github.com/pedrosousa13/lnpm.git
cd lnpm
make install# In your library package
cd ~/code/my-library
lnpm publish
# In your app that uses the library
cd ~/code/my-app
lnpm add my-library
# Make changes to my-library, then push updates
cd ~/code/my-library
lnpm push- Monorepo Guide — Turborepo, Nx, PNPM/NPM/Yarn workspaces integration
- Architecture — System design and implementation details
- Changelog — Version history and updates
- Roadmap — Planned features and improvements
| Command | Description |
|---|---|
lnpm publish |
Publish package to local store |
lnpm add <pkg...> |
Add package(s) from store to project |
lnpm remove <pkg> |
Remove linked package |
lnpm push |
Push changes to all linked projects |
lnpm status |
Show current project's links |
lnpm list |
List this project's linked packages (--store lists the store, --projects lists consumers) |
lnpm doctor |
Diagnose issues |
lnpm gc |
Garbage collect unused packages (--dry-run, --older-than 30d, --fix-links) |
lnpm retreat |
Remove all lnpm changes |
lnpm config |
View/edit configuration |
lnpm update |
Update lnpm to the latest version |
lnpm completion |
Generate shell completions |
# Publish a package
lnpm publish
# Add to a project (supports multiple packages)
lnpm add my-package
lnpm add pkg-a pkg-b pkg-c
# Push updates after making changes
lnpm pushlnpm integrates seamlessly with Turborepo, Nx, and all workspace managers:
# lnpm is installed globally (one-time system install)
# See installation section above
# Publish all workspace packages
lnpm publish --all
# Push updates to all linked projects
lnpm push📖 See MONOREPO.md for complete guides on:
- Turborepo integration
- Nx integration
- PNPM/NPM/Yarn workspaces
- Best practices and troubleshooting
# Remove all lnpm links and restore original dependencies
lnpm retreat --force
npm install # Restore original packages
npm publishQuick setup (recommended):
lnpm completion installThis auto-detects your shell and installs completions. Follow the on-screen instructions to enable.
Alternative - Dynamic loading (add to shell config):
# Zsh (~/.zshrc)
eval "$(lnpm completion zsh)"
# Bash (~/.bashrc)
eval "$(lnpm completion bash)"
# Fish (~/.config/fish/config.fish)
lnpm completion fish | sourceManual installation:
# Zsh
lnpm completion zsh > ~/.zsh/completions/_lnpm
# Bash
lnpm completion bash > /etc/bash_completion.d/lnpm
# Fish
lnpm completion fish > ~/.config/fish/completions/lnpm.fishConfiguration file: ~/.lnpm/config.yaml
# Custom store location (default: ~/.lnpm)
store_path: /path/to/store
# Link mode: "hardlink" (try reflink/hardlink) or "copy" (force copy)
# Default: hardlink
link_mode: hardlink
# Auto-manage .gitignore (add/remove .lnpm/ entry)
# Default: true
manage_gitignore: true
# Build scripts and hooks
hooks:
# Skip prepare scripts (prepare, prepublishOnly, prepack) on publish/push
skip_prepare: false # Default: false (run scripts)
# Custom hooks (optional)
pre_publish: npm run build # Runs before publish/push packs files
post_publish: echo done # Runs after publish/push completes
# Runs only when `lnpm add --install` is used; add does NOT install by default
post_add: npm installView/modify config:
lnpm config # Show all
lnpm config store_path # Get value
lnpm config store_path /data # Set value
lnpm config --path # Show config file path
lnpm config --edit # Open config file in $EDITOREnvironment overrides: LNPM_STORE (store location, wins over store_path), LNPM_CONFIG (config file path), LNPM_DEBUG (debug logging).
lnpm automatically runs your package's build (prepare) scripts before publishing so linked output is up to date. Dependency installation after add is opt-in via --install (see below).
Before publishing or pushing, lnpm runs these scripts from your package.json (in order of precedence):
prepare— General build scriptprepublishOnly— Runs only on publishprepack— Runs before packing files
Example package.json:
{
"name": "my-library",
"scripts": {
"build": "tsc",
"prepare": "npm run build"
}
}Commands:
lnpm publish # Runs prepare, then publishes
lnpm push # Runs prepare, then pushes
lnpm publish --skip-hooks # Skip prepare scriptsBy default, lnpm add does NOT run npm install (matching yalc). Pass --install to have lnpm run your package manager after adding, to resolve peer dependencies and install the linked package's dependencies.
lnpm add my-package # Adds package, no install (default)
lnpm add my-package --install # Adds package, then runs npm installlnpm validates packages before publishing:
- Checks
package.jsonhasnameandversion - Verifies
mainentry point exists (if declared)
lnpm publish # With validation
lnpm publish --skip-validation # Skip validation (for broken packages)# In your TypeScript library
cd my-library
# package.json has "prepare": "tsc"
lnpm publish # Automatically builds TypeScript → dist/
# In your app
cd my-app
lnpm add my-library # Links and runs npm install
# Make changes to library
cd my-library
# Edit src/index.ts
lnpm push # Rebuilds and updates all linked projectsHusky/git hooks fail during npm install:
- lnpm strips
prepareandprepublishscripts from stored packages (like yalc) - If you see husky errors, re-publish the package:
lnpm publish
Duplicate React or other peer dependency issues:
- Don't use
--installflag; runnpm installmanually with your preferred flags - Or configure npm:
npm config set legacy-peer-deps true
npm ERESOLVE peer dependency errors:
- npm has a bug where
file:dependencies show as@undefined(npm/cli#2199) - When using
--install, lnpm uses--legacy-peer-depsautomatically - Or run
npm install --legacy-peer-depsmanually
Retreat restores wrong version:
- Fixed in latest version - lnpm now properly tracks original versions
- If stuck, manually edit package.json and delete lnpm.lock
Enable debug mode for verbose logging:
# Flag
lnpm --debug publish
lnpm -d status
# Environment variable
LNPM_DEBUG=1 lnpm publishDebug output goes to stderr with timestamps, useful for diagnosing slow operations or unexpected behavior.
- Publish — Uses
npm packrules to determine files, strips lifecycle scripts (prepare/prepublish), then links or copies to~/.lnpm/store/{name}/{hash}/ - Add — Creates reflinks or hard links from store to
project/.lnpm/{package}/, updates package.json tofile:.lnpm/{package} - Symlink — Links
node_modules/{package}→.lnpm/{package} - Push — Updates store and re-links changed files
- Auto .gitignore — Optionally manages
.lnpm/in.gitignore(enabled by default)
Note: lnpm add does NOT run npm install automatically (matches yalc) — pass --install or run it yourself if you need to resolve peer dependencies. lnpm remove, however, runs your package manager's install afterward to restore the removed dependency.
Source Package Store Project
────────────── ───── ───────
src/index.ts ──► (reflink/hardlink)
dist/index.js ──► ~/.lnpm/store/ ══► .lnpm/pkg/ ──► node_modules/pkg
package.json ──► pkg/abc123/ (reflink/hardlink) (symlink)
lnpm uses npm's standard packing rules (via npm pack --dry-run) to determine which files to include, ensuring consistency with actual npm publish behavior. This means:
- Respects
package.jsonfilesfield - Honors
.npmignoreor falls back to.gitignore - Follows npm's default exclusions (
.git,node_modules, etc.) - Additional safety: Explicit
.gitfiltering prevents any VCS files from being linked - Symlinks are skipped, never followed — a symlink inside a package can't pull files from outside it (e.g.
~/.ssh) into the store - Package names with path separators or
..are rejected to prevent path traversal - Automatic fallback to custom filtering if npm is unavailable
This approach prevents issues with git hooks (like Husky) running in linked packages and ensures lnpm behaves identically to npm publish.
lnpm automatically manages .gitignore entries to prevent committing linked packages (enabled by default, configurable via manage_gitignore: false):
- On
lnpm add— Adds.lnpm/to.gitignorewith marker comment# Added by lnpm - On
lnpm retreat— Removes.lnpm/entry and marker from.gitignore - Smart detection — Skips if pattern already exists
- Atomic writes — Uses temp file + rename to prevent corruption
- Configurable — Set
manage_gitignore: falsein config to disable
lnpm uses an intelligent priority system for maximum performance:
Priority Order: Reflink → Hard Link → Parallel Copy
| Method | Speed | Platform Support | Disk Usage |
|---|---|---|---|
| Reflink (CoW) | Instant | macOS APFS, Linux Btrfs/XFS | Zero (copy-on-write) |
| Hard Link | Instant | Same filesystem | Zero (shared inodes) |
| Parallel Copy | Fast | Always works | Full copy (8 workers) |
Benefits:
- ⚡ 10,000+ files? Instant with reflink or hard links
- 🔄 Copy-on-write — Modify files safely without affecting store
- 🚀 Parallel copying — 4-8x faster when copying is needed
- 💾 Space efficient — No duplication on modern filesystems
lnpm automatically detects the best method and falls back gracefully with helpful warnings.
| Feature | lnpm | yalc |
|---|---|---|
| Link method | Reflink → Hard link → Parallel copy | Sequential copy only |
| Large packages (10k+ files) | Instant (~5ms) on APFS/Btrfs | Slow (~5s+) |
| State tracking | bbolt database | Hash files |
| Monorepo | Native support | Manual |
| Speed | ~10ms startup | ~100ms startup |
| Cross-filesystem | Parallel copy (4-8x faster) | Sequential copy |
| Visibility | lnpm status |
Limited |
| Operation | lnpm | yalc | Speedup |
|---|---|---|---|
publish |
75ms | 164ms | 2.2x |
add |
71ms | 251ms | 3.5x |
push |
90ms | 385ms | 4.3x |
| Operation | Files | Time | Memory |
|---|---|---|---|
publish |
100 | 17ms | 3.5 MB |
add |
100 | 48ms | 340 KB |
push (1 project) |
100 | 146ms | 3.8 MB |
push (5 projects) |
100 | 183ms | 4.5 MB |
publish |
500 | 41ms | 17 MB |
Note: These figures are illustrative — measured once on the machine above. The
vs yalcand memory numbers are not reproduced by the committedscripts/benchmark-compare.sh(which measures wall-clock only). Run the benchmarks yourself for numbers on your hardware.
Run benchmarks yourself:
make bench # Go benchmarks
make bench-mem # With memory stats
make bench-compare # Compare vs yalc (if installed)- Linux — amd64, arm64
- macOS — amd64 (Intel), arm64 (Apple Silicon)
- Windows — amd64
- Go 1.22+ (for building from source)
- Node.js project with
package.json
Contributions are welcome!
- Go 1.22+
- Node.js (for integration tests that invoke npm)
Linux / macOS:
git clone https://github.com/pedrosousa13/lnpm.git
cd lnpm
make deps
make build
make test # all tests
make test-coverage # with coverage report
make lint # golangci-lint (falls back to go vet)Windows (PowerShell):
git clone https://github.com/pedrosousa13/lnpm.git
cd lnpm
go mod download
go build ./cmd/lnpm
go test -v ./...
go vet ./...Verify your changes compile for all platforms:
GOOS=linux go build ./...
GOOS=darwin go build ./...
GOOS=windows go build ./...go test -v -race ./internal/... # unit tests
go test -v -race ./tests/... # integration testsWindows note: Some symlink tests are skipped on Windows since lnpm uses NTFS junctions (absolute paths) instead of relative symlinks. This is expected.
MIT License — see LICENSE for details.
Note: This project was built with assistance from Claude (Anthropic's AI assistant). The architecture, implementation, and documentation were developed through an AI-assisted development process. While the code has been reviewed and tested, users should evaluate it according to their own requirements and standards before use in production environments.