UNPKG

@sentry/nextjs

Version:
115 lines (112 loc) 4.37 kB
import { stripUrlQueryAndFragment, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_OP, parseBaggageHeader, browserPerformanceTimeOrigin, debug } from '@sentry/core'; import { startBrowserTracingNavigationSpan, getAbsoluteUrl, WINDOW, startBrowserTracingPageLoadSpan } from '@sentry/react'; import RouterImport from 'next/router'; import { DEBUG_BUILD } from '../../common/debug-build.js'; import { URL_TEMPLATE } from '@sentry/conventions/attributes'; const Router = RouterImport.events ? RouterImport : RouterImport.default; const globalObject = WINDOW; function extractNextDataTagInformation() { let nextData; const nextDataTag = globalObject.document.getElementById("__NEXT_DATA__"); if (nextDataTag?.innerHTML) { try { nextData = JSON.parse(nextDataTag.innerHTML); } catch { DEBUG_BUILD && debug.warn("Could not extract __NEXT_DATA__"); } } if (!nextData) { return {}; } const nextDataTagInfo = {}; const { page, query, props } = nextData; nextDataTagInfo.route = page; nextDataTagInfo.params = query; if (props?.pageProps) { nextDataTagInfo.sentryTrace = props.pageProps._sentryTraceData; nextDataTagInfo.baggage = props.pageProps._sentryBaggage; } return nextDataTagInfo; } function pagesRouterInstrumentPageLoad(client) { const { route, params, sentryTrace, baggage } = extractNextDataTagInformation(); const parsedBaggage = parseBaggageHeader(baggage); let name = route || globalObject.location.pathname; if (parsedBaggage?.["sentry-transaction"] && name === "/_error") { name = parsedBaggage["sentry-transaction"]; name = name.replace(/^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|TRACE|CONNECT)\s+/i, ""); } const origin = browserPerformanceTimeOrigin(); startBrowserTracingPageLoadSpan( client, { name, // pageload should always start at timeOrigin (and needs to be in s, not ms) startTime: origin ? origin / 1e3 : void 0, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload", [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.nextjs.pages_router_instrumentation", [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: route ? "route" : "url", ...route && { [URL_TEMPLATE]: route }, ...params && { ...params } } }, { sentryTrace, baggage } ); } function pagesRouterInstrumentNavigation(client) { Router.events.on("routeChangeStart", (navigationTarget) => { const strippedNavigationTarget = stripUrlQueryAndFragment(navigationTarget); const matchedRoute = getNextRouteFromPathname(strippedNavigationTarget); let newLocation; let spanSource; if (matchedRoute) { newLocation = matchedRoute; spanSource = "route"; } else { newLocation = strippedNavigationTarget; spanSource = "url"; } startBrowserTracingNavigationSpan( client, { name: newLocation, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation", [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.nextjs.pages_router_instrumentation", [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource, ...spanSource === "route" && { [URL_TEMPLATE]: newLocation } } }, { url: getAbsoluteUrl(navigationTarget) } ); }); } function getNextRouteFromPathname(pathname) { const pageRoutes = globalObject.__BUILD_MANIFEST?.sortedPages; if (!pageRoutes) { return; } return pageRoutes.find((route) => { const routeRegExp = convertNextRouteToRegExp(route); return pathname.match(routeRegExp); }); } function convertNextRouteToRegExp(route) { const routeParts = route.split("/"); let optionalCatchallWildcardRegex = ""; if (routeParts[routeParts.length - 1]?.match(/^\[\[\.\.\..+\]\]$/)) { routeParts.pop(); optionalCatchallWildcardRegex = "(?:/(.+?))?"; } const rejoinedRouteParts = routeParts.map( (routePart) => routePart.replace(/^\[\.\.\..+\]$/, "(.+?)").replace(/^\[.*\]$/, "([^/]+?)") // Replace route wildcards with lazy regex wildcards ).join("/"); return new RegExp( `^${rejoinedRouteParts}${optionalCatchallWildcardRegex}(?:/)?$` // optional slash at the end ); } export { pagesRouterInstrumentNavigation, pagesRouterInstrumentPageLoad }; //# sourceMappingURL=pagesRouterRoutingInstrumentation.js.map