npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@forgefield/portlens

v0.1.2

Published

Scan local projects for configured service ports.

Readme

PortLens

PortLens - Logo

@forgefield/portlens is a CLI tool for scanning local project directories, discovering configured service ports from env files, and detecting port collisions across services.

Repository: https://github.com/forgefield/portlens

Why PortLens

  • You have multiple microservices under one root folder (monorepo/workspace), each with its own .env file, and need a quick way to see all configured ports in one place.
  • Your team keeps hitting "address already in use" errors; PortLens surfaces duplicate port assignments early so you can fix collisions before running services.
  • You are onboarding to an unfamiliar codebase and need to understand which services are expected to run, what ports they use, and whether they are currently running locally.
  • You want to organize services by top-level repository folder and inspect or update configuration in a local UI instead of manually checking dozens of files.

Requirements

  • Node.js >=18 (from package engines)
  • macOS/Linux/Windows supported
  • For port status checks:
    • macOS/Linux uses lsof
    • Windows uses netstat + findstr

Installation

One-off (no install)

npx @forgefield/portlens

Global install

npm install -g @forgefield/portlens
portlens

Local project install

npm install --save-dev @forgefield/portlens
npx portlens

Quick Start

Scan the current directory:

portlens

Scan a specific root:

portlens /path/to/workspace

Show only collisions:

portlens --collisions

Machine-readable JSON:

portlens --json

Start UI mode:

portlens ui

CLI Reference

Usage:

portlens [rootPath] [--config <path>] [--json] [--collisions]
portlens ui [rootPath] [--config <path>]

Positional arguments

  • rootPath (optional): root directory to scan. Defaults to process.cwd().

Commands

  • ui: starts the local PortLens UI server and opens your default browser.

Flags

  • --json: print JSON instead of console tables.
  • --collisions: limit output to collision results.
  • --config <path>: use a custom config file path (resolved relative to current shell directory).

Behavior notes

  • --json --collisions outputs only collisionSummary.
  • Without --json, --collisions prints only the collision text report.
  • Unknown flags are ignored by the current parser (no hard failure).

How Scanning Works

  1. PortLens recursively walks the target rootPath.
  2. It skips ignored directories (node_modules, .git, dist, build by default).
  3. It looks for configured env filenames (default list shown below).
  4. It extracts the first matching configured port variable from each matching env file.
  5. It groups findings by top-level directory under rootPath.
  6. It marks collisions when the same port appears in multiple services.
  7. It checks runtime status of each port (running or stopped) using OS-specific commands.

Configuration

PortLens supports JSON config with these keys:

  • envFilePatterns: env file names/patterns to scan.
  • portVariablePatterns: env variable names/patterns to treat as ports.
  • ignoredDirectories: directory names to skip.

Default config lookup (CLI)

If you do not pass --config, the CLI uses:

<rootPath>/ports.config.json

Where <rootPath> is:

  • the positional rootPath argument, or
  • the current directory if rootPath is omitted.

Custom config path

portlens --config ./config/ports.config.json
portlens /path/to/root --config ../shared/ports.config.json

Example config

{
  "envFilePatterns": [
    ".env.local",
    ".env.development",
    ".env.production",
    ".env"
  ],
  "portVariablePatterns": [
    "PORT",
    "VITE_PORT",
    "NEXT_PUBLIC_PORT",
    "REACT_APP_PORT"
  ],
  "ignoredDirectories": ["node_modules", ".git", "dist", "build"]
}

Schema file:

  • config/ports.config.schema.json

Example file:

  • config/ports.config.example.json

Output Contract

Standard mode (portlens)

  • Prints a collisions section first.
  • Prints per-repo service tables with columns:
    • Service
    • Type
    • Port
    • Status
    • Collision
    • Location
    • From

JSON mode (portlens --json)

Returns an object with:

  • rootPath
  • servicesByRepo
  • allServices
  • portMap
  • collisions
  • collisionSummary

Representative shape:

{
  "rootPath": "/workspace",
  "servicesByRepo": {
    "repo-a": [
      {
        "Repo": "repo-a",
        "Service": "frontend",
        "Type": "frontend",
        "Port": "3000",
        "From": ".env (PORT)",
        "Status": "running",
        "Location": "apps/frontend",
        "Collision": true,
        "collision": true
      }
    ]
  },
  "allServices": [
    {
      "Repo": "repo-a",
      "Service": "frontend",
      "Type": "frontend",
      "Port": "3000",
      "From": ".env (PORT)",
      "Status": "running",
      "Location": "apps/frontend",
      "Collision": true,
      "collision": true
    },
    {
      "Repo": "repo-b",
      "Service": "backend",
      "Type": "backend",
      "Port": "3000",
      "From": ".env (PORT)",
      "Status": "stopped",
      "Location": "services/backend",
      "Collision": true,
      "collision": true
    }
  ],
  "portMap": {
    "3000": [
      {
        "Repo": "repo-a",
        "Service": "frontend"
      },
      {
        "Repo": "repo-b",
        "Service": "backend"
      }
    ]
  },
  "collisions": [
    {
      "port": "3000",
      "services": [
        {
          "Repo": "repo-a",
          "Service": "frontend"
        },
        {
          "Repo": "repo-b",
          "Service": "backend"
        }
      ]
    }
  ],
  "collisionSummary": {
    "count": 1,
    "byPort": {
      "3000": [
        {
          "Repo": "repo-a",
          "Service": "frontend"
        },
        {
          "Repo": "repo-b",
          "Service": "backend"
        }
      ]
    }
  }
}

Collisions JSON only (portlens --json --collisions)

Returns only:

{
  "count": 1,
  "byPort": {
    "3000": [
      {
        "Repo": "repo-a",
        "Service": "frontend"
      }
    ]
  }
}

UI Mode

Run:

portlens ui

What it does:

  • Starts a local HTTP server on an ephemeral port.
  • Opens your browser automatically:
    • open on macOS
    • start on Windows
    • xdg-open on Linux
  • Serves the built UI from ui/dist.

UI/API endpoints currently served by the local server:

  • GET /data and GET /api/scan: scan results + merged config
  • GET /config: current merged config
  • POST /config: persist config changes

If UI assets are missing, you will see:

UI build not found. Run npm run build:ui.

Common Workflows

Scan current project:

portlens

Scan monorepo root:

portlens ~/code/my-monorepo

Export full JSON for tooling:

portlens ~/code/my-monorepo --json > portlens-report.json

CI-friendly collision check artifact:

portlens ~/code/my-monorepo --json --collisions > collisions.json

Screenshots

CLI screenshots

CLI - Default Scan Output

CLI - Collisions Output

CLI - JSON Output

CLI - JSON Output Collisions

UI screenshots

UI - Dashboard Overview

UI - Config Editor

UI - Filters

UI - Filters Applied

UI - Collision View

UI - Collision View - Collision Only Filter Applied

Troubleshooting

No services found

  • Confirm env files exist and match envFilePatterns.
  • Confirm port variable names match portVariablePatterns.
  • Confirm folders are not excluded by ignoredDirectories.

Port status looks incorrect

  • Ensure lsof (macOS/Linux) or netstat/findstr (Windows) is available.
  • Status is determined at scan time; rerun if services started/stopped recently.

UI command fails to render app

  • Build UI assets first:
npm run build:ui

Invalid config payload in UI

  • POST /config expects non-empty string arrays for:
    • envFilePatterns
    • portVariablePatterns

Development And Release

This repository currently uses:

  • Bun for dependency installation
  • npm for build and packaging scripts

Install dependencies:

bun install

Build UI assets:

npm run build:ui

Verify package contents before publish:

npm pack --dry-run

Contributing Workflow

For external contributors, please follow a fork-based workflow.

  1. Fork the repository to your GitHub account.
  2. Clone your fork locally.
  3. Create a feature branch for your change.
  4. Make and test your changes locally.
  5. Push your branch to your fork.
  6. Open a Pull Request from your fork branch to the main repository.

This keeps the upstream repository clean and makes reviews easier to manage.

Security And Privacy

  • PortLens scans local files under the path you provide.
  • It inspects env file contents for configured port variables.
  • It performs local OS port checks.
  • It does not require outbound network access for scanning itself.

License

MIT. See LICENSE.