Setup and context — Why Build npm Packages with an AI IDE?
The npm registry hosts over 2 million packages, and many developers dream of publishing their own utilities or open-source libraries. Yet the overhead of setting up TypeScript builds, configuring test frameworks, managing semantic versioning, and handling the publish workflow can slow you down before you even write your first function.
Antigravity's AI agents can automate much of this boilerplate. From scaffolding a dual-format project to generating comprehensive test suites and managing the entire publish pipeline, you can stay focused on what matters most: the library's core logic.
Project Scaffolding — Let the AI Handle the Setup
The first step is to ask Antigravity's agent to generate the project structure. In the terminal or chat panel, provide a prompt like this:
Create a TypeScript npm package project:
- Package name: @myorg/utils
- ESM + CJS dual output
- Bundle with tsup
- Test with vitest
- Lint and format with biome
The agent will generate a directory structure like the following:
@myorg/utils/
├── src/
│ └── index.ts # Entry point
├── tests/
│ └── index.test.ts # Test file
├── tsconfig.json # TypeScript config
├── tsup.config.ts # Bundle config (ESM + CJS)
├── vitest.config.ts # Test config
├── biome.json # Lint & format config
├── package.json # npm metadata
├── README.md # Documentation
├── LICENSE # License file
└── .gitignoreThe key here is requesting tsup for dual ESM and CJS output from the start. This ensures your library works with both modern import statements and legacy require() calls.
Designing package.json — Getting the exports Field Right
The most critical part of any npm package is the package.json configuration. When you ask the AI agent, it will generate an optimal setup like this:
{
"name": "@myorg/utils",
"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", "README.md", "LICENSE"],
"scripts": {
"build": "tsup",
"test": "vitest run",
"test:watch": "vitest",
"lint": "biome check .",
"prepublishOnly": "npm run build && npm run test"
}
}The exports field with conditional exports ensures that Node.js resolves the correct file based on the module system being used. Placing types first within each condition is a best practice for TypeScript resolution.
Antigravity's agent handles these nuances automatically, saving you the time of researching the correct configuration patterns.
Build Configuration with tsup — Dual ESM and CJS Output
tsup is a zero-config bundler purpose-built for TypeScript libraries, and its configuration is refreshingly simple.
// tsup.config.ts
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm", "cjs"], // Dual output
dts: true, // Auto-generate type definitions
splitting: false,
sourcemap: true,
clean: true, // Clean dist/ before build
outDir: "dist",
target: "es2022",
minify: false, // Libraries typically skip minification
});Run the build with a simple command:
# Run the build
npx tsup
# Verify the output
# dist/index.js — ESM format
# dist/index.cjs — CommonJS format
# dist/index.d.ts — Type definitions
# dist/index.d.cts — CJS type definitionsJust tell the Antigravity agent to "build and verify the output," and it will handle both the execution and file validation for you.
AI-Powered Test Generation
Reliable tests are essential for any published library. Antigravity's agent can read your implementation code and generate comprehensive tests, including edge cases.
Consider the following utility functions:
// src/index.ts
export function slugify(text: string): string {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/[\s_]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
export function truncate(str: string, maxLength: number, suffix = "..."): string {
if (str.length <= maxLength) return str;
return str.slice(0, maxLength - suffix.length) + suffix;
}
export function groupBy<T>(arr: T[], key: keyof T): Record<string, T[]> {
return arr.reduce((acc, item) => {
const group = String(item[key]);
(acc[group] ??= []).push(item);
return acc;
}, {} as Record<string, T[]>);
}Ask the agent to "generate tests for src/index.ts," and it will produce something like this:
// tests/index.test.ts
import { describe, it, expect } from "vitest";
import { slugify, truncate, groupBy } from "../src/index";
describe("slugify", () => {
it("converts a basic string to a slug", () => {
expect(slugify("Hello World")).toBe("hello-world");
});
it("removes special characters", () => {
expect(slugify("Hello, World! @2026")).toBe("hello-world-2026");
});
it("strips leading and trailing hyphens", () => {
expect(slugify(" --hello-- ")).toBe("hello");
});
it("handles empty strings", () => {
expect(slugify("")).toBe("");
});
it("collapses consecutive spaces into a single hyphen", () => {
expect(slugify("a b c")).toBe("a-b-c");
});
});
describe("truncate", () => {
it("returns strings shorter than the limit unchanged", () => {
expect(truncate("short", 10)).toBe("short");
});
it("truncates strings exceeding the limit", () => {
expect(truncate("this is a long sentence", 10)).toBe("this is...");
});
it("applies a custom suffix", () => {
expect(truncate("abcdefghij", 8, "…")).toBe("abcdefg…");
});
});
describe("groupBy", () => {
it("groups items by the specified key", () => {
const data = [
{ category: "a", value: 1 },
{ category: "b", value: 2 },
{ category: "a", value: 3 },
];
const result = groupBy(data, "category");
expect(result.a).toHaveLength(2);
expect(result.b).toHaveLength(1);
});
it("returns an empty object for empty arrays", () => {
expect(groupBy([], "key" as never)).toEqual({});
});
});Let the agent run vitest run and confirm all tests pass. If you're interested in a TDD-driven approach, check out Antigravity × TDD Test Automation Guide for an in-depth walkthrough.
Version Management and CHANGELOG Automation
Proper semantic versioning (semver) is critical when publishing packages. Antigravity's agent can analyze your changes and automate the entire version bump and CHANGELOG update process.
Analyze the recent commit history and suggest a semver version bump.
Update CHANGELOG.md in Keep a Changelog format.
The agent will categorize commits and make recommendations:
- fix: Patch version bump (0.1.0 → 0.1.1)
- feat: Minor version bump (0.1.0 → 0.2.0)
- BREAKING CHANGE: Major version bump (0.1.0 → 1.0.0)
The generated CHANGELOG follows a clean, standardized format:
## [0.2.0] - 2026-03-31
### Added
- `groupBy` utility function
- ESM + CJS dual output support
### Fixed
- Consecutive hyphens not being collapsed in `slugify`Publishing to npm — Pre-Publish Checks and Execution
Before publishing, ask the Antigravity agent to run a final checklist:
Run pre-publish checks:
- Verify the files field in package.json
- Confirm dist/ contains build artifacts
- Ensure README.md exists with usage instructions
- Verify LICENSE file exists
- Run npm pack --dry-run to inspect included files
The agent will execute npm pack --dry-run and display the package contents:
# Pre-publish inspection
npm pack --dry-run
# Example output:
# npm notice Tarball Contents
# npm notice 1.2kB dist/index.js
# npm notice 1.0kB dist/index.cjs
# npm notice 892B dist/index.d.ts
# npm notice 892B dist/index.d.cts
# npm notice 2.1kB README.md
# npm notice 1.1kB LICENSE
# npm notice 580B package.json
# npm notice Tarball Details
# npm notice name: @myorg/utils
# npm notice version: 0.2.0
# npm notice package size: 3.2 kBOnce everything checks out, publish with:
# Log in to npm (first time only)
npm login
# Publish (use --access public for scoped packages)
npm publish --access publicThe prepublishOnly script in package.json ensures that a build and test run happen automatically before every publish, preventing broken packages from reaching the registry.
CI/CD Integration — Auto-Publishing with GitHub Actions
For a more robust workflow, you can set up GitHub Actions to automatically publish your package when you create a release. Ask the Antigravity agent to generate the workflow:
# .github/workflows/publish.yml
name: Publish Package
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
registry-url: "https://registry.npmjs.org"
- run: npm ci
- run: npm run lint
- run: npm run test
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}The --provenance flag enables npm's supply chain security feature (provenance attestations), which increases trust in your package by linking it to a verifiable build process.
For more advanced CI/CD pipeline patterns, see Antigravity × GitHub Actions Advanced CI/CD Pipeline Guide for a comprehensive deep dive.
Summary
Antigravity's AI agents can streamline every phase of TypeScript npm package development. From project scaffolding and optimal package.json configuration to automated test generation, version management, and CI/CD-powered publishing, the agent handles the tedious parts so you can focus on building great library APIs.
Give it a try — publish your favorite utilities as npm packages and contribute to the open-source ecosystem.