DEV Community

girigirianish
girigirianish

Posted on

The packaging bugs I kept shipping (and the tool I built to stop)

I've published TypeScript packages that passed all my tests, built cleanly, and had types resolving perfectly in my editor then broke for consumers.

The bug was always in the exports field. A require condition pointing to an ESM file. The "types" key in the wrong position. A subpath missing its .d.ts entirely. These failures are silent until someone files an issue.

After debugging the same class of problem for the third time, I decided to automate the checks.

Demo

What goes wrong

1. CJS/ESM format mismatch

Your package.json says "require": "./dist/cjs/index.cjs". But that file contains import and export statements it's actually ESM. Works fine in Webpack and esbuild (they don't care). Crashes in Node.js with ERR_REQUIRE_ESM.

This happens because build tools sometimes output ESM syntax even when targeting CJS, especially with certain configurations. You'd never know unless you manually read the output file.

2. Wrong condition ordering

{
  ".": {
    "import": "./dist/index.js",
    "types": "./dist/index.d.ts",
    "default": "./dist/index.js"
  }
}
Enter fullscreen mode Exit fullscreen mode

Spot the bug? "types" should be first. TypeScript resolves conditions top to bottom and stops at the first match. Here it matches "import" first, never sees "types", and falls back to inferring types from the JS file everything becomes any.

This one has bitten me multiple times because the exports field looks correct.

3. Missing declarations for subpaths

Your main entry has index.d.ts. But your package exports 20 subpaths:

{
  "./utils": "./dist/utils.js",
  "./hooks": "./dist/hooks.js"
}
Enter fullscreen mode Exit fullscreen mode

If dist/utils.d.ts doesn't exist, TypeScript consumers importing from your-package/utils get any or "cannot find module." And you'll never know unless someone reports it, because your own project's tests use the source files directly.

The tool

I built tspub to catch these. It checks 70 rules across exports, types, files, metadata, imports, and package size.

$ npx @tspub-dev/tspub check

exports/format-mismatch    ./dist/cjs/index.cjs contains ESM syntax
exports/types-first        "types" should be first in conditions
types/no-any-export        ./dist/index.d.ts exports 12 `any` types

3 problems (1 auto-fixable)
Enter fullscreen mode Exit fullscreen mode

Some rules I haven't seen in other tools:

  • exports/cjs-esmodule-interop detects CJS files using the __esModule flag that causes different behavior across Webpack, Node, and esbuild
  • types/no-any-export flags declaration files where your exported types contain excessive any (usually a build tool misconfiguration, not intentional)
  • exports/format-mismatch actually reads file contents to verify format, not just the extension
  • files/sensitive catches .env, private keys, or credentials accidentally included in the published package

You can also auto fix certain issues:

$ npx @tspub-dev/tspub check --fix
Enter fullscreen mode Exit fullscreen mode

This handles safe fixes like condition reordering.

Check any package without installing

I built a web version too: visit tspub.dev/check/YOUR-PACKAGE to check any npm package instantly.

Beyond checking

tspub also handles building (esbuild-based ESM/CJS + .d.ts), type declaration tests (.test-d.ts files), scaffolding new packages, and publishing to npm.

tspub init       # scaffold a correctly-configured package
tspub build      # ESM + CJS + .d.ts generation
tspub check      # 70-rule validation
tspub test-types # type-level test runner
tspub publish    # npm + GitHub releases
Enter fullscreen mode Exit fullscreen mode

I built it as one tool because managing separate configs for building, linting, type testing, and publishing across multiple packages was getting unsustainable for me. Your mileage may vary the checking works standalone even if you don't use the build or publish features.

It's still early and I'd genuinely appreciate feedback:

  • Does it catch real issues in your packages?
  • False positives?
  • Missing rules?

GitHub: link
npm: npm i -D @tspub-dev/tspub
Web: tspub.dev

Top comments (0)