@sentry/wizard 3.34.3 → 3.35.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 (78) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/dist/e2e-tests/tests/nuxt-3.test.d.ts +1 -0
  3. package/dist/e2e-tests/tests/nuxt-3.test.js +231 -0
  4. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -0
  5. package/dist/e2e-tests/tests/nuxt-4.test.d.ts +1 -0
  6. package/dist/e2e-tests/tests/nuxt-4.test.js +232 -0
  7. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -0
  8. package/dist/e2e-tests/tests/remix.test.js +92 -27
  9. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/sveltekit.test.js +102 -42
  11. package/dist/e2e-tests/tests/sveltekit.test.js.map +1 -1
  12. package/dist/e2e-tests/utils/index.d.ts +18 -1
  13. package/dist/e2e-tests/utils/index.js +31 -2
  14. package/dist/e2e-tests/utils/index.js.map +1 -1
  15. package/dist/lib/Constants.d.ts +1 -0
  16. package/dist/lib/Constants.js +1 -0
  17. package/dist/lib/Constants.js.map +1 -1
  18. package/dist/package.json +3 -3
  19. package/dist/src/apple/templates.js +2 -2
  20. package/dist/src/apple/templates.js.map +1 -1
  21. package/dist/src/nextjs/nextjs-wizard.js +1 -0
  22. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  23. package/dist/src/nuxt/nuxt-wizard.d.ts +3 -0
  24. package/dist/src/nuxt/nuxt-wizard.js +220 -0
  25. package/dist/src/nuxt/nuxt-wizard.js.map +1 -0
  26. package/dist/src/nuxt/sdk-example.d.ts +8 -0
  27. package/dist/src/nuxt/sdk-example.js +179 -0
  28. package/dist/src/nuxt/sdk-example.js.map +1 -0
  29. package/dist/src/nuxt/sdk-setup.d.ts +8 -0
  30. package/dist/src/nuxt/sdk-setup.js +275 -0
  31. package/dist/src/nuxt/sdk-setup.js.map +1 -0
  32. package/dist/src/nuxt/templates.d.ts +22 -0
  33. package/dist/src/nuxt/templates.js +84 -0
  34. package/dist/src/nuxt/templates.js.map +1 -0
  35. package/dist/src/nuxt/utils.d.ts +1 -0
  36. package/dist/src/nuxt/utils.js +71 -0
  37. package/dist/src/nuxt/utils.js.map +1 -0
  38. package/dist/src/remix/remix-wizard.js +2 -1
  39. package/dist/src/remix/remix-wizard.js.map +1 -1
  40. package/dist/src/run.d.ts +1 -1
  41. package/dist/src/run.js +30 -23
  42. package/dist/src/run.js.map +1 -1
  43. package/dist/src/sourcemaps/tools/rollup.js +1 -1
  44. package/dist/src/sourcemaps/tools/rollup.js.map +1 -1
  45. package/dist/src/sveltekit/sveltekit-wizard.js +2 -1
  46. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  47. package/dist/src/utils/clack-utils.d.ts +6 -2
  48. package/dist/src/utils/clack-utils.js +30 -10
  49. package/dist/src/utils/clack-utils.js.map +1 -1
  50. package/dist/test/nuxt/templates.test.d.ts +1 -0
  51. package/dist/test/nuxt/templates.test.js +70 -0
  52. package/dist/test/nuxt/templates.test.js.map +1 -0
  53. package/e2e-tests/README.md +59 -0
  54. package/e2e-tests/test-applications/nuxt-3-test-app/README.md +75 -0
  55. package/e2e-tests/test-applications/nuxt-3-test-app/nuxt.config.ts +5 -0
  56. package/e2e-tests/test-applications/nuxt-3-test-app/package.json +18 -0
  57. package/e2e-tests/test-applications/nuxt-3-test-app/public/favicon.ico +0 -0
  58. package/e2e-tests/test-applications/nuxt-3-test-app/public/robots.txt +1 -0
  59. package/e2e-tests/tests/nuxt-3.test.ts +169 -0
  60. package/e2e-tests/tests/nuxt-4.test.ts +168 -0
  61. package/e2e-tests/tests/remix.test.ts +163 -50
  62. package/e2e-tests/tests/sveltekit.test.ts +180 -79
  63. package/e2e-tests/utils/index.ts +31 -1
  64. package/lib/Constants.ts +1 -0
  65. package/package.json +3 -3
  66. package/src/apple/templates.ts +14 -2
  67. package/src/nextjs/nextjs-wizard.ts +1 -0
  68. package/src/nuxt/nuxt-wizard.ts +166 -0
  69. package/src/nuxt/sdk-example.ts +135 -0
  70. package/src/nuxt/sdk-setup.ts +209 -0
  71. package/src/nuxt/templates.ts +296 -0
  72. package/src/nuxt/utils.ts +32 -0
  73. package/src/remix/remix-wizard.ts +2 -1
  74. package/src/run.ts +7 -0
  75. package/src/sourcemaps/tools/rollup.ts +1 -1
  76. package/src/sveltekit/sveltekit-wizard.ts +2 -1
  77. package/src/utils/clack-utils.ts +32 -8
  78. package/test/nuxt/templates.test.ts +228 -0
@@ -9,6 +9,7 @@ import {
9
9
  checkIfRunsOnProdMode,
10
10
  checkPackageJson,
11
11
  cleanupGit,
12
+ createFile,
12
13
  KEYS,
13
14
  revertLocalChanges,
14
15
  startWizardInstance,
@@ -16,65 +17,93 @@ import {
16
17
  } from '../utils';
17
18
  import * as path from 'path';
18
19
 
19
- describe('Sveltekit', () => {
20
- const integration = Integration.sveltekit;
21
- const projectDir = path.resolve(
22
- __dirname,
23
- '../test-applications/sveltekit-test-app',
24
- );
25
-
26
- beforeAll(async () => {
27
- const wizardInstance = startWizardInstance(integration, projectDir);
20
+ const SERVER_HOOK_TEMPLATE = `import type { Handle } from '@sveltejs/kit';
21
+
22
+ export const handle: Handle = async ({ event, resolve }) => {
23
+ if (event.url.pathname.startsWith('/custom')) {
24
+ return new Response('custom response');
25
+ }
26
+
27
+ const response = await resolve(event);
28
+ return response;
29
+ };
30
+ `;
31
+ const CLIENT_HOOK_TEMPLATE = `
32
+ export async function handleError({ error, event }) {
33
+ // you can capture the \`error\` and \`event\` from the client
34
+ // but it only runs if the unexpected error comes from \`+ page.ts\`
35
+ console.log(error)
36
+
37
+ return {
38
+ // don't show sensitive data to the user
39
+ message: 'Yikes! 💩',
40
+ }
41
+ }
42
+ `;
43
+
44
+ async function runWizardOnSvelteKitProject(projectDir: string, integration: Integration, fileModificationFn?: (projectDir: string, integration: Integration) => unknown) {
45
+ const wizardInstance = startWizardInstance(integration, projectDir);
46
+ let packageManagerPrompted = false;
47
+
48
+ if (fileModificationFn) {
49
+ fileModificationFn(projectDir, integration);
50
+
51
+ // As we modified project, we have a warning prompt before we get the package manager prompt
52
+ await wizardInstance.waitForOutput(
53
+ 'Do you want to continue anyway?',
54
+ );
28
55
 
29
- const packageManagerPrompted = await wizardInstance.waitForOutput(
56
+ packageManagerPrompted = await wizardInstance.sendStdinAndWaitForOutput(
57
+ [KEYS.ENTER],
30
58
  'Please select your package manager.',
31
- {
32
- optional: true,
33
- }
34
59
  );
35
-
36
- const tracingOptionPrompted =
37
- packageManagerPrompted &&
38
- (await wizardInstance.sendStdinAndWaitForOutput(
39
- // Selecting `yarn` as the package manager
40
- [KEYS.DOWN, KEYS.ENTER],
41
- // "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
42
- 'to track the performance of your application?',
43
- {
44
- timeout: 240_000,
45
- }
46
- ));
47
-
48
- const replayOptionPrompted =
49
- tracingOptionPrompted &&
50
- (await wizardInstance.sendStdinAndWaitForOutput(
51
- [KEYS.ENTER],
52
- // "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.
53
- 'to get a video-like reproduction of errors during a user session?',
54
- ));
55
-
56
- replayOptionPrompted &&
57
- (await wizardInstance.sendStdinAndWaitForOutput(
58
- [KEYS.ENTER],
59
- 'Do you want to create an example page',
60
- {
61
- optional: true,
62
- },
63
- ));
64
-
65
- await wizardInstance.sendStdinAndWaitForOutput(
66
- [KEYS.ENTER, KEYS.ENTER],
67
- 'Successfully installed the Sentry SvelteKit SDK!',
60
+ } else {
61
+ packageManagerPrompted = await wizardInstance.waitForOutput(
62
+ 'Please select your package manager'
68
63
  );
64
+ }
65
+
66
+ const tracingOptionPrompted =
67
+ packageManagerPrompted &&
68
+ (await wizardInstance.sendStdinAndWaitForOutput(
69
+ // Selecting `yarn` as the package manager
70
+ [KEYS.DOWN, KEYS.ENTER],
71
+ // "Do you want to enable Tracing", sometimes doesn't work as `Tracing` can be printed in bold.
72
+ 'to track the performance of your application?',
73
+ {
74
+ timeout: 240_000,
75
+ }
76
+ ));
77
+
78
+ const replayOptionPrompted =
79
+ tracingOptionPrompted &&
80
+ (await wizardInstance.sendStdinAndWaitForOutput(
81
+ [KEYS.ENTER],
82
+ // "Do you want to enable Sentry Session Replay", sometimes doesn't work as `Sentry Session Replay` can be printed in bold.
83
+ 'to get a video-like reproduction of errors during a user session?',
84
+ ));
85
+
86
+ replayOptionPrompted &&
87
+ (await wizardInstance.sendStdinAndWaitForOutput(
88
+ [KEYS.ENTER],
89
+ 'Do you want to create an example page',
90
+ {
91
+ optional: true,
92
+ },
93
+ ));
69
94
 
70
- wizardInstance.kill();
71
- });
95
+ await wizardInstance.sendStdinAndWaitForOutput(
96
+ [KEYS.ENTER, KEYS.ENTER],
97
+ 'Successfully installed the Sentry SvelteKit SDK!',
98
+ );
72
99
 
73
- afterAll(() => {
74
- revertLocalChanges(projectDir);
75
- cleanupGit(projectDir);
76
- });
100
+ wizardInstance.kill();
101
+ }
77
102
 
103
+ function checkSvelteKitProject(projectDir: string, integration: Integration, options?: {
104
+ devModeExpectedOutput: string;
105
+ prodModeExpectedOutput: string;
106
+ }) {
78
107
  test('should have the correct package.json', () => {
79
108
  checkPackageJson(projectDir, integration);
80
109
  });
@@ -99,11 +128,43 @@ describe('Sveltekit', () => {
99
128
  checkFileExists(path.resolve(projectDir, 'src/hooks.client.ts'));
100
129
  });
101
130
 
102
- test('hooks.client.ts contains sentry import', () => {
103
- checkFileContents(
104
- path.resolve(projectDir, 'src/hooks.client.ts'),
105
- [`import * as Sentry from '@sentry/sveltekit';`,
106
- `Sentry.init({
131
+ test('builds successfully', async () => {
132
+ await checkIfBuilds(projectDir, 'Successfully uploaded source maps to Sentry');
133
+ });
134
+
135
+ test('runs on dev mode correctly', async () => {
136
+ await checkIfRunsOnDevMode(projectDir, options?.devModeExpectedOutput || 'ready in');
137
+ });
138
+
139
+ test('runs on prod mode correctly', async () => {
140
+ await checkIfRunsOnProdMode(projectDir, options?.prodModeExpectedOutput || 'to expose', 'preview');
141
+ });
142
+ }
143
+
144
+ describe('Sveltekit', () => {
145
+ describe('without existing hooks', () => {
146
+ const integration = Integration.sveltekit;
147
+ const projectDir = path.resolve(
148
+ __dirname,
149
+ '../test-applications/sveltekit-test-app',
150
+ );
151
+
152
+ beforeAll(async () => {
153
+ await runWizardOnSvelteKitProject(projectDir, integration);
154
+ });
155
+
156
+ afterAll(() => {
157
+ revertLocalChanges(projectDir);
158
+ cleanupGit(projectDir);
159
+ });
160
+
161
+ checkSvelteKitProject(projectDir, integration);
162
+
163
+ test('hooks.client.ts contains sentry', () => {
164
+ checkFileContents(
165
+ path.resolve(projectDir, 'src/hooks.client.ts'),
166
+ [`import * as Sentry from '@sentry/sveltekit';`,
167
+ `Sentry.init({
107
168
  dsn: '${TEST_ARGS.PROJECT_DSN}',
108
169
 
109
170
  tracesSampleRate: 1.0,
@@ -118,37 +179,77 @@ describe('Sveltekit', () => {
118
179
 
119
180
  // If you don't want to use Session Replay, just remove the line below:
120
181
  integrations: [replayIntegration()],
121
- });`]);
122
- });
123
-
124
-
125
- test('hooks.server.ts contains sentry import', () => {
126
- checkFileContents(
127
- path.resolve(projectDir, 'src/hooks.server.ts'),
128
- [
129
- `import * as Sentry from '@sentry/sveltekit';`,
130
- `Sentry.init({
182
+ });`, 'export const handleError = handleErrorWithSentry(']);
183
+ });
184
+
185
+ test('hooks.server.ts contains sentry', () => {
186
+ checkFileContents(
187
+ path.resolve(projectDir, 'src/hooks.server.ts'),
188
+ [
189
+ `import * as Sentry from '@sentry/sveltekit';`,
190
+ `Sentry.init({
131
191
  dsn: '${TEST_ARGS.PROJECT_DSN}',
132
192
 
133
193
  tracesSampleRate: 1.0,
134
194
 
135
195
  // uncomment the line below to enable Spotlight (https://spotlightjs.com)
136
196
  // spotlight: import.meta.env.DEV,
137
- });`])
138
- });
139
-
140
-
141
- test('runs on dev mode correctly', async () => {
142
- await checkIfRunsOnDevMode(projectDir, 'ready in');
197
+ });`, 'export const handleError = handleErrorWithSentry();']);
198
+ });
143
199
  });
144
200
 
145
- test('should build successfully', async () => {
146
- await checkIfBuilds(projectDir, 'Successfully uploaded source maps to Sentry');
147
- });
201
+ describe('with existing hooks', () => {
202
+ const integration = Integration.sveltekit;
203
+ const projectDir = path.resolve(
204
+ __dirname,
205
+ '../test-applications/sveltekit-test-app',
206
+ );
148
207
 
149
- test('runs on prod mode correctly', async () => {
150
- // We can't use the full prompt `Network: use--host to expose` as `--host` can be printed in bold.
151
- await checkIfRunsOnProdMode(projectDir, 'to expose', "preview");
208
+ beforeAll(async () => {
209
+ await runWizardOnSvelteKitProject(projectDir, integration, (projectDir) => {
210
+ createFile(
211
+ path.resolve(projectDir, 'src/hooks.server.ts'),
212
+ SERVER_HOOK_TEMPLATE,
213
+ )
214
+
215
+ createFile(
216
+ path.resolve(projectDir, 'src/hooks.client.ts'),
217
+ CLIENT_HOOK_TEMPLATE,
218
+ )
219
+ });
220
+ });
221
+
222
+ afterAll(() => {
223
+ revertLocalChanges(projectDir);
224
+ cleanupGit(projectDir);
225
+ });
226
+
227
+ checkSvelteKitProject(projectDir, integration);
228
+
229
+ // These are removed from the common tests as the content is different
230
+ // when the hooks are merged instead of created from the template
231
+ test('hooks.client.ts contains sentry instrumentation', () => {
232
+ checkFileContents(
233
+ path.resolve(projectDir, 'src/hooks.client.ts'),
234
+ [`import * as Sentry from '@sentry/sveltekit';`,
235
+ `Sentry.init({
236
+ dsn: "${TEST_ARGS.PROJECT_DSN}",
237
+ tracesSampleRate: 1,
238
+ replaysSessionSampleRate: 0.1,
239
+ replaysOnErrorSampleRate: 1,
240
+ integrations: [Sentry.replayIntegration()]
241
+ })`, 'export const handleError = Sentry.handleErrorWithSentry(']);
242
+ });
243
+
244
+ test('hooks.server.ts contains sentry init', () => {
245
+ checkFileContents(
246
+ path.resolve(projectDir, 'src/hooks.server.ts'),
247
+ [`import * as Sentry from '@sentry/sveltekit';`,
248
+ `Sentry.init({
249
+ dsn: "${TEST_ARGS.PROJECT_DSN}",
250
+ tracesSampleRate: 1
251
+ })`, 'export const handleError = Sentry.handleErrorWithSentry();']);
252
+ });
152
253
  });
153
254
  });
154
255
 
@@ -227,11 +227,39 @@ export function startWizardInstance(
227
227
  }
228
228
 
229
229
  /**
230
- * Read the file contents and check if it contains the given content
230
+ * Create a file with the given content
231
231
  *
232
232
  * @param filePath
233
233
  * @param content
234
234
  */
235
+ export function createFile(filePath: string, content?: string) {
236
+ return fs.writeFileSync(filePath, content || '');
237
+ }
238
+
239
+ /**
240
+ * Modify the file with the new content
241
+ *
242
+ * @param filePath
243
+ * @param oldContent
244
+ * @param newContent
245
+ */
246
+ export function modifyFile(filePath: string, replaceMap: Record<string, string>) {
247
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
248
+ let newFileContent = fileContent;
249
+
250
+ for (const [oldContent, newContent] of Object.entries(replaceMap)) {
251
+ newFileContent = newFileContent.replace(oldContent, newContent);
252
+ }
253
+
254
+ fs.writeFileSync(filePath, newFileContent);
255
+ }
256
+
257
+ /**
258
+ * Read the file contents and check if it contains the given content
259
+ *
260
+ * @param {string} filePath
261
+ * @param {(string | string[])} content
262
+ */
235
263
  export function checkFileContents(
236
264
  filePath: string,
237
265
  content: string | string[],
@@ -255,6 +283,7 @@ export function checkFileExists(filePath: string) {
255
283
 
256
284
  /**
257
285
  * Check if the package.json contains the given integration
286
+ *
258
287
  * @param projectDir
259
288
  * @param integration
260
289
  */
@@ -264,6 +293,7 @@ export function checkPackageJson(projectDir: string, integration: Integration) {
264
293
 
265
294
  /**
266
295
  * Check if the .sentryclirc contains the auth token
296
+ *
267
297
  * @param projectDir
268
298
  */
269
299
  export function checkSentryCliRc(projectDir: string) {
package/lib/Constants.ts CHANGED
@@ -6,6 +6,7 @@ export enum Integration {
6
6
  cordova = 'cordova',
7
7
  electron = 'electron',
8
8
  nextjs = 'nextjs',
9
+ nuxt = 'nuxt',
9
10
  remix = 'remix',
10
11
  sveltekit = 'sveltekit',
11
12
  sourcemaps = 'sourcemaps',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wizard",
3
- "version": "3.34.3",
3
+ "version": "3.35.0",
4
4
  "homepage": "https://github.com/getsentry/sentry-wizard",
5
5
  "repository": "https://github.com/getsentry/sentry-wizard",
6
6
  "description": "Sentry wizard helping you to configure your project",
@@ -25,8 +25,8 @@
25
25
  "dependencies": {
26
26
  "@clack/core": "^0.3.4",
27
27
  "@clack/prompts": "0.7.0",
28
- "@sentry/cli": "^1.72.0",
29
- "@sentry/node": "^7.69.0",
28
+ "@sentry/cli": "^1.77.3",
29
+ "@sentry/node": "^7.119.2",
30
30
  "axios": "1.7.4",
31
31
  "chalk": "^2.4.1",
32
32
  "glob": "^8.1.0",
@@ -20,7 +20,13 @@ export function getSwiftSnippet(dsn: string): string {
20
20
  return ` SentrySDK.start { options in
21
21
  options.dsn = "${dsn}"
22
22
  options.debug = true // Enabled debug when first installing is always helpful
23
- options.enableTracing = true
23
+ // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
24
+ // We recommend adjusting this value in production.
25
+ options.tracesSampleRate = 1.0
26
+
27
+ // Sample rate for profiling, applied on top of TracesSampleRate.
28
+ // We recommend adjusting this value in production.
29
+ options.profilesSampleRate = 1.0
24
30
 
25
31
  // Uncomment the following lines to add more data to your events
26
32
  // options.attachScreenshot = true // This adds a screenshot to the error events
@@ -34,7 +40,13 @@ export function getObjcSnippet(dsn: string): string {
34
40
  return ` [SentrySDK startWithConfigureOptions:^(SentryOptions * options) {
35
41
  options.dsn = @"${dsn}";
36
42
  options.debug = YES; // Enabled debug when first installing is always helpful
37
- options.enableTracing = YES;
43
+ // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
44
+ // We recommend adjusting this value in production.
45
+ options.tracesSampleRate = @1.0;
46
+
47
+ // Sample rate for profiling, applied on top of TracesSampleRate.
48
+ // We recommend adjusting this value in production.
49
+ options.profilesSampleRate = @1.0;
38
50
 
39
51
  //Uncomment the following lines to add more data to your events
40
52
  //options.attachScreenshot = YES; //This will add a screenshot to the error events
@@ -94,6 +94,7 @@ export async function runNextjsWizardWithTelemetry(
94
94
  const { packageManager: packageManagerFromInstallStep } =
95
95
  await installPackage({
96
96
  packageName: '@sentry/nextjs@^8',
97
+ packageNameDisplayLabel: '@sentry/nextjs',
97
98
  alreadyInstalled: !!packageJson?.dependencies?.['@sentry/nextjs'],
98
99
  });
99
100
 
@@ -0,0 +1,166 @@
1
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
2
+ import * as clack from '@clack/prompts';
3
+ import * as Sentry from '@sentry/node';
4
+ import { lt, minVersion } from 'semver';
5
+ import type { WizardOptions } from '../utils/types';
6
+ import { traceStep, withTelemetry } from '../telemetry';
7
+ import {
8
+ abort,
9
+ abortIfCancelled,
10
+ addDotEnvSentryBuildPluginFile,
11
+ askShouldCreateExampleComponent,
12
+ askShouldCreateExamplePage,
13
+ confirmContinueIfNoOrDirtyGitRepo,
14
+ ensurePackageIsInstalled,
15
+ getOrAskForProjectData,
16
+ getPackageDotJson,
17
+ installPackage,
18
+ printWelcome,
19
+ runPrettierIfInstalled,
20
+ } from '../utils/clack-utils';
21
+ import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
22
+ import { addSDKModule, getNuxtConfig, createConfigFiles } from './sdk-setup';
23
+ import {
24
+ createExampleComponent,
25
+ createExamplePage,
26
+ supportsExamplePage,
27
+ } from './sdk-example';
28
+ import { isNuxtV4 } from './utils';
29
+ import chalk from 'chalk';
30
+
31
+ export function runNuxtWizard(options: WizardOptions) {
32
+ return withTelemetry(
33
+ {
34
+ enabled: options.telemetryEnabled,
35
+ integration: 'nuxt',
36
+ wizardOptions: options,
37
+ },
38
+ () => runNuxtWizardWithTelemetry(options),
39
+ );
40
+ }
41
+
42
+ export async function runNuxtWizardWithTelemetry(
43
+ options: WizardOptions,
44
+ ): Promise<void> {
45
+ printWelcome({
46
+ wizardName: 'Sentry Nuxt Wizard',
47
+ promoCode: options.promoCode,
48
+ telemetryEnabled: options.telemetryEnabled,
49
+ });
50
+
51
+ await confirmContinueIfNoOrDirtyGitRepo();
52
+
53
+ const packageJson = await getPackageDotJson();
54
+
55
+ await ensurePackageIsInstalled(packageJson, 'nuxt', 'Nuxt');
56
+
57
+ const nuxtVersion = getPackageVersion('nuxt', packageJson);
58
+ Sentry.setTag('nuxt-version', nuxtVersion);
59
+
60
+ const minVer = minVersion(nuxtVersion || '0.0.0');
61
+
62
+ if (!nuxtVersion || !minVer || lt(minVer, '3.7.0')) {
63
+ clack.log.warn(
64
+ "It seems you're using a Nuxt version <3.7.0 which is not supported by Sentry.\nWe recommend upgrading to the latest version before you continue.",
65
+ );
66
+ const shouldContinue = await abortIfCancelled(
67
+ clack.select({
68
+ message: 'Do you want to continue anyway?',
69
+ options: [
70
+ {
71
+ label: 'Yes, continue',
72
+ hint: 'The SDK might not work correctly',
73
+ value: true,
74
+ },
75
+ { label: "No, I'll upgrade first", value: false },
76
+ ],
77
+ }),
78
+ );
79
+ if (!shouldContinue) {
80
+ await abort('Exiting Wizard', 0);
81
+ return;
82
+ }
83
+ }
84
+
85
+ const { authToken, selectedProject, selfHosted, sentryUrl } =
86
+ await getOrAskForProjectData(options, 'javascript-nuxt');
87
+
88
+ const sdkAlreadyInstalled = hasPackageInstalled('@sentry/nuxt', packageJson);
89
+ Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
90
+
91
+ await installPackage({
92
+ packageName: '@sentry/nuxt',
93
+ alreadyInstalled: sdkAlreadyInstalled,
94
+ });
95
+
96
+ await addDotEnvSentryBuildPluginFile(authToken);
97
+
98
+ const nuxtConfig = await traceStep('load-nuxt-config', getNuxtConfig);
99
+
100
+ const projectData = {
101
+ org: selectedProject.organization.slug,
102
+ project: selectedProject.slug,
103
+ projectId: selectedProject.id,
104
+ url: sentryUrl,
105
+ selfHosted,
106
+ };
107
+
108
+ await traceStep('configure-sdk', async () => {
109
+ await addSDKModule(nuxtConfig, projectData);
110
+ await createConfigFiles(selectedProject.keys[0].dsn.public);
111
+ });
112
+
113
+ let shouldCreateExamplePage = false;
114
+ let shouldCreateExampleButton = false;
115
+
116
+ const isV4 = await isNuxtV4(nuxtConfig, nuxtVersion);
117
+ const canCreateExamplePage = await supportsExamplePage(isV4);
118
+ Sentry.setTag('supports-example-page-creation', canCreateExamplePage);
119
+
120
+ if (canCreateExamplePage) {
121
+ shouldCreateExamplePage = await askShouldCreateExamplePage();
122
+
123
+ if (shouldCreateExamplePage) {
124
+ await traceStep('create-example-page', async () =>
125
+ createExamplePage(isV4, projectData),
126
+ );
127
+ }
128
+ } else {
129
+ shouldCreateExampleButton = await askShouldCreateExampleComponent();
130
+
131
+ if (shouldCreateExampleButton) {
132
+ await traceStep('create-example-component', async () =>
133
+ createExampleComponent(isV4),
134
+ );
135
+ }
136
+ }
137
+
138
+ await runPrettierIfInstalled();
139
+
140
+ clack.outro(
141
+ buildOutroMessage(shouldCreateExamplePage, shouldCreateExampleButton),
142
+ );
143
+ }
144
+
145
+ function buildOutroMessage(
146
+ shouldCreateExamplePage: boolean,
147
+ shouldCreateExampleButton: boolean,
148
+ ): string {
149
+ let msg = chalk.green('\nSuccessfully installed the Sentry Nuxt SDK!');
150
+
151
+ if (shouldCreateExamplePage) {
152
+ msg += `\n\nYou can validate your setup by visiting ${chalk.cyan(
153
+ '"/sentry-example-page"',
154
+ )}.`;
155
+ }
156
+ if (shouldCreateExampleButton) {
157
+ msg += `\n\nYou can validate your setup by adding the ${chalk.cyan(
158
+ '`SentryExampleButton`',
159
+ )} component to a page and triggering it.`;
160
+ }
161
+
162
+ msg += `\n\nCheck out the SDK documentation for further configuration:
163
+ https://docs.sentry.io/platforms/javascript/guides/nuxt/`;
164
+
165
+ return msg;
166
+ }