From 316c610649730c903d7db234f9183e48ad173caf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 8 Sep 2023 09:57:48 +0200 Subject: [PATCH] core-plugin-api: refactor translation ref to be opaque + fix some types Signed-off-by: Patrik Oldsberg --- packages/core-plugin-api/alpha-api-report.md | 95 +++++-------- .../src/apis/definitions/AppTranslationApi.ts | 6 +- .../src/translation/TranslationRef.test.ts | 27 ++-- .../src/translation/TranslationRef.ts | 133 +++++++++++++----- .../core-plugin-api/src/translation/index.ts | 12 +- .../core-plugin-api/src/translation/types.ts | 45 ------ .../src/translation/useTranslationRef.ts | 23 +-- 7 files changed, 170 insertions(+), 171 deletions(-) delete mode 100644 packages/core-plugin-api/src/translation/types.ts diff --git a/packages/core-plugin-api/alpha-api-report.md b/packages/core-plugin-api/alpha-api-report.md index 57cf4a2cb4..119f5c570e 100644 --- a/packages/core-plugin-api/alpha-api-report.md +++ b/packages/core-plugin-api/alpha-api-report.md @@ -11,8 +11,8 @@ import { ReactNode } from 'react'; // @alpha (undocumented) export type AppTranslationApi = { getI18n(): i18n; - addResourcesByRef>( - translationRef: TranslationRef, + addResourcesByRef>( + translationRef: TranslationRef, ): void; }; @@ -20,11 +20,14 @@ export type AppTranslationApi = { export const appTranslationApiRef: ApiRef; // @alpha (undocumented) -export const createTranslationRef: < - Messages extends Record = {}, +export function createTranslationRef< + TMessages extends { + [key in string]: string; + }, + TId extends string = string, >( - config: TranslationRefConfig, -) => TranslationRef; + config: TranslationRefOptions, +): TranslationRef; // @alpha export interface PluginOptionsProviderProps { @@ -38,75 +41,45 @@ export interface PluginOptionsProviderProps { export const PluginProvider: (props: PluginOptionsProviderProps) => JSX.Element; // @alpha (undocumented) -export type TranslationOptions< - Messages extends Record = Record, -> = Messages; +export interface TranslationOptions {} // @alpha (undocumented) export interface TranslationRef< - Messages extends Record = Record, + TMessages extends { + [key in string]: string; + } = { + [key in string]: string; + }, + TId extends string = string, > { // (undocumented) - getDefaultMessages(): Messages; + $$type: '@backstage/TranslationRef'; // (undocumented) - getId(): string; + id: TId; // (undocumented) - getLazyResources(): - | Record< - string, - () => Promise<{ - messages: Messages; - }> - > - | undefined; - // (undocumented) - getResources(): Record | undefined; + T: TMessages; } // @alpha (undocumented) -export interface TranslationRefConfig< - Messages extends Record, +export interface TranslationRefOptions< + TMessages extends { + [key in string]: string; + }, + TId extends string = string, > { // (undocumented) - id: string; + id: TId; // (undocumented) lazyResources?: Record< string, () => Promise<{ - messages: Messages; + messages: TMessages; }> >; // (undocumented) - messages: Messages; + messages: TMessages; // (undocumented) - resources?: Record; -} - -// @alpha (undocumented) -export class TranslationRefImpl> - implements TranslationRef -{ - // (undocumented) - static create>( - config: TranslationRefConfig, - ): TranslationRefImpl; - // (undocumented) - getDefaultMessages(): Messages; - // (undocumented) - getId(): string; - // (undocumented) - getLazyResources(): - | Record< - string, - () => Promise<{ - messages: Messages; - }> - > - | undefined; - // (undocumented) - getResources(): Record | undefined; - // (undocumented) - toString(): string; + resources?: Record; } // @alpha @@ -116,13 +89,15 @@ export function usePluginOptions< // @alpha (undocumented) export const useTranslationRef: < - Messages extends Record, + TMessages extends { + [x: string]: string; + }, >( - translationRef: TranslationRef, -) => ( - key: Tkey, + translationRef: TranslationRef, +) => ( + key: TKey, options?: TranslationOptions, -) => Messages[Tkey]; +) => TMessages[TKey]; // (No @packageDocumentation comment for this package) ``` diff --git a/packages/core-plugin-api/src/apis/definitions/AppTranslationApi.ts b/packages/core-plugin-api/src/apis/definitions/AppTranslationApi.ts index 463aee02bf..b94e5add87 100644 --- a/packages/core-plugin-api/src/apis/definitions/AppTranslationApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AppTranslationApi.ts @@ -22,8 +22,8 @@ import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; export type AppTranslationApi = { getI18n(): i18n; - addResourcesByRef>( - translationRef: TranslationRef, + addResourcesByRef>( + translationRef: TranslationRef, ): void; }; @@ -31,5 +31,5 @@ export type AppTranslationApi = { * @alpha */ export const appTranslationApiRef: ApiRef = createApiRef({ - id: 'core.apptranslation', + id: 'core.translation', }); diff --git a/packages/core-plugin-api/src/translation/TranslationRef.test.ts b/packages/core-plugin-api/src/translation/TranslationRef.test.ts index 155624a122..41d5d9ab85 100644 --- a/packages/core-plugin-api/src/translation/TranslationRef.test.ts +++ b/packages/core-plugin-api/src/translation/TranslationRef.test.ts @@ -13,30 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { TranslationRefImpl, createTranslationRef } from './TranslationRef'; +import { + createTranslationRef, + toInternalTranslationRef, +} from './TranslationRef'; describe('TranslationRefImpl', () => { - it('should create a TranslationRef instance', () => { - const config = { - id: 'testId', - messages: { key: 'value' }, - }; - - const translationRef = TranslationRefImpl.create(config); - - expect(translationRef.getId()).toBe('testId'); - expect(translationRef.getDefaultMessages()).toEqual({ key: 'value' }); - }); - it('should create a TranslationRef instance using the factory function', () => { const config = { id: 'testId', messages: { key: 'value' }, }; - const translationRef = createTranslationRef(config); + const translationRef = toInternalTranslationRef( + createTranslationRef(config), + ); - expect(translationRef.getId()).toBe('testId'); + expect(translationRef.id).toBe('testId'); expect(translationRef.getDefaultMessages()).toEqual({ key: 'value' }); }); @@ -49,7 +42,9 @@ describe('TranslationRefImpl', () => { }, }; - const translationRef = TranslationRefImpl.create(config); + const translationRef = toInternalTranslationRef( + createTranslationRef(config), + ); const lazyResources = translationRef.getLazyResources(); diff --git a/packages/core-plugin-api/src/translation/TranslationRef.ts b/packages/core-plugin-api/src/translation/TranslationRef.ts index d01f7ca261..2a135fe825 100644 --- a/packages/core-plugin-api/src/translation/TranslationRef.ts +++ b/packages/core-plugin-api/src/translation/TranslationRef.ts @@ -14,48 +14,109 @@ * limitations under the License. */ -import { TranslationRef, TranslationRefConfig } from './types'; - /** @alpha */ -export class TranslationRefImpl> - implements TranslationRef -{ - static create>( - config: TranslationRefConfig, - ) { - return new TranslationRefImpl(config); - } +export interface TranslationRef< + TMessages extends { [key in string]: string } = { [key in string]: string }, + TId extends string = string, +> { + $$type: '@backstage/TranslationRef'; - getId() { - return this.config.id; - } + id: TId; - getDefaultMessages(): Messages { - return this.config.messages; - } + T: TMessages; +} + +/** @internal */ +type AnyMessages = { [key in string]: string }; + +/** @internal */ +export interface InternalTranslationRef< + TMessages extends { [key in string]: string } = { [key in string]: string }, + TId extends string = string, +> extends TranslationRef { + version: 'v1'; + + getDefaultMessages(): AnyMessages; + + getResources(): Record | undefined; getLazyResources(): - | Record Promise<{ messages: Messages }>> - | undefined { - return this.config.lazyResources; - } - - getResources(): Record | undefined { - return this.config.resources; - } - - toString() { - return `TranslationRef(${this.getId()})`; - } - - private constructor( - private readonly config: TranslationRefConfig, - ) {} + | Record Promise<{ messages: AnyMessages }>> + | undefined; } /** @alpha */ -export const createTranslationRef = < - Messages extends Record = {}, +export interface TranslationRefOptions< + TMessages extends { [key in string]: string }, + TId extends string = string, +> { + id: TId; + messages: TMessages; + lazyResources?: Record Promise<{ messages: TMessages }>>; + resources?: Record; +} + +/** @internal */ +class TranslationRefImpl< + TMessages extends { [key in string]: string }, + TId extends string = string, +> implements InternalTranslationRef +{ + constructor( + private readonly options: TranslationRefOptions, + ) {} + + $$type = '@backstage/TranslationRef' as const; + + version = 'v1' as const; + + get id(): TId { + return this.options.id; + } + + get T(): never { + throw new Error('Not implemented'); + } + + getDefaultMessages(): AnyMessages { + return this.options.messages; + } + + getLazyResources(): + | Record Promise<{ messages: AnyMessages }>> + | undefined { + return this.options.lazyResources; + } + + getResources(): Record | undefined { + return this.options.resources; + } + + toString() { + return `TranslationRef{id=${this.id}}`; + } +} + +/** @alpha */ +export function createTranslationRef< + TMessages extends { [key in string]: string }, + TId extends string = string, >( - config: TranslationRefConfig, -): TranslationRef => TranslationRefImpl.create(config); + config: TranslationRefOptions, +): TranslationRef { + return new TranslationRefImpl(config); +} + +/** @internal */ +export function toInternalTranslationRef( + ref: TranslationRef, +): InternalTranslationRef { + const r = ref as InternalTranslationRef; + if (r.$$type !== '@backstage/TranslationRef') { + throw new Error(`Invalid translation ref, bad type '${r.$$type}'`); + } + if (r.version !== 'v1') { + throw new Error(`Invalid translation ref, bad version '${r.version}'`); + } + return r; +} diff --git a/packages/core-plugin-api/src/translation/index.ts b/packages/core-plugin-api/src/translation/index.ts index 9eaef57206..6c97fb772e 100644 --- a/packages/core-plugin-api/src/translation/index.ts +++ b/packages/core-plugin-api/src/translation/index.ts @@ -14,6 +14,12 @@ * limitations under the License. */ -export * from './TranslationRef'; -export * from './types'; -export * from './useTranslationRef'; +export { + type TranslationRef, + type TranslationRefOptions, + createTranslationRef, +} from './TranslationRef'; +export { + type TranslationOptions, + useTranslationRef, +} from './useTranslationRef'; diff --git a/packages/core-plugin-api/src/translation/types.ts b/packages/core-plugin-api/src/translation/types.ts deleted file mode 100644 index 4811c82eb7..0000000000 --- a/packages/core-plugin-api/src/translation/types.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** @alpha */ -export interface TranslationRefConfig< - Messages extends Record, -> { - id: string; - messages: Messages; - lazyResources?: Record Promise<{ messages: Messages }>>; - resources?: Record; -} - -/** @alpha */ -export interface TranslationRef< - Messages extends Record = Record, -> { - getId(): string; - - getDefaultMessages(): Messages; - - getResources(): Record | undefined; - - getLazyResources(): - | Record Promise<{ messages: Messages }>> - | undefined; -} - -/** @alpha */ -export type TranslationOptions< - Messages extends Record = Record, -> = Messages; diff --git a/packages/core-plugin-api/src/translation/useTranslationRef.ts b/packages/core-plugin-api/src/translation/useTranslationRef.ts index 083bcd1f0e..f88cba36a4 100644 --- a/packages/core-plugin-api/src/translation/useTranslationRef.ts +++ b/packages/core-plugin-api/src/translation/useTranslationRef.ts @@ -16,26 +16,33 @@ import { useTranslation } from 'react-i18next'; -import { TranslationOptions, TranslationRef } from './types'; import { useApi } from '../apis'; import { appTranslationApiRef } from '../apis/alpha'; +import { toInternalTranslationRef, TranslationRef } from './TranslationRef'; + +/** @alpha */ +export interface TranslationOptions { + /* no options supported for now */ +} /** @alpha */ export const useTranslationRef = < - Messages extends Record, + TMessages extends { [key in string]: string }, >( - translationRef: TranslationRef, + translationRef: TranslationRef, ) => { const appTranslationApi = useApi(appTranslationApiRef); appTranslationApi.addResourcesByRef(translationRef); - const { t } = useTranslation(translationRef.getId()); + const internalRef = toInternalTranslationRef(translationRef); - const defaulteMessage = translationRef.getDefaultMessages(); + const { t } = useTranslation(internalRef.id); - return ( - key: Tkey, + const defaultMessages = internalRef.getDefaultMessages(); + + return ( + key: TKey, options?: TranslationOptions, - ): Messages[Tkey] => t(key as string, defaulteMessage[key], options); + ): TMessages[TKey] => t(key, defaultMessages[key], options); };