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

pubcheck-cli

v0.1.3

Published

One combined pass/fail verdict from publint, arethetypeswrong, and Knip — instead of running all three separately.

Readme

pubcheck

npm version npm downloads license node

One combined pass/fail verdict from publint, arethetypeswrong (attw), and Knip — instead of running all three separately and reconciling three different outputs by hand.

pubcheck  ✔ publint   ✔ attw   ✖ knip (3 issues)

✖ FAIL — 0 errors, 3 warnings

knip
  ✖ unused-exports: formatDate (src/utils.ts)
  ✖ unused-exports: Config (src/types.ts)
  ✖ unused-dependencies: lodash

Published on npm as pubcheck-cli — the plain pubcheck name was already taken by an unrelated package. The CLI command itself is still pubcheck.

Why

Every one of these tools catches something the other two miss before you npm publish — and none of them catches what the other two do:

| Tool | Catches | Misses | | ----------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | | publint | Broken exports/main/module/types fields, ESM/CJS mismatches | Whether your types actually resolve correctly, unused code | | attw | Type declarations that don't resolve the way your published code does, across node10/node16/bundler resolution | package.json field correctness, unused code | | Knip | Unused files, exports, and dependencies you're shipping (or forgot to ship) | Type/export-map correctness |

Running all three separately means three commands, three install steps, three output formats to reconcile in your head — every time, on every release. pubcheck runs them together, in parallel, and gives you one verdict and one exit code.

Install

yarn add -D pubcheck-cli
# or: npm install -D pubcheck-cli

Usage

# check the current directory
npx pubcheck

# check a specific package (e.g. one package in a monorepo)
npx pubcheck ./packages/my-lib

# machine-readable output for CI
npx pubcheck --json

# show every issue, including low-severity suggestions
npx pubcheck --verbose

# skip a tool (e.g. no TypeScript types to check)
npx pubcheck --skip attw

Exit code is 0 when everything passes, 1 otherwise — drop it straight into CI or a prepublishOnly script:

{
  "scripts": {
    "prepublishOnly": "pubcheck"
  }
}

Configuration

pubcheck looks for config in (in order): the pubcheck field in package.json, pubcheck.config.ts, pubcheck.config.js, .pubcheckrc.

// pubcheck.config.ts
import type { PubcheckConfig } from 'pubcheck-cli';

export default {
  skip: ['attw'], // e.g. for a package with no type declarations
  tools: {
    publint: { level: 'warning' }, // ignore suggestions, only fail on warning/error
    knip: { args: ['--config', 'knip.custom.json'] },
  },
} satisfies PubcheckConfig;

Programmatic API

import { runCheck } from 'pubcheck-cli';

const verdict = await runCheck({ cwd: './packages/my-lib' });

if (!verdict.pass) {
  console.log(verdict.summary); // { publint: true, attw: false, knip: true }
  process.exit(1);
}

Verdict shape:

interface Verdict {
  pass: boolean;
  summary: { publint: boolean; attw: boolean; knip: boolean };
  results: RunnerResult[]; // full per-tool detail, including individual issues
  errorCount: number;
  warningCount: number;
  suggestionCount: number;
}

How each tool is invoked

  • publint — called programmatically (publint({ pkgDir })); it's a pure function with a stable API. pack is forced to "npm" rather than publint's own "auto" detection — "auto" picks a package manager by walking up for a lockfile, and if pkgDir sits nested inside another project's node_modules (e.g. pubcheck ./node_modules/some-pkg) with a yarn.lock in that outer project, "auto" picks yarn pack, which has a real bug on nested node_modules targets — it reports every declared file as "not published" even though it plainly exists. npm publish is what actually ships to the registry regardless of what package manager a project's author uses locally, so forcing "npm" is correct generally, not just a workaround. Override via tools.publint.pack if you need to.
  • attw — called programmatically against a real npm pack tarball (via @arethetypeswrong/core), packed into an isolated temp directory (never your project's own folder) so it sees exactly what would ship, after files/.npmignore filtering, without racing any other check running in parallel. @arethetypeswrong/core is still pre-1.0 and its README warns the API can change between minors — the dependency is pinned to an exact version rather than a caret range.
  • Knip — shelled out to (npx knip --reporter json --no-exit-code), not embedded. Knip's main() export exists but mutates process.cwd/console and its internal shape isn't guaranteed stable across minors; the JSON reporter is the one contract Knip documents as stable.

Note: pubcheck is meant to run against your own source checkout before publishing — not against an already-installed node_modules copy. Knip in particular needs source code to prove a dependency is used; pointed at a trimmed, already-published package (no src/, by design) it will report everything as unused. That's expected, not a bug.

Development

yarn install
yarn build       # tsup -> dist/
yarn typecheck
yarn test        # aggregate(), each runner (mocked), runCheck() orchestration, loadConfig() against real fixtures
yarn dev         # watch mode

Built with TypeScript pinned to the 6.x line rather than 7.x: TypeScript 7's Go-native compiler doesn't ship a stable programmatic/declaration-emit API until 7.1, which tsup's .d.ts generation depends on. Worth revisiting once the ecosystem catches up.

Contributing

Issues and PRs welcome at github.com/Dreamyplayer/pubcheck-cli.

License

MIT © Dreamy Player