@mmstack/translate-tools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +146 -0
- package/cli.d.mts +1 -0
- package/cli.mjs +103 -0
- package/cli.mjs.map +1 -0
- package/commands-yoWN-Tne.mjs +722 -0
- package/commands-yoWN-Tne.mjs.map +1 -0
- package/index.d.mts +204 -0
- package/index.d.mts.map +1 -0
- package/index.mjs +2 -0
- package/package.json +38 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands-yoWN-Tne.mjs","names":[],"sources":["../../../packages/translate-tools/src/lib/identifier.ts","../../../packages/translate-tools/src/lib/nested.ts","../../../packages/translate-tools/src/lib/codegen.ts","../../../packages/translate-tools/src/lib/lift.ts","../../../packages/translate-tools/src/lib/loader.ts","../../../packages/translate-tools/src/lib/registry.ts","../../../packages/translate-tools/src/lib/discover.ts","../../../packages/translate-tools/src/lib/icu.ts","../../../packages/translate-tools/src/lib/update.ts","../../../packages/translate-tools/src/lib/commands.ts"],"sourcesContent":["/** A bare (unquoted) JavaScript identifier — used by both codegen and registry insertion to decide\n * whether an object key / locale must be string-quoted. Kept in one place so the two emit paths\n * can't disagree on quoting. */\nconst IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;\n\nexport function isIdentifier(value: string): boolean {\n return IDENTIFIER.test(value);\n}\n\n/** Best-effort valid identifier derived from an arbitrary name (e.g. a namespace). */\nexport function toIdentifier(name: string): string {\n const cleaned = name.replace(/[^A-Za-z0-9_$]/g, '');\n if (!cleaned) return 'source';\n return isIdentifier(cleaned) ? cleaned : `ns${cleaned}`;\n}\n","/**\n * A translator-facing translation tree: nested objects with string leaves (ICU messages). This is\n * the shape the tool exports to / imports from disk and the shape `createNamespace` /\n * `createTranslation` are authored in — the tool never touches the store's flattened form.\n */\nexport type NestedTranslation = { [key: string]: string | NestedTranslation };\n\n/** Serialize a nested translation tree to a JSON file body (one namespace, one locale). */\nexport function toJson(nested: NestedTranslation): string {\n return JSON.stringify(nested, null, 2) + '\\n';\n}\n\n/** Parse + validate a nested translation JSON file back into a {@link NestedTranslation}. */\nexport function fromJson(json: string): NestedTranslation {\n const parsed: unknown = JSON.parse(json);\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))\n throw new Error('Translation JSON must be a plain object.');\n return validate(parsed as Record<string, unknown>, '');\n}\n\n/** Walk a tree's string leaves, yielding `[dottedPath, message]` for each (used for validation). */\nexport function leafEntries(\n nested: NestedTranslation,\n prefix = '',\n): [string, string][] {\n const out: [string, string][] = [];\n for (const [key, value] of Object.entries(nested)) {\n const at = prefix ? `${prefix}.${key}` : key;\n if (typeof value === 'string') out.push([at, value]);\n else out.push(...leafEntries(value, at));\n }\n return out;\n}\n\n/** A flat `Map<dottedPath, message>` view of a tree, for key-by-key comparison. */\nexport function leafMap(nested: NestedTranslation): Map<string, string> {\n return new Map(leafEntries(nested));\n}\n\nfunction validate(value: Record<string, unknown>, path: string): NestedTranslation {\n for (const [key, child] of Object.entries(value)) {\n const at = path ? `${path}.${key}` : key;\n if (typeof child === 'string') continue;\n if (child !== null && typeof child === 'object' && !Array.isArray(child)) {\n validate(child as Record<string, unknown>, at);\n continue;\n }\n throw new Error(\n `Invalid translation value at \"${at}\": expected a string or a nested object.`,\n );\n }\n return value as NestedTranslation;\n}\n","import { isIdentifier } from './identifier';\nimport { type NestedTranslation } from './nested';\n\nexport type TranslationFileInput = {\n /** The imported source-namespace const (e.g. `quote`), whose `.createTranslation` we call. */\n namespaceVar: string;\n /** The exported const name for the generated locale (e.g. `quoteDe`). */\n exportName: string;\n /** Target locale (e.g. `de`). */\n locale: string;\n /** Import specifier for the source namespace module, relative to the generated file. */\n importPath: string;\n /** Whether the source namespace is the module's default export (→ a default import). */\n defaultImport?: boolean;\n /** The translated tree (plain strings — target locales never need `withParams`). */\n translation: NestedTranslation;\n};\n\nfunction key(k: string): string {\n return isIdentifier(k) ? k : JSON.stringify(k);\n}\n\n/**\n * Serialize a nested translation tree to a TypeScript object-literal string. Double-quoted via\n * `JSON.stringify`: correct escaping for ICU (which uses `'` for its own escaping, so single-quoting\n * would be unsafe). A consumer's formatter normalizes quote style/indentation on save.\n */\nexport function objectLiteralText(obj: NestedTranslation, depth = 1): string {\n const entries = Object.entries(obj);\n if (entries.length === 0) return '{}';\n const inner = ' '.repeat(depth + 1);\n const pad = ' '.repeat(depth);\n const lines = entries.map(([k, value]) => {\n const v =\n typeof value === 'string'\n ? JSON.stringify(value)\n : objectLiteralText(value, depth + 1);\n return `${inner}${key(k)}: ${v},`;\n });\n return `{\\n${lines.join('\\n')}\\n${pad}}`;\n}\n\n/**\n * Emit a target-locale TypeScript module that calls `createTranslation` on the source namespace —\n * the TS-first import format. The output is valid TS that round-trips through {@link liftObjectLiteral}.\n */\nexport function codegenTranslationFile(input: TranslationFileInput): string {\n const importClause = input.defaultImport\n ? input.namespaceVar\n : `{ ${input.namespaceVar} }`;\n return (\n `import ${importClause} from ${JSON.stringify(input.importPath)};\\n\\n` +\n `export const ${input.exportName} = ${input.namespaceVar}.createTranslation(\\n` +\n ` ${JSON.stringify(input.locale)},\\n` +\n ` ${objectLiteralText(input.translation, 1)},\\n` +\n `);\\n`\n );\n}\n","import { Node, type ObjectLiteralExpression } from 'ts-morph';\nimport { type NestedTranslation } from './nested';\n\n/**\n * Statically lift a `createNamespace`/`createTranslation` translation object literal into a plain\n * {@link NestedTranslation} — no code execution. String leaves and nested objects pass through;\n * `withParams<…>('msg')` is unwrapped to its message string (the type arg is compile-time only and\n * irrelevant to the round-trip). Anything dynamic (a variable, a concatenation, a spread) throws —\n * the author must inline a literal for the tool to round-trip it.\n */\nexport function liftObjectLiteral(obj: ObjectLiteralExpression): NestedTranslation {\n const out: NestedTranslation = {};\n\n for (const prop of obj.getProperties()) {\n if (!Node.isPropertyAssignment(prop))\n throw new Error(\n `Unsupported entry in translation object (only \"key: value\" is supported): ${prop.getText()}`,\n );\n\n out[propKey(prop.getNameNode())] = liftValue(prop.getInitializerOrThrow());\n }\n\n return out;\n}\n\nfunction propKey(name: Node): string {\n if (Node.isStringLiteral(name) || Node.isNoSubstitutionTemplateLiteral(name))\n return name.getLiteralText();\n if (Node.isIdentifier(name)) return name.getText();\n throw new Error(`Unsupported translation key: ${name.getText()}`);\n}\n\nfunction liftValue(node: Node): string | NestedTranslation {\n if (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node))\n return node.getLiteralText();\n\n if (Node.isObjectLiteralExpression(node)) return liftObjectLiteral(node);\n\n if (Node.isCallExpression(node)) {\n if (node.getExpression().getText() === 'withParams') {\n const arg = node.getArguments()[0];\n if (\n arg &&\n (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg))\n )\n return arg.getLiteralText();\n }\n throw new Error(\n `Unsupported call in a translation value (only withParams('literal') is supported): ${node.getText()}`,\n );\n }\n\n throw new Error(\n `Unsupported translation value (expected a string literal, withParams('literal'), or a nested object): ${node.getText()}`,\n );\n}\n","import {\n type ArrowFunction,\n type FunctionExpression,\n Node,\n SyntaxKind,\n} from 'ts-morph';\n\nexport type ParsedLoader = {\n /** The module specifier of the dynamic `import(...)`. */\n importPath: string;\n /**\n * The property chain accessed on the resolved module (`m.quote.translation` → `['quote',\n * 'translation']`). Empty for the `() => import('path')` shorthand, where `@mmstack/translate`\n * auto-resolves the module's `default` / `translation` export.\n */\n accessor: string[];\n};\n\n/**\n * Parse a namespace loader into its import path + accessor chain, matching every shape\n * `@mmstack/translate` accepts at runtime:\n * - `() => import('path').then((m) => m.a.b)` → `['a', 'b']`\n * - `() => import('path').then((m) => m.default)` → `['default']`\n * - `() => import('path')` (shorthand) → `[]` (default / translation auto-resolve)\n * Returns `null` for any other shape (async/await, computed access) — the caller warns and skips.\n */\nexport function parseLoader(\n fn: ArrowFunction | FunctionExpression,\n): ParsedLoader | null {\n const body = Node.isArrowFunction(fn) ? fn.getBody() : undefined;\n if (!body || !Node.isCallExpression(body)) return null;\n\n // `() => import('path')` shorthand — no `.then`, no accessor.\n if (body.getExpression().getKind() === SyntaxKind.ImportKeyword) {\n const pathArg = body.getArguments()[0];\n if (!isPathLiteral(pathArg)) return null;\n return { importPath: pathArg.getLiteralText(), accessor: [] };\n }\n\n const thenAccess = body.getExpression();\n if (!Node.isPropertyAccessExpression(thenAccess) || thenAccess.getName() !== 'then')\n return null;\n\n const importCall = thenAccess.getExpression();\n if (\n !Node.isCallExpression(importCall) ||\n importCall.getExpression().getKind() !== SyntaxKind.ImportKeyword\n )\n return null;\n\n const pathArg = importCall.getArguments()[0];\n if (!isPathLiteral(pathArg)) return null;\n\n const cb = body.getArguments()[0];\n if (!cb || !Node.isArrowFunction(cb)) return null;\n const accessor = accessorChain(cb.getBody());\n if (!accessor) return null;\n\n return { importPath: pathArg.getLiteralText(), accessor };\n}\n\n// runtime `import()` accepts a template-literal specifier too — treat `` import(`./x`) `` like import('./x')\nfunction isPathLiteral(\n node: Node | undefined,\n): node is Node & { getLiteralText(): string } {\n return (\n !!node &&\n (Node.isStringLiteral(node) || Node.isNoSubstitutionTemplateLiteral(node))\n );\n}\n\nfunction accessorChain(node: Node): string[] | null {\n const parts: string[] = [];\n let cur: Node = node;\n while (Node.isPropertyAccessExpression(cur)) {\n parts.unshift(cur.getName());\n cur = cur.getExpression();\n }\n // the chain must bottom out at the callback parameter identifier (e.g. `m`)\n return Node.isIdentifier(cur) && parts.length > 0 ? parts : null;\n}\n","import {\n type CallExpression,\n Node,\n type ObjectLiteralExpression,\n type SourceFile,\n SyntaxKind,\n} from 'ts-morph';\nimport { isIdentifier } from './identifier';\n\n/**\n * Find every `registerNamespace(...)` call in a source file. `registerRemoteNamespace` is\n * deliberately ignored — its translations are served, not authored in files, so there's nothing to\n * export or round-trip.\n */\nexport function findRegisterNamespaceCalls(sf: SourceFile): CallExpression[] {\n return sf\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .filter((call) => call.getExpression().getText() === 'registerNamespace');\n}\n\nfunction localeProperty(obj: ObjectLiteralExpression, locale: string) {\n return obj.getProperties().find((p) => {\n if (!Node.isPropertyAssignment(p)) return false;\n const name = p.getNameNode();\n const text = Node.isStringLiteral(name) ? name.getLiteralText() : name.getText();\n return text === locale;\n });\n}\n\n/**\n * Insert (or replace) a locale's loader in a `registerNamespace(default, other)` call's `other`\n * map, so a freshly-imported locale is actually registered. Mutates the call in place — the caller\n * saves the source file afterwards.\n */\nexport function addLocaleLoader(\n call: CallExpression,\n locale: string,\n importPath: string,\n exportName: string,\n): void {\n if (call.getArguments().length < 2) call.addArgument('{}');\n\n const other = call.getArguments()[1];\n if (!other || !Node.isObjectLiteralExpression(other))\n throw new Error(\n \"registerNamespace's second argument must be an object literal of locale loaders.\",\n );\n\n localeProperty(other, locale)?.remove();\n\n other.addPropertyAssignment({\n name: isIdentifier(locale) ? locale : JSON.stringify(locale),\n initializer: `() => import(${JSON.stringify(importPath)}).then((m) => m.${exportName})`,\n });\n}\n","import * as path from 'node:path';\nimport {\n type CallExpression,\n Node,\n type ObjectLiteralExpression,\n type Project,\n type SourceFile,\n} from 'ts-morph';\nimport { toIdentifier } from './identifier';\nimport { liftObjectLiteral } from './lift';\nimport { parseLoader } from './loader';\nimport { type NestedTranslation } from './nested';\nimport { findRegisterNamespaceCalls } from './registry';\n\nexport type DiscoveredLocale = {\n locale: string;\n /** File the loader imports the translation from. */\n moduleFilePath: string;\n /** The binding name to import (named export name, or a name to default-import as). */\n exportName: string;\n /** Whether the binding is the module's default export (→ a default, not named, import). */\n isDefaultExport: boolean;\n translation: NestedTranslation;\n};\n\nexport type DiscoveredNamespace = {\n namespace: string;\n /** File containing the `registerNamespace(...)` call (where new-locale loaders are inserted). */\n registryFilePath: string;\n /** Index of the matching `registerNamespace(...)` call within that file's calls. */\n registryCallIndex: number;\n /** The default loader's namespace = the source/default-locale translation. */\n source: DiscoveredLocale;\n /** Target locales declared in the `other` map. */\n locales: DiscoveredLocale[];\n};\n\nexport type DiscoverOptions = {\n /** Label for the default/source translation (the app's defaultLocale). Defaults to `en`. */\n sourceLocale?: string;\n onWarn?: (message: string) => void;\n};\n\n/** Discover every `registerNamespace` registry in a ts-morph project and lift its translations. */\nexport function discoverFromProject(\n project: Project,\n options: DiscoverOptions = {},\n): DiscoveredNamespace[] {\n const sourceLocale = options.sourceLocale ?? 'en';\n const warn = options.onWarn ?? (() => undefined);\n const out: DiscoveredNamespace[] = [];\n\n for (const sf of project.getSourceFiles()) {\n findRegisterNamespaceCalls(sf).forEach((call, index) => {\n const found = discoverCall(project, sf, call, index, sourceLocale, warn);\n if (found) out.push(found);\n });\n }\n return out;\n}\n\ntype Resolved = {\n kind: 'namespace' | 'translation';\n namespace?: string;\n translation: NestedTranslation;\n moduleFilePath: string;\n exportName: string;\n isDefaultExport: boolean;\n};\n\nfunction discoverCall(\n project: Project,\n registryFile: SourceFile,\n call: CallExpression,\n registryCallIndex: number,\n sourceLocale: string,\n warn: (m: string) => void,\n): DiscoveredNamespace | null {\n const defaultArg = call.getArguments()[0];\n if (!defaultArg || !Node.isArrowFunction(defaultArg)) {\n warn(`registerNamespace default loader is not an arrow function in ${registryFile.getFilePath()}`);\n return null;\n }\n const parsedDefault = parseLoader(defaultArg);\n if (!parsedDefault) {\n warn(`Unsupported default loader shape in ${registryFile.getFilePath()}`);\n return null;\n }\n const resolvedDefault = resolveExport(project, registryFile, parsedDefault, warn);\n if (!resolvedDefault || resolvedDefault.kind !== 'namespace' || !resolvedDefault.namespace) {\n warn(`Could not resolve namespace from default loader in ${registryFile.getFilePath()}`);\n return null;\n }\n\n const locales: DiscoveredLocale[] = [];\n const other = call.getArguments()[1];\n if (other && Node.isObjectLiteralExpression(other)) {\n for (const prop of other.getProperties()) {\n if (!Node.isPropertyAssignment(prop)) continue;\n const locale = nameText(prop.getNameNode());\n const init = prop.getInitializer();\n if (!init || !Node.isArrowFunction(init)) {\n warn(`Locale \"${locale}\" loader is not an arrow function`);\n continue;\n }\n const parsed = parseLoader(init);\n if (!parsed) {\n warn(`Unsupported loader shape for locale \"${locale}\"`);\n continue;\n }\n const resolved = resolveExport(project, registryFile, parsed, warn);\n if (!resolved) continue;\n locales.push({\n locale,\n moduleFilePath: resolved.moduleFilePath,\n exportName: resolved.exportName,\n isDefaultExport: resolved.isDefaultExport,\n translation: resolved.translation,\n });\n }\n }\n\n return {\n namespace: resolvedDefault.namespace,\n registryFilePath: registryFile.getFilePath(),\n registryCallIndex,\n source: {\n locale: sourceLocale,\n moduleFilePath: resolvedDefault.moduleFilePath,\n exportName: resolvedDefault.exportName,\n isDefaultExport: resolvedDefault.isDefaultExport,\n translation: resolvedDefault.translation,\n },\n locales,\n };\n}\n\nfunction resolveExport(\n project: Project,\n fromFile: SourceFile,\n parsed: { importPath: string; accessor: string[] },\n warn: (m: string) => void,\n): Resolved | null {\n const moduleFile = resolveModule(project, fromFile.getFilePath(), parsed.importPath);\n if (!moduleFile) {\n warn(`Cannot resolve module \"${parsed.importPath}\" from ${fromFile.getFilePath()}`);\n return null;\n }\n\n const target = resolveLoaderTarget(moduleFile, parsed.accessor);\n if (!target) {\n const what = parsed.accessor[0] ? `export \"${parsed.accessor[0]}\"` : 'default/translation export';\n warn(`Could not resolve a ${what} in ${moduleFile.getFilePath()}`);\n return null;\n }\n const { call: init, isDefaultExport } = target;\n const callee = init.getExpression();\n const base = {\n moduleFilePath: moduleFile.getFilePath(),\n isDefaultExport,\n };\n\n // `createNamespace('ns', {…})` and `base.createMergedNamespace('ns', {…})` share the same\n // (namespace, literal) shape — and a merged namespace's literal holds only ITS own keys (the\n // merge is type-level), so each namespace exports independently with no duplication.\n const isCreateNamespace =\n Node.isIdentifier(callee) && callee.getText() === 'createNamespace';\n const isMergedNamespace =\n Node.isPropertyAccessExpression(callee) &&\n callee.getName() === 'createMergedNamespace';\n\n if (isCreateNamespace || isMergedNamespace) {\n const ns = stringArg(init, 0);\n const lit = objArg(init, 1);\n if (ns === null || !lit) return null;\n return {\n kind: 'namespace',\n namespace: ns,\n translation: liftObjectLiteral(lit),\n exportName: target.bindingName || toIdentifier(ns),\n ...base,\n };\n }\n\n if (Node.isPropertyAccessExpression(callee) && callee.getName() === 'createTranslation') {\n const lit = objArg(init, 1);\n if (!lit) return null;\n return {\n kind: 'translation',\n translation: liftObjectLiteral(lit),\n exportName: target.bindingName || 'translation',\n ...base,\n };\n }\n\n warn(`Export in ${moduleFile.getFilePath()} is not a createNamespace/createMergedNamespace/createTranslation call`);\n return null;\n}\n\ntype LoaderTarget = {\n call: CallExpression;\n /** Binding name to import; empty when the default export is anonymous (caller derives one). */\n bindingName: string;\n isDefaultExport: boolean;\n};\n\n/** Map a parsed loader accessor to the `create*` call it resolves to, matching the same\n * default / translation auto-resolution `@mmstack/translate` does at runtime. */\nfunction resolveLoaderTarget(\n moduleFile: SourceFile,\n accessor: string[],\n): LoaderTarget | null {\n // Explicit named accessor: m.quote.translation, m.quoteDe, m.translation.\n if (accessor.length > 0 && accessor[0] !== 'default') {\n const call = variableCall(moduleFile, accessor[0]);\n return call ? { call, bindingName: accessor[0], isDefaultExport: false } : null;\n }\n\n // `() => import('x')` (accessor []) or `.then((m) => m.default)`: resolve the default export,\n // and for the shorthand fall back to a named `translation` export (the `{ translation }` shape).\n const fromDefault = defaultExportCall(moduleFile);\n if (fromDefault) return fromDefault;\n if (accessor.length === 0) {\n const call = variableCall(moduleFile, 'translation');\n if (call) return { call, bindingName: 'translation', isDefaultExport: false };\n }\n return null;\n}\n\nfunction variableCall(moduleFile: SourceFile, name: string): CallExpression | null {\n const init = moduleFile.getVariableDeclaration(name)?.getInitializer();\n return init && Node.isCallExpression(init) ? init : null;\n}\n\nfunction defaultExportCall(moduleFile: SourceFile): LoaderTarget | null {\n const assignment = moduleFile.getExportAssignment((a) => !a.isExportEquals());\n if (!assignment) return null;\n const expr = assignment.getExpression();\n if (Node.isIdentifier(expr)) {\n const call = variableCall(moduleFile, expr.getText());\n return call ? { call, bindingName: expr.getText(), isDefaultExport: true } : null;\n }\n if (Node.isCallExpression(expr))\n return { call: expr, bindingName: '', isDefaultExport: true };\n return null;\n}\n\nfunction resolveModule(\n project: Project,\n fromFilePath: string,\n importPath: string,\n): SourceFile | undefined {\n const base = path.resolve(path.dirname(fromFilePath), importPath);\n for (const candidate of [base, `${base}.ts`, path.join(base, 'index.ts')]) {\n const sf = project.getSourceFile(candidate);\n if (sf) return sf;\n }\n return undefined;\n}\n\nfunction stringArg(call: CallExpression, index: number): string | null {\n const arg = call.getArguments()[index];\n return arg && (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg))\n ? arg.getLiteralText()\n : null;\n}\n\nfunction objArg(call: CallExpression, index: number): ObjectLiteralExpression | undefined {\n const arg = call.getArguments()[index];\n return arg && Node.isObjectLiteralExpression(arg) ? arg : undefined;\n}\n\nfunction nameText(node: Node): string {\n return Node.isStringLiteral(node) ? node.getLiteralText() : node.getText();\n}\n","import {\n type MessageFormatElement,\n parse,\n TYPE,\n} from '@formatjs/icu-messageformat-parser';\n\nexport type IcuValidation = { ok: true } | { ok: false; error: string };\n\n/** Validate an ICU message string; returns the parser's error message when invalid. */\nexport function validateMessage(message: string): IcuValidation {\n try {\n parse(message);\n return { ok: true };\n } catch (e) {\n return { ok: false, error: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/**\n * Collect every argument/placeholder name an ICU message references, including names nested inside\n * plural/select options and tag children (e.g. `{count, plural, one {# by {author}} ...}` → count,\n * author). Throws if the message is not valid ICU — call {@link validateMessage} first.\n */\nexport function extractPlaceholders(message: string): Set<string> {\n const names = new Set<string>();\n\n const walk = (elements: MessageFormatElement[]): void => {\n for (const el of elements) {\n switch (el.type) {\n case TYPE.argument:\n case TYPE.number:\n case TYPE.date:\n case TYPE.time:\n names.add(el.value);\n break;\n case TYPE.select:\n case TYPE.plural:\n names.add(el.value);\n for (const opt of Object.values(el.options)) walk(opt.value);\n break;\n case TYPE.tag:\n names.add(el.value);\n walk(el.children);\n break;\n default:\n break; // literal / pound carry no placeholder name\n }\n }\n };\n\n walk(parse(message));\n return names;\n}\n\nexport type ParityResult =\n | { ok: true }\n | { ok: false; missing: string[]; extra: string[] };\n\n/**\n * Check a translated message references exactly the same placeholders as its source — the common\n * way a translation silently breaks (a dropped `{name}`, or a renamed one). `missing` are present in\n * the source but not the target; `extra` are in the target but not the source.\n */\nexport function placeholderParity(source: string, target: string): ParityResult {\n const s = extractPlaceholders(source);\n const t = extractPlaceholders(target);\n const missing = [...s].filter((name) => !t.has(name));\n const extra = [...t].filter((name) => !s.has(name));\n if (missing.length === 0 && extra.length === 0) return { ok: true };\n return { ok: false, missing, extra };\n}\n","import { Node, type Project } from 'ts-morph';\nimport { objectLiteralText } from './codegen';\nimport { type NestedTranslation } from './nested';\n\n/**\n * Replace the translation object of an existing `createTranslation(...)` call in place (used when\n * re-importing a locale that already exists), preserving the rest of the file. Returns `false` if\n * the export/call can't be found. The file is re-formatted so the replacement indents cleanly.\n */\nexport function replaceTranslationLiteral(\n project: Project,\n filePath: string,\n exportName: string,\n translation: NestedTranslation,\n): boolean {\n const sf = project.getSourceFile(filePath);\n const init = sf?.getVariableDeclaration(exportName)?.getInitializer();\n if (!init || !Node.isCallExpression(init)) return false;\n\n const arg = init.getArguments()[1];\n if (!arg) return false;\n\n arg.replaceWithText(objectLiteralText(translation, 1));\n sf?.formatText();\n return true;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { Node, Project } from 'ts-morph';\nimport { codegenTranslationFile } from './codegen';\nimport { type DiscoveredNamespace, discoverFromProject } from './discover';\nimport { placeholderParity, validateMessage } from './icu';\nimport { toIdentifier } from './identifier';\nimport {\n fromJson,\n leafEntries,\n leafMap,\n type NestedTranslation,\n toJson,\n} from './nested';\nimport { addLocaleLoader, findRegisterNamespaceCalls } from './registry';\nimport { replaceTranslationLiteral } from './update';\n\nexport type ExportFile = { fileName: string; content: string };\n\n/** Plan the per-namespace, per-locale JSON files to write (source locale + every target locale). */\nexport function planExport(namespaces: DiscoveredNamespace[]): ExportFile[] {\n const files: ExportFile[] = [];\n for (const ns of namespaces) {\n files.push({\n fileName: `${ns.namespace}.${ns.source.locale}.json`,\n content: toJson(ns.source.translation),\n });\n for (const loc of ns.locales)\n files.push({\n fileName: `${ns.namespace}.${loc.locale}.json`,\n content: toJson(loc.translation),\n });\n }\n return files;\n}\n\nexport type ImportIssue = { key: string; message: string };\n\n/** Validate an incoming translation: every leaf is valid ICU, and placeholders match the source. */\nexport function validateImport(\n translation: NestedTranslation,\n source: NestedTranslation,\n): ImportIssue[] {\n const issues: ImportIssue[] = [];\n const src = leafMap(source);\n const present = new Set<string>();\n\n for (const [key, message] of leafEntries(translation)) {\n present.add(key);\n const valid = validateMessage(message);\n if (!valid.ok) {\n issues.push({ key, message: `invalid ICU — ${valid.error}` });\n continue;\n }\n const sourceMessage = src.get(key);\n if (sourceMessage === undefined) {\n // `createTranslation` is typed to the source shape, so an unknown key would\n // generate TypeScript that doesn't compile — reject it here with a clear message.\n issues.push({ key, message: 'unknown key (not in the source) — remove it' });\n continue;\n }\n // The source comes from the developer's own TS; a broken source ICU would make\n // placeholderParity throw, so reject it cleanly per-key instead of aborting the run.\n const sourceValid = validateMessage(sourceMessage);\n if (!sourceValid.ok) {\n issues.push({ key, message: `source message is invalid ICU — ${sourceValid.error}` });\n continue;\n }\n const parity = placeholderParity(sourceMessage, message);\n if (!parity.ok) {\n const parts: string[] = [];\n if (parity.missing.length) parts.push(`missing ${parity.missing.join(', ')}`);\n if (parity.extra.length) parts.push(`unexpected ${parity.extra.join(', ')}`);\n issues.push({ key, message: `placeholder mismatch (${parts.join('; ')})` });\n }\n }\n\n for (const key of src.keys())\n if (!present.has(key)) issues.push({ key, message: 'missing key' });\n\n return issues;\n}\n\nexport type ApplyResult = { created: boolean; filePath: string };\n\n/**\n * Apply one imported locale to the project: update the existing locale module in place, or — for a\n * new locale — codegen a `createTranslation` module next to the source namespace and register its\n * loader in the `registerNamespace` call. Mutates the project; the caller saves.\n */\nexport function applyImport(\n project: Project,\n ns: DiscoveredNamespace,\n locale: string,\n translation: NestedTranslation,\n force = false,\n): ApplyResult {\n const existing = ns.locales.find((l) => l.locale === locale);\n if (existing) {\n const replaced = replaceTranslationLiteral(\n project,\n existing.moduleFilePath,\n existing.exportName,\n translation,\n );\n // A silent false would count as applied while writing nothing (e.g. the module was\n // hand-edited and the export no longer is a createTranslation call).\n if (!replaced)\n throw new Error(\n `Could not update locale \"${locale}\": expected an export \"${existing.exportName}\" ` +\n `initialized by a createTranslation(...) call in ${existing.moduleFilePath}.`,\n );\n return { created: false, filePath: existing.moduleFilePath };\n }\n\n const localeId = localeIdentifier(locale);\n if (!localeId)\n throw new Error(\n `Locale \"${locale}\" has no alphanumeric characters to form a valid identifier.`,\n );\n // The generated const name is derived from the namespace (so a `{ translation }` named export\n // yields `settingsDe`, not `translationDe`); the import binding stays the actual source export.\n const exportName = `${toIdentifier(ns.namespace)}${localeId}`;\n const collision = ns.locales.find(\n (l) => l.locale !== locale && localeIdentifier(l.locale) === localeId,\n );\n if (collision)\n throw new Error(\n `Locale \"${locale}\" collides with already-registered locale \"${collision.locale}\" ` +\n `(both generate identifier \"${exportName}\").`,\n );\n\n const sourceBase = path.basename(ns.source.moduleFilePath, '.ts');\n const newFilePath = path.join(\n path.dirname(ns.source.moduleFilePath),\n `${ns.namespace}.${locale}.ts`,\n );\n\n if (!force && fs.existsSync(newFilePath))\n throw new Error(\n `${newFilePath} already exists but locale \"${locale}\" isn't a registered locale. ` +\n `Remove it or pass --force to overwrite.`,\n );\n\n // Resolve + validate the registry call BEFORE creating the module, so a failure\n // can't leave an orphan file in the project (the caller saves everything on success).\n const registrySf = project.getSourceFileOrThrow(ns.registryFilePath);\n const registryCall = registryCallFor(registrySf, ns);\n const otherArg = registryCall.getArguments()[1];\n if (otherArg && !Node.isObjectLiteralExpression(otherArg))\n throw new Error(\n \"registerNamespace's second argument must be an object literal of locale loaders.\",\n );\n\n project.createSourceFile(\n newFilePath,\n codegenTranslationFile({\n namespaceVar: ns.source.exportName,\n exportName,\n locale,\n importPath: `./${sourceBase}`,\n defaultImport: ns.source.isDefaultExport,\n translation,\n }),\n { overwrite: true },\n );\n\n addLocaleLoader(\n registryCall,\n locale,\n relativeImport(ns.registryFilePath, newFilePath),\n exportName,\n );\n\n return { created: true, filePath: newFilePath };\n}\n\n/** Parse an export file name `namespace.locale.json`. Neither segment may contain a dot, so a\n * stray-dot name (`quote.sl.si.json`) is rejected rather than folded into a bogus locale. */\nexport function parseImportFileName(\n fileName: string,\n): { namespace: string; locale: string } | null {\n const m = /^([^.]+)\\.([^.]+)\\.json$/.exec(fileName);\n return m ? { namespace: m[1], locale: m[2] } : null;\n}\n\n// ---- fs-performing runners (thin wrappers the CLI calls) -------------------------------------\n\nexport type ProjectOptions = { cwd: string; srcGlobs: string[] };\n\n/** Sidecar written into the export dir so a later `import` knows which locale was the source,\n * even if `--source-locale` is omitted (it would otherwise default to `en` and mis-treat the\n * source dump as a target locale). */\nconst META_FILE = '.mmtranslate-meta.json';\n\nconst warnToStderr = (message: string): void => console.warn(`⚠ ${message}`);\n\n/** A namespace name must map to exactly one source; duplicates would clobber files on export and\n * drop all-but-one entry on import, both silently. Fail fast so the user resolves it first. */\nfunction assertUniqueNamespaces(namespaces: DiscoveredNamespace[]): void {\n const seen = new Set<string>();\n const dupes = new Set<string>();\n for (const n of namespaces) {\n if (seen.has(n.namespace)) dupes.add(n.namespace);\n seen.add(n.namespace);\n }\n if (dupes.size)\n throw new Error(\n `Duplicate namespace name(s): ${[...dupes].join(', ')}. ` +\n `Each namespace must be registered once — rename or remove the duplicate before export/import.`,\n );\n}\n\nfunction readMetaSourceLocale(inDir: string): string | undefined {\n const metaPath = path.join(inDir, META_FILE);\n if (!fs.existsSync(metaPath)) return undefined;\n try {\n const meta: unknown = JSON.parse(fs.readFileSync(metaPath, 'utf8'));\n const value = (meta as { sourceLocale?: unknown })?.sourceLocale;\n return typeof value === 'string' ? value : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction buildProject(opts: ProjectOptions): Project {\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n compilerOptions: { allowJs: false },\n });\n for (const glob of opts.srcGlobs)\n project.addSourceFilesAtPaths(\n path.isAbsolute(glob) ? glob : path.join(opts.cwd, glob),\n );\n return project;\n}\n\nexport function runExport(\n opts: ProjectOptions & { outDir: string; sourceLocale?: string },\n): ExportFile[] {\n const sourceLocale = opts.sourceLocale ?? 'en';\n const namespaces = discoverFromProject(buildProject(opts), {\n sourceLocale,\n onWarn: warnToStderr,\n });\n assertUniqueNamespaces(namespaces);\n // Import parses file names as <namespace>.<locale>.json — a dot in either segment\n // exports a file the import will skip, so surface it now rather than at import time.\n for (const ns of namespaces) {\n for (const [what, value] of [\n ['Namespace', ns.namespace] as const,\n ...ns.locales.map((l) => ['Locale', l.locale] as const),\n ])\n if (value.includes('.'))\n warnToStderr(\n `${what} \"${value}\" contains a \".\" — its exported JSON cannot be re-imported ` +\n `(import expects <namespace>.<locale>.json with no dots in either segment).`,\n );\n }\n const files = planExport(namespaces);\n fs.mkdirSync(opts.outDir, { recursive: true });\n for (const file of files)\n fs.writeFileSync(path.join(opts.outDir, file.fileName), file.content);\n fs.writeFileSync(\n path.join(opts.outDir, META_FILE),\n JSON.stringify({ sourceLocale }, null, 2) + '\\n',\n );\n return files;\n}\n\nexport type ImportReport = {\n applied: number;\n rejected: { file: string; issues: ImportIssue[] }[];\n /** `.json` files the run did not act on (a stray-dot name, an unknown namespace) — likely typos. */\n skipped: { file: string; reason: string }[];\n};\n\nexport function runImport(\n opts: ProjectOptions & { inDir: string; sourceLocale?: string; force?: boolean },\n): ImportReport {\n // Prefer the explicit flag, then the sidecar the matching export wrote, then the `en` default —\n // so a round-trip with a non-`en` source locale doesn't re-import the source dump as a target.\n const sourceLocale = opts.sourceLocale ?? readMetaSourceLocale(opts.inDir);\n const project = buildProject(opts);\n const namespaces = discoverFromProject(project, {\n sourceLocale,\n onWarn: warnToStderr,\n });\n assertUniqueNamespaces(namespaces);\n const byName = new Map(namespaces.map((n) => [n.namespace, n]));\n\n const rejected: ImportReport['rejected'] = [];\n const skipped: ImportReport['skipped'] = [];\n let applied = 0;\n\n for (const fileName of fs.readdirSync(opts.inDir)) {\n if (fileName === META_FILE) continue;\n const parsed = parseImportFileName(fileName);\n if (!parsed) {\n if (fileName.endsWith('.json'))\n skipped.push({\n file: fileName,\n reason: 'not a <namespace>.<locale>.json name (neither segment may contain a dot)',\n });\n continue;\n }\n const ns = byName.get(parsed.namespace);\n if (!ns) {\n skipped.push({\n file: fileName,\n reason: `no namespace \"${parsed.namespace}\" found in the scanned sources`,\n });\n continue;\n }\n if (parsed.locale === ns.source.locale) continue; // the source dump itself\n\n let translation: NestedTranslation;\n try {\n translation = fromJson(\n fs.readFileSync(path.join(opts.inDir, fileName), 'utf8'),\n );\n } catch (e) {\n rejected.push({\n file: fileName,\n issues: [{ key: '(parse)', message: e instanceof Error ? e.message : String(e) }],\n });\n continue;\n }\n const issues = validateImport(translation, ns.source.translation);\n if (issues.length) {\n rejected.push({ file: fileName, issues });\n continue;\n }\n // Per-file: one unappliable locale (e.g. an unregistered file already at the target\n // path without --force) must not abort the other files' import.\n try {\n applyImport(project, ns, parsed.locale, translation, opts.force);\n applied++;\n } catch (e) {\n rejected.push({\n file: fileName,\n issues: [{ key: '(apply)', message: e instanceof Error ? e.message : String(e) }],\n });\n }\n }\n\n project.saveSync();\n return { applied, rejected, skipped };\n}\n\nexport function runGenerateManifest(\n opts: ProjectOptions & { outFile: string; sourceLocale?: string },\n): string {\n const sourceLocale = opts.sourceLocale ?? 'en';\n const namespaces = discoverFromProject(buildProject(opts), {\n sourceLocale,\n onWarn: warnToStderr,\n });\n assertUniqueNamespaces(namespaces);\n const entries = namespaces.map((n) => ({\n namespace: n.namespace,\n sourceLocale,\n registry: path.relative(opts.cwd, n.registryFilePath),\n source: path.relative(opts.cwd, n.source.moduleFilePath),\n locales: n.locales.map((l) => l.locale),\n }));\n const content =\n `// Generated by \\`mmtranslate generate-manifest\\`. Edit the globs / entries as needed.\\n` +\n `export default ${JSON.stringify(entries, null, 2)};\\n`;\n fs.writeFileSync(opts.outFile, content);\n return content;\n}\n\n// ---- helpers ----------------------------------------------------------------------------------\n\nfunction localeIdentifier(locale: string): string {\n return locale\n .split(/[^A-Za-z0-9]/)\n .filter(Boolean)\n .map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1).toLowerCase())\n .join('');\n}\n\nfunction relativeImport(fromFile: string, toFile: string): string {\n const rel = path\n .relative(path.dirname(fromFile), toFile)\n .replace(/\\.ts$/, '')\n .split(path.sep)\n .join('/');\n return rel.startsWith('.') ? rel : `./${rel}`;\n}\n\nfunction registryCallFor(\n sf: ReturnType<Project['getSourceFileOrThrow']>,\n ns: DiscoveredNamespace,\n) {\n // Discovery records which call this namespace came from, so we re-find it by index rather than\n // re-matching loader shapes (robust across every loader form, and unambiguous between namespaces).\n const call = findRegisterNamespaceCalls(sf)[ns.registryCallIndex];\n if (!call)\n throw new Error(\n `Could not locate the registerNamespace call for \"${ns.namespace}\" in ${sf.getFilePath()}.`,\n );\n return call;\n}\n"],"mappings":"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"}
|
package/index.d.mts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { ArrowFunction, CallExpression, FunctionExpression, ObjectLiteralExpression, Project, SourceFile } from "ts-morph";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/nested.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* A translator-facing translation tree: nested objects with string leaves (ICU messages). This is
|
|
6
|
+
* the shape the tool exports to / imports from disk and the shape `createNamespace` /
|
|
7
|
+
* `createTranslation` are authored in — the tool never touches the store's flattened form.
|
|
8
|
+
*/
|
|
9
|
+
type NestedTranslation = {
|
|
10
|
+
[key: string]: string | NestedTranslation;
|
|
11
|
+
};
|
|
12
|
+
/** Serialize a nested translation tree to a JSON file body (one namespace, one locale). */
|
|
13
|
+
declare function toJson(nested: NestedTranslation): string;
|
|
14
|
+
/** Parse + validate a nested translation JSON file back into a {@link NestedTranslation}. */
|
|
15
|
+
declare function fromJson(json: string): NestedTranslation;
|
|
16
|
+
/** Walk a tree's string leaves, yielding `[dottedPath, message]` for each (used for validation). */
|
|
17
|
+
declare function leafEntries(nested: NestedTranslation, prefix?: string): [string, string][];
|
|
18
|
+
/** A flat `Map<dottedPath, message>` view of a tree, for key-by-key comparison. */
|
|
19
|
+
declare function leafMap(nested: NestedTranslation): Map<string, string>;
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/lib/codegen.d.ts
|
|
22
|
+
type TranslationFileInput = {
|
|
23
|
+
/** The imported source-namespace const (e.g. `quote`), whose `.createTranslation` we call. */namespaceVar: string; /** The exported const name for the generated locale (e.g. `quoteDe`). */
|
|
24
|
+
exportName: string; /** Target locale (e.g. `de`). */
|
|
25
|
+
locale: string; /** Import specifier for the source namespace module, relative to the generated file. */
|
|
26
|
+
importPath: string; /** Whether the source namespace is the module's default export (→ a default import). */
|
|
27
|
+
defaultImport?: boolean; /** The translated tree (plain strings — target locales never need `withParams`). */
|
|
28
|
+
translation: NestedTranslation;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Serialize a nested translation tree to a TypeScript object-literal string. Double-quoted via
|
|
32
|
+
* `JSON.stringify`: correct escaping for ICU (which uses `'` for its own escaping, so single-quoting
|
|
33
|
+
* would be unsafe). A consumer's formatter normalizes quote style/indentation on save.
|
|
34
|
+
*/
|
|
35
|
+
declare function objectLiteralText(obj: NestedTranslation, depth?: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Emit a target-locale TypeScript module that calls `createTranslation` on the source namespace —
|
|
38
|
+
* the TS-first import format. The output is valid TS that round-trips through {@link liftObjectLiteral}.
|
|
39
|
+
*/
|
|
40
|
+
declare function codegenTranslationFile(input: TranslationFileInput): string;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/lib/discover.d.ts
|
|
43
|
+
type DiscoveredLocale = {
|
|
44
|
+
locale: string; /** File the loader imports the translation from. */
|
|
45
|
+
moduleFilePath: string; /** The binding name to import (named export name, or a name to default-import as). */
|
|
46
|
+
exportName: string; /** Whether the binding is the module's default export (→ a default, not named, import). */
|
|
47
|
+
isDefaultExport: boolean;
|
|
48
|
+
translation: NestedTranslation;
|
|
49
|
+
};
|
|
50
|
+
type DiscoveredNamespace = {
|
|
51
|
+
namespace: string; /** File containing the `registerNamespace(...)` call (where new-locale loaders are inserted). */
|
|
52
|
+
registryFilePath: string; /** Index of the matching `registerNamespace(...)` call within that file's calls. */
|
|
53
|
+
registryCallIndex: number; /** The default loader's namespace = the source/default-locale translation. */
|
|
54
|
+
source: DiscoveredLocale; /** Target locales declared in the `other` map. */
|
|
55
|
+
locales: DiscoveredLocale[];
|
|
56
|
+
};
|
|
57
|
+
type DiscoverOptions = {
|
|
58
|
+
/** Label for the default/source translation (the app's defaultLocale). Defaults to `en`. */sourceLocale?: string;
|
|
59
|
+
onWarn?: (message: string) => void;
|
|
60
|
+
};
|
|
61
|
+
/** Discover every `registerNamespace` registry in a ts-morph project and lift its translations. */
|
|
62
|
+
declare function discoverFromProject(project: Project, options?: DiscoverOptions): DiscoveredNamespace[];
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/lib/commands.d.ts
|
|
65
|
+
type ExportFile = {
|
|
66
|
+
fileName: string;
|
|
67
|
+
content: string;
|
|
68
|
+
};
|
|
69
|
+
/** Plan the per-namespace, per-locale JSON files to write (source locale + every target locale). */
|
|
70
|
+
declare function planExport(namespaces: DiscoveredNamespace[]): ExportFile[];
|
|
71
|
+
type ImportIssue = {
|
|
72
|
+
key: string;
|
|
73
|
+
message: string;
|
|
74
|
+
};
|
|
75
|
+
/** Validate an incoming translation: every leaf is valid ICU, and placeholders match the source. */
|
|
76
|
+
declare function validateImport(translation: NestedTranslation, source: NestedTranslation): ImportIssue[];
|
|
77
|
+
type ApplyResult = {
|
|
78
|
+
created: boolean;
|
|
79
|
+
filePath: string;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Apply one imported locale to the project: update the existing locale module in place, or — for a
|
|
83
|
+
* new locale — codegen a `createTranslation` module next to the source namespace and register its
|
|
84
|
+
* loader in the `registerNamespace` call. Mutates the project; the caller saves.
|
|
85
|
+
*/
|
|
86
|
+
declare function applyImport(project: Project, ns: DiscoveredNamespace, locale: string, translation: NestedTranslation, force?: boolean): ApplyResult;
|
|
87
|
+
/** Parse an export file name `namespace.locale.json`. Neither segment may contain a dot, so a
|
|
88
|
+
* stray-dot name (`quote.sl.si.json`) is rejected rather than folded into a bogus locale. */
|
|
89
|
+
declare function parseImportFileName(fileName: string): {
|
|
90
|
+
namespace: string;
|
|
91
|
+
locale: string;
|
|
92
|
+
} | null;
|
|
93
|
+
type ProjectOptions = {
|
|
94
|
+
cwd: string;
|
|
95
|
+
srcGlobs: string[];
|
|
96
|
+
};
|
|
97
|
+
declare function runExport(opts: ProjectOptions & {
|
|
98
|
+
outDir: string;
|
|
99
|
+
sourceLocale?: string;
|
|
100
|
+
}): ExportFile[];
|
|
101
|
+
type ImportReport = {
|
|
102
|
+
applied: number;
|
|
103
|
+
rejected: {
|
|
104
|
+
file: string;
|
|
105
|
+
issues: ImportIssue[];
|
|
106
|
+
}[]; /** `.json` files the run did not act on (a stray-dot name, an unknown namespace) — likely typos. */
|
|
107
|
+
skipped: {
|
|
108
|
+
file: string;
|
|
109
|
+
reason: string;
|
|
110
|
+
}[];
|
|
111
|
+
};
|
|
112
|
+
declare function runImport(opts: ProjectOptions & {
|
|
113
|
+
inDir: string;
|
|
114
|
+
sourceLocale?: string;
|
|
115
|
+
force?: boolean;
|
|
116
|
+
}): ImportReport;
|
|
117
|
+
declare function runGenerateManifest(opts: ProjectOptions & {
|
|
118
|
+
outFile: string;
|
|
119
|
+
sourceLocale?: string;
|
|
120
|
+
}): string;
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/lib/icu.d.ts
|
|
123
|
+
type IcuValidation = {
|
|
124
|
+
ok: true;
|
|
125
|
+
} | {
|
|
126
|
+
ok: false;
|
|
127
|
+
error: string;
|
|
128
|
+
};
|
|
129
|
+
/** Validate an ICU message string; returns the parser's error message when invalid. */
|
|
130
|
+
declare function validateMessage(message: string): IcuValidation;
|
|
131
|
+
/**
|
|
132
|
+
* Collect every argument/placeholder name an ICU message references, including names nested inside
|
|
133
|
+
* plural/select options and tag children (e.g. `{count, plural, one {# by {author}} ...}` → count,
|
|
134
|
+
* author). Throws if the message is not valid ICU — call {@link validateMessage} first.
|
|
135
|
+
*/
|
|
136
|
+
declare function extractPlaceholders(message: string): Set<string>;
|
|
137
|
+
type ParityResult = {
|
|
138
|
+
ok: true;
|
|
139
|
+
} | {
|
|
140
|
+
ok: false;
|
|
141
|
+
missing: string[];
|
|
142
|
+
extra: string[];
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Check a translated message references exactly the same placeholders as its source — the common
|
|
146
|
+
* way a translation silently breaks (a dropped `{name}`, or a renamed one). `missing` are present in
|
|
147
|
+
* the source but not the target; `extra` are in the target but not the source.
|
|
148
|
+
*/
|
|
149
|
+
declare function placeholderParity(source: string, target: string): ParityResult;
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/lib/lift.d.ts
|
|
152
|
+
/**
|
|
153
|
+
* Statically lift a `createNamespace`/`createTranslation` translation object literal into a plain
|
|
154
|
+
* {@link NestedTranslation} — no code execution. String leaves and nested objects pass through;
|
|
155
|
+
* `withParams<…>('msg')` is unwrapped to its message string (the type arg is compile-time only and
|
|
156
|
+
* irrelevant to the round-trip). Anything dynamic (a variable, a concatenation, a spread) throws —
|
|
157
|
+
* the author must inline a literal for the tool to round-trip it.
|
|
158
|
+
*/
|
|
159
|
+
declare function liftObjectLiteral(obj: ObjectLiteralExpression): NestedTranslation;
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/lib/loader.d.ts
|
|
162
|
+
type ParsedLoader = {
|
|
163
|
+
/** The module specifier of the dynamic `import(...)`. */importPath: string;
|
|
164
|
+
/**
|
|
165
|
+
* The property chain accessed on the resolved module (`m.quote.translation` → `['quote',
|
|
166
|
+
* 'translation']`). Empty for the `() => import('path')` shorthand, where `@mmstack/translate`
|
|
167
|
+
* auto-resolves the module's `default` / `translation` export.
|
|
168
|
+
*/
|
|
169
|
+
accessor: string[];
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Parse a namespace loader into its import path + accessor chain, matching every shape
|
|
173
|
+
* `@mmstack/translate` accepts at runtime:
|
|
174
|
+
* - `() => import('path').then((m) => m.a.b)` → `['a', 'b']`
|
|
175
|
+
* - `() => import('path').then((m) => m.default)` → `['default']`
|
|
176
|
+
* - `() => import('path')` (shorthand) → `[]` (default / translation auto-resolve)
|
|
177
|
+
* Returns `null` for any other shape (async/await, computed access) — the caller warns and skips.
|
|
178
|
+
*/
|
|
179
|
+
declare function parseLoader(fn: ArrowFunction | FunctionExpression): ParsedLoader | null;
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/lib/registry.d.ts
|
|
182
|
+
/**
|
|
183
|
+
* Find every `registerNamespace(...)` call in a source file. `registerRemoteNamespace` is
|
|
184
|
+
* deliberately ignored — its translations are served, not authored in files, so there's nothing to
|
|
185
|
+
* export or round-trip.
|
|
186
|
+
*/
|
|
187
|
+
declare function findRegisterNamespaceCalls(sf: SourceFile): CallExpression[];
|
|
188
|
+
/**
|
|
189
|
+
* Insert (or replace) a locale's loader in a `registerNamespace(default, other)` call's `other`
|
|
190
|
+
* map, so a freshly-imported locale is actually registered. Mutates the call in place — the caller
|
|
191
|
+
* saves the source file afterwards.
|
|
192
|
+
*/
|
|
193
|
+
declare function addLocaleLoader(call: CallExpression, locale: string, importPath: string, exportName: string): void;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/lib/update.d.ts
|
|
196
|
+
/**
|
|
197
|
+
* Replace the translation object of an existing `createTranslation(...)` call in place (used when
|
|
198
|
+
* re-importing a locale that already exists), preserving the rest of the file. Returns `false` if
|
|
199
|
+
* the export/call can't be found. The file is re-formatted so the replacement indents cleanly.
|
|
200
|
+
*/
|
|
201
|
+
declare function replaceTranslationLiteral(project: Project, filePath: string, exportName: string, translation: NestedTranslation): boolean;
|
|
202
|
+
//#endregion
|
|
203
|
+
export { ApplyResult, DiscoverOptions, DiscoveredLocale, DiscoveredNamespace, ExportFile, IcuValidation, ImportIssue, ImportReport, NestedTranslation, ParityResult, ParsedLoader, ProjectOptions, TranslationFileInput, addLocaleLoader, applyImport, codegenTranslationFile, discoverFromProject, extractPlaceholders, findRegisterNamespaceCalls, fromJson, leafEntries, leafMap, liftObjectLiteral, objectLiteralText, parseImportFileName, parseLoader, placeholderParity, planExport, replaceTranslationLiteral, runExport, runGenerateManifest, runImport, toJson, validateImport, validateMessage };
|
|
204
|
+
//# sourceMappingURL=index.d.mts.map
|
package/index.d.mts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../packages/translate-tools/src/lib/nested.ts","../../../packages/translate-tools/src/lib/codegen.ts","../../../packages/translate-tools/src/lib/discover.ts","../../../packages/translate-tools/src/lib/commands.ts","../../../packages/translate-tools/src/lib/icu.ts","../../../packages/translate-tools/src/lib/lift.ts","../../../packages/translate-tools/src/lib/loader.ts","../../../packages/translate-tools/src/lib/registry.ts","../../../packages/translate-tools/src/lib/update.ts"],"mappings":"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"}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { S as toJson, _ as codegenTranslationFile, a as runGenerateManifest, b as leafEntries, c as replaceTranslationLiteral, d as validateMessage, f as discoverFromProject, g as liftObjectLiteral, h as parseLoader, i as runExport, l as extractPlaceholders, m as findRegisterNamespaceCalls, n as parseImportFileName, o as runImport, p as addLocaleLoader, r as planExport, s as validateImport, t as applyImport, u as placeholderParity, v as objectLiteralText, x as leafMap, y as fromJson } from "./commands-yoWN-Tne.mjs";
|
|
2
|
+
export { addLocaleLoader, applyImport, codegenTranslationFile, discoverFromProject, extractPlaceholders, findRegisterNamespaceCalls, fromJson, leafEntries, leafMap, liftObjectLiteral, objectLiteralText, parseImportFileName, parseLoader, placeholderParity, planExport, replaceTranslationLiteral, runExport, runGenerateManifest, runImport, toJson, validateImport, validateMessage };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mmstack/translate-tools",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"mmtranslate": "./cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"main": "./index.mjs",
|
|
9
|
+
"module": "./index.mjs",
|
|
10
|
+
"types": "./index.d.mts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./index.d.mts",
|
|
14
|
+
"default": "./index.mjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": ["*.mjs", "*.d.mts", "*.map"],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"translate",
|
|
20
|
+
"localize",
|
|
21
|
+
"export",
|
|
22
|
+
"import",
|
|
23
|
+
"angular",
|
|
24
|
+
"mmstack"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/mihajm/mmstack.git",
|
|
30
|
+
"directory": "packages/translate-tools"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/mihajm/mmstack/blob/master/packages/translate-tools",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@formatjs/icu-messageformat-parser": "^3.5.12",
|
|
35
|
+
"ts-morph": "^28.0.0"
|
|
36
|
+
},
|
|
37
|
+
"sideEffects": false
|
|
38
|
+
}
|