The packaging bugs I kept shipping (and the tool I built to stop)
Source: Dev.to
What goes wrong
1. CJS/ESM format mismatch
Your package.json says:
{
"exports": {
"require": "./dist/cjs/index.cjs"
}
}
but ./dist/cjs/index.cjs contains import/export statements—i.e. it’s actually ESM.
Webpack and esbuild don’t mind, but Node.js crashes with ERR_REQUIRE_ESM. This happens when a build tool outputs ESM syntax even though you targeted CJS.
2. Wrong condition ordering
{
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
"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 any for everything.
3. Missing declarations for subpaths
{
"exports": {
"./utils": "./dist/utils.js",
"./hooks": "./dist/hooks.js"
}
}
If dist/utils.d.ts (or dist/hooks.d.ts) doesn’t exist, consumers importing your-package/utils get any or “cannot find module.” Your own tests may pass because they import the source files directly, so you won’t notice until someone reports it.
The tool
I built tspub to catch these problems. 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)
Notable rules
exports/cjs-esmodule-interop– detects CJS files using the__esModuleflag that behave differently in Webpack, Node, and esbuild.types/no-any-export– flags declaration files where exported types contain excessiveany(usually a build‑tool misconfiguration).exports/format-mismatch– reads file contents to verify format, not just the extension.files/sensitive– catches.env, private keys, or credentials accidentally included in the published package.
Auto‑fix
$ npx @tspub-dev/tspub check --fix
This safely rewrites condition ordering and applies other trivial fixes.
Check any package without installing
A web version is available: tspub.dev/check/YOUR-PACKAGE lets you validate any npm package instantly.
Beyond checking
tspub also handles building, type‑declaration testing, scaffolding new packages, and publishing.
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
I built it as a single tool because juggling separate configs for building, linting, type testing, and publishing across many packages became unsustainable. The checking works standalone even if you don’t use the other features.
GitHub
npm: npm i -D @tspub-dev/tspub
Web: https://tspub.dev/