Write the publish manifest
package.json tells Node.js what users may import and tells pnpm what to install, pack, and publish.
A publishable SDK manifest
{
"name": "@your-scope/quiz-sdk",
"version": "0.1.0",
"description": "A typed SDK for fetching validated quiz questions.",
"license": "Apache-2.0",
"private": true,
"type": "module",
"exports": "./src/index.ts",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"prepack": "pnpm build"
},
"files": [
"dist",
"src/index.ts"
],
"publishConfig": {
"access": "public",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
},
"dependencies": {
"@your-scope/quiz-contracts": "workspace:*",
"arktype": "^2.2.3"
},
"devDependencies": {
"@your-scope/quiz-api": "workspace:*"
}
}
Keep private: true during development. Remove it after every release check passes. The rest of this lesson explains why each field exists.
Identity and legal metadata
name- The permanent registry identity. A scoped name belongs to an npm user or organization.
version- The immutable release identifier. npm will not let you replace the same name and version later.
description- Helps users understand search results and the package page.
license- Names the license using an SPDX identifier. Include the full matching text in
LICENSE. repository,homepage, andbugs- Add real URLs after the repository exists. In a monorepo, include the package directory in the repository object.
"repository": {
"type": "git",
"url": "git+https://github.com/your-name/quiz-sdk.git",
"directory": "packages/sdk"
},
"homepage": "https://github.com/your-name/quiz-sdk#readme",
"bugs": {
"url": "https://github.com/your-name/quiz-sdk/issues"
}
Do not invent repository URLs. Incorrect metadata breaks provenance and sends users to the wrong project.
Module format and public entry point
type: "module" tells Node.js to treat .js files as ES modules. The exports map defines which files consumers can import.
This workspace uses source during local development:
"exports": "./src/index.ts"
pnpm applies the publish-only override before packing:
"publishConfig": {
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
}
}
Users receive JavaScript and declarations from dist. They do not execute raw TypeScript. The exports map also blocks unsupported deep imports.
ESM-only is a compatibility choice
The import condition does not promise CommonJS require support. Add a tested require build only when consumers need it. Claiming both formats without testing both is worse than publishing ESM-only clearly.
Allow only intended files
"files": [
"dist",
"src/index.ts"
]
The files array is an allowlist. Tests, local configuration, and unrelated source stay out. npm always includes package.json, README, and LICENSE when present.
Keeping source alongside source maps improves debugging and editor navigation. For a small SDK, the size cost is minor. Large packages should measure before deciding.
Classify dependencies by who needs them
dependencies- Packages imported by published runtime JavaScript. Consumers install them automatically.
devDependencies- Tools and private packages needed only to develop, test, or build. Consumers do not install them as part of your package.
peerDependencies- Host libraries that the consumer must provide so your package shares the same instance, often frameworks or plugins. Do not use peers merely to reduce install size.
optionalDependencies- Runtime features that can be absent without making the package unusable. Code must handle their absence.
If emitted JavaScript contains import "arktype", ArkType belongs in dependencies. A successful local workspace build can hide a wrong classification because root dependencies are nearby.
Prevent stale build output
"scripts": {
"build": "tsc -p tsconfig.build.json",
"prepack": "pnpm build"
}
pnpm pack does not run an ordinary script named build. It does run the special prepack lifecycle. This guarantees that every tarball contains output generated from current source.
Write the files humans need
The package-root README.md should show installation, the smallest working call, configuration, important return types, and expected errors. npm renders it on the package page.
Choose a license intentionally. MIT is short and permissive. Apache-2.0 is also permissive and includes an explicit patent grant. License selection can have legal consequences, so organizations should follow their legal policy.
Official references
Check your understanding
Why is ArkType a dependency while Vitest is a development dependency?
Show answer
Published JavaScript runs ArkType in consumer projects. Vitest runs only while maintainers test the package.
Why keep
private: trueuntil release checks finish?Show answer
It turns an accidental publish command into a local error. Remove it only from packages you have deliberately prepared for publication.