@sentry/nextjs
Version:
Official Sentry SDK for Next.js
118 lines (114 loc) • 4.39 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const core = require('@sentry/core');
const react = require('@sentry/react');
const RouterImport = require('next/router');
const debugBuild = require('../../common/debug-build.js');
const attributes = require('@sentry/conventions/attributes');
const Router = RouterImport.default.events ? RouterImport.default : RouterImport.default.default;
const globalObject = react.WINDOW;
function extractNextDataTagInformation() {
let nextData;
const nextDataTag = globalObject.document.getElementById("__NEXT_DATA__");
if (nextDataTag?.innerHTML) {
try {
nextData = JSON.parse(nextDataTag.innerHTML);
} catch {
debugBuild.DEBUG_BUILD && core.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 = core.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 = core.browserPerformanceTimeOrigin();
react.startBrowserTracingPageLoadSpan(
client,
{
name,
// pageload should always start at timeOrigin (and needs to be in s, not ms)
startTime: origin ? origin / 1e3 : void 0,
attributes: {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "pageload",
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.pageload.nextjs.pages_router_instrumentation",
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: route ? "route" : "url",
...route && { [attributes.URL_TEMPLATE]: route },
...params && { ...params }
}
},
{ sentryTrace, baggage }
);
}
function pagesRouterInstrumentNavigation(client) {
Router.events.on("routeChangeStart", (navigationTarget) => {
const strippedNavigationTarget = core.stripUrlQueryAndFragment(navigationTarget);
const matchedRoute = getNextRouteFromPathname(strippedNavigationTarget);
let newLocation;
let spanSource;
if (matchedRoute) {
newLocation = matchedRoute;
spanSource = "route";
} else {
newLocation = strippedNavigationTarget;
spanSource = "url";
}
react.startBrowserTracingNavigationSpan(
client,
{
name: newLocation,
attributes: {
[core.SEMANTIC_ATTRIBUTE_SENTRY_OP]: "navigation",
[core.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.navigation.nextjs.pages_router_instrumentation",
[core.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource,
...spanSource === "route" && { [attributes.URL_TEMPLATE]: newLocation }
}
},
{ url: react.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
);
}
exports.pagesRouterInstrumentNavigation = pagesRouterInstrumentNavigation;
exports.pagesRouterInstrumentPageLoad = pagesRouterInstrumentPageLoad;
//# sourceMappingURL=pagesRouterRoutingInstrumentation.js.map