@sentry/wizard 3.12.0 → 3.14.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.
Files changed (86) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/lib/Steps/ChooseIntegration.js +1 -0
  3. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  4. package/dist/package.json +2 -2
  5. package/dist/src/android/android-wizard.js +8 -8
  6. package/dist/src/android/android-wizard.js.map +1 -1
  7. package/dist/src/apple/apple-wizard.js +1 -1
  8. package/dist/src/apple/apple-wizard.js.map +1 -1
  9. package/dist/src/nextjs/nextjs-wizard.d.ts +1 -0
  10. package/dist/src/nextjs/nextjs-wizard.js +257 -163
  11. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  12. package/dist/src/nextjs/templates.d.ts +1 -1
  13. package/dist/src/nextjs/templates.js +2 -2
  14. package/dist/src/nextjs/templates.js.map +1 -1
  15. package/dist/src/nextjs/utils.d.ts +1 -0
  16. package/dist/src/nextjs/utils.js +25 -0
  17. package/dist/src/nextjs/utils.js.map +1 -0
  18. package/dist/src/remix/remix-wizard.js +13 -11
  19. package/dist/src/remix/remix-wizard.js.map +1 -1
  20. package/dist/src/remix/sdk-setup.d.ts +5 -1
  21. package/dist/src/remix/sdk-setup.js +13 -6
  22. package/dist/src/remix/sdk-setup.js.map +1 -1
  23. package/dist/src/sourcemaps/sourcemaps-wizard.js +1 -1
  24. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  25. package/dist/src/sourcemaps/tools/sentry-cli.d.ts +9 -0
  26. package/dist/src/sourcemaps/tools/sentry-cli.js +26 -22
  27. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  28. package/dist/src/sourcemaps/tools/tsc.d.ts +6 -0
  29. package/dist/src/sourcemaps/tools/tsc.js +98 -17
  30. package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
  31. package/dist/src/sourcemaps/tools/vite.js +3 -13
  32. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  33. package/dist/src/sourcemaps/tools/webpack.js +3 -13
  34. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  35. package/dist/src/sveltekit/sdk-setup.js +122 -48
  36. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  37. package/dist/src/sveltekit/sveltekit-wizard.d.ts +1 -0
  38. package/dist/src/sveltekit/sveltekit-wizard.js +113 -42
  39. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  40. package/dist/src/sveltekit/utils.d.ts +2 -0
  41. package/dist/src/sveltekit/utils.js +48 -0
  42. package/dist/src/sveltekit/utils.js.map +1 -0
  43. package/dist/src/telemetry.d.ts +1 -0
  44. package/dist/src/telemetry.js +27 -12
  45. package/dist/src/telemetry.js.map +1 -1
  46. package/dist/src/utils/ast-utils.d.ts +70 -0
  47. package/dist/src/utils/ast-utils.js +152 -1
  48. package/dist/src/utils/ast-utils.js.map +1 -1
  49. package/dist/src/utils/clack-utils.d.ts +49 -7
  50. package/dist/src/utils/clack-utils.js +238 -168
  51. package/dist/src/utils/clack-utils.js.map +1 -1
  52. package/dist/src/utils/package-manager.d.ts +5 -0
  53. package/dist/src/utils/package-manager.js +23 -14
  54. package/dist/src/utils/package-manager.js.map +1 -1
  55. package/dist/test/sourcemaps/tools/sentry-cli.test.d.ts +1 -0
  56. package/dist/test/sourcemaps/tools/sentry-cli.test.js +112 -0
  57. package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -0
  58. package/dist/test/sourcemaps/tools/tsc.test.d.ts +1 -0
  59. package/dist/test/sourcemaps/tools/tsc.test.js +121 -0
  60. package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -0
  61. package/dist/test/utils/ast-utils.test.js +157 -26
  62. package/dist/test/utils/ast-utils.test.js.map +1 -1
  63. package/lib/Steps/ChooseIntegration.ts +1 -0
  64. package/package.json +2 -2
  65. package/src/android/android-wizard.ts +12 -10
  66. package/src/apple/apple-wizard.ts +2 -2
  67. package/src/nextjs/nextjs-wizard.ts +277 -198
  68. package/src/nextjs/templates.ts +3 -2
  69. package/src/nextjs/utils.ts +21 -0
  70. package/src/remix/remix-wizard.ts +15 -20
  71. package/src/remix/sdk-setup.ts +20 -5
  72. package/src/sourcemaps/sourcemaps-wizard.ts +2 -2
  73. package/src/sourcemaps/tools/sentry-cli.ts +16 -9
  74. package/src/sourcemaps/tools/tsc.ts +133 -28
  75. package/src/sourcemaps/tools/vite.ts +15 -39
  76. package/src/sourcemaps/tools/webpack.ts +16 -39
  77. package/src/sveltekit/sdk-setup.ts +109 -37
  78. package/src/sveltekit/sveltekit-wizard.ts +86 -21
  79. package/src/sveltekit/utils.ts +50 -0
  80. package/src/telemetry.ts +22 -11
  81. package/src/utils/ast-utils.ts +180 -0
  82. package/src/utils/clack-utils.ts +238 -149
  83. package/src/utils/package-manager.ts +24 -12
  84. package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
  85. package/test/sourcemaps/tools/tsc.test.ts +181 -0
  86. package/test/utils/ast-utils.test.ts +233 -32
@@ -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,255 +33,344 @@ 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!')}
98
+
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
+ }
65
114
 
66
- let shouldWriteFile = true;
115
+ async function createOrMergeNextJsFiles(
116
+ selectedProject: SentryProjectData,
117
+ selfHosted: boolean,
118
+ sentryUrl: string,
119
+ ) {
120
+ const typeScriptDetected = isUsingTypeScript();
67
121
 
68
- if (jsConfigExists || tsConfigExists) {
69
- const existingConfigs = [];
122
+ const configVariants = ['server', 'client', 'edge'] as const;
70
123
 
71
- if (jsConfigExists) {
72
- existingConfigs.push(jsConfig);
73
- }
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`;
74
128
 
75
- if (tsConfigExists) {
76
- existingConfigs.push(tsConfig);
77
- }
129
+ const jsConfigExists = fs.existsSync(path.join(process.cwd(), jsConfig));
130
+ const tsConfigExists = fs.existsSync(path.join(process.cwd(), tsConfig));
78
131
 
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
- );
132
+ let shouldWriteFile = true;
86
133
 
87
- shouldWriteFile = overwriteExistingConfigs;
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(
119
191
  selectedProject.organization.slug,
120
192
  selectedProject.slug,
193
+ selfHosted,
194
+ sentryUrl,
121
195
  );
122
196
  const sentryBuildOptionsTemplate = getNextjsSentryBuildOptionsTemplate();
123
197
 
124
198
  const nextConfigJs = 'next.config.js';
125
199
  const nextConfigMjs = 'next.config.mjs';
126
200
 
127
- const nextConfigJsExists = fs.existsSync(
128
- path.join(process.cwd(), nextConfigJs),
129
- );
130
- const nextConfigMjsExists = fs.existsSync(
131
- path.join(process.cwd(), nextConfigMjs),
132
- );
133
-
134
- if (!nextConfigJsExists && !nextConfigMjsExists) {
135
- await fs.promises.writeFile(
201
+ await traceStep('setup-next-config', async () => {
202
+ const nextConfigJsExists = fs.existsSync(
136
203
  path.join(process.cwd(), nextConfigJs),
137
- getNextjsConfigCjsTemplate(
138
- sentryWebpackOptionsTemplate,
139
- sentryBuildOptionsTemplate,
140
- ),
141
- { encoding: 'utf8', flag: 'w' },
142
- );
143
-
144
- clack.log.success(
145
- `Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
146
204
  );
147
- }
148
-
149
- if (nextConfigJsExists) {
150
- const nextConfgiJsContent = fs.readFileSync(
151
- path.join(process.cwd(), nextConfigJs),
152
- 'utf8',
205
+ const nextConfigMjsExists = fs.existsSync(
206
+ path.join(process.cwd(), nextConfigMjs),
153
207
  );
154
208
 
155
- const probablyIncludesSdk =
156
- nextConfgiJsContent.includes('@sentry/nextjs') &&
157
- nextConfgiJsContent.includes('withSentryConfig');
158
-
159
- let shouldInject = true;
160
-
161
- if (probablyIncludesSdk) {
162
- const injectAnyhow = await abortIfCancelled(
163
- clack.confirm({
164
- message: `${chalk.bold(
165
- nextConfigJs,
166
- )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
167
- }),
168
- );
169
-
170
- shouldInject = injectAnyhow;
171
- }
209
+ if (!nextConfigJsExists && !nextConfigMjsExists) {
210
+ Sentry.setTag('next-config-strategy', 'create');
172
211
 
173
- if (shouldInject) {
174
- await fs.promises.appendFile(
212
+ await fs.promises.writeFile(
175
213
  path.join(process.cwd(), nextConfigJs),
176
- getNextjsConfigCjsAppendix(
214
+ getNextjsConfigCjsTemplate(
177
215
  sentryWebpackOptionsTemplate,
178
216
  sentryBuildOptionsTemplate,
179
217
  ),
180
- 'utf8',
218
+ { encoding: 'utf8', flag: 'w' },
181
219
  );
182
220
 
183
221
  clack.log.success(
184
- `Added Sentry configuration to ${chalk.bold(nextConfigJs)}. ${chalk.dim(
185
- '(you probably want to clean this up a bit!)',
186
- )}`,
222
+ `Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
187
223
  );
188
224
  }
189
- }
190
225
 
191
- if (nextConfigMjsExists) {
192
- const nextConfgiMjsContent = fs.readFileSync(
193
- path.join(process.cwd(), nextConfigMjs),
194
- 'utf8',
195
- );
226
+ if (nextConfigJsExists) {
227
+ Sentry.setTag('next-config-strategy', 'modify');
196
228
 
197
- const probablyIncludesSdk =
198
- nextConfgiMjsContent.includes('@sentry/nextjs') &&
199
- nextConfgiMjsContent.includes('withSentryConfig');
229
+ const nextConfgiJsContent = fs.readFileSync(
230
+ path.join(process.cwd(), nextConfigJs),
231
+ 'utf8',
232
+ );
200
233
 
201
- let shouldInject = true;
234
+ const probablyIncludesSdk =
235
+ nextConfgiJsContent.includes('@sentry/nextjs') &&
236
+ nextConfgiJsContent.includes('withSentryConfig');
202
237
 
203
- if (probablyIncludesSdk) {
204
- const injectAnyhow = await abortIfCancelled(
205
- clack.confirm({
206
- message: `${chalk.bold(
207
- nextConfigMjs,
208
- )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
209
- }),
210
- );
238
+ let shouldInject = true;
211
239
 
212
- shouldInject = injectAnyhow;
213
- }
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
+ );
214
248
 
215
- try {
216
- if (shouldInject) {
217
- const mod = parseModule(nextConfgiMjsContent);
218
- mod.imports.$add({
219
- from: '@sentry/nextjs',
220
- imported: 'withSentryConfig',
221
- local: 'withSentryConfig',
222
- });
223
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
224
- const expressionToWrap = generateCode(mod.exports.default.$ast).code;
225
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
226
- mod.exports.default = builders.raw(`withSentryConfig(
227
- ${expressionToWrap},
228
- ${sentryWebpackOptionsTemplate},
229
- ${sentryBuildOptionsTemplate}
230
- )`);
231
- const newCode = mod.generate().code;
249
+ shouldInject = injectAnyhow;
250
+ }
232
251
 
233
- await fs.promises.writeFile(
234
- path.join(process.cwd(), nextConfigMjs),
235
- newCode,
236
- {
237
- encoding: 'utf8',
238
- flag: 'w',
239
- },
252
+ if (shouldInject) {
253
+ await fs.promises.appendFile(
254
+ path.join(process.cwd(), nextConfigJs),
255
+ getNextjsConfigCjsAppendix(
256
+ sentryWebpackOptionsTemplate,
257
+ sentryBuildOptionsTemplate,
258
+ ),
259
+ 'utf8',
240
260
  );
261
+
241
262
  clack.log.success(
242
263
  `Added Sentry configuration to ${chalk.bold(
243
- nextConfigMjs,
264
+ nextConfigJs,
244
265
  )}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
245
266
  );
246
267
  }
247
- } catch {
248
- clack.log.warn(
249
- chalk.yellow(
250
- `Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
251
- ),
252
- );
253
- clack.log.info(
254
- `Please put the following code snippet into ${chalk.bold(
255
- nextConfigMjs,
256
- )}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
257
- );
258
268
 
259
- // eslint-disable-next-line no-console
260
- console.log(
261
- getNextjsConfigEsmCopyPasteSnippet(
262
- sentryWebpackOptionsTemplate,
263
- sentryBuildOptionsTemplate,
264
- ),
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',
265
276
  );
266
277
 
267
- const shouldContinue = await abortIfCancelled(
268
- clack.confirm({
269
- 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(
270
339
  nextConfigMjs,
271
- )}?`,
272
- active: 'Yes',
273
- inactive: 'No, get me out of here',
274
- }),
275
- );
340
+ )}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
341
+ );
276
342
 
277
- if (!shouldContinue) {
278
- await abort();
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
+ );
360
+
361
+ if (!shouldContinue) {
362
+ await abort();
363
+ }
279
364
  }
280
365
  }
281
- }
366
+ });
367
+ }
282
368
 
369
+ async function createExamplePage(
370
+ selfHosted: boolean,
371
+ selectedProject: SentryProjectData,
372
+ sentryUrl: string,
373
+ ): Promise<void> {
283
374
  const srcDir = path.join(process.cwd(), 'src');
284
375
  const maybePagesDirPath = path.join(process.cwd(), 'pages');
285
376
  const maybeSrcPagesDirPath = path.join(srcDir, 'pages');
@@ -314,6 +405,8 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
314
405
  });
315
406
  }
316
407
 
408
+ Sentry.setTag('nextjs-app-dir', !!appLocation);
409
+
317
410
  if (appLocation) {
318
411
  const examplePageContents = getSentryExamplePageContents({
319
412
  selfHosted,
@@ -323,6 +416,13 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
323
416
  useClient: true,
324
417
  });
325
418
 
419
+ fs.mkdirSync(
420
+ path.join(process.cwd(), ...appLocation, 'sentry-example-page'),
421
+ {
422
+ recursive: true,
423
+ },
424
+ );
425
+
326
426
  await fs.promises.writeFile(
327
427
  path.join(
328
428
  process.cwd(),
@@ -340,9 +440,12 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
340
440
  )}.`,
341
441
  );
342
442
 
343
- fs.mkdirSync(path.join(process.cwd(), ...appLocation, 'api'), {
344
- recursive: true,
345
- });
443
+ fs.mkdirSync(
444
+ path.join(process.cwd(), ...appLocation, 'api', 'sentry-example-api'),
445
+ {
446
+ recursive: true,
447
+ },
448
+ );
346
449
 
347
450
  await fs.promises.writeFile(
348
451
  path.join(
@@ -403,28 +506,4 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
403
506
  )}.`,
404
507
  );
405
508
  }
406
-
407
- await addSentryCliConfig(authToken);
408
-
409
- const mightBeUsingVercel = fs.existsSync(
410
- path.join(process.cwd(), 'vercel.json'),
411
- );
412
-
413
- clack.outro(
414
- `${chalk.green('Everything is set up!')}
415
-
416
- ${chalk.cyan(
417
- 'You can validate your setup by starting your dev environment (`next dev`) and visiting "/sentry-example-page".',
418
- )}
419
- ${
420
- mightBeUsingVercel
421
- ? `
422
- ▲ It seems like you're using Vercel. We recommend using the Sentry Vercel integration: https://vercel.com/integrations/sentry
423
- `
424
- : ''
425
- }
426
- ${chalk.dim(
427
- 'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
428
- )}`,
429
- );
430
509
  }
@@ -1,6 +1,8 @@
1
1
  export function getNextjsWebpackPluginOptionsTemplate(
2
2
  orgSlug: string,
3
3
  projectSlug: string,
4
+ selfHosted: boolean,
5
+ url: string,
4
6
  ): string {
5
7
  return `{
6
8
  // For all available options, see:
@@ -8,9 +10,8 @@ export function getNextjsWebpackPluginOptionsTemplate(
8
10
 
9
11
  // Suppresses source map uploading logs during build
10
12
  silent: true,
11
-
12
13
  org: "${orgSlug}",
13
- project: "${projectSlug}",
14
+ project: "${projectSlug}",${selfHosted ? `\n url: "${url}"` : ''}
14
15
  }`;
15
16
  }
16
17
 
@@ -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
+ }