route.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import type {IndexRouteProps, PlainRoute, RouteProps} from 'react-router';
  2. import {IndexRoute as BaseIndexRoute, Route as BaseRoute} from 'react-router';
  3. import {USING_CUSTOMER_DOMAIN} from 'sentry/constants';
  4. import withDomainRedirect from 'sentry/utils/withDomainRedirect';
  5. import withDomainRequired from 'sentry/utils/withDomainRequired';
  6. // This module contains customized react-router route components used to
  7. // construct the app routing tree.
  8. //
  9. // The primary customization here relates to supporting rendering dual-routes for customer domains
  10. type CustomProps = {
  11. /**
  12. * Human readable route name. This is primarily used in the settings routes.
  13. */
  14. name?: string;
  15. /**
  16. * Ensure this route renders two routes, one for the "org" path which
  17. * includes the :orgId slug, and one without.
  18. *
  19. * Setting this to `true` will prefix the provided path of the secondary
  20. * route with:
  21. *
  22. * /organizations/:orgId
  23. *
  24. * Setting this will wrap the two routes in withDomainRequired and
  25. * withDomainRedirect respectively.
  26. */
  27. withOrgPath?: boolean;
  28. };
  29. interface SentryRouteProps extends React.PropsWithChildren<RouteProps & CustomProps> {}
  30. type RouteElement = React.ReactElement<SentryRouteProps>;
  31. // The original createRouteFromReactElement extracted from the base route. This
  32. // is not properly typed hence the ts-ignore.
  33. //
  34. // @ts-ignore
  35. const createRouteFromReactElement = BaseRoute.createRouteFromReactElement;
  36. /**
  37. * Customized React Router Route configuration component.
  38. */
  39. const Route = BaseRoute as React.ComponentClass<SentryRouteProps>;
  40. // We override the createRouteFromReactElement property to provide support for
  41. // the withOrgPath property.
  42. //
  43. // XXX(epurkhiser): It is important to note! The `Route` component is a
  44. // CONFIGURATION ONLY COMPONENT. It DOES NOT render! This function is part of
  45. // the react-router magic internals that are used to build the route tree by
  46. // traversing the component tree, that is why this logic lives here and not
  47. // inside a custom Route component.
  48. //
  49. // To understand deeper how this works, see [0].
  50. //
  51. // When `withOrgPath` is provided to the Route configuration component the
  52. // react-router router builder will use this function which splits the single
  53. // Route into two, one for the route with :orgId and one for the new-style
  54. // route.
  55. //
  56. // [0]: https://github.com/remix-run/react-router/blob/850de933444d260bfc5460135d308f9d74b52c97/modules/RouteUtils.js#L15
  57. //
  58. // @ts-ignore
  59. Route.createRouteFromReactElement = function (element: RouteElement): PlainRoute {
  60. const {withOrgPath, component, path} = element.props;
  61. if (!withOrgPath) {
  62. return createRouteFromReactElement(element);
  63. }
  64. const childRoutes: PlainRoute[] = [
  65. {
  66. ...createRouteFromReactElement(element),
  67. path: `/organizations/:orgId${path}`,
  68. component: withDomainRedirect(component ?? NoOp),
  69. },
  70. ];
  71. if (USING_CUSTOMER_DOMAIN) {
  72. childRoutes.push({
  73. ...createRouteFromReactElement(element),
  74. path,
  75. component: withDomainRequired(component ?? NoOp),
  76. });
  77. }
  78. return {childRoutes};
  79. };
  80. function NoOp({children}: {children: JSX.Element}) {
  81. return children;
  82. }
  83. const IndexRoute = BaseIndexRoute as React.ComponentClass<IndexRouteProps & CustomProps>;
  84. export {Route, IndexRoute};