@sentry/wizard 3.13.0 → 3.14.1

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/dist/package.json +2 -2
  3. package/dist/src/android/android-wizard.js +2 -4
  4. package/dist/src/android/android-wizard.js.map +1 -1
  5. package/dist/src/apple/apple-wizard.js +1 -1
  6. package/dist/src/apple/apple-wizard.js.map +1 -1
  7. package/dist/src/nextjs/nextjs-wizard.d.ts +1 -0
  8. package/dist/src/nextjs/nextjs-wizard.js +252 -161
  9. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  10. package/dist/src/nextjs/utils.d.ts +1 -0
  11. package/dist/src/nextjs/utils.js +25 -0
  12. package/dist/src/nextjs/utils.js.map +1 -0
  13. package/dist/src/remix/remix-wizard.js +5 -7
  14. package/dist/src/remix/remix-wizard.js.map +1 -1
  15. package/dist/src/remix/sdk-setup.js +10 -4
  16. package/dist/src/remix/sdk-setup.js.map +1 -1
  17. package/dist/src/sourcemaps/sourcemaps-wizard.js +1 -1
  18. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  19. package/dist/src/sveltekit/sdk-setup.js +7 -3
  20. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  21. package/dist/src/sveltekit/sveltekit-wizard.js +6 -10
  22. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  23. package/dist/src/telemetry.d.ts +1 -0
  24. package/dist/src/telemetry.js +27 -12
  25. package/dist/src/telemetry.js.map +1 -1
  26. package/dist/src/utils/clack-utils.d.ts +11 -1
  27. package/dist/src/utils/clack-utils.js +190 -126
  28. package/dist/src/utils/clack-utils.js.map +1 -1
  29. package/dist/src/utils/package-manager.js +12 -7
  30. package/dist/src/utils/package-manager.js.map +1 -1
  31. package/package.json +2 -2
  32. package/src/android/android-wizard.ts +4 -5
  33. package/src/apple/apple-wizard.ts +2 -2
  34. package/src/nextjs/nextjs-wizard.ts +262 -195
  35. package/src/nextjs/utils.ts +21 -0
  36. package/src/remix/remix-wizard.ts +7 -9
  37. package/src/remix/sdk-setup.ts +12 -3
  38. package/src/sourcemaps/sourcemaps-wizard.ts +2 -2
  39. package/src/sveltekit/sdk-setup.ts +6 -3
  40. package/src/sveltekit/sveltekit-wizard.ts +9 -12
  41. package/src/telemetry.ts +22 -11
  42. package/src/utils/clack-utils.ts +177 -107
  43. package/src/utils/package-manager.ts +12 -6
@@ -7,11 +7,13 @@ import * as fs from 'fs';
7
7
  import { builders, generateCode, parseModule } from 'magicast';
8
8
  import * as path from 'path';
9
9
 
10
+ import * as Sentry from '@sentry/node';
11
+
10
12
  import {
11
13
  abort,
12
14
  abortIfCancelled,
13
15
  addSentryCliConfig,
14
- confirmContinueEvenThoughNoGitRepo,
16
+ confirmContinueIfNoOrDirtyGitRepo,
15
17
  ensurePackageIsInstalled,
16
18
  getOrAskForProjectData,
17
19
  getPackageDotJson,
@@ -19,7 +21,7 @@ import {
19
21
  isUsingTypeScript,
20
22
  printWelcome,
21
23
  } from '../utils/clack-utils';
22
- import { WizardOptions } from '../utils/types';
24
+ import { SentryProjectData, WizardOptions } from '../utils/types';
23
25
  import {
24
26
  getNextjsConfigCjsAppendix,
25
27
  getNextjsConfigCjsTemplate,
@@ -31,88 +33,158 @@ import {
31
33
  getSentryExampleAppDirApiRoute,
32
34
  getSentryExamplePageContents,
33
35
  } from './templates';
36
+ import { traceStep, withTelemetry } from '../telemetry';
37
+ import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
38
+ import { getNextJsVersionBucket } from './utils';
39
+
40
+ export function runNextjsWizard(options: WizardOptions) {
41
+ return withTelemetry(
42
+ {
43
+ enabled: options.telemetryEnabled,
44
+ integration: 'nextjs',
45
+ },
46
+ () => runNextjsWizardWithTelemetry(options),
47
+ );
48
+ }
34
49
 
35
- // eslint-disable-next-line complexity
36
- export async function runNextjsWizard(options: WizardOptions): Promise<void> {
50
+ export async function runNextjsWizardWithTelemetry(
51
+ options: WizardOptions,
52
+ ): Promise<void> {
37
53
  printWelcome({
38
54
  wizardName: 'Sentry Next.js Wizard',
39
55
  promoCode: options.promoCode,
56
+ telemetryEnabled: options.telemetryEnabled,
40
57
  });
41
58
 
42
- await confirmContinueEvenThoughNoGitRepo();
59
+ await confirmContinueIfNoOrDirtyGitRepo();
43
60
 
44
61
  const packageJson = await getPackageDotJson();
62
+
45
63
  await ensurePackageIsInstalled(packageJson, 'next', 'Next.js');
46
64
 
65
+ const nextVersion = getPackageVersion('next', packageJson);
66
+ Sentry.setTag('nextjs-version', getNextJsVersionBucket(nextVersion));
67
+
47
68
  const { selectedProject, authToken, selfHosted, sentryUrl } =
48
69
  await getOrAskForProjectData(options, 'javascript-nextjs');
49
70
 
71
+ const sdkAlreadyInstalled = hasPackageInstalled(
72
+ '@sentry/nextjs',
73
+ packageJson,
74
+ );
75
+ Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
76
+
50
77
  await installPackage({
51
78
  packageName: '@sentry/nextjs',
52
79
  alreadyInstalled: !!packageJson?.dependencies?.['@sentry/nextjs'],
53
80
  });
54
81
 
55
- const typeScriptDetected = isUsingTypeScript();
82
+ await traceStep('configure-sdk', async () =>
83
+ createOrMergeNextJsFiles(selectedProject, selfHosted, sentryUrl),
84
+ );
56
85
 
57
- const configVariants = ['server', 'client', 'edge'] as const;
86
+ await traceStep('create-example-page', async () =>
87
+ createExamplePage(selfHosted, selectedProject, sentryUrl),
88
+ );
58
89
 
59
- for (const configVariant of configVariants) {
60
- const jsConfig = `sentry.${configVariant}.config.js`;
61
- const tsConfig = `sentry.${configVariant}.config.ts`;
90
+ await addSentryCliConfig(authToken);
91
+
92
+ const mightBeUsingVercel = fs.existsSync(
93
+ path.join(process.cwd(), 'vercel.json'),
94
+ );
62
95
 
63
- const jsConfigExists = fs.existsSync(path.join(process.cwd(), jsConfig));
64
- const tsConfigExists = fs.existsSync(path.join(process.cwd(), tsConfig));
96
+ clack.outro(
97
+ `${chalk.green('Everything is set up!')}
65
98
 
66
- let shouldWriteFile = true;
99
+ ${chalk.cyan(
100
+ 'You can validate your setup by starting your dev environment (`next dev`) and visiting "/sentry-example-page".',
101
+ )}
102
+ ${
103
+ mightBeUsingVercel
104
+ ? `
105
+ ▲ It seems like you're using Vercel. We recommend using the Sentry Vercel integration: https://vercel.com/integrations/sentry
106
+ `
107
+ : ''
108
+ }
109
+ ${chalk.dim(
110
+ 'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
111
+ )}`,
112
+ );
113
+ }
67
114
 
68
- if (jsConfigExists || tsConfigExists) {
69
- const existingConfigs = [];
115
+ async function createOrMergeNextJsFiles(
116
+ selectedProject: SentryProjectData,
117
+ selfHosted: boolean,
118
+ sentryUrl: string,
119
+ ) {
120
+ const typeScriptDetected = isUsingTypeScript();
70
121
 
71
- if (jsConfigExists) {
72
- existingConfigs.push(jsConfig);
73
- }
122
+ const configVariants = ['server', 'client', 'edge'] as const;
74
123
 
75
- if (tsConfigExists) {
76
- existingConfigs.push(tsConfig);
77
- }
124
+ for (const configVariant of configVariants) {
125
+ await traceStep(`create-sentry-${configVariant}-config`, async () => {
126
+ const jsConfig = `sentry.${configVariant}.config.js`;
127
+ const tsConfig = `sentry.${configVariant}.config.ts`;
78
128
 
79
- const overwriteExistingConfigs = await abortIfCancelled(
80
- clack.confirm({
81
- message: `Found existing Sentry ${configVariant} config (${existingConfigs.join(
82
- ', ',
83
- )}). Overwrite ${existingConfigs.length > 1 ? 'them' : 'it'}?`,
84
- }),
85
- );
129
+ const jsConfigExists = fs.existsSync(path.join(process.cwd(), jsConfig));
130
+ const tsConfigExists = fs.existsSync(path.join(process.cwd(), tsConfig));
86
131
 
87
- shouldWriteFile = overwriteExistingConfigs;
132
+ let shouldWriteFile = true;
133
+
134
+ if (jsConfigExists || tsConfigExists) {
135
+ const existingConfigs = [];
88
136
 
89
- if (overwriteExistingConfigs) {
90
137
  if (jsConfigExists) {
91
- fs.unlinkSync(path.join(process.cwd(), jsConfig));
92
- clack.log.warn(`Removed existing ${chalk.bold(jsConfig)}.`);
138
+ existingConfigs.push(jsConfig);
93
139
  }
140
+
94
141
  if (tsConfigExists) {
95
- fs.unlinkSync(path.join(process.cwd(), tsConfig));
96
- clack.log.warn(`Removed existing ${chalk.bold(tsConfig)}.`);
142
+ existingConfigs.push(tsConfig);
143
+ }
144
+
145
+ const overwriteExistingConfigs = await abortIfCancelled(
146
+ clack.confirm({
147
+ message: `Found existing Sentry ${configVariant} config (${existingConfigs.join(
148
+ ', ',
149
+ )}). Overwrite ${existingConfigs.length > 1 ? 'them' : 'it'}?`,
150
+ }),
151
+ );
152
+ Sentry.setTag(
153
+ `overwrite-${configVariant}-config`,
154
+ overwriteExistingConfigs,
155
+ );
156
+
157
+ shouldWriteFile = overwriteExistingConfigs;
158
+
159
+ if (overwriteExistingConfigs) {
160
+ if (jsConfigExists) {
161
+ fs.unlinkSync(path.join(process.cwd(), jsConfig));
162
+ clack.log.warn(`Removed existing ${chalk.bold(jsConfig)}.`);
163
+ }
164
+ if (tsConfigExists) {
165
+ fs.unlinkSync(path.join(process.cwd(), tsConfig));
166
+ clack.log.warn(`Removed existing ${chalk.bold(tsConfig)}.`);
167
+ }
97
168
  }
98
169
  }
99
- }
100
170
 
101
- if (shouldWriteFile) {
102
- await fs.promises.writeFile(
103
- path.join(process.cwd(), typeScriptDetected ? tsConfig : jsConfig),
104
- getSentryConfigContents(
105
- selectedProject.keys[0].dsn.public,
106
- configVariant,
107
- ),
108
- { encoding: 'utf8', flag: 'w' },
109
- );
110
- clack.log.success(
111
- `Created fresh ${chalk.bold(
112
- typeScriptDetected ? tsConfig : jsConfig,
113
- )}.`,
114
- );
115
- }
171
+ if (shouldWriteFile) {
172
+ await fs.promises.writeFile(
173
+ path.join(process.cwd(), typeScriptDetected ? tsConfig : jsConfig),
174
+ getSentryConfigContents(
175
+ selectedProject.keys[0].dsn.public,
176
+ configVariant,
177
+ ),
178
+ { encoding: 'utf8', flag: 'w' },
179
+ );
180
+ clack.log.success(
181
+ `Created fresh ${chalk.bold(
182
+ typeScriptDetected ? tsConfig : jsConfig,
183
+ )}.`,
184
+ );
185
+ Sentry.setTag(`created-${configVariant}-config`, true);
186
+ }
187
+ });
116
188
  }
117
189
 
118
190
  const sentryWebpackOptionsTemplate = getNextjsWebpackPluginOptionsTemplate(
@@ -126,162 +198,179 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
126
198
  const nextConfigJs = 'next.config.js';
127
199
  const nextConfigMjs = 'next.config.mjs';
128
200
 
129
- const nextConfigJsExists = fs.existsSync(
130
- path.join(process.cwd(), nextConfigJs),
131
- );
132
- const nextConfigMjsExists = fs.existsSync(
133
- path.join(process.cwd(), nextConfigMjs),
134
- );
135
-
136
- if (!nextConfigJsExists && !nextConfigMjsExists) {
137
- await fs.promises.writeFile(
201
+ await traceStep('setup-next-config', async () => {
202
+ const nextConfigJsExists = fs.existsSync(
138
203
  path.join(process.cwd(), nextConfigJs),
139
- getNextjsConfigCjsTemplate(
140
- sentryWebpackOptionsTemplate,
141
- sentryBuildOptionsTemplate,
142
- ),
143
- { encoding: 'utf8', flag: 'w' },
144
- );
145
-
146
- clack.log.success(
147
- `Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
148
204
  );
149
- }
150
-
151
- if (nextConfigJsExists) {
152
- const nextConfgiJsContent = fs.readFileSync(
153
- path.join(process.cwd(), nextConfigJs),
154
- 'utf8',
205
+ const nextConfigMjsExists = fs.existsSync(
206
+ path.join(process.cwd(), nextConfigMjs),
155
207
  );
156
208
 
157
- const probablyIncludesSdk =
158
- nextConfgiJsContent.includes('@sentry/nextjs') &&
159
- nextConfgiJsContent.includes('withSentryConfig');
160
-
161
- let shouldInject = true;
162
-
163
- if (probablyIncludesSdk) {
164
- const injectAnyhow = await abortIfCancelled(
165
- clack.confirm({
166
- message: `${chalk.bold(
167
- nextConfigJs,
168
- )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
169
- }),
170
- );
171
-
172
- shouldInject = injectAnyhow;
173
- }
209
+ if (!nextConfigJsExists && !nextConfigMjsExists) {
210
+ Sentry.setTag('next-config-strategy', 'create');
174
211
 
175
- if (shouldInject) {
176
- await fs.promises.appendFile(
212
+ await fs.promises.writeFile(
177
213
  path.join(process.cwd(), nextConfigJs),
178
- getNextjsConfigCjsAppendix(
214
+ getNextjsConfigCjsTemplate(
179
215
  sentryWebpackOptionsTemplate,
180
216
  sentryBuildOptionsTemplate,
181
217
  ),
182
- 'utf8',
218
+ { encoding: 'utf8', flag: 'w' },
183
219
  );
184
220
 
185
221
  clack.log.success(
186
- `Added Sentry configuration to ${chalk.bold(nextConfigJs)}. ${chalk.dim(
187
- '(you probably want to clean this up a bit!)',
188
- )}`,
222
+ `Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
189
223
  );
190
224
  }
191
- }
192
225
 
193
- if (nextConfigMjsExists) {
194
- const nextConfgiMjsContent = fs.readFileSync(
195
- path.join(process.cwd(), nextConfigMjs),
196
- 'utf8',
197
- );
226
+ if (nextConfigJsExists) {
227
+ Sentry.setTag('next-config-strategy', 'modify');
198
228
 
199
- const probablyIncludesSdk =
200
- nextConfgiMjsContent.includes('@sentry/nextjs') &&
201
- nextConfgiMjsContent.includes('withSentryConfig');
229
+ const nextConfgiJsContent = fs.readFileSync(
230
+ path.join(process.cwd(), nextConfigJs),
231
+ 'utf8',
232
+ );
202
233
 
203
- let shouldInject = true;
234
+ const probablyIncludesSdk =
235
+ nextConfgiJsContent.includes('@sentry/nextjs') &&
236
+ nextConfgiJsContent.includes('withSentryConfig');
204
237
 
205
- if (probablyIncludesSdk) {
206
- const injectAnyhow = await abortIfCancelled(
207
- clack.confirm({
208
- message: `${chalk.bold(
209
- nextConfigMjs,
210
- )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
211
- }),
212
- );
238
+ let shouldInject = true;
213
239
 
214
- shouldInject = injectAnyhow;
215
- }
240
+ if (probablyIncludesSdk) {
241
+ const injectAnyhow = await abortIfCancelled(
242
+ clack.confirm({
243
+ message: `${chalk.bold(
244
+ nextConfigJs,
245
+ )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
246
+ }),
247
+ );
216
248
 
217
- try {
218
- if (shouldInject) {
219
- const mod = parseModule(nextConfgiMjsContent);
220
- mod.imports.$add({
221
- from: '@sentry/nextjs',
222
- imported: 'withSentryConfig',
223
- local: 'withSentryConfig',
224
- });
225
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
226
- const expressionToWrap = generateCode(mod.exports.default.$ast).code;
227
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
228
- mod.exports.default = builders.raw(`withSentryConfig(
229
- ${expressionToWrap},
230
- ${sentryWebpackOptionsTemplate},
231
- ${sentryBuildOptionsTemplate}
232
- )`);
233
- const newCode = mod.generate().code;
249
+ shouldInject = injectAnyhow;
250
+ }
234
251
 
235
- await fs.promises.writeFile(
236
- path.join(process.cwd(), nextConfigMjs),
237
- newCode,
238
- {
239
- encoding: 'utf8',
240
- flag: 'w',
241
- },
252
+ if (shouldInject) {
253
+ await fs.promises.appendFile(
254
+ path.join(process.cwd(), nextConfigJs),
255
+ getNextjsConfigCjsAppendix(
256
+ sentryWebpackOptionsTemplate,
257
+ sentryBuildOptionsTemplate,
258
+ ),
259
+ 'utf8',
242
260
  );
261
+
243
262
  clack.log.success(
244
263
  `Added Sentry configuration to ${chalk.bold(
245
- nextConfigMjs,
264
+ nextConfigJs,
246
265
  )}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
247
266
  );
248
267
  }
249
- } catch {
250
- clack.log.warn(
251
- chalk.yellow(
252
- `Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
253
- ),
254
- );
255
- clack.log.info(
256
- `Please put the following code snippet into ${chalk.bold(
257
- nextConfigMjs,
258
- )}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
259
- );
260
268
 
261
- // eslint-disable-next-line no-console
262
- console.log(
263
- getNextjsConfigEsmCopyPasteSnippet(
264
- sentryWebpackOptionsTemplate,
265
- sentryBuildOptionsTemplate,
266
- ),
269
+ Sentry.setTag('next-config-mod-result', 'success');
270
+ }
271
+
272
+ if (nextConfigMjsExists) {
273
+ const nextConfgiMjsContent = fs.readFileSync(
274
+ path.join(process.cwd(), nextConfigMjs),
275
+ 'utf8',
267
276
  );
268
277
 
269
- const shouldContinue = await abortIfCancelled(
270
- clack.confirm({
271
- message: `Are you done putting the snippet above into ${chalk.bold(
278
+ const probablyIncludesSdk =
279
+ nextConfgiMjsContent.includes('@sentry/nextjs') &&
280
+ nextConfgiMjsContent.includes('withSentryConfig');
281
+
282
+ let shouldInject = true;
283
+
284
+ if (probablyIncludesSdk) {
285
+ const injectAnyhow = await abortIfCancelled(
286
+ clack.confirm({
287
+ message: `${chalk.bold(
288
+ nextConfigMjs,
289
+ )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
290
+ }),
291
+ );
292
+
293
+ shouldInject = injectAnyhow;
294
+ }
295
+
296
+ try {
297
+ if (shouldInject) {
298
+ const mod = parseModule(nextConfgiMjsContent);
299
+ mod.imports.$add({
300
+ from: '@sentry/nextjs',
301
+ imported: 'withSentryConfig',
302
+ local: 'withSentryConfig',
303
+ });
304
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
305
+ const expressionToWrap = generateCode(mod.exports.default.$ast).code;
306
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
307
+ mod.exports.default = builders.raw(`withSentryConfig(
308
+ ${expressionToWrap},
309
+ ${sentryWebpackOptionsTemplate},
310
+ ${sentryBuildOptionsTemplate}
311
+ )`);
312
+ const newCode = mod.generate().code;
313
+
314
+ await fs.promises.writeFile(
315
+ path.join(process.cwd(), nextConfigMjs),
316
+ newCode,
317
+ {
318
+ encoding: 'utf8',
319
+ flag: 'w',
320
+ },
321
+ );
322
+ clack.log.success(
323
+ `Added Sentry configuration to ${chalk.bold(
324
+ nextConfigMjs,
325
+ )}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
326
+ );
327
+
328
+ Sentry.setTag('next-config-mod-result', 'success');
329
+ }
330
+ } catch {
331
+ Sentry.setTag('next-config-mod-result', 'fail');
332
+ clack.log.warn(
333
+ chalk.yellow(
334
+ `Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
335
+ ),
336
+ );
337
+ clack.log.info(
338
+ `Please put the following code snippet into ${chalk.bold(
272
339
  nextConfigMjs,
273
- )}?`,
274
- active: 'Yes',
275
- inactive: 'No, get me out of here',
276
- }),
277
- );
340
+ )}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
341
+ );
342
+
343
+ // eslint-disable-next-line no-console
344
+ console.log(
345
+ getNextjsConfigEsmCopyPasteSnippet(
346
+ sentryWebpackOptionsTemplate,
347
+ sentryBuildOptionsTemplate,
348
+ ),
349
+ );
350
+
351
+ const shouldContinue = await abortIfCancelled(
352
+ clack.confirm({
353
+ message: `Are you done putting the snippet above into ${chalk.bold(
354
+ nextConfigMjs,
355
+ )}?`,
356
+ active: 'Yes',
357
+ inactive: 'No, get me out of here',
358
+ }),
359
+ );
278
360
 
279
- if (!shouldContinue) {
280
- await abort();
361
+ if (!shouldContinue) {
362
+ await abort();
363
+ }
281
364
  }
282
365
  }
283
- }
366
+ });
367
+ }
284
368
 
369
+ async function createExamplePage(
370
+ selfHosted: boolean,
371
+ selectedProject: SentryProjectData,
372
+ sentryUrl: string,
373
+ ): Promise<void> {
285
374
  const srcDir = path.join(process.cwd(), 'src');
286
375
  const maybePagesDirPath = path.join(process.cwd(), 'pages');
287
376
  const maybeSrcPagesDirPath = path.join(srcDir, 'pages');
@@ -316,6 +405,8 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
316
405
  });
317
406
  }
318
407
 
408
+ Sentry.setTag('nextjs-app-dir', !!appLocation);
409
+
319
410
  if (appLocation) {
320
411
  const examplePageContents = getSentryExamplePageContents({
321
412
  selfHosted,
@@ -415,28 +506,4 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
415
506
  )}.`,
416
507
  );
417
508
  }
418
-
419
- await addSentryCliConfig(authToken);
420
-
421
- const mightBeUsingVercel = fs.existsSync(
422
- path.join(process.cwd(), 'vercel.json'),
423
- );
424
-
425
- clack.outro(
426
- `${chalk.green('Everything is set up!')}
427
-
428
- ${chalk.cyan(
429
- 'You can validate your setup by starting your dev environment (`next dev`) and visiting "/sentry-example-page".',
430
- )}
431
- ${
432
- mightBeUsingVercel
433
- ? `
434
- ▲ It seems like you're using Vercel. We recommend using the Sentry Vercel integration: https://vercel.com/integrations/sentry
435
- `
436
- : ''
437
- }
438
- ${chalk.dim(
439
- 'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
440
- )}`,
441
- );
442
509
  }
@@ -0,0 +1,21 @@
1
+ import { major, minVersion } from 'semver';
2
+
3
+ export function getNextJsVersionBucket(version: string | undefined) {
4
+ if (!version) {
5
+ return 'none';
6
+ }
7
+
8
+ try {
9
+ const minVer = minVersion(version);
10
+ if (!minVer) {
11
+ return 'invalid';
12
+ }
13
+ const majorVersion = major(minVer);
14
+ if (majorVersion >= 11) {
15
+ return `${majorVersion}.x`;
16
+ }
17
+ return '<11.0.0';
18
+ } catch {
19
+ return 'unknown';
20
+ }
21
+ }
@@ -4,7 +4,7 @@ import chalk from 'chalk';
4
4
 
5
5
  import {
6
6
  addSentryCliConfig,
7
- confirmContinueEvenThoughNoGitRepo,
7
+ confirmContinueIfNoOrDirtyGitRepo,
8
8
  ensurePackageIsInstalled,
9
9
  getOrAskForProjectData,
10
10
  getPackageDotJson,
@@ -45,7 +45,7 @@ async function runRemixWizardWithTelemetry(
45
45
  telemetryEnabled: options.telemetryEnabled,
46
46
  });
47
47
 
48
- await confirmContinueEvenThoughNoGitRepo();
48
+ await confirmContinueIfNoOrDirtyGitRepo();
49
49
 
50
50
  const remixConfig = await loadRemixConfig();
51
51
  const packageJson = await getPackageDotJson();
@@ -56,12 +56,10 @@ async function runRemixWizardWithTelemetry(
56
56
  const { selectedProject, authToken, sentryUrl } =
57
57
  await getOrAskForProjectData(options, 'javascript-remix');
58
58
 
59
- await traceStep('Install Sentry SDK', () =>
60
- installPackage({
61
- packageName: '@sentry/remix',
62
- alreadyInstalled: hasPackageInstalled('@sentry/remix', packageJson),
63
- }),
64
- );
59
+ await installPackage({
60
+ packageName: '@sentry/remix',
61
+ alreadyInstalled: hasPackageInstalled('@sentry/remix', packageJson),
62
+ });
65
63
 
66
64
  const dsn = selectedProject.keys[0].dsn.public;
67
65
 
@@ -107,7 +105,7 @@ async function runRemixWizardWithTelemetry(
107
105
 
108
106
  await traceStep('Initialize Sentry on server entry', async () => {
109
107
  try {
110
- await initializeSentryOnEntryServer(dsn, isTS, isV2);
108
+ await initializeSentryOnEntryServer(dsn, isV2, isTS);
111
109
  } catch (e) {
112
110
  clack.log.warn(`Could not initialize Sentry on server entry.
113
111
  Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/`);
@@ -12,7 +12,7 @@ import * as url from 'url';
12
12
  // @ts-expect-error - clack is ESM and TS complains about that. It works though
13
13
  import clack from '@clack/prompts';
14
14
  import chalk from 'chalk';
15
- import { parse } from 'semver';
15
+ import { gte, minVersion } from 'semver';
16
16
 
17
17
  // @ts-expect-error - magicast is ESM and TS complains about that. It works though
18
18
  import { builders, generateCode, loadFile, writeFile } from 'magicast';
@@ -97,8 +97,17 @@ export function isRemixV2(
97
97
  packageJson: PackageDotJson,
98
98
  ): boolean {
99
99
  const remixVersion = getPackageVersion('@remix-run/react', packageJson);
100
- const remixVersionMajor = remixVersion && parse(remixVersion)?.major;
101
- const isV2Remix = remixVersionMajor && remixVersionMajor >= 2;
100
+ if (!remixVersion) {
101
+ return false;
102
+ }
103
+
104
+ const minVer = minVersion(remixVersion);
105
+
106
+ if (!minVer) {
107
+ return false;
108
+ }
109
+
110
+ const isV2Remix = gte(minVer, '2.0.0');
102
111
 
103
112
  return isV2Remix || remixConfig?.future?.v2_errorBoundary || false;
104
113
  }
@@ -6,7 +6,7 @@ import * as Sentry from '@sentry/node';
6
6
  import {
7
7
  abort,
8
8
  abortIfCancelled,
9
- confirmContinueEvenThoughNoGitRepo,
9
+ confirmContinueIfNoOrDirtyGitRepo,
10
10
  SENTRY_DOT_ENV_FILE,
11
11
  printWelcome,
12
12
  SENTRY_CLI_RC_FILE,
@@ -70,7 +70,7 @@ You can turn this off by running the wizard with the '--disable-telemetry' flag.
70
70
  return;
71
71
  }
72
72
 
73
- await traceStep('detect-git', confirmContinueEvenThoughNoGitRepo);
73
+ await confirmContinueIfNoOrDirtyGitRepo();
74
74
 
75
75
  await traceStep('check-sdk-version', ensureMinimumSdkVersionIsInstalled);
76
76