@sentry/wizard 3.34.2 → 3.34.4

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 (50) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/e2e-tests/jest.config.d.ts +1 -0
  3. package/dist/e2e-tests/jest.config.js +5 -0
  4. package/dist/e2e-tests/jest.config.js.map +1 -1
  5. package/dist/e2e-tests/tests/nextjs.test.d.ts +1 -0
  6. package/dist/e2e-tests/tests/nextjs.test.js +221 -0
  7. package/dist/e2e-tests/tests/nextjs.test.js.map +1 -0
  8. package/dist/e2e-tests/tests/remix.test.js +47 -43
  9. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/sveltekit.test.d.ts +1 -0
  11. package/dist/e2e-tests/tests/sveltekit.test.js +186 -0
  12. package/dist/e2e-tests/tests/sveltekit.test.js.map +1 -0
  13. package/dist/e2e-tests/utils/index.d.ts +13 -2
  14. package/dist/e2e-tests/utils/index.js +45 -13
  15. package/dist/e2e-tests/utils/index.js.map +1 -1
  16. package/dist/package.json +5 -7
  17. package/dist/src/apple/templates.d.ts +1 -1
  18. package/dist/src/apple/templates.js +6 -2
  19. package/dist/src/apple/templates.js.map +1 -1
  20. package/dist/src/apple/xcode-manager.js +2 -1
  21. package/dist/src/apple/xcode-manager.js.map +1 -1
  22. package/dist/src/nextjs/nextjs-wizard.js +68 -47
  23. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  24. package/dist/src/nextjs/templates.d.ts +1 -0
  25. package/dist/src/nextjs/templates.js +5 -1
  26. package/dist/src/nextjs/templates.js.map +1 -1
  27. package/e2e-tests/.env.example +11 -0
  28. package/e2e-tests/jest.config.ts +8 -1
  29. package/e2e-tests/package.json +14 -0
  30. package/e2e-tests/run.sh +15 -0
  31. package/e2e-tests/test-applications/nextjs-test-app/next.config.mjs +4 -0
  32. package/e2e-tests/test-applications/nextjs-test-app/package.json +22 -0
  33. package/e2e-tests/test-applications/nextjs-test-app/src/app/layout.tsx +20 -0
  34. package/e2e-tests/test-applications/nextjs-test-app/src/app/page.tsx +90 -0
  35. package/e2e-tests/test-applications/sveltekit-test-app/package.json +21 -0
  36. package/e2e-tests/test-applications/sveltekit-test-app/src/app.d.ts +13 -0
  37. package/e2e-tests/test-applications/sveltekit-test-app/src/app.html +11 -0
  38. package/e2e-tests/test-applications/sveltekit-test-app/src/lib/index.ts +1 -0
  39. package/e2e-tests/test-applications/sveltekit-test-app/src/routes/+page.svelte +2 -0
  40. package/e2e-tests/test-applications/sveltekit-test-app/svelte.config.js +18 -0
  41. package/e2e-tests/test-applications/sveltekit-test-app/vite.config.ts +6 -0
  42. package/e2e-tests/tests/nextjs.test.ts +161 -0
  43. package/e2e-tests/tests/remix.test.ts +35 -44
  44. package/e2e-tests/tests/sveltekit.test.ts +154 -0
  45. package/e2e-tests/utils/index.ts +54 -11
  46. package/package.json +5 -7
  47. package/src/apple/templates.ts +5 -1
  48. package/src/apple/xcode-manager.ts +2 -0
  49. package/src/nextjs/nextjs-wizard.ts +31 -6
  50. package/src/nextjs/templates.ts +15 -0
@@ -0,0 +1,154 @@
1
+ /* eslint-disable jest/expect-expect */
2
+ import { Integration } from '../../lib/Constants';
3
+ import {
4
+ checkEnvBuildPlugin,
5
+ checkFileContents,
6
+ checkFileExists,
7
+ checkIfBuilds,
8
+ checkIfRunsOnDevMode,
9
+ checkIfRunsOnProdMode,
10
+ checkPackageJson,
11
+ cleanupGit,
12
+ KEYS,
13
+ revertLocalChanges,
14
+ startWizardInstance,
15
+ TEST_ARGS,
16
+ } from '../utils';
17
+ import * as path from 'path';
18
+
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);
28
+
29
+ const packageManagerPrompted = await wizardInstance.waitForOutput(
30
+ 'Please select your package manager.',
31
+ {
32
+ optional: true,
33
+ }
34
+ );
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!',
68
+ );
69
+
70
+ wizardInstance.kill();
71
+ });
72
+
73
+ afterAll(() => {
74
+ revertLocalChanges(projectDir);
75
+ cleanupGit(projectDir);
76
+ });
77
+
78
+ test('should have the correct package.json', () => {
79
+ checkPackageJson(projectDir, integration);
80
+ });
81
+
82
+ test('should have the correct .env.sentry-build-plugin', () => {
83
+ checkEnvBuildPlugin(projectDir);
84
+ });
85
+
86
+ test('example page exists', () => {
87
+ checkFileExists(path.resolve(projectDir, 'src/routes/sentry-example/+page.svelte'));
88
+ checkFileExists(path.resolve(projectDir, 'src/routes/sentry-example/+server.js'));
89
+ });
90
+
91
+ test('vite.config contains sentry plugin', () => {
92
+ checkFileContents(path.resolve(projectDir, 'vite.config.ts'), `plugins: [sentrySvelteKit({
93
+ sourceMapsUploadOptions: {
94
+ `);
95
+ });
96
+
97
+ test('hook files created', () => {
98
+ checkFileExists(path.resolve(projectDir, 'src/hooks.server.ts'));
99
+ checkFileExists(path.resolve(projectDir, 'src/hooks.client.ts'));
100
+ });
101
+
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({
107
+ dsn: '${TEST_ARGS.PROJECT_DSN}',
108
+
109
+ tracesSampleRate: 1.0,
110
+
111
+ // This sets the sample rate to be 10%. You may want this to be 100% while
112
+ // in development and sample at a lower rate in production
113
+ replaysSessionSampleRate: 0.1,
114
+
115
+ // If the entire session is not sampled, use the below sample rate to sample
116
+ // sessions when an error occurs.
117
+ replaysOnErrorSampleRate: 1.0,
118
+
119
+ // If you don't want to use Session Replay, just remove the line below:
120
+ 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({
131
+ dsn: '${TEST_ARGS.PROJECT_DSN}',
132
+
133
+ tracesSampleRate: 1.0,
134
+
135
+ // uncomment the line below to enable Spotlight (https://spotlightjs.com)
136
+ // spotlight: import.meta.env.DEV,
137
+ });`])
138
+ });
139
+
140
+
141
+ test('runs on dev mode correctly', async () => {
142
+ await checkIfRunsOnDevMode(projectDir, 'ready in');
143
+ });
144
+
145
+ test('should build successfully', async () => {
146
+ await checkIfBuilds(projectDir, 'Successfully uploaded source maps to Sentry');
147
+ });
148
+
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");
152
+ });
153
+ });
154
+
@@ -16,8 +16,11 @@ export const KEYS = {
16
16
  };
17
17
 
18
18
  export const TEST_ARGS = {
19
- AUTH_TOKEN: 'TEST_AUTH_TOKEN',
20
- PROJECT_DSN: 'https://public@dsn.ingest.sentry.io/1337',
19
+ AUTH_TOKEN: process.env.SENTRY_TEST_AUTH_TOKEN || 'TEST_AUTH_TOKEN',
20
+ PROJECT_DSN:
21
+ process.env.SENTRY_TEST_DSN || 'https://public@dsn.ingest.sentry.io/1337',
22
+ ORG_SLUG: process.env.SENTRY_TEST_ORG || 'TEST_ORG_SLUG',
23
+ PROJECT_SLUG: process.env.SENTRY_TEST_PROJECT || 'TEST_PROJECT_SLUG',
21
24
  };
22
25
 
23
26
  export const log = {
@@ -55,6 +58,28 @@ export class WizardTestEnv {
55
58
  this.taskHandle.stdin.write(input);
56
59
  }
57
60
 
61
+ /**
62
+ * Sends the input and waits for the output.
63
+ * @returns a promise that resolves when the output was found
64
+ * @throws an error when the output was not found within the timeout
65
+ */
66
+ sendStdinAndWaitForOutput(
67
+ input: string | string[],
68
+ output: string,
69
+ options?: { timeout?: number; optional?: boolean },
70
+ ) {
71
+ const outputPromise = this.waitForOutput(output, options);
72
+
73
+ if (Array.isArray(input)) {
74
+ for (const i of input) {
75
+ this.sendStdin(i);
76
+ }
77
+ } else {
78
+ this.sendStdin(input);
79
+ }
80
+ return outputPromise;
81
+ }
82
+
58
83
  /**
59
84
  * Waits for the provided output with `.includes()` logic.
60
85
  *
@@ -71,7 +96,7 @@ export class WizardTestEnv {
71
96
  } = {},
72
97
  ) {
73
98
  const { timeout, optional } = {
74
- timeout: 30_000,
99
+ timeout: 60_000,
75
100
  optional: false,
76
101
  ...options,
77
102
  };
@@ -122,7 +147,7 @@ export function initGit(projectDir: string): void {
122
147
  execSync('git commit -m init', { cwd: projectDir });
123
148
  } catch (e) {
124
149
  log.error('Error initializing git');
125
- throw e;
150
+ log.error(e);
126
151
  }
127
152
  }
128
153
 
@@ -139,7 +164,7 @@ export function cleanupGit(projectDir: string): void {
139
164
  execSync(`rm -rf ${projectDir}/.git`);
140
165
  } catch (e) {
141
166
  log.error('Error cleaning up git');
142
- throw e;
167
+ log.error(e);
143
168
  }
144
169
  }
145
170
 
@@ -159,7 +184,7 @@ export function revertLocalChanges(projectDir: string): void {
159
184
  execSync('git clean -fd .', { cwd: projectDir });
160
185
  } catch (e) {
161
186
  log.error('Error reverting local changes');
162
- throw e;
187
+ log.error(e);
163
188
  }
164
189
  }
165
190
 
@@ -173,6 +198,7 @@ export function revertLocalChanges(projectDir: string): void {
173
198
  export function startWizardInstance(
174
199
  integration: Integration,
175
200
  projectDir: string,
201
+ debug = false,
176
202
  ): WizardTestEnv {
177
203
  const binPath = path.join(__dirname, '../../dist/bin.js');
178
204
 
@@ -191,8 +217,12 @@ export function startWizardInstance(
191
217
  TEST_ARGS.AUTH_TOKEN,
192
218
  '--preSelectedProject.dsn',
193
219
  TEST_ARGS.PROJECT_DSN,
220
+ '--preSelectedProject.orgSlug',
221
+ TEST_ARGS.ORG_SLUG,
222
+ '--preSelectedProject.projectSlug',
223
+ TEST_ARGS.PROJECT_SLUG,
194
224
  ],
195
- { cwd: projectDir },
225
+ { cwd: projectDir, debug },
196
226
  );
197
227
  }
198
228
 
@@ -266,7 +296,11 @@ export async function checkIfBuilds(
266
296
  cwd: projectDir,
267
297
  });
268
298
 
269
- await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
299
+ await expect(
300
+ testEnv.waitForOutput(expectedOutput, {
301
+ timeout: 120_000,
302
+ }),
303
+ ).resolves.toBe(true);
270
304
  }
271
305
 
272
306
  /**
@@ -280,7 +314,11 @@ export async function checkIfRunsOnDevMode(
280
314
  ) {
281
315
  const testEnv = new WizardTestEnv('npm', ['run', 'dev'], { cwd: projectDir });
282
316
 
283
- await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
317
+ await expect(
318
+ testEnv.waitForOutput(expectedOutput, {
319
+ timeout: 120_000,
320
+ }),
321
+ ).resolves.toBe(true);
284
322
  testEnv.kill();
285
323
  }
286
324
 
@@ -292,11 +330,16 @@ export async function checkIfRunsOnDevMode(
292
330
  export async function checkIfRunsOnProdMode(
293
331
  projectDir: string,
294
332
  expectedOutput: string,
333
+ startCommand = 'start',
295
334
  ) {
296
- const testEnv = new WizardTestEnv('npm', ['run', 'start'], {
335
+ const testEnv = new WizardTestEnv('npm', ['run', startCommand], {
297
336
  cwd: projectDir,
298
337
  });
299
338
 
300
- await expect(testEnv.waitForOutput(expectedOutput)).resolves.toBe(true);
339
+ await expect(
340
+ testEnv.waitForOutput(expectedOutput, {
341
+ timeout: 120_000,
342
+ }),
343
+ ).resolves.toBe(true);
301
344
  testEnv.kill();
302
345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wizard",
3
- "version": "3.34.2",
3
+ "version": "3.34.4",
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",
@@ -55,6 +55,7 @@
55
55
  "@types/yargs": "^16.0.9",
56
56
  "@typescript-eslint/eslint-plugin": "^5.13.0",
57
57
  "@typescript-eslint/parser": "^5.13.0",
58
+ "dotenv": "^16.4.5",
58
59
  "eslint": "^8.18.0",
59
60
  "eslint-config-prettier": "^8.3.0",
60
61
  "eslint-plugin-jest": "^25.3.0",
@@ -66,9 +67,6 @@
66
67
  "tsx": "^3.14.0",
67
68
  "typescript": "^5.0.4"
68
69
  },
69
- "resolutions": {
70
- "**/xmldom": "^0.6.0"
71
- },
72
70
  "engines": {
73
71
  "node": ">=14.18.0",
74
72
  "npm": ">=3.10.7",
@@ -87,7 +85,7 @@
87
85
  "fix:prettier": "prettier --write \"{lib,src,test}/**/*.ts\"",
88
86
  "fix:eslint": "eslint . --format stylish --fix",
89
87
  "test": "yarn build && jest",
90
- "test:e2e": "yarn build && jest -c=\"./e2e-tests/jest.config.ts\" ./e2e-tests/",
88
+ "test:e2e": "yarn build && ./e2e-tests/run.sh",
91
89
  "try": "ts-node bin.ts",
92
90
  "try:uninstall": "ts-node bin.ts --uninstall",
93
91
  "test:watch": "jest --watch"
@@ -2,9 +2,13 @@ export function getRunScriptTemplate(
2
2
  orgSlug: string,
3
3
  projectSlug: string,
4
4
  uploadSource = true,
5
+ includeHomebrewPath = false,
5
6
  ): string {
6
7
  // eslint-disable-next-line no-useless-escape
7
- return `# This script is responsable to upload debug symbols and source context for Sentry.\\nif which sentry-cli >/dev/null; then\\nexport SENTRY_ORG=${orgSlug}\\nexport SENTRY_PROJECT=${projectSlug}\\nERROR=$(sentry-cli debug-files upload ${
8
+ const includeHomebrew = includeHomebrewPath
9
+ ? '\\nif [[ "$(uname -m)" == arm64 ]]; then\\nexport PATH="/opt/homebrew/bin:$PATH"\\nfi'
10
+ : '';
11
+ return `# This script is responsable to upload debug symbols and source context for Sentry.${includeHomebrew}\\nif which sentry-cli >/dev/null; then\\nexport SENTRY_ORG=${orgSlug}\\nexport SENTRY_PROJECT=${projectSlug}\\nERROR=$(sentry-cli debug-files upload ${
8
12
  uploadSource ? '--include-sources ' : ''
9
13
  }"$DWARF_DSYM_FOLDER_PATH" 2>&1 >/dev/null)\\nif [ ! $? -eq 0 ]; then\\necho "warning: sentry-cli - $ERROR"\\nfi\\nelse\\necho "warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases"\\nfi\\n`;
10
14
  }
@@ -164,6 +164,7 @@ function addUploadSymbolsScript(
164
164
  }
165
165
  }
166
166
 
167
+ const isHomebrewInstalled = fs.existsSync('/opt/homebrew/bin/sentry-cli');
167
168
  xcodeProject.addBuildPhase(
168
169
  [],
169
170
  'PBXShellScriptBuildPhase',
@@ -178,6 +179,7 @@ function addUploadSymbolsScript(
178
179
  sentryProject.organization.slug,
179
180
  sentryProject.slug,
180
181
  uploadSource,
182
+ isHomebrewInstalled,
181
183
  ),
182
184
  },
183
185
  );
@@ -44,6 +44,7 @@ import {
44
44
  getSentryExamplePageContents,
45
45
  getSimpleUnderscoreErrorCopyPasteSnippet,
46
46
  getWithSentryConfigOptionsTemplate,
47
+ getNextjsConfigMjsTemplate,
47
48
  } from './templates';
48
49
  import { traceStep, withTelemetry } from '../telemetry';
49
50
  import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
@@ -196,7 +197,7 @@ export async function runNextjsWizardWithTelemetry(
196
197
 
197
198
  const shouldContinue = await abortIfCancelled(
198
199
  clack.confirm({
199
- message: `Did add the code to your ${chalk.cyan(
200
+ message: `Did you add the code to your ${chalk.cyan(
200
201
  path.join(...pagesLocation, underscoreErrorPageFile),
201
202
  )} file as described above?`,
202
203
  active: 'Yes',
@@ -278,7 +279,7 @@ export async function runNextjsWizardWithTelemetry(
278
279
 
279
280
  const shouldContinue = await abortIfCancelled(
280
281
  clack.confirm({
281
- message: `Did add the code to your ${chalk.cyan(
282
+ message: `Did you add the code to your ${chalk.cyan(
282
283
  path.join(...appDirLocation, globalErrorPageFile),
283
284
  )} file as described above?`,
284
285
  active: 'Yes',
@@ -564,15 +565,39 @@ async function createOrMergeNextJsFiles(
564
565
  if (!foundNextConfigFile) {
565
566
  Sentry.setTag('next-config-strategy', 'create');
566
567
 
568
+ // Try to figure out whether the user prefers ESM
569
+ let isTypeModule = false;
570
+ try {
571
+ const packageJsonText = await fs.promises.readFile(
572
+ path.join(process.cwd(), 'package.json'),
573
+ 'utf8',
574
+ );
575
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
576
+ const packageJson = JSON.parse(packageJsonText);
577
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
578
+ if (packageJson.type === 'module') {
579
+ isTypeModule = true;
580
+ }
581
+ } catch {
582
+ // noop
583
+ }
584
+
585
+ // We are creating `next.config.(m)js` files by default as they are supported by the most Next.js versions
586
+ const configFilename = isTypeModule
587
+ ? nextConfigPossibleFilesMap.mjs
588
+ : nextConfigPossibleFilesMap.js;
589
+ const configContent = isTypeModule
590
+ ? getNextjsConfigMjsTemplate(withSentryConfigOptionsTemplate)
591
+ : getNextjsConfigCjsTemplate(withSentryConfigOptionsTemplate);
592
+
567
593
  await fs.promises.writeFile(
568
- // We are creating a `next.config.js` file by default as it is supported by the most Next.js versions
569
- path.join(process.cwd(), nextConfigPossibleFilesMap.js),
570
- getNextjsConfigCjsTemplate(withSentryConfigOptionsTemplate),
594
+ path.join(process.cwd(), configFilename),
595
+ configContent,
571
596
  { encoding: 'utf8', flag: 'w' },
572
597
  );
573
598
 
574
599
  clack.log.success(
575
- `Created ${chalk.cyan('next.config.js')} with Sentry configuration.`,
600
+ `Created ${chalk.cyan(configFilename)} with Sentry configuration.`,
576
601
  );
577
602
 
578
603
  return;
@@ -81,6 +81,21 @@ module.exports = withSentryConfig(
81
81
  `;
82
82
  }
83
83
 
84
+ export function getNextjsConfigMjsTemplate(
85
+ withSentryConfigOptionsTemplate: string,
86
+ ): string {
87
+ return `import { withSentryConfig } from "@sentry/nextjs";
88
+
89
+ /** @type {import('next').NextConfig} */
90
+ const nextConfig = {};
91
+
92
+ export default withSentryConfig(
93
+ nextConfig,
94
+ ${withSentryConfigOptionsTemplate}
95
+ );
96
+ `;
97
+ }
98
+
84
99
  export function getNextjsConfigCjsAppendix(
85
100
  withSentryConfigOptionsTemplate: string,
86
101
  ): string {