npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oliveryasuna/tsdown-config

v1.0.1

Published

Readme

@oliveryasuna/tsdown-config

npm

A tiny set of composable tsdown presets for the three build targets worth naming — node, browser, and library — behind one defineConfig entry point.

The resolved config is a function of the platform you name and the overrides you pass, and nothing else. Each preset is a plain UserConfig object you can import and inspect; there is no probing, no environment sniffing, and no magic — if an option is set, it is set because you can see it in a preset or in your own override.

Status: the source is complete and dogfooded — this package builds itself with defineConfig('library', {format: 'esm'}). Not yet covered: there are no tests and no CI workflow. See Not yet wired.

Install

bun add -D tsdown @oliveryasuna/tsdown-config

tsdown is a peer dependency (^0.22.12) — you bring your own, and this package never pulls a second copy into your tree.

Usage

// tsdown.config.ts
import {defineConfig} from '@oliveryasuna/tsdown-config';

export default defineConfig('library');

That is the whole common case. Name a platform and you get that preset merged on top of the shared base.

Presets

Each preset is the base defaults plus a small platform layer. They are plain objects, so they are diffable, spreadable, and importable on their own.

| Platform | Intent | Format | target | Notable | | --- | --- | --- | --- | --- | | library | Platform-neutral package for both Node and the browser | ESM + CJS | es2020 | platform: 'neutral', fixedExtension | | node | Node 20+ runtime code | ESM + CJS | node20 | shims, nodeProtocol, fixedExtension | | browser | Modern-browser bundle | ESM | es2020 | minify, platform: 'browser' |

Passing anything else throws Unknown platform: <value> rather than silently producing a config with the base defaults dropped — so a typo in a plain-JS config file fails loudly instead of shipping a broken build.

Base defaults

Shared by every preset, from base:

| Option | Value | Why | | --- | --- | --- | | entry | ['src/index.ts'] | The conventional single entry; override for multi-entry packages | | clean | true | Wipe dist/ before each build | | dts | true | Emit type declarations | | sourcemap | true | Emit source maps | | treeshake | true | Drop dead code | | hash | false | Stable, predictable output filenames | | deps | {neverBundle: true} | Leave node_modules external; bundle only your own source |

format is deliberately not set in base — every preset names its own, so a base value would only ever be overwritten.

Overrides

defineConfig(platform, overrides?) accepts a second argument in three shapes. They compose, so you can reach for exactly as much power as a given tweak needs.

An object — merged one level deep onto the resolved preset (tsdown's own mergeConfig), which is enough for almost everything:

defineConfig('library', {format: 'esm', minify: true});

A function — receives the fully resolved config and returns the final one. This is the escape hatch for what a merge structurally cannot express: removing a preset default, or extending an array instead of replacing it.

// Append an entry rather than replacing the whole array
defineConfig('node', (config) => ({
  ...config,
  entry: [...config.entry, 'src/cli.ts']
}));

// Remove a preset default
defineConfig('browser', ({minify, ...rest}) => rest);

A function override is total: whatever it returns is the config. Spread ...config to keep the preset — return a bare {minify: true} and you have thrown the preset away.

An array — a list of layers folded left-to-right, each an object or a function. Later layers see the accumulated result of the earlier ones, and the last write wins:

defineConfig('node', [
  sharedOrgDefaults,                                   // your org's base tweaks
  (config) => ({...config, entry: [...config.entry, 'src/cli.ts']}),
  {minify: false}                                      // wins over anything above
]);

This is how you build a shared middle tier on top of these presets without forking them.

A top-level array is always treated as layers, not as tsdown's multiple-build array form. To override to a multi-build config, return it from a function layer: (config) => [buildA, buildB].

API

Everything is exported from the package root.

| Export | Kind | Description | | --- | --- | --- | | defineConfig | function | (platform, overrides?) => UserConfig — the entry point | | base | object | The shared base config | | library, node, browser | object | The three platform layers, for hand-composition | | Platform | type | 'node' \| 'browser' \| 'library' | | Override | type | Partial<UserConfig> \| ((resolved: UserConfig) => UserConfig) |

The raw presets are exported so you can compose them yourself when the factory's shape doesn't fit — mergeConfig(base, node, myLayer) is always available.

Development

bun install
bun run lint        # eslint, via @oliveryasuna/eslint-config
bun run typecheck   # tsc --noEmit
bun run build       # tsdown → dist/

Bun is pinned by .bun-version and checked by check:bun; husky, lint-staged, syncpack, and secretlint run on commit.

Build

tsdown bundles src/index.ts to ESM only — the entire consumer surface is an ESM config file:

| Artifact | Referenced by | | --- | --- | | dist/index.mjs | main, module, exports["."].default | | dist/index.d.mts | exports["."].types |

prepack runs the build, so npm publish cannot ship a stale or missing dist/ — which matters because dist/ is gitignored. Nothing runs on a consumer's machine: prepare and prepack are skipped when the package is installed as a dependency, and there are no preinstall/postinstall scripts.

Not yet wired

Honest gaps, so nobody finds them the hard way:

  • No tests. A handful of snapshot tests over the resolved presets and the override-folding are planned but do not exist yet.
  • No CI workflow, so lint, typecheck, and build are not enforced anywhere.
  • No LICENSE file yet, though package.json declares MIT.

License

MIT © Oliver Yasuna