@savvy-web/tsdown-plugins
v1.1.10
Published
Interface-only tsdown/rolldown plugin pack powering @savvy-web/bundler
Readme
@savvy-web/tsdown-plugins
The plugin pack behind @savvy-web/bundler. It holds every build behavior the bundler drives — entry detection, manifest emission and catalog resolution, the dts tsconfig port, the per-target build loop and the build-output reporter — as composable helpers and one rolldown plugin. It is authored against rolldown's plugin type only, so it imports no tsdown runtime and declares no tsdown peer dependency: bring your own tsdown.
Most packages should use @savvy-web/bundler directly. Reach for this package when you have outgrown the bundler's front door and want to compose the same building blocks in a hand-written tsdown.config.ts.
Install
npm install --save-dev @savvy-web/tsdown-plugins tsdown
# or
pnpm add -D @savvy-web/tsdown-plugins tsdowntsdown is a peer of your own choosing — the plugin pack never pins it.
Quick start
Compose the helpers in a tsdown.config.ts to reproduce the bundler's front door yourself:
// tsdown.config.ts
import { packageJsonEntries, emitManifest } from "@savvy-web/tsdown-plugins";
import { defineConfig } from "tsdown";
const sourceDir = process.cwd();
export default defineConfig({
entry: packageJsonEntries({ cwd: sourceDir }),
// emitManifest writes a transformed, catalog-resolved package.json into the output pkg/
plugins: [emitManifest({ sourceDir, targetGroup: { id: "npm", isProd: true } })],
});packageJsonEntries reads a package's exports and bin and returns the Record<name, path> that tsdown accepts as entry. emitManifest reads the package.json under sourceDir, writes the transformed manifest and copies LICENSE/README.md into the output folder.
Features
- Entry detection —
packageJsonEntriesandextractEntriesderive build entries from a package'sexportsandbin, matching the rules used across the Silk Suite builders. - Manifest transforms —
transformManifest,transformExports,transformBinandnormalizeBinPathsrewrite a sourcepackage.jsoninto a publishable one;emitManifestis the rolldown plugin that writes it. A dual-format build emits bothimportandrequireexport conditions, and a"./package.json": "./package.json"entry is added to the exports map so consumers canimport "<pkg>/package.json". - Ambient
.d.tsexports —extractAmbientDtsandclassifyDtsExportpick the types-only, hand-authored declaration exports out of a package'sexportsmap;transformExportsrewrites each to a key-derived{ types }pointer andcopyAmbientDtscopies the source declaration verbatim into every target dir, preserving its extension.findRelativeSpecifiersrejects a non-self-contained declaration andmixedDtsExportErrorrejects an export that mixes a hand-authoredtypeswith a runtime source. - Catalog resolution —
resolveManifestresolvescatalog:andworkspace:specifiers against the workspace, delegating toworkspaces-effect'sCatalogResolver. - Multi-target resolution —
resolveTargetsturns apublishConfig.targetsmap into the distinct byte-variant groups to build and the registry bindings for each;writeTargetsBindingpersists that resolution asdist/prod/targets.jsonfor the release step. - JSX resolution —
resolveJsxConfigandreadTsconfigJsxderive the effective JSX transform from a package's tsconfig, with an explicit override winning. - Executable binaries —
normalizeExeOptionsfills the SEA defaults and infers targets from the package'sos/cpu;runExeBuilddrives@tsdown/exeto compile the binaries. - Config validation — the
ConfigValidatorEffect service (withConfigValidatorLive) fast-fails on a badpublishConfig.targets,exeormetaconfig, raising the typedConfigValidationError. - dts tsconfig port —
buildResolvedTsconfigandwriteResolvedTsconfigwrite a temp tsconfig with absolute paths so type declarations emit cleanly under pnpm symlinks. - Per-target build loop —
deriveTargetGroupOptionsandbuildTargetGroupsmap a target to itstsdownoptions and run the build once per target, exposed as a helper so the escape hatch gets multi-target builds too. Aformatof["esm", "cjs"](theBuildFormattype) derives a dual-format build — a require-able CJS output with default-export interop and.d.ctsdeclarations alongside the ESM one. ThebundleNodeModules,bundledPackagesanddtsExternalsoptions thread the dependency-bundling posture into both the JS and declaration passes. A per-entry override partition can also setplatform(the JS-pass target,"browser"for a client bundle),css(forwarded to tsdown'scssoption for@tsdown/css) andoutSubdir(build the partition into an isolated<group>/pkg/<subdir>/sub-package). Thedefineoption forwards compile-time global replacements to both passes, merged with an auto-injectedprocess.env.__PACKAGE_VERSION__constant. - Loose files —
normalizeLooseFilesresolves aLooseFilesmap of literal output filenames toNormalizedLooseFiledescriptors, inferring the module format from each.mjs/.cjskey and raisingConfigValidationErroron a path separator, an unsupported extension or an ambiguous.js.buildTargetGroupstakes the normalized form as itslooseFilesoption and emits one extra single-entry, bundled, declaration-free and manifest-free pass per file per target group, inheriting the group's bundling posture so each file is self-contained. - Bundled declarations — each target runs two
tsdownpasses: a JavaScript pass that preserves per-module output, then a declaration-only pass that rolls every re-exported type into a single.d.tsper public entry (deriveDtsPassOptions). Per-module JavaScript stays intact while consumers keep reaching re-exported types through your published subpaths. - API Extractor meta —
runMetaPassis the single meta-generation orchestrator the bundler front door and both self-hosting escape hatches share: it derives the export paths, applies the optimistic next-version forward-look and drives API Extractor over a package's emitted.d.tsto write an api-model bundle (.api.json,tsdoc-metadata.json, resolvedtsconfig.json).generateMetais the lower-level pass it wraps, andnormalizeMetaOptionsfills theMetaOptionsdefaults that drive it. - Output reporter —
renderReportplus theBuildReportschema and a set of formatters (terminal, JSON, markdown, CI annotations, silent) render a build report for humans, agents or CI. - Issues artifact —
writeIssuesArtifact(with the pureflattenIssuesandserializeIssuesbehind it) writes a deduplicateddist/<target>/issues.jsonon every build, collecting the build's warnings, errors and suppressed diagnostics in a stable JSON shape (BuildIssues/PlainDiagnostic) so an agent or CI script reads the diagnostics straight from disk instead of parsing terminal output.
Effect
The package is implemented in Effect, but Effect runs behind the plugin boundary: the catalog wrapper returns a Promise and the reporter is rendered with Effect.runPromise at the call site. The plugin and helper values you compose are plain rolldown-conformant objects. effect and its @effect/* companions ship as regular dependencies, so you install nothing extra and your project's own Effect versions never affect the plugin pack.
