Keep your TypeScript
.d.tsfiles in sync with your runtime defaults.
SyncDocDefaults automatically injects literal values from your runtime defaults into TypeScript declaration files (.d.ts) via JSDoc @default tags — and can assert that those declarations stay in sync in CI.
| Feature | Description |
|---|---|
| Inject mode | Synchronize @default JSDoc tags automatically |
| Assert mode | Ensure .d.ts reflects real runtime values |
| TS-aware | Works with both TypeScript sources and compiled JS |
| Simple config | Just point to your defaults and interface |
| CI-ready | Clear exit codes and machine-readable results |
When writing library types, you often want consumers to see real default values inline:
export interface ExampleOptions {
/**
* Foo string.
* @default "bar"
*/
foo?: string;
}...but those defaults actually live elsewhere:
// constants.ts
export const DEFAULTS = { foo: 'bar' };Over time, those can drift apart. SyncDocDefaults eliminates that duplication.
✅ Source of truth = your runtime defaults
✅ .d.ts JSDoc updated automatically on build/publish
✅ CI can verify correctness (assert mode)
pnpm add -D sync-doc-defaults
# or
npm i -D sync-doc-defaultsRequires Node 18+
sync-doc-defaults inject # Patch @default tags in .d.ts files
sync-doc-defaults assert # Verify they are correctShort alias:
sdd inject
sdd assertpnpm build
sdd inject
git diff # View updated defaults in dist/*.d.tspnpm build
sdd assertUsage:
sync-doc-defaults <inject|assert> [options]
sdd <inject|assert> [options]
Options:
-c, --config <file> Path to config file (searched upward if omitted)
--dry (inject) Show changes without writing files
--quiet Suppress normal logs
--debug-paths Print detailed resolution breadcrumbs
--ts <auto|on|off> TypeScript mode (default: auto)
--tag <default|defaultValue> JSDoc tag to render for defaults (default: default)
Exit codes:
0 success
1 assertion / validation failure
2 config not found
3 loading or import error (missing file, tsx, etc.)
4 invalid config
5 CLI usage error
6 unexpected / general error
By default, the CLI searches upward from cwd for one of:
docdefaults.config.(ts|mjs|cjs|js|json)— recommendedsync-doc-defaults.config.(ts|mjs|cjs|js|json)— explicit alternative
/** @type {import('sync-doc-defaults').DocDefaultsConfig} */
export default {
// Path to the module exporting your defaults (TS, JS, or JSON)
defaults: 'src/constants.ts',
// Optional tsconfig path (used to infer declaration locations)
tsconfig: 'tsconfig.json',
// Optional preferred JSDoc tag to inject: 'default' (recommended) or 'defaultValue'
tag: 'default',
// One or more targets to sync
targets: [
{
types: 'src/types.ts', // Type source
interface: 'ExampleOptions', // Interface name
dts: 'dist/types.d.ts', // Optional explicit .d.ts path
member: 'DEFAULTS', // Exported symbol or dotted path
},
],
};-
Config discovery: upward search for
docdefaults.config.*, unless--configis provided. -
Defaults module (
defaults): supports.ts,.js,.json.--ts auto(default): prefer built JS; fallback totsxif present.--ts on: requiretsx; load TS directly.--ts off: require compiled JS/JSON only.- Env override:
SYNCDOCDEFAULTS_TS=on|off|auto
-
Built types (
dts): inferred via yourtsconfig’srootDiranddeclarationDirif not specified.
sync-doc-defaults reads your generated .d.ts files to update JSDoc.
Ensure your build emits them before running.
Example tsconfig:
If using ESM, make sure all relative imports in your compiled JS include
.jsextensions (e.g.import './util/logger.js').
In package.json:
{
"scripts": {
"build": "tsup",
"test": "vitest",
"types:inject": "pnpm build && sync-doc-defaults inject",
"types:assert": "pnpm build && sync-doc-defaults assert"
}
}SyncDocDefaults automatically serializes primitives (string, number, boolean, null) and JSON-serializable objects.
Short values are written inline:
/** @default ["a","b","c"] */
items?: string[];Nested or long objects are pretty-printed:
/**
* @default
* {
* "retry": 3,
* "backoffMs": 200
* }
*/
options?: { retry?: number; backoffMs?: number };Functions, classes, and other computed defaults cannot be serialized automatically. Document them manually:
/**
* @default computed at runtime
* @remarks Derived from NODE_ENV and feature flags.
*/
transform?: (input: string) => string;or:
/**
* @default "DefaultTransform"
* @remarks See src/transform/default.ts
*/
transform?: (input: string) => string;-
Modular architecture:
src/dts-ops/*,src/infra/*, etc. -
Run tests locally:
pnpm vitest
-
Use
--dryto preview injected blocks without writing files.
Fix
- Add
"type": "module"to the nearestpackage.json - or rename the file to
.mjs - or run within a project that already uses ESM.
Likely your compiled JS has missing .js extensions.
Fix
- Add
.jsto all relative imports in compiled JS - or run with
--ts on/SYNCDOCDEFAULTS_TS=on - or
pnpm add -D tsxand keep--ts auto
Fix
- Build your project so compiled JS exists in your
outDir - or install
tsxlocally and keep--ts auto - or force TypeScript loading with
--ts on
Fix
Add tsx to your project (not globally):
pnpm add -D tsxFor path-resolution breadcrumbs, pass
--debug-pathsor setSYNCDOCDEFAULTS_DEBUG_PATHS=1.
MIT © Enkosi Ventures
See CONTRIBUTING.md to get started, and SECURITY.md for how to report vulnerabilities.
typedoc— generate full API docschangesets— version & release automation used here
{ "compilerOptions": { "rootDir": "src", "outDir": "dist/src", "declaration": true, "declarationDir": "dist/types", "module": "ESNext", "target": "ES2020", "moduleResolution": "Bundler" } }