dev-npm-package

Develop npm packages with Node.js and TypeScript following modern best practices. Use when: (1) Creating a new npm package, (2) Setting up package.json exports (dual ESM/CJS or ESM-only), (3) Configuring TypeScript for library authoring (Bundler or Node16 moduleResolution), (4) Building/publishing with tsup or tsc, (5) Creating CLI tools with bin field, (6) Testing with vitest, (7) CI/CD for npm publishing, (8) ESM/CJS interop issues. Keywords: npm package, publish to npm, library development.

6 stars

Best use case

dev-npm-package is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Develop npm packages with Node.js and TypeScript following modern best practices. Use when: (1) Creating a new npm package, (2) Setting up package.json exports (dual ESM/CJS or ESM-only), (3) Configuring TypeScript for library authoring (Bundler or Node16 moduleResolution), (4) Building/publishing with tsup or tsc, (5) Creating CLI tools with bin field, (6) Testing with vitest, (7) CI/CD for npm publishing, (8) ESM/CJS interop issues. Keywords: npm package, publish to npm, library development.

Teams using dev-npm-package should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/dev-npm-package/SKILL.md --create-dirs "https://raw.githubusercontent.com/Takazudo/claude-resources/main/skills/dev-npm-package/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/dev-npm-package/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How dev-npm-package Compares

Feature / Agentdev-npm-packageStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Develop npm packages with Node.js and TypeScript following modern best practices. Use when: (1) Creating a new npm package, (2) Setting up package.json exports (dual ESM/CJS or ESM-only), (3) Configuring TypeScript for library authoring (Bundler or Node16 moduleResolution), (4) Building/publishing with tsup or tsc, (5) Creating CLI tools with bin field, (6) Testing with vitest, (7) CI/CD for npm publishing, (8) ESM/CJS interop issues. Keywords: npm package, publish to npm, library development.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# npm Package Development

## Quick Start: Recommended Stack

- **Build**: tsup (esbuild-powered, zero-config, dual CJS/ESM) or tsc alone (for ESM-only packages)
- **Test**: vitest (native ESM/TS, Jest-compatible API)
- **Lint**: Biome (all-in-one linter+formatter) or ESLint flat config + Prettier
- **Types**: TypeScript with `moduleResolution: "Bundler"` (with tsup) or `"Node16"` (with tsc alone)
- **Dev**: tsx for running TS, tsup --watch for rebuilding
- **Publish validation**: publint + @arethetypeswrong/cli (attw)

## Determine Package Type

1. **Library package** -> Follow "Library Setup" below
2. **CLI tool package** -> Follow "CLI Setup" below
3. **Both** -> Combine both patterns

## Library Setup

### Minimal package.json

```json
{
  "name": "my-library",
  "version": "0.1.0",
  "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"
      }
    }
  },
  "files": ["dist"],
  "sideEffects": false,
  "engines": { "node": ">=18" },
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "test": "vitest",
    "test:run": "vitest run",
    "lint": "biome check .",
    "typecheck": "tsc --noEmit",
    "prepublishOnly": "npm run build"
  },
  "devDependencies": {
    "@biomejs/biome": "^2.3",
    "tsup": "^8.4",
    "typescript": "^5.7",
    "vitest": "^3.0"
  }
}
```

### tsup.config.ts

```ts
import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["src/index.ts"],
  format: ["cjs", "esm"],
  dts: true,
  splitting: false,
  sourcemap: true,
  clean: true,
});
```

### tsconfig.json

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": ["ES2022"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "noUncheckedIndexedAccess": true,
    "noEmit": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
```

### File Structure

```
my-library/
  src/
    index.ts
    index.test.ts
  package.json
  tsconfig.json
  tsup.config.ts
  vitest.config.ts
  biome.json
  .gitignore
  LICENSE
  README.md
```

## ESM-Only Library Setup

For packages targeting modern Node.js (>=18) without CJS compatibility needs. Simpler than dual publishing.

### Minimal package.json

```json
{
  "name": "@myorg/my-library",
  "version": "0.1.0",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "files": ["dist"],
  "engines": { "node": ">=18" },
  "scripts": {
    "build": "tsc",
    "test": "vitest run",
    "prepublishOnly": "tsc && vitest run"
  },
  "devDependencies": {
    "typescript": "^5.7",
    "vitest": "^3.0"
  }
}
```

### tsconfig.json (Node16, no bundler)

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
```

**Important**: With `Node16` resolution, all relative imports must include the `.js` extension (even for `.ts` source files): `import { foo } from './utils.js'`.

### When to choose ESM-only over dual CJS/ESM

- Your package targets Node.js >=18 (or >=22 where CJS can `require()` ESM natively)
- You don't need CJS consumers
- You want the simplest possible setup with `tsc` only (no bundler)

## CLI Setup

### package.json (CLI-specific fields)

```json
{
  "bin": {
    "my-cli": "dist/cli.js"
  },
  "files": ["dist"]
}
```

**Note**: npm recommends bin paths without a `./` prefix (`"dist/cli.js"` not `"./dist/cli.js"`). Modern npm normalizes this automatically, but omitting `./` avoids warnings in older npm versions. Run `npm pkg fix` to check for issues.

### Entry file (src/cli.ts)

```ts
#!/usr/bin/env node
import { program } from "commander";

program
  .name("my-cli")
  .version("1.0.0")
  .description("Description here");

program
  .command("init")
  .option("-t, --template <name>", "template to use", "default")
  .action((options) => {
    console.log(`Template: ${options.template}`);
  });

program.parse();
```

CLI argument parsing libraries: **commander** (most popular, subcommands), **yargs** (validation, middleware), **citty** (lightweight ESM-first).

## Key Rules

### exports Field

- Always place `types` before `default` within each condition block
- `import` condition for ESM, `require` condition for CJS
- `main`/`module`/`types` at top level exist for backward compatibility with older tools

### files Field

Always use `files` as a whitelist (not `.npmignore`). Set to `["dist"]` to publish only build output. Verify with `npm pack --dry-run`.

### prepublishOnly

Always include a `prepublishOnly` script to build (and ideally test) before publishing:

```json
{ "prepublishOnly": "npm run build && npm test" }
```

For tsc-only projects, you can call commands directly: `"prepublishOnly": "tsc && vitest run"`.

### Scoped Packages

For scoped packages (`@myorg/pkg`), configure public access via `.npmrc` in the project root:

```
access=public
```

Alternatively, use `publishConfig` in `package.json`:

```json
{ "publishConfig": { "access": "public" } }
```

### sideEffects

Set `"sideEffects": false` for pure utility libraries to enable tree-shaking. If some files have side effects, list them: `"sideEffects": ["*.css"]`.

### Tree-Shaking

Use named exports (not default export of objects). Avoid classes when individual functions suffice.

## Pre-Publish Checklist

```bash
npm run build              # Build the package
npx publint                # Validate package.json/exports
npx attw --pack .          # Validate TypeScript types
npm pack --dry-run         # Inspect package contents
npm publish --dry-run      # Simulate publish
```

## Detailed References

Read these when you need specifics:

- **Build tools, tsconfig, testing, linting, monorepo**: [references/tooling.md](references/tooling.md) - tsup/tsdown/unbuild comparison, TypeScript config, vitest setup, Biome vs ESLint, pnpm workspaces + Turborepo, dev workflow
- **Publishing, versioning, CI/CD, security**: [references/publishing.md](references/publishing.md) - semver, Changesets/semantic-release, GitHub Actions OIDC trusted publishing, npm provenance, publint/attw, size-limit, supply chain security
- **Architecture, ESM/CJS, exports, CLI, dependencies**: [references/patterns.md](references/patterns.md) - dual publishing patterns, conditional exports, subpath exports, dependency types (peer/optional/bundled), CLI bin setup, argument parsing, tree-shaking optimization

Related Skills

dev-wip-package-upstream-wt-dev

6
from Takazudo/claude-resources

Workflow for editing a WIP upstream package (consumed by this project via a sibling `file:../upstream/...` dep) when a fix or feature requires changing the upstream's code. ALWAYS work in a git worktree of the upstream — never on the shared `../upstream/` root checkout — because every consumer using that sibling shares the same on-disk HEAD. Use when: (1) The fix lives in the upstream package's source, not the consumer's, (2) User says 'edit upstream', 'fix upstream', 'patch upstream', 'upstream PR', 'I need to change the upstream framework', 'fix zfb / zdtp upstream', (3) Triaging a consumer-side issue and the root cause is in the upstream library, (4) Bumping the consumer's pin requires landing an upstream PR first.

dev-wip-package-refer

6
from Takazudo/claude-resources

Pattern for consuming an in-progress (WIP) upstream npm/pnpm package from a sibling git checkout via a `file:../{name}/...` relative dep — without publishing the package. Use when: (1) Setting up a consumer project that needs to depend on a local in-development library or framework checked out next to it, (2) User mentions 'file: dep', 'sibling repo', 'upstream package', 'wip package', 'monorepo-style refer', 'how do we consume the upstream', (3) Deciding between this pattern and a published npm version or a `github:` git-URL dep, (4) Setting up a fresh machine that already has a consumer project but the sibling upstream isn't cloned yet, (5) A consumer's CI is failing because the sibling upstream isn't where the `file:` spec expects it.

dev-tweak-serve-package-json

6
from Takazudo/claude-resources

Tweak serve/dev commands in package.json. Use when: (1) User says 'tweak serve', 'dev tweak serve', or 'tweak-serve', (2) User wants to add port-kill before dev/serve (--kill), (3) User wants :net LAN-accessible variants of dev/serve (--net). Flags: --kill adds predev port cleanup, --net adds 0.0.0.0 host variants.

dev-package-json

6
from Takazudo/claude-resources

Organize and maintain package.json and npm config (.npmrc) for readability and security. Use when: (1) Reorganizing scripts section or adding separators, (2) Extracting multi-process commands into shell scripts, (3) Setting up multi-environment dev commands (local/preview/prod), (4) Handling pnpm "Ignored build scripts" warnings, (5) Configuring .npmrc security (strictDepBuilds, allowBuilds, ignoredBuilds), (6) Managing pnpm via corepack and packageManager field, (7) Adding predev port cleanup. Keywords: package.json, npm scripts, .npmrc, pnpm, build scripts, supply chain, corepack, packageManager, predev, kill port, port in use.

zudoesa-articlify

6
from Takazudo/claude-resources

Convert conversation context into an esa article via the zudoesa-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write esa article', 'esa記事', 'esaに書いて', 'articlify for esa', or /zudoesa-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudoesa-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's esa writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's esa style, (2) User says 'apply voice', 'esa voice', 'esa文体で', 'esa風に書いて', '文体を適用', (3) User provides text to transform to esa style. Reads writing-style.md and vocabulary-rule.md from takazudo-esa-writing repo and applies the rules.

zudocg-articlify

6
from Takazudo/claude-resources

Convert conversation context into a CodeGrid article via the zudocg-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write codegrid article', 'CodeGrid記事', 'codegridに書いて', 'articlify for codegrid', or /zudocg-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudocg-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's CodeGrid writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's CodeGrid style, (2) User says 'apply voice', 'codegrid voice', 'codegrid文体で', 'codegrid風に書いて', '文体を適用', (3) User provides text to transform to CodeGrid style. Reads writing-style.md and vocabulary-rule.md from takazudo-codegrid-writing repo and applies the rules.

zpaper-articlify

6
from Takazudo/claude-resources

Convert conversation context into a zpaper blog article via the zpaper-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write zpaper article', 'zpaper記事', 'zpaperに書いて', 'articlify for zpaper', or /zpaper-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zpaper-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's zpaper blog writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's zpaper style, (2) User says 'apply voice', 'zpaper voice', 'zpaper文体で', 'zpaper風に書いて', 'ブログ文体を適用', (3) User provides text to transform to zpaper style. Reads writing-style.md and vocabulary-rule.md from the zpaper repo and applies the rules.

xlsx

6
from Takazudo/claude-resources

Spreadsheet creation, editing, and analysis. Use when working with .xlsx, .xlsm, .csv, .tsv files for: (1) Creating spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modifying existing spreadsheets while preserving formulas, (4) Data analysis and visualization, (5) Recalculating formulas.

x

6
from Takazudo/claude-resources

Facade for development workflows. Routes on two axes: plan-first vs implement-now (escalates to /big-plan -a when the request needs research / decomposition / has unclear scope — the appended -a makes the plan chain into implementation in-session), then single vs multi on the ready-to-build fast paths (/x-as-pr single-topic, /x-wt-teams multi-topic parallel). Use when: (1) User says '/x' followed by dev instructions, (2) User wants to start development without choosing the workflow skill, (3) User says 'dev', 'implement', or 'build' with a task. Default option: -v (verify-ui). Review-loop (-l) is opt-in — without -l the downstream skill runs a single /deep-review pass. Forwards -a (autonomy/auto-chain) and -m (merge at the end + cleanup + CI watch) through every route; auto-fix of raised findings (-f) and issue-raising (-ri) are downstream defaults, with -nf/--no-fix and -nori/--no-raise-issues as the forwarded opt-outs. -a and -m are orthogonal — full hands-off end-to-end is -a -m.