@@ -5,9 +5,58 @@ type AutoInstrumentMiddlewareOptions = {
55 debug ?: boolean ;
66} ;
77
8+ type WrapResult = {
9+ code : string ;
10+ didWrap : boolean ;
11+ skipped : string [ ] ;
12+ } ;
13+
14+ /**
15+ * Core function that wraps middleware arrays matching the given regex.
16+ */
17+ function wrapMiddlewareArrays ( code : string , id : string , debug : boolean , regex : RegExp ) : WrapResult {
18+ const skipped : string [ ] = [ ] ;
19+ let didWrap = false ;
20+
21+ const transformed = code . replace ( regex , ( match : string , key : string , contents : string ) => {
22+ const objContents = arrayToObjectShorthand ( contents ) ;
23+ if ( objContents ) {
24+ didWrap = true ;
25+ if ( debug ) {
26+ // eslint-disable-next-line no-console
27+ console . log ( `[Sentry] Auto-wrapping ${ key } in ${ id } ` ) ;
28+ }
29+ return `${ key } : wrapMiddlewaresWithSentry(${ objContents } )` ;
30+ }
31+ // Track middlewares that couldn't be auto-wrapped
32+ // Skip if we matched whitespace only
33+ if ( contents . trim ( ) ) {
34+ skipped . push ( key ) ;
35+ }
36+ return match ;
37+ } ) ;
38+
39+ return { code : transformed , didWrap, skipped } ;
40+ }
41+
42+ /**
43+ * Wraps global middleware arrays (requestMiddleware, functionMiddleware) in createStart() files.
44+ */
45+ export function wrapGlobalMiddleware ( code : string , id : string , debug : boolean ) : WrapResult {
46+ return wrapMiddlewareArrays ( code , id , debug , / ( r e q u e s t M i d d l e w a r e | f u n c t i o n M i d d l e w a r e ) \s * : \s * \[ ( [ ^ \] ] * ) \] / g) ;
47+ }
48+
49+ /**
50+ * Wraps route middleware arrays in createFileRoute() files.
51+ */
52+ export function wrapRouteMiddleware ( code : string , id : string , debug : boolean ) : WrapResult {
53+ return wrapMiddlewareArrays ( code , id , debug , / ( m i d d l e w a r e ) \s * : \s * \[ ( [ ^ \] ] * ) \] / g) ;
54+ }
55+
856/**
9- * A Vite plugin that automatically instruments TanStack Start middlewares
10- * by wrapping `requestMiddleware` and `functionMiddleware` arrays in `createStart()`.
57+ * A Vite plugin that automatically instruments TanStack Start middlewares:
58+ * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()`
59+ * - `middleware` arrays in `createFileRoute()` route definitions
1160 */
1261export function makeAutoInstrumentMiddlewarePlugin ( options : AutoInstrumentMiddlewareOptions = { } ) : Plugin {
1362 const { enabled = true , debug = false } = options ;
@@ -26,9 +75,11 @@ export function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddle
2675 return null ;
2776 }
2877
29- // Only wrap requestMiddleware and functionMiddleware in createStart()
30- // createStart() should always be in a file named start.ts
31- if ( ! id . includes ( 'start' ) || ! code . includes ( 'createStart(' ) ) {
78+ // Detect file types that should be instrumented
79+ const isStartFile = id . includes ( 'start' ) && code . includes ( 'createStart(' ) ;
80+ const isRouteFile = code . includes ( 'createFileRoute(' ) && / m i d d l e w a r e \s * : \s * \[ / . test ( code ) ;
81+
82+ if ( ! isStartFile && ! isRouteFile ) {
3283 return null ;
3384 }
3485
@@ -41,26 +92,26 @@ export function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddle
4192 let needsImport = false ;
4293 const skippedMiddlewares : string [ ] = [ ] ;
4394
44- transformed = transformed . replace (
45- / ( r e q u e s t M i d d l e w a r e | f u n c t i o n M i d d l e w a r e ) \s * : \s * \[ ( [ ^ \] ] * ) \] / g ,
46- ( match : string , key : string , contents : string ) => {
47- const objContents = arrayToObjectShorthand ( contents ) ;
48- if ( objContents ) {
49- needsImport = true ;
50- if ( debug ) {
51- // eslint-disable-next-line no-console
52- console . log ( `[Sentry] Auto-wrapping ${ key } in ${ id } ` ) ;
53- }
54- return ` ${ key } : wrapMiddlewaresWithSentry( ${ objContents } )` ;
55- }
56- // Track middlewares that couldn't be auto-wrapped
57- // Skip if we matched whitespace only
58- if ( contents . trim ( ) ) {
59- skippedMiddlewares . push ( key ) ;
60- }
61- return match ;
62- } ,
63- ) ;
95+ switch ( true ) {
96+ // global middleware
97+ case isStartFile : {
98+ const result = wrapGlobalMiddleware ( transformed , id , debug ) ;
99+ transformed = result . code ;
100+ needsImport = needsImport || result . didWrap ;
101+ skippedMiddlewares . push ( ... result . skipped ) ;
102+ break ;
103+ }
104+ // route middleware
105+ case isRouteFile : {
106+ const result = wrapRouteMiddleware ( transformed , id , debug ) ;
107+ transformed = result . code ;
108+ needsImport = needsImport || result . didWrap ;
109+ skippedMiddlewares . push ( ... result . skipped ) ;
110+ break ;
111+ }
112+ default :
113+ break ;
114+ }
64115
65116 // Warn about middlewares that couldn't be auto-wrapped
66117 if ( skippedMiddlewares . length > 0 ) {
@@ -76,17 +127,7 @@ export function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddle
76127 return null ;
77128 }
78129
79- const sentryImport = "import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n" ;
80-
81- // Check for 'use server' or 'use client' directives, these need to be before any imports
82- const directiveMatch = transformed . match ( / ^ ( [ ' " ] ) u s e ( c l i e n t | s e r v e r ) \1; ? \s * \n ? / ) ;
83- if ( directiveMatch ) {
84- // Insert import after the directive
85- const directive = directiveMatch [ 0 ] ;
86- transformed = directive + sentryImport + transformed . slice ( directive . length ) ;
87- } else {
88- transformed = sentryImport + transformed ;
89- }
130+ transformed = addSentryImport ( transformed ) ;
90131
91132 return { code : transformed , map : null } ;
92133 } ,
@@ -117,3 +158,26 @@ export function arrayToObjectShorthand(contents: string): string | null {
117158
118159 return `{ ${ uniqueItems . join ( ', ' ) } }` ;
119160}
161+
162+ /**
163+ * Adds the wrapMiddlewaresWithSentry import to the code.
164+ * Handles 'use client' and 'use server' directives by inserting the import after them.
165+ */
166+ export function addSentryImport ( code : string ) : string {
167+ const sentryImport = "import { wrapMiddlewaresWithSentry } from '@sentry/tanstackstart-react';\n" ;
168+
169+ // Don't add the import if it already exists
170+ if ( code . includes ( sentryImport . trimEnd ( ) ) ) {
171+ return code ;
172+ }
173+
174+ // Check for 'use server' or 'use client' directives, these need to be before any imports
175+ const directiveMatch = code . match ( / ^ ( [ ' " ] ) u s e ( c l i e n t | s e r v e r ) \1; ? \s * \n ? / ) ;
176+
177+ if ( ! directiveMatch ) {
178+ return sentryImport + code ;
179+ }
180+
181+ const directive = directiveMatch [ 0 ] ;
182+ return directive + sentryImport + code . slice ( directive . length ) ;
183+ }
0 commit comments