Fix: publint Not Working — Package Exports Invalid, Types Not Found, or Dual Package Errors
Quick Answer
How to fix publint package validation issues — exports field configuration, dual ESM/CJS packaging, type resolution, main/module/types fields, files array, and common packaging mistakes.
The Problem
publint reports errors on your package:
npx publint
# ✗ "exports['.'].import.types" types is not the first in the object
# ✗ "main" file does not exist
# ✗ "module" field should be ESM but found CJSOr consumers can’t import your package correctly:
import { something } from 'my-package';
// Error: Cannot find module 'my-package' or its corresponding type declarationsOr the package works in one environment but not another:
Works with: import { foo } from 'my-package'
Fails with: const { foo } = require('my-package')Why This Happens
publint checks that your package.json is configured correctly for publishing to npm. Modern packages need to support multiple module systems and provide proper type information:
exportsfield is the modern standard — it replacesmain,module, andtypesfor Node.js 12+. But many tools still read the legacy fields, so you need both.typesmust be first in conditional exports — TypeScript resolves types from the first matching condition. Iftypesisn’t listed beforedefault, TypeScript may not find your type declarations.- ESM and CJS have different file extensions —
.mjsis always ESM,.cjsis always CJS,.jsfollows"type"inpackage.json. Mismatched extensions cause “Cannot use import/require” errors. filescontrols what’s published — ifdist/isn’t in thefilesarray, npm publish excludes it, and consumers get an empty package.
Fix 1: Run publint and Fix Errors
npx publint
# Or check online: https://publint.dev/
# Common output:
# ✗ "exports['.'].import.types" types is not the first in the object
# ✗ "exports['.'].require" file does not exist
# ✓ "main" is valid
# ✓ "types" is validFix each error type:
// ❌ WRONG — types is not first
{
"exports": {
".": {
"import": {
"default": "./dist/index.js",
"types": "./dist/index.d.ts"
}
}
}
}
// ✅ CORRECT — types must be first
{
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
}
}Fix 2: Correct package.json for Dual ESM/CJS
// package.json — the complete modern setup
{
"name": "my-package",
"version": "1.0.0",
"description": "My awesome package",
"type": "module",
// Legacy fields — for older tools and bundlers
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
// Modern exports — takes precedence over main/module
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./utils": {
"import": {
"types": "./dist/utils.d.ts",
"default": "./dist/utils.js"
},
"require": {
"types": "./dist/utils.d.cts",
"default": "./dist/utils.cjs"
}
},
// Export CSS
"./styles.css": "./dist/styles.css",
// Export package.json (some tools need this)
"./package.json": "./package.json"
},
// What's included in the published package
"files": [
"dist",
"README.md",
"LICENSE"
],
// Side effects — enables tree-shaking
"sideEffects": false,
// Or specify files with side effects:
// "sideEffects": ["./dist/styles.css"]
// Engines
"engines": {
"node": ">=18"
},
// Keywords for npm search
"keywords": ["utility", "typescript"],
// Repository
"repository": {
"type": "git",
"url": "https://github.com/user/my-package"
},
"license": "MIT",
"scripts": {
"build": "tsup",
"lint": "publint && attw --pack",
"prepublishOnly": "npm run build && npm run lint"
}
}Fix 3: ESM-Only Package
// Simpler setup when you don't need CJS support
{
"name": "my-esm-package",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"sideEffects": false
}Fix 4: CJS-Only Package (Legacy)
// For packages that must support older Node.js
{
"name": "my-cjs-package",
"version": "1.0.0",
// No "type": "module" — defaults to CJS
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"require": "./dist/index.js"
}
},
"files": ["dist"]
}Fix 5: Validate with Multiple Tools
# publint — checks package.json configuration
npx publint
# arethetypeswrong — checks TypeScript resolution
npx @arethetypeswrong/cli --pack
# Shows how different moduleResolution settings resolve your types
# Are the Types Wrong output:
# ┌───────────────────┬──────────────────┬──────────────────┐
# │ │ node16 (import) │ node16 (require) │
# ├───────────────────┼──────────────────┼──────────────────┤
# │ "my-package" │ ✅ │ ✅ │
# │ "my-package/utils"│ ✅ │ ❌ No types │
# └───────────────────┴──────────────────┴──────────────────┘
# npm pack --dry-run — see what will be published
npm pack --dry-run
# Lists all files that will be in the tarball
# Check package size
npx pkg-size
# Or: npx bundlephobia my-package// package.json — validation scripts
{
"scripts": {
"build": "tsup",
"check:exports": "publint",
"check:types": "attw --pack",
"check:size": "pkg-size",
"prerelease": "npm run build && npm run check:exports && npm run check:types"
}
}Fix 6: Common Patterns and Fixes
// React component library
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./styles.css": "./dist/styles.css"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"sideEffects": ["*.css"]
}
// CLI tool
{
"type": "module",
"bin": {
"my-cli": "./dist/cli.js"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"]
}
// Package with subpath exports (e.g., my-pkg/server, my-pkg/client)
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./server": {
"types": "./dist/server.d.ts",
"default": "./dist/server.js"
},
"./client": {
"types": "./dist/client.d.ts",
"default": "./dist/client.js"
}
}
}Still Not Working?
“types is not the first in the object” — in each exports condition block, the types key must come before default, import, or require. TypeScript resolves types from the first matching condition, so types must be first for correct resolution.
“file does not exist” — the file referenced in exports, main, module, or types isn’t in the built output. Run your build command first, then run publint. Check that files in package.json includes the dist/ directory.
Package works with import but not require — the require condition in exports is missing or points to an ESM file. ESM files (.js with "type": "module", or .mjs) can’t be require()d. Generate a .cjs output for CJS consumers.
Types resolve in one moduleResolution but not another — run npx @arethetypeswrong/cli --pack to check all resolution modes. node16 and bundler resolve differently. The types condition in exports fixes most issues. For node16 CJS, you need .d.cts files alongside .cjs files.
For related packaging issues, see Fix: tsup Not Working and Fix: Changesets Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Changesets Not Working — Version Not Bumping, Changelog Empty, or GitHub Action Failing
How to fix @changesets/cli issues — changeset creation, version bumping, changelog generation, monorepo support, npm publishing, and GitHub Actions automation.
Fix: tsup Not Working — Build Failing, Types Not Generated, or ESM/CJS Output Wrong
How to fix tsup bundler issues — entry points, dual ESM/CJS output, TypeScript declaration files, external dependencies, tree shaking, and package.json exports configuration.
Fix: unbuild Not Working — Build Output Empty, Stub Mode Failing, or Rollup Errors
How to fix unbuild issues — build configuration, stub mode for development, ESM and CJS output, TypeScript declarations, external dependencies, and monorepo workspace builds.
Fix: Clack Not Working — Prompts Not Displaying, Spinners Stuck, or Cancel Not Handled
How to fix @clack/prompts issues — interactive CLI prompts, spinners, multi-select, confirm dialogs, grouped tasks, cancellation handling, and building CLI tools with beautiful output.