Build and publish npm packages using Bun as the primary toolchain with npm-compatible output. Use when the user wants to create a new npm library, set up a TypeScript package for publishing, configure build/test/lint tooling for a package, fix CJS/ESM interop issues, or publish to npm. Covers scaffolding, strict TypeScript, Biome + ESLint linting, Vitest testing, Bunup bundling, and publishing workflows. Keywords: npm, package, library, publish, bun, bunup, esm, cjs, exports, typescript, biome, vitest, changesets.
npx claudepluginhub joshuarweaver/cascade-content-creation-misc-1 --plugin jwynia-agent-skills-1This skill uses the workspace's default tool permissions.
Build and publish npm packages using Bun as the primary runtime and toolchain, producing output that works everywhere npm packages are consumed.
reference/esm-cjs-guide.mdreference/publishing-workflow.mdreference/strict-typescript.mdscripts/scaffold.tstemplates/biome.jsontemplates/bunup.config.tstemplates/eslint.config.tstemplates/gitignoretemplates/package.json.hbstemplates/src/index.test.ts.hbstemplates/src/index.ts.hbstemplates/tsconfig.jsontemplates/vitest.config.tsGuides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Build and publish npm packages using Bun as the primary runtime and toolchain, producing output that works everywhere npm packages are consumed.
Use when:
Do NOT use when:
npx-cli skill)| Concern | Tool | Why |
|---|---|---|
| Runtime / package manager | Bun | Fast install, run, transpile |
| Bundler | Bunup | Bun-native, dual output, .d.ts generation |
| Type declarations | Bunup (via tsc) | Integrated with build |
| TypeScript | module: "nodenext", strict: true + extras | Maximum correctness for published code |
| Formatting + basic linting | Biome v2 | 10-25x faster than ESLint, single tool |
| Type-aware linting | ESLint + typescript-eslint | 40+ type-aware rules Biome can't do |
| Testing | Vitest | Test isolation, mature mocking, coverage |
| Versioning | Changesets | File-based, explicit, monorepo-ready |
| Publishing | npm publish --provenance | Trusted Publishing / OIDC |
Run the scaffold script to generate a complete project:
bun run <skill-path>/scripts/scaffold.ts ./my-package \
--name my-package \
--description "What this package does" \
--author "Your Name" \
--license MIT
Options:
--dual — Generate dual CJS/ESM output (default: ESM-only)--no-eslint — Skip ESLint, use Biome onlyThen install dependencies:
cd my-package
bun install
bun add -d bunup typescript vitest @vitest/coverage-v8 @biomejs/biome @changesets/cli
bun add -d eslint typescript-eslint # unless --no-eslint
my-package/
├── src/
│ ├── index.ts # Package entry point — all public API exports here
│ └── index.test.ts # Tests co-located with source
├── dist/ # Built output (gitignored, included in published tarball)
├── .changeset/
│ └── config.json
├── package.json
├── tsconfig.json
├── bunup.config.ts
├── biome.json
├── eslint.config.ts # Type-aware rules only
├── vitest.config.ts
├── .gitignore
├── README.md
└── LICENSE
Read these reference docs before modifying any configuration. They contain the reasoning behind each decision and the sharp edges that cause subtle breakage:
exports map configuration, dual package hazard, module-sync, common mistakesfiles field, Trusted Publishing, CI pipelineThese are the rules that, when violated, cause the most common and painful bugs in published packages. Follow these without exception.
Always use "type": "module" in package.json. ESM-only is the correct default. require(esm) works in all supported Node.js versions.
Always use exports field, not main. main is legacy. exports gives precise control over what consumers can access.
types must be the first condition in every exports block. TypeScript silently fails to resolve types if it isn't.
Always export "./package.json": "./package.json". Many tools need access to the package.json and exports encapsulates completely.
Use files: ["dist"] in package.json. Whitelist approach prevents shipping secrets. Never use .npmignore.
Run npm pack --dry-run before every publish. Verify the tarball contains exactly what you intend.
Use module: "nodenext" for published packages. Not "bundler". Code satisfying nodenext works everywhere; the reverse is not true.
strict: true is non-negotiable. Without it, your .d.ts files can contain types that error for consumers using strict mode.
Enable noUncheckedIndexedAccess. Catches real runtime bugs from unguarded array/object access.
Ship declarationMap: true. Enables "Go to Definition" to reach original source for consumers.
Do not use path aliases (paths) in published packages. tsc does not rewrite them in emitted code. Consumers can't resolve them.
any is banned. Use unknown and narrow. Suppress with // biome-ignore suspicious/noExplicitAny: <reason> only when genuinely unavoidable, and always include the reason.
Prefer named exports over default exports. Default exports behave differently across CJS/ESM boundaries.
Always use import type for type-only imports. Enforced by both verbatimModuleSyntax and Biome's useImportType rule.
Build with Bunup using format: ['esm'] (or ['esm', 'cjs'] for dual). Bunup handles .d.ts generation, external detection, and correct file extensions.
Set engines.node to >=20.19.0 in package.json. This documents the minimum supported Node.js version (first LTS with stable require(esm)).
Use Vitest, not bun:test. bun:test lacks test isolation — module mocks leak between files. Vitest runs each test file in its own worker.
Set coverage thresholds (branches, functions, lines, statements all ≥ 80%). Enforced in vitest.config.ts.
# Write code and tests
bun run test:watch # Vitest watch mode
# Check everything
bun run lint # Biome + ESLint
bun run typecheck # tsc --noEmit
bun run test # Vitest run
# Build
bun run build # Bunup → dist/
# Prepare release
bunx changeset # Create changeset describing changes
bunx changeset version # Bump version, update CHANGELOG
# Publish
bun run release # Build + npm publish --provenance
When the package needs to expose multiple entry points:
src/utils.tsentry: ['src/index.ts', 'src/utils.ts']{
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"default": "./dist/utils.js"
},
"./package.json": "./package.json"
}
}
Reminder: Adding or removing export paths is a semver-major change.
If consumers require CJS support for Node.js < 20.19.0:
format: ['esm', 'cjs']module-sync, import, and require conditionsbun build does not generate .d.ts files. Use Bunup (which delegates to tsc) or run tsc --emitDeclarationOnly separately.bun build CJS output is experimental. Always use target: "node" for npm-publishable CJS. target: "bun" produces Bun-specific wrappers.bun build does not downlevel syntax. Modern ES2022+ syntax ships as-is. If targeting older runtimes, additional transpilation is needed.bun publish does not support --provenance. Use npm publish for provenance signing.bun publish uses NPM_CONFIG_TOKEN, not NODE_AUTH_TOKEN. CI pipelines may need adjustment.