@xndrjs/i18n-react 0.8.1 → 0.8.2-alpha.1
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/README.md +125 -0
- package/dist/cli/codegen.js +305 -0
- package/dist/cli/codegen.js.map +1 -0
- package/dist/index.js +37 -21
- package/dist/index.js.map +1 -1
- package/package.json +6 -9
- package/bin/codegen.mjs +0 -17
- package/src/codegen/emit/react-bindings-file.test.ts +0 -44
- package/src/codegen/emit/react-bindings-file.ts +0 -223
- package/src/codegen/generate-react-bindings.ts +0 -86
- package/src/codegen/react-codegen-config.test.ts +0 -39
- package/src/codegen/react-codegen-config.ts +0 -70
- package/src/codegen/write-file-if-changed.ts +0 -16
- package/src/create-load-coordinator.test.ts +0 -421
- package/src/create-load-coordinator.ts +0 -340
- package/src/create-scoped-t.ts +0 -50
- package/src/index.ts +0 -18
- package/src/namespace-load-gate.test.tsx +0 -608
- package/src/namespace-load-gate.tsx +0 -265
- package/src/root-context.tsx +0 -21
- package/src/root-provider.tsx +0 -65
- package/src/runtime.test.tsx +0 -88
- package/src/types.ts +0 -138
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/create-scoped-t.ts","../src/create-load-coordinator.ts","../src/namespace-load-gate.tsx","../src/root-context.tsx","../src/root-provider.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Builds the scoped `t` function used by providers and load gates.\n *\n * Takes a loaded `@xndrjs/i18n` scope and returns a capability-safe translator:\n * namespaces outside the provider list throw at runtime. Locale is always bound.\n */\nimport type { CreateScopedTOptions, ScopedScopeLike, ScopedTranslateFn } from \"./types.js\";\n\nfunction assertNamespace(namespace: string, namespaces: readonly string[]): void {\n if (!namespaces.includes(namespace)) {\n throw new Error(\n `Namespace \"${namespace}\" is not loaded by this provider. Allowed: [${namespaces.join(\", \")}]`\n );\n }\n}\n\nfunction resolveActiveScope(scope: ScopedScopeLike, locale: string): ScopedScopeLike {\n if (typeof scope.forLocale === \"function\" && scope.locale !== locale) {\n return scope.forLocale(locale);\n }\n return scope;\n}\n\n/**\n * Wraps a loaded scope `t` with namespace guards and locale binding.\n */\nexport function createScopedT(\n scope: ScopedScopeLike,\n options: CreateScopedTOptions\n): ScopedTranslateFn {\n const activeScope = resolveActiveScope(scope, options.locale);\n const { namespaces } = options;\n\n return ((namespace: string, key: string, ...rest: unknown[]) => {\n assertNamespace(namespace, namespaces);\n return activeScope.t(namespace, key, ...rest);\n }) as ScopedTranslateFn;\n}\n\n/**\n * Curries a multi-namespace context `t(ns, key, …)` into a namespace-bound `t(key, …)`.\n * Used when composing gate values without introducing a separate hooks API.\n */\nexport function bindNamespaceTranslate(\n t: ScopedTranslateFn,\n namespace: string\n): (key: string, params?: Record<string, unknown>) => string {\n return (key: string, params?: Record<string, unknown>) =>\n params === undefined ? t(namespace, key) : t(namespace, key, params);\n}\n","/**\n * Load deduplication for lazy i18n namespace gates / HOCs.\n *\n * Supports multiple concurrent keys so parallel per-namespace gates can share\n * one coordinator. Each `(engine, partition, namespaces)` entry keeps its own\n * promise and status. Display state (keep-previous / pendingLocale / stable `t`)\n * lives here so gate components stay pure observers of `useSyncExternalStore`.\n */\nimport { createScopedT } from \"./create-scoped-t.js\";\nimport type {\n GetDisplayEntryOptions,\n LoadCoordinator,\n LoadCoordinatorEntry,\n LoadCoordinatorKey,\n LoadCoordinatorRequest,\n LoadDisplayEntry,\n ScopedScopeLike,\n ScopedTranslateFn,\n} from \"./types.js\";\n\nconst engineIds = new WeakMap<object, number>();\nlet nextEngineId = 1;\n\nfunction engineId(ref: unknown): number {\n if (ref === null || (typeof ref !== \"object\" && typeof ref !== \"function\")) {\n return 0;\n }\n const obj = ref as object;\n let id = engineIds.get(obj);\n if (id === undefined) {\n id = nextEngineId++;\n engineIds.set(obj, id);\n }\n return id;\n}\n\nfunction cacheKey(\n engineRef: unknown,\n partition: string | undefined,\n namespaces: readonly string[]\n): string {\n return `${engineId(engineRef)}\\0${partition ?? \"\"}\\0${namespaces.join(\"\\0\")}`;\n}\n\n/** Gate identity for keep-previous (namespaces only — partition/locale can change). */\nfunction gateId(engineRef: unknown, namespaces: readonly string[]): string {\n return `${engineId(engineRef)}\\0${namespaces.join(\"\\0\")}`;\n}\n\ninterface CacheEntry<Scope> {\n status: \"pending\" | \"resolved\" | \"error\";\n promise: Promise<Scope>;\n resolvedScope: Scope | null;\n error: unknown;\n requestId: number;\n /** Stable LoadCoordinatorEntry snapshot for getEntry(). */\n entrySnapshot: LoadCoordinatorEntry<Scope>;\n}\n\ninterface DisplayCache {\n lastReady: { scope: ScopedScopeLike; locale: string } | null;\n /** Stable display entry object until inputs change. */\n displaySnapshot: LoadDisplayEntry<ScopedScopeLike> | null;\n /** Identity key for the last displaySnapshot. */\n displayKey: string | null;\n boundT: ScopedTranslateFn | null;\n boundTKey: string | null;\n}\n\n/**\n * Dedupes in-flight loads by `(engineRef, partition, namespaces)` and exposes\n * display snapshots + retry for manual pending/resolved/error rendering.\n */\nexport function createLoadCoordinator<Scope = ScopedScopeLike>(): LoadCoordinator<Scope> {\n let revision = 0;\n let nextRequestId = 0;\n const entries = new Map<string, CacheEntry<Scope>>();\n const displayByGate = new Map<string, DisplayCache>();\n const listeners = new Set<() => void>();\n let lastKey: string | null = null;\n\n function notify(): void {\n for (const listener of listeners) {\n listener();\n }\n }\n\n function bumpAndNotify(): void {\n revision += 1;\n // Invalidate display snapshots that depend on entry status.\n for (const display of displayByGate.values()) {\n display.displaySnapshot = null;\n display.displayKey = null;\n }\n notify();\n }\n\n function markResolvedSync(key: string, syncScope: Scope): void {\n const entrySnapshot: LoadCoordinatorEntry<Scope> = {\n status: \"resolved\",\n scope: syncScope,\n };\n entries.set(key, {\n status: \"resolved\",\n promise: Promise.resolve(syncScope),\n resolvedScope: syncScope,\n error: null,\n requestId: ++nextRequestId,\n entrySnapshot,\n });\n }\n\n /**\n * SSR / getServerSnapshot: hydrate from peek/state only — never kick `load()`.\n */\n function ensureSync(input: LoadCoordinatorRequest<Scope>): void {\n const key = cacheKey(input.engineRef, input.partition, input.namespaces);\n lastKey = key;\n\n if (entries.has(key)) {\n return;\n }\n\n if (input.tryResolveSync === undefined) {\n return;\n }\n\n const syncScope = input.tryResolveSync();\n if (syncScope !== null) {\n markResolvedSync(key, syncScope);\n }\n }\n\n function ensure(input: LoadCoordinatorRequest<Scope>): void {\n const key = cacheKey(input.engineRef, input.partition, input.namespaces);\n lastKey = key;\n\n const existing = entries.get(key);\n if (existing !== undefined) {\n return;\n }\n\n if (input.tryResolveSync !== undefined) {\n const syncScope = input.tryResolveSync();\n if (syncScope !== null) {\n markResolvedSync(key, syncScope);\n return;\n }\n }\n\n const requestId = ++nextRequestId;\n const promise = input.load().then(\n (scope) => {\n const entry = entries.get(key);\n if (entry === undefined || entry.requestId !== requestId) {\n return scope;\n }\n entry.status = \"resolved\";\n entry.resolvedScope = scope;\n entry.error = null;\n entry.entrySnapshot = { status: \"resolved\", scope };\n bumpAndNotify();\n return scope;\n },\n (error: unknown) => {\n const entry = entries.get(key);\n if (entry !== undefined && entry.requestId === requestId) {\n entry.status = \"error\";\n entry.error = error;\n entry.resolvedScope = null;\n entry.entrySnapshot = { status: \"error\", error };\n bumpAndNotify();\n }\n throw error;\n }\n );\n // Avoid unhandled rejection when only gate subscribers attach (no getPromise).\n void promise.catch((error: unknown) => {\n console.error(\"[i18n-react] namespace load failed:\", error);\n });\n\n entries.set(key, {\n status: \"pending\",\n promise,\n resolvedScope: null,\n error: null,\n requestId,\n entrySnapshot: { status: \"pending\" },\n });\n }\n\n function getEntry(keyInput: LoadCoordinatorKey): LoadCoordinatorEntry<Scope> {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n const entry = entries.get(key);\n if (entry === undefined) {\n return { status: \"pending\" };\n }\n return entry.entrySnapshot;\n }\n\n function getOrCreateDisplay(engineRef: unknown, namespaces: readonly string[]): DisplayCache {\n const id = gateId(engineRef, namespaces);\n let display = displayByGate.get(id);\n if (display === undefined) {\n display = {\n lastReady: null,\n displaySnapshot: null,\n displayKey: null,\n boundT: null,\n boundTKey: null,\n };\n displayByGate.set(id, display);\n }\n return display;\n }\n\n function bindT(\n display: DisplayCache,\n scope: ScopedScopeLike,\n locale: string,\n namespaces: readonly string[],\n scopeToken: string\n ): ScopedTranslateFn {\n const tKey = `${scopeToken}\\0${locale}\\0${namespaces.join(\"\\0\")}`;\n if (display.boundT !== null && display.boundTKey === tKey) {\n return display.boundT;\n }\n const t = createScopedT(scope, { namespaces, locale });\n display.boundT = t;\n display.boundTKey = tKey;\n return t;\n }\n\n function getDisplayEntry(\n keyInput: LoadCoordinatorKey,\n options: GetDisplayEntryOptions\n ): LoadDisplayEntry<Scope> {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n const entry = entries.get(key);\n const display = getOrCreateDisplay(keyInput.engineRef, options.namespaces);\n const keepPrevious = options.keepPrevious !== false;\n\n const retry = () => {\n retryKey(keyInput);\n };\n\n let result: LoadDisplayEntry<Scope>;\n\n if (entry === undefined || entry.status === \"pending\") {\n const kept = keepPrevious ? display.lastReady : null;\n result = {\n status: \"pending\",\n display: kept as { scope: Scope & ScopedScopeLike; locale: string } | null,\n pendingLocale: kept ? options.locale : undefined,\n t: kept\n ? bindT(display, kept.scope, kept.locale, options.namespaces, `kept:${kept.locale}`)\n : null,\n } as LoadDisplayEntry<Scope>;\n } else if (entry.status === \"resolved\") {\n const scope = entry.resolvedScope as Scope & ScopedScopeLike;\n display.lastReady = { scope, locale: options.locale };\n result = {\n status: \"ready\",\n scope,\n locale: options.locale,\n t: bindT(display, scope, options.locale, options.namespaces, `ready:${key}`),\n } as LoadDisplayEntry<Scope>;\n } else {\n const kept = keepPrevious ? display.lastReady : null;\n result = {\n status: \"error\",\n error: entry.error,\n display: kept as { scope: Scope & ScopedScopeLike; locale: string } | null,\n pendingLocale: kept ? options.locale : undefined,\n t: kept\n ? bindT(display, kept.scope, kept.locale, options.namespaces, `kept:${kept.locale}`)\n : null,\n retry,\n } as LoadDisplayEntry<Scope>;\n }\n\n const displayKey = `${key}\\0${result.status}\\0${options.locale}\\0${String(!!result.t)}`;\n if (display.displaySnapshot !== null && display.displayKey === displayKey) {\n return display.displaySnapshot as LoadDisplayEntry<Scope>;\n }\n display.displaySnapshot = result as LoadDisplayEntry<ScopedScopeLike>;\n display.displayKey = displayKey;\n return result;\n }\n\n function retryKey(keyInput: LoadCoordinatorKey): void {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n if (!entries.has(key)) {\n return;\n }\n entries.delete(key);\n bumpAndNotify();\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n function getPromise(): Promise<Scope> {\n if (lastKey === null) {\n throw new Error(\"createLoadCoordinator: call ensure()/request() before getPromise()\");\n }\n const entry = entries.get(lastKey);\n if (entry === undefined) {\n throw new Error(\"createLoadCoordinator: call ensure()/request() before getPromise()\");\n }\n return entry.promise;\n }\n\n function getResolvedScope(): Scope | null {\n if (lastKey === null) {\n return null;\n }\n const entry = entries.get(lastKey);\n return entry?.status === \"resolved\" ? entry.resolvedScope : null;\n }\n\n return {\n get revision() {\n return revision;\n },\n request: ensure,\n ensure,\n ensureSync,\n getEntry,\n getDisplayEntry,\n retry: retryKey,\n subscribe,\n getPromise,\n getResolvedScope,\n };\n}\n","/**\n * Manual (non-Suspense) i18n load gate: ensure + subscribe via the load\n * coordinator, then render fallback / keep-previous / error UI / throw.\n */\nimport {\n forwardRef,\n useSyncExternalStore,\n type ForwardedRef,\n type ForwardRefExoticComponent,\n type ReactNode,\n type RefAttributes,\n} from \"react\";\nimport type {\n LoadCoordinator,\n LoadCoordinatorRequest,\n LoadDisplayEntry,\n LoadPartition,\n ScopedScopeLike,\n ScopedTranslateFn,\n} from \"./types.js\";\n\n/** Inputs for {@link useNamespaceLoad}. */\nexport interface UseNamespaceLoadInput<Scope> {\n coordinator: LoadCoordinator<Scope>;\n engineRef: unknown;\n partition: LoadPartition;\n namespaces: readonly string[];\n locale: string;\n load: () => Promise<Scope>;\n tryResolveSync?: () => Scope | null;\n keepPrevious?: boolean;\n}\n\n/**\n * Starts (or reuses) a coordinator load and re-renders when that entry settles.\n * Client getSnapshot may kick `load()`; SSR getServerSnapshot only peeks sync\n * (hydrated `state`) and never starts a client fetch during prerender.\n */\nexport function useNamespaceLoad<Scope>(\n input: UseNamespaceLoadInput<Scope>\n): LoadDisplayEntry<Scope> {\n const {\n coordinator,\n engineRef,\n partition,\n namespaces,\n locale,\n load,\n tryResolveSync,\n keepPrevious,\n } = input;\n\n const request: LoadCoordinatorRequest<Scope> = {\n engineRef,\n partition,\n namespaces,\n load,\n ...(tryResolveSync !== undefined ? { tryResolveSync } : {}),\n };\n\n const displayOptions = {\n locale,\n namespaces,\n ...(keepPrevious !== undefined ? { keepPrevious } : {}),\n };\n\n return useSyncExternalStore(\n coordinator.subscribe,\n () => {\n coordinator.ensure(request);\n return coordinator.getDisplayEntry({ engineRef, partition, namespaces }, displayOptions);\n },\n () => {\n coordinator.ensureSync(request);\n return coordinator.getDisplayEntry({ engineRef, partition, namespaces }, displayOptions);\n }\n );\n}\n\n/** Scoped `{ t, locale }` injected into gate children / HOC when ready (or kept). */\nexport interface I18nLoadGateValue {\n t: ScopedTranslateFn;\n locale: string;\n /** Set during keep-then-switch while a new partition is loading. */\n pendingLocale?: string;\n error?: unknown;\n retry?: () => void;\n}\n\n/** Load wiring from React context (coordinator, engine, partition, load). */\nexport interface I18nLoadArgs {\n coordinator: LoadCoordinator<ScopedScopeLike>;\n engineRef: unknown;\n partition: LoadPartition;\n locale: string;\n load: (namespaces: readonly string[]) => Promise<ScopedScopeLike>;\n tryResolveSync?: (namespaces: readonly string[]) => ScopedScopeLike | null;\n}\n\nexport interface CreateI18nLoadGateOptions {\n /**\n * When true (default), keep last resolved `{ t, locale }` while a new\n * partition is pending instead of showing fallback.\n */\n keepPreviousOnPartitionChange?: boolean;\n /** Hook that resolves coordinator + load inputs from React context. */\n useLoadArgs: () => I18nLoadArgs;\n}\n\nexport interface I18nGateProps {\n namespaces: readonly string[];\n /** Shown only while the load is pending (not on error). */\n fallback?: ReactNode;\n /**\n * When set, called for error with no keep-previous display.\n * Otherwise the load error is thrown for a React error boundary.\n */\n renderError?: (args: { error: unknown; retry: () => void }) => ReactNode;\n children: (value: I18nLoadGateValue) => ReactNode;\n}\n\n/** Pending UI for `withI18n` — static node or derived from own props. */\nexport type WithI18nFallback<P extends object> = ReactNode | ((props: P) => ReactNode);\n\nexport interface I18nHocOptions<P extends object = object> {\n namespaces: readonly string[];\n fallback?: WithI18nFallback<P>;\n renderError?: (args: { error: unknown; retry: () => void; props: P }) => ReactNode;\n}\n\n/** Render fn for `withI18n`: own props, i18n bag, optional forwarded ref. */\nexport type WithI18nRender<P extends object, R = never> = (\n props: P,\n i18n: I18nLoadGateValue,\n ref?: ForwardedRef<R>\n) => ReactNode;\n\nfunction resolveNamespaces(namespaces: readonly string[] | undefined): readonly string[] {\n if (namespaces === undefined || namespaces.length === 0) {\n throw new Error(\"withI18n / I18n: requires a non-empty namespaces list\");\n }\n return namespaces;\n}\n\nfunction toThrownError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n if (typeof error === \"string\") {\n return new Error(error);\n }\n return new Error(\"i18n namespace load failed\");\n}\n\nfunction renderDisplayEntry(\n entry: LoadDisplayEntry<ScopedScopeLike>,\n options: {\n fallback?: ReactNode;\n renderError?: (args: { error: unknown; retry: () => void }) => ReactNode;\n children: (value: I18nLoadGateValue) => ReactNode;\n }\n): ReactNode {\n if (entry.status === \"ready\") {\n return options.children({ t: entry.t, locale: entry.locale });\n }\n\n if (entry.status === \"error\" && entry.t === null) {\n if (options.renderError) {\n return options.renderError({ error: entry.error, retry: entry.retry! });\n }\n throw toThrownError(entry.error);\n }\n\n if (entry.t !== null && entry.display) {\n const value: I18nLoadGateValue = {\n t: entry.t,\n locale: entry.display.locale,\n };\n if (entry.pendingLocale !== undefined) {\n value.pendingLocale = entry.pendingLocale;\n }\n if (entry.status === \"error\") {\n value.error = entry.error;\n value.retry = entry.retry;\n }\n return options.children(value);\n }\n\n return options.fallback ?? null;\n}\n\n/**\n * Factory for generic `I18n` gate + `withI18n` HOC over the same Outer.\n * Codegen emits typed wrappers; tests use this directly.\n */\nexport function createI18nLoadGate(options: CreateI18nLoadGateOptions): {\n I18n: (props: I18nGateProps) => ReactNode;\n withI18n: <P extends object = object, R = never>(\n hocOptions: I18nHocOptions<P>,\n render: WithI18nRender<P, R>\n ) => ForwardRefExoticComponent<P & RefAttributes<R>>;\n} {\n const { useLoadArgs } = options;\n const keepPrevious = options.keepPreviousOnPartitionChange !== false;\n\n function useGateEntry(namespaces: readonly string[]): LoadDisplayEntry<ScopedScopeLike> {\n const resolved = resolveNamespaces(namespaces);\n const args = useLoadArgs();\n return useNamespaceLoad({\n coordinator: args.coordinator,\n engineRef: args.engineRef,\n partition: args.partition,\n namespaces: resolved,\n locale: args.locale,\n load: () => args.load(resolved),\n ...(args.tryResolveSync !== undefined\n ? { tryResolveSync: () => args.tryResolveSync!(resolved) }\n : {}),\n keepPrevious,\n });\n }\n\n function I18n({ namespaces, fallback, renderError, children }: I18nGateProps): ReactNode {\n const entry = useGateEntry(namespaces);\n return renderDisplayEntry(entry, {\n ...(fallback !== undefined ? { fallback } : {}),\n ...(renderError !== undefined ? { renderError } : {}),\n children,\n });\n }\n\n function withI18n<P extends object = object, R = never>(\n hocOptions: I18nHocOptions<P>,\n render: WithI18nRender<P, R>\n ): ForwardRefExoticComponent<P & RefAttributes<R>> {\n const { fallback, renderError } = hocOptions;\n const Outer = forwardRef<R, P>(function I18nHocOuter(props, ref) {\n const entry = useGateEntry(hocOptions.namespaces);\n\n const needsFallback = entry.status === \"pending\" && entry.t === null;\n const resolvedFallback =\n !needsFallback || fallback === undefined\n ? undefined\n : typeof fallback === \"function\"\n ? fallback(props as P)\n : fallback;\n\n return renderDisplayEntry(entry, {\n ...(resolvedFallback !== undefined ? { fallback: resolvedFallback } : {}),\n ...(renderError !== undefined\n ? {\n renderError: ({ error, retry }) => renderError({ error, retry, props: props as P }),\n }\n : {}),\n children: (value) => render(props as P, value, ref),\n });\n });\n\n const displayName = render.name || \"Component\";\n Outer.displayName = `withI18n(${displayName})`;\n return Outer as ForwardRefExoticComponent<P & RefAttributes<R>>;\n }\n\n return { I18n, withI18n };\n}\n","/**\n * Holds the shared i18n root: handle + coordinator + active locale.\n *\n * Generated {@link useI18nRoot} casts the handle to the project's typed factory return.\n */\nimport { createContext, useContext } from \"react\";\nimport type { I18nRootContextValue } from \"./types.js\";\n\nconst I18nRootContext = createContext<I18nRootContextValue | null>(null);\n\n/** Resolves the shared root from an ancestor {@link I18nRootProvider}. */\nexport function useI18nRootContext(): I18nRootContextValue {\n const value = useContext(I18nRootContext);\n if (value === null) {\n throw new Error(\"useI18nRoot must be used within I18nRoot / I18nRootProvider\");\n }\n return value;\n}\n\n/** Internal — provider wiring. */\nexport { I18nRootContext };\n","/**\n * Single provider that shares handle + coordinator + locale across a lazy subtree.\n */\nimport { useRef, type ReactNode } from \"react\";\nimport type { FetchArtifact, I18nCreateInput, OnMissingTranslation } from \"@xndrjs/i18n\";\nimport { createLoadCoordinator } from \"./create-load-coordinator.js\";\nimport { I18nRootContext } from \"./root-context.js\";\nimport type {\n CreateI18nFactory,\n I18nHandleLike,\n I18nRootContextValue,\n ScopedScopeLike,\n} from \"./types.js\";\n\nexport interface I18nRootProviderProps {\n createI18n: CreateI18nFactory;\n locale: string;\n /**\n * Cold-start / eager dictionary seed (defaults to `{}` when neither prop is set).\n * Mutually exclusive with {@link state}.\n */\n dictionary?: Record<string, unknown>;\n /** Full create/hydrate input from `handle.serialize()` — mutually exclusive with {@link dictionary}. */\n state?: I18nCreateInput | undefined;\n onMissing?: OnMissingTranslation;\n /** Custom JSON loader for `loaderStrategy: \"fetch\"` projects (ignored by import loaders). */\n fetchImpl?: FetchArtifact;\n children: ReactNode;\n}\n\n/** Creates and shares one handle + load coordinator for descendant gates. */\nexport function I18nRootProvider({\n createI18n,\n locale,\n dictionary,\n state,\n onMissing,\n fetchImpl,\n children,\n}: I18nRootProviderProps): ReactNode {\n if (state !== undefined && dictionary !== undefined) {\n throw new Error(\"I18nRootProvider: pass either `state` or `dictionary`, not both.\");\n }\n\n const valueRef = useRef<I18nRootContextValue | null>(null);\n\n if (valueRef.current === null) {\n const seed: I18nCreateInput =\n state ?? ({ dictionary: dictionary ?? {} } satisfies I18nCreateInput);\n const handle = createI18n({\n state: seed,\n ...(onMissing !== undefined ? { onMissing } : {}),\n ...(fetchImpl !== undefined ? { fetchImpl } : {}),\n });\n valueRef.current = {\n handle: handle as I18nHandleLike,\n coordinator: createLoadCoordinator<ScopedScopeLike>(),\n locale,\n };\n } else {\n valueRef.current.locale = locale;\n }\n\n return <I18nRootContext.Provider value={valueRef.current}>{children}</I18nRootContext.Provider>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/create-scoped-t.ts","../src/create-load-coordinator.ts","../src/namespace-load-gate.tsx","../src/root-context.tsx","../src/root-provider.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Builds the scoped `t` function used by providers and load gates.\n *\n * Takes a loaded `@xndrjs/i18n` scope and returns a capability-safe translator:\n * namespaces outside the provider list throw at runtime. Locale is always bound.\n */\nimport type { CreateScopedTOptions, ScopedScopeLike, ScopedTranslateFn } from \"./types.js\";\n\nfunction assertNamespace(namespace: string, namespaces: readonly string[]): void {\n if (!namespaces.includes(namespace)) {\n throw new Error(\n `Namespace \"${namespace}\" is not loaded by this provider. Allowed: [${namespaces.join(\", \")}]`\n );\n }\n}\n\nfunction resolveActiveScope(scope: ScopedScopeLike, locale: string): ScopedScopeLike {\n if (typeof scope.forLocale === \"function\" && scope.locale !== locale) {\n return scope.forLocale(locale);\n }\n return scope;\n}\n\n/**\n * Wraps a loaded scope `t` with namespace guards and locale binding.\n */\nexport function createScopedT(\n scope: ScopedScopeLike,\n options: CreateScopedTOptions\n): ScopedTranslateFn {\n const activeScope = resolveActiveScope(scope, options.locale);\n const { namespaces } = options;\n\n return ((namespace: string, key: string, ...rest: unknown[]) => {\n assertNamespace(namespace, namespaces);\n return activeScope.t(namespace, key, ...rest);\n }) as ScopedTranslateFn;\n}\n\n/**\n * Curries a multi-namespace context `t(ns, key, …)` into a namespace-bound `t(key, …)`.\n * Used when composing gate values without introducing a separate hooks API.\n */\nexport function bindNamespaceTranslate(\n t: ScopedTranslateFn,\n namespace: string\n): (key: string, params?: Record<string, unknown>) => string {\n return (key: string, params?: Record<string, unknown>) =>\n params === undefined ? t(namespace, key) : t(namespace, key, params);\n}\n","/**\n * Load deduplication for lazy i18n namespace gates / HOCs.\n *\n * Supports multiple concurrent keys so parallel per-namespace gates can share\n * one coordinator. Each `(engine, partition, namespaces)` entry keeps its own\n * promise and status. Display state (keep-previous / pendingLocale / stable `t`)\n * lives here so gate components stay pure observers of `useSyncExternalStore`.\n */\nimport { createScopedT } from \"./create-scoped-t.js\";\nimport type {\n GetDisplayEntryOptions,\n LoadCoordinator,\n LoadCoordinatorEntry,\n LoadCoordinatorKey,\n LoadCoordinatorRequest,\n LoadDisplayEntry,\n ScopedScopeLike,\n ScopedTranslateFn,\n} from \"./types.js\";\n\nconst engineIds = new WeakMap<object, number>();\nlet nextEngineId = 1;\n\nfunction engineId(ref: unknown): number {\n if (ref === null || (typeof ref !== \"object\" && typeof ref !== \"function\")) {\n return 0;\n }\n const obj = ref as object;\n let id = engineIds.get(obj);\n if (id === undefined) {\n id = nextEngineId++;\n engineIds.set(obj, id);\n }\n return id;\n}\n\nfunction cacheKey(\n engineRef: unknown,\n partition: string | undefined,\n namespaces: readonly string[]\n): string {\n return `${engineId(engineRef)}\\0${partition ?? \"\"}\\0${namespaces.join(\"\\0\")}`;\n}\n\n/** Gate identity for keep-previous (namespaces only — partition/locale can change). */\nfunction gateId(engineRef: unknown, namespaces: readonly string[]): string {\n return `${engineId(engineRef)}\\0${namespaces.join(\"\\0\")}`;\n}\n\ninterface CacheEntry<Scope> {\n status: \"pending\" | \"resolved\" | \"error\";\n promise: Promise<Scope>;\n resolvedScope: Scope | null;\n error: unknown;\n requestId: number;\n /** Stable LoadCoordinatorEntry snapshot for getEntry(). */\n entrySnapshot: LoadCoordinatorEntry<Scope>;\n}\n\ninterface DisplayCache {\n lastReady: { scope: ScopedScopeLike; locale: string } | null;\n /** Stable display entry object until inputs change. */\n displaySnapshot: LoadDisplayEntry<ScopedScopeLike> | null;\n /** Identity key for the last displaySnapshot. */\n displayKey: string | null;\n boundT: ScopedTranslateFn | null;\n boundTKey: string | null;\n}\n\n/**\n * Dedupes in-flight loads by `(engineRef, partition, namespaces)` and exposes\n * display snapshots + retry for manual pending/resolved/error rendering.\n */\nexport function createLoadCoordinator<Scope = ScopedScopeLike>(): LoadCoordinator<Scope> {\n let revision = 0;\n let nextRequestId = 0;\n const entries = new Map<string, CacheEntry<Scope>>();\n const displayByGate = new Map<string, DisplayCache>();\n const listeners = new Set<() => void>();\n let lastKey: string | null = null;\n\n function notify(): void {\n for (const listener of listeners) {\n listener();\n }\n }\n\n function bumpAndNotify(): void {\n revision += 1;\n // Invalidate display snapshots that depend on entry status.\n for (const display of displayByGate.values()) {\n display.displaySnapshot = null;\n display.displayKey = null;\n }\n notify();\n }\n\n function markResolvedSync(key: string, syncScope: Scope): void {\n const entrySnapshot: LoadCoordinatorEntry<Scope> = {\n status: \"resolved\",\n scope: syncScope,\n };\n entries.set(key, {\n status: \"resolved\",\n promise: Promise.resolve(syncScope),\n resolvedScope: syncScope,\n error: null,\n requestId: ++nextRequestId,\n entrySnapshot,\n });\n }\n\n /**\n * SSR / getServerSnapshot: hydrate from peek/state only — never kick `load()`.\n */\n function ensureSync(input: LoadCoordinatorRequest<Scope>): void {\n const key = cacheKey(input.engineRef, input.partition, input.namespaces);\n lastKey = key;\n\n if (entries.has(key)) {\n return;\n }\n\n if (input.tryResolveSync === undefined) {\n return;\n }\n\n const syncScope = input.tryResolveSync();\n if (syncScope !== null) {\n markResolvedSync(key, syncScope);\n }\n }\n\n function ensure(input: LoadCoordinatorRequest<Scope>): void {\n const key = cacheKey(input.engineRef, input.partition, input.namespaces);\n lastKey = key;\n\n const existing = entries.get(key);\n if (existing !== undefined) {\n return;\n }\n\n if (input.tryResolveSync !== undefined) {\n const syncScope = input.tryResolveSync();\n if (syncScope !== null) {\n markResolvedSync(key, syncScope);\n return;\n }\n }\n\n const requestId = ++nextRequestId;\n const promise = input.load().then(\n (scope) => {\n const entry = entries.get(key);\n if (entry === undefined || entry.requestId !== requestId) {\n return scope;\n }\n entry.status = \"resolved\";\n entry.resolvedScope = scope;\n entry.error = null;\n entry.entrySnapshot = { status: \"resolved\", scope };\n bumpAndNotify();\n return scope;\n },\n (error: unknown) => {\n const entry = entries.get(key);\n if (entry !== undefined && entry.requestId === requestId) {\n entry.status = \"error\";\n entry.error = error;\n entry.resolvedScope = null;\n entry.entrySnapshot = { status: \"error\", error };\n bumpAndNotify();\n }\n throw error;\n }\n );\n // Avoid unhandled rejection when only gate subscribers attach (no getPromise).\n void promise.catch((error: unknown) => {\n console.error(\"[i18n-react] namespace load failed:\", error);\n });\n\n entries.set(key, {\n status: \"pending\",\n promise,\n resolvedScope: null,\n error: null,\n requestId,\n entrySnapshot: { status: \"pending\" },\n });\n }\n\n function getEntry(keyInput: LoadCoordinatorKey): LoadCoordinatorEntry<Scope> {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n const entry = entries.get(key);\n if (entry === undefined) {\n return { status: \"pending\" };\n }\n return entry.entrySnapshot;\n }\n\n function getOrCreateDisplay(engineRef: unknown, namespaces: readonly string[]): DisplayCache {\n const id = gateId(engineRef, namespaces);\n let display = displayByGate.get(id);\n if (display === undefined) {\n display = {\n lastReady: null,\n displaySnapshot: null,\n displayKey: null,\n boundT: null,\n boundTKey: null,\n };\n displayByGate.set(id, display);\n }\n return display;\n }\n\n function bindT(\n display: DisplayCache,\n scope: ScopedScopeLike,\n locale: string,\n namespaces: readonly string[],\n scopeToken: string\n ): ScopedTranslateFn {\n const tKey = `${scopeToken}\\0${locale}\\0${namespaces.join(\"\\0\")}`;\n if (display.boundT !== null && display.boundTKey === tKey) {\n return display.boundT;\n }\n const t = createScopedT(scope, { namespaces, locale });\n display.boundT = t;\n display.boundTKey = tKey;\n return t;\n }\n\n function getDisplayEntry(\n keyInput: LoadCoordinatorKey,\n options: GetDisplayEntryOptions\n ): LoadDisplayEntry<Scope> {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n const entry = entries.get(key);\n const display = getOrCreateDisplay(keyInput.engineRef, options.namespaces);\n const keepPrevious = options.keepPrevious !== false;\n\n const retry = () => {\n retryKey(keyInput);\n };\n\n let result: LoadDisplayEntry<Scope>;\n\n if (entry === undefined || entry.status === \"pending\") {\n const kept = keepPrevious ? display.lastReady : null;\n result = {\n status: \"pending\",\n display: kept as { scope: Scope & ScopedScopeLike; locale: string } | null,\n pendingLocale: kept ? options.locale : undefined,\n t: kept\n ? bindT(display, kept.scope, kept.locale, options.namespaces, `kept:${kept.locale}`)\n : null,\n } as LoadDisplayEntry<Scope>;\n } else if (entry.status === \"resolved\") {\n const scope = entry.resolvedScope as Scope & ScopedScopeLike;\n display.lastReady = { scope, locale: options.locale };\n result = {\n status: \"ready\",\n scope,\n locale: options.locale,\n t: bindT(display, scope, options.locale, options.namespaces, `ready:${key}`),\n } as LoadDisplayEntry<Scope>;\n } else {\n const kept = keepPrevious ? display.lastReady : null;\n result = {\n status: \"error\",\n error: entry.error,\n display: kept as { scope: Scope & ScopedScopeLike; locale: string } | null,\n pendingLocale: kept ? options.locale : undefined,\n t: kept\n ? bindT(display, kept.scope, kept.locale, options.namespaces, `kept:${kept.locale}`)\n : null,\n retry,\n } as LoadDisplayEntry<Scope>;\n }\n\n const displayKey = `${key}\\0${result.status}\\0${options.locale}\\0${String(!!result.t)}`;\n if (display.displaySnapshot !== null && display.displayKey === displayKey) {\n return display.displaySnapshot as LoadDisplayEntry<Scope>;\n }\n display.displaySnapshot = result as LoadDisplayEntry<ScopedScopeLike>;\n display.displayKey = displayKey;\n return result;\n }\n\n function retryKey(keyInput: LoadCoordinatorKey): void {\n const key = cacheKey(keyInput.engineRef, keyInput.partition, keyInput.namespaces);\n if (!entries.has(key)) {\n return;\n }\n entries.delete(key);\n bumpAndNotify();\n }\n\n function subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n }\n\n function getPromise(): Promise<Scope> {\n if (lastKey === null) {\n throw new Error(\"createLoadCoordinator: call ensure()/request() before getPromise()\");\n }\n const entry = entries.get(lastKey);\n if (entry === undefined) {\n throw new Error(\"createLoadCoordinator: call ensure()/request() before getPromise()\");\n }\n return entry.promise;\n }\n\n function getResolvedScope(): Scope | null {\n if (lastKey === null) {\n return null;\n }\n const entry = entries.get(lastKey);\n return entry?.status === \"resolved\" ? entry.resolvedScope : null;\n }\n\n return {\n get revision() {\n return revision;\n },\n request: ensure,\n ensure,\n ensureSync,\n getEntry,\n getDisplayEntry,\n retry: retryKey,\n subscribe,\n getPromise,\n getResolvedScope,\n };\n}\n","/**\n * Manual (non-Suspense) i18n load gate: ensure + subscribe via the load\n * coordinator, then render fallback / keep-previous / error UI / throw.\n */\nimport {\n forwardRef,\n useSyncExternalStore,\n type ForwardedRef,\n type ForwardRefExoticComponent,\n type ReactNode,\n type RefAttributes,\n} from \"react\";\nimport type {\n LoadCoordinator,\n LoadCoordinatorRequest,\n LoadDisplayEntry,\n LoadPartition,\n ScopedScopeLike,\n ScopedTranslateFn,\n} from \"./types.js\";\n\n/** Inputs for {@link useNamespaceLoad}. */\nexport interface UseNamespaceLoadInput<Scope> {\n coordinator: LoadCoordinator<Scope>;\n engineRef: unknown;\n partition: LoadPartition;\n namespaces: readonly string[];\n locale: string;\n load: () => Promise<Scope>;\n tryResolveSync?: () => Scope | null;\n keepPrevious?: boolean;\n}\n\n/**\n * Starts (or reuses) a coordinator load and re-renders when that entry settles.\n * Client getSnapshot may kick `load()`; SSR getServerSnapshot only peeks sync\n * (hydrated `state`) and never starts a client fetch during prerender.\n */\nexport function useNamespaceLoad<Scope>(\n input: UseNamespaceLoadInput<Scope>\n): LoadDisplayEntry<Scope> {\n const {\n coordinator,\n engineRef,\n partition,\n namespaces,\n locale,\n load,\n tryResolveSync,\n keepPrevious,\n } = input;\n\n const request: LoadCoordinatorRequest<Scope> = {\n engineRef,\n partition,\n namespaces,\n load,\n ...(tryResolveSync !== undefined ? { tryResolveSync } : {}),\n };\n\n const displayOptions = {\n locale,\n namespaces,\n ...(keepPrevious !== undefined ? { keepPrevious } : {}),\n };\n\n return useSyncExternalStore(\n coordinator.subscribe,\n () => {\n coordinator.ensure(request);\n return coordinator.getDisplayEntry({ engineRef, partition, namespaces }, displayOptions);\n },\n () => {\n coordinator.ensureSync(request);\n return coordinator.getDisplayEntry({ engineRef, partition, namespaces }, displayOptions);\n }\n );\n}\n\n/** Scoped `{ t, locale }` injected into gate children / HOC when ready (or kept). */\nexport interface I18nLoadGateValue {\n t: ScopedTranslateFn;\n locale: string;\n /** Set during keep-then-switch while a new partition is loading. */\n pendingLocale?: string;\n error?: unknown;\n retry?: () => void;\n}\n\n/** Load wiring from React context (coordinator, engine, partition, load). */\nexport interface I18nLoadArgs {\n coordinator: LoadCoordinator<ScopedScopeLike>;\n engineRef: unknown;\n partition: LoadPartition;\n locale: string;\n load: (namespaces: readonly string[]) => Promise<ScopedScopeLike>;\n tryResolveSync?: (namespaces: readonly string[]) => ScopedScopeLike | null;\n}\n\nexport interface CreateI18nLoadGateOptions {\n /**\n * When true (default), keep last resolved `{ t, locale }` while a new\n * partition is pending instead of showing fallback.\n */\n keepPreviousOnPartitionChange?: boolean;\n /** Hook that resolves coordinator + load inputs from React context. */\n useLoadArgs: () => I18nLoadArgs;\n}\n\nexport interface I18nGateProps {\n namespaces: readonly string[];\n /** Shown only while the load is pending (not on error). */\n fallback?: ReactNode;\n /**\n * When set, called for error with no keep-previous display.\n * Otherwise the load error is thrown for a React error boundary.\n */\n renderError?: (args: { error: unknown; retry: () => void }) => ReactNode;\n children: (value: I18nLoadGateValue) => ReactNode;\n}\n\n/** Pending UI for `withI18n` — static node or derived from own props. */\nexport type WithI18nFallback<P extends object> = ReactNode | ((props: P) => ReactNode);\n\nexport interface I18nHocOptions<P extends object = object> {\n namespaces: readonly string[];\n fallback?: WithI18nFallback<P>;\n renderError?: (args: { error: unknown; retry: () => void; props: P }) => ReactNode;\n}\n\n/** Render fn for `withI18n`: own props, i18n bag, optional forwarded ref. */\nexport type WithI18nRender<P extends object, R = never> = (\n props: P,\n i18n: I18nLoadGateValue,\n ref?: ForwardedRef<R>\n) => ReactNode;\n\nfunction resolveNamespaces(namespaces: readonly string[] | undefined): readonly string[] {\n if (namespaces === undefined || namespaces.length === 0) {\n throw new Error(\"withI18n / I18n: requires a non-empty namespaces list\");\n }\n return namespaces;\n}\n\nfunction toThrownError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n if (typeof error === \"string\") {\n return new Error(error);\n }\n return new Error(\"i18n namespace load failed\");\n}\n\nconst noopT: ScopedTranslateFn = () => \"\";\n\n/** Gate value for display, or `null` when only fallback / error UI should show. */\nfunction gateValueFromEntry(entry: LoadDisplayEntry<ScopedScopeLike>): I18nLoadGateValue | null {\n if (entry.status === \"ready\") {\n return { t: entry.t, locale: entry.locale };\n }\n\n if (entry.t !== null && entry.display) {\n const value: I18nLoadGateValue = {\n t: entry.t,\n locale: entry.display.locale,\n };\n if (entry.pendingLocale !== undefined) {\n value.pendingLocale = entry.pendingLocale;\n }\n if (entry.status === \"error\") {\n value.error = entry.error;\n value.retry = entry.retry;\n }\n return value;\n }\n\n return null;\n}\n\nfunction renderDisplayEntry(\n entry: LoadDisplayEntry<ScopedScopeLike>,\n options: {\n fallback?: ReactNode;\n renderError?: (args: { error: unknown; retry: () => void }) => ReactNode;\n children: (value: I18nLoadGateValue) => ReactNode;\n }\n): ReactNode {\n if (entry.status === \"error\" && entry.t === null) {\n if (options.renderError) {\n return options.renderError({ error: entry.error, retry: entry.retry! });\n }\n throw toThrownError(entry.error);\n }\n\n const value = gateValueFromEntry(entry);\n if (value !== null) {\n return options.children(value);\n }\n\n return options.fallback ?? null;\n}\n\n/**\n * Factory for generic `I18n` gate + `withI18n` HOC over the same Outer.\n * Codegen emits typed wrappers; tests use this directly.\n */\nexport function createI18nLoadGate(options: CreateI18nLoadGateOptions): {\n I18n: (props: I18nGateProps) => ReactNode;\n withI18n: <P extends object = object, R = never>(\n hocOptions: I18nHocOptions<P>,\n render: WithI18nRender<P, R>\n ) => ForwardRefExoticComponent<P & RefAttributes<R>>;\n} {\n const { useLoadArgs } = options;\n const keepPrevious = options.keepPreviousOnPartitionChange !== false;\n\n function useGateLoad(namespaces: readonly string[]): {\n entry: LoadDisplayEntry<ScopedScopeLike>;\n locale: string;\n } {\n const resolved = resolveNamespaces(namespaces);\n const args = useLoadArgs();\n const entry = useNamespaceLoad({\n coordinator: args.coordinator,\n engineRef: args.engineRef,\n partition: args.partition,\n namespaces: resolved,\n locale: args.locale,\n load: () => args.load(resolved),\n ...(args.tryResolveSync !== undefined\n ? { tryResolveSync: () => args.tryResolveSync!(resolved) }\n : {}),\n keepPrevious,\n });\n return { entry, locale: args.locale };\n }\n\n function I18n({ namespaces, fallback, renderError, children }: I18nGateProps): ReactNode {\n const { entry } = useGateLoad(namespaces);\n return renderDisplayEntry(entry, {\n ...(fallback !== undefined ? { fallback } : {}),\n ...(renderError !== undefined ? { renderError } : {}),\n children,\n });\n }\n\n function withI18n<P extends object = object, R = never>(\n hocOptions: I18nHocOptions<P>,\n render: WithI18nRender<P, R>\n ): ForwardRefExoticComponent<P & RefAttributes<R>> {\n const { fallback, renderError } = hocOptions;\n const Outer = forwardRef<R, P>(function I18nHocOuter(props, ref) {\n const { entry, locale } = useGateLoad(hocOptions.namespaces);\n\n // Always invoke `render` so hooks inside it are Outer hooks and stay\n // unconditional across pending → ready (e.g. I18nRoot without hydrated state).\n const displayValue = gateValueFromEntry(entry);\n const value = displayValue ?? ({ t: noopT, locale } satisfies I18nLoadGateValue);\n const rendered = render(props as P, value, ref);\n\n if (entry.status === \"error\" && entry.t === null) {\n if (renderError) {\n return renderError({ error: entry.error, retry: entry.retry!, props: props as P });\n }\n throw toThrownError(entry.error);\n }\n\n if (displayValue !== null) {\n return rendered;\n }\n\n if (fallback === undefined) {\n return null;\n }\n return typeof fallback === \"function\" ? fallback(props as P) : fallback;\n });\n\n const displayName = render.name || \"Component\";\n Outer.displayName = `withI18n(${displayName})`;\n return Outer as ForwardRefExoticComponent<P & RefAttributes<R>>;\n }\n\n return { I18n, withI18n };\n}\n","/**\n * Holds the shared i18n root: handle + coordinator + active locale.\n *\n * Generated {@link useI18nRoot} casts the handle to the project's typed factory return.\n */\nimport { createContext, useContext } from \"react\";\nimport type { I18nRootContextValue } from \"./types.js\";\n\nconst I18nRootContext = createContext<I18nRootContextValue | null>(null);\n\n/** Resolves the shared root from an ancestor {@link I18nRootProvider}. */\nexport function useI18nRootContext(): I18nRootContextValue {\n const value = useContext(I18nRootContext);\n if (value === null) {\n throw new Error(\"useI18nRoot must be used within I18nRoot / I18nRootProvider\");\n }\n return value;\n}\n\n/** Internal — provider wiring. */\nexport { I18nRootContext };\n","/**\n * Single provider that shares handle + coordinator + locale across a lazy subtree.\n */\nimport { useRef, type ReactNode } from \"react\";\nimport type { FetchArtifact, I18nCreateInput, OnMissingTranslation } from \"@xndrjs/i18n\";\nimport { createLoadCoordinator } from \"./create-load-coordinator.js\";\nimport { I18nRootContext } from \"./root-context.js\";\nimport type {\n CreateI18nFactory,\n I18nHandleLike,\n I18nRootContextValue,\n ScopedScopeLike,\n} from \"./types.js\";\n\nexport interface I18nRootProviderProps {\n createI18n: CreateI18nFactory;\n locale: string;\n /**\n * Cold-start / eager dictionary seed (defaults to `{}` when neither prop is set).\n * Mutually exclusive with {@link state}.\n */\n dictionary?: Record<string, unknown>;\n /** Full create/hydrate input from `handle.serialize()` — mutually exclusive with {@link dictionary}. */\n state?: I18nCreateInput | undefined;\n onMissing?: OnMissingTranslation;\n /** Custom JSON loader for `loaderStrategy: \"fetch\"` projects (ignored by import loaders). */\n fetchImpl?: FetchArtifact;\n children: ReactNode;\n}\n\n/** Creates and shares one handle + load coordinator for descendant gates. */\nexport function I18nRootProvider({\n createI18n,\n locale,\n dictionary,\n state,\n onMissing,\n fetchImpl,\n children,\n}: I18nRootProviderProps): ReactNode {\n if (state !== undefined && dictionary !== undefined) {\n throw new Error(\"I18nRootProvider: pass either `state` or `dictionary`, not both.\");\n }\n\n const valueRef = useRef<I18nRootContextValue | null>(null);\n\n if (valueRef.current === null) {\n const seed: I18nCreateInput =\n state ?? ({ dictionary: dictionary ?? {} } satisfies I18nCreateInput);\n const handle = createI18n({\n state: seed,\n ...(onMissing !== undefined ? { onMissing } : {}),\n ...(fetchImpl !== undefined ? { fetchImpl } : {}),\n });\n valueRef.current = {\n handle: handle as I18nHandleLike,\n coordinator: createLoadCoordinator<ScopedScopeLike>(),\n locale,\n };\n } else {\n valueRef.current.locale = locale;\n }\n\n return <I18nRootContext.Provider value={valueRef.current}>{children}</I18nRootContext.Provider>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xndrjs/i18n-react",
|
|
3
|
-
"version": "0.8.1",
|
|
3
|
+
"version": "0.8.2-alpha.1",
|
|
4
4
|
"description": "React runtime primitives and codegen for @xndrjs/i18n client bindings.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
13
|
"bin": {
|
|
14
|
-
"xndrjs-i18n-react-codegen": "./
|
|
14
|
+
"xndrjs-i18n-react-codegen": "./dist/cli/codegen.js"
|
|
15
15
|
},
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
17
|
"exports": {
|
|
@@ -22,17 +22,14 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
|
-
"dist"
|
|
26
|
-
"bin",
|
|
27
|
-
"src"
|
|
25
|
+
"dist"
|
|
28
26
|
],
|
|
29
27
|
"engines": {
|
|
30
28
|
"node": ">=18"
|
|
31
29
|
},
|
|
32
30
|
"peerDependencies": {
|
|
33
|
-
"@xndrjs/i18n": "^0.8.1",
|
|
31
|
+
"@xndrjs/i18n": "^0.8.2-alpha.1",
|
|
34
32
|
"react": ">=19",
|
|
35
|
-
"tsx": ">=4",
|
|
36
33
|
"zod": "^4.0.0"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
@@ -46,11 +43,11 @@
|
|
|
46
43
|
"tsx": "^4.22.4",
|
|
47
44
|
"vitest": "^4.1.0",
|
|
48
45
|
"zod": "^4.3.6",
|
|
49
|
-
"@xndrjs/i18n": "^0.8.1"
|
|
46
|
+
"@xndrjs/i18n": "^0.8.2-alpha.1"
|
|
50
47
|
},
|
|
51
48
|
"scripts": {
|
|
52
49
|
"build": "tsup",
|
|
53
|
-
"codegen": "
|
|
50
|
+
"codegen": "node dist/cli/codegen.js",
|
|
54
51
|
"pack": "pnpm run build && mkdir -p artifacts && npm pack --pack-destination artifacts",
|
|
55
52
|
"lint": "eslint .",
|
|
56
53
|
"lint:fix": "eslint . --fix",
|
package/bin/codegen.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
5
|
-
|
|
6
|
-
const script = join(
|
|
7
|
-
dirname(fileURLToPath(import.meta.url)),
|
|
8
|
-
"../src/codegen/generate-react-bindings.ts"
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const result = spawnSync("tsx", [script, ...process.argv.slice(2)], {
|
|
12
|
-
stdio: "inherit",
|
|
13
|
-
cwd: process.cwd(),
|
|
14
|
-
env: process.env,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
process.exit(result.status ?? 1);
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { formatReactBindingsFile } from "./react-bindings-file.js";
|
|
3
|
-
|
|
4
|
-
const baseOptions = {
|
|
5
|
-
factoryName: "createI18n",
|
|
6
|
-
paramsTypeName: "MyProjectParams",
|
|
7
|
-
schemaTypeName: "MyProjectSchema",
|
|
8
|
-
localeTypeName: "MyProjectLocale",
|
|
9
|
-
instanceImport: "./instance.generated",
|
|
10
|
-
typesImport: "./i18n-types.generated",
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
describe("formatReactBindingsFile", () => {
|
|
14
|
-
it("emits I18nRoot + withI18n / I18n over a single context", () => {
|
|
15
|
-
const output = formatReactBindingsFile(baseOptions);
|
|
16
|
-
|
|
17
|
-
expect(output).toContain("I18nRoot");
|
|
18
|
-
expect(output).toContain("I18nRootProvider");
|
|
19
|
-
expect(output).toContain("useI18nRoot");
|
|
20
|
-
expect(output).toContain("export function withI18n");
|
|
21
|
-
expect(output).toContain("export type I18nProps");
|
|
22
|
-
expect(output).toContain("Ns extends readonly []");
|
|
23
|
-
expect(output).toContain("namespace: never, key: never");
|
|
24
|
-
expect(output).toContain("WithI18nFallback");
|
|
25
|
-
expect(output).toContain("ForwardRefExoticComponent");
|
|
26
|
-
expect(output).toContain("RefAttributes");
|
|
27
|
-
expect(output).toContain("handle.load");
|
|
28
|
-
expect(output).toContain("handle.peek");
|
|
29
|
-
expect(output).toContain("useI18nRootContext()");
|
|
30
|
-
expect(output).not.toContain("fetchImpl");
|
|
31
|
-
expect(output).not.toContain("FetchArtifact");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("requires fetchImpl on I18nRoot when loaderStrategy is fetch", () => {
|
|
35
|
-
const output = formatReactBindingsFile({
|
|
36
|
-
...baseOptions,
|
|
37
|
-
loaderStrategy: "fetch",
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
expect(output).toContain("import type { FetchArtifact, I18nCreateInput }");
|
|
41
|
-
expect(output).toContain("fetchImpl: FetchArtifact");
|
|
42
|
-
expect(output).toContain("fetchImpl={fetchImpl}");
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
export const GENERATED_FILE_BANNER = "// Automatically generated code. Do not edit manually.\n";
|
|
4
|
-
|
|
5
|
-
export type ImportExtension = "none" | ".ts" | ".js";
|
|
6
|
-
|
|
7
|
-
export function importExtensionSuffix(importExtension: ImportExtension): string {
|
|
8
|
-
return importExtension === "none" ? "" : importExtension;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function toModuleBasename(filePath: string): string {
|
|
12
|
-
return path.basename(filePath).replace(/\.tsx?$/, "");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function toRelativeModuleImport(
|
|
16
|
-
moduleBasename: string,
|
|
17
|
-
importExtension: ImportExtension
|
|
18
|
-
): string {
|
|
19
|
-
return `./${moduleBasename}${importExtensionSuffix(importExtension)}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ReactBindingsFileOptions {
|
|
23
|
-
factoryName: string;
|
|
24
|
-
paramsTypeName: string;
|
|
25
|
-
schemaTypeName: string;
|
|
26
|
-
localeTypeName: string;
|
|
27
|
-
instanceImport: string;
|
|
28
|
-
typesImport: string;
|
|
29
|
-
/** When `"fetch"`, {@link I18nRoot} requires `fetchImpl`. */
|
|
30
|
-
loaderStrategy?: "import" | "fetch";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** When `Ns` is `[]`, reject every `t(...)` call so refactoring can clear namespaces and re-add only what is used. */
|
|
34
|
-
function formatMultiScopedTType(
|
|
35
|
-
schemaTypeName: string,
|
|
36
|
-
paramsTypeName: string,
|
|
37
|
-
localeTypeName: string
|
|
38
|
-
): string {
|
|
39
|
-
const emptyBranch = `(namespace: never, key: never, ...args: never[]) => string`;
|
|
40
|
-
return (
|
|
41
|
-
`type ScopedT<Ns extends readonly AppNamespace[]> = Ns extends readonly []\n` +
|
|
42
|
-
` ? ${emptyBranch}\n` +
|
|
43
|
-
` : I18nScopeMultiForLocale<\n` +
|
|
44
|
-
` SchemaForNamespaces<${schemaTypeName}, Ns>,\n` +
|
|
45
|
-
` ParamsForNamespaces<${schemaTypeName}, ${paramsTypeName}, Ns>,\n` +
|
|
46
|
-
` ${localeTypeName},\n` +
|
|
47
|
-
` ${localeTypeName}\n` +
|
|
48
|
-
`>["t"];\n\n`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function formatI18nRoot(options: {
|
|
53
|
-
factoryName: string;
|
|
54
|
-
localeTypeName: string;
|
|
55
|
-
loaderStrategy: "import" | "fetch";
|
|
56
|
-
}): string {
|
|
57
|
-
const { factoryName, localeTypeName, loaderStrategy } = options;
|
|
58
|
-
|
|
59
|
-
if (loaderStrategy === "fetch") {
|
|
60
|
-
return (
|
|
61
|
-
`export function I18nRoot({\n` +
|
|
62
|
-
` locale,\n` +
|
|
63
|
-
` children,\n` +
|
|
64
|
-
` state,\n` +
|
|
65
|
-
` dictionary,\n` +
|
|
66
|
-
` fetchImpl,\n` +
|
|
67
|
-
`}: {\n` +
|
|
68
|
-
` locale: ${localeTypeName};\n` +
|
|
69
|
-
` children: ReactNode;\n` +
|
|
70
|
-
` state?: I18nCreateInput | undefined;\n` +
|
|
71
|
-
` dictionary?: Record<string, unknown> | undefined;\n` +
|
|
72
|
-
` fetchImpl: FetchArtifact;\n` +
|
|
73
|
-
`}) {\n` +
|
|
74
|
-
` return (\n` +
|
|
75
|
-
` <I18nRootProvider\n` +
|
|
76
|
-
` createI18n={${factoryName}}\n` +
|
|
77
|
-
` locale={locale}\n` +
|
|
78
|
-
` fetchImpl={fetchImpl}\n` +
|
|
79
|
-
` {...(state !== undefined ? { state } : {})}\n` +
|
|
80
|
-
` {...(dictionary !== undefined ? { dictionary } : {})}\n` +
|
|
81
|
-
` >\n` +
|
|
82
|
-
` {children}\n` +
|
|
83
|
-
` </I18nRootProvider>\n` +
|
|
84
|
-
` );\n` +
|
|
85
|
-
`}\n`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
`export function I18nRoot({\n` +
|
|
91
|
-
` locale,\n` +
|
|
92
|
-
` children,\n` +
|
|
93
|
-
` state,\n` +
|
|
94
|
-
` dictionary,\n` +
|
|
95
|
-
`}: {\n` +
|
|
96
|
-
` locale: ${localeTypeName};\n` +
|
|
97
|
-
` children: ReactNode;\n` +
|
|
98
|
-
` state?: I18nCreateInput | undefined;\n` +
|
|
99
|
-
` dictionary?: Record<string, unknown> | undefined;\n` +
|
|
100
|
-
`}) {\n` +
|
|
101
|
-
` return (\n` +
|
|
102
|
-
` <I18nRootProvider\n` +
|
|
103
|
-
` createI18n={${factoryName}}\n` +
|
|
104
|
-
` locale={locale}\n` +
|
|
105
|
-
` {...(state !== undefined ? { state } : {})}\n` +
|
|
106
|
-
` {...(dictionary !== undefined ? { dictionary } : {})}\n` +
|
|
107
|
-
` >\n` +
|
|
108
|
-
` {children}\n` +
|
|
109
|
-
` </I18nRootProvider>\n` +
|
|
110
|
-
` );\n` +
|
|
111
|
-
`}\n`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Emits typed root + withI18n / I18n for split-by-locale or custom delivery.
|
|
117
|
-
* Every namespace loads through the handle + namespaceLoaders.
|
|
118
|
-
* Partition→area mapping for custom delivery is injected into the handle by core codegen.
|
|
119
|
-
*/
|
|
120
|
-
export function formatReactBindingsFile(options: ReactBindingsFileOptions): string {
|
|
121
|
-
const {
|
|
122
|
-
factoryName,
|
|
123
|
-
localeTypeName,
|
|
124
|
-
instanceImport,
|
|
125
|
-
paramsTypeName,
|
|
126
|
-
schemaTypeName,
|
|
127
|
-
typesImport,
|
|
128
|
-
loaderStrategy = "import",
|
|
129
|
-
} = options;
|
|
130
|
-
|
|
131
|
-
const createInputImport =
|
|
132
|
-
loaderStrategy === "fetch"
|
|
133
|
-
? `import type { FetchArtifact, I18nCreateInput } from "@xndrjs/i18n";\n`
|
|
134
|
-
: `import type { I18nCreateInput } from "@xndrjs/i18n";\n`;
|
|
135
|
-
|
|
136
|
-
return (
|
|
137
|
-
`${GENERATED_FILE_BANNER}` +
|
|
138
|
-
`"use client";\n\n` +
|
|
139
|
-
`import {\n` +
|
|
140
|
-
` type ForwardedRef,\n` +
|
|
141
|
-
` type ForwardRefExoticComponent,\n` +
|
|
142
|
-
` type ReactNode,\n` +
|
|
143
|
-
` type RefAttributes,\n` +
|
|
144
|
-
`} from "react";\n` +
|
|
145
|
-
`import {\n` +
|
|
146
|
-
` createI18nLoadGate,\n` +
|
|
147
|
-
` I18nRootProvider,\n` +
|
|
148
|
-
` useI18nRootContext,\n` +
|
|
149
|
-
`} from "@xndrjs/i18n-react";\n` +
|
|
150
|
-
createInputImport +
|
|
151
|
-
`import type {\n` +
|
|
152
|
-
` I18nScopeMultiForLocale,\n` +
|
|
153
|
-
` ParamsForNamespaces,\n` +
|
|
154
|
-
` SchemaForNamespaces,\n` +
|
|
155
|
-
`} from "@xndrjs/i18n";\n` +
|
|
156
|
-
`import { ${factoryName} } from "${instanceImport}";\n` +
|
|
157
|
-
`import type { ${paramsTypeName}, ${schemaTypeName}, ${localeTypeName} } from "${typesImport}";\n` +
|
|
158
|
-
`\n` +
|
|
159
|
-
`export function useI18nRoot(): ReturnType<typeof ${factoryName}> {\n` +
|
|
160
|
-
` return useI18nRootContext().handle as ReturnType<typeof ${factoryName}>;\n` +
|
|
161
|
-
`}\n\n` +
|
|
162
|
-
`type AppNamespace = keyof ${schemaTypeName} & string;\n\n` +
|
|
163
|
-
formatMultiScopedTType(schemaTypeName, paramsTypeName, localeTypeName) +
|
|
164
|
-
`type I18nInjected<Ns extends readonly AppNamespace[]> = {\n` +
|
|
165
|
-
` t: ScopedT<Ns>;\n` +
|
|
166
|
-
` locale: ${localeTypeName};\n` +
|
|
167
|
-
` pendingLocale?: ${localeTypeName};\n` +
|
|
168
|
-
` error?: unknown;\n` +
|
|
169
|
-
` retry?: () => void;\n` +
|
|
170
|
-
`};\n` +
|
|
171
|
-
`export type I18nProps<Ns extends readonly AppNamespace[]> = I18nInjected<Ns>;\n\n` +
|
|
172
|
-
`export type WithI18nFallback<P extends object> = ReactNode | ((props: P) => ReactNode);\n\n` +
|
|
173
|
-
`const gate = createI18nLoadGate({\n` +
|
|
174
|
-
` useLoadArgs: () => {\n` +
|
|
175
|
-
` const root = useI18nRootContext();\n` +
|
|
176
|
-
` const { handle, coordinator, locale } = root;\n` +
|
|
177
|
-
` return {\n` +
|
|
178
|
-
` coordinator,\n` +
|
|
179
|
-
` engineRef: handle,\n` +
|
|
180
|
-
` partition: locale,\n` +
|
|
181
|
-
` locale,\n` +
|
|
182
|
-
` load: (namespaces: readonly string[]) =>\n` +
|
|
183
|
-
` handle.load({\n` +
|
|
184
|
-
` namespaces: namespaces as [AppNamespace, ...AppNamespace[]],\n` +
|
|
185
|
-
` locale,\n` +
|
|
186
|
-
` }),\n` +
|
|
187
|
-
` tryResolveSync: (namespaces: readonly string[]) =>\n` +
|
|
188
|
-
` handle.peek({\n` +
|
|
189
|
-
` namespaces: namespaces as [AppNamespace, ...AppNamespace[]],\n` +
|
|
190
|
-
` locale,\n` +
|
|
191
|
-
` }),\n` +
|
|
192
|
-
` };\n` +
|
|
193
|
-
` },\n` +
|
|
194
|
-
`});\n\n` +
|
|
195
|
-
`export function withI18n<\n` +
|
|
196
|
-
` P extends object = object,\n` +
|
|
197
|
-
` R = never,\n` +
|
|
198
|
-
` const Ns extends readonly AppNamespace[] = readonly AppNamespace[],\n` +
|
|
199
|
-
`>(\n` +
|
|
200
|
-
` options: {\n` +
|
|
201
|
-
` namespaces: Ns;\n` +
|
|
202
|
-
` fallback?: WithI18nFallback<P>;\n` +
|
|
203
|
-
` renderError?: (args: { error: unknown; retry: () => void; props: P }) => ReactNode;\n` +
|
|
204
|
-
` },\n` +
|
|
205
|
-
` render: (props: P, i18n: I18nProps<Ns>, ref?: ForwardedRef<R>) => ReactNode\n` +
|
|
206
|
-
`): ForwardRefExoticComponent<P & RefAttributes<R>> {\n` +
|
|
207
|
-
` return gate.withI18n(options, render as never) as ForwardRefExoticComponent<\n` +
|
|
208
|
-
` P & RefAttributes<R>\n` +
|
|
209
|
-
` >;\n` +
|
|
210
|
-
`}\n\n` +
|
|
211
|
-
`export function I18n<\n` +
|
|
212
|
-
` const Ns extends readonly AppNamespace[],\n` +
|
|
213
|
-
`>(props: {\n` +
|
|
214
|
-
` namespaces: Ns;\n` +
|
|
215
|
-
` fallback?: ReactNode;\n` +
|
|
216
|
-
` renderError?: (args: { error: unknown; retry: () => void }) => ReactNode;\n` +
|
|
217
|
-
` children: (value: I18nProps<Ns>) => ReactNode;\n` +
|
|
218
|
-
`}) {\n` +
|
|
219
|
-
` return gate.I18n(props as never);\n` +
|
|
220
|
-
`}\n\n` +
|
|
221
|
-
formatI18nRoot({ factoryName, localeTypeName, loaderStrategy })
|
|
222
|
-
);
|
|
223
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { loadConfig } from "@xndrjs/i18n/codegen";
|
|
4
|
-
import type { CodegenConfig } from "@xndrjs/i18n/codegen";
|
|
5
|
-
import { resolveCodegenPaths } from "@xndrjs/i18n/codegen";
|
|
6
|
-
import {
|
|
7
|
-
formatReactBindingsFile,
|
|
8
|
-
toModuleBasename,
|
|
9
|
-
toRelativeModuleImport,
|
|
10
|
-
} from "./emit/react-bindings-file.js";
|
|
11
|
-
import {
|
|
12
|
-
defaultReactConfigPath,
|
|
13
|
-
loadReactCodegenConfig,
|
|
14
|
-
resolveReactBindingsOutputPath,
|
|
15
|
-
} from "./react-codegen-config.js";
|
|
16
|
-
import { writeFileIfChanged } from "./write-file-if-changed.js";
|
|
17
|
-
|
|
18
|
-
function readCliFlag(flag: string): string | undefined {
|
|
19
|
-
const index = process.argv.indexOf(flag);
|
|
20
|
-
if (index < 0) {
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
const value = process.argv[index + 1];
|
|
24
|
-
if (value === undefined || value.startsWith("-")) {
|
|
25
|
-
throw new Error(`[i18n-react Codegen Error] Missing value for ${flag}`);
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* React bindings codegen CLI.
|
|
32
|
-
*
|
|
33
|
-
* Reads `i18n.codegen.json` from `@xndrjs/i18n` and optionally `i18n-react.codegen.json`.
|
|
34
|
-
* Output path: `--out` > react config `output` > default next to instance under `output/`.
|
|
35
|
-
*/
|
|
36
|
-
function main(): void {
|
|
37
|
-
const configPath = path.resolve(
|
|
38
|
-
process.cwd(),
|
|
39
|
-
readCliFlag("--config") ?? "i18n/i18n.codegen.json"
|
|
40
|
-
);
|
|
41
|
-
const projectRoot = path.dirname(configPath);
|
|
42
|
-
|
|
43
|
-
if (!fs.existsSync(configPath)) {
|
|
44
|
-
throw new Error(`[i18n-react Codegen Error] Config file not found: ${configPath}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const config: CodegenConfig = loadConfig(configPath);
|
|
48
|
-
const paths = resolveCodegenPaths(config);
|
|
49
|
-
|
|
50
|
-
const instanceImport = toRelativeModuleImport(toModuleBasename(paths.instanceOutput), "none");
|
|
51
|
-
const typesImport = toRelativeModuleImport(toModuleBasename(paths.typesOutput), "none");
|
|
52
|
-
|
|
53
|
-
const reactConfigPath = path.resolve(
|
|
54
|
-
projectRoot,
|
|
55
|
-
readCliFlag("--react-config") ?? defaultReactConfigPath(configPath)
|
|
56
|
-
);
|
|
57
|
-
const reactConfig = loadReactCodegenConfig(reactConfigPath);
|
|
58
|
-
const cliOut = readCliFlag("--out");
|
|
59
|
-
const reactOutputRelative = resolveReactBindingsOutputPath({
|
|
60
|
-
instanceOutput: paths.instanceOutput,
|
|
61
|
-
...(cliOut ? { cliOut } : {}),
|
|
62
|
-
reactConfig,
|
|
63
|
-
});
|
|
64
|
-
const reactOutputPath = path.resolve(projectRoot, reactOutputRelative);
|
|
65
|
-
|
|
66
|
-
const content = formatReactBindingsFile({
|
|
67
|
-
factoryName: paths.factoryName,
|
|
68
|
-
paramsTypeName: paths.paramsTypeName,
|
|
69
|
-
schemaTypeName: paths.schemaTypeName,
|
|
70
|
-
localeTypeName: paths.localeTypeName,
|
|
71
|
-
instanceImport,
|
|
72
|
-
typesImport,
|
|
73
|
-
loaderStrategy: config.loaderStrategy ?? "import",
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const written = writeFileIfChanged(reactOutputPath, content);
|
|
77
|
-
const displayPath = path.relative(projectRoot, reactOutputPath);
|
|
78
|
-
|
|
79
|
-
if (written) {
|
|
80
|
-
console.log(`[i18n-react Codegen] Wrote ${displayPath}`);
|
|
81
|
-
} else {
|
|
82
|
-
console.log(`[i18n-react Codegen] Unchanged ${displayPath}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
main();
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
defaultReactBindingsOutput,
|
|
4
|
-
defaultReactConfigPath,
|
|
5
|
-
resolveReactBindingsOutputPath,
|
|
6
|
-
} from "./react-codegen-config.js";
|
|
7
|
-
|
|
8
|
-
describe("react-codegen-config", () => {
|
|
9
|
-
it("defaults output next to instanceOutput", () => {
|
|
10
|
-
expect(defaultReactBindingsOutput("generated/instance.generated.ts")).toBe(
|
|
11
|
-
"generated/react-bindings.generated.tsx"
|
|
12
|
-
);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("prefers --out over react config and default", () => {
|
|
16
|
-
expect(
|
|
17
|
-
resolveReactBindingsOutputPath({
|
|
18
|
-
instanceOutput: "generated/instance.generated.ts",
|
|
19
|
-
cliOut: "custom/bindings.tsx",
|
|
20
|
-
reactConfig: { output: "ignored.tsx" },
|
|
21
|
-
})
|
|
22
|
-
).toBe("custom/bindings.tsx");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("uses react config output when --out is omitted", () => {
|
|
26
|
-
expect(
|
|
27
|
-
resolveReactBindingsOutputPath({
|
|
28
|
-
instanceOutput: "generated/instance.generated.ts",
|
|
29
|
-
reactConfig: { output: "generated/custom-react.tsx" },
|
|
30
|
-
})
|
|
31
|
-
).toBe("generated/custom-react.tsx");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("derives default react config path from i18n.codegen.json", () => {
|
|
35
|
-
expect(defaultReactConfigPath("/app/i18n/i18n.codegen.json")).toBe(
|
|
36
|
-
"/app/i18n/i18n-react.codegen.json"
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
});
|