@sentry/wizard 3.23.2 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -2
- package/dist/package.json +2 -2
- package/dist/src/nextjs/nextjs-wizard.js +1 -1
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/templates.d.ts +1 -1
- package/dist/src/nextjs/templates.js +4 -2
- package/dist/src/nextjs/templates.js.map +1 -1
- package/dist/src/react-native/glob.js +24 -4
- package/dist/src/react-native/glob.js.map +1 -1
- package/dist/src/remix/codemods/express-server.d.ts +0 -4
- package/dist/src/remix/codemods/express-server.js +1 -80
- package/dist/src/remix/codemods/express-server.js.map +1 -1
- package/dist/src/remix/codemods/handle-error.js +2 -2
- package/dist/src/remix/codemods/handle-error.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +62 -19
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-example.js +1 -1
- package/dist/src/remix/sdk-example.js.map +1 -1
- package/dist/src/remix/sdk-setup.d.ts +5 -3
- package/dist/src/remix/sdk-setup.js +98 -38
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/remix/templates.d.ts +1 -1
- package/dist/src/remix/templates.js +1 -1
- package/dist/src/remix/templates.js.map +1 -1
- package/dist/src/remix/utils.d.ts +9 -3
- package/dist/src/remix/utils.js +39 -10
- package/dist/src/remix/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/nextjs/nextjs-wizard.ts +1 -1
- package/src/nextjs/templates.ts +32 -4
- package/src/react-native/glob.ts +1 -1
- package/src/remix/codemods/express-server.ts +0 -121
- package/src/remix/codemods/handle-error.ts +3 -2
- package/src/remix/remix-wizard.ts +45 -17
- package/src/remix/sdk-example.ts +0 -2
- package/src/remix/sdk-setup.ts +125 -33
- package/src/remix/templates.ts +1 -1
- package/src/remix/utils.ts +57 -7
package/src/remix/sdk-setup.ts
CHANGED
|
@@ -15,18 +15,26 @@ import clack from '@clack/prompts';
|
|
|
15
15
|
import chalk from 'chalk';
|
|
16
16
|
import { gte, minVersion } from 'semver';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import {
|
|
19
|
+
builders,
|
|
20
|
+
generateCode,
|
|
21
|
+
loadFile,
|
|
22
|
+
parseModule,
|
|
23
|
+
writeFile,
|
|
24
|
+
// @ts-expect-error - magicast is ESM and TS complains about that. It works though
|
|
25
|
+
} from 'magicast';
|
|
26
|
+
import type { PackageDotJson } from '../utils/package-json';
|
|
27
|
+
import { getPackageVersion } from '../utils/package-json';
|
|
28
|
+
import {
|
|
29
|
+
getAfterImportsInsertionIndex,
|
|
30
|
+
hasSentryContent,
|
|
31
|
+
serverHasInstrumentationImport,
|
|
32
|
+
} from './utils';
|
|
22
33
|
import { instrumentRootRouteV1 } from './codemods/root-v1';
|
|
23
34
|
import { instrumentRootRouteV2 } from './codemods/root-v2';
|
|
24
35
|
import { instrumentHandleError } from './codemods/handle-error';
|
|
25
|
-
import {
|
|
26
|
-
findCustomExpressServerImplementation,
|
|
27
|
-
instrumentExpressCreateRequestHandler,
|
|
28
|
-
} from './codemods/express-server';
|
|
29
36
|
import { getPackageDotJson } from '../utils/clack-utils';
|
|
37
|
+
import { findCustomExpressServerImplementation } from './codemods/express-server';
|
|
30
38
|
|
|
31
39
|
export type PartialRemixConfig = {
|
|
32
40
|
unstable_dev?: boolean;
|
|
@@ -87,7 +95,8 @@ function insertClientInitCall(
|
|
|
87
95
|
});
|
|
88
96
|
|
|
89
97
|
const originalHooksModAST = originalHooksMod.$ast as Program;
|
|
90
|
-
const initCallInsertionIndex =
|
|
98
|
+
const initCallInsertionIndex =
|
|
99
|
+
getAfterImportsInsertionIndex(originalHooksModAST);
|
|
91
100
|
|
|
92
101
|
originalHooksModAST.body.splice(
|
|
93
102
|
initCallInsertionIndex,
|
|
@@ -98,26 +107,76 @@ function insertClientInitCall(
|
|
|
98
107
|
);
|
|
99
108
|
}
|
|
100
109
|
|
|
101
|
-
function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
)
|
|
110
|
+
export async function createServerInstrumentationFile(dsn: string) {
|
|
111
|
+
// create an empty file named `instrument.server.mjs`
|
|
112
|
+
const instrumentationFile = 'instrumentation.server.mjs';
|
|
113
|
+
const instrumentationFileMod = parseModule('');
|
|
114
|
+
|
|
115
|
+
instrumentationFileMod.imports.$add({
|
|
116
|
+
from: '@sentry/remix',
|
|
117
|
+
imported: '*',
|
|
118
|
+
local: 'Sentry',
|
|
119
|
+
});
|
|
120
|
+
|
|
105
121
|
const initCall = builders.functionCall('Sentry.init', {
|
|
106
122
|
dsn,
|
|
107
123
|
tracesSampleRate: 1.0,
|
|
124
|
+
autoInstrumentRemix: true,
|
|
108
125
|
});
|
|
109
126
|
|
|
110
|
-
const
|
|
127
|
+
const instrumentationFileModAST = instrumentationFileMod.$ast as Program;
|
|
111
128
|
|
|
112
|
-
const initCallInsertionIndex =
|
|
129
|
+
const initCallInsertionIndex = getAfterImportsInsertionIndex(
|
|
130
|
+
instrumentationFileModAST,
|
|
131
|
+
);
|
|
113
132
|
|
|
114
|
-
|
|
133
|
+
instrumentationFileModAST.body.splice(
|
|
115
134
|
initCallInsertionIndex,
|
|
116
135
|
0,
|
|
117
136
|
// @ts-expect-error - string works here because the AST is proxified by magicast
|
|
118
137
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
119
138
|
generateCode(initCall).code,
|
|
120
139
|
);
|
|
140
|
+
|
|
141
|
+
await writeFile(instrumentationFileModAST, instrumentationFile);
|
|
142
|
+
|
|
143
|
+
return instrumentationFile;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function insertServerInstrumentationFile(dsn: string) {
|
|
147
|
+
const instrumentationFile = await createServerInstrumentationFile(dsn);
|
|
148
|
+
|
|
149
|
+
const expressServerPath = await findCustomExpressServerImplementation();
|
|
150
|
+
|
|
151
|
+
if (!expressServerPath) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const originalExpressServerMod = await loadFile(expressServerPath);
|
|
156
|
+
|
|
157
|
+
if (
|
|
158
|
+
serverHasInstrumentationImport(
|
|
159
|
+
expressServerPath,
|
|
160
|
+
originalExpressServerMod.$code,
|
|
161
|
+
)
|
|
162
|
+
) {
|
|
163
|
+
clack.log.warn(
|
|
164
|
+
`File ${chalk.cyan(
|
|
165
|
+
path.basename(expressServerPath),
|
|
166
|
+
)} already contains instrumentation import.
|
|
167
|
+
Skipping adding instrumentation functionality to ${chalk.cyan(
|
|
168
|
+
path.basename(expressServerPath),
|
|
169
|
+
)}.`,
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
originalExpressServerMod.$code = `import './${instrumentationFile}';\n${originalExpressServerMod.$code}`;
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(expressServerPath, originalExpressServerMod.$code);
|
|
178
|
+
|
|
179
|
+
return true;
|
|
121
180
|
}
|
|
122
181
|
|
|
123
182
|
export function isRemixV2(
|
|
@@ -294,8 +353,56 @@ export async function initializeSentryOnEntryClient(
|
|
|
294
353
|
);
|
|
295
354
|
}
|
|
296
355
|
|
|
297
|
-
export async function
|
|
298
|
-
|
|
356
|
+
export async function updateStartScript(instrumentationFile: string) {
|
|
357
|
+
const packageJson = await getPackageDotJson();
|
|
358
|
+
|
|
359
|
+
if (!packageJson.scripts || !packageJson.scripts.start) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
"Couldn't find a `start` script in your package.json. Please add one manually.",
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (packageJson.scripts.start.includes('NODE_OPTIONS')) {
|
|
366
|
+
clack.log.warn(
|
|
367
|
+
`Found existing NODE_OPTIONS in ${chalk.cyan(
|
|
368
|
+
'start',
|
|
369
|
+
)} script. Skipping adding Sentry initialization.`,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
!packageJson.scripts.start.includes('remix-serve') &&
|
|
377
|
+
// Adding a following empty space not to match a path that includes `node`
|
|
378
|
+
!packageJson.scripts.start.includes('node ')
|
|
379
|
+
) {
|
|
380
|
+
clack.log.warn(
|
|
381
|
+
`Found a ${chalk.cyan('start')} script that doesn't use ${chalk.cyan(
|
|
382
|
+
'remix-serve',
|
|
383
|
+
)} or ${chalk.cyan('node')}. Skipping adding Sentry initialization.`,
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const startCommand = packageJson.scripts.start;
|
|
390
|
+
|
|
391
|
+
packageJson.scripts.start = `NODE_OPTIONS='--import ./${instrumentationFile}' ${startCommand}`;
|
|
392
|
+
|
|
393
|
+
await fs.promises.writeFile(
|
|
394
|
+
path.join(process.cwd(), 'package.json'),
|
|
395
|
+
JSON.stringify(packageJson, null, 2),
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
clack.log.success(
|
|
399
|
+
`Successfully updated ${chalk.cyan('start')} script in ${chalk.cyan(
|
|
400
|
+
'package.json',
|
|
401
|
+
)} to include Sentry initialization on start.`,
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export async function instrumentSentryOnEntryServer(
|
|
299
406
|
isV2: boolean,
|
|
300
407
|
isTS: boolean,
|
|
301
408
|
): Promise<void> {
|
|
@@ -319,8 +426,6 @@ export async function initializeSentryOnEntryServer(
|
|
|
319
426
|
local: 'Sentry',
|
|
320
427
|
});
|
|
321
428
|
|
|
322
|
-
insertServerInitCall(dsn, originalEntryServerMod);
|
|
323
|
-
|
|
324
429
|
if (isV2) {
|
|
325
430
|
const handleErrorInstrumented = instrumentHandleError(
|
|
326
431
|
originalEntryServerMod,
|
|
@@ -347,16 +452,3 @@ export async function initializeSentryOnEntryServer(
|
|
|
347
452
|
)}.`,
|
|
348
453
|
);
|
|
349
454
|
}
|
|
350
|
-
|
|
351
|
-
export async function instrumentExpressServer() {
|
|
352
|
-
const expressServerPath = await findCustomExpressServerImplementation();
|
|
353
|
-
|
|
354
|
-
if (!expressServerPath) {
|
|
355
|
-
clack.log.warn(
|
|
356
|
-
`Could not find custom Express server implementation. Please instrument it manually.`,
|
|
357
|
-
);
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
await instrumentExpressCreateRequestHandler(expressServerPath);
|
|
362
|
-
}
|
package/src/remix/templates.ts
CHANGED
|
@@ -6,6 +6,6 @@ export const ERROR_BOUNDARY_TEMPLATE_V2 = `const ErrorBoundary = () => {
|
|
|
6
6
|
`;
|
|
7
7
|
|
|
8
8
|
export const HANDLE_ERROR_TEMPLATE_V2 = `function handleError(error, { request }) {
|
|
9
|
-
Sentry.captureRemixServerException(error, 'remix.server', request);
|
|
9
|
+
Sentry.captureRemixServerException(error, 'remix.server', request, true);
|
|
10
10
|
}
|
|
11
11
|
`;
|
package/src/remix/utils.ts
CHANGED
|
@@ -7,16 +7,23 @@ import clack from '@clack/prompts';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { PackageDotJson, hasPackageInstalled } from '../utils/package-json';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
export const POSSIBLE_SERVER_INSTRUMENTATION_PATHS = [
|
|
11
|
+
'./instrumentation',
|
|
12
|
+
'./instrumentation.server',
|
|
13
|
+
];
|
|
14
|
+
|
|
11
15
|
export function hasSentryContent(
|
|
12
16
|
fileName: string,
|
|
13
17
|
fileContent: string,
|
|
18
|
+
expectedContent = '@sentry/remix',
|
|
14
19
|
): boolean {
|
|
15
|
-
const includesContent = fileContent.includes(
|
|
20
|
+
const includesContent = fileContent.includes(expectedContent);
|
|
16
21
|
|
|
17
22
|
if (includesContent) {
|
|
18
23
|
clack.log.warn(
|
|
19
|
-
`File ${chalk.cyan(
|
|
24
|
+
`File ${chalk.cyan(
|
|
25
|
+
path.basename(fileName),
|
|
26
|
+
)} already contains ${expectedContent}.
|
|
20
27
|
Skipping adding Sentry functionality to ${chalk.cyan(
|
|
21
28
|
path.basename(fileName),
|
|
22
29
|
)}.`,
|
|
@@ -26,14 +33,57 @@ Skipping adding Sentry functionality to ${chalk.cyan(
|
|
|
26
33
|
return includesContent;
|
|
27
34
|
}
|
|
28
35
|
|
|
36
|
+
export function serverHasInstrumentationImport(
|
|
37
|
+
serverFileName: string,
|
|
38
|
+
serverFileContent: string,
|
|
39
|
+
): boolean {
|
|
40
|
+
const includesServerInstrumentationImport =
|
|
41
|
+
POSSIBLE_SERVER_INSTRUMENTATION_PATHS.some((path) =>
|
|
42
|
+
serverFileContent.includes(path),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (includesServerInstrumentationImport) {
|
|
46
|
+
clack.log.warn(
|
|
47
|
+
`File ${chalk.cyan(
|
|
48
|
+
path.basename(serverFileName),
|
|
49
|
+
)} already contains instrumentation import.
|
|
50
|
+
Skipping adding instrumentation functionality to ${chalk.cyan(
|
|
51
|
+
path.basename(serverFileName),
|
|
52
|
+
)}.`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return includesServerInstrumentationImport;
|
|
57
|
+
}
|
|
58
|
+
|
|
29
59
|
/**
|
|
30
|
-
* We want to insert the init call on top of the file
|
|
60
|
+
* We want to insert the init call on top of the file, before any other imports.
|
|
31
61
|
*/
|
|
32
|
-
export function
|
|
62
|
+
export function getBeforeImportsInsertionIndex(
|
|
33
63
|
originalHooksModAST: Program,
|
|
34
64
|
): number {
|
|
35
|
-
for (let x = originalHooksModAST.body.length - 1; x
|
|
36
|
-
if (
|
|
65
|
+
for (let x = 0; x < originalHooksModAST.body.length - 1; x++) {
|
|
66
|
+
if (
|
|
67
|
+
originalHooksModAST.body[x].type === 'ImportDeclaration' &&
|
|
68
|
+
// @ts-expect-error - source is available in body
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
70
|
+
originalHooksModAST.body[x].source.value === '@sentry/remix'
|
|
71
|
+
) {
|
|
72
|
+
return x + 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* We want to insert the handleError function just after all imports
|
|
81
|
+
*/
|
|
82
|
+
export function getAfterImportsInsertionIndex(
|
|
83
|
+
originalEntryServerModAST: Program,
|
|
84
|
+
): number {
|
|
85
|
+
for (let x = originalEntryServerModAST.body.length - 1; x >= 0; x--) {
|
|
86
|
+
if (originalEntryServerModAST.body[x].type === 'ImportDeclaration') {
|
|
37
87
|
return x + 1;
|
|
38
88
|
}
|
|
39
89
|
}
|