<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: girigirianish</title>
    <description>The latest articles on DEV Community by girigirianish (@girigirianish).</description>
    <link>https://dev.to/girigirianish</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F481512%2F4247c6da-ee1a-41d7-9659-42e4a4ef832d.jpeg</url>
      <title>DEV Community: girigirianish</title>
      <link>https://dev.to/girigirianish</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/girigirianish"/>
    <language>en</language>
    <item>
      <title>The packaging bugs I kept shipping (and the tool I built to stop)</title>
      <dc:creator>girigirianish</dc:creator>
      <pubDate>Wed, 18 Feb 2026 12:33:00 +0000</pubDate>
      <link>https://dev.to/girigirianish/the-packaging-bugs-i-kept-shipping-and-the-tool-i-built-to-stop-lkh</link>
      <guid>https://dev.to/girigirianish/the-packaging-bugs-i-kept-shipping-and-the-tool-i-built-to-stop-lkh</guid>
      <description>&lt;p&gt;I've published TypeScript packages that passed all my tests, built cleanly, and had types resolving perfectly in my editor then broke for consumers.&lt;/p&gt;

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

&lt;p&gt;After debugging the same class of problem for the third time, I decided to automate the checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5l9ji55eqyz4puuqm7db.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5l9ji55eqyz4puuqm7db.png" alt="Demo" width="800" height="763"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes wrong
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. CJS/ESM format mismatch
&lt;/h3&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Wrong condition ordering
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.d.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This one has bitten me multiple times because the exports field looks correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Missing declarations for subpaths
&lt;/h3&gt;

&lt;p&gt;Your main entry has &lt;code&gt;index.d.ts&lt;/code&gt;. But your package exports 20 subpaths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"./utils"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/utils.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"./hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist/hooks.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;I built tspub to catch these. It checks 70 rules across exports, types, files, metadata, imports, and package size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx @tspub-dev/tspub check

exports/format-mismatch    ./dist/cjs/index.cjs contains ESM syntax
exports/types-first        &lt;span class="s2"&gt;"types"&lt;/span&gt; should be first &lt;span class="k"&gt;in &lt;/span&gt;conditions
types/no-any-export        ./dist/index.d.ts exports 12 &lt;span class="sb"&gt;`&lt;/span&gt;any&lt;span class="sb"&gt;`&lt;/span&gt; types

3 problems &lt;span class="o"&gt;(&lt;/span&gt;1 auto-fixable&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some rules I haven't seen in other tools:&lt;/p&gt;

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

&lt;p&gt;You can also auto fix certain issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx @tspub-dev/tspub check &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This handles safe fixes like condition reordering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any package without installing
&lt;/h2&gt;

&lt;p&gt;I built a web version too: visit &lt;strong&gt;&lt;a href="https://tspub.dev" rel="noopener noreferrer"&gt;tspub.dev/check/YOUR-PACKAGE&lt;/a&gt;&lt;/strong&gt; to check any npm package instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beyond checking
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tspub init       &lt;span class="c"&gt;# scaffold a correctly-configured package&lt;/span&gt;
tspub build      &lt;span class="c"&gt;# ESM + CJS + .d.ts generation&lt;/span&gt;
tspub check      &lt;span class="c"&gt;# 70-rule validation&lt;/span&gt;
tspub test-types &lt;span class="c"&gt;# type-level test runner&lt;/span&gt;
tspub publish    &lt;span class="c"&gt;# npm + GitHub releases&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;It's still early and I'd genuinely appreciate feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does it catch real issues in your packages?&lt;/li&gt;
&lt;li&gt;False positives?&lt;/li&gt;
&lt;li&gt;Missing rules?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/tspub/tspub" rel="noopener noreferrer"&gt;link&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;npm&lt;/strong&gt;: &lt;code&gt;npm i -D @tspub-dev/tspub&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Web&lt;/strong&gt;: &lt;a href="https://tspub.dev" rel="noopener noreferrer"&gt;tspub.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>npm</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
