Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(tanstackstart-react): Auto-instrument server function middleware#19001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
b877f5140a7ba43660c729a8026be985b3ef7f4c22092858fce8dbc7330d7f8b262eaa14873c3a910011c12298725e86bc1b2c92fb94322ae7f8e23df42f71File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -11,6 +11,12 @@ type WrapResult = { | ||
| skipped: string[]; | ||
| }; | ||
| type FileTransformState = { | ||
| code: string; | ||
| needsImport: boolean; | ||
| skippedMiddlewares: string[]; | ||
| }; | ||
| /** | ||
| * Core function that wraps middleware arrays matching the given regex. | ||
| */ | ||
| @@ -26,6 +32,10 @@ function wrapMiddlewareArrays(code: string, id: string, debug: boolean, regex: R | ||
| // eslint-disable-next-line no-console | ||
| console.log(`[Sentry] Auto-wrapping ${key} in ${id}`); | ||
| } | ||
| // Handle method call syntax like `.middleware([...])` vs object property syntax like `middleware: [...]` | ||
| if (key.endsWith('(')) { | ||
| return `${key}wrapMiddlewaresWithSentry(${objContents}))`; | ||
| } | ||
| return `${key}: wrapMiddlewaresWithSentry(${objContents})`; | ||
| } | ||
| // Track middlewares that couldn't be auto-wrapped | ||
| @@ -53,6 +63,30 @@ export function wrapRouteMiddleware(code: string, id: string, debug: boolean): W | ||
| return wrapMiddlewareArrays(code, id, debug, /(middleware)\s*:\s*\[([^\]]*)\]/g); | ||
nicohrubec marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| /** | ||
| * Wraps middleware arrays in createServerFn().middleware([...]) calls. | ||
| */ | ||
| export function wrapServerFnMiddleware(code: string, id: string, debug: boolean): WrapResult { | ||
| return wrapMiddlewareArrays(code, id, debug, /(\.middleware\s*\()\s*\[([^\]]*)\]\s*\)/g); | ||
| } | ||
| /** | ||
| * Applies a wrap function to the current state and returns the updated state. | ||
| */ | ||
| function applyWrap( | ||
| state: FileTransformState, | ||
| wrapFn: (code: string, id: string, debug: boolean) => WrapResult, | ||
| id: string, | ||
| debug: boolean, | ||
| ): FileTransformState { | ||
| const result = wrapFn(state.code, id, debug); | ||
| return { | ||
| code: result.code, | ||
| needsImport: state.needsImport || result.didWrap, | ||
| skippedMiddlewares: [...state.skippedMiddlewares, ...result.skipped], | ||
| }; | ||
| } | ||
| /** | ||
| * A Vite plugin that automatically instruments TanStack Start middlewares: | ||
| * - `requestMiddleware` and `functionMiddleware` arrays in `createStart()` | ||
| @@ -78,8 +112,9 @@ export function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddle | ||
| // Detect file types that should be instrumented | ||
| const isStartFile = id.includes('start') && code.includes('createStart('); | ||
| const isRouteFile = code.includes('createFileRoute(') && /middleware\s*:\s*\[/.test(code); | ||
| const isServerFnFile = code.includes('createServerFn') && /\.middleware\s*\(\s*\[/.test(code); | ||
| if (!isStartFile && !isRouteFile) { | ||
| if (!isStartFile && !isRouteFile && !isServerFnFile) { | ||
| return null; | ||
| } | ||
| @@ -88,48 +123,38 @@ export function makeAutoInstrumentMiddlewarePlugin(options: AutoInstrumentMiddle | ||
| return null; | ||
nicohrubec marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| let transformed = code; | ||
| let needsImport = false; | ||
| const skippedMiddlewares: string[] = []; | ||
| switch (true) { | ||
| // global middleware | ||
| case isStartFile: { | ||
| const result = wrapGlobalMiddleware(transformed, id, debug); | ||
| transformed = result.code; | ||
| needsImport = needsImport || result.didWrap; | ||
| skippedMiddlewares.push(...result.skipped); | ||
| break; | ||
| } | ||
| // route middleware | ||
| case isRouteFile: { | ||
| const result = wrapRouteMiddleware(transformed, id, debug); | ||
| transformed = result.code; | ||
| needsImport = needsImport || result.didWrap; | ||
| skippedMiddlewares.push(...result.skipped); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| let fileTransformState: FileTransformState = { | ||
| code, | ||
| needsImport: false, | ||
| skippedMiddlewares: [], | ||
| }; | ||
| // Wrap middlewares | ||
| if (isStartFile) { | ||
| fileTransformState = applyWrap(fileTransformState, wrapGlobalMiddleware, id, debug); | ||
| } | ||
| if (isRouteFile) { | ||
| fileTransformState = applyWrap(fileTransformState, wrapRouteMiddleware, id, debug); | ||
| } | ||
| if (isServerFnFile) { | ||
| fileTransformState = applyWrap(fileTransformState, wrapServerFnMiddleware, id, debug); | ||
| } | ||
| // Warn about middlewares that couldn't be auto-wrapped | ||
| if (skippedMiddlewares.length > 0) { | ||
| if (fileTransformState.skippedMiddlewares.length > 0) { | ||
| // eslint-disable-next-line no-console | ||
| console.warn( | ||
| `[Sentry] Could not auto-instrument ${skippedMiddlewares.join(' and ')} in ${id}. ` + | ||
| `[Sentry] Could not auto-instrument ${fileTransformState.skippedMiddlewares.join(' and ')} in ${id}. ` + | ||
| 'To instrument these middlewares, use wrapMiddlewaresWithSentry() manually. ', | ||
| ); | ||
| } | ||
| // We didn't wrap any middlewares, so we don't need to import the wrapMiddlewaresWithSentry function | ||
| if (!needsImport) { | ||
| if (!fileTransformState.needsImport) { | ||
| return null; | ||
| } | ||
| transformed = addSentryImport(transformed); | ||
| return { code: transformed, map: null }; | ||
| return { code: addSentryImport(fileTransformState.code), map: null }; | ||
| }, | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
m: I think these whitespaces got added by accident
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no actually, this was wrongly formatted (we always have the following paragraphs after the feature title indented with two spaces)