/npm-cache

Primary LanguageJavaScript

Local NPM Cache Registry

A local NPM registry system using Verdaccio for completely offline package management. This allows you to cache NPM packages locally and work without internet access or the global NPM registry.

Directory Structure

./npm-cache/               # Project directory
  ├── serve                # Start the local registry server
  ├── add-npm              # Download and cache NPM packages
  ├── scan_projects        # Scan projects and cache their dependencies
  ├── add-package          # Add a local package to the cache
  ├── config.yaml          # Verdaccio configuration
  └── package.json         # Project dependencies

~/npm-cache/               # Storage directory (where packages are cached)

Setup

  1. Install dependencies (already done):

    cd ./npm-cache
    npm install
  2. Ensure scripts are executable (already done):

    chmod +x serve add-npm scan_projects add-package

Usage

1. Start the Local Registry Server

./npm-cache/serve

This will:

  • Start Verdaccio on http://localhost:4873
  • Store packages in ~/npm-cache
  • Display the web interface at http://localhost:4873

Leave this running in a terminal window.

2. Configure NPM to Use the Local Registry

In a new terminal, set your NPM registry:

Option A: Environment variable (temporary)

export NPM_CONFIG_REGISTRY=http://localhost:4873/

Option B: NPM config (persistent)

npm config set registry http://localhost:4873/

Option C: Project-specific (.npmrc) Create a .npmrc file in your project:

registry=http://localhost:4873/

3. Cache Packages

Cache a specific package from NPM:

./npm-cache/add-npm <package-name>[@version-spec]

Supports version ranges and pinning:

./npm-cache/add-npm express                  # Latest version
./npm-cache/add-npm express@4.18.0           # Exact version (pinned)
./npm-cache/add-npm express@^4.18.0          # Caret range (^)
./npm-cache/add-npm express@~4.18.0          # Tilde range (~)
./npm-cache/add-npm @types/node@>=20.0.0     # Greater than or equal
./npm-cache/add-npm @types/node              # Latest version

This will:

  • Download the package from NPM, respecting the exact version spec
  • Cache it in ~/npm-cache
  • Recursively cache all dependencies with their pinned versions

Scan projects and cache all dependencies:

./npm-cache/scan_projects [directory]

Examples:

./npm-cache/scan_projects                    # Scan current directory
./npm-cache/scan_projects ~/Projects         # Scan specific directory

This will:

  • Find all package.json files (excluding node_modules)
  • Extract all dependencies with their exact version specs from package.json
  • Cache each unique package@version combination
  • Respects pinned versions and version ranges (^, ~, >=, etc.)
  • If different projects use different versions of the same package, all versions are cached

Add a local package to the cache:

./npm-cache/add-package <path-to-package>

Examples:

./npm-cache/add-package ./my-local-package
./npm-cache/add-package ~/Projects/my-lib

This will:

  • Pack the local package using npm pack
  • Store it in the local cache
  • Make it available for installation

4. Install Packages

Once the registry is running and configured, use npm normally:

npm install express
npm install

Packages will be fetched from your local cache instead of the global NPM registry.

Configuration Details

Verdaccio Configuration (config.yaml)

  • Storage: ~/npm-cache - All packages are stored here
  • Uplinks: Empty ({}) - No upstream registries (fully offline)
  • Packages:
    • access: $all - Anyone can read packages
    • publish: $authenticated - Only authenticated users can publish
    • proxy: [] - No proxying to external registries
  • Server: Listens on http://0.0.0.0:4873

Environment Variables

# Set registry
export NPM_CONFIG_REGISTRY=http://localhost:4873/

# Alternative for yarn
export YARN_REGISTRY=http://localhost:4873/

Reset to Global NPM Registry

# Using environment variable
unset NPM_CONFIG_REGISTRY

# Using npm config
npm config delete registry

# Or manually set to default
npm config set registry https://registry.npmjs.org/

Workflow Examples

Scenario 1: Offline Development Setup

# 1. Start the local registry
./npm-cache/serve &

# 2. Configure NPM
export NPM_CONFIG_REGISTRY=http://localhost:4873/

# 3. Scan and cache all dependencies from your projects
./npm-cache/scan_projects ~/Projects

# 4. Now you can work offline - all packages are cached

Scenario 2: Adding New Packages

# Registry is running and configured

# Add a specific package
./npm-cache/add-npm lodash

# Now install it in your project
cd ~/Projects/my-app
npm install lodash

Scenario 3: Development with Local Packages

# Add your local package to the cache
./npm-cache/add-package ~/Projects/my-library

# Install it in another project
cd ~/Projects/my-app
npm install my-library

Troubleshooting

Packages not found

  • Ensure the registry server is running (./npm-cache/serve)
  • Verify NPM is configured: npm config get registry
  • Check if the package was cached: ls ~/npm-cache

Registry connection refused

  • Make sure ./npm-cache/serve is running
  • Check if port 4873 is available: lsof -i :4873

Cannot publish

  • First login: npm adduser --registry http://localhost:4873
  • Create a user when prompted

Clear cache and start fresh

# Stop the server (Ctrl+C)
rm -rf ~/npm-cache
# Restart the server
./npm-cache/serve

Notes

  • Version pinning is fully respected: The scripts honor exact versions and ranges (^, ~, >=) from package.json files
  • The add-npm script recursively downloads dependencies with their pinned versions
  • scan_projects caches the exact version specs specified in your package.json files
  • Scoped packages (e.g., @types/node) are fully supported
  • The system is designed for offline use - uplinks: {} ensures no external registry calls
  • For production use, consider using HTTPS and proper authentication

Deduplication

The system intelligently avoids duplicate downloads at multiple levels:

  1. scan_projects: Deduplicates package@version specs across all scanned projects
  2. In-memory cache: Within a single run, packages are only downloaded once
  3. On-disk cache: Checks if packages already exist before downloading (works across multiple runs)

You can safely:

  • Run scan_projects multiple times - already cached packages are skipped
  • Add individual packages that may already be cached - they'll be skipped if present
  • Dependencies shared across packages are only downloaded once

Limitations

  • No automatic package updates - you must manually re-cache packages when you want newer versions
  • Authentication is basic (htpasswd file)