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

@ster5/check-version-consistency

v0.10.0

Published

verify packages consistency for node-opcua

Readme

check-version-consistency

A set of diagnostic tools to help identify and resolve version mismatches, peer dependency issues, and configuration problems in projects using the node-opcua ecosystem (and @sterfive/* packages).

Installation

npm install -g check-version-consistency

Tools

This package provides two main CLI tools:

  1. check-package-version-consistency
  2. diagnose-node-opcua

1. check-package-version-consistency

Checks that all modules in the project use consistent versions. This is useful for large monorepos or projects with many related dependencies.

Usage

check-package-version-consistency [projectFolder] [options]

| Option | Description | | :----- | :---------- | | projectFolder | The root folder to explore (default: current working directory). | | --fix | Rewrite every manifest so each module is written one way. | | --dry-run | Show what --fix would rewrite, without touching any file. | | --no-install | With --fix, skip refreshing the lockfile afterwards. | | --strict | Treat any difference in how a version is written as an error, even when every package resolves to the same version. |

Example

check-package-version-consistency ./my-project

How workspaces are discovered

Every tool declares workspace membership somewhere different. Reading only one convention makes a monorepo using any other look like it has no workspaces at all — nothing gets checked and the run still reports success, which is worse than failing. All four are supported, in this order:

| Order | Source | Field | Used by | | :---- | :----- | :---- | :------ | | 1 | pnpm-workspace.yaml | packages: | pnpm | | 2 | package.json | "workspaces": [...] | npm, yarn berry, bun | | 3 | package.json | "workspaces": { "packages": [...] } | yarn classic | | 4 | lerna.json | "packages": [...] | lerna |

pnpm-workspace.yaml wins when several exist, because it is the one that actually installs and therefore cannot be stale. If your repo uses pnpm, the workspaces field in package.json is dead weight — delete it rather than maintain two lists that will drift.

Errors vs suggestions

Two very different situations used to be reported identically:

  • error — the specifiers cannot all be satisfied at once (^1.0.0 against ^2.0.0). Something is genuinely broken; exit code is 1.
  • suggestion — one version written several ways (2.175.1 against ^2.175.1). Worth converging, but nothing is broken, so it never fails a build. The report names the version that satisfies everyone and the single form to prefer. Use --strict to make these errors again.

A specifier that points at a workspace sibling — *, link:../x, workspace:^, file:../x — is not a version and is never compared as one.

Converging with --fix

--fix rewrites every manifest so each module is written one way:

check-package-version-consistency --dry-run   # show what would change
check-package-version-consistency --fix       # apply, then refresh the lockfile
  • dependencies and devDependencies get an exact pin. A pin is the only form that installs the same tree twice.
  • peerDependencies get >=. Pinning a peer forces one exact version on every host — the thing the peer check exists to catch.
  • The version chosen is the highest floor anyone asked for, so no package is ever downgraded. ^2.175.1, 2.175.2 and >=2.175.1 all converge on 2.175.2.
  • Workspace links (*, link:../x) are never rewritten into versions.
  • Genuine conflicts are not touched. The tool cannot know which of two incompatible majors the code actually wants; that stays a human decision.

Rewriting a manifest invalidates the lockfile, so --fix runs the install itself — pnpm m install, pnpm install, yarn install or npm install, whichever the project uses, detected from its lockfile and workspace file. Pass --no-install to skip it, and the run tells you which command to run instead.

Every --fix (here and in diagnose-node-opcua) writes a manifest back in the style it was written in: the indent unit it already uses — whichever is most common, tabs or spaces — its line ending, and a trailing newline only if it had one. A two-line change stays a two-line diff instead of re-emitting the whole file in the tool's own style and burying the actual edit.

Exact pins are right for an application, where reproducibility is everything. For a published library they are restrictive: a consumer that resolves any other patch ends up with a duplicate copy in its tree. Consider running --fix on the app and leaving published packages on ranges.

peerDependencies must be ranges

A pinned peer forces one exact version on every host: any consumer that resolves a different patch gets an unsatisfiable peer and a duplicate copy in the tree. 1.2.3, =1.2.3, v1.2.3 and 1.2.3-beta.1 are all equally pinned and all reported, with the >= form to use instead.


2. diagnose-node-opcua

A more advanced diagnostic tool specifically designed for the node-opcua ecosystem.

Features

  • Version Mismatch Detection: Scans installed dependencies and flags version inconsistencies against a reference baseline.
  • Peer Dependency Validator (--check-peer): Recursively checks if all peer dependencies required by your dependency tree are correctly declared in your root package.json.
  • Import Validator (--check-imports): Scans your source code and checks that every node-opcua* / @sterfive/* package you actually import is declared in your package.json. Add --workspaces to cover every package of a monorepo in one run.
  • Online Resolution: Queries the NPM registry to find the latest compatible versions and metadata, ensuring up-to-date checks.
  • Thorough Scan: Performs a deep recursive analysis of node-opcua dependencies without relying solely on local node_modules structure.
  • Auto-Fix: Can automatically update your package.json to fix missing peer dependencies or undeclared imports.
  • Private Registry Support: Respects .npmrc configuration for resolving private/scoped packages.

Which check do I want?

The two validators look similar but catch disjoint sets of problems. Neither finds the other's defects, so a project that wants full coverage runs both.

| | Question it answers | Reads | Needs network | | :--- | :--- | :--- | :--- | | --check-imports | Do I declare everything I import? | your source tree | no | | --check-peer | Do I declare everything my dependencies demand? | the NPM registry graph | yes |

A package you import directly is nobody's peer requirement, so no amount of dependency-graph walking will ever surface it — only reading the sources will. Conversely, a package required by one of your dependencies' peerDependencies never appears in your sources.

Because --check-imports is offline and fast, it is the one to put in a pre-commit or pre-push hook; --check-peer suits CI, where the network is available.

Usage

Run the script using Node.js (v14+):

diagnose-node-opcua [options]

Options

| Option | Alias | Description | | :----------------- | :---- | :----------------------------------------------------------------------------------------------------------- | | --package <path> | -p | Path to the package.json to diagnose (default: current directory). | | --online | -o | Enable online resolution of package versions and metadata from NPM registry. | | --thorough | -t | Perform a recursive online diagnosis of all node-opcua related packages. Automatically enables --online. | | --check-peer | | Check that every peer dependency your dependencies require is declared. Automatically enables --online. | | --check-imports | | Check that every package your sources import is declared. Offline unless --online is given. | | --workspaces | -w | With --check-imports, check every workspace package instead of one. See the monorepo note below. | | --include-tests | | Also scan test/, tests/, __tests__/ and report test-only imports separately. Requires --also-in. | | --source <dir> | | Source directory to scan (repeatable). Defaults to every one of source/, src/, lib/ that exists and is not git-ignored. | | --also-in <path> | | A second package.json (a monorepo root, typically) that must also declare each imported package. | | --fix | | Automatically fix the issues found in package.json (use with --check-peer or --check-imports). | | --exact | | With --fix, write exact versions instead of ^ ranges (for repos that pin strictly). | | --dry-run | | Preview changes that would be made by --fix without modifying files. | | --silent | | Suppress output (useful for scripted checks). | | --help | -h | Show help message. |

Exit codes

| Code | Meaning | | :--- | :--- | | 0 | No issues found (or --fix repaired them). | | 1 | Issues found, or the tool could not run. |

--check-peer and --check-imports exit non-zero when they find something, so they can be used directly as a CI or git-hook gate.

Examples

1. Quick Local Diagnosis

Checks formatting and basic version consistency in the current project's node_modules.

diagnose-node-opcua

2. Online Diagnosis with Recommendations

Fetches latest version info from NPM to ensure your project is up-to-date and consistent with published releases.

diagnose-node-opcua --online

3. Deep Thorough Scan

Recursively scans the entire dependency tree (virtualized via NPM metadata) to find deep inconsistencies, even if not locally installed.

diagnose-node-opcua --thorough

4. Peer Dependency Check & Fix

Identifies missing peer dependencies (common source of "module not found" or weird behavior) and adds them to your package.json.

# Check issues
diagnose-node-opcua --check-peer

# Preview fixes
diagnose-node-opcua --check-peer --dry-run

# Apply fixes
diagnose-node-opcua --check-peer --fix

5. Diagnose a Specific Project

You can run the tool from anywhere against a specific package.json.

diagnose-node-opcua --package /path/to/other/project/package.json --online

6. Undeclared Import Check

Finds packages your code imports but never declares — the class of bug that only shows up when someone installs your package fresh, or when a bundler resolves the import from a hoisted node_modules that will not exist elsewhere.

# Check (offline, fast enough for a git hook)
diagnose-node-opcua --check-imports

# Non-standard source layout
diagnose-node-opcua --check-imports --source lib --source tools

# Preview and apply
diagnose-node-opcua --check-imports --dry-run
diagnose-node-opcua --check-imports --fix

Sample output:

Scanned 80 source file(s) in /project/source
Found 14 node-opcua* / @sterfive/* package(s) imported.

=== Import / Declaration Issues ===

[UNDECLARED] Imported by your sources but absent from package.json:
┌────────────────────────────┬──────────────────────────────┬──────────────┐
│ Package                    │ Imported By                  │ Add as       │
├────────────────────────────┼──────────────────────────────┼──────────────┤
│ node-opcua-nodeid          │ source/bindings/file.ts (+6) │ >=2.175.1    │
└────────────────────────────┴──────────────────────────────┴──────────────┘

[UNSATISFIABLE] Declared as a peer with a floor above any published version.
  Package managers that auto-install peers fail the entire install on these.
┌────────────────────────────┬───────────────┬───────────────┐
│ Package                    │ Declared      │ Should be     │
├────────────────────────────┼───────────────┼───────────────┤
│ node-opcua-variant         │ >=2.175.2     │ >=2.175.1     │
└────────────────────────────┴───────────────┴───────────────┘

7. Monorepo: package declares a peer, root installs it

A common layout is a library package that declares its node-opcua surface as peerDependencies, while the workspace root carries the real dependencies that get installed. --also-in enforces both halves at once:

diagnose-node-opcua \
  --check-imports \
  --package packages/my-lib/package.json \
  --also-in package.json

Every imported package must then be declared as a peer in packages/my-lib and as a dependency at the root. Packages supplied by the workspace itself (link:/workspace: specifiers, or a node_modules entry symlinked back into the repository) are excluded — the workspace already provides those.

8. Monorepo: check every package at once (--workspaces)

Naming one package at a time does not scale, and pointing --check-imports at a monorepo root is worse than useless: the root usually has no source/ of its own, so the run scans zero files and reports success — a check that can never fail. --workspaces resolves the members and checks each one:

diagnose-node-opcua --check-imports --workspaces --also-in package.json
=== @sterfive/my-lib ===
Scanned 24 source file(s) in packages/my-lib/source
🎉 Every imported package is declared.
...
Checked 13 package(s), 163 source file(s).

Members are resolved through the same patterns check-version-consistency uses (pnpm-workspace.yaml, package.json "workspaces", lerna.json), so a glob such as packages/* picks up a new package the moment it exists. Members with no source directory are skipped rather than reported as passing. --fix applies per member and accumulates into the single --also-in manifest. --source names a path inside one package, so it cannot be combined with --workspaces.

9. Checking what the tests import (--include-tests)

Test folders are outside the default scan, so a package imported only by a test is invisible — nothing verifies that anything installs it. --include-tests adds test/, tests/ and __tests__/:

diagnose-node-opcua --check-imports --workspaces --include-tests --also-in package.json

Test imports are judged by a different rule, and reported in their own section so the two are never conflated:

A package imported by a test must be declared in the --also-in manifest — something has to install it. It is not required in the package's own peerDependencies: a test import is not part of what the package exposes to a host.

Scanned 14 source file(s) in packages/node-opcua-json/source
Scanned 14 test file(s) in packages/node-opcua-json/tests
Found 1 package(s) imported by tests only.

[TEST-ONLY] Imported by tests and absent from package.json:
  (a test import need not be a peer of the package, but the root must install it)
┌────────────────────┬────────────────────────────────┬────────────┐
│ node-opcua-nodesets│ tests/json_basic_encode_deco…  │ 2.175.1    │
└────────────────────┴────────────────────────────────┴────────────┘

--fix therefore writes a test-only import to the --also-in manifest's dependencies and never to the package's peerDependencies. A package imported by both sources and tests is a source requirement and stays in the normal sections. The flag is opt-in, so existing gates keep their current exit codes. --include-tests requires --also-in: without it there is no manifest to check against.

How the scanned directories are chosen

  1. --source <dir> wins outright, and is never filtered — you named it, so it is scanned.
  2. Otherwise every one of source/, src/, lib/ that exists and is not git-ignored. All of them, not just the first: a package keeping code in both source/ and lib/ used to have half of it silently unchecked.
  3. Git-ignored candidates are dropped because lib/ is the compiled output of source/ as often as it is a source folder, and scanning the compiled copy yields duplicate findings and stale-artifact false positives. Outside a git repository nothing is treated as ignored, so every candidate is scanned.
  4. Inside each directory the walk is fully recursive, skipping node_modules, dist, build, out, coverage and .git. It reads .js/.jsx/.ts/.tsx/.mjs/.cjs/.mts/.cts and ignores .d.ts.

Development

npm test          # vitest run
npm run coverage
npm run build     # tsup -> dist/

Sources live in lib/ and bin/; the tests run against those directly. Only dist/ is published: tsup bundles each CLI into one self-contained ESM file with every dependency inlined and minified, so the installed package has no runtime dependencies at all.

That matters because the usual way to run this is npx from a CI job or a git hook, where install time is the whole cost:

| | install size | files | | :------------------ | :----------- | :---- | | unbundled (0.4.0) | 9.1 MB | 1122 | | bundled | 0.9 MB | 14 |

Notes for anyone touching the build:

  • Sourcemaps are off on purpose. esbuild embeds sourcesContent, so publishing them would put the entire original source back in the tarball.
  • The banner in tsup.config.mjs injects a real require via createRequire. Some bundled dependencies are CommonJS and call require() at runtime, which an ESM bundle otherwise answers with "Dynamic require of X is not supported".
  • The shebang comes from that same banner, not from the entry files, because it has to be the first line of the output.
  • prepack builds, so npm pack and npm publish always ship a fresh dist/.

The suite is offline: test/mocks.mjs intercepts npm view and serves a fake registry from test/mock-database.mjs, so no test reaches the network.

test/regressions.test.mjs holds one scenario per defect that has actually shipped, each documenting the symptom it guards against — notably that npm view specs must be shell-quoted (an unquoted pkg@>=1.2.3 is parsed by the shell as an output redirection, which silently empties stdout and drops a file named 1.2.3 in the working directory), and that findings must never be reported as "conformant".

Troubleshooting

  • Private Packages: Ensure your .npmrc is correctly configured in the project root or user home directory. The tool runs npm commands using the project root as the working directory to pick up local config.
  • Network: Online modes require internet access to reach the NPM registry.
  • Cache: The tool caches NPM metadata in your system's cache directory (e.g., ~/.cache/diagnose-node-opcua or %LOCALAPPDATA%\diagnose-node-opcua). If you suspect stale data, you can clear this directory.

License

MIT

reference:

see also: https://github.com/bmish/check-dependency-version-consistency