Simple utilities for modern npm packages.
npm install @sanity/pkg-utils -D# Initialize a new package
pnpx @sanity/pkg-utils@latest init my-package
# In a Node.js package directory with `package.json` present
# Check the package
pkg-utils check
# Build the package
pkg-utils build
# Watch the package
pkg-utils watchRun pkg-utils -h for more information on CLI usage.
Since v12, @sanity/pkg-utils composes tsdown and
@sanity/tsdown-config:
the hand-written exports map in package.json stays the input that decides what gets built, and
for every platform (the default build, plus one extra build per browser/node exports condition)
pkg-utils resolves a config from @sanity/tsdown-config, layers its own opinions over it
(browserslist-driven syntax targets, build-time constants, exports reconciliation), and runs
tsdown's programmatic build().
tsdown.config.* files are never loaded — package.config.ts is the only configuration source of
pkg build. For customizations beyond the options below, use tsdown +
@sanity/tsdown-config directly instead.
Builds regenerate the exports map from the build (with source conditions for development, and
a source-less publishConfig.exports for publishing) and keep it in sync — including in CI —
so environments that set CI=true without meaning "skip package.json" still behave correctly.
For conditional entries, generated conditions are materialized in both maps, then the authored
subpath and condition order of each map is preserved independently on later builds. Plain-string
entries keep their compact shape. This lets you reorder conditions directly in package.json;
earlier matching conditions take precedence over later ones.
@sanity/pkg-utils reads most of its configuration from package.json. But sometimes you need more
control. You may then add a configuration file named package.config.ts (or .mts, .js, or
.mjs).
// package.config.ts
import {defineConfig} from '@sanity/pkg-utils'
export default defineConfig({
tsdoc: {
rules: {
// do not require internal members to be prefixed with `_`
'ae-internal-missing-underscore': 'off',
},
},
// the path to the tsconfig file for distributed builds
tsconfig: 'tsconfig.dist.json',
})- Type:
PkgBundle[] - Default:
undefined
An array of entry points to bundle. This is useful if you want to bundle something that should not be exported by the package, e.g. CLI scripts or Node.js workers.
- Type:
boolean | string[] - Default:
true
tsdown's clean option, passed through as-is. Cleaning is
on by default: true cleans the dist folder before the build, false skips cleaning, and a
string[] replaces the default with the listed paths/globs — include dist when you still want it
cleaned alongside other folders (e.g. clean: ['dist', 'coverage'] replaces a
"clean": "rimraf dist coverage" script). pkg build --no-clean skips cleaning for a single run.
- Type:
Record<string, string | number | boolean | null | undefined> - Default:
{}
An object defining globals within the package. process.env.PKG_VERSION is always defined,
replaced with the package's version (overridable with a PKG_VERSION environment variable).
- Type:
UserConfig['deps'] - Default:
undefined
tsdown's deps option, passed through as-is:
neverBundle marks dependencies as external, alwaysBundle forces a dependency to be inlined
(types included — type inlining follows the bundling decisions). Note tsdown's matching semantics:
strings match exactly (or as globs), so covering subpath imports takes a pattern like
/^@scope\/name(\/|$)/.
- Type:
string - Default:
'./dist'
The path to the directory to which bundle and chunk files should be written.
- Type:
false | DtsOptions - Default:
undefined
tsdown's dts options, passed through as-is (an object, or
false to skip generating .d.ts files entirely). For example dts: {tsgo: true} selects the
Go-native TypeScript compiler for type generation.
- Type:
PkgConfigProperty<PkgExports> - Default: the value of
"exports"inpackage.json
Override or modify the value of the exports before it’s parsed internally.
- Type:
boolean - Default:
process.env.NODE_ENV !== 'production'
Gates the migration checks for options that were removed or deprecated in v12 (they throw helpful
errors with migration instructions). On by default outside production builds, where migration
mistakes surface during development; skipped when NODE_ENV=production.
- Type:
boolean - Default:
false
Whether to fully minify the bundled JavaScript (identifier mangling and whitespace removal included). The output is always compressed (constant folding, dead code elimination) with function/class names preserved.
- Type:
UserConfig['plugins'] - Default:
[]
Extra rolldown plugins, appended after the plugins pkg-utils sets up. Most Rollup plugins are also compatible.
- Type:
boolean | ReactCompilerOptions - Default:
false
Runs babel-plugin-react-compiler (which must be installed) on the source files before they are
bundled, so published components are memoized automatically. Pass true for the defaults, or an
options object to configure the compiler (e.g. {target: '18'}).
- Type:
'*' | 'browser' | 'node' - Default:
'*'
Default runtime of package exports
- Type:
boolean - Default:
true
Whether to include source map files.
- Type:
string - Default:
'./src'
The path to the directory in which source code is located.
- Type:
boolean | StyledComponentsOptions - Default:
false
Applies the styled-components transform (displayName, componentId, CSS minification, etc)
using oxc's native port of babel-plugin-styled-components — no Babel dependencies required.
- Type:
string - Default:
'tsconfig.json'
The path to the TypeScript configuration file.
- Type:
boolean | PackageTsdocOptions - Default:
true
Runs API Extractor during pkg build and again during
pkg check to check that TSDoc tags are valid and release tags are correct. Set
tsdoc: false to disable it.
- Type:
boolean | PackageVanillaExtractOptions - Default:
false
Extracts the CSS from vanilla-extract .css.ts files into
dist/bundle.css (minified and lowered with lightningcss), injects the self-referential
import "<pkg>/bundle.css", emits a no-op bundle-css.js shim for runtimes that cannot import
.css files, and writes the conditional "./bundle.css" export to package.json.
- Type:
PackageCssOptions - Default:
undefined
Enables the @tsdown/css pipeline (install it
first — it is an optional peer dependency) for plain CSS, CSS modules, preprocessors, and
Lightning CSS / PostCSS. The emitted CSS gets the same minify and lowering settings as
vanillaExtract, and the same conditional CSS export treatment: the
self-referential import "<pkg>/style.css", a no-op style-css.js shim with its
style-css.d.ts declaration, and the conditional "./style.css" export written to
package.json.
A package can also publish a stylesheet directly, without any JS importing it. Declare a .css
export subpath with a source and nothing else:
{
"exports": {
".": {"source": "./src/index.ts", "default": "./dist/index.js"},
"./ui/styles.css": {"source": "./src/ui/styles.css"},
"./package.json": "./package.json"
}
}pkg build compiles ./src/ui/styles.css to dist/ui/styles.css — the emitted path follows the
export subpath — and fills in the rest of the conditions:
"./ui/styles.css": {
"source": "./src/ui/styles.css",
"types": "./dist/ui/styles-css.d.ts",
"browser": "./dist/ui/styles.css",
"style": "./dist/ui/styles.css",
"node": "./dist/ui/styles-css.js",
"default": "./dist/ui/styles-css.js"
}Consumers then import "<pkg>/ui/styles.css", which resolves to the stylesheet in bundlers and
browsers, and to the no-op shim in Node and similar runtimes. This turns on the @tsdown/css
pipeline automatically; set css explicitly to customize it.
Add the CSS to sideEffects in package.json so it survives tree-shaking in consumers:
{
"sideEffects": ["*.css"]
}v12 replaced the rollup/rolldown build stack with tsdown + @sanity/tsdown-config. Most
packages need no changes; removed options throw an error with migration instructions when set
(gated by legacyChecks), and deprecated options keep working with a warning.
See MIGRATE.md for the full guide — including how
conditional package.json#imports replace the
removed PKG_FORMAT/PKG_RUNTIME constants, and import.meta.url replaces PKG_FILE_PATH.
MIT