@backstage/frontend-app-api 0.12.0-next.3 → 0.12.1-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.
- package/CHANGELOG.md +68 -0
- package/dist/catalog-model/src/entity/constants.esm.js.map +1 -1
- package/dist/catalog-model/src/entity/ref.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js.map +1 -1
- package/dist/extensions/Root.esm.js +1 -1
- package/dist/extensions/Root.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalExtensionDefinition.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalFrontendPlugin.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/createExtensionDataContainer.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/ExternalRouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/RouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/SubRouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/wiring/createFrontendModule.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
- package/dist/opaque-internal/src/OpaqueType.esm.js.map +1 -1
- package/dist/routing/RouteAliasResolver.esm.js.map +1 -1
- package/dist/routing/RouteResolver.esm.js +30 -33
- package/dist/routing/RouteResolver.esm.js.map +1 -1
- package/dist/routing/collectRouteIds.esm.js.map +1 -1
- package/dist/routing/extractRouteInfoFromAppNode.esm.js +1 -7
- package/dist/routing/extractRouteInfoFromAppNode.esm.js.map +1 -1
- package/dist/routing/getBasePath.esm.js.map +1 -1
- package/dist/routing/resolveRouteBindings.esm.js.map +1 -1
- package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
- package/dist/tree/readAppExtensionsConfig.esm.js.map +1 -1
- package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
- package/dist/tree/resolveAppTree.esm.js.map +1 -1
- package/dist/wiring/createPluginInfoAttacher.esm.js.map +1 -1
- package/dist/wiring/createSpecializedApp.esm.js +4 -3
- package/dist/wiring/createSpecializedApp.esm.js.map +1 -1
- package/package.json +7 -7
- package/dist/routing/toLegacyPlugin.esm.js +0 -35
- package/dist/routing/toLegacyPlugin.esm.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteAliasResolver.esm.js","sources":["../../src/routing/RouteAliasResolver.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef } from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n\n/**\n * @internal\n */\nexport type RouteAliasResolver = {\n (routeRef: RouteRef, pluginId?: string): RouteRef;\n (routeRef?: RouteRef, pluginId?: string): RouteRef | undefined;\n};\n\n/**\n * Creates a route alias resolver that resolves aliases based on the route IDs\n * @internal\n */\nexport function createRouteAliasResolver(\n routeRefsById: RouteRefsById,\n): RouteAliasResolver {\n const resolver = (routeRef: RouteRef | undefined, pluginId?: string) => {\n if (!routeRef) {\n return undefined;\n }\n\n let currentRef = routeRef;\n for (let i = 0; i < 100; i++) {\n const alias = toInternalRouteRef(currentRef).alias;\n if (alias) {\n if (pluginId) {\n const [aliasPluginId] = alias.split('.');\n if (aliasPluginId !== pluginId) {\n throw new Error(\n `Refused to resolve alias '${alias}' for ${currentRef} as it points to a different plugin, the expected plugin is '${pluginId}' but the alias points to '${aliasPluginId}'`,\n );\n }\n }\n const aliasRef = routeRefsById.routes.get(alias);\n if (!aliasRef) {\n throw new Error(\n `Unable to resolve RouteRef alias '${alias}' for ${currentRef}`,\n );\n }\n if (aliasRef.$$type === '@backstage/SubRouteRef') {\n throw new Error(\n `RouteRef alias '${alias}' for ${currentRef} points to a SubRouteRef, which is not supported`,\n );\n }\n currentRef = aliasRef;\n } else {\n return currentRef;\n }\n }\n throw new Error(`Alias loop detected for ${routeRef}`);\n };\n\n return resolver as RouteAliasResolver;\n}\n\n/**\n * Creates a route alias resolver that resolves aliases based on a map of route refs to their aliases\n * @internal\n */\nexport function createExactRouteAliasResolver(\n routeAliases: Map<RouteRef, RouteRef | undefined>,\n): RouteAliasResolver {\n const resolver = (routeRef?: RouteRef) => {\n if (routeRef && routeAliases.has(routeRef)) {\n return routeAliases.get(routeRef);\n }\n return routeRef;\n };\n return resolver as RouteAliasResolver;\n}\n"],"names":[],"mappings":";;AAiCO,SAAS,yBACd,
|
|
1
|
+
{"version":3,"file":"RouteAliasResolver.esm.js","sources":["../../src/routing/RouteAliasResolver.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef } from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n\n/**\n * @internal\n */\nexport type RouteAliasResolver = {\n (routeRef: RouteRef, pluginId?: string): RouteRef;\n (routeRef?: RouteRef, pluginId?: string): RouteRef | undefined;\n};\n\n/**\n * Creates a route alias resolver that resolves aliases based on the route IDs\n * @internal\n */\nexport function createRouteAliasResolver(\n routeRefsById: RouteRefsById,\n): RouteAliasResolver {\n const resolver = (routeRef: RouteRef | undefined, pluginId?: string) => {\n if (!routeRef) {\n return undefined;\n }\n\n let currentRef = routeRef;\n for (let i = 0; i < 100; i++) {\n const alias = toInternalRouteRef(currentRef).alias;\n if (alias) {\n if (pluginId) {\n const [aliasPluginId] = alias.split('.');\n if (aliasPluginId !== pluginId) {\n throw new Error(\n `Refused to resolve alias '${alias}' for ${currentRef} as it points to a different plugin, the expected plugin is '${pluginId}' but the alias points to '${aliasPluginId}'`,\n );\n }\n }\n const aliasRef = routeRefsById.routes.get(alias);\n if (!aliasRef) {\n throw new Error(\n `Unable to resolve RouteRef alias '${alias}' for ${currentRef}`,\n );\n }\n if (aliasRef.$$type === '@backstage/SubRouteRef') {\n throw new Error(\n `RouteRef alias '${alias}' for ${currentRef} points to a SubRouteRef, which is not supported`,\n );\n }\n currentRef = aliasRef;\n } else {\n return currentRef;\n }\n }\n throw new Error(`Alias loop detected for ${routeRef}`);\n };\n\n return resolver as RouteAliasResolver;\n}\n\n/**\n * Creates a route alias resolver that resolves aliases based on a map of route refs to their aliases\n * @internal\n */\nexport function createExactRouteAliasResolver(\n routeAliases: Map<RouteRef, RouteRef | undefined>,\n): RouteAliasResolver {\n const resolver = (routeRef?: RouteRef) => {\n if (routeRef && routeAliases.has(routeRef)) {\n return routeAliases.get(routeRef);\n }\n return routeRef;\n };\n return resolver as RouteAliasResolver;\n}\n"],"names":[],"mappings":";;AAiCO,SAAS,yBACd,aAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,EAAgC,QAAA,KAAsB;AACtE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,UAAA,GAAa,QAAA;AACjB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAC5B,MAAA,MAAM,KAAA,GAAQ,kBAAA,CAAmB,UAAU,CAAA,CAAE,KAAA;AAC7C,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,MAAM,CAAC,aAAa,CAAA,GAAI,KAAA,CAAM,MAAM,GAAG,CAAA;AACvC,UAAA,IAAI,kBAAkB,QAAA,EAAU;AAC9B,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,6BAA6B,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,6DAAA,EAAgE,QAAQ,8BAA8B,aAAa,CAAA,CAAA;AAAA,aAC1K;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,QAAA,GAAW,aAAA,CAAc,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAC/C,QAAA,IAAI,CAAC,QAAA,EAAU;AACb,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,kCAAA,EAAqC,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA;AAAA,WAC/D;AAAA,QACF;AACA,QAAA,IAAI,QAAA,CAAS,WAAW,wBAAA,EAA0B;AAChD,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,gBAAA,EAAmB,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,gDAAA;AAAA,WAC7C;AAAA,QACF;AACA,QAAA,UAAA,GAAa,QAAA;AAAA,MACf,CAAA,MAAO;AACL,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACvD,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAMO,SAAS,8BACd,YAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,KAAwB;AACxC,IAAA,IAAI,QAAA,IAAY,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC1C,MAAA,OAAO,YAAA,CAAa,IAAI,QAAQ,CAAA;AAAA,IAClC;AACA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { generatePath, matchRoutes } from 'react-router-dom';
|
|
2
2
|
import mapValues from 'lodash/mapValues';
|
|
3
3
|
import { isRouteRef } from '../frontend-plugin-api/src/routing/RouteRef.esm.js';
|
|
4
4
|
import { isSubRouteRef, toInternalSubRouteRef } from '../frontend-plugin-api/src/routing/SubRouteRef.esm.js';
|
|
5
|
-
import { isExternalRouteRef } from '../frontend-plugin-api/src/routing/ExternalRouteRef.esm.js';
|
|
5
|
+
import { isExternalRouteRef, toInternalExternalRouteRef } from '../frontend-plugin-api/src/routing/ExternalRouteRef.esm.js';
|
|
6
6
|
|
|
7
7
|
function joinPaths(...paths) {
|
|
8
8
|
const normalized = paths.join("/").replace(/\/\/+/g, "/");
|
|
@@ -11,43 +11,38 @@ function joinPaths(...paths) {
|
|
|
11
11
|
}
|
|
12
12
|
return normalized;
|
|
13
13
|
}
|
|
14
|
-
function resolveTargetRef(
|
|
15
|
-
let
|
|
16
|
-
let
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
} else if (isSubRouteRef(anyRouteRef)) {
|
|
20
|
-
const internal = toInternalSubRouteRef(anyRouteRef);
|
|
21
|
-
targetRef = internal.getParent();
|
|
22
|
-
subRoutePath = internal.path;
|
|
23
|
-
} else if (isExternalRouteRef(anyRouteRef)) {
|
|
24
|
-
const resolvedRoute = routeBindings.get(anyRouteRef);
|
|
14
|
+
function resolveTargetRef(targetRouteRef, routePaths, routeBindings, routeRefsById) {
|
|
15
|
+
let ref = targetRouteRef;
|
|
16
|
+
let path = "";
|
|
17
|
+
if (isExternalRouteRef(ref)) {
|
|
18
|
+
let resolvedRoute = routeBindings.get(ref);
|
|
25
19
|
if (!resolvedRoute) {
|
|
26
|
-
|
|
20
|
+
const internal = toInternalExternalRouteRef(ref);
|
|
21
|
+
const defaultTarget = internal.getDefaultTarget();
|
|
22
|
+
if (defaultTarget) {
|
|
23
|
+
resolvedRoute = routeRefsById.get(defaultTarget);
|
|
24
|
+
}
|
|
27
25
|
}
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
} else if (isSubRouteRef(resolvedRoute)) {
|
|
31
|
-
const internal = toInternalSubRouteRef(resolvedRoute);
|
|
32
|
-
targetRef = internal.getParent();
|
|
33
|
-
subRoutePath = resolvedRoute.path;
|
|
34
|
-
} else {
|
|
35
|
-
throw new Error(
|
|
36
|
-
`ExternalRouteRef was bound to invalid target, ${resolvedRoute}`
|
|
37
|
-
);
|
|
26
|
+
if (!resolvedRoute) {
|
|
27
|
+
return [void 0, ""];
|
|
38
28
|
}
|
|
39
|
-
|
|
40
|
-
throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);
|
|
29
|
+
ref = resolvedRoute;
|
|
41
30
|
}
|
|
42
|
-
if (
|
|
43
|
-
|
|
31
|
+
if (isSubRouteRef(ref)) {
|
|
32
|
+
const internal = toInternalSubRouteRef(ref);
|
|
33
|
+
path = ref.path;
|
|
34
|
+
ref = internal.getParent();
|
|
35
|
+
}
|
|
36
|
+
if (!isRouteRef(ref)) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Unexpectedly resolved ${targetRouteRef} to a non-route ref ${ref}`
|
|
39
|
+
);
|
|
44
40
|
}
|
|
45
|
-
const resolvedPath = routePaths.get(
|
|
41
|
+
const resolvedPath = routePaths.get(ref);
|
|
46
42
|
if (resolvedPath === void 0) {
|
|
47
43
|
return [void 0, ""];
|
|
48
44
|
}
|
|
49
|
-
|
|
50
|
-
return [targetRef, targetPath];
|
|
45
|
+
return [ref, path ? joinPaths(resolvedPath, path) : resolvedPath];
|
|
51
46
|
}
|
|
52
47
|
function resolveBasePath(targetRef, sourceLocation, routePaths, routeParents, routeObjects) {
|
|
53
48
|
const match = matchRoutes(routeObjects, sourceLocation) ?? [];
|
|
@@ -81,19 +76,21 @@ function resolveBasePath(targetRef, sourceLocation, routePaths, routeParents, ro
|
|
|
81
76
|
return `${joinPaths(parentPath, ...diffPaths)}/`;
|
|
82
77
|
}
|
|
83
78
|
class RouteResolver {
|
|
84
|
-
constructor(routePaths, routeParents, routeObjects, routeBindings, appBasePath, routeAliasResolver) {
|
|
79
|
+
constructor(routePaths, routeParents, routeObjects, routeBindings, appBasePath, routeAliasResolver, routeRefsById) {
|
|
85
80
|
this.routePaths = routePaths;
|
|
86
81
|
this.routeParents = routeParents;
|
|
87
82
|
this.routeObjects = routeObjects;
|
|
88
83
|
this.routeBindings = routeBindings;
|
|
89
84
|
this.appBasePath = appBasePath;
|
|
90
85
|
this.routeAliasResolver = routeAliasResolver;
|
|
86
|
+
this.routeRefsById = routeRefsById;
|
|
91
87
|
}
|
|
92
88
|
resolve(anyRouteRef, options) {
|
|
93
89
|
const [targetRef, targetPath] = resolveTargetRef(
|
|
94
90
|
anyRouteRef?.$$type === "@backstage/RouteRef" ? this.routeAliasResolver(anyRouteRef) : anyRouteRef,
|
|
95
91
|
this.routePaths,
|
|
96
|
-
this.routeBindings
|
|
92
|
+
this.routeBindings,
|
|
93
|
+
this.routeRefsById
|
|
97
94
|
);
|
|
98
95
|
if (!targetRef) {
|
|
99
96
|
return void 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.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 { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\nimport { RouteAliasResolver } from './RouteAliasResolver';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n anyRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let targetRef: RouteRef;\n let subRoutePath = '';\n if (isRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef;\n } else if (isSubRouteRef(anyRouteRef)) {\n const internal = toInternalSubRouteRef(anyRouteRef);\n targetRef = internal.getParent();\n subRoutePath = internal.path;\n } else if (isExternalRouteRef(anyRouteRef)) {\n const resolvedRoute = routeBindings.get(anyRouteRef);\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n if (isRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute;\n } else if (isSubRouteRef(resolvedRoute)) {\n const internal = toInternalSubRouteRef(resolvedRoute);\n targetRef = internal.getParent();\n subRoutePath = resolvedRoute.path;\n } else {\n throw new Error(\n `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`,\n );\n }\n } else {\n throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);\n }\n\n // Bail if no absolute path could be resolved\n if (!targetRef) {\n return [undefined, ''];\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(targetRef);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n // SubRouteRefs join the path from the parent route with its own path\n const targetPath = joinPaths(resolvedPath, subRoutePath);\n return [targetRef, targetPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n private readonly routeAliasResolver: RouteAliasResolver,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef?.$$type === '@backstage/RouteRef'\n ? this.routeAliasResolver(anyRouteRef)\n : anyRouteRef,\n this.routePaths,\n this.routeBindings,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.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 { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isExternalRouteRef,\n toInternalExternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\nimport { RouteAliasResolver } from './RouteAliasResolver';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n targetRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let ref: AnyRouteRef = targetRouteRef;\n let path = '';\n\n if (isExternalRouteRef(ref)) {\n let resolvedRoute = routeBindings.get(ref);\n if (!resolvedRoute) {\n const internal = toInternalExternalRouteRef(ref);\n const defaultTarget = internal.getDefaultTarget();\n if (defaultTarget) {\n resolvedRoute = routeRefsById.get(defaultTarget);\n }\n }\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n ref = resolvedRoute;\n }\n\n if (isSubRouteRef(ref)) {\n const internal = toInternalSubRouteRef(ref);\n path = ref.path;\n ref = internal.getParent();\n }\n\n if (!isRouteRef(ref)) {\n throw new Error(\n `Unexpectedly resolved ${targetRouteRef} to a non-route ref ${ref}`,\n );\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(ref);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n return [ref, path ? joinPaths(resolvedPath, path) : resolvedPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n private readonly routeAliasResolver: RouteAliasResolver,\n private readonly routeRefsById: Map<string, RouteRef | SubRouteRef>,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef?.$$type === '@backstage/RouteRef'\n ? this.routeAliasResolver(anyRouteRef)\n : anyRouteRef,\n this.routePaths,\n this.routeBindings,\n this.routeRefsById,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectRouteIds.esm.js","sources":["../../src/routing/collectRouteIds.ts"],"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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isRouteRef,\n toInternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n\n/** @internal */\nexport interface RouteRefsById {\n routes: Map<string, RouteRef | SubRouteRef>;\n externalRoutes: Map<string, ExternalRouteRef>;\n}\n\n/** @internal */\nexport function collectRouteIds(features: FrontendFeature[]): RouteRefsById {\n const routesById = new Map<string, RouteRef | SubRouteRef>();\n const externalRoutesById = new Map<string, ExternalRouteRef>();\n\n for (const feature of features) {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n continue;\n }\n\n for (const [name, ref] of Object.entries(feature.routes)) {\n const refId = `${feature.id}.${name}`;\n if (routesById.has(refId)) {\n throw new Error(`Unexpected duplicate route '${refId}'`);\n }\n\n if (isRouteRef(ref)) {\n const internalRef = toInternalRouteRef(ref);\n internalRef.setId(refId);\n routesById.set(refId, ref);\n } else {\n const internalRef = toInternalSubRouteRef(ref);\n routesById.set(refId, internalRef);\n }\n }\n for (const [name, ref] of Object.entries(feature.externalRoutes)) {\n const refId = `${feature.id}.${name}`;\n if (externalRoutesById.has(refId)) {\n throw new Error(`Unexpected duplicate external route '${refId}'`);\n }\n\n const internalRef = toInternalExternalRouteRef(ref);\n internalRef.setId(refId);\n externalRoutesById.set(refId, ref);\n }\n }\n\n return { routes: routesById, externalRoutes: externalRoutesById };\n}\n"],"names":[],"mappings":";;;;;;;AAwCO,SAAS,gBAAgB,
|
|
1
|
+
{"version":3,"file":"collectRouteIds.esm.js","sources":["../../src/routing/collectRouteIds.ts"],"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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isRouteRef,\n toInternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n\n/** @internal */\nexport interface RouteRefsById {\n routes: Map<string, RouteRef | SubRouteRef>;\n externalRoutes: Map<string, ExternalRouteRef>;\n}\n\n/** @internal */\nexport function collectRouteIds(features: FrontendFeature[]): RouteRefsById {\n const routesById = new Map<string, RouteRef | SubRouteRef>();\n const externalRoutesById = new Map<string, ExternalRouteRef>();\n\n for (const feature of features) {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n continue;\n }\n\n for (const [name, ref] of Object.entries(feature.routes)) {\n const refId = `${feature.id}.${name}`;\n if (routesById.has(refId)) {\n throw new Error(`Unexpected duplicate route '${refId}'`);\n }\n\n if (isRouteRef(ref)) {\n const internalRef = toInternalRouteRef(ref);\n internalRef.setId(refId);\n routesById.set(refId, ref);\n } else {\n const internalRef = toInternalSubRouteRef(ref);\n routesById.set(refId, internalRef);\n }\n }\n for (const [name, ref] of Object.entries(feature.externalRoutes)) {\n const refId = `${feature.id}.${name}`;\n if (externalRoutesById.has(refId)) {\n throw new Error(`Unexpected duplicate external route '${refId}'`);\n }\n\n const internalRef = toInternalExternalRouteRef(ref);\n internalRef.setId(refId);\n externalRoutesById.set(refId, ref);\n }\n }\n\n return { routes: routesById, externalRoutes: externalRoutesById };\n}\n"],"names":[],"mappings":";;;;;;;AAwCO,SAAS,gBAAgB,QAAA,EAA4C;AAC1E,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAoC;AAC3D,EAAA,MAAM,kBAAA,uBAAyB,GAAA,EAA8B;AAE7D,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,IAAA,IAAI,CAAC,oBAAA,CAAqB,MAAA,CAAO,OAAO,CAAA,EAAG;AACzC,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,CAAC,MAAM,GAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AACxD,MAAA,MAAM,KAAA,GAAQ,CAAA,EAAG,OAAA,CAAQ,EAAE,IAAI,IAAI,CAAA,CAAA;AACnC,MAAA,IAAI,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,EAAG;AACzB,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,KAAK,CAAA,CAAA,CAAG,CAAA;AAAA,MACzD;AAEA,MAAA,IAAI,UAAA,CAAW,GAAG,CAAA,EAAG;AACnB,QAAA,MAAM,WAAA,GAAc,mBAAmB,GAAG,CAAA;AAC1C,QAAA,WAAA,CAAY,MAAM,KAAK,CAAA;AACvB,QAAA,UAAA,CAAW,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,MAC3B,CAAA,MAAO;AACL,QAAA,MAAM,WAAA,GAAc,sBAAsB,GAAG,CAAA;AAC7C,QAAA,UAAA,CAAW,GAAA,CAAI,OAAO,WAAW,CAAA;AAAA,MACnC;AAAA,IACF;AACA,IAAA,KAAA,MAAW,CAAC,MAAM,GAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,cAAc,CAAA,EAAG;AAChE,MAAA,MAAM,KAAA,GAAQ,CAAA,EAAG,OAAA,CAAQ,EAAE,IAAI,IAAI,CAAA,CAAA;AACnC,MAAA,IAAI,kBAAA,CAAmB,GAAA,CAAI,KAAK,CAAA,EAAG;AACjC,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,qCAAA,EAAwC,KAAK,CAAA,CAAA,CAAG,CAAA;AAAA,MAClE;AAEA,MAAA,MAAM,WAAA,GAAc,2BAA2B,GAAG,CAAA;AAClD,MAAA,WAAA,CAAY,MAAM,KAAK,CAAA;AACvB,MAAA,kBAAA,CAAmB,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,IACnC;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,MAAA,EAAQ,UAAA,EAAY,cAAA,EAAgB,kBAAA,EAAmB;AAClE;;;;"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { coreExtensionData } from '@backstage/frontend-plugin-api';
|
|
2
|
-
import { toLegacyPlugin } from './toLegacyPlugin.esm.js';
|
|
3
2
|
import { createExactRouteAliasResolver } from './RouteAliasResolver.esm.js';
|
|
4
3
|
|
|
5
4
|
const MATCH_ALL_ROUTE = {
|
|
@@ -7,8 +6,7 @@ const MATCH_ALL_ROUTE = {
|
|
|
7
6
|
path: "*",
|
|
8
7
|
element: "match-all",
|
|
9
8
|
// These elements aren't used, so we add in a bit of debug information
|
|
10
|
-
routeRefs: /* @__PURE__ */ new Set()
|
|
11
|
-
plugins: /* @__PURE__ */ new Set()
|
|
9
|
+
routeRefs: /* @__PURE__ */ new Set()
|
|
12
10
|
};
|
|
13
11
|
function joinPaths(...paths) {
|
|
14
12
|
const normalized = paths.join("/").replace(/\/\/+/g, "/");
|
|
@@ -42,7 +40,6 @@ function extractRouteInfoFromAppNode(node, routeAliasResolver) {
|
|
|
42
40
|
routeRefs: /* @__PURE__ */ new Set(),
|
|
43
41
|
caseSensitive: false,
|
|
44
42
|
children: [MATCH_ALL_ROUTE],
|
|
45
|
-
plugins: /* @__PURE__ */ new Set(),
|
|
46
43
|
appNode: current
|
|
47
44
|
};
|
|
48
45
|
parentChildren.push(currentObj);
|
|
@@ -65,9 +62,6 @@ function extractRouteInfoFromAppNode(node, routeAliasResolver) {
|
|
|
65
62
|
}
|
|
66
63
|
routeParents.set(routeRef, newParentRef);
|
|
67
64
|
currentObj?.routeRefs.add(routeRef);
|
|
68
|
-
if (current.spec.plugin) {
|
|
69
|
-
currentObj?.plugins.add(toLegacyPlugin(current.spec.plugin));
|
|
70
|
-
}
|
|
71
65
|
}
|
|
72
66
|
for (const children of current.edges.attachments.values()) {
|
|
73
67
|
for (const child of children) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractRouteInfoFromAppNode.esm.js","sources":["../../src/routing/extractRouteInfoFromAppNode.ts"],"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 { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { AppNode } from '@backstage/frontend-plugin-api';\nimport {
|
|
1
|
+
{"version":3,"file":"extractRouteInfoFromAppNode.esm.js","sources":["../../src/routing/extractRouteInfoFromAppNode.ts"],"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 { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { AppNode } from '@backstage/frontend-plugin-api';\nimport {\n createExactRouteAliasResolver,\n RouteAliasResolver,\n} from './RouteAliasResolver';\n\n/** @internal */\nexport type RouteInfo = {\n routePaths: Map<RouteRef, string>;\n routeParents: Map<RouteRef, RouteRef | undefined>;\n routeObjects: BackstageRouteObject[];\n routeAliasResolver: RouteAliasResolver;\n};\n\n// We always add a child that matches all subroutes but without any route refs. This makes\n// sure that we're always able to match each route no matter how deep the navigation goes.\n// The route resolver then takes care of selecting the most specific match in order to find\n// mount points that are as deep in the routing tree as possible.\nexport const MATCH_ALL_ROUTE: BackstageRouteObject = {\n caseSensitive: false,\n path: '*',\n element: 'match-all', // These elements aren't used, so we add in a bit of debug information\n routeRefs: new Set(),\n};\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function extractRouteInfoFromAppNode(\n node: AppNode,\n routeAliasResolver: RouteAliasResolver,\n): RouteInfo {\n // This tracks the route path for each route ref, the value is the route path relative to the parent ref\n const routePaths = new Map<RouteRef, string>();\n // This tracks the parents of each route ref. To find the full path of any route ref you traverse\n // upwards in this tree and substitute each route ref for its route path along then way.\n const routeParents = new Map<RouteRef, RouteRef | undefined>();\n // This route object tree is passed to react-router in order to be able to look up the current route\n // ref or extension/source based on our current location.\n const routeObjects = new Array<BackstageRouteObject>();\n // This tracks all resolved route aliases. By storing and re-using the resolutions here we make sure that it's not\n // possible to pass an aliased route ref directly to the resolver, e.g. `useRouteRef(createRouteRef({ aliasFor: 'example.root' }))`\n const routeAliases = new Map<RouteRef, RouteRef | undefined>();\n\n function visit(\n current: AppNode,\n collectedPath?: string,\n foundRefForCollectedPath: boolean = false,\n parentRef?: RouteRef,\n candidateParentRef?: RouteRef,\n parentObj?: BackstageRouteObject,\n ) {\n const routePath = current.instance\n ?.getData(coreExtensionData.routePath)\n ?.replace(/^\\//, '');\n\n const foundRouteRef = current.instance?.getData(coreExtensionData.routeRef);\n const routeRef = routeAliasResolver(foundRouteRef, current.spec.plugin?.id);\n if (foundRouteRef && routeRef !== foundRouteRef) {\n routeAliases.set(foundRouteRef, routeRef);\n }\n\n const parentChildren = parentObj?.children ?? routeObjects;\n let currentObj = parentObj;\n\n let newCollectedPath = collectedPath;\n let newFoundRefForCollectedPath = foundRefForCollectedPath;\n\n let newParentRef = parentRef;\n let newCandidateParentRef = candidateParentRef;\n\n // Whenever a route path is encountered, a new node is created in the routing tree.\n if (routePath !== undefined) {\n currentObj = {\n path: routePath,\n element: 'mounted',\n routeRefs: new Set<RouteRef>(),\n caseSensitive: false,\n children: [MATCH_ALL_ROUTE],\n appNode: current,\n };\n parentChildren.push(currentObj);\n\n // Each route path that we discover creates a new node in the routing tree, at that point\n // we also switch out our candidate parent ref to be the active one.\n newParentRef = candidateParentRef;\n newCandidateParentRef = undefined;\n\n // We need to collect and concatenate route paths until the path has been assigned a route ref:\n // Once we find a route ref the collection starts over from an empty path, that way each route\n // path assignment only contains the diff from the parent ref.\n if (newFoundRefForCollectedPath) {\n newCollectedPath = routePath;\n newFoundRefForCollectedPath = false;\n } else {\n newCollectedPath = collectedPath\n ? joinPaths(collectedPath, routePath)\n : routePath;\n }\n }\n\n // Whenever a route ref is encountered, we need to give it a route path and position in the ref tree.\n if (routeRef) {\n // The first route ref we find after encountering a route path is selected to be used as the\n // parent ref further down the tree. We don't start using this candidate ref until we encounter\n // another route path though, at which point we repeat the process and select another candidate.\n if (!newCandidateParentRef) {\n newCandidateParentRef = routeRef;\n }\n\n // Check if we've encountered any route paths since the closest route ref, in that case we assign\n // that path to this and following route refs until we encounter another route path.\n if (newCollectedPath !== undefined) {\n routePaths.set(routeRef, newCollectedPath);\n newFoundRefForCollectedPath = true;\n }\n\n routeParents.set(routeRef, newParentRef);\n currentObj?.routeRefs.add(routeRef);\n }\n\n for (const children of current.edges.attachments.values()) {\n for (const child of children) {\n visit(\n child,\n newCollectedPath,\n newFoundRefForCollectedPath,\n newParentRef,\n newCandidateParentRef,\n currentObj,\n );\n }\n }\n }\n\n visit(node);\n\n return {\n routePaths,\n routeParents,\n routeObjects,\n routeAliasResolver: createExactRouteAliasResolver(routeAliases),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getBasePath.esm.js","sources":["../../src/routing/getBasePath.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 { ConfigApi } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport function getBasePath(configApi: ConfigApi) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://sample.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n"],"names":[],"mappings":"AAmBO,SAAS,YAAY,
|
|
1
|
+
{"version":3,"file":"getBasePath.esm.js","sources":["../../src/routing/getBasePath.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 { ConfigApi } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport function getBasePath(configApi: ConfigApi) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://sample.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n"],"names":[],"mappings":"AAmBO,SAAS,YAAY,SAAA,EAAsB;AAChD,EAAA,IAAI,EAAE,QAAA,EAAS,GAAI,IAAI,GAAA;AAAA,IACrB,SAAA,CAAU,iBAAA,CAAkB,aAAa,CAAA,IAAK,GAAA;AAAA,IAC9C;AAAA;AAAA,GACF;AACA,EAAA,QAAA,GAAW,QAAA,CAAS,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACtC,EAAA,OAAO,QAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveRouteBindings.esm.js","sources":["../../src/routing/resolveRouteBindings.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 {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { Config } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n/**\n * Extracts a union of the keys in a map whose value extends the given type\n *\n * @ignore\n */\ntype KeysWithType<Obj extends { [key in string]: any }, Type> = {\n [key in keyof Obj]: Obj[key] extends Type ? key : never;\n}[keyof Obj];\n\n/**\n * Takes a map Map required values and makes all keys matching Keys optional\n *\n * @ignore\n */\ntype PartialKeys<\n Map extends { [name in string]: any },\n Keys extends keyof Map,\n> = Partial<Pick<Map, Keys>> & Required<Omit<Map, Keys>>;\n\n/**\n * Creates a map of target routes with matching parameters based on a map of external routes.\n *\n * @ignore\n */\ntype TargetRouteMap<\n ExternalRoutes extends { [name: string]: ExternalRouteRef },\n> = {\n [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef<\n infer Params\n >\n ? RouteRef<Params> | SubRouteRef<Params> | false\n : never;\n};\n\n/**\n * A function that can bind from external routes of a given plugin, to concrete\n * routes of other plugins. See {@link @backstage/frontend-defaults#createApp}.\n *\n * @public\n */\nexport type CreateAppRouteBinder = <\n TExternalRoutes extends { [name: string]: ExternalRouteRef },\n>(\n externalRoutes: TExternalRoutes,\n targetRoutes: PartialKeys<\n TargetRouteMap<TExternalRoutes>,\n KeysWithType<TExternalRoutes, ExternalRouteRef<any>>\n >,\n) => void;\n\n/** @internal */\nexport function resolveRouteBindings(\n bindRoutes: ((context: { bind: CreateAppRouteBinder }) => void) | undefined,\n config: Config,\n routesById: RouteRefsById,\n): Map<ExternalRouteRef, RouteRef | SubRouteRef> {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n const disabledExternalRefs = new Set<ExternalRouteRef>();\n\n // Perform callback bindings first with highest priority\n if (bindRoutes) {\n const bind: CreateAppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (value) {\n result.set(externalRoute, value);\n } else if (value === false) {\n disabledExternalRefs.add(externalRoute);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n // Then perform config based bindings with lower priority\n const bindings = config\n .getOptionalConfig('app.routes.bindings')\n ?.get<JsonObject>();\n if (bindings) {\n for (const [externalRefId, targetRefId] of Object.entries(bindings)) {\n if (!isValidTargetRefId(targetRefId)) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,\n );\n }\n\n const externalRef = routesById.externalRoutes.get(externalRefId);\n if (!externalRef) {\n throw new Error(\n `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,\n );\n }\n\n // Skip if binding was already defined in code\n if (result.has(externalRef) || disabledExternalRefs.has(externalRef)) {\n continue;\n }\n\n if (targetRefId === false) {\n disabledExternalRefs.add(externalRef);\n } else {\n const targetRef = routesById.routes.get(targetRefId);\n if (!targetRef) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`,\n );\n }\n\n result.set(externalRef, targetRef);\n }\n }\n }\n\n // Finally fall back to attempting to map defaults, at lowest priority\n for (const externalRef of routesById.externalRoutes.values()) {\n if (!result.has(externalRef) && !disabledExternalRefs.has(externalRef)) {\n const defaultRefId =\n toInternalExternalRouteRef(externalRef).getDefaultTarget();\n if (defaultRefId) {\n const defaultRef = routesById.routes.get(defaultRefId);\n if (defaultRef) {\n result.set(externalRef, defaultRef);\n }\n }\n }\n }\n\n return result;\n}\n\nfunction isValidTargetRefId(value: unknown): value is string | false {\n if (value === false) {\n return true;\n }\n\n if (typeof value === 'string' && value) {\n return true;\n }\n\n return false;\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"resolveRouteBindings.esm.js","sources":["../../src/routing/resolveRouteBindings.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 {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { Config } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n/**\n * Extracts a union of the keys in a map whose value extends the given type\n *\n * @ignore\n */\ntype KeysWithType<Obj extends { [key in string]: any }, Type> = {\n [key in keyof Obj]: Obj[key] extends Type ? key : never;\n}[keyof Obj];\n\n/**\n * Takes a map Map required values and makes all keys matching Keys optional\n *\n * @ignore\n */\ntype PartialKeys<\n Map extends { [name in string]: any },\n Keys extends keyof Map,\n> = Partial<Pick<Map, Keys>> & Required<Omit<Map, Keys>>;\n\n/**\n * Creates a map of target routes with matching parameters based on a map of external routes.\n *\n * @ignore\n */\ntype TargetRouteMap<\n ExternalRoutes extends { [name: string]: ExternalRouteRef },\n> = {\n [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef<\n infer Params\n >\n ? RouteRef<Params> | SubRouteRef<Params> | false\n : never;\n};\n\n/**\n * A function that can bind from external routes of a given plugin, to concrete\n * routes of other plugins. See {@link @backstage/frontend-defaults#createApp}.\n *\n * @public\n */\nexport type CreateAppRouteBinder = <\n TExternalRoutes extends { [name: string]: ExternalRouteRef },\n>(\n externalRoutes: TExternalRoutes,\n targetRoutes: PartialKeys<\n TargetRouteMap<TExternalRoutes>,\n KeysWithType<TExternalRoutes, ExternalRouteRef<any>>\n >,\n) => void;\n\n/** @internal */\nexport function resolveRouteBindings(\n bindRoutes: ((context: { bind: CreateAppRouteBinder }) => void) | undefined,\n config: Config,\n routesById: RouteRefsById,\n): Map<ExternalRouteRef, RouteRef | SubRouteRef> {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n const disabledExternalRefs = new Set<ExternalRouteRef>();\n\n // Perform callback bindings first with highest priority\n if (bindRoutes) {\n const bind: CreateAppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (value) {\n result.set(externalRoute, value);\n } else if (value === false) {\n disabledExternalRefs.add(externalRoute);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n // Then perform config based bindings with lower priority\n const bindings = config\n .getOptionalConfig('app.routes.bindings')\n ?.get<JsonObject>();\n if (bindings) {\n for (const [externalRefId, targetRefId] of Object.entries(bindings)) {\n if (!isValidTargetRefId(targetRefId)) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,\n );\n }\n\n const externalRef = routesById.externalRoutes.get(externalRefId);\n if (!externalRef) {\n throw new Error(\n `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,\n );\n }\n\n // Skip if binding was already defined in code\n if (result.has(externalRef) || disabledExternalRefs.has(externalRef)) {\n continue;\n }\n\n if (targetRefId === false) {\n disabledExternalRefs.add(externalRef);\n } else {\n const targetRef = routesById.routes.get(targetRefId);\n if (!targetRef) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`,\n );\n }\n\n result.set(externalRef, targetRef);\n }\n }\n }\n\n // Finally fall back to attempting to map defaults, at lowest priority\n for (const externalRef of routesById.externalRoutes.values()) {\n if (!result.has(externalRef) && !disabledExternalRefs.has(externalRef)) {\n const defaultRefId =\n toInternalExternalRouteRef(externalRef).getDefaultTarget();\n if (defaultRefId) {\n const defaultRef = routesById.routes.get(defaultRefId);\n if (defaultRef) {\n result.set(externalRef, defaultRef);\n }\n }\n }\n }\n\n return result;\n}\n\nfunction isValidTargetRefId(value: unknown): value is string | false {\n if (value === false) {\n return true;\n }\n\n if (typeof value === 'string' && value) {\n return true;\n }\n\n return false;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../src/tree/instantiateAppNodeTree.ts"],"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 ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionFactoryMiddleware,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { createExtensionDataContainer } from '@internal/frontend';\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: ExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return mapValues(dataMap, ref => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return value;\n });\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<ExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n): { node: AppNode } & ExtensionDataContainer<ExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n for (const ref of extensionData) {\n if (dataMap.has(ref.id)) {\n throw new Error(`Unexpected duplicate input data '${ref.id}'`);\n }\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n\n dataMap.set(ref.id, value);\n }\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<ExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n };\n }\n\n return attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(input.extensionData, attachment, inputName),\n }));\n }) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n): ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n}> {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n );\n }\n\n return attachedNodes.map(attachment =>\n resolveInputDataContainer(input.extensionData, attachment, inputName),\n );\n }) as ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n}): AppNodeInstance {\n const { node, apis, attachments } = options;\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: { [x: string]: any };\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {}) as {\n [x: string]: any;\n };\n } catch (e) {\n throw new Error(\n `Invalid configuration for extension '${id}'; caused by ${e}`,\n );\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const context = {\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(internalExtension.inputs, attachments),\n };\n const outputDataValues = options.extensionFactoryMiddleware\n ? createExtensionDataContainer(\n options.extensionFactoryMiddleware(overrideContext => {\n return createExtensionDataContainer(\n internalExtension.factory({\n node: context.node,\n apis: context.apis,\n inputs: context.inputs,\n config: overrideContext?.config ?? context.config,\n }),\n 'extension factory',\n );\n }, context),\n 'extension factory middleware',\n )\n : internalExtension.factory(context);\n\n if (\n typeof outputDataValues !== 'object' ||\n !outputDataValues?.[Symbol.iterator]\n ) {\n throw new Error('extension factory did not provide an iterable object');\n }\n\n const outputDataMap = new Map<string, unknown>();\n for (const value of outputDataValues) {\n if (outputDataMap.has(value.id)) {\n throw new Error(`duplicate extension data output '${value.id}'`);\n }\n outputDataMap.set(value.id, value.value);\n }\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n throw new Error(\n `missing required extension data output '${ref.id}'`,\n );\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n throw new Error(\n `unexpected output '${Array.from(outputDataMap.keys()).join(\n \"', '\",\n )}'`,\n );\n }\n } else {\n throw new Error(\n `unexpected extension version '${(internalExtension as any).version}'`,\n );\n }\n } catch (e) {\n throw new Error(\n `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}`\n }`,\n );\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware,\n): void {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n extensionFactoryMiddleware,\n node,\n apis,\n attachments: instantiatedAttachments,\n });\n\n return node.instance;\n }\n\n createInstance(rootNode);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../src/tree/instantiateAppNodeTree.ts"],"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 ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionFactoryMiddleware,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { createExtensionDataContainer } from '@internal/frontend';\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: ExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return mapValues(dataMap, ref => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return value;\n });\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<ExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n): { node: AppNode } & ExtensionDataContainer<ExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n for (const ref of extensionData) {\n if (dataMap.has(ref.id)) {\n throw new Error(`Unexpected duplicate input data '${ref.id}'`);\n }\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n\n dataMap.set(ref.id, value);\n }\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<ExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n };\n }\n\n return attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(input.extensionData, attachment, inputName),\n }));\n }) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n): ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n}> {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n );\n }\n\n return attachedNodes.map(attachment =>\n resolveInputDataContainer(input.extensionData, attachment, inputName),\n );\n }) as ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n}): AppNodeInstance {\n const { node, apis, attachments } = options;\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: { [x: string]: any };\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {}) as {\n [x: string]: any;\n };\n } catch (e) {\n throw new Error(\n `Invalid configuration for extension '${id}'; caused by ${e}`,\n );\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const context = {\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(internalExtension.inputs, attachments),\n };\n const outputDataValues = options.extensionFactoryMiddleware\n ? createExtensionDataContainer(\n options.extensionFactoryMiddleware(overrideContext => {\n return createExtensionDataContainer(\n internalExtension.factory({\n node: context.node,\n apis: context.apis,\n inputs: context.inputs,\n config: overrideContext?.config ?? context.config,\n }),\n 'extension factory',\n );\n }, context),\n 'extension factory middleware',\n )\n : internalExtension.factory(context);\n\n if (\n typeof outputDataValues !== 'object' ||\n !outputDataValues?.[Symbol.iterator]\n ) {\n throw new Error('extension factory did not provide an iterable object');\n }\n\n const outputDataMap = new Map<string, unknown>();\n for (const value of outputDataValues) {\n if (outputDataMap.has(value.id)) {\n throw new Error(`duplicate extension data output '${value.id}'`);\n }\n outputDataMap.set(value.id, value.value);\n }\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n throw new Error(\n `missing required extension data output '${ref.id}'`,\n );\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n throw new Error(\n `unexpected output '${Array.from(outputDataMap.keys()).join(\n \"', '\",\n )}'`,\n );\n }\n } else {\n throw new Error(\n `unexpected extension version '${(internalExtension as any).version}'`,\n );\n }\n } catch (e) {\n throw new Error(\n `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}`\n }`,\n );\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware,\n): void {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n extensionFactoryMiddleware,\n node,\n apis,\n attachments: instantiatedAttachments,\n });\n\n return node.instance;\n }\n\n createInstance(rootNode);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readAppExtensionsConfig.esm.js","sources":["../../src/tree/readAppExtensionsConfig.ts"],"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 { Config } from '@backstage/config';\nimport { JsonValue } from '@backstage/types';\n\nexport interface ExtensionParameters {\n id: string;\n attachTo?: { id: string; input: string };\n disabled?: boolean;\n config?: unknown;\n}\n\nconst knownExtensionParameters = ['attachTo', 'disabled', 'config'];\n\n// Since we'll never merge arrays in config the config reader context\n// isn't too much of a help. Fall back to manual config reading logic\n// as the Config interface makes it quite hard for us otherwise.\n/** @internal */\nexport function readAppExtensionsConfig(\n rootConfig: Config,\n): ExtensionParameters[] {\n const arr = rootConfig.getOptional('app.extensions');\n if (!Array.isArray(arr)) {\n if (arr === undefined) {\n return [];\n }\n // This will throw, and show which part of config had the wrong type\n rootConfig.getConfigArray('app.extensions');\n return [];\n }\n\n return arr.map((arrayEntry, arrayIndex) =>\n expandShorthandExtensionParameters(arrayEntry, arrayIndex),\n );\n}\n\n/** @internal */\nexport function expandShorthandExtensionParameters(\n arrayEntry: JsonValue,\n arrayIndex: number,\n): ExtensionParameters {\n function errorMsg(msg: string, key?: string, prop?: string) {\n return `Invalid extension configuration at app.extensions[${arrayIndex}]${\n key ? `[${key}]` : ''\n }${prop ? `.${prop}` : ''}, ${msg}`;\n }\n\n // NOTE(freben): This check is intentionally not complete and doesn't check\n // whether letters and digits are used, etc. It's not up to the config reading\n // logic to decide what constitutes a valid extension ID; that should be\n // decided by the logic that loads and instantiates the extensions. This check\n // is just here to catch real mistakes or truly conceptually wrong input.\n function assertValidId(id: string) {\n if (!id || id !== id.trim()) {\n throw new Error(\n errorMsg('extension ID must not be empty or contain whitespace'),\n );\n }\n }\n\n // Example YAML:\n // - entity.card.about\n if (typeof arrayEntry === 'string') {\n assertValidId(arrayEntry);\n return {\n id: arrayEntry,\n disabled: false,\n };\n }\n\n // All remaining cases are single-key objects\n if (\n typeof arrayEntry !== 'object' ||\n arrayEntry === null ||\n Array.isArray(arrayEntry)\n ) {\n throw new Error(errorMsg('must be a string or an object'));\n }\n const keys = Object.keys(arrayEntry);\n if (keys.length !== 1) {\n const joinedKeys = keys.length ? `'${keys.join(\"', '\")}'` : 'none';\n throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`));\n }\n\n const id = String(keys[0]);\n const value = arrayEntry[id];\n assertValidId(id);\n\n // This example covers a potentially common mistake in the syntax\n // Example YAML:\n // - entity.card.about:\n if (value === null) {\n return {\n id,\n disabled: false,\n };\n }\n\n // Example YAML:\n // - catalog.page.cicd: false\n if (typeof value === 'boolean') {\n return {\n id,\n disabled: !value,\n };\n }\n\n // The remaining case is the generic object. Example YAML:\n // - tech-radar.page:\n // at: core.router/routes\n // disabled: false\n // config:\n // path: /tech-radar\n // width: 1500\n // height: 800\n if (typeof value !== 'object' || Array.isArray(value)) {\n // We don't mention null here - we don't want people to explicitly enter\n // - entity.card.about: null\n throw new Error(errorMsg('value must be a boolean or object', id));\n }\n\n const attachTo = value.attachTo as { id: string; input: string } | undefined;\n const disabled = value.disabled;\n const config = value.config;\n\n if (attachTo !== undefined) {\n if (\n attachTo === null ||\n typeof attachTo !== 'object' ||\n Array.isArray(attachTo)\n ) {\n throw new Error(errorMsg('must be an object', id, 'attachTo'));\n }\n if (typeof attachTo.id !== 'string' || attachTo.id === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.id'),\n );\n }\n if (typeof attachTo.input !== 'string' || attachTo.input === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.input'),\n );\n }\n }\n if (disabled !== undefined && typeof disabled !== 'boolean') {\n throw new Error(errorMsg('must be a boolean', id, 'disabled'));\n }\n if (\n config !== undefined &&\n (typeof config !== 'object' || config === null || Array.isArray(config))\n ) {\n throw new Error(errorMsg('must be an object', id, 'config'));\n }\n\n const unknownKeys = Object.keys(value).filter(\n k => !knownExtensionParameters.includes(k),\n );\n if (unknownKeys.length > 0) {\n throw new Error(\n errorMsg(\n `unknown parameter; expected one of '${knownExtensionParameters.join(\n \"', '\",\n )}'`,\n id,\n unknownKeys.join(', '),\n ),\n );\n }\n\n return {\n id,\n attachTo,\n disabled,\n config,\n };\n}\n"],"names":["id"],"mappings":"AA0BA,MAAM,
|
|
1
|
+
{"version":3,"file":"readAppExtensionsConfig.esm.js","sources":["../../src/tree/readAppExtensionsConfig.ts"],"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 { Config } from '@backstage/config';\nimport { JsonValue } from '@backstage/types';\n\nexport interface ExtensionParameters {\n id: string;\n attachTo?: { id: string; input: string };\n disabled?: boolean;\n config?: unknown;\n}\n\nconst knownExtensionParameters = ['attachTo', 'disabled', 'config'];\n\n// Since we'll never merge arrays in config the config reader context\n// isn't too much of a help. Fall back to manual config reading logic\n// as the Config interface makes it quite hard for us otherwise.\n/** @internal */\nexport function readAppExtensionsConfig(\n rootConfig: Config,\n): ExtensionParameters[] {\n const arr = rootConfig.getOptional('app.extensions');\n if (!Array.isArray(arr)) {\n if (arr === undefined) {\n return [];\n }\n // This will throw, and show which part of config had the wrong type\n rootConfig.getConfigArray('app.extensions');\n return [];\n }\n\n return arr.map((arrayEntry, arrayIndex) =>\n expandShorthandExtensionParameters(arrayEntry, arrayIndex),\n );\n}\n\n/** @internal */\nexport function expandShorthandExtensionParameters(\n arrayEntry: JsonValue,\n arrayIndex: number,\n): ExtensionParameters {\n function errorMsg(msg: string, key?: string, prop?: string) {\n return `Invalid extension configuration at app.extensions[${arrayIndex}]${\n key ? `[${key}]` : ''\n }${prop ? `.${prop}` : ''}, ${msg}`;\n }\n\n // NOTE(freben): This check is intentionally not complete and doesn't check\n // whether letters and digits are used, etc. It's not up to the config reading\n // logic to decide what constitutes a valid extension ID; that should be\n // decided by the logic that loads and instantiates the extensions. This check\n // is just here to catch real mistakes or truly conceptually wrong input.\n function assertValidId(id: string) {\n if (!id || id !== id.trim()) {\n throw new Error(\n errorMsg('extension ID must not be empty or contain whitespace'),\n );\n }\n }\n\n // Example YAML:\n // - entity.card.about\n if (typeof arrayEntry === 'string') {\n assertValidId(arrayEntry);\n return {\n id: arrayEntry,\n disabled: false,\n };\n }\n\n // All remaining cases are single-key objects\n if (\n typeof arrayEntry !== 'object' ||\n arrayEntry === null ||\n Array.isArray(arrayEntry)\n ) {\n throw new Error(errorMsg('must be a string or an object'));\n }\n const keys = Object.keys(arrayEntry);\n if (keys.length !== 1) {\n const joinedKeys = keys.length ? `'${keys.join(\"', '\")}'` : 'none';\n throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`));\n }\n\n const id = String(keys[0]);\n const value = arrayEntry[id];\n assertValidId(id);\n\n // This example covers a potentially common mistake in the syntax\n // Example YAML:\n // - entity.card.about:\n if (value === null) {\n return {\n id,\n disabled: false,\n };\n }\n\n // Example YAML:\n // - catalog.page.cicd: false\n if (typeof value === 'boolean') {\n return {\n id,\n disabled: !value,\n };\n }\n\n // The remaining case is the generic object. Example YAML:\n // - tech-radar.page:\n // at: core.router/routes\n // disabled: false\n // config:\n // path: /tech-radar\n // width: 1500\n // height: 800\n if (typeof value !== 'object' || Array.isArray(value)) {\n // We don't mention null here - we don't want people to explicitly enter\n // - entity.card.about: null\n throw new Error(errorMsg('value must be a boolean or object', id));\n }\n\n const attachTo = value.attachTo as { id: string; input: string } | undefined;\n const disabled = value.disabled;\n const config = value.config;\n\n if (attachTo !== undefined) {\n if (\n attachTo === null ||\n typeof attachTo !== 'object' ||\n Array.isArray(attachTo)\n ) {\n throw new Error(errorMsg('must be an object', id, 'attachTo'));\n }\n if (typeof attachTo.id !== 'string' || attachTo.id === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.id'),\n );\n }\n if (typeof attachTo.input !== 'string' || attachTo.input === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.input'),\n );\n }\n }\n if (disabled !== undefined && typeof disabled !== 'boolean') {\n throw new Error(errorMsg('must be a boolean', id, 'disabled'));\n }\n if (\n config !== undefined &&\n (typeof config !== 'object' || config === null || Array.isArray(config))\n ) {\n throw new Error(errorMsg('must be an object', id, 'config'));\n }\n\n const unknownKeys = Object.keys(value).filter(\n k => !knownExtensionParameters.includes(k),\n );\n if (unknownKeys.length > 0) {\n throw new Error(\n errorMsg(\n `unknown parameter; expected one of '${knownExtensionParameters.join(\n \"', '\",\n )}'`,\n id,\n unknownKeys.join(', '),\n ),\n );\n }\n\n return {\n id,\n attachTo,\n disabled,\n config,\n };\n}\n"],"names":["id"],"mappings":"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"}
|