@backstage/core-app-api 1.0.6-next.0 → 1.1.0-next.1

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @backstage/core-app-api
2
2
 
3
+ ## 1.1.0-next.1
4
+
5
+ ### Minor Changes
6
+
7
+ - a448fea691: Updated the routing system to be compatible with React Router v6 stable.
8
+
9
+ ### Patch Changes
10
+
11
+ - 817f3196f6: Updated React Router dependencies to be peer dependencies.
12
+ - 70299c99d5: Updated `FlatRoutes` to be compatible with React Router v6 stable.
13
+ - Updated dependencies
14
+ - @backstage/core-plugin-api@1.0.6-next.1
15
+
3
16
  ## 1.0.6-next.0
4
17
 
5
18
  ### Patch Changes
package/dist/index.esm.js CHANGED
@@ -6,7 +6,7 @@ import { SessionState, FeatureFlagState, getComponentData, attachComponentData,
6
6
  import { z } from 'zod';
7
7
  import { ConfigReader } from '@backstage/config';
8
8
  export { ConfigReader } from '@backstage/config';
9
- import { matchRoutes, generatePath, useLocation, Routes, Route, useRoutes } from 'react-router-dom';
9
+ import { matchRoutes, generatePath, useLocation, createRoutesFromChildren, Route, Routes, useRoutes } from 'react-router-dom';
10
10
  import useAsync from 'react-use/lib/useAsync';
11
11
  import useObservable from 'react-use/lib/useObservable';
12
12
 
@@ -1745,10 +1745,142 @@ attachComponentData(FeatureFlagged, "core.featureFlagged", true);
1745
1745
 
1746
1746
  const MATCH_ALL_ROUTE = {
1747
1747
  caseSensitive: false,
1748
- path: "/*",
1748
+ path: "*",
1749
1749
  element: "match-all",
1750
1750
  routeRefs: /* @__PURE__ */ new Set()
1751
1751
  };
1752
+ function stringifyNode(node) {
1753
+ var _a, _b;
1754
+ const anyNode = node;
1755
+ if (anyNode == null ? void 0 : anyNode.type) {
1756
+ return (_b = (_a = anyNode.type.displayName) != null ? _a : anyNode.type.name) != null ? _b : String(anyNode.type);
1757
+ }
1758
+ return String(anyNode);
1759
+ }
1760
+ function collectSubTree(node, entries = new Array()) {
1761
+ Children.forEach(node, (element) => {
1762
+ if (!isValidElement(element)) {
1763
+ return;
1764
+ }
1765
+ if (element.props.path) {
1766
+ throw new Error(
1767
+ `Elements within the element prop tree may not have paths, found "${element.props.path}"`
1768
+ );
1769
+ }
1770
+ const routeRef = getComponentData(element, "core.mountPoint");
1771
+ if (routeRef) {
1772
+ const plugin = getComponentData(element, "core.plugin");
1773
+ entries.push({ routeRef, plugin });
1774
+ }
1775
+ collectSubTree(element.props.children, entries);
1776
+ });
1777
+ return entries;
1778
+ }
1779
+ const routingV2Collector = createCollector(
1780
+ () => ({
1781
+ paths: /* @__PURE__ */ new Map(),
1782
+ parents: /* @__PURE__ */ new Map(),
1783
+ objects: new Array()
1784
+ }),
1785
+ (acc, node, parent, ctx) => {
1786
+ var _a, _b, _c, _d, _e, _f;
1787
+ const pathProp = (_a = node.props) == null ? void 0 : _a.path;
1788
+ const mountPoint = getComponentData(node, "core.mountPoint");
1789
+ if (mountPoint && pathProp) {
1790
+ throw new Error(
1791
+ `Path property may not be set directly on a routable extension "${stringifyNode(
1792
+ node
1793
+ )}"`
1794
+ );
1795
+ }
1796
+ if (ctx == null ? void 0 : ctx.isElementAncestor) {
1797
+ return ctx;
1798
+ }
1799
+ if ((parent == null ? void 0 : parent.props.element) === node) {
1800
+ return { ...ctx, isElementAncestor: true };
1801
+ }
1802
+ const parentChildren = (_c = (_b = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _b.children) != null ? _c : acc.objects;
1803
+ if (pathProp !== void 0) {
1804
+ if (typeof pathProp !== "string") {
1805
+ throw new Error(
1806
+ `Element path must be a string at "${stringifyNode(node)}"`
1807
+ );
1808
+ }
1809
+ const path = pathProp.startsWith("/") ? pathProp.slice(1) : pathProp;
1810
+ const elementProp = node.props.element;
1811
+ if (getComponentData(node, "core.gatherMountPoints")) {
1812
+ if (elementProp) {
1813
+ throw new Error(
1814
+ `Mount point gatherers may not have an element prop "${stringifyNode(
1815
+ node
1816
+ )}"`
1817
+ );
1818
+ }
1819
+ const newObj = {
1820
+ path,
1821
+ element: "gathered",
1822
+ routeRefs: /* @__PURE__ */ new Set(),
1823
+ caseSensitive: Boolean((_d = node.props) == null ? void 0 : _d.caseSensitive),
1824
+ children: [MATCH_ALL_ROUTE],
1825
+ plugin: void 0
1826
+ };
1827
+ parentChildren.push(newObj);
1828
+ return {
1829
+ obj: newObj,
1830
+ gatherPath: path,
1831
+ routeRef: ctx == null ? void 0 : ctx.routeRef,
1832
+ gatherRouteRef: ctx == null ? void 0 : ctx.routeRef
1833
+ };
1834
+ }
1835
+ if (elementProp) {
1836
+ const [extension, ...others] = collectSubTree(elementProp);
1837
+ if (others.length > 0) {
1838
+ throw new Error(
1839
+ `Route element with path "${pathProp}" may not contain multiple routable extensions`
1840
+ );
1841
+ }
1842
+ if (!extension) {
1843
+ return ctx;
1844
+ }
1845
+ const { routeRef, plugin } = extension;
1846
+ const newObj = {
1847
+ path,
1848
+ element: "mounted",
1849
+ routeRefs: /* @__PURE__ */ new Set([routeRef]),
1850
+ caseSensitive: Boolean((_e = node.props) == null ? void 0 : _e.caseSensitive),
1851
+ children: [MATCH_ALL_ROUTE],
1852
+ plugin
1853
+ };
1854
+ parentChildren.push(newObj);
1855
+ acc.paths.set(routeRef, path);
1856
+ acc.parents.set(routeRef, ctx == null ? void 0 : ctx.routeRef);
1857
+ return {
1858
+ obj: newObj,
1859
+ routeRef: routeRef != null ? routeRef : ctx == null ? void 0 : ctx.routeRef,
1860
+ gatherPath: path,
1861
+ gatherRouteRef: ctx == null ? void 0 : ctx.gatherRouteRef
1862
+ };
1863
+ }
1864
+ }
1865
+ if (mountPoint) {
1866
+ if (!(ctx == null ? void 0 : ctx.gatherPath)) {
1867
+ throw new Error(
1868
+ `Routable extension "${stringifyNode(
1869
+ node
1870
+ )}" with mount point "${mountPoint}" must be assigned a path`
1871
+ );
1872
+ }
1873
+ (_f = ctx == null ? void 0 : ctx.obj) == null ? void 0 : _f.routeRefs.add(mountPoint);
1874
+ acc.paths.set(mountPoint, ctx.gatherPath);
1875
+ acc.parents.set(mountPoint, ctx == null ? void 0 : ctx.gatherRouteRef);
1876
+ return {
1877
+ ...ctx,
1878
+ routeRef: mountPoint
1879
+ };
1880
+ }
1881
+ return ctx;
1882
+ }
1883
+ );
1752
1884
  const routingV1Collector = createCollector(
1753
1885
  () => ({
1754
1886
  paths: /* @__PURE__ */ new Map(),
@@ -1880,6 +2012,7 @@ function joinPaths(...paths) {
1880
2012
  }
1881
2013
  return normalized;
1882
2014
  }
2015
+
1883
2016
  function resolveTargetRef(anyRouteRef, routePaths, routeBindings) {
1884
2017
  let targetRef;
1885
2018
  let subRoutePath = "";
@@ -1914,7 +2047,7 @@ function resolveTargetRef(anyRouteRef, routePaths, routeBindings) {
1914
2047
  return [void 0, ""];
1915
2048
  }
1916
2049
  const resolvedPath = routePaths.get(targetRef);
1917
- if (!resolvedPath) {
2050
+ if (resolvedPath === void 0) {
1918
2051
  return [void 0, ""];
1919
2052
  }
1920
2053
  const targetPath = joinPaths(resolvedPath, subRoutePath);
@@ -1938,21 +2071,19 @@ function resolveBasePath(targetRef, sourceLocation, routePaths, routeParents, ro
1938
2071
  matchIndex -= 1;
1939
2072
  }
1940
2073
  const parentPath = matchIndex === -1 ? "" : match[matchIndex].pathname;
1941
- const diffPath = joinPaths(
1942
- ...refDiffList.slice(0, -1).map((ref) => {
1943
- const path = routePaths.get(ref);
1944
- if (!path) {
1945
- throw new Error(`No path for ${ref}`);
1946
- }
1947
- if (path.includes(":")) {
1948
- throw new Error(
1949
- `Cannot route to ${targetRef} with parent ${ref} as it has parameters`
1950
- );
1951
- }
1952
- return path;
1953
- })
1954
- );
1955
- return parentPath + diffPath;
2074
+ const diffPaths = refDiffList.slice(0, -1).map((ref) => {
2075
+ const path = routePaths.get(ref);
2076
+ if (path === void 0) {
2077
+ throw new Error(`No path for ${ref}`);
2078
+ }
2079
+ if (path.includes(":")) {
2080
+ throw new Error(
2081
+ `Cannot route to ${targetRef} with parent ${ref} as it has parameters`
2082
+ );
2083
+ }
2084
+ return path;
2085
+ });
2086
+ return `${joinPaths(parentPath, ...diffPaths)}/`;
1956
2087
  }
1957
2088
  class RouteResolver {
1958
2089
  constructor(routePaths, routeParents, routeObjects, routeBindings, appBasePath) {
@@ -1990,7 +2121,7 @@ class RouteResolver {
1990
2121
  this.routeObjects
1991
2122
  );
1992
2123
  const routeFunc = (...[params]) => {
1993
- return basePath + generatePath(targetPath, params);
2124
+ return joinPaths(basePath, generatePath(targetPath, params));
1994
2125
  };
1995
2126
  return routeFunc;
1996
2127
  }
@@ -2088,10 +2219,10 @@ function validateRouteParameters(routePaths, routeParents) {
2088
2219
  let fullPath = "";
2089
2220
  while (currentRouteRef) {
2090
2221
  const path = routePaths.get(currentRouteRef);
2091
- if (!path) {
2222
+ if (path === void 0) {
2092
2223
  throw new Error(`No path for ${currentRouteRef}`);
2093
2224
  }
2094
- fullPath = `${path}${fullPath}`;
2225
+ fullPath = joinPaths(path, fullPath);
2095
2226
  currentRouteRef = routeParents.get(currentRouteRef);
2096
2227
  }
2097
2228
  const params = fullPath.match(/:(\w+)/g);
@@ -2360,6 +2491,14 @@ function resolveRouteBindings(bindRoutes) {
2360
2491
  return result;
2361
2492
  }
2362
2493
 
2494
+ function isReactRouterBeta() {
2495
+ const [obj] = createRoutesFromChildren(/* @__PURE__ */ React.createElement(Route, {
2496
+ index: true,
2497
+ element: /* @__PURE__ */ React.createElement("div", null)
2498
+ }));
2499
+ return !obj.index;
2500
+ }
2501
+
2363
2502
  const InternalAppContext = createContext({ routeObjects: [] });
2364
2503
  function getBasePath(configApi) {
2365
2504
  var _a;
@@ -2452,7 +2591,7 @@ class AppManager {
2452
2591
  root: children,
2453
2592
  discoverers: [childDiscoverer, routeElementDiscoverer],
2454
2593
  collectors: {
2455
- routing: routingV1Collector,
2594
+ routing: isReactRouterBeta() ? routingV1Collector : routingV2Collector,
2456
2595
  collectedPlugins: pluginCollector,
2457
2596
  featureFlags: featureFlagCollector
2458
2597
  }
@@ -2680,9 +2819,11 @@ function createSpecializedApp(options) {
2680
2819
  return new AppManager(options);
2681
2820
  }
2682
2821
 
2822
+ let warned = false;
2683
2823
  const FlatRoutes = (props) => {
2684
2824
  const app = useApp();
2685
2825
  const { NotFoundErrorPage } = app.getComponents();
2826
+ const isBeta = useMemo(() => isReactRouterBeta(), []);
2686
2827
  const routes = useElementFilter(
2687
2828
  props.children,
2688
2829
  (elements) => elements.getElements().flatMap((child) => {
@@ -2692,28 +2833,38 @@ const FlatRoutes = (props) => {
2692
2833
  return [];
2693
2834
  }
2694
2835
  path = (_a = path == null ? void 0 : path.replace(/\/\*$/, "")) != null ? _a : "/";
2836
+ let element = isBeta ? child : child.props.element;
2837
+ if (!isBeta && !element) {
2838
+ element = child;
2839
+ if (!warned && process.env.NODE_ENV !== "test") {
2840
+ console.warn(
2841
+ "DEPRECATION WARNING: All elements within <FlatRoutes> must be of type <Route> with an element prop. Existing usages of <Navigate key=[path] to=[to] /> should be replaced with <Route path=[path] element={<Navigate to=[to] />} />."
2842
+ );
2843
+ warned = true;
2844
+ }
2845
+ }
2695
2846
  return [
2696
2847
  {
2697
2848
  path,
2698
- element: child,
2849
+ element,
2699
2850
  children: child.props.children ? [
2700
2851
  {
2701
- path: path === "/" ? "/" : "/*",
2852
+ path: path === "/" ? "/" : "*",
2702
2853
  element: child.props.children
2703
2854
  }
2704
2855
  ] : void 0
2705
2856
  }
2706
2857
  ];
2707
- }).sort((a, b) => b.path.localeCompare(a.path)).map((obj) => {
2708
- obj.path = obj.path === "/" ? "/" : `${obj.path}/*`;
2709
- return obj;
2710
- })
2858
+ }).sort((a, b) => b.path.localeCompare(a.path)).map((obj) => ({ ...obj, path: obj.path === "/" ? "/" : `${obj.path}/*` }))
2711
2859
  );
2712
- routes.push({
2713
- element: /* @__PURE__ */ React.createElement(NotFoundErrorPage, null),
2714
- path: "/*"
2715
- });
2716
- return useRoutes(routes);
2860
+ const withNotFound = [
2861
+ ...routes,
2862
+ {
2863
+ path: "*",
2864
+ element: /* @__PURE__ */ React.createElement(NotFoundErrorPage, null)
2865
+ }
2866
+ ];
2867
+ return useRoutes(withNotFound);
2717
2868
  };
2718
2869
 
2719
2870
  export { AlertApiForwarder, ApiFactoryRegistry, ApiProvider, ApiResolver, AppThemeSelector, AtlassianAuth, BitbucketAuth, ErrorAlerter, ErrorApiForwarder, FeatureFlagged, FetchMiddlewares, FlatRoutes, GithubAuth, GitlabAuth, GoogleAuth, LocalStorageFeatureFlags, MicrosoftAuth, NoOpAnalyticsApi, OAuth2, OAuthRequestManager, OktaAuth, OneLoginAuth, SamlAuth, UnhandledErrorForwarder, UrlPatternDiscovery, WebStorage, createFetchApi, createSpecializedApp, defaultConfigLoader };