Skip to Content
Exports@zamdevio/i18nprune/core

@zamdevio/i18nprune/core

The programmatic heart of i18nprune — battle-tested primitives used by the CLI itself.

API tiers (semver intent)

TierMeaning
StableDocumented for integrations; breaking changes ship on a major bump.
AdvancedSupported and typed, but sharper edges (heuristics, ordering, or TTY-only helpers) — read the section notes before upgrading blindly.

Stable (initial classification): resolveContext, clearContextCache, scanProjectLiteralKeyUsage, computeMissingLiteralKeys, computeMissingLiteralKeysFromResolvedKeys, resolvedLiteralKeysInProject, readJsonFile, collectStringLeaves, scanSources, isPreservePath, buildKeyReferenceContext, resolveReferenceConfig, scanProjectDynamicKeySites, CLI JSON output types (ValidateJsonOutput, CleanupJsonOutput, MissingJsonOutput, SyncJsonOutput), config types (I18nPruneConfig, Policies), Context, ResolvedPaths, ProjectLiteralKeyUsage.

Advanced: lower-level observation pipeline (scanProjectKeyObservations, literalKeyUsageFromObservations, resolvedKeysFromObservations, scanKeyObservations, exactLiteralKeys), per-file dynamic analysis helpers (findDynamicKeySites, analyzeDynamicKeysFromSourceText), template rebuild utilities (tryRebuildTemplateKeyFromConsts, tryResolveTemplatePrefixBeforeUnknown), and interactive helpers (canAsk, promptApprovedRemovalKeys, groupKeysByTopSegment) — use only when you need the same hooks as the CLI.

defineConfig is also re-exported from core for convenience; prefer @zamdevio/i18nprune/config when authoring config files.

Flat vs namespaced imports (both supported)

We fully support both styles for the same symbols:

  • Flatimport { resolveContext, scanProjectLiteralKeyUsage } from '@zamdevio/i18nprune/core'
    Fine for small scripts, snippets, and existing code. Not deprecated.

  • Namespacedimport { context, extractor } from '@zamdevio/i18nprune/core'
    Recommended for new integrations and larger tools: clearer grouping, easier discovery in the IDE, and a stable mental model that mirrors core/ domains.

Recommendation: prefer namespaces for new code; keep flat where it already works or reads shorter. There is no plan to remove flat exports before an explicit major version with a migration story. For CLI machine-readable output, see JSON output (--json).

Namespaced imports

import { context, extractor, validate, files } from '@zamdevio/i18nprune/core'; const ctx = context.resolveContext(); const usage = extractor.scanProjectLiteralKeyUsage(ctx); const raw = files.readJsonFile(ctx.paths.sourceLocale); const missing = validate.computeMissingLiteralKeys(ctx, raw);

Namespaces: context, extractor, dynamic, json, ask, preserve, reference, validate, scanner, files, result (envelope helpers + RESULT_API_VERSION).

Structured CLI JSON: global --json — see JSON output (--json).

When to use

Use this when building:

  • Custom CI scripts
  • Pre-commit hooks
  • Bulk migration tools
  • IDE extensions
  • Internal devops tooling

Core Functions

Context

import { resolveContext, clearContextCache } from '@zamdevio/i18nprune/core'; const ctx = resolveContext(); // uses process.cwd() const ctx2 = resolveContext('/path/to/project'); clearContextCache(); // for tests or long-running processes

Headless envelopes (no process.exit)

tryResolveContext returns a Result (no throw). runValidate, runConfig, runMissing, runSync, runCleanupCheck, runDoctor, runQuality, runReview, runLanguages, runGenerate (async), runReport (async) return the same CliJsonEnvelope the CLI uses for global --json (including issues[]). stringifyEnvelope serializes like stdout.

import { tryResolveContext, runValidate, runConfig, stringifyEnvelope, ISSUE_VALIDATE_MISSING_LITERAL_KEYS, } from '@zamdevio/i18nprune/core'; const res = tryResolveContext('/path/to/project'); if (!res.ok) { console.error(res.issues); } else { const v = runValidate(res.data); console.log(v.ok, v.issues, v.data.missing); console.log(stringifyEnvelope(runConfig(res.data), true)); }

generate / report: see JSON: programmatic — both have run* helpers; report still uses --format for the on-disk artifact.

buildConfigSnapshot (and thus config --json / runConfig) sets cliVersion from CLI_VERSION in packages/cli/src/constants/cli.ts (kept in sync with packages/cli/package.json).

run* vs commands/<name>/run.ts: the command file owns argv, resolveContext, human tables, logger, report file, exit code. run* in core/*/jsonEnvelope.ts builds the same domain outcome the JSON branch needs: gather data, attach issues[], wrap buildCliJsonEnvelope. The human branch often recomputes similar work (e.g. quality) because presentation (tables, colors, truncation) is separate; refactoring to one shared “compute then render” core is possible but not required for correctness.

See JSON: programmatic and issue codes.

Key Extraction & Scanning

import { scanProjectLiteralKeyUsage, scanProjectDynamicKeySites } from '@zamdevio/i18nprune/core'; const usage = scanProjectLiteralKeyUsage(ctx); const literalKeys = usage.resolvedKeys; const dynamicSites = scanProjectDynamicKeySites(ctx);

Missing literal keys (same as validate)

Uses per-file template + const resolution (same as keySites), not a single merged-source const map.

Typed CLI JSON payloads (import from @zamdevio/i18nprune/core): ValidateJsonOutput, CleanupJsonOutput, MissingJsonOutput — for validate, cleanup --json, and missing --json respectively.

import { computeMissingLiteralKeys, resolvedLiteralKeysInProject, readJsonFile, resolveContext, } from '@zamdevio/i18nprune/core'; const ctx = resolveContext(); const raw = readJsonFile(ctx.paths.sourceLocale); const missing = computeMissingLiteralKeys(ctx, raw); // dotted paths in code but not in that JSON const allLiterals = resolvedLiteralKeysInProject(ctx); // Set of resolved keys across src

To scan the project once (e.g. validate-style pipelines), reuse the same observations for missing keys and usage:

import { computeMissingLiteralKeysFromResolvedKeys, literalKeyUsageFromObservations, readJsonFile, resolveContext, scanProjectKeyObservations, } from '@zamdevio/i18nprune/core'; const ctx = resolveContext(); const observations = scanProjectKeyObservations(ctx); const usage = literalKeyUsageFromObservations(observations); const raw = readJsonFile(ctx.paths.sourceLocale); const missing = computeMissingLiteralKeysFromResolvedKeys(raw, usage.resolvedKeys);

Template rebuild and partial prefix

import { tryRebuildTemplateKeyFromConsts, tryResolveTemplatePrefixBeforeUnknown, } from '@zamdevio/i18nprune/core'; tryRebuildTemplateKeyFromConsts('a.${NS}.b', { NS: 'section' }); // => 'a.section.b' or null tryResolveTemplatePrefixBeforeUnknown('a.b.${id}.x', {}); // => 'a.b' when id is unknown

scanProjectDynamicKeySites uses the same rules: fully rebuilt templates are omitted from dynamic results; otherwise resolvedPrefix may be set. See docs/architecture/decisions/005-dynamic-key-rebuild-and-prefix.md.

Interactive cleanup helpers (--ask)

import { canAsk, promptApprovedRemovalKeys, groupKeysByTopSegment, } from '@zamdevio/i18nprune/core'; import type { PromptRemovalKeysMode } from '@zamdevio/i18nprune/core'; // Same primitives the `cleanup --ask` command uses; call only when canAsk() is true.

JSON Utilities

import { collectStringLeaves, readJsonFile } from '@zamdevio/i18nprune/core'; const sourceData = readJsonFile(ctx.paths.sourceLocale); const allLeaves = collectStringLeaves(sourceData);

Safety & Design

  • Same resolution logic as the CLI (no drift)
  • Pure functions where possible
  • No side effects except readJsonFile
  • Full TypeScript support

Heuristic extraction limits

Literal keys, key-site observations, and dynamic sites use pattern matching and per-file const maps, not a full TS program analysis. Indirect calls (e.g. const fn = t) and cross-file constants may not match CLI expectations.

Read Detection limits in docs/regex/ for an honest scope statement, then extraction and key-sites-and-dynamic for details.

See also