Local package development
Publish types and build output
Compile only when necessary and align JavaScript entry points, source maps, declarations, and package exports.
TypeScript source is for maintainers. Consumers need JavaScript their runtime can execute plus type declarations (.d.ts files) if they use TypeScript.
For @acme/slugify-title, we author src/index.ts and publish compiled output in dist/.
Build JavaScript and declarations together
tsup.config.ts:
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
dts: true,
clean: true,
sourcemap: true
})
Run:
npm run build
ls dist
You should see files such as:
index.js
index.cjs
index.d.ts
index.js.map
Point exports at those files, not at src/:
{
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
}
}
Verify from the consumer fixture
In your TypeScript smoke project:
import { slugifyTitle } from '@acme/slugify-title'
const slug: string = slugifyTitle('Hello World!')
Run npx tsc --noEmit. If types resolve, TypeScript found index.d.ts through the types condition in exports.
Common failure: exports aim at missing files
If you rename a build output but forget package.json, Node fails at runtime and TypeScript fails in the editor. After every build config change, run the consumer fixture again.
Pure JavaScript libraries can skip a compile step and publish src/ directly. The moment you add TypeScript or non-standard syntax, a build step becomes part of the public contract.
Break one path on purpose once
Delete dist/index.d.ts, run the consumer TypeScript check, and read the error:
Could not find a declaration file for module '@acme/slugify-title'
That is what your users see when declarations are missing from the tarball. Add a CI step that fails on that error so it never ships again.
Source maps in dist/ help consumers debug minified or compiled output. They are optional for tiny libraries, but I include them when the compiled JS is hard to read.
Keep tsconfig.json strict enabled for library code. A loose type inside the package becomes a loose type in every project that installs it.
See Build and publish a TypeScript package to npm for a full walkthrough with dual ESM and CJS outputs.
Lesson completed