SayKit
ReferenceAPI

@saykit/config

API reference for the configuration package and CLI building blocks

@saykit/config exports the defineConfig helper, the underlying Zod schemas / types, and a small set of building blocks used by the CLI and plugins.

import { defineConfig } from '@saykit/config';
import type { Config, Bucket, Formatter, Transformer, Message } from '@saykit/config';

Most application code only ever uses defineConfig and the types. The other exports exist for plugin and transformer authors.

defineConfig(config)

Validate a SayKit configuration against the Zod schema and return the normalised result.

saykit.config.ts
import { defineConfig } from '@saykit/config';
import po from '@saykit/format-po';
import js from '@saykit/transform-js';

export default defineConfig({
  locales: ['en', 'fr'],
  buckets: [
    {
      include: ['src/**/*.ts'],
      output: 'src/locales/{locale}.{extension}',
      formatter: po(),
      transformer: js(),
    },
  ],
});

Returns the parsed Config. Throws if validation fails (with a Zod error containing the path and reason).

Types

Config

Prop

Type

Bucket

Prop

Type

After parsing, each bucket also exposes match(id) and an output value with a match(id) helper. Plugin authors can use these to test whether a file belongs to a bucket:

config.buckets[0]!.match('src/app/page.tsx'); // true if matched by include
config.buckets[0]!.output.match('src/locales/en.po'); // true if it's a translation file

Formatter

Prop

Type

Transformer

Prop

Type

Message

Prop

Type

Subpath exports

@saykit/config also exposes a few subpaths used by plugins and transformer authors.

@saykit/config/features/loader

import { resolveConfig } from '@saykit/config/features/loader';

resolveConfig(name?)

Search up from process.cwd() for saykit.config.{ts,mts,cts,js,mjs,cjs} (or ${name}.config.*), load it, and return the validated Config.

const config = resolveConfig(); // looks for saykit.config.*
const config = resolveConfig('my-tool'); // looks for my-tool.config.*

Used by:

  • the saykit CLI
  • unplugin-saykit
  • babel-plugin-saykit

The config is loaded from where it sits, so its relative imports and import.meta.dirname resolve against its own directory. Reading TypeScript requires Node 22.18+ or a runtime that loads it itself (Bun, Deno, tsx).

@saykit/config/features/messages

import {
  generateHash,
  assignSequenceIdentifiers,
  CompositeMessage,
  LiteralMessage,
  ArgumentMessage,
  ChoiceMessage,
  ElementMessage,
} from '@saykit/config/features/messages';

The internal Message types used by transformers (different from the public catalogue Message shape), plus helpers:

  • generateHash(text, context?), produces the same 6-character id SayKit uses elsewhere
  • assignSequenceIdentifiers(message, { current: 0 }), numbers positional placeholders depth-first

These exist so that custom transformers can produce the same id and placeholder shape the built-in transformers do.

@saykit/config/features/catalogue

import {
  expandBucketOutputPath,
  resolveFallbackChain,
  resolveCatalogueSources,
  assembleCatalogueRecord,
  pruneLocaleMessages,
} from '@saykit/config/features/catalogue';

Catalogue path and fallback helpers shared by the build plugins. Most apps never import these directly, they're what unplugin-saykit and babel-plugin-saykit use to resolve a locale's fallback chain and inline the merged record at build time.

  • expandBucketOutputPath(bucket, locale), the concrete output path for a locale.
  • resolveFallbackChain(config, locale), the ordered list of locales to try, ending at the source.
  • resolveCatalogueSources(config, bucket, id), resolves an imported catalogue path to its { locale, sources } (fallback files, most specific first).
  • assembleCatalogueRecord(bucket, contents), merges parsed catalogue contents into a single id → string record, most specific winning.
  • pruneLocaleMessages(existing, source), the filter used by saykit clean, drops orphaned and untranslated entries and adds nothing.

Next

On this page