diff --git a/.changeset/fuzzy-shrimps-refuse.md b/.changeset/fuzzy-shrimps-refuse.md new file mode 100644 index 0000000000..c2814da631 --- /dev/null +++ b/.changeset/fuzzy-shrimps-refuse.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Implemented support for the `internal` extension input option. diff --git a/.changeset/many-bags-brake.md b/.changeset/many-bags-brake.md new file mode 100644 index 0000000000..91b48d63f4 --- /dev/null +++ b/.changeset/many-bags-brake.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added a new `internal` option to `createExtensionInput` that marks the input as only allowing attachments from the same plugin. diff --git a/.changeset/quiet-humans-hammer.md b/.changeset/quiet-humans-hammer.md new file mode 100644 index 0000000000..1878473ff0 --- /dev/null +++ b/.changeset/quiet-humans-hammer.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-app': patch +--- + +The deprecation of the following blueprints listed below is not complete. These blueprints must now be provided via an override or a module for the `app` plugin. Extensions from other plugins will now trigger a warning in the app and be ignored. + +- `IconBundleBlueprint` +- `NavContentBlueprint` +- `RouterBlueprint` +- `SignInPageBlueprint` +- `SwappableComponentBlueprint` +- `ThemeBlueprint` +- `TranslationBlueprint` diff --git a/packages/frontend-app-api/report.api.md b/packages/frontend-app-api/report.api.md index 06b5e55278..e8fc019007 100644 --- a/packages/frontend-app-api/report.api.md +++ b/packages/frontend-app-api/report.api.md @@ -59,6 +59,14 @@ export type AppErrorTypes = { inputName: string; }; }; + EXTENSION_INPUT_INTERNAL_IGNORED: { + context: { + node: AppNode; + inputName: string; + extensionId: string; + plugin: FrontendPlugin; + }; + }; EXTENSION_ATTACHMENT_CONFLICT: { context: { node: AppNode; diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index a07fdf352d..8156ae7a12 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -142,6 +142,36 @@ function createV1Extension(opts: { return ext; } +function mirrorInputs(ctx: { + inputs: { + [name in string]: + | undefined + | ResolvedExtensionInput + | Array>; + }; +}) { + return [ + inputMirrorDataRef( + Object.fromEntries( + Object.entries(ctx.inputs).map(([k, v]) => [ + k, + Array.isArray(v) + ? v.map(vi => ({ + node: vi.node, + test: vi.get(testDataRef), + other: vi.get(otherDataRef), + })) + : { + node: v?.node, + test: v?.get(testDataRef), + other: v?.get(otherDataRef), + }, + ]), + ), + ), + ]; +} + describe('instantiateAppNodeTree', () => { describe('v1', () => { const simpleExtension = createV1Extension({ @@ -237,6 +267,60 @@ describe('instantiateAppNodeTree', () => { expect(childNode?.instance).toBeDefined(); }); + it('should ignore non-matching plugin attachments for internal inputs', () => { + const otherPlugin = createFrontendPlugin({ pluginId: 'other' }); + const tree = resolveAppTree( + 'root-node', + [ + makeSpec( + resolveExtensionDefinition( + createExtension({ + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput([testDataRef], { + singleton: true, + internal: true, + }), + }, + output: [inputMirrorDataRef], + factory: mirrorInputs, + }), + { namespace: 'root-node' }, + ), + ), + makeSpec(simpleExtension, { + id: 'child-node-app', + attachTo: { id: 'root-node', input: 'test' }, + }), + makeSpec(simpleExtension, { + id: 'child-node-other', + attachTo: { id: 'root-node', input: 'test' }, + plugin: otherPlugin, + }), + ], + collector, + ); + + instantiateAppNodeTree(tree.root, testApis, collector); + + expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({ + test: { node: { spec: { id: 'child-node-app' } }, test: 'test' }, + }); + expect(collector.collectErrors()).toEqual([ + { + code: 'EXTENSION_INPUT_INTERNAL_IGNORED', + message: + "extension 'child-node-other' from plugin 'other' attached to input 'test' on 'root-node' was ignored, the input is marked as internal and attached extensions must therefore be provided via an override or a module for the 'app' plugin, not the 'other' plugin", + context: { + node: tree.root, + inputName: 'test', + extensionId: 'child-node-other', + plugin: otherPlugin, + }, + }, + ]); + }); + it('should not instantiate disabled attachments', () => { const tree = resolveAppTree( 'root-node', @@ -783,36 +867,6 @@ describe('instantiateAppNodeTree', () => { { namespace: 'app' }, ); - function mirrorInputs(ctx: { - inputs: { - [name in string]: - | undefined - | ResolvedExtensionInput - | Array>; - }; - }) { - return [ - inputMirrorDataRef( - Object.fromEntries( - Object.entries(ctx.inputs).map(([k, v]) => [ - k, - Array.isArray(v) - ? v.map(vi => ({ - node: vi.node, - test: vi.get(testDataRef), - other: vi.get(otherDataRef), - })) - : { - node: v?.node, - test: v?.get(testDataRef), - other: v?.get(otherDataRef), - }, - ]), - ), - ), - ]; - } - it('should instantiate a single node', () => { const tree = resolveAppTree( 'root-node', diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 04082f8520..85638f316d 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -246,10 +246,32 @@ function resolveV2Inputs( inputMap: { [inputName in string]: ExtensionInput }, attachments: ReadonlyMap, parentCollector: ErrorCollector<{ node: AppNode }>, + node: AppNode, ): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> { return mapValues(inputMap, (input, inputName) => { - const attachedNodes = attachments.get(inputName) ?? []; + const allAttachedNodes = attachments.get(inputName) ?? []; const collector = parentCollector.child({ inputName }); + const inputPluginId = node.spec.plugin.id; + + const attachedNodes = input.config.internal + ? allAttachedNodes.filter(attachment => { + const attachmentPluginId = attachment.spec.plugin.id; + if (attachmentPluginId !== inputPluginId) { + collector.report({ + code: 'EXTENSION_INPUT_INTERNAL_IGNORED', + message: + `extension '${attachment.spec.id}' from plugin '${attachmentPluginId}' attached to input '${inputName}' on '${node.spec.id}' was ignored, ` + + `the input is marked as internal and attached extensions must therefore be provided via an override or a module for the '${inputPluginId}' plugin, not the '${attachmentPluginId}' plugin`, + context: { + extensionId: attachment.spec.id, + plugin: attachment.spec.plugin, + }, + }); + return false; + } + return true; + }) + : allAttachedNodes; if (input.config.singleton) { if (attachedNodes.length > 1) { @@ -354,6 +376,7 @@ export function createAppNodeInstance(options: { internalExtension.inputs, attachments, collector, + node, ), }; const outputDataValues = options.extensionFactoryMiddleware diff --git a/packages/frontend-app-api/src/wiring/createErrorCollector.ts b/packages/frontend-app-api/src/wiring/createErrorCollector.ts index dab69ba19e..5f081bc092 100644 --- a/packages/frontend-app-api/src/wiring/createErrorCollector.ts +++ b/packages/frontend-app-api/src/wiring/createErrorCollector.ts @@ -38,6 +38,14 @@ export type AppErrorTypes = { EXTENSION_INPUT_DATA_MISSING: { context: { node: AppNode; inputName: string }; }; + EXTENSION_INPUT_INTERNAL_IGNORED: { + context: { + node: AppNode; + inputName: string; + extensionId: string; + plugin: FrontendPlugin; + }; + }; EXTENSION_ATTACHMENT_CONFLICT: { context: { node: AppNode; inputName: string }; }; diff --git a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx index 6bad97f666..c0fb807deb 100644 --- a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx +++ b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx @@ -22,6 +22,7 @@ const DEFAULT_WARNING_CODES: Array = [ 'EXTENSION_IGNORED', 'INVALID_EXTENSION_CONFIG_KEY', 'EXTENSION_INPUT_DATA_IGNORED', + 'EXTENSION_INPUT_INTERNAL_IGNORED', 'EXTENSION_OUTPUT_IGNORED', ]; diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 1a7f2e3c7a..3d5efd5765 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -604,7 +604,7 @@ export function createExtensionDataRef(): { }): ConfigurableExtensionDataRef; }; -// @public (undocumented) +// @public export function createExtensionInput< UExtensionData extends ExtensionDataRef< unknown, @@ -616,6 +616,7 @@ export function createExtensionInput< TConfig extends { singleton?: boolean; optional?: boolean; + internal?: boolean; }, >( extensionData: Array, @@ -630,6 +631,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } >; @@ -1288,9 +1290,11 @@ export interface ExtensionInput< TConfig extends { singleton: boolean; optional: boolean; + internal?: boolean; } = { singleton: boolean; optional: boolean; + internal?: boolean; }, > { // (undocumented) diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts index 279878542a..9b32ca947f 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts @@ -29,28 +29,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: false, optional: false }, + config: { singleton: false, optional: false, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -64,7 +64,7 @@ describe('createExtensionInput', () => { expect(inputWithContext).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: false, optional: false }, + config: { singleton: false, optional: false, internal: false }, withContext: expect.any(Function), context, }); @@ -77,28 +77,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: true, optional: false }, + config: { singleton: true, optional: false, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -112,28 +112,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: true, optional: true }, + config: { singleton: true, optional: true, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: true } + { singleton: true; optional: true; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: true; optional: true } + { singleton: true; optional: true; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -144,4 +144,14 @@ describe('createExtensionInput', () => { createExtensionInput([stringDataRef, stringDataRef], { singleton: true }), ).toThrow("ExtensionInput may not have duplicate data refs: 'str'"); }); + + it('should create an internal input', () => { + const input = createExtensionInput([stringDataRef], { internal: true }); + expect(input).toEqual({ + $$type: '@backstage/ExtensionInput', + extensionData: [stringDataRef], + config: { singleton: false, optional: false, internal: true }, + withContext: expect.any(Function), + }); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index 57040c8adf..b4fe9983b9 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -27,9 +27,14 @@ export interface ExtensionInput< string, { optional?: true } > = ExtensionDataRef, - TConfig extends { singleton: boolean; optional: boolean } = { + TConfig extends { singleton: boolean; optional: boolean; + internal?: boolean; + } = { + singleton: boolean; + optional: boolean; + internal?: boolean; }, > { readonly $$type: '@backstage/ExtensionInput'; @@ -38,10 +43,60 @@ export interface ExtensionInput< readonly replaces?: Array<{ id: string; input: string }>; } -/** @public */ +/** + * Creates a new extension input to be passed to the input map of an extension. + * + * @remarks + * + * Extension inputs created with this function can be passed to any `inputs` map + * as part of creating or overriding an extension. + * + * The array of extension data references defines the data this input expects. + * If the required data is not provided by the attached extension, the + * attachment will fail. + * + * The `config` object can be used to restrict the behavior and shape of the + * input. By default an input will accept zero or more extensions from any + * plugin. The following options are available: + * + * - `singleton`: If set to `true`, only one extension can be attached to the + * input at a time. Additional extension will trigger an app error and be + * ignored. + * - `optional`: If set to `true`, the input is optional and can be omitted, + * this only has an effect if the `singleton` is set to `true`. + * - `internal`: If set to `true`, only extensions from the same plugins will be + * allowed to attach to this input. Other extensions will trigger an app error + * and be ignored. + * + * @param extensionData - The array of extension data references that this input + * expects. + * @param config - The configuration object for the input. + * @returns An extension input declaration. + * @example + * ```ts + * const extension = createExtension({ + * attachTo: { id: 'example-parent', input: 'example-input' }, + * inputs: { + * content: createExtensionInput([coreExtensionData.reactElement], { + * singleton: true, + * }), + * }, + * output: [coreExtensionData.reactElement], + * *factory({ inputs }) { + * const content = inputs.content?.get(coreExtensionData.reactElement); + * yield coreExtensionData.reactElement({content}); + * }, + * }); + * ``` + * @public + */ export function createExtensionInput< UExtensionData extends ExtensionDataRef, - TConfig extends { singleton?: boolean; optional?: boolean }, + TConfig extends { + singleton?: boolean; + optional?: boolean; + internal?: boolean; + }, >( extensionData: Array, config?: TConfig & { replaces?: Array<{ id: string; input: string }> }, @@ -50,6 +105,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } > { if (process.env.NODE_ENV !== 'production') { @@ -81,6 +137,9 @@ export function createExtensionInput< optional: Boolean(config?.optional) as TConfig['optional'] extends true ? true : false, + internal: Boolean(config?.internal) as TConfig['internal'] extends true + ? true + : false, }, replaces: config?.replaces, }; @@ -90,6 +149,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } > { return OpaqueExtensionInput.createInstance(undefined, { diff --git a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts index b46bb426fa..b9f239842d 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts @@ -37,6 +37,7 @@ export type ResolvedInputValueOverrides< { optional: infer IOptional extends boolean; singleton: boolean; + internal?: boolean; } > ? IOptional extends true @@ -44,7 +45,11 @@ export type ResolvedInputValueOverrides< : KName : never]: TInputs[KName] extends ExtensionInput< infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } + { + optional: boolean; + singleton: infer ISingleton extends boolean; + internal?: boolean; + } > ? ISingleton extends true ? Iterable> @@ -56,6 +61,7 @@ export type ResolvedInputValueOverrides< { optional: infer IOptional extends boolean; singleton: boolean; + internal?: boolean; } > ? IOptional extends true @@ -63,7 +69,11 @@ export type ResolvedInputValueOverrides< : never : never]?: TInputs[KName] extends ExtensionInput< infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } + { + optional: boolean; + singleton: infer ISingleton extends boolean; + internal?: boolean; + } > ? ISingleton extends true ? Iterable> diff --git a/plugins/app/report.api.md b/plugins/app/report.api.md index c5ee77ef01..a966ddeff1 100644 --- a/plugins/app/report.api.md +++ b/plugins/app/report.api.md @@ -40,6 +40,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; }; @@ -57,6 +58,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; content: ExtensionInput< @@ -64,6 +66,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; }; @@ -89,6 +92,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; content: ExtensionInput< @@ -100,6 +104,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; }; @@ -121,6 +126,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; signInPage: ExtensionInput< @@ -132,6 +138,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; children: ExtensionInput< @@ -139,6 +146,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; elements: ExtensionInput< @@ -146,6 +154,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; wrappers: ExtensionInput< @@ -157,6 +166,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -182,6 +192,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -218,6 +229,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -262,6 +274,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -471,6 +484,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -592,6 +606,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -669,6 +684,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -703,6 +719,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; diff --git a/plugins/app/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx index 8e76a16781..bba27d1414 100644 --- a/plugins/app/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -90,18 +90,11 @@ export const AppNav = createExtension({ content: createExtensionInput([NavContentBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), }, output: [coreExtensionData.reactElement], *factory({ inputs }) { - if (inputs.content && inputs.content.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: NavContent should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.content.node.spec.id}`, - ); - } - const Content = inputs.content?.get(NavContentBlueprint.dataRefs.component) ?? DefaultNavContent; diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index c9f3035e5f..43e10a2288 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -59,37 +59,26 @@ export const AppRoot = createExtension({ router: createExtensionInput([RouterBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), signInPage: createExtensionInput([SignInPageBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), children: createExtensionInput([coreExtensionData.reactElement], { singleton: true, }), elements: createExtensionInput([coreExtensionData.reactElement]), - wrappers: createExtensionInput([ - AppRootWrapperBlueprint.dataRefs.component, - ]), + wrappers: createExtensionInput( + [AppRootWrapperBlueprint.dataRefs.component], + { + internal: true, + }, + ), }, output: [coreExtensionData.reactElement], factory({ inputs, apis }) { - if (inputs.router && inputs.router.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Router should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.router.node.spec.id}`, - ); - } - - if (inputs.signInPage && inputs.signInPage.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: SignInPage should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.signInPage.node.spec.id}`, - ); - } - if (isProtectedApp()) { const identityApi = apis.get(identityApiRef); if (!identityApi) { @@ -117,16 +106,8 @@ export const AppRoot = createExtension({ for (const wrapper of inputs.wrappers) { const Component = wrapper.get(AppRootWrapperBlueprint.dataRefs.component); - const pluginId = wrapper.node.spec.plugin.id; if (Component) { content = {content}; - if (pluginId !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: AppRootWrappers should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${wrapper.node.spec.id}`, - ); - } } } diff --git a/plugins/app/src/extensions/AppThemeApi.tsx b/plugins/app/src/extensions/AppThemeApi.tsx index 2b265ddb53..cf46cab09e 100644 --- a/plugins/app/src/extensions/AppThemeApi.tsx +++ b/plugins/app/src/extensions/AppThemeApi.tsx @@ -37,6 +37,7 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({ inputs: { themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { replaces: [{ id: 'app', input: 'themes' }], + internal: true, }), }, factory: (originalFactory, { inputs }) => { @@ -45,19 +46,6 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({ api: appThemeApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.themes.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Theme should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return AppThemeSelector.createWithStorage( inputs.themes.map(i => i.get(ThemeBlueprint.dataRefs.theme)), ); diff --git a/plugins/app/src/extensions/IconsApi.ts b/plugins/app/src/extensions/IconsApi.ts index f10e927161..9be4a3e79a 100644 --- a/plugins/app/src/extensions/IconsApi.ts +++ b/plugins/app/src/extensions/IconsApi.ts @@ -33,6 +33,7 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({ inputs: { icons: createExtensionInput([IconBundleBlueprint.dataRefs.icons], { replaces: [{ id: 'app', input: 'icons' }], + internal: true, }), }, factory: (originalFactory, { inputs }) => { @@ -41,19 +42,6 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({ api: iconsApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.icons.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: IconBundle should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return new DefaultIconsApi( inputs.icons .map(i => i.get(IconBundleBlueprint.dataRefs.icons)) diff --git a/plugins/app/src/extensions/SwappableComponentsApi.ts b/plugins/app/src/extensions/SwappableComponentsApi.ts index f261d999c0..d72b52a332 100644 --- a/plugins/app/src/extensions/SwappableComponentsApi.ts +++ b/plugins/app/src/extensions/SwappableComponentsApi.ts @@ -28,9 +28,12 @@ import { DefaultSwappableComponentsApi } from '../apis/SwappableComponentsApi'; export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({ name: 'swappable-components', inputs: { - components: createExtensionInput([ - SwappableComponentBlueprint.dataRefs.component, - ]), + components: createExtensionInput( + [SwappableComponentBlueprint.dataRefs.component], + { + internal: true, + }, + ), }, factory: (originalFactory, { inputs }) => { return originalFactory(defineParams => @@ -38,25 +41,8 @@ export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({ api: swappableComponentsApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.components.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - // eslint-disable-next-line no-console - console.warn( - `SwappableComponents should only be installed as an extension in the app plugin. You can either use appPlugin.override(), or provide a module for the app-plugin with the extension there instead. Invalid extensions: ${nonAppExtensions - .map(i => i.node.spec.id) - .join(', ')}`, - ); - } - - const appExtensions = inputs.components.filter( - i => i.node.spec.plugin?.id === 'app', - ); - return DefaultSwappableComponentsApi.fromComponents( - appExtensions.map(i => + inputs.components.map(i => i.get(SwappableComponentBlueprint.dataRefs.component), ), ); diff --git a/plugins/app/src/extensions/TranslationsApi.tsx b/plugins/app/src/extensions/TranslationsApi.tsx index 2a6017af4b..8421dba9d9 100644 --- a/plugins/app/src/extensions/TranslationsApi.tsx +++ b/plugins/app/src/extensions/TranslationsApi.tsx @@ -34,7 +34,7 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({ inputs: { translations: createExtensionInput( [TranslationBlueprint.dataRefs.translation], - { replaces: [{ id: 'app', input: 'translations' }] }, + { replaces: [{ id: 'app', input: 'translations' }], internal: true }, ), }, factory: (originalFactory, { inputs }) => { @@ -43,19 +43,6 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({ api: translationApiRef, deps: { languageApi: appLanguageApiRef }, factory: ({ languageApi }) => { - const nonAppExtensions = inputs.translations.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Translations should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return I18nextTranslationApi.create({ languageApi, resources: inputs.translations.map(i => diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index f70572e371..e83089b611 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -345,6 +345,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -794,6 +795,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; cards: ExtensionInput< @@ -822,6 +824,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -1000,6 +1003,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -1064,6 +1068,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; contents: ExtensionInput< @@ -1105,6 +1110,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; contextMenuItems: ExtensionInput< @@ -1119,6 +1125,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 3f78a90587..8f3e24f7f3 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -92,6 +92,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: true; + internal: false; } >; }; diff --git a/plugins/home/report-alpha.api.md b/plugins/home/report-alpha.api.md index 6344805372..99e9b07463 100644 --- a/plugins/home/report-alpha.api.md +++ b/plugins/home/report-alpha.api.md @@ -85,6 +85,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; }; diff --git a/plugins/scaffolder-react/report-alpha.api.md b/plugins/scaffolder-react/report-alpha.api.md index 2839851521..1befe9ca46 100644 --- a/plugins/scaffolder-react/report-alpha.api.md +++ b/plugins/scaffolder-react/report-alpha.api.md @@ -219,6 +219,7 @@ export const formFieldsApi: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index 7ffe367327..b9aa82782f 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -91,6 +91,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -118,6 +119,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -218,6 +220,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -411,6 +414,7 @@ export const formDecoratorsApi: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/search/report-alpha.api.md b/plugins/search/report-alpha.api.md index b129d4ab6c..2e82c77416 100644 --- a/plugins/search/report-alpha.api.md +++ b/plugins/search/report-alpha.api.md @@ -97,6 +97,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; resultTypes: ExtensionInput< @@ -112,6 +113,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; searchFilters: ExtensionInput< @@ -125,6 +127,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -215,6 +218,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; resultTypes: ExtensionInput< @@ -230,6 +234,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; searchFilters: ExtensionInput< @@ -243,6 +248,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/techdocs/report-alpha.api.md b/plugins/techdocs/report-alpha.api.md index 96c778b09b..cbdb912938 100644 --- a/plugins/techdocs/report-alpha.api.md +++ b/plugins/techdocs/report-alpha.api.md @@ -84,6 +84,7 @@ const _default: OverridableFrontendPlugin< { singleton: boolean; optional: boolean; + internal?: boolean; } >; }; @@ -146,6 +147,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; emptyState: ExtensionInput< @@ -159,6 +161,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; }; @@ -290,6 +293,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/user-settings/report-alpha.api.md b/plugins/user-settings/report-alpha.api.md index 73ef5cfa3d..7ff63e2403 100644 --- a/plugins/user-settings/report-alpha.api.md +++ b/plugins/user-settings/report-alpha.api.md @@ -66,6 +66,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; };