diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5caa55f6c4e..356bc6fa31c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,10 @@
Server and client traces are now automatically connected, allowing you to see the full request lifecycle from server-side rendering through client-side hydration in a single trace.
+- **feat(tanstackstart-react): Add server-side route parametrization ([#21147](https://github.com/getsentry/sentry-javascript/pull/21147))**
+
+ Server transaction names are now parametrized automatically (e.g., `GET /users/123` becomes `GET /users/$userId`), improving transaction grouping in Sentry.
+
## 10.54.0
### Important Changes
diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.user.$id.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.user.$id.ts
new file mode 100644
index 000000000000..ce20e2f74ce1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/api.user.$id.ts
@@ -0,0 +1,13 @@
+import { createFileRoute } from '@tanstack/react-router';
+
+export const Route = createFileRoute('/api/user/$id')({
+ server: {
+ handlers: {
+ GET: async ({ params }) => {
+ return new Response(JSON.stringify({ id: params.id }), {
+ headers: { 'Content-Type': 'application/json' },
+ });
+ },
+ },
+ },
+});
diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/param.$id.tsx b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/param.$id.tsx
new file mode 100644
index 000000000000..43d32823168e
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/param.$id.tsx
@@ -0,0 +1,14 @@
+import { createFileRoute } from '@tanstack/react-router';
+
+export const Route = createFileRoute('/param/$id')({
+ component: ParamPage,
+});
+
+function ParamPage() {
+ const { id } = Route.useParams();
+ return (
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.$userId.tsx b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.$userId.tsx
new file mode 100644
index 000000000000..60f16f5cfece
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.$userId.tsx
@@ -0,0 +1,14 @@
+import { createFileRoute } from '@tanstack/react-router';
+
+export const Route = createFileRoute('/users/$userId')({
+ component: UserPage,
+});
+
+function UserPage() {
+ const { userId } = Route.useParams();
+ return (
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.tsx b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.tsx
new file mode 100644
index 000000000000..97684c05df19
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/src/routes/users.tsx
@@ -0,0 +1,13 @@
+import { Outlet, createFileRoute } from '@tanstack/react-router';
+
+export const Route = createFileRoute('/users')({
+ component: UsersLayout,
+});
+
+function UsersLayout() {
+ return (
+
+
+
+ );
+}
diff --git a/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/route-parametrization.test.ts b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/route-parametrization.test.ts
new file mode 100644
index 000000000000..39058a45bf29
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/tanstackstart-react/tests/route-parametrization.test.ts
@@ -0,0 +1,82 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/test-utils';
+
+const usesManagedTunnelRoute =
+ (process.env.E2E_TEST_TUNNEL_ROUTE_MODE ?? 'off') !== 'off' || process.env.E2E_TEST_CUSTOM_TUNNEL_ROUTE === '1';
+
+test.skip(usesManagedTunnelRoute, 'Default e2e suites run only in the proxy variant');
+
+test('should parametrize server and client transaction names for dynamic routes', async ({ page }) => {
+ const serverTxPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ typeof transactionEvent?.transaction === 'string' &&
+ transactionEvent.transaction.includes('/param/')
+ );
+ });
+
+ const clientTxPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'pageload' &&
+ typeof transactionEvent?.transaction === 'string' &&
+ transactionEvent.transaction.includes('/param/')
+ );
+ });
+
+ await page.goto('/param/42');
+
+ const serverTx = await serverTxPromise;
+ const clientTx = await clientTxPromise;
+
+ expect(serverTx.transaction).toBe('GET /param/$id');
+ expect(serverTx.transaction_info?.source).toBe('route');
+
+ expect(clientTx.transaction).toBe('/param/$id');
+ expect(clientTx.transaction_info?.source).toBe('route');
+});
+
+test('should parametrize server and client transaction names for nested dynamic routes', async ({ page }) => {
+ const serverTxPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ typeof transactionEvent?.transaction === 'string' &&
+ transactionEvent.transaction.includes('/users/')
+ );
+ });
+
+ const clientTxPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'pageload' &&
+ typeof transactionEvent?.transaction === 'string' &&
+ transactionEvent.transaction.includes('/users/')
+ );
+ });
+
+ await page.goto('/users/123');
+
+ const serverTx = await serverTxPromise;
+ const clientTx = await clientTxPromise;
+
+ expect(serverTx.transaction).toBe('GET /users/$userId');
+ expect(serverTx.transaction_info?.source).toBe('route');
+
+ expect(clientTx.transaction).toBe('/users/$userId');
+ expect(clientTx.transaction_info?.source).toBe('route');
+});
+
+test('should parametrize API route transaction names', async ({ baseURL }) => {
+ const serverTxPromise = waitForTransaction('tanstackstart-react', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'http.server' &&
+ typeof transactionEvent?.transaction === 'string' &&
+ transactionEvent.transaction.includes('/api/user/')
+ );
+ });
+
+ await fetch(`${baseURL}/api/user/456`);
+
+ const serverTx = await serverTxPromise;
+
+ expect(serverTx.transaction).toBe('GET /api/user/$id');
+ expect(serverTx.transaction_info?.source).toBe('route');
+});
diff --git a/packages/tanstackstart-react/src/server/routeParametrization.ts b/packages/tanstackstart-react/src/server/routeParametrization.ts
new file mode 100644
index 000000000000..bd1d1d904770
--- /dev/null
+++ b/packages/tanstackstart-react/src/server/routeParametrization.ts
@@ -0,0 +1,66 @@
+import { ATTR_HTTP_ROUTE } from '@opentelemetry/semantic-conventions';
+import {
+ escapeStringForRegex,
+ getActiveSpan,
+ getCurrentScope,
+ getRootSpan,
+ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
+ spanToJSON,
+ updateSpanName,
+} from '@sentry/core';
+
+function patternToRegex(pattern: string): RegExp {
+ const segments = pattern
+ .split('/')
+ .map(segment => {
+ if (segment.startsWith('$')) {
+ return '[^/]+';
+ }
+ return escapeStringForRegex(segment);
+ })
+ .join('/');
+ return new RegExp(`^${segments}$`);
+}
+
+/**
+ * Matches a URL pathname against a list of TanStack Start route patterns.
+ * Patterns use `$param` syntax for dynamic segments (e.g., `/users/$id`).
+ *
+ * Patterns are expected to be pre-sorted by specificity (more segments first, static before dynamic).
+ */
+export function matchUrlToRoutePattern(pathname: string, patterns: string[]): string | undefined {
+ const normalizedPathname = pathname.length > 1 ? pathname.replace(/\/$/, '') : pathname;
+ for (const pattern of patterns) {
+ if (patternToRegex(pattern).test(normalizedPathname)) {
+ return pattern;
+ }
+ }
+ return undefined;
+}
+
+/**
+ * Updates the active root span with a parametrized route name.
+ */
+export function updateSpanWithRouteParametrization(method: string, pathname: string, patterns: string[]): void {
+ const matchedPattern = matchUrlToRoutePattern(pathname, patterns);
+ if (!matchedPattern) {
+ return;
+ }
+
+ const activeSpan = getActiveSpan();
+ if (!activeSpan) {
+ return;
+ }
+
+ const rootSpan = getRootSpan(activeSpan);
+ const rootSpanData = spanToJSON(rootSpan).data;
+ if (rootSpanData?.[ATTR_HTTP_ROUTE]) {
+ return;
+ }
+
+ const transactionName = `${method} ${matchedPattern}`;
+ updateSpanName(rootSpan, transactionName);
+ rootSpan.setAttribute(ATTR_HTTP_ROUTE, matchedPattern);
+ rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
+ getCurrentScope().setTransactionName(transactionName);
+}
diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts
index 516604a94db1..73ea5604959e 100644
--- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts
+++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts
@@ -5,8 +5,11 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
startSpan,
} from '@sentry/node';
+import { updateSpanWithRouteParametrization } from './routeParametrization';
import { extractServerFunctionSha256 } from './utils';
+declare const __SENTRY_ROUTE_PATTERNS__: string[] | undefined;
+
export type ServerEntry = {
fetch: (request: Request, opts?: unknown) => Promise | Response;
};
@@ -161,6 +164,10 @@ export function wrapFetchWithSentry(serverEntry: ServerEntry): ServerEntry {
);
}
+ if (typeof __SENTRY_ROUTE_PATTERNS__ !== 'undefined') {
+ updateSpanWithRouteParametrization(method, url.pathname, __SENTRY_ROUTE_PATTERNS__);
+ }
+
return injectMetaTagsInResponse(await target.apply(thisArg, args));
} finally {
await flushIfServerless();
diff --git a/packages/tanstackstart-react/src/vite/routePatterns.ts b/packages/tanstackstart-react/src/vite/routePatterns.ts
new file mode 100644
index 000000000000..1c1b07fe0036
--- /dev/null
+++ b/packages/tanstackstart-react/src/vite/routePatterns.ts
@@ -0,0 +1,81 @@
+import * as fs from 'node:fs';
+import * as path from 'node:path';
+import type { Plugin } from 'vite';
+
+/**
+ * Extracts route patterns from TanStack Start's generated routeTree.gen.ts
+ * and replaces `__SENTRY_ROUTE_PATTERNS__` references with the extracted patterns.
+ *
+ * The route tree file is read during `transform` rather than `config` because
+ * TanStack Start generates it during the build.
+ */
+export function makeRoutePatternPlugin(): Plugin {
+ let resolvedRoot = '';
+
+ return {
+ name: 'sentry-tanstackstart-route-patterns',
+ enforce: 'post',
+
+ configResolved(config) {
+ resolvedRoot = config.root || process.cwd();
+ },
+
+ transform(code, id) {
+ // this is set in the `wrapFetchWithSentry` where the paths are getting replaced by their parametrized counterparts
+ // so this extraction should only happen once during the build (for the `wrapFetchWithSentry` file)
+ if (!code.includes('__SENTRY_ROUTE_PATTERNS__')) {
+ return null;
+ }
+
+ // extract the patterns from the route tree file
+ const routeTreePath = path.resolve(resolvedRoot, 'src/routeTree.gen.ts');
+ let patterns: string[] = [];
+ try {
+ if (fs.existsSync(routeTreePath)) {
+ patterns = extractRoutePatterns(fs.readFileSync(routeTreePath, 'utf-8'));
+ }
+ } catch {
+ // skip
+ }
+
+ return {
+ code: code.replace(/__SENTRY_ROUTE_PATTERNS__/g, JSON.stringify(patterns)),
+ map: null,
+ };
+ },
+ };
+}
+
+/**
+ * Extracts full route path patterns from the content of routeTree.gen.ts.
+ *
+ * Parses the `fullPaths` type union which contains the resolved full paths
+ * (e.g., `fullPaths: '/' | '/page-a' | '/users/$userId'`).
+ * This is more reliable than `path:` properties which can be relative for nested routes.
+ */
+export function extractRoutePatterns(content: string): string[] {
+ const fullPathsMatch = content.match(/fullPaths:\s*([\s\S]*?)(?:\n\s*\w|\n\})/);
+ if (!fullPathsMatch) {
+ return [];
+ }
+
+ const patterns: string[] = [];
+ const pathRegex = /['"]([^'"]+)['"]/g;
+ let match;
+ while ((match = pathRegex.exec(fullPathsMatch[1] || '')) !== null) {
+ if (match[1]) {
+ patterns.push(match[1]);
+ }
+ }
+
+ return [...new Set(patterns)].sort((a, b) => {
+ const aSegments = a.split('/');
+ const bSegments = b.split('/');
+ if (bSegments.length !== aSegments.length) {
+ return bSegments.length - aSegments.length;
+ }
+ const aDynamic = aSegments.filter(s => s.startsWith('$')).length;
+ const bDynamic = bSegments.filter(s => s.startsWith('$')).length;
+ return aDynamic - bDynamic;
+ });
+}
diff --git a/packages/tanstackstart-react/src/vite/sentryTanstackStart.ts b/packages/tanstackstart-react/src/vite/sentryTanstackStart.ts
index 5682b67050ae..a440e791e242 100644
--- a/packages/tanstackstart-react/src/vite/sentryTanstackStart.ts
+++ b/packages/tanstackstart-react/src/vite/sentryTanstackStart.ts
@@ -1,6 +1,7 @@
import type { BuildTimeOptionsBase } from '@sentry/core';
import type { Plugin } from 'vite';
import { makeAutoInstrumentMiddlewarePlugin } from './autoInstrumentMiddleware';
+import { makeRoutePatternPlugin } from './routePatterns';
import { makeAddSentryVitePlugin, makeEnableSourceMapsVitePlugin } from './sourceMaps';
import type { TunnelRouteOptions } from './tunnelRoute';
import { makeTunnelRoutePlugin } from './tunnelRoute';
@@ -84,18 +85,18 @@ export interface SentryTanstackStartOptions extends BuildTimeOptionsBase {
* @returns An array of Vite plugins
*/
export function sentryTanstackStart(options: SentryTanstackStartOptions = {}): Plugin[] {
- const tunnelRoutePlugin = options.tunnelRoute ? makeTunnelRoutePlugin(options.tunnelRoute, options.debug) : undefined;
+ const plugins: Plugin[] = [makeRoutePatternPlugin()];
+
+ if (options.tunnelRoute) {
+ plugins.push(makeTunnelRoutePlugin(options.tunnelRoute, options.debug));
+ }
// only add build-time plugins in production builds
if (process.env.NODE_ENV === 'development') {
- return tunnelRoutePlugin ? [tunnelRoutePlugin] : [];
+ return plugins;
}
- const plugins: Plugin[] = [...makeAddSentryVitePlugin(options)];
-
- if (tunnelRoutePlugin) {
- plugins.push(tunnelRoutePlugin);
- }
+ plugins.push(...makeAddSentryVitePlugin(options));
// middleware auto-instrumentation
if (options.autoInstrumentMiddleware !== false) {
diff --git a/packages/tanstackstart-react/test/server/routeParametrization.test.ts b/packages/tanstackstart-react/test/server/routeParametrization.test.ts
new file mode 100644
index 000000000000..88fd2643dc9b
--- /dev/null
+++ b/packages/tanstackstart-react/test/server/routeParametrization.test.ts
@@ -0,0 +1,46 @@
+import { describe, expect, it } from 'vitest';
+import { matchUrlToRoutePattern } from '../../src/server/routeParametrization';
+
+describe('matchUrlToRoutePattern', () => {
+ // Pre-sorted by specificity: more segments first, static before dynamic
+ const patterns = ['/users/$userId/posts/$postId', '/api/health', '/page-a', '/page-b/$id', '/'];
+
+ it('matches the root route', () => {
+ expect(matchUrlToRoutePattern('/', patterns)).toBe('/');
+ });
+
+ it('matches a static route', () => {
+ expect(matchUrlToRoutePattern('/page-a', patterns)).toBe('/page-a');
+ });
+
+ it('matches a single-param route', () => {
+ expect(matchUrlToRoutePattern('/page-b/42', patterns)).toBe('/page-b/$id');
+ });
+
+ it('matches a multi-param route', () => {
+ expect(matchUrlToRoutePattern('/users/123/posts/456', patterns)).toBe('/users/$userId/posts/$postId');
+ });
+
+ it('returns undefined for unmatched paths', () => {
+ expect(matchUrlToRoutePattern('/unknown', patterns)).toBeUndefined();
+ });
+
+ it('returns undefined for partially matched paths', () => {
+ expect(matchUrlToRoutePattern('/page-b', patterns)).toBeUndefined();
+ });
+
+ it('prefers static over dynamic matches when pre-sorted', () => {
+ const patternsWithOverlap = ['/page-b/special', '/page-b/$id'];
+ expect(matchUrlToRoutePattern('/page-b/special', patternsWithOverlap)).toBe('/page-b/special');
+ });
+
+ it('prefers more specific routes when pre-sorted', () => {
+ const patternsNested = ['/users/$id/profile', '/users/$id'];
+ expect(matchUrlToRoutePattern('/users/123/profile', patternsNested)).toBe('/users/$id/profile');
+ });
+
+ it('handles URLs with trailing slashes', () => {
+ expect(matchUrlToRoutePattern('/page-a/', patterns)).toBe('/page-a');
+ expect(matchUrlToRoutePattern('/page-b/42/', patterns)).toBe('/page-b/$id');
+ });
+});
diff --git a/packages/tanstackstart-react/test/vite/routePatterns.test.ts b/packages/tanstackstart-react/test/vite/routePatterns.test.ts
new file mode 100644
index 000000000000..07b1c2803bff
--- /dev/null
+++ b/packages/tanstackstart-react/test/vite/routePatterns.test.ts
@@ -0,0 +1,94 @@
+import { describe, expect, it } from 'vitest';
+import { extractRoutePatterns } from '../../src/vite/routePatterns';
+
+describe('extractRoutePatterns', () => {
+ it('extracts full path patterns from single-line fullPaths union', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths: '/' | '/page-a' | '/page-b/$id'
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns).toContain('/');
+ expect(patterns).toContain('/page-a');
+ expect(patterns).toContain('/page-b/$id');
+ expect(patterns).toHaveLength(3);
+ });
+
+ it('extracts full path patterns from multi-line fullPaths union', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths:
+ | '/'
+ | '/page-a'
+ | '/page-b/$id'
+ | '/api/error'
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns).toContain('/');
+ expect(patterns).toContain('/page-a');
+ expect(patterns).toContain('/page-b/$id');
+ expect(patterns).toContain('/api/error');
+ expect(patterns).toHaveLength(4);
+ });
+
+ it('returns empty array when fullPaths is not found', () => {
+ const patterns = extractRoutePatterns('');
+ expect(patterns).toEqual([]);
+ });
+
+ it('extracts nested route full paths correctly', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths:
+ | '/'
+ | '/users'
+ | '/users/$userId'
+ | '/users/$userId/posts/$postId'
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns).toContain('/users');
+ expect(patterns).toContain('/users/$userId');
+ expect(patterns).toContain('/users/$userId/posts/$postId');
+ });
+
+ it('sorts patterns by specificity: more segments first, static before dynamic', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths: '/' | '/page-b/$id' | '/page-b/special' | '/users/$id/profile' | '/users/$id'
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns).toEqual(['/users/$id/profile', '/page-b/special', '/page-b/$id', '/users/$id', '/']);
+ });
+
+ it('handles double-quoted paths (quoteStyle: "double")', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths: "/" | "/page-a" | "/page-b/$id"
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns).toContain('/');
+ expect(patterns).toContain('/page-a');
+ expect(patterns).toContain('/page-b/$id');
+ });
+
+ it('deduplicates patterns', () => {
+ const content = `
+export interface FileRouteTypes {
+ fullPaths: '/' | '/page-a' | '/page-a'
+ fileRoutesByTo: FileRoutesByTo
+}
+`;
+ const patterns = extractRoutePatterns(content);
+ expect(patterns.filter(p => p === '/page-a')).toHaveLength(1);
+ });
+});
diff --git a/packages/tanstackstart-react/test/vite/sentryTanstackStart.test.ts b/packages/tanstackstart-react/test/vite/sentryTanstackStart.test.ts
index 516edadd0bb0..cba4508de1a1 100644
--- a/packages/tanstackstart-react/test/vite/sentryTanstackStart.test.ts
+++ b/packages/tanstackstart-react/test/vite/sentryTanstackStart.test.ts
@@ -35,6 +35,16 @@ const mockTunnelRoutePlugin: Plugin = {
transform: vi.fn(),
};
+const mockRoutePatternPlugin: Plugin = {
+ name: 'sentry-tanstackstart-route-patterns',
+ enforce: 'pre',
+ config: vi.fn(),
+};
+
+vi.mock('../../src/vite/routePatterns', () => ({
+ makeRoutePatternPlugin: vi.fn(() => mockRoutePatternPlugin),
+}));
+
vi.mock('../../src/vite/sourceMaps', () => ({
makeAddSentryVitePlugin: vi.fn(() => [mockSourceMapsConfigPlugin, mockSentryVitePlugin]),
makeEnableSourceMapsVitePlugin: vi.fn(() => [mockEnableSourceMapsPlugin]),
@@ -62,7 +72,12 @@ describe('sentryTanstackStart()', () => {
it('returns source maps plugins in production mode', () => {
const plugins = sentryTanstackStart({ autoInstrumentMiddleware: false });
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin, mockEnableSourceMapsPlugin]);
+ expect(plugins).toEqual([
+ mockRoutePatternPlugin,
+ mockSourceMapsConfigPlugin,
+ mockSentryVitePlugin,
+ mockEnableSourceMapsPlugin,
+ ]);
});
it('returns no plugins in development mode when tunnelRoute is not configured', () => {
@@ -70,7 +85,7 @@ describe('sentryTanstackStart()', () => {
const plugins = sentryTanstackStart({ autoInstrumentMiddleware: false });
- expect(plugins).toEqual([]);
+ expect(plugins).toEqual([mockRoutePatternPlugin]);
});
it('returns only the tunnel route plugin in development mode when tunnelRoute is configured', () => {
@@ -81,7 +96,7 @@ describe('sentryTanstackStart()', () => {
tunnelRoute: { allowedDsns: ['https://public@o0.ingest.sentry.io/0'] },
});
- expect(plugins).toEqual([mockTunnelRoutePlugin]);
+ expect(plugins).toEqual([mockRoutePatternPlugin, mockTunnelRoutePlugin]);
});
it('returns Sentry Vite plugins but not enable source maps plugin when sourcemaps.disable is true', () => {
@@ -90,7 +105,7 @@ describe('sentryTanstackStart()', () => {
sourcemaps: { disable: true },
});
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
+ expect(plugins).toEqual([mockRoutePatternPlugin, mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
});
it('returns Sentry Vite plugins but not enable source maps plugin when sourcemaps.disable is "disable-upload"', () => {
@@ -99,7 +114,7 @@ describe('sentryTanstackStart()', () => {
sourcemaps: { disable: 'disable-upload' },
});
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
+ expect(plugins).toEqual([mockRoutePatternPlugin, mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
});
it('returns Sentry Vite plugins and enable source maps plugin when sourcemaps.disable is false', () => {
@@ -108,7 +123,12 @@ describe('sentryTanstackStart()', () => {
sourcemaps: { disable: false },
});
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin, mockEnableSourceMapsPlugin]);
+ expect(plugins).toEqual([
+ mockRoutePatternPlugin,
+ mockSourceMapsConfigPlugin,
+ mockSentryVitePlugin,
+ mockEnableSourceMapsPlugin,
+ ]);
});
});
@@ -116,7 +136,12 @@ describe('sentryTanstackStart()', () => {
it('includes middleware plugin by default', () => {
const plugins = sentryTanstackStart({ sourcemaps: { disable: true } });
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin, mockMiddlewarePlugin]);
+ expect(plugins).toEqual([
+ mockRoutePatternPlugin,
+ mockSourceMapsConfigPlugin,
+ mockSentryVitePlugin,
+ mockMiddlewarePlugin,
+ ]);
});
it('includes middleware plugin when autoInstrumentMiddleware is true', () => {
@@ -125,7 +150,12 @@ describe('sentryTanstackStart()', () => {
sourcemaps: { disable: true },
});
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin, mockMiddlewarePlugin]);
+ expect(plugins).toEqual([
+ mockRoutePatternPlugin,
+ mockSourceMapsConfigPlugin,
+ mockSentryVitePlugin,
+ mockMiddlewarePlugin,
+ ]);
});
it('does not include middleware plugin when autoInstrumentMiddleware is false', () => {
@@ -134,7 +164,7 @@ describe('sentryTanstackStart()', () => {
sourcemaps: { disable: true },
});
- expect(plugins).toEqual([mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
+ expect(plugins).toEqual([mockRoutePatternPlugin, mockSourceMapsConfigPlugin, mockSentryVitePlugin]);
});
it('passes correct options to makeAutoInstrumentMiddlewarePlugin', () => {
@@ -161,9 +191,10 @@ describe('sentryTanstackStart()', () => {
});
expect(plugins).toEqual([
+ mockRoutePatternPlugin,
+ mockTunnelRoutePlugin,
mockSourceMapsConfigPlugin,
mockSentryVitePlugin,
- mockTunnelRoutePlugin,
mockMiddlewarePlugin,
]);
});