@backstage/frontend-app-api 0.12.1-next.0 → 0.13.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 +29 -0
- package/dist/index.d.ts +111 -2
- package/dist/routing/collectRouteIds.esm.js +13 -3
- package/dist/routing/collectRouteIds.esm.js.map +1 -1
- package/dist/routing/resolveRouteBindings.esm.js +27 -11
- package/dist/routing/resolveRouteBindings.esm.js.map +1 -1
- package/dist/tree/instantiateAppNodeTree.esm.js +166 -83
- package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
- package/dist/tree/resolveAppNodeSpecs.esm.js +56 -54
- package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
- package/dist/tree/resolveAppTree.esm.js +11 -5
- package/dist/tree/resolveAppTree.esm.js.map +1 -1
- package/dist/wiring/createErrorCollector.esm.js +31 -0
- package/dist/wiring/createErrorCollector.esm.js.map +1 -0
- package/dist/wiring/createSpecializedApp.esm.js +23 -12
- package/dist/wiring/createSpecializedApp.esm.js.map +1 -1
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @backstage/frontend-app-api
|
|
2
2
|
|
|
3
|
+
## 0.13.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/frontend-defaults@0.3.2-next.0
|
|
9
|
+
- @backstage/frontend-plugin-api@0.12.1-next.0
|
|
10
|
+
- @backstage/config@1.3.3
|
|
11
|
+
- @backstage/core-app-api@1.19.0
|
|
12
|
+
- @backstage/core-plugin-api@1.11.0
|
|
13
|
+
- @backstage/errors@1.2.7
|
|
14
|
+
- @backstage/types@1.2.2
|
|
15
|
+
- @backstage/version-bridge@1.0.11
|
|
16
|
+
|
|
17
|
+
## 0.13.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- 6516c3d: The `createSpecializedApp` no longer throws when encountering many common errors when starting up the app. It will instead return them through the `errors` property so that they can be handled more gracefully in the app.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies
|
|
26
|
+
- @backstage/frontend-plugin-api@0.12.0
|
|
27
|
+
- @backstage/core-plugin-api@1.11.0
|
|
28
|
+
- @backstage/types@1.2.2
|
|
29
|
+
- @backstage/frontend-defaults@0.3.1
|
|
30
|
+
- @backstage/core-app-api@1.19.0
|
|
31
|
+
|
|
3
32
|
## 0.12.1-next.0
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExternalRouteRef, RouteRef, SubRouteRef, FrontendPluginInfo, FrontendFeature, ExtensionFactoryMiddleware, AppTree } from '@backstage/frontend-plugin-api';
|
|
1
|
+
import { ExternalRouteRef, RouteRef, SubRouteRef, FrontendPluginInfo, FrontendPlugin, AppNode, FrontendFeature, ExtensionFactoryMiddleware, AppTree } from '@backstage/frontend-plugin-api';
|
|
2
2
|
import { ConfigApi, ApiHolder } from '@backstage/core-plugin-api';
|
|
3
3
|
import { JsonObject } from '@backstage/types';
|
|
4
4
|
|
|
@@ -58,6 +58,114 @@ type FrontendPluginInfoResolver = (ctx: {
|
|
|
58
58
|
info: FrontendPluginInfo;
|
|
59
59
|
}>;
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
type AppErrorTypes = {
|
|
65
|
+
EXTENSION_IGNORED: {
|
|
66
|
+
context: {
|
|
67
|
+
plugin: FrontendPlugin;
|
|
68
|
+
extensionId: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
INVALID_EXTENSION_CONFIG_KEY: {
|
|
72
|
+
context: {
|
|
73
|
+
extensionId: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
EXTENSION_INPUT_REDIRECT_CONFLICT: {
|
|
77
|
+
context: {
|
|
78
|
+
node: AppNode;
|
|
79
|
+
inputName: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
EXTENSION_INPUT_DATA_IGNORED: {
|
|
83
|
+
context: {
|
|
84
|
+
node: AppNode;
|
|
85
|
+
inputName: string;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
EXTENSION_INPUT_DATA_MISSING: {
|
|
89
|
+
context: {
|
|
90
|
+
node: AppNode;
|
|
91
|
+
inputName: string;
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
EXTENSION_ATTACHMENT_CONFLICT: {
|
|
95
|
+
context: {
|
|
96
|
+
node: AppNode;
|
|
97
|
+
inputName: string;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
EXTENSION_ATTACHMENT_MISSING: {
|
|
101
|
+
context: {
|
|
102
|
+
node: AppNode;
|
|
103
|
+
inputName: string;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
EXTENSION_CONFIGURATION_INVALID: {
|
|
107
|
+
context: {
|
|
108
|
+
node: AppNode;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
EXTENSION_INVALID: {
|
|
112
|
+
context: {
|
|
113
|
+
node: AppNode;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
EXTENSION_OUTPUT_CONFLICT: {
|
|
117
|
+
context: {
|
|
118
|
+
node: AppNode;
|
|
119
|
+
dataRefId: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
EXTENSION_OUTPUT_MISSING: {
|
|
123
|
+
context: {
|
|
124
|
+
node: AppNode;
|
|
125
|
+
dataRefId: string;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
EXTENSION_OUTPUT_IGNORED: {
|
|
129
|
+
context: {
|
|
130
|
+
node: AppNode;
|
|
131
|
+
dataRefId: string;
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
EXTENSION_FACTORY_ERROR: {
|
|
135
|
+
context: {
|
|
136
|
+
node: AppNode;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
API_EXTENSION_INVALID: {
|
|
140
|
+
context: {
|
|
141
|
+
node: AppNode;
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
ROUTE_DUPLICATE: {
|
|
145
|
+
context: {
|
|
146
|
+
routeId: string;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
ROUTE_BINDING_INVALID_VALUE: {
|
|
150
|
+
context: {
|
|
151
|
+
routeId: string;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
ROUTE_NOT_FOUND: {
|
|
155
|
+
context: {
|
|
156
|
+
routeId: string;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* @public
|
|
162
|
+
*/
|
|
163
|
+
type AppError = keyof AppErrorTypes extends infer ICode extends keyof AppErrorTypes ? ICode extends any ? {
|
|
164
|
+
code: ICode;
|
|
165
|
+
message: string;
|
|
166
|
+
context: AppErrorTypes[ICode]['context'];
|
|
167
|
+
} : never : never;
|
|
168
|
+
|
|
61
169
|
/**
|
|
62
170
|
* Options for {@link createSpecializedApp}.
|
|
63
171
|
*
|
|
@@ -130,6 +238,7 @@ type CreateSpecializedAppOptions = {
|
|
|
130
238
|
declare function createSpecializedApp(options?: CreateSpecializedAppOptions): {
|
|
131
239
|
apis: ApiHolder;
|
|
132
240
|
tree: AppTree;
|
|
241
|
+
errors?: AppError[];
|
|
133
242
|
};
|
|
134
243
|
|
|
135
|
-
export { type CreateAppRouteBinder, type CreateSpecializedAppOptions, type FrontendPluginInfoResolver, createSpecializedApp };
|
|
244
|
+
export { type AppError, type AppErrorTypes, type CreateAppRouteBinder, type CreateSpecializedAppOptions, type FrontendPluginInfoResolver, createSpecializedApp };
|
|
@@ -5,7 +5,7 @@ import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFr
|
|
|
5
5
|
import '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
|
|
6
6
|
import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
|
|
7
7
|
|
|
8
|
-
function collectRouteIds(features) {
|
|
8
|
+
function collectRouteIds(features, collector) {
|
|
9
9
|
const routesById = /* @__PURE__ */ new Map();
|
|
10
10
|
const externalRoutesById = /* @__PURE__ */ new Map();
|
|
11
11
|
for (const feature of features) {
|
|
@@ -15,7 +15,12 @@ function collectRouteIds(features) {
|
|
|
15
15
|
for (const [name, ref] of Object.entries(feature.routes)) {
|
|
16
16
|
const refId = `${feature.id}.${name}`;
|
|
17
17
|
if (routesById.has(refId)) {
|
|
18
|
-
|
|
18
|
+
collector.report({
|
|
19
|
+
code: "ROUTE_DUPLICATE",
|
|
20
|
+
message: `Duplicate route id '${refId}' encountered while collecting routes`,
|
|
21
|
+
context: { routeId: refId }
|
|
22
|
+
});
|
|
23
|
+
continue;
|
|
19
24
|
}
|
|
20
25
|
if (isRouteRef(ref)) {
|
|
21
26
|
const internalRef = toInternalRouteRef(ref);
|
|
@@ -29,7 +34,12 @@ function collectRouteIds(features) {
|
|
|
29
34
|
for (const [name, ref] of Object.entries(feature.externalRoutes)) {
|
|
30
35
|
const refId = `${feature.id}.${name}`;
|
|
31
36
|
if (externalRoutesById.has(refId)) {
|
|
32
|
-
|
|
37
|
+
collector.report({
|
|
38
|
+
code: "ROUTE_DUPLICATE",
|
|
39
|
+
message: `Duplicate external route id '${refId}' encountered while collecting routes`,
|
|
40
|
+
context: { routeId: refId }
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
33
43
|
}
|
|
34
44
|
const internalRef = toInternalExternalRouteRef(ref);
|
|
35
45
|
internalRef.setId(refId);
|
|
@@ -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
|
|
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';\nimport { ErrorCollector } from '../wiring/createErrorCollector';\n\n/** @internal */\nexport interface RouteRefsById {\n routes: Map<string, RouteRef | SubRouteRef>;\n externalRoutes: Map<string, ExternalRouteRef>;\n}\n\n/** @internal */\nexport function collectRouteIds(\n features: FrontendFeature[],\n collector: ErrorCollector,\n): 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 collector.report({\n code: 'ROUTE_DUPLICATE',\n message: `Duplicate route id '${refId}' encountered while collecting routes`,\n context: { routeId: refId },\n });\n continue;\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 collector.report({\n code: 'ROUTE_DUPLICATE',\n message: `Duplicate external route id '${refId}' encountered while collecting routes`,\n context: { routeId: refId },\n });\n continue;\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":";;;;;;;AAyCO,SAAS,eAAA,CACd,UACA,SAAA,EACe;AACf,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,SAAA,CAAU,MAAA,CAAO;AAAA,UACf,IAAA,EAAM,iBAAA;AAAA,UACN,OAAA,EAAS,uBAAuB,KAAK,CAAA,qCAAA,CAAA;AAAA,UACrC,OAAA,EAAS,EAAE,OAAA,EAAS,KAAA;AAAM,SAC3B,CAAA;AACD,QAAA;AAAA,MACF;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,SAAA,CAAU,MAAA,CAAO;AAAA,UACf,IAAA,EAAM,iBAAA;AAAA,UACN,OAAA,EAAS,gCAAgC,KAAK,CAAA,qCAAA,CAAA;AAAA,UAC9C,OAAA,EAAS,EAAE,OAAA,EAAS,KAAA;AAAM,SAC3B,CAAA;AACD,QAAA;AAAA,MACF;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,6 +1,6 @@
|
|
|
1
1
|
import { toInternalExternalRouteRef } from '../frontend-plugin-api/src/routing/ExternalRouteRef.esm.js';
|
|
2
2
|
|
|
3
|
-
function resolveRouteBindings(bindRoutes, config, routesById) {
|
|
3
|
+
function resolveRouteBindings(bindRoutes, config, routesById, collector) {
|
|
4
4
|
const result = /* @__PURE__ */ new Map();
|
|
5
5
|
const disabledExternalRefs = /* @__PURE__ */ new Set();
|
|
6
6
|
if (bindRoutes) {
|
|
@@ -8,7 +8,12 @@ function resolveRouteBindings(bindRoutes, config, routesById) {
|
|
|
8
8
|
for (const [key, value] of Object.entries(targetRoutes)) {
|
|
9
9
|
const externalRoute = externalRoutes[key];
|
|
10
10
|
if (!externalRoute) {
|
|
11
|
-
|
|
11
|
+
collector.report({
|
|
12
|
+
code: "ROUTE_NOT_FOUND",
|
|
13
|
+
message: `Key ${key} is not an existing external route`,
|
|
14
|
+
context: { routeId: String(key) }
|
|
15
|
+
});
|
|
16
|
+
continue;
|
|
12
17
|
}
|
|
13
18
|
if (value) {
|
|
14
19
|
result.set(externalRoute, value);
|
|
@@ -23,15 +28,21 @@ function resolveRouteBindings(bindRoutes, config, routesById) {
|
|
|
23
28
|
if (bindings) {
|
|
24
29
|
for (const [externalRefId, targetRefId] of Object.entries(bindings)) {
|
|
25
30
|
if (!isValidTargetRefId(targetRefId)) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
collector.report({
|
|
32
|
+
code: "ROUTE_BINDING_INVALID_VALUE",
|
|
33
|
+
message: `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,
|
|
34
|
+
context: { routeId: externalRefId }
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
29
37
|
}
|
|
30
38
|
const externalRef = routesById.externalRoutes.get(externalRefId);
|
|
31
39
|
if (!externalRef) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
collector.report({
|
|
41
|
+
code: "ROUTE_NOT_FOUND",
|
|
42
|
+
message: `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,
|
|
43
|
+
context: { routeId: externalRefId }
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
35
46
|
}
|
|
36
47
|
if (result.has(externalRef) || disabledExternalRefs.has(externalRef)) {
|
|
37
48
|
continue;
|
|
@@ -41,9 +52,14 @@ function resolveRouteBindings(bindRoutes, config, routesById) {
|
|
|
41
52
|
} else {
|
|
42
53
|
const targetRef = routesById.routes.get(targetRefId);
|
|
43
54
|
if (!targetRef) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
collector.report({
|
|
56
|
+
code: "ROUTE_NOT_FOUND",
|
|
57
|
+
message: `Invalid config at app.routes.bindings['${externalRefId}'], '${String(
|
|
58
|
+
targetRefId
|
|
59
|
+
)}' is not a valid route`,
|
|
60
|
+
context: { routeId: String(targetRefId) }
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
47
63
|
}
|
|
48
64
|
result.set(externalRef, targetRef);
|
|
49
65
|
}
|
|
@@ -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
|
|
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 { ErrorCollector } from '../wiring/createErrorCollector';\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 collector: ErrorCollector,\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 collector.report({\n code: 'ROUTE_NOT_FOUND',\n message: `Key ${key} is not an existing external route`,\n context: { routeId: String(key) },\n });\n continue;\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 collector.report({\n code: 'ROUTE_BINDING_INVALID_VALUE',\n message: `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,\n context: { routeId: externalRefId },\n });\n continue;\n }\n\n const externalRef = routesById.externalRoutes.get(externalRefId);\n if (!externalRef) {\n collector.report({\n code: 'ROUTE_NOT_FOUND',\n message: `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,\n context: { routeId: externalRefId },\n });\n continue;\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 collector.report({\n code: 'ROUTE_NOT_FOUND',\n message: `Invalid config at app.routes.bindings['${externalRefId}'], '${String(\n targetRefId,\n )}' is not a valid route`,\n context: { routeId: String(targetRefId) },\n });\n continue;\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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"}
|