diff --git a/.changeset/cold-lemons-design.md b/.changeset/cold-lemons-design.md
new file mode 100644
index 0000000000..9bdadac9c7
--- /dev/null
+++ b/.changeset/cold-lemons-design.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-app': patch
+---
+
+Log a warning when `SwappableComponent` extensions are installed outside of using the `app` plugin
diff --git a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx
index 823d48e21c..07ef891926 100644
--- a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx
+++ b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx
@@ -17,6 +17,7 @@
import {
ApiBlueprint,
createExtensionInput,
+ createFrontendModule,
createSwappableComponent,
SwappableComponentBlueprint,
swappableComponentsApiRef,
@@ -202,7 +203,13 @@ describe('DefaultSwappableComponentsApi', () => {
});
renderInTestApp(, {
- extensions: [api, override],
+ extensions: [api],
+ features: [
+ createFrontendModule({
+ pluginId: 'app',
+ extensions: [override],
+ }),
+ ],
});
await expect(
@@ -225,7 +232,13 @@ describe('DefaultSwappableComponentsApi', () => {
});
renderInTestApp(, {
- extensions: [api, override],
+ extensions: [api],
+ features: [
+ createFrontendModule({
+ pluginId: 'app',
+ extensions: [override],
+ }),
+ ],
});
await expect(
@@ -252,7 +265,13 @@ describe('DefaultSwappableComponentsApi', () => {
});
renderInTestApp(, {
- extensions: [api, override],
+ extensions: [api],
+ features: [
+ createFrontendModule({
+ pluginId: 'app',
+ extensions: [override],
+ }),
+ ],
});
await expect(
diff --git a/plugins/app/src/extensions/SwappableComponentsApi.ts b/plugins/app/src/extensions/SwappableComponentsApi.ts
index ea57a6f487..f261d999c0 100644
--- a/plugins/app/src/extensions/SwappableComponentsApi.ts
+++ b/plugins/app/src/extensions/SwappableComponentsApi.ts
@@ -37,12 +37,30 @@ export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({
defineParams({
api: swappableComponentsApiRef,
deps: {},
- factory: () =>
- DefaultSwappableComponentsApi.fromComponents(
- inputs.components.map(i =>
+ 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 =>
i.get(SwappableComponentBlueprint.dataRefs.component),
),
- ),
+ );
+ },
}),
);
},