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

workspace-version-resolver

v0.1.0

Published

A JavaScript/TypeScript package to resolve monorepo internal dependencies using workspace protocol

Readme

workspace-version-resolver

NPM Latest Version Downloads Count Project License

A small Node.js library and CLI that resolves workspace protocol versions in a monorepo. It reads each workspace package’s package.json, finds dependencies that use the workspace: protocol (e.g. workspace:*, workspace:^), replaces them with the actual package versions, and writes the changes back without reordering or reformatting the rest of the file.

Useful for pre-publish steps or tooling that needs concrete versions instead of workspace:* in dependency maps.


Table of contents


Installation

npm install workspace-version-resolver
# or
pnpm add workspace-version-resolver
# or
bun add workspace-version-resolver

Quick start

CLI — run from the monorepo root (or pass a path). The binary is also available under the alias wvr:

npx workspace-version-resolver
# or
npx workspace-version-resolver /path/to/monorepo

API — call the default export with the root directory:

import resolveWorkspaceVersions from 'workspace-version-resolver';

await resolveWorkspaceVersions('/path/to/monorepo');

CLI

The package ships a binary: workspace-version-resolver (alias wvr). Run via npx, bunx, or after npm install -g.

Usage

workspace-version-resolver [path]
  • [path] — Optional path to the monorepo root. If omitted, the current working directory is used.

Options

| Option | Alias | Type | Default | Description | |--------|--------|------|---------|-------------| | --warn-on-circular | — | boolean | true | When true, print a warning for each circular workspace dependency. Use --no-warn-on-circular to disable. | | --help | -h | boolean | — | Show help and exit. | | --version | -v | boolean | — | Show package version and exit. |

Examples

# Use current directory as monorepo root
npx workspace-version-resolver

# Explicit path
npx workspace-version-resolver path/to/monorepo/

# Disable circular dependency warnings
npx workspace-version-resolver --no-warn-on-circular

# Help and version
npx workspace-version-resolver --help
npx workspace-version-resolver --version

On validation or runtime errors, the process exits with code 1 and prints the error message to stderr.


Programmatic API

The package exports a default function and the individual steps + types so you can reuse or test them.

Default export: resolveWorkspaceVersions

Runs the full pipeline: validate root → resolve workspace paths → read manifests → build graph → topological sort → resolve versions → write package.json files.

function resolveWorkspaceVersions(
  rootDir: string,
  options?: ResolveWorkspaceVersionsOptions
): Promise<void>
  • rootDir — Absolute or relative path to the monorepo root (directory that contains a package.json with a "workspaces" array).
  • options — Optional:
    • warnOnCircular?: boolean — If true (default), console.warn is used when circular workspace dependencies are detected. Does not throw.

Example

import resolveWorkspaceVersions from 'workspace-version-resolver';

await resolveWorkspaceVersions(process.cwd(), { warnOnCircular: true });

Options type

interface ResolveWorkspaceVersionsOptions {
  /** If true (default), emit console.warn when circular dependencies are detected. */
  warnOnCircular?: boolean;
}

Named exports (modular steps)

You can call each step yourself for custom workflows or tests.

| Export | Description | |--------|-------------| | validateRoot(rootDir: string) | Ensures rootDir is a directory and has a package.json with a "workspaces" array of strings. Returns { workspaces: string[] }. Throws on invalid root. | | resolveWorkspacePaths(rootPath, workspaces) | Expands workspace globs (e.g. packages/*) to absolute directory paths and keeps only directories that contain a package.json. Returns string[]. | | readManifests(dirPaths) | Reads each directory’s package.json, extracts name, version, dependencies, devDependencies, peerDependencies, and workspace-only deps. Returns PackageInfo[]. Throws on duplicate package names. | | buildDependencyGraph(packages) | Builds a directed graph of workspace packages (by name, edges = “depends on”). Returns DependencyGraph. | | topologicalSort(graph) | Returns a topological order (packages with no workspace deps first) and any cycles found. Returns TopoResult ({ ordered: string[], cycles: string[][] }). Does not throw on cycles. | | resolveVersions(ordered, packages) | Resolves each workspace specifier to the depended-on package’s version. Returns ResolvedUpdates ({ byPackage: Map<string, Map<string, string>> }). | | resolveVersionSpec(specifier, targetVersion) | Resolves a single specifier string (e.g. "workspace:*") given the target package version. Returns the resolved version string. | | writePackageJson(dirPath, updates, workspaceDeps) | Writes back only the resolved version values into the given directory’s package.json, preserving formatting and key order. |

Types

type DependencyMap = Record<string, string>;

interface PackageInfo {
  dirPath: string;
  name: string;
  version: string;
  dependencies: DependencyMap;
  devDependencies: DependencyMap;
  peerDependencies: DependencyMap;
  workspaceDeps: DependencyMap;  // workspace: entries only
}

type DependencyGraph = Map<string, Set<string>>;

interface TopoResult {
  ordered: string[];
  cycles: string[][];
}

Example: custom pipeline

import path from 'node:path';
import {
  validateRoot,
  resolveWorkspacePaths,
  readManifests,
  buildDependencyGraph,
  topologicalSort,
  resolveVersions,
  writePackageJson,
} from 'workspace-version-resolver';

const rootDir = path.resolve('./my-monorepo');
const { workspaces } = validateRoot(rootDir);
const dirPaths = resolveWorkspacePaths(rootDir, workspaces);
const packages = readManifests(dirPaths);
const graph = buildDependencyGraph(packages);
const { ordered, cycles } = topologicalSort(graph);
if (cycles.length > 0) console.warn('Cycles:', cycles);
const { byPackage } = resolveVersions(ordered, packages);
for (const pkg of packages) {
  const updates = byPackage.get(pkg.dirPath);
  if (updates?.size) writePackageJson(pkg.dirPath, updates, pkg.workspaceDeps);
}

How it works

  1. Validate root — Checks that rootDir is a directory and that its package.json has a "workspaces" array of strings.
  2. Resolve workspace paths — Expands each workspace entry (e.g. packages/*) to absolute directory paths and keeps only directories that contain a package.json.
  3. Read manifests — Reads each package’s package.json and extracts name, version, dependencies, devDependencies, and peerDependencies. Duplicate package names cause an error.
  4. Workspace deps — From each manifest, only entries whose value starts with workspace: are considered (e.g. workspace:*, workspace:^, workspace:~, workspace:1.2.3).
  5. Dependency graph — Builds a directed graph of packages based only on workspace dependencies.
  6. Topological sort — Orders packages so that those with no workspace deps come first, then their dependents. Cycles are detected and reported (warning only, no throw).
  7. Resolve versions — For each workspace dependency, computes the resolved version string from the depended-on package’s version and the specifier (see Version resolution rules).
  8. Write back — Updates each package.json by replacing only the workspace:... values with the resolved versions, using a surgical string replace so that key order and formatting are preserved.

Version resolution rules

For each dependency that uses the workspace: protocol, the resolved value is derived from the depended-on package’s version and the specifier:

| Specifier | Resolved value | Example (target version 1.2.3) | |-----------|----------------|----------------------------------| | workspace:* | Exact version | 1.2.3 | | workspace:^ | Caret + version | ^1.2.3 | | workspace:~ | Tilde + version | ~1.2.3 | | workspace:1.2.3 | Strip workspace: | 1.2.3 | | workspace:^1.2.3 | Strip workspace: | ^1.2.3 | | workspace:~1.0.0 | Strip workspace: | ~1.0.0 |

Only dependency entries whose value is a string starting with workspace: are modified; all other keys and values are left unchanged.


Requirements and behavior

  • Root must be a directory whose package.json contains a "workspaces" property set to an array of strings (e.g. ["packages/*"]).
  • Workspace entries are resolved relative to the root and expanded (e.g. globs). Only directories that contain a package.json are included.
  • Duplicate package names (same name in more than one workspace package) cause the run to throw (or the CLI to exit with code 1).
  • Circular workspace dependencies are detected and reported with a warning only; the run continues and versions are still resolved where possible.
  • File writes change only the version strings for workspace-resolved dependencies; property order and formatting (whitespace, newlines) are preserved.

License

MIT. See LICENSE in the repository.


Made with ❤️ by Lacus Solutions