From e41c80d8a0c1184a20e0e9314bc6c6108954cb1a Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Mon, 10 Mar 2025 12:38:56 +0100 Subject: [PATCH] Clean up API surface Signed-off-by: Eric Peterson --- packages/frontend-defaults/report.api.md | 10 ++++-- packages/frontend-defaults/src/createApp.tsx | 8 ++--- .../frontend-defaults/src/discovery.test.ts | 34 +++++++++--------- packages/frontend-defaults/src/discovery.ts | 35 ++++++++++--------- packages/frontend-defaults/src/index.ts | 4 +-- ...lveFeatures.test.ts => resolution.test.ts} | 12 +++---- .../src/{resolveFeatures.ts => resolution.ts} | 6 ++-- 7 files changed, 58 insertions(+), 51 deletions(-) rename packages/frontend-defaults/src/{resolveFeatures.test.ts => resolution.test.ts} (92%) rename packages/frontend-defaults/src/{resolveFeatures.ts => resolution.ts} (91%) diff --git a/packages/frontend-defaults/report.api.md b/packages/frontend-defaults/report.api.md index a65d1a5d1f..5d19d9bc98 100644 --- a/packages/frontend-defaults/report.api.md +++ b/packages/frontend-defaults/report.api.md @@ -46,11 +46,15 @@ export function createPublicSignInApp(options?: CreateAppOptions): { }; // @public (undocumented) -export function getAvailableFeatures(config: Config): FrontendFeature[]; +export function discoverAvailableFeatures(config: Config): { + features: FrontendFeature[]; +}; // @public (undocumented) -export function resolveFeatures(options: { +export function resolveAsyncFeatures(options: { config: Config; features?: (FrontendFeature | CreateAppFeatureLoader)[]; -}): Promise; +}): Promise<{ + features: FrontendFeature[]; +}>; ``` diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx index 70c308518e..d63f254a49 100644 --- a/packages/frontend-defaults/src/createApp.tsx +++ b/packages/frontend-defaults/src/createApp.tsx @@ -28,8 +28,8 @@ import { createSpecializedApp, } from '@backstage/frontend-app-api'; import appPlugin from '@backstage/plugin-app'; -import { getAvailableFeatures } from './discovery'; -import { resolveFeatures } from './resolveFeatures'; +import { discoverAvailableFeatures } from './discovery'; +import { resolveAsyncFeatures } from './resolution'; /** * A source of dynamically loaded frontend features. @@ -89,8 +89,8 @@ export function createApp(options?: CreateAppOptions): { overrideBaseUrlConfigs(defaultConfigLoaderSync()), ); - const discoveredFeatures = getAvailableFeatures(config); - const providedFeatures = await resolveFeatures({ + const { features: discoveredFeatures } = discoverAvailableFeatures(config); + const { features: providedFeatures } = await resolveAsyncFeatures({ config, features: options?.features, }); diff --git a/packages/frontend-defaults/src/discovery.test.ts b/packages/frontend-defaults/src/discovery.test.ts index 8570ca1bc7..80be0b25c5 100644 --- a/packages/frontend-defaults/src/discovery.test.ts +++ b/packages/frontend-defaults/src/discovery.test.ts @@ -15,7 +15,7 @@ */ import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; -import { getAvailableFeatures } from './discovery'; +import { discoverAvailableFeatures } from './discovery'; import { ConfigReader } from '@backstage/config'; const globalSpy = jest.fn(); @@ -27,18 +27,18 @@ const config = new ConfigReader({ app: { experimental: { packages: 'all' } }, }); -describe('getAvailableFeatures', () => { +describe('discoverAvailableFeatures', () => { afterEach(jest.resetAllMocks); it('should discover nothing with undefined global', () => { - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); }); it('should discover nothing with empty global', () => { globalSpy.mockReturnValue({ modules: [], }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); }); it('should discover a plugin', () => { @@ -46,24 +46,26 @@ describe('getAvailableFeatures', () => { globalSpy.mockReturnValue({ modules: [{ default: testPlugin }], }); - expect(getAvailableFeatures(config)).toEqual([testPlugin]); + expect(discoverAvailableFeatures(config)).toEqual({ + features: [testPlugin], + }); }); it('should ignore garbage', () => { globalSpy.mockReturnValueOnce({ modules: [{ default: null }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: undefined }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: Symbol() }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: () => {} }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: 0 }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: false }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); globalSpy.mockReturnValueOnce({ modules: [{ default: true }] }); - expect(getAvailableFeatures(config)).toEqual([]); + expect(discoverAvailableFeatures(config)).toEqual({ features: [] }); }); it('should discover multiple plugins', () => { @@ -77,10 +79,8 @@ describe('getAvailableFeatures', () => { { default: test3Plugin }, ], }); - expect(getAvailableFeatures(config)).toEqual([ - test1Plugin, - test2Plugin, - test3Plugin, - ]); + expect(discoverAvailableFeatures(config)).toEqual({ + features: [test1Plugin, test2Plugin, test3Plugin], + }); }); }); diff --git a/packages/frontend-defaults/src/discovery.ts b/packages/frontend-defaults/src/discovery.ts index 6f9d2e2d16..e82f2acfda 100644 --- a/packages/frontend-defaults/src/discovery.ts +++ b/packages/frontend-defaults/src/discovery.ts @@ -55,30 +55,33 @@ function readPackageDetectionConfig(config: Config) { /** * @public */ -export function getAvailableFeatures(config: Config): FrontendFeature[] { +export function discoverAvailableFeatures(config: Config): { + features: FrontendFeature[]; +} { const discovered = ( window as { '__@backstage/discovered__'?: DiscoveryGlobal } )['__@backstage/discovered__']; const detection = readPackageDetectionConfig(config); if (!detection) { - return []; + return { features: [] }; } - return ( - discovered?.modules - .filter(({ name }) => { - if (detection.exclude?.includes(name)) { - return false; - } - if (detection.include && !detection.include.includes(name)) { - return false; - } - return true; - }) - .map(m => m.default) - .filter(isBackstageFeature) ?? [] - ); + return { + features: + discovered?.modules + .filter(({ name }) => { + if (detection.exclude?.includes(name)) { + return false; + } + if (detection.include && !detection.include.includes(name)) { + return false; + } + return true; + }) + .map(m => m.default) + .filter(isBackstageFeature) ?? [], + }; } function isBackstageFeature(obj: unknown): obj is FrontendFeature { diff --git a/packages/frontend-defaults/src/index.ts b/packages/frontend-defaults/src/index.ts index c65e053dd5..573cc9b08c 100644 --- a/packages/frontend-defaults/src/index.ts +++ b/packages/frontend-defaults/src/index.ts @@ -26,5 +26,5 @@ export { type CreateAppFeatureLoader, } from './createApp'; export { createPublicSignInApp } from './createPublicSignInApp'; -export { getAvailableFeatures } from './discovery'; -export { resolveFeatures } from './resolveFeatures'; +export { discoverAvailableFeatures } from './discovery'; +export { resolveAsyncFeatures } from './resolution'; diff --git a/packages/frontend-defaults/src/resolveFeatures.test.ts b/packages/frontend-defaults/src/resolution.test.ts similarity index 92% rename from packages/frontend-defaults/src/resolveFeatures.test.ts rename to packages/frontend-defaults/src/resolution.test.ts index b91e6710b6..d833716666 100644 --- a/packages/frontend-defaults/src/resolveFeatures.test.ts +++ b/packages/frontend-defaults/src/resolution.test.ts @@ -19,12 +19,12 @@ import { PageBlueprint, } from '@backstage/frontend-plugin-api'; import { CreateAppFeatureLoader } from './createApp'; -import { resolveFeatures } from './resolveFeatures'; +import { resolveAsyncFeatures } from './resolution'; import { mockApis } from '@backstage/test-utils'; -describe('resolveFeatures', () => { +describe('resolveAsyncFeatures', () => { it('returns empty array when no features are provided', async () => { - const features = await resolveFeatures({ + const { features } = await resolveAsyncFeatures({ config: mockApis.config(), }); @@ -32,7 +32,7 @@ describe('resolveFeatures', () => { }); it('returns expected array when features are directly provided', async () => { - const features = await resolveFeatures({ + const { features } = await resolveAsyncFeatures({ config: mockApis.config(), features: [ createFrontendPlugin({ @@ -93,7 +93,7 @@ describe('resolveFeatures', () => { }, }; - const features = await resolveFeatures({ + const { features } = await resolveAsyncFeatures({ config: mockApis.config(), features: [loader], }); @@ -129,7 +129,7 @@ describe('resolveFeatures', () => { }; await expect(() => - resolveFeatures({ + resolveAsyncFeatures({ config: mockApis.config(), features: [loader], }), diff --git a/packages/frontend-defaults/src/resolveFeatures.ts b/packages/frontend-defaults/src/resolution.ts similarity index 91% rename from packages/frontend-defaults/src/resolveFeatures.ts rename to packages/frontend-defaults/src/resolution.ts index dddf0d2a1b..e51ecbc2d3 100644 --- a/packages/frontend-defaults/src/resolveFeatures.ts +++ b/packages/frontend-defaults/src/resolution.ts @@ -20,10 +20,10 @@ import { FrontendFeature } from '@backstage/frontend-app-api'; import { CreateAppFeatureLoader } from './createApp'; /** @public */ -export async function resolveFeatures(options: { +export async function resolveAsyncFeatures(options: { config: Config; features?: (FrontendFeature | CreateAppFeatureLoader)[]; -}): Promise { +}): Promise<{ features: FrontendFeature[] }> { const features = []; for (const entry of options.features ?? []) { if ('load' in entry) { @@ -41,5 +41,5 @@ export async function resolveFeatures(options: { features.push(entry); } } - return features; + return { features }; }