What are the metadata fields of a module?
TL;DR
For an npm package, package.json metadata describes identity (name, version), discovery and ownership (description, keywords, repository, bugs, license, author), runtime contract (type, main, exports, imports, types, files, bin, engines), dependencies, and scripts. Not every field is standardized by JavaScript itself, and package managers, Node.js, TypeScript, and bundlers consume different fields.
The most consequential fields are often exports, type, dependency categories, and files: mistakes there can break consumers or publish unintended files. Validate the packed artifact in both ESM/CommonJS environments that the package claims to support.
Example package contract
{"name": "@example/slugify","version": "2.1.0","description": "Create URL-safe slugs","type": "module","exports": {".": "./dist/index.js"},"types": "./dist/index.d.ts","files": ["dist", "README.md", "LICENSE"],"engines": {"node": ">=20"},"scripts": {"build": "build-command","test": "test-command"},"license": "MIT","repository": {"type": "git","url": "https://github.com/example/slugify.git"}}
Identity and publishing
nameidentifies the package in a registry and in import specifiers. Scoped names use forms such as@example/slugify.versionis the published package version. Registries commonly require a valid semantic version, but a dependency range such as^2.1.0has different meaning from the package's own exact version.private: trueprevents accidental publication in package managers that honor it; use it for application/workspace-only packages.filesis an allowlist for packed content, subject to package-manager inclusion and ignore rules. Inspect the actual package tarball before publishing.
Module entry points
typecontrols how Node.js interprets.jsfiles in that package scope:"module"for ESM or"commonjs"for CommonJS.exportsdefines public entry points and can provide conditional targets. When present, it also encapsulates undeclared subpaths.mainis the legacy primary CommonJS-style entry field and remains relevant to older tools.importsdefines package-internal aliases beginning with#under Node.js package rules.typespoints TypeScript-compatible tooling to the package's declaration entry.binexposes executable commands.
Supporting both ESM and CommonJS requires deliberate conditional exports and tests. Simply adding a module field is not a cross-runtime guarantee; that field is a bundler convention rather than the Node.js package contract.
Dependency fields
dependenciesare required when consumers run the package.devDependenciessupport development, build, and tests and are not runtime requirements of the published library.peerDependenciesdeclare a compatible host dependency that the consuming application should provide, commonly used for plugins.optionalDependenciesmay fail to install without making the whole installation fail; runtime code must handle their absence.
Misclassifying a runtime import as a development dependency can make the packed library work in its repository but fail for consumers. Conversely, putting build tools in runtime dependencies increases consumer installation cost.
Descriptive and operational metadata
description, keywords, homepage, repository, bugs, author or contributors, funding, and license help users evaluate and maintain the package. scripts defines lifecycle and project commands; install lifecycle scripts execute code and therefore deserve supply-chain review. engines communicates supported runtimes, but enforcement depends on the package manager and its configuration.
Validation before publishing
Pack the package locally, inspect the file list, install that tarball into a clean fixture, and test every documented import path. Check that source maps do not reveal unintended source, secrets, or internal paths, and that README examples use only exported entry points.