@backstage/frontend-plugin-api 0.13.2 → 0.14.0-next.0

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/alpha.d.ts +71 -0
  3. package/dist/alpha.esm.js +3 -0
  4. package/dist/alpha.esm.js.map +1 -0
  5. package/dist/apis/definitions/PluginWrapperApi.esm.js +8 -0
  6. package/dist/apis/definitions/PluginWrapperApi.esm.js.map +1 -0
  7. package/dist/apis/system/ApiRef.esm.js.map +1 -1
  8. package/dist/blueprints/PluginWrapperBlueprint.esm.js +24 -0
  9. package/dist/blueprints/PluginWrapperBlueprint.esm.js.map +1 -0
  10. package/dist/components/ExtensionBoundary.esm.js +18 -2
  11. package/dist/components/ExtensionBoundary.esm.js.map +1 -1
  12. package/dist/index.d.ts +81 -234
  13. package/dist/index.esm.js +0 -8
  14. package/dist/index.esm.js.map +1 -1
  15. package/dist/wiring/createExtension.esm.js.map +1 -1
  16. package/dist/wiring/createExtensionInput.esm.js +2 -1
  17. package/dist/wiring/createExtensionInput.esm.js.map +1 -1
  18. package/dist/wiring/createFrontendPlugin.esm.js +1 -0
  19. package/dist/wiring/createFrontendPlugin.esm.js.map +1 -1
  20. package/dist/wiring/resolveInputOverrides.esm.js.map +1 -1
  21. package/package.json +19 -11
  22. package/dist/blueprints/AppRootWrapperBlueprint.esm.js +0 -21
  23. package/dist/blueprints/AppRootWrapperBlueprint.esm.js.map +0 -1
  24. package/dist/blueprints/IconBundleBlueprint.esm.js +0 -21
  25. package/dist/blueprints/IconBundleBlueprint.esm.js.map +0 -1
  26. package/dist/blueprints/NavContentBlueprint.esm.js +0 -23
  27. package/dist/blueprints/NavContentBlueprint.esm.js.map +0 -1
  28. package/dist/blueprints/RouterBlueprint.esm.js +0 -21
  29. package/dist/blueprints/RouterBlueprint.esm.js.map +0 -1
  30. package/dist/blueprints/SignInPageBlueprint.esm.js +0 -52
  31. package/dist/blueprints/SignInPageBlueprint.esm.js.map +0 -1
  32. package/dist/blueprints/SwappableComponentBlueprint.esm.js +0 -27
  33. package/dist/blueprints/SwappableComponentBlueprint.esm.js.map +0 -1
  34. package/dist/blueprints/ThemeBlueprint.esm.js +0 -21
  35. package/dist/blueprints/ThemeBlueprint.esm.js.map +0 -1
  36. package/dist/blueprints/TranslationBlueprint.esm.js +0 -21
  37. package/dist/blueprints/TranslationBlueprint.esm.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # @backstage/frontend-plugin-api
2
2
 
3
+ ## 0.14.0-next.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c38b74d: **BREAKING**: The following blueprints have been removed and are now only available from `@backstage/plugin-app-react`:
8
+
9
+ - `IconBundleBlueprint`
10
+ - `NavContentBlueprint`
11
+ - `RouterBlueprint`
12
+ - `SignInPageBlueprint`
13
+ - `SwappableComponentBlueprint`
14
+ - `ThemeBlueprint`
15
+ - `TranslationBlueprint`
16
+
17
+ ### Patch Changes
18
+
19
+ - 7edb810: Added a new `internal` option to `createExtensionInput` that marks the input as only allowing attachments from the same plugin.
20
+ - 9554c36: **DEPRECATED**: Multiple attachment points for extensions have been deprecated. The functionality continues to work for backward compatibility, but will log a deprecation warning and be removed in a future release.
21
+
22
+ Extensions using array attachment points should migrate to using Utility APIs instead. See the [Sharing Extensions Across Multiple Locations](https://backstage.io/docs/frontend-system/architecture/27-sharing-extensions) guide for the recommended pattern.
23
+
24
+ - 53b6549: Plugins in the new frontend system now have a `pluginId` field rather than `id` to better align with naming conventions used throughout the frontend and backend systems. The old field is still present but marked as deprecated. All internal code has been updated to prefer `pluginId` while maintaining backward compatibility by falling back to `id` when needed.
25
+ - 69d880e: Bump to latest zod to ensure it has the latest features
26
+ - Updated dependencies
27
+ - @backstage/errors@1.2.7
28
+ - @backstage/types@1.2.2
29
+ - @backstage/version-bridge@1.0.11
30
+
31
+ ## 0.13.3
32
+
33
+ ### Patch Changes
34
+
35
+ - 3bd2a1a: Updated documentation for `createApiRef` to clarify the role of the ID in specifying the owning plugin of an API.
36
+ - 9ccf84e: The following blueprints are being restricted to only be used in app plugin overrides and modules. They are being moved to the `@backstage/plugin-app-react` package and have been deprecated:
37
+
38
+ - `AppRootWrapperBlueprint`
39
+ - `IconBundleBlueprint`
40
+ - `NavContentBlueprint`
41
+ - `RouterBlueprint`
42
+ - `SignInPageBlueprint`
43
+ - `SwappableComponentBlueprint`
44
+ - `ThemeBlueprint`
45
+ - `TranslationBlueprint`
46
+
47
+ - 4554a4e: Added an alpha `PluginWrapperBlueprint` exported from `@backstage/frontend-plugin-api/alpha`, which can install components that will wrap all plugin elements.
48
+ - 872eb91: Upgrade `zod-to-json-schema` to latest version
49
+
3
50
  ## 0.13.2
4
51
 
5
52
  ### Patch Changes
@@ -0,0 +1,71 @@
1
+ import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
2
+ import { ComponentType, ReactNode } from 'react';
3
+
4
+ /**
5
+ * Creates extensions that wrap plugin extensions with providers.
6
+ *
7
+ * @alpha
8
+ */
9
+ declare const PluginWrapperBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
10
+ kind: "plugin-wrapper";
11
+ params: (params: {
12
+ loader: () => Promise<{
13
+ component: ComponentType<{
14
+ children: ReactNode;
15
+ }>;
16
+ }>;
17
+ }) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<{
18
+ loader: () => Promise<{
19
+ component: ComponentType<{
20
+ children: ReactNode;
21
+ }>;
22
+ }>;
23
+ }>;
24
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<() => Promise<{
25
+ component: ComponentType<{
26
+ children: ReactNode;
27
+ }>;
28
+ }>, "core.plugin-wrapper.loader", {}>;
29
+ inputs: {};
30
+ config: {};
31
+ configInput: {};
32
+ dataRefs: {
33
+ wrapper: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<() => Promise<{
34
+ component: ComponentType<{
35
+ children: ReactNode;
36
+ }>;
37
+ }>, "core.plugin-wrapper.loader", {}>;
38
+ };
39
+ }>;
40
+
41
+ /**
42
+ * The Plugin Wrapper API is used to wrap plugin extensions with providers,
43
+ * plugins should generally use `ExtensionBoundary` instead.
44
+ *
45
+ * @remarks
46
+ *
47
+ * This API is primarily intended for internal use by the Backstage frontend
48
+ * system, but can be used for advanced use-cases. If you do override it, be
49
+ * sure to include the default implementation as well.
50
+ *
51
+ * @alpha
52
+ */
53
+ type PluginWrapperApi = {
54
+ /**
55
+ * Returns a wrapper component for a specific plugin, or undefined if no
56
+ * wrappers exist. Do not use this API directly, instead use
57
+ * `ExtensionBoundary` to wrap your plugin components if needed.
58
+ */
59
+ getPluginWrapper(pluginId: string): ComponentType<{
60
+ children: ReactNode;
61
+ }> | undefined;
62
+ };
63
+ /**
64
+ * The API reference of {@link PluginWrapperApi}.
65
+ *
66
+ * @alpha
67
+ */
68
+ declare const pluginWrapperApiRef: _backstage_frontend_plugin_api.ApiRef<PluginWrapperApi>;
69
+
70
+ export { PluginWrapperBlueprint, pluginWrapperApiRef };
71
+ export type { PluginWrapperApi };
@@ -0,0 +1,3 @@
1
+ export { PluginWrapperBlueprint } from './blueprints/PluginWrapperBlueprint.esm.js';
2
+ export { pluginWrapperApiRef } from './apis/definitions/PluginWrapperApi.esm.js';
3
+ //# sourceMappingURL=alpha.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -0,0 +1,8 @@
1
+ import { createApiRef } from '@backstage/frontend-plugin-api';
2
+
3
+ const pluginWrapperApiRef = createApiRef({
4
+ id: "core.plugin-wrapper.alpha"
5
+ });
6
+
7
+ export { pluginWrapperApiRef };
8
+ //# sourceMappingURL=PluginWrapperApi.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginWrapperApi.esm.js","sources":["../../../src/apis/definitions/PluginWrapperApi.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentType, ReactNode } from 'react';\nimport { createApiRef } from '@backstage/frontend-plugin-api';\n\n/**\n * The Plugin Wrapper API is used to wrap plugin extensions with providers,\n * plugins should generally use `ExtensionBoundary` instead.\n *\n * @remarks\n *\n * This API is primarily intended for internal use by the Backstage frontend\n * system, but can be used for advanced use-cases. If you do override it, be\n * sure to include the default implementation as well.\n *\n * @alpha\n */\nexport type PluginWrapperApi = {\n /**\n * Returns a wrapper component for a specific plugin, or undefined if no\n * wrappers exist. Do not use this API directly, instead use\n * `ExtensionBoundary` to wrap your plugin components if needed.\n */\n getPluginWrapper(\n pluginId: string,\n ): ComponentType<{ children: ReactNode }> | undefined;\n};\n\n/**\n * The API reference of {@link PluginWrapperApi}.\n *\n * @alpha\n */\nexport const pluginWrapperApiRef = createApiRef<PluginWrapperApi>({\n id: 'core.plugin-wrapper.alpha',\n});\n"],"names":[],"mappings":";;AA+CO,MAAM,sBAAsB,YAAA,CAA+B;AAAA,EAChE,EAAA,EAAI;AACN,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ApiRef.esm.js","sources":["../../../src/apis/system/ApiRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ApiRef } from './types';\n\n/**\n * API reference configuration - holds an ID of the referenced API.\n *\n * @public\n */\nexport type ApiRefConfig = {\n id: string;\n};\n\nclass ApiRefImpl<T> implements ApiRef<T> {\n constructor(private readonly config: ApiRefConfig) {\n const valid = config.id\n .split('.')\n .flatMap(part => part.split('-'))\n .every(part => part.match(/^[a-z][a-z0-9]*$/));\n if (!valid) {\n throw new Error(\n `API id must only contain period separated lowercase alphanum tokens with dashes, got '${config.id}'`,\n );\n }\n }\n\n get id(): string {\n return this.config.id;\n }\n\n // Utility for getting type of an api, using `typeof apiRef.T`\n get T(): T {\n throw new Error(`tried to read ApiRef.T of ${this}`);\n }\n\n toString() {\n return `apiRef{${this.config.id}}`;\n }\n}\n\n/**\n * Creates a reference to an API.\n *\n * @param config - The descriptor of the API to reference.\n * @returns An API reference.\n * @public\n */\nexport function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {\n return new ApiRefImpl<T>(config);\n}\n"],"names":[],"mappings":"AA2BA,MAAM,UAAA,CAAmC;AAAA,EACvC,YAA6B,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAC3B,IAAA,MAAM,QAAQ,MAAA,CAAO,EAAA,CAClB,MAAM,GAAG,CAAA,CACT,QAAQ,CAAA,IAAA,KAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA,CAC/B,KAAA,CAAM,UAAQ,IAAA,CAAK,KAAA,CAAM,kBAAkB,CAAC,CAAA;AAC/C,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sFAAA,EAAyF,OAAO,EAAE,CAAA,CAAA;AAAA,OACpG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,EAAA,GAAa;AACf,IAAA,OAAO,KAAK,MAAA,CAAO,EAAA;AAAA,EACrB;AAAA;AAAA,EAGA,IAAI,CAAA,GAAO;AACT,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAI,CAAA,CAAE,CAAA;AAAA,EACrD;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,OAAO,CAAA,OAAA,EAAU,IAAA,CAAK,MAAA,CAAO,EAAE,CAAA,CAAA,CAAA;AAAA,EACjC;AACF;AASO,SAAS,aAAgB,MAAA,EAAiC;AAC/D,EAAA,OAAO,IAAI,WAAc,MAAM,CAAA;AACjC;;;;"}
1
+ {"version":3,"file":"ApiRef.esm.js","sources":["../../../src/apis/system/ApiRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ApiRef } from './types';\n\n/**\n * API reference configuration - holds an ID of the referenced API.\n *\n * @public\n */\nexport type ApiRefConfig = {\n id: string;\n};\n\nclass ApiRefImpl<T> implements ApiRef<T> {\n constructor(private readonly config: ApiRefConfig) {\n const valid = config.id\n .split('.')\n .flatMap(part => part.split('-'))\n .every(part => part.match(/^[a-z][a-z0-9]*$/));\n if (!valid) {\n throw new Error(\n `API id must only contain period separated lowercase alphanum tokens with dashes, got '${config.id}'`,\n );\n }\n }\n\n get id(): string {\n return this.config.id;\n }\n\n // Utility for getting type of an api, using `typeof apiRef.T`\n get T(): T {\n throw new Error(`tried to read ApiRef.T of ${this}`);\n }\n\n toString() {\n return `apiRef{${this.config.id}}`;\n }\n}\n\n/**\n * Creates a reference to an API. The provided `id` is a stable identifier for\n * the API implementation.\n *\n * @remarks\n *\n * The frontend system infers the owning plugin for an API from the `id`. The\n * recommended pattern is `plugin.<plugin-id>.*` (for example,\n * `plugin.catalog.entity-presentation`). This ensures that other plugins can't\n * mistakenly override your API implementation.\n *\n * @param config - The descriptor of the API to reference.\n * @returns An API reference.\n * @public\n */\nexport function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {\n return new ApiRefImpl<T>(config);\n}\n"],"names":[],"mappings":"AA2BA,MAAM,UAAA,CAAmC;AAAA,EACvC,YAA6B,MAAA,EAAsB;AAAtB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAC3B,IAAA,MAAM,QAAQ,MAAA,CAAO,EAAA,CAClB,MAAM,GAAG,CAAA,CACT,QAAQ,CAAA,IAAA,KAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA,CAC/B,KAAA,CAAM,UAAQ,IAAA,CAAK,KAAA,CAAM,kBAAkB,CAAC,CAAA;AAC/C,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sFAAA,EAAyF,OAAO,EAAE,CAAA,CAAA;AAAA,OACpG;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,EAAA,GAAa;AACf,IAAA,OAAO,KAAK,MAAA,CAAO,EAAA;AAAA,EACrB;AAAA;AAAA,EAGA,IAAI,CAAA,GAAO;AACT,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,IAAI,CAAA,CAAE,CAAA;AAAA,EACrD;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,OAAO,CAAA,OAAA,EAAU,IAAA,CAAK,MAAA,CAAO,EAAE,CAAA,CAAA,CAAA;AAAA,EACjC;AACF;AAiBO,SAAS,aAAgB,MAAA,EAAiC;AAC/D,EAAA,OAAO,IAAI,WAAc,MAAM,CAAA;AACjC;;;;"}
@@ -0,0 +1,24 @@
1
+ import '../wiring/coreExtensionData.esm.js';
2
+ import 'zod';
3
+ import 'zod-to-json-schema';
4
+ import { createExtensionDataRef } from '../wiring/createExtensionDataRef.esm.js';
5
+ import { createExtensionBlueprint, createExtensionBlueprintParams } from '../wiring/createExtensionBlueprint.esm.js';
6
+
7
+ const wrapperDataRef = createExtensionDataRef().with({ id: "core.plugin-wrapper.loader" });
8
+ const PluginWrapperBlueprint = createExtensionBlueprint({
9
+ kind: "plugin-wrapper",
10
+ attachTo: { id: "api:app/plugin-wrapper", input: "wrappers" },
11
+ output: [wrapperDataRef],
12
+ dataRefs: {
13
+ wrapper: wrapperDataRef
14
+ },
15
+ defineParams(params) {
16
+ return createExtensionBlueprintParams(params);
17
+ },
18
+ *factory(params) {
19
+ yield wrapperDataRef(params.loader);
20
+ }
21
+ });
22
+
23
+ export { PluginWrapperBlueprint };
24
+ //# sourceMappingURL=PluginWrapperBlueprint.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginWrapperBlueprint.esm.js","sources":["../../src/blueprints/PluginWrapperBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ComponentType, ReactNode } from 'react';\nimport {\n createExtensionBlueprint,\n createExtensionBlueprintParams,\n createExtensionDataRef,\n} from '../wiring';\n\nconst wrapperDataRef = createExtensionDataRef<\n () => Promise<{ component: ComponentType<{ children: ReactNode }> }>\n>().with({ id: 'core.plugin-wrapper.loader' });\n\n/**\n * Creates extensions that wrap plugin extensions with providers.\n *\n * @alpha\n */\nexport const PluginWrapperBlueprint = createExtensionBlueprint({\n kind: 'plugin-wrapper',\n attachTo: { id: 'api:app/plugin-wrapper', input: 'wrappers' },\n output: [wrapperDataRef],\n dataRefs: {\n wrapper: wrapperDataRef,\n },\n defineParams(params: {\n loader: () => Promise<{\n component: ComponentType<{ children: ReactNode }>;\n }>;\n }) {\n return createExtensionBlueprintParams(params);\n },\n *factory(params) {\n yield wrapperDataRef(params.loader);\n },\n});\n"],"names":[],"mappings":";;;;;;AAuBA,MAAM,iBAAiB,sBAAA,EAErB,CAAE,KAAK,EAAE,EAAA,EAAI,8BAA8B,CAAA;AAOtC,MAAM,yBAAyB,wBAAA,CAAyB;AAAA,EAC7D,IAAA,EAAM,gBAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,wBAAA,EAA0B,OAAO,UAAA,EAAW;AAAA,EAC5D,MAAA,EAAQ,CAAC,cAAc,CAAA;AAAA,EACvB,QAAA,EAAU;AAAA,IACR,OAAA,EAAS;AAAA,GACX;AAAA,EACA,aAAa,MAAA,EAIV;AACD,IAAA,OAAO,+BAA+B,MAAM,CAAA;AAAA,EAC9C,CAAA;AAAA,EACA,CAAC,QAAQ,MAAA,EAAQ;AACf,IAAA,MAAM,cAAA,CAAe,OAAO,MAAM,CAAA;AAAA,EACpC;AACF,CAAC;;;;"}
@@ -1,5 +1,5 @@
1
1
  import { jsx, Fragment } from 'react/jsx-runtime';
2
- import { lazy, Suspense, useEffect } from 'react';
2
+ import { lazy, useMemo, Suspense, useEffect } from 'react';
3
3
  import { AnalyticsContext } from '../analytics/AnalyticsContext.esm.js';
4
4
  import { useAnalytics } from '../analytics/useAnalytics.esm.js';
5
5
  import { ErrorDisplayBoundary } from './ErrorDisplayBoundary.esm.js';
@@ -25,6 +25,7 @@ import '../apis/definitions/StorageApi.esm.js';
25
25
  import '../apis/definitions/AnalyticsApi.esm.js';
26
26
  import '../apis/definitions/TranslationApi.esm.js';
27
27
  import { useApi } from '../apis/system/useApi.esm.js';
28
+ import { pluginWrapperApiRef } from '../apis/definitions/PluginWrapperApi.esm.js';
28
29
  import { coreExtensionData } from '../wiring/coreExtensionData.esm.js';
29
30
  import 'zod';
30
31
  import 'zod-to-json-schema';
@@ -39,6 +40,13 @@ function useOptionalErrorApi() {
39
40
  return void 0;
40
41
  }
41
42
  }
43
+ function useOptionalPluginWrapperApi() {
44
+ try {
45
+ return useApi(pluginWrapperApiRef);
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
42
50
  const RouteTracker = (props) => {
43
51
  const { enabled, children } = props;
44
52
  const analytics = useAnalytics();
@@ -56,11 +64,19 @@ function ExtensionBoundary(props) {
56
64
  node.instance?.getData(coreExtensionData.routePath)
57
65
  );
58
66
  const plugin = node.spec.plugin;
67
+ const pluginId = plugin.pluginId ?? "app";
68
+ const pluginWrapperApi = useOptionalPluginWrapperApi();
69
+ const PluginWrapper = useMemo(() => {
70
+ return pluginWrapperApi?.getPluginWrapper(pluginId);
71
+ }, [pluginWrapperApi, pluginId]);
59
72
  const attributes = {
60
73
  extensionId: node.spec.id,
61
- pluginId: plugin.id ?? "app"
74
+ pluginId
62
75
  };
63
76
  let content = /* @__PURE__ */ jsx(AnalyticsContext, { attributes, children: /* @__PURE__ */ jsx(RouteTracker, { enabled: hasRoutePathOutput, children }) });
77
+ if (PluginWrapper) {
78
+ content = /* @__PURE__ */ jsx(PluginWrapper, { children: content });
79
+ }
64
80
  if (props.errorPresentation === "error-api") {
65
81
  content = /* @__PURE__ */ jsx(ErrorApiBoundary, { node, errorApi, children: content });
66
82
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"ExtensionBoundary.esm.js","sources":["../../src/components/ExtensionBoundary.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PropsWithChildren,\n ReactNode,\n Suspense,\n useEffect,\n lazy as reactLazy,\n} from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { ErrorDisplayBoundary } from './ErrorDisplayBoundary';\nimport { ErrorApiBoundary } from './ErrorApiBoundary';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';\nimport { AppNode, ErrorApi, errorApiRef, useApi } from '../apis';\nimport { coreExtensionData } from '../wiring';\nimport { AppNodeProvider } from './AppNodeProvider';\nimport { Progress } from './DefaultSwappableComponents';\n\nfunction useOptionalErrorApi(): ErrorApi | undefined {\n try {\n return useApi(errorApiRef);\n } catch {\n return undefined;\n }\n}\n\ntype RouteTrackerProps = PropsWithChildren<{\n enabled?: boolean;\n}>;\n\nconst RouteTracker = (props: RouteTrackerProps) => {\n const { enabled, children } = props;\n const analytics = useAnalytics();\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n if (enabled) {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }\n }, [analytics, enabled]);\n\n return <>{children}</>;\n};\n\n/** @public */\nexport interface ExtensionBoundaryProps {\n errorPresentation?: 'error-api' | 'error-display';\n node: AppNode;\n children: ReactNode;\n}\n\n/** @public */\nexport function ExtensionBoundary(props: ExtensionBoundaryProps) {\n const { node, children } = props;\n\n const errorApi = useOptionalErrorApi();\n\n const hasRoutePathOutput = Boolean(\n node.instance?.getData(coreExtensionData.routePath),\n );\n\n const plugin = node.spec.plugin;\n\n // Skipping \"routeRef\" attribute in the new system, the extension \"id\" should provide more insight\n const attributes = {\n extensionId: node.spec.id,\n pluginId: plugin.id ?? 'app',\n };\n\n let content = (\n <AnalyticsContext attributes={attributes}>\n <RouteTracker enabled={hasRoutePathOutput}>{children}</RouteTracker>\n </AnalyticsContext>\n );\n\n if (props.errorPresentation === 'error-api') {\n content = (\n <ErrorApiBoundary node={node} errorApi={errorApi}>\n {content}\n </ErrorApiBoundary>\n );\n } else {\n content = (\n <ErrorDisplayBoundary plugin={plugin}>{content}</ErrorDisplayBoundary>\n );\n }\n\n return (\n <AppNodeProvider node={node}>\n <Suspense fallback={<Progress />}>{content}</Suspense>\n </AppNodeProvider>\n );\n}\n\n/** @public */\nexport namespace ExtensionBoundary {\n export function lazy(\n appNode: AppNode,\n loader: () => Promise<JSX.Element>,\n ): JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(element => ({ default: () => element })),\n );\n return (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent />\n </ExtensionBoundary>\n );\n }\n\n export function lazyComponent<TProps extends {}>(\n appNode: AppNode,\n loader: () => Promise<(props: TProps) => JSX.Element>,\n ): (props: TProps) => JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(Component => ({ default: Component })),\n ) as unknown as React.ComponentType<TProps>;\n\n return (props: TProps) => (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent {...props} />\n </ExtensionBoundary>\n );\n }\n}\n"],"names":["ExtensionBoundary","lazy","reactLazy"],"mappings":"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"}
1
+ {"version":3,"file":"ExtensionBoundary.esm.js","sources":["../../src/components/ExtensionBoundary.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n PropsWithChildren,\n ReactNode,\n Suspense,\n useEffect,\n useMemo,\n lazy as reactLazy,\n} from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { ErrorDisplayBoundary } from './ErrorDisplayBoundary';\nimport { ErrorApiBoundary } from './ErrorApiBoundary';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';\nimport { AppNode, ErrorApi, errorApiRef, useApi } from '../apis';\nimport {\n PluginWrapperApi,\n pluginWrapperApiRef,\n} from '../apis/definitions/PluginWrapperApi';\nimport { coreExtensionData } from '../wiring';\nimport { AppNodeProvider } from './AppNodeProvider';\nimport { Progress } from './DefaultSwappableComponents';\n\nfunction useOptionalErrorApi(): ErrorApi | undefined {\n try {\n return useApi(errorApiRef);\n } catch {\n return undefined;\n }\n}\n\nfunction useOptionalPluginWrapperApi(): PluginWrapperApi | undefined {\n try {\n return useApi(pluginWrapperApiRef);\n } catch {\n return undefined;\n }\n}\ntype RouteTrackerProps = PropsWithChildren<{\n enabled?: boolean;\n}>;\n\nconst RouteTracker = (props: RouteTrackerProps) => {\n const { enabled, children } = props;\n const analytics = useAnalytics();\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n if (enabled) {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }\n }, [analytics, enabled]);\n\n return <>{children}</>;\n};\n\n/** @public */\nexport interface ExtensionBoundaryProps {\n errorPresentation?: 'error-api' | 'error-display';\n node: AppNode;\n children: ReactNode;\n}\n\n/** @public */\nexport function ExtensionBoundary(props: ExtensionBoundaryProps) {\n const { node, children } = props;\n\n const errorApi = useOptionalErrorApi();\n\n const hasRoutePathOutput = Boolean(\n node.instance?.getData(coreExtensionData.routePath),\n );\n\n const plugin = node.spec.plugin;\n const pluginId = plugin.pluginId ?? 'app';\n\n const pluginWrapperApi = useOptionalPluginWrapperApi();\n\n const PluginWrapper = useMemo(() => {\n return pluginWrapperApi?.getPluginWrapper(pluginId);\n }, [pluginWrapperApi, pluginId]);\n\n // Skipping \"routeRef\" attribute in the new system, the extension \"id\" should provide more insight\n const attributes = {\n extensionId: node.spec.id,\n pluginId,\n };\n\n let content = (\n <AnalyticsContext attributes={attributes}>\n <RouteTracker enabled={hasRoutePathOutput}>{children}</RouteTracker>\n </AnalyticsContext>\n );\n\n if (PluginWrapper) {\n content = <PluginWrapper>{content}</PluginWrapper>;\n }\n\n if (props.errorPresentation === 'error-api') {\n content = (\n <ErrorApiBoundary node={node} errorApi={errorApi}>\n {content}\n </ErrorApiBoundary>\n );\n } else {\n content = (\n <ErrorDisplayBoundary plugin={plugin}>{content}</ErrorDisplayBoundary>\n );\n }\n\n return (\n <AppNodeProvider node={node}>\n <Suspense fallback={<Progress />}>{content}</Suspense>\n </AppNodeProvider>\n );\n}\n\n/** @public */\nexport namespace ExtensionBoundary {\n export function lazy(\n appNode: AppNode,\n loader: () => Promise<JSX.Element>,\n ): JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(element => ({ default: () => element })),\n );\n return (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent />\n </ExtensionBoundary>\n );\n }\n\n export function lazyComponent<TProps extends {}>(\n appNode: AppNode,\n loader: () => Promise<(props: TProps) => JSX.Element>,\n ): (props: TProps) => JSX.Element {\n const ExtensionComponent = reactLazy(() =>\n loader().then(Component => ({ default: Component })),\n ) as unknown as React.ComponentType<TProps>;\n\n return (props: TProps) => (\n <ExtensionBoundary node={appNode}>\n <ExtensionComponent {...props} />\n </ExtensionBoundary>\n );\n }\n}\n"],"names":["ExtensionBoundary","lazy","reactLazy"],"mappings":"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"}