chenzhaoyang
2025-12-17 063da0bf961e1d35e25dc107f883f7492f4c5a7c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import React from "react";
import { SentryRoute as Route } from "../config/Sentry";
import { RouteWithStaticFallback } from "../routes/RouteWithStaticFallback";
 
export const RouteContext = React.createContext();
 
const resolveWithConfig = (routes, config) => {
  return routes instanceof Function ? routes(config) : routes;
};
 
/**
 * Resolves a set of components in a set of routes usign component
 * properties.
 *
 * Component can has:
 * @param {String|Function} title Title of a page. Can be either a string or a function that accepts route props as an argument
 * @param {String} path Path of the page. It is always relative to the parent
 * @param {boolean} exact Exact match of the route
 * @param {Array} routes A list of raw routes
 * @param {Object} pages Object with component that define pages. If provided, the function will run recursively
 *
 * The function itself accepts only pages argument.
 * @param {Object} pages Object with components that define pages
 */
export const pageSetToRoutes = (pages, config) => {
  const pageProcessor = ([name, page]) => {
    const route = {
      path: page.path,
    };
 
    route.exact = !!page.exact;
    route.modal = !!page.modal;
 
    if (page.title) route.title = page.title;
    if (page.render) route.render = page.render;
 
    if (page instanceof React.Component || page instanceof Function) {
      if (name && /Layout/.test(name)) route.layout = page;
      else route.component = page;
    } else {
      route.component = page.component;
      route.layout = page.layout;
    }
 
    if (page.pages) {
      route.routes = pageSetToRoutes(resolveWithConfig(page.pages, config), config);
    } else if (page.routes) {
      route.routes = pageSetToRoutes(resolveWithConfig(page.routes, config), config);
    }
 
    if (route.component?.context) route.context = route.component.context;
 
    return route;
  };
 
  try {
    if (Array.isArray(pages)) {
      return pages.map((page) => pageProcessor([null, page]));
    }
    return Object.entries(pages).map(pageProcessor);
  } catch (err) {
    console.log(err);
    return [];
  }
};
 
/**
 *
 * @param {Array} routes List of routes
 * @param {*} props Props to pass to every component and layout
 * @param {*} onRender Callback that runs on every route render
 */
export const resolveRoutes = (routes, props) => {
  const resolver = (route, parentPath) => {
    const { component: Component, layout: Layout, path, modal, routes: _routes, ...rest } = route;
 
    const fullPath = parentPath ? `${parentPath}${path}` : path;
 
    if (_routes) {
      const resolvedNestedRoutes = processRoutes(_routes, fullPath);
 
      const RouteComponent = (routeProps) => {
        const children = [];
 
        // If a component provided for the set of routes/pages,
        // we render one level higher to preserve nesting
        if (Component) {
          children.push(
            processRoutes(
              [
                {
                  path,
                  modal,
                  ...rest,
                  component: Component,
                },
              ],
              parentPath,
            ),
          );
        }
 
        children.push(...resolvedNestedRoutes);
 
        return Layout ? <Layout {...routeProps}>{children}</Layout> : children;
      };
 
      return <RouteWithStaticFallback key={fullPath} path={fullPath} render={RouteComponent} />;
    }
    const routeProps = { path: fullPath, modal: !!Component.modal };
 
    return <Route {...routeProps} key={fullPath} exact render={() => <Component {...(props ?? {})} />} {...rest} />;
  };
 
  const processRoutes = (routes, fullPath) => {
    return routes.map((route) => resolver(route, fullPath));
  };
 
  return processRoutes(routes);
};