Skip to content

Commit b3c2eb9

Browse files
committed
Get Remix version from build object.
1 parent 27fed30 commit b3c2eb9

File tree

3 files changed

+102
-95
lines changed

3 files changed

+102
-95
lines changed

packages/remix/src/index.server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { NodeOptions } from '@sentry/node';
33
import { configureScope, getCurrentHub, init as nodeInit } from '@sentry/node';
44
import { logger } from '@sentry/utils';
55

6-
import { getRemixVersionFromPkg } from './utils/futureFlags';
76
import { instrumentServer } from './utils/instrumentServer';
87
import { buildMetadata } from './utils/metadata';
98
import type { RemixOptions } from './utils/remixOptions';
@@ -77,7 +76,7 @@ export function init(options: RemixOptions): void {
7776
return;
7877
}
7978

80-
instrumentServer(getRemixVersionFromPkg() === 2);
79+
instrumentServer();
8180

8281
nodeInit(options as NodeOptions);
8382

packages/remix/src/utils/futureFlags.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GLOBAL_OBJ, loadModule, parseSemver } from '@sentry/utils';
1+
import { GLOBAL_OBJ } from '@sentry/utils';
22

33
import type { FutureConfig, ServerBuild } from './vendor/types';
44

@@ -42,15 +42,17 @@ export function getFutureFlagsServer(build: ServerBuild): FutureConfig | undefin
4242
}
4343

4444
/**
45-
* Read Remix version from module package.json
45+
* Learn Remix version from the server build object
46+
* V2 Server builds have a non-optional `mode` property
4647
*
4748
* @returns The major version number
4849
*/
49-
export function getRemixVersionFromPkg(): number | undefined {
50-
const pkg = loadModule<{ version: string }>('@remix-run/react/package.json');
51-
const version = pkg ? pkg.version : '0.0.0';
50+
export function getRemixVersionFromBuild(build: ServerBuild): number {
51+
if ('mode' in build) {
52+
return 2;
53+
}
5254

53-
return parseSemver(version).major;
55+
return 1;
5456
}
5557

5658
/**

packages/remix/src/utils/instrumentServer.ts

Lines changed: 93 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
tracingContextFromHeaders,
1414
} from '@sentry/utils';
1515

16-
import { getFutureFlagsServer, getRemixVersionFromPkg } from './futureFlags';
16+
import { getFutureFlagsServer, getRemixVersionFromBuild } from './futureFlags';
1717
import { extractData, getRequestMatch, isDeferredData, isResponse, json, matchServerRoutes } from './vendor/response';
1818
import type {
1919
AppData,
@@ -33,7 +33,6 @@ import type {
3333
import { normalizeRemixRequest } from './web-fetch';
3434

3535
let FUTURE_FLAGS: FutureConfig | undefined;
36-
let IS_REMIX_V2: boolean | undefined;
3736

3837
// Flag to track if the core request handler is instrumented.
3938
export let isRequestHandlerWrapped = false;
@@ -118,55 +117,60 @@ export async function captureRemixServerException(err: unknown, name: string, re
118117
});
119118
}
120119

121-
function makeWrappedDocumentRequestFunction(
122-
origDocumentRequestFunction: HandleDocumentRequestFunction,
123-
): HandleDocumentRequestFunction {
124-
return async function (
125-
this: unknown,
126-
request: Request,
127-
responseStatusCode: number,
128-
responseHeaders: Headers,
129-
context: EntryContext,
130-
loadContext?: Record<string, unknown>,
131-
): Promise<Response> {
132-
let res: Response;
120+
function makeWrappedDocumentRequestFunction(remixVersion: number) {
121+
return function (origDocumentRequestFunction: HandleDocumentRequestFunction): HandleDocumentRequestFunction {
122+
return async function (
123+
this: unknown,
124+
request: Request,
125+
responseStatusCode: number,
126+
responseHeaders: Headers,
127+
context: EntryContext,
128+
loadContext?: Record<string, unknown>,
129+
): Promise<Response> {
130+
let res: Response;
133131

134-
const activeTransaction = getActiveTransaction();
135-
136-
try {
137-
const span = activeTransaction?.startChild({
138-
op: 'function.remix.document_request',
139-
origin: 'auto.function.remix',
140-
description: activeTransaction.name,
141-
tags: {
142-
method: request.method,
143-
url: request.url,
144-
},
145-
});
132+
const activeTransaction = getActiveTransaction();
146133

147-
res = await origDocumentRequestFunction.call(
148-
this,
149-
request,
150-
responseStatusCode,
151-
responseHeaders,
152-
context,
153-
loadContext,
154-
);
134+
try {
135+
const span = activeTransaction?.startChild({
136+
op: 'function.remix.document_request',
137+
origin: 'auto.function.remix',
138+
description: activeTransaction.name,
139+
tags: {
140+
method: request.method,
141+
url: request.url,
142+
},
143+
});
144+
145+
res = await origDocumentRequestFunction.call(
146+
this,
147+
request,
148+
responseStatusCode,
149+
responseHeaders,
150+
context,
151+
loadContext,
152+
);
153+
154+
span?.finish();
155+
} catch (err) {
156+
if (!FUTURE_FLAGS?.v2_errorBoundary && remixVersion !== 2) {
157+
await captureRemixServerException(err, 'documentRequest', request);
158+
}
155159

156-
span?.finish();
157-
} catch (err) {
158-
if (!FUTURE_FLAGS?.v2_errorBoundary && !IS_REMIX_V2) {
159-
await captureRemixServerException(err, 'documentRequest', request);
160+
throw err;
160161
}
161162

162-
throw err;
163-
}
164-
165-
return res;
163+
return res;
164+
};
166165
};
167166
}
168167

169-
function makeWrappedDataFunction(origFn: DataFunction, id: string, name: 'action' | 'loader'): DataFunction {
168+
function makeWrappedDataFunction(
169+
origFn: DataFunction,
170+
id: string,
171+
name: 'action' | 'loader',
172+
remixVersion: number,
173+
): DataFunction {
170174
return async function (this: unknown, args: DataFunctionArgs): Promise<Response | AppData> {
171175
let res: Response | AppData;
172176
const activeTransaction = getActiveTransaction();
@@ -192,7 +196,7 @@ function makeWrappedDataFunction(origFn: DataFunction, id: string, name: 'action
192196
currentScope.setSpan(activeTransaction);
193197
span?.finish();
194198
} catch (err) {
195-
if (!FUTURE_FLAGS?.v2_errorBoundary && !IS_REMIX_V2) {
199+
if (!FUTURE_FLAGS?.v2_errorBoundary && remixVersion !== 2) {
196200
await captureRemixServerException(err, name, args.request);
197201
}
198202

@@ -204,15 +208,15 @@ function makeWrappedDataFunction(origFn: DataFunction, id: string, name: 'action
204208
}
205209

206210
const makeWrappedAction =
207-
(id: string) =>
211+
(id: string, remixVersion: number) =>
208212
(origAction: DataFunction): DataFunction => {
209-
return makeWrappedDataFunction(origAction, id, 'action');
213+
return makeWrappedDataFunction(origAction, id, 'action', remixVersion);
210214
};
211215

212216
const makeWrappedLoader =
213-
(id: string) =>
217+
(id: string, remixVersion: number) =>
214218
(origLoader: DataFunction): DataFunction => {
215-
return makeWrappedDataFunction(origLoader, id, 'loader');
219+
return makeWrappedDataFunction(origLoader, id, 'loader', remixVersion);
216220
};
217221

218222
function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string } {
@@ -235,44 +239,46 @@ function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string }
235239
return {};
236240
}
237241

238-
function makeWrappedRootLoader(origLoader: DataFunction): DataFunction {
239-
return async function (this: unknown, args: DataFunctionArgs): Promise<Response | AppData> {
240-
const res = await origLoader.call(this, args);
241-
const traceAndBaggage = getTraceAndBaggage();
242-
const remixVersion = getRemixVersionFromPkg();
243-
244-
if (isDeferredData(res)) {
245-
return {
246-
...res.data,
247-
...traceAndBaggage,
248-
remixVersion,
249-
};
250-
}
242+
function makeWrappedRootLoader(remixVersion: number) {
243+
return function (origLoader: DataFunction): DataFunction {
244+
return async function (this: unknown, args: DataFunctionArgs): Promise<Response | AppData> {
245+
const res = await origLoader.call(this, args);
246+
const traceAndBaggage = getTraceAndBaggage();
247+
248+
if (isDeferredData(res)) {
249+
return {
250+
...res.data,
251+
...traceAndBaggage,
252+
remixVersion,
253+
};
254+
}
251255

252-
if (isResponse(res)) {
253-
// Note: `redirect` and `catch` responses do not have bodies to extract.
254-
// We skip injection of trace and baggage in those cases.
255-
// For `redirect`, a valid internal redirection target will have the trace and baggage injected.
256-
if (isRedirectResponse(res) || isCatchResponse(res)) {
257-
__DEBUG_BUILD__ && logger.warn('Skipping injection of trace and baggage as the response does not have a body');
258-
return res;
259-
} else {
260-
const data = await extractData(res);
261-
262-
if (typeof data === 'object') {
263-
return json(
264-
{ ...data, ...traceAndBaggage, remixVersion },
265-
{ headers: res.headers, statusText: res.statusText, status: res.status },
266-
);
267-
} else {
256+
if (isResponse(res)) {
257+
// Note: `redirect` and `catch` responses do not have bodies to extract.
258+
// We skip injection of trace and baggage in those cases.
259+
// For `redirect`, a valid internal redirection target will have the trace and baggage injected.
260+
if (isRedirectResponse(res) || isCatchResponse(res)) {
268261
__DEBUG_BUILD__ &&
269-
logger.warn('Skipping injection of trace and baggage as the response body is not an object');
262+
logger.warn('Skipping injection of trace and baggage as the response does not have a body');
270263
return res;
264+
} else {
265+
const data = await extractData(res);
266+
267+
if (typeof data === 'object') {
268+
return json(
269+
{ ...data, ...traceAndBaggage, remixVersion },
270+
{ headers: res.headers, statusText: res.statusText, status: res.status },
271+
);
272+
} else {
273+
__DEBUG_BUILD__ &&
274+
logger.warn('Skipping injection of trace and baggage as the response body is not an object');
275+
return res;
276+
}
271277
}
272278
}
273-
}
274279

275-
return { ...res, ...traceAndBaggage, remixVersion };
280+
return { ...res, ...traceAndBaggage, remixVersion };
281+
};
276282
};
277283
}
278284

@@ -405,6 +411,8 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui
405411
export function instrumentBuild(build: ServerBuild): ServerBuild {
406412
const routes: ServerRouteManifest = {};
407413

414+
const remixVersion = getRemixVersionFromBuild(build);
415+
408416
const wrappedEntry = { ...build.entry, module: { ...build.entry.module } };
409417

410418
// Not keeping boolean flags like it's done for `requestHandler` functions,
@@ -413,20 +421,20 @@ export function instrumentBuild(build: ServerBuild): ServerBuild {
413421
// We should be able to wrap them, as they may not be wrapped before.
414422
const defaultExport = wrappedEntry.module.default as undefined | WrappedFunction;
415423
if (defaultExport && !defaultExport.__sentry_original__) {
416-
fill(wrappedEntry.module, 'default', makeWrappedDocumentRequestFunction);
424+
fill(wrappedEntry.module, 'default', makeWrappedDocumentRequestFunction(remixVersion));
417425
}
418426

419427
for (const [id, route] of Object.entries(build.routes)) {
420428
const wrappedRoute = { ...route, module: { ...route.module } };
421429

422430
const routeAction = wrappedRoute.module.action as undefined | WrappedFunction;
423431
if (routeAction && !routeAction.__sentry_original__) {
424-
fill(wrappedRoute.module, 'action', makeWrappedAction(id));
432+
fill(wrappedRoute.module, 'action', makeWrappedAction(id, remixVersion));
425433
}
426434

427435
const routeLoader = wrappedRoute.module.loader as undefined | WrappedFunction;
428436
if (routeLoader && !routeLoader.__sentry_original__) {
429-
fill(wrappedRoute.module, 'loader', makeWrappedLoader(id));
437+
fill(wrappedRoute.module, 'loader', makeWrappedLoader(id, remixVersion));
430438
}
431439

432440
// Entry module should have a loader function to provide `sentry-trace` and `baggage`
@@ -437,7 +445,7 @@ export function instrumentBuild(build: ServerBuild): ServerBuild {
437445
}
438446

439447
// We want to wrap the root loader regardless of whether it's already wrapped before.
440-
fill(wrappedRoute.module, 'loader', makeWrappedRootLoader);
448+
fill(wrappedRoute.module, 'loader', makeWrappedRootLoader(remixVersion));
441449
}
442450

443451
routes[id] = wrappedRoute;
@@ -466,9 +474,7 @@ function makeWrappedCreateRequestHandler(
466474
* Monkey-patch Remix's `createRequestHandler` from `@remix-run/server-runtime`
467475
* which Remix Adapters (https://remix.run/docs/en/v1/api/remix) use underneath.
468476
*/
469-
export function instrumentServer(isRemixV2?: boolean): void {
470-
IS_REMIX_V2 = isRemixV2;
471-
477+
export function instrumentServer(): void {
472478
const pkg = loadModule<{ createRequestHandler: CreateRequestHandlerFunction }>('@remix-run/server-runtime');
473479

474480
if (!pkg) {

0 commit comments

Comments
 (0)