@sentry/wizard 3.34.4 → 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 +13 -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 +1 -1
  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 +1 -1
  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
package/src/run.ts CHANGED
@@ -8,6 +8,7 @@ import type { PreselectedProject, WizardOptions } from './utils/types';
8
8
  import { runAndroidWizard } from './android/android-wizard';
9
9
  import { runAppleWizard } from './apple/apple-wizard';
10
10
  import { runNextjsWizard } from './nextjs/nextjs-wizard';
11
+ import { runNuxtWizard } from './nuxt/nuxt-wizard';
11
12
  import { runRemixWizard } from './remix/remix-wizard';
12
13
  import { runSvelteKitWizard } from './sveltekit/sveltekit-wizard';
13
14
  import { runSourcemapsWizard } from './sourcemaps/sourcemaps-wizard';
@@ -22,6 +23,7 @@ type WizardIntegration =
22
23
  | 'cordova'
23
24
  | 'electron'
24
25
  | 'nextjs'
26
+ | 'nuxt'
25
27
  | 'remix'
26
28
  | 'sveltekit'
27
29
  | 'sourcemaps';
@@ -103,6 +105,7 @@ export async function run(argv: Args) {
103
105
  { value: 'cordova', label: 'Cordova' },
104
106
  { value: 'electron', label: 'Electron' },
105
107
  { value: 'nextjs', label: 'Next.js' },
108
+ { value: 'nuxt', label: 'Nuxt' },
106
109
  { value: 'remix', label: 'Remix' },
107
110
  { value: 'sveltekit', label: 'SvelteKit' },
108
111
  { value: 'sourcemaps', label: 'Configure Source Maps Upload' },
@@ -148,6 +151,10 @@ export async function run(argv: Args) {
148
151
  await runNextjsWizard(wizardOptions);
149
152
  break;
150
153
 
154
+ case 'nuxt':
155
+ await runNuxtWizard(wizardOptions);
156
+ break;
157
+
151
158
  case 'remix':
152
159
  await runRemixWizard(wizardOptions);
153
160
  break;
@@ -51,7 +51,7 @@ export const configureRollupPlugin: SourceMapUploadToolConfigurationFunction =
51
51
 
52
52
  clack.log.step(`Add the following code to your rollup config:`);
53
53
 
54
- // Intentially logging directly to console here so that the code can be copied/pasted directly
54
+ // Intentionally logging directly to console here so that the code can be copied/pasted directly
55
55
  // eslint-disable-next-line no-console
56
56
  console.log(getCodeSnippet(options));
57
57
 
@@ -95,7 +95,8 @@ export async function runSvelteKitWizardWithTelemetry(
95
95
  Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
96
96
 
97
97
  await installPackage({
98
- packageName: '@sentry/sveltekit',
98
+ packageName: '@sentry/sveltekit@^8',
99
+ packageNameDisplayLabel: '@sentry/sveltekit',
99
100
  alreadyInstalled: sdkAlreadyInstalled,
100
101
  });
101
102
 
@@ -354,17 +354,21 @@ export async function installPackage({
354
354
  packageName,
355
355
  alreadyInstalled,
356
356
  askBeforeUpdating = true,
357
+ packageNameDisplayLabel,
357
358
  }: {
359
+ /** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
358
360
  packageName: string;
359
361
  alreadyInstalled: boolean;
360
362
  askBeforeUpdating?: boolean;
363
+ /** Overrides what is shown in the installation logs in place of the `packageName` option. Useful if the `packageName` is ugly (e.g. `@sentry/nextjs@^8`) */
364
+ packageNameDisplayLabel?: string;
361
365
  }): Promise<{ packageManager?: PackageManager }> {
362
366
  return traceStep('install-package', async () => {
363
367
  if (alreadyInstalled && askBeforeUpdating) {
364
368
  const shouldUpdatePackage = await abortIfCancelled(
365
369
  clack.confirm({
366
370
  message: `The ${chalk.bold.cyan(
367
- packageName,
371
+ packageNameDisplayLabel ?? packageName,
368
372
  )} package is already installed. Do you want to update it to the latest version?`,
369
373
  }),
370
374
  );
@@ -380,7 +384,7 @@ export async function installPackage({
380
384
 
381
385
  sdkInstallSpinner.start(
382
386
  `${alreadyInstalled ? 'Updating' : 'Installing'} ${chalk.bold.cyan(
383
- packageName,
387
+ packageNameDisplayLabel ?? packageName,
384
388
  )} with ${chalk.bold(packageManager.label)}.`,
385
389
  );
386
390
 
@@ -425,7 +429,7 @@ export async function installPackage({
425
429
 
426
430
  sdkInstallSpinner.stop(
427
431
  `${alreadyInstalled ? 'Updated' : 'Installed'} ${chalk.bold.cyan(
428
- packageName,
432
+ packageNameDisplayLabel ?? packageName,
429
433
  )} with ${chalk.bold(packageManager.label)}.`,
430
434
  );
431
435
 
@@ -593,7 +597,7 @@ SENTRY_AUTH_TOKEN=${authToken}
593
597
 
594
598
  if (hasAuthToken) {
595
599
  clack.log.warn(
596
- `${chalk.bold(
600
+ `${chalk.bold.cyan(
597
601
  SENTRY_DOT_ENV_FILE,
598
602
  )} already has auth token. Will not add one.`,
599
603
  );
@@ -608,11 +612,11 @@ SENTRY_AUTH_TOKEN=${authToken}
608
612
  },
609
613
  );
610
614
  clack.log.success(
611
- `Added auth token to ${chalk.bold(SENTRY_DOT_ENV_FILE)}`,
615
+ `Added auth token to ${chalk.bold.cyan(SENTRY_DOT_ENV_FILE)}`,
612
616
  );
613
617
  } catch {
614
618
  clack.log.warning(
615
- `Failed to add auth token to ${chalk.bold(
619
+ `Failed to add auth token to ${chalk.bold.cyan(
616
620
  SENTRY_DOT_ENV_FILE,
617
621
  )}. Uploading source maps during build will likely not work locally.`,
618
622
  );
@@ -625,13 +629,13 @@ SENTRY_AUTH_TOKEN=${authToken}
625
629
  flag: 'w',
626
630
  });
627
631
  clack.log.success(
628
- `Created ${chalk.bold(
632
+ `Created ${chalk.bold.cyan(
629
633
  SENTRY_DOT_ENV_FILE,
630
634
  )} with auth token for you to test source map uploading locally.`,
631
635
  );
632
636
  } catch {
633
637
  clack.log.warning(
634
- `Failed to create ${chalk.bold(
638
+ `Failed to create ${chalk.bold.cyan(
635
639
  SENTRY_DOT_ENV_FILE,
636
640
  )} with auth token. Uploading source maps during build will likely not work locally.`,
637
641
  );
@@ -850,6 +854,7 @@ export async function getOrAskForProjectData(
850
854
  options: WizardOptions,
851
855
  platform?:
852
856
  | 'javascript-nextjs'
857
+ | 'javascript-nuxt'
853
858
  | 'javascript-remix'
854
859
  | 'javascript-sveltekit'
855
860
  | 'apple-ios'
@@ -1011,6 +1016,7 @@ async function askForWizardLogin(options: {
1011
1016
  promoCode?: string;
1012
1017
  platform?:
1013
1018
  | 'javascript-nextjs'
1019
+ | 'javascript-nuxt'
1014
1020
  | 'javascript-remix'
1015
1021
  | 'javascript-sveltekit'
1016
1022
  | 'apple-ios'
@@ -1413,6 +1419,24 @@ export async function askShouldCreateExamplePage(
1413
1419
  );
1414
1420
  }
1415
1421
 
1422
+ export async function askShouldCreateExampleComponent(): Promise<boolean> {
1423
+ return traceStep('ask-create-example-component', () =>
1424
+ abortIfCancelled(
1425
+ clack.select({
1426
+ message: `Do you want to create an example component to test your Sentry setup?`,
1427
+ options: [
1428
+ {
1429
+ value: true,
1430
+ label: 'Yes',
1431
+ hint: 'Recommended - Check your git status before committing!',
1432
+ },
1433
+ { value: false, label: 'No' },
1434
+ ],
1435
+ }),
1436
+ ),
1437
+ );
1438
+ }
1439
+
1416
1440
  export async function featureSelectionPrompt<F extends ReadonlyArray<Feature>>(
1417
1441
  features: F,
1418
1442
  ): Promise<{ [key in F[number]['id']]: boolean }> {
@@ -0,0 +1,228 @@
1
+ import {
2
+ getDefaultNuxtConfig,
3
+ getNuxtModuleFallbackTemplate,
4
+ getSentryConfigContents,
5
+ } from '../../src/nuxt/templates';
6
+
7
+ describe('Nuxt code templates', () => {
8
+ describe('getDefaultNuxtConfig', () => {
9
+ it('returns a default nuxt config', () => {
10
+ expect(getDefaultNuxtConfig()).toMatchInlineSnapshot(`
11
+ "// https://nuxt.com/docs/api/configuration/nuxt-config
12
+ export default defineNuxtConfig({
13
+ compatibilityDate: '2024-04-03',
14
+ devtools: { enabled: true }
15
+ })
16
+ "
17
+ `);
18
+ });
19
+ });
20
+
21
+ describe('getSentryConfigContents', () => {
22
+ describe('client config', () => {
23
+ it('generates Sentry config with all features enabled', () => {
24
+ const template = getSentryConfigContents(
25
+ 'https://sentry.io/123',
26
+ 'client',
27
+ {
28
+ performance: true,
29
+ replay: true,
30
+ },
31
+ );
32
+
33
+ expect(template).toMatchInlineSnapshot(`
34
+ "import * as Sentry from "@sentry/nuxt";
35
+
36
+ Sentry.init({
37
+ // If set up, you can use your runtime config here
38
+ // dsn: useRuntimeConfig().public.sentry.dsn,
39
+ dsn: "https://sentry.io/123",
40
+
41
+ // We recommend adjusting this value in production, or using tracesSampler
42
+ // for finer control
43
+ tracesSampleRate: 1.0,
44
+
45
+ // This sets the sample rate to be 10%. You may want this to be 100% while
46
+ // in development and sample at a lower rate in production
47
+ replaysSessionSampleRate: 0.1,
48
+
49
+ // If the entire session is not sampled, use the below sample rate to sample
50
+ // sessions when an error occurs.
51
+ replaysOnErrorSampleRate: 1.0,
52
+
53
+ // If you don't want to use Session Replay, just remove the line below:
54
+ integrations: [Sentry.replayIntegration()],
55
+
56
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
57
+ debug: false,
58
+ });
59
+ "
60
+ `);
61
+ });
62
+
63
+ it('generates Sentry config with performance monitoring disabled', () => {
64
+ const template = getSentryConfigContents(
65
+ 'https://sentry.io/123',
66
+ 'client',
67
+ {
68
+ performance: false,
69
+ replay: true,
70
+ },
71
+ );
72
+
73
+ expect(template).toMatchInlineSnapshot(`
74
+ "import * as Sentry from "@sentry/nuxt";
75
+
76
+ Sentry.init({
77
+ // If set up, you can use your runtime config here
78
+ // dsn: useRuntimeConfig().public.sentry.dsn,
79
+ dsn: "https://sentry.io/123",
80
+
81
+ // This sets the sample rate to be 10%. You may want this to be 100% while
82
+ // in development and sample at a lower rate in production
83
+ replaysSessionSampleRate: 0.1,
84
+
85
+ // If the entire session is not sampled, use the below sample rate to sample
86
+ // sessions when an error occurs.
87
+ replaysOnErrorSampleRate: 1.0,
88
+
89
+ // If you don't want to use Session Replay, just remove the line below:
90
+ integrations: [Sentry.replayIntegration()],
91
+
92
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
93
+ debug: false,
94
+ });
95
+ "
96
+ `);
97
+ });
98
+
99
+ it('generates Sentry config with session replay disabled', () => {
100
+ const template = getSentryConfigContents(
101
+ 'https://sentry.io/123',
102
+ 'client',
103
+ {
104
+ performance: true,
105
+ replay: false,
106
+ },
107
+ );
108
+
109
+ expect(template).toMatchInlineSnapshot(`
110
+ "import * as Sentry from "@sentry/nuxt";
111
+
112
+ Sentry.init({
113
+ // If set up, you can use your runtime config here
114
+ // dsn: useRuntimeConfig().public.sentry.dsn,
115
+ dsn: "https://sentry.io/123",
116
+
117
+ // We recommend adjusting this value in production, or using tracesSampler
118
+ // for finer control
119
+ tracesSampleRate: 1.0,
120
+
121
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
122
+ debug: false,
123
+ });
124
+ "
125
+ `);
126
+ });
127
+
128
+ it('generates Sentry config with performance monitoring and session replay disabled', () => {
129
+ const template = getSentryConfigContents(
130
+ 'https://sentry.io/123',
131
+ 'client',
132
+ {
133
+ performance: false,
134
+ replay: false,
135
+ },
136
+ );
137
+
138
+ expect(template).toMatchInlineSnapshot(`
139
+ "import * as Sentry from "@sentry/nuxt";
140
+
141
+ Sentry.init({
142
+ // If set up, you can use your runtime config here
143
+ // dsn: useRuntimeConfig().public.sentry.dsn,
144
+ dsn: "https://sentry.io/123",
145
+
146
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
147
+ debug: false,
148
+ });
149
+ "
150
+ `);
151
+ });
152
+ });
153
+
154
+ describe('server config', () => {
155
+ it('generates Sentry config with all features enabled', () => {
156
+ const template = getSentryConfigContents(
157
+ 'https://sentry.io/123',
158
+ 'server',
159
+ {
160
+ performance: true,
161
+ replay: true,
162
+ },
163
+ );
164
+
165
+ expect(template).toMatchInlineSnapshot(`
166
+ "import * as Sentry from "@sentry/nuxt";
167
+
168
+ Sentry.init({
169
+ dsn: "https://sentry.io/123",
170
+
171
+ // We recommend adjusting this value in production, or using tracesSampler
172
+ // for finer control
173
+ tracesSampleRate: 1.0,
174
+
175
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
176
+ debug: false,
177
+ });
178
+ "
179
+ `);
180
+ });
181
+
182
+ it('generates Sentry config with performance monitoring disabled', () => {
183
+ const template = getSentryConfigContents(
184
+ 'https://sentry.io/123',
185
+ 'server',
186
+ {
187
+ performance: false,
188
+ replay: true,
189
+ },
190
+ );
191
+
192
+ expect(template).toMatchInlineSnapshot(`
193
+ "import * as Sentry from "@sentry/nuxt";
194
+
195
+ Sentry.init({
196
+ dsn: "https://sentry.io/123",
197
+
198
+ // Setting this option to true will print useful information to the console while you're setting up Sentry.
199
+ debug: false,
200
+ });
201
+ "
202
+ `);
203
+ });
204
+ });
205
+ });
206
+
207
+ describe('getNuxtModuleFallbackTemplate', () => {
208
+ it('generates configuration options for the nuxt config', () => {
209
+ const template = getNuxtModuleFallbackTemplate({
210
+ org: 'my-org',
211
+ project: 'my-project',
212
+ url: 'https://sentry.io',
213
+ selfHosted: false,
214
+ });
215
+
216
+ expect(template).toMatchInlineSnapshot(`
217
+ " modules: ["@sentry/nuxt/module"],
218
+ sentry: {
219
+ sourceMapsUploadOptions: {
220
+ org: "my-org",
221
+ project: "my-project",
222
+ },
223
+ },
224
+ sourcemap: { client: "hidden" },"
225
+ `);
226
+ });
227
+ });
228
+ });