reactrouterv3.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const browser = require('@sentry/browser');
  3. const core = require('@sentry/core');
  4. // Many of the types below had to be mocked out to prevent typescript issues
  5. // these types are required for correct functionality.
  6. /**
  7. * A browser tracing integration that uses React Router v3 to instrument navigations.
  8. * Expects `history` (and optionally `routes` and `matchPath`) to be passed as options.
  9. */
  10. function reactRouterV3BrowserTracingIntegration(
  11. options,
  12. ) {
  13. const integration = browser.browserTracingIntegration({
  14. ...options,
  15. instrumentPageLoad: false,
  16. instrumentNavigation: false,
  17. });
  18. const { history, routes, match, instrumentPageLoad = true, instrumentNavigation = true } = options;
  19. return {
  20. ...integration,
  21. afterAllSetup(client) {
  22. integration.afterAllSetup(client);
  23. const startPageloadCallback = (startSpanOptions) => {
  24. browser.startBrowserTracingPageLoadSpan(client, startSpanOptions);
  25. return undefined;
  26. };
  27. const startNavigationCallback = (startSpanOptions) => {
  28. browser.startBrowserTracingNavigationSpan(client, startSpanOptions);
  29. return undefined;
  30. };
  31. // eslint-disable-next-line deprecation/deprecation
  32. const instrumentation = reactRouterV3Instrumentation(history, routes, match);
  33. // Now instrument page load & navigation with correct settings
  34. instrumentation(startPageloadCallback, instrumentPageLoad, false);
  35. instrumentation(startNavigationCallback, false, instrumentNavigation);
  36. },
  37. };
  38. }
  39. /**
  40. * Creates routing instrumentation for React Router v3
  41. * Works for React Router >= 3.2.0 and < 4.0.0
  42. *
  43. * @param history object from the `history` library
  44. * @param routes a list of all routes, should be
  45. * @param match `Router.match` utility
  46. *
  47. * @deprecated Use `reactRouterV3BrowserTracingIntegration()` instead
  48. */
  49. function reactRouterV3Instrumentation(
  50. history,
  51. routes,
  52. match,
  53. ) {
  54. return (
  55. startTransaction,
  56. startTransactionOnPageLoad = true,
  57. startTransactionOnLocationChange = true,
  58. ) => {
  59. let activeTransaction;
  60. let prevName;
  61. // Have to use window.location because history.location might not be defined.
  62. if (startTransactionOnPageLoad && browser.WINDOW && browser.WINDOW.location) {
  63. normalizeTransactionName(
  64. routes,
  65. browser.WINDOW.location ,
  66. match,
  67. (localName, source = 'url') => {
  68. prevName = localName;
  69. activeTransaction = startTransaction({
  70. name: prevName,
  71. attributes: {
  72. [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload',
  73. [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.react.reactrouter_v3',
  74. [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
  75. },
  76. });
  77. },
  78. );
  79. }
  80. if (startTransactionOnLocationChange && history.listen) {
  81. history.listen(location => {
  82. if (location.action === 'PUSH' || location.action === 'POP') {
  83. if (activeTransaction) {
  84. activeTransaction.end();
  85. }
  86. normalizeTransactionName(routes, location, match, (localName, source = 'url') => {
  87. prevName = localName;
  88. const attributes = {
  89. [core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation',
  90. [core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v3',
  91. [core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source,
  92. };
  93. activeTransaction = startTransaction({
  94. name: prevName,
  95. attributes,
  96. });
  97. });
  98. }
  99. });
  100. }
  101. };
  102. }
  103. /**
  104. * Normalize transaction names using `Router.match`
  105. */
  106. function normalizeTransactionName(
  107. appRoutes,
  108. location,
  109. match,
  110. callback,
  111. ) {
  112. let name = location.pathname;
  113. match(
  114. {
  115. location,
  116. routes: appRoutes,
  117. },
  118. (error, _redirectLocation, renderProps) => {
  119. if (error || !renderProps) {
  120. return callback(name);
  121. }
  122. const routePath = getRouteStringFromRoutes(renderProps.routes || []);
  123. if (routePath.length === 0 || routePath === '/*') {
  124. return callback(name);
  125. }
  126. name = routePath;
  127. return callback(name, 'route');
  128. },
  129. );
  130. }
  131. /**
  132. * Generate route name from array of routes
  133. */
  134. function getRouteStringFromRoutes(routes) {
  135. if (!Array.isArray(routes) || routes.length === 0) {
  136. return '';
  137. }
  138. const routesWithPaths = routes.filter((route) => !!route.path);
  139. let index = -1;
  140. for (let x = routesWithPaths.length - 1; x >= 0; x--) {
  141. const route = routesWithPaths[x];
  142. if (route.path && route.path.startsWith('/')) {
  143. index = x;
  144. break;
  145. }
  146. }
  147. return routesWithPaths
  148. .slice(index)
  149. .filter(({ path }) => !!path)
  150. .map(({ path }) => path)
  151. .join('');
  152. }
  153. exports.reactRouterV3BrowserTracingIntegration = reactRouterV3BrowserTracingIntegration;
  154. exports.reactRouterV3Instrumentation = reactRouterV3Instrumentation;
  155. //# sourceMappingURL=reactrouterv3.js.map