@sentry/wizard 3.22.1 → 3.22.2

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.
@@ -12,25 +12,27 @@ import * as Sentry from '@sentry/node';
12
12
  import {
13
13
  abort,
14
14
  abortIfCancelled,
15
- addSentryCliConfig,
15
+ addDotEnvSentryBuildPluginFile,
16
16
  askShouldCreateExamplePage,
17
17
  confirmContinueIfNoOrDirtyGitRepo,
18
+ createNewConfigFile,
18
19
  ensurePackageIsInstalled,
19
20
  getOrAskForProjectData,
20
21
  getPackageDotJson,
21
22
  installPackage,
22
23
  isUsingTypeScript,
23
24
  printWelcome,
25
+ showCopyPasteInstructions,
24
26
  } from '../utils/clack-utils';
25
27
  import { SentryProjectData, WizardOptions } from '../utils/types';
26
28
  import {
27
29
  getFullUnderscoreErrorCopyPasteSnippet,
28
30
  getGlobalErrorCopyPasteSnippet,
31
+ getInstrumentationHookContent,
32
+ getInstrumentationHookCopyPasteSnippet,
29
33
  getNextjsConfigCjsAppendix,
30
34
  getNextjsConfigCjsTemplate,
31
35
  getNextjsConfigEsmCopyPasteSnippet,
32
- getNextjsSentryBuildOptionsTemplate,
33
- getNextjsWebpackPluginOptionsTemplate,
34
36
  getSentryConfigContents,
35
37
  getSentryDefaultGlobalErrorPage,
36
38
  getSentryDefaultUnderscoreErrorPage,
@@ -38,6 +40,7 @@ import {
38
40
  getSentryExampleAppDirApiRoute,
39
41
  getSentryExamplePageContents,
40
42
  getSimpleUnderscoreErrorCopyPasteSnippet,
43
+ getWithSentryConfigOptionsTemplate,
41
44
  } from './templates';
42
45
  import { traceStep, withTelemetry } from '../telemetry';
43
46
  import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
@@ -82,7 +85,7 @@ export async function runNextjsWizardWithTelemetry(
82
85
  Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
83
86
 
84
87
  await installPackage({
85
- packageName: '@sentry/nextjs@^7.105.0',
88
+ packageName: '@sentry/nextjs@^8',
86
89
  alreadyInstalled: !!packageJson?.dependencies?.['@sentry/nextjs'],
87
90
  });
88
91
 
@@ -248,7 +251,7 @@ export async function runNextjsWizardWithTelemetry(
248
251
  clack.log.info(
249
252
  `It seems like you already have a custom error page for your app directory.\n\nPlease add the following code to your custom error page\nat ${chalk.cyan(
250
253
  path.join(...appDirLocation, globalErrorPageFile),
251
- )}:`,
254
+ )}:\n`,
252
255
  );
253
256
 
254
257
  // eslint-disable-next-line no-console
@@ -282,7 +285,7 @@ export async function runNextjsWizardWithTelemetry(
282
285
  );
283
286
  }
284
287
 
285
- await addSentryCliConfig({ authToken });
288
+ await addDotEnvSentryBuildPluginFile(authToken);
286
289
 
287
290
  const mightBeUsingVercel = fs.existsSync(
288
291
  path.join(process.cwd(), 'vercel.json'),
@@ -390,23 +393,75 @@ async function createOrMergeNextJsFiles(
390
393
  });
391
394
  }
392
395
 
393
- const sentryWebpackOptionsTemplate = getNextjsWebpackPluginOptionsTemplate(
394
- selectedProject.organization.slug,
395
- selectedProject.slug,
396
- selfHosted,
397
- sentryUrl,
398
- );
396
+ await traceStep('setup-instrumentation-hook', async () => {
397
+ const srcInstrumentationTsExists = fs.existsSync(
398
+ path.join(process.cwd(), 'src', 'instrumentation.ts'),
399
+ );
400
+ const srcInstrumentationJsExists = fs.existsSync(
401
+ path.join(process.cwd(), 'src', 'instrumentation.js'),
402
+ );
403
+ const instrumentationTsExists = fs.existsSync(
404
+ path.join(process.cwd(), 'instrumentation.ts'),
405
+ );
406
+ const instrumentationJsExists = fs.existsSync(
407
+ path.join(process.cwd(), 'instrumentation.js'),
408
+ );
409
+
410
+ let instrumentationHookLocation: 'src' | 'root' | 'does-not-exist';
411
+ if (srcInstrumentationTsExists || srcInstrumentationJsExists) {
412
+ instrumentationHookLocation = 'src';
413
+ } else if (instrumentationTsExists || instrumentationJsExists) {
414
+ instrumentationHookLocation = 'root';
415
+ } else {
416
+ instrumentationHookLocation = 'does-not-exist';
417
+ }
399
418
 
400
- const { tunnelRoute } = sdkConfigOptions;
419
+ if (instrumentationHookLocation === 'does-not-exist') {
420
+ const srcFolderExists = fs.existsSync(path.join(process.cwd(), 'src'));
401
421
 
402
- const sentryBuildOptionsTemplate = getNextjsSentryBuildOptionsTemplate({
403
- tunnelRoute,
404
- });
422
+ const instrumentationHookPath = srcFolderExists
423
+ ? path.join(process.cwd(), 'src', 'instrumentation.ts')
424
+ : path.join(process.cwd(), 'instrumentation.ts');
405
425
 
406
- const nextConfigJs = 'next.config.js';
407
- const nextConfigMjs = 'next.config.mjs';
426
+ const successfullyCreated = await createNewConfigFile(
427
+ instrumentationHookPath,
428
+ getInstrumentationHookContent(srcFolderExists ? 'src' : 'root'),
429
+ );
430
+
431
+ if (!successfullyCreated) {
432
+ await showCopyPasteInstructions(
433
+ 'instrumentation.ts',
434
+ getInstrumentationHookCopyPasteSnippet(
435
+ srcFolderExists ? 'src' : 'root',
436
+ ),
437
+ );
438
+ }
439
+ } else {
440
+ await showCopyPasteInstructions(
441
+ srcInstrumentationTsExists
442
+ ? 'instrumentation.ts'
443
+ : srcInstrumentationJsExists
444
+ ? 'instrumentation.js'
445
+ : instrumentationTsExists
446
+ ? 'instrumentation.ts'
447
+ : 'instrumentation.js',
448
+ getInstrumentationHookCopyPasteSnippet(instrumentationHookLocation),
449
+ );
450
+ }
451
+ });
408
452
 
409
453
  await traceStep('setup-next-config', async () => {
454
+ const withSentryConfigOptionsTemplate = getWithSentryConfigOptionsTemplate({
455
+ orgSlug: selectedProject.organization.slug,
456
+ projectSlug: selectedProject.slug,
457
+ selfHosted,
458
+ url: sentryUrl,
459
+ tunnelRoute: sdkConfigOptions.tunnelRoute,
460
+ });
461
+
462
+ const nextConfigJs = 'next.config.js';
463
+ const nextConfigMjs = 'next.config.mjs';
464
+
410
465
  const nextConfigJsExists = fs.existsSync(
411
466
  path.join(process.cwd(), nextConfigJs),
412
467
  );
@@ -419,10 +474,7 @@ async function createOrMergeNextJsFiles(
419
474
 
420
475
  await fs.promises.writeFile(
421
476
  path.join(process.cwd(), nextConfigJs),
422
- getNextjsConfigCjsTemplate(
423
- sentryWebpackOptionsTemplate,
424
- sentryBuildOptionsTemplate,
425
- ),
477
+ getNextjsConfigCjsTemplate(withSentryConfigOptionsTemplate),
426
478
  { encoding: 'utf8', flag: 'w' },
427
479
  );
428
480
 
@@ -460,10 +512,7 @@ async function createOrMergeNextJsFiles(
460
512
  if (shouldInject) {
461
513
  await fs.promises.appendFile(
462
514
  path.join(process.cwd(), nextConfigJs),
463
- getNextjsConfigCjsAppendix(
464
- sentryWebpackOptionsTemplate,
465
- sentryBuildOptionsTemplate,
466
- ),
515
+ getNextjsConfigCjsAppendix(withSentryConfigOptionsTemplate),
467
516
  'utf8',
468
517
  );
469
518
 
@@ -514,8 +563,7 @@ async function createOrMergeNextJsFiles(
514
563
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
515
564
  mod.exports.default = builders.raw(`withSentryConfig(
516
565
  ${expressionToWrap},
517
- ${sentryWebpackOptionsTemplate},
518
- ${sentryBuildOptionsTemplate}
566
+ ${withSentryConfigOptionsTemplate}
519
567
  )`);
520
568
  const newCode = mod.generate().code;
521
569
 
@@ -550,10 +598,7 @@ async function createOrMergeNextJsFiles(
550
598
 
551
599
  // eslint-disable-next-line no-console
552
600
  console.log(
553
- getNextjsConfigEsmCopyPasteSnippet(
554
- sentryWebpackOptionsTemplate,
555
- sentryBuildOptionsTemplate,
556
- ),
601
+ getNextjsConfigEsmCopyPasteSnippet(withSentryConfigOptionsTemplate),
557
602
  );
558
603
 
559
604
  const shouldContinue = await abortIfCancelled(
@@ -1,30 +1,31 @@
1
1
  import chalk from 'chalk';
2
+ import { makeCodeSnippet } from '../utils/clack-utils';
2
3
 
3
- export function getNextjsWebpackPluginOptionsTemplate(
4
- orgSlug: string,
5
- projectSlug: string,
6
- selfHosted: boolean,
7
- url: string,
8
- ): string {
4
+ type WithSentryConfigOptions = {
5
+ orgSlug: string;
6
+ projectSlug: string;
7
+ selfHosted: boolean;
8
+ url: string;
9
+ tunnelRoute: boolean;
10
+ };
11
+
12
+ export function getWithSentryConfigOptionsTemplate({
13
+ orgSlug,
14
+ projectSlug,
15
+ selfHosted,
16
+ tunnelRoute,
17
+ url,
18
+ }: WithSentryConfigOptions): string {
9
19
  return `{
10
20
  // For all available options, see:
11
21
  // https://github.com/getsentry/sentry-webpack-plugin#options
12
22
 
13
- // Suppresses source map uploading logs during build
14
- silent: true,
15
23
  org: "${orgSlug}",
16
24
  project: "${projectSlug}",${selfHosted ? `\n url: "${url}"` : ''}
17
- }`;
18
- }
19
25
 
20
- type SentryNextjsBuildOptions = {
21
- tunnelRoute: boolean;
22
- };
26
+ // Only print logs for uploading source maps in CI
27
+ silent: !process.env.CI,
23
28
 
24
- export function getNextjsSentryBuildOptionsTemplate({
25
- tunnelRoute,
26
- }: SentryNextjsBuildOptions): string {
27
- return `{
28
29
  // For all available options, see:
29
30
  // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
30
31
 
@@ -48,7 +49,7 @@ export function getNextjsSentryBuildOptionsTemplate({
48
49
  // Automatically tree-shake Sentry logger statements to reduce bundle size
49
50
  disableLogger: true,
50
51
 
51
- // Enables automatic instrumentation of Vercel Cron Monitors.
52
+ // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
52
53
  // See the following for more information:
53
54
  // https://docs.sentry.io/product/crons/
54
55
  // https://vercel.com/docs/cron-jobs
@@ -57,8 +58,7 @@ export function getNextjsSentryBuildOptionsTemplate({
57
58
  }
58
59
 
59
60
  export function getNextjsConfigCjsTemplate(
60
- sentryWebpackPluginOptionsTemplate: string,
61
- sentryBuildOptionsTemplate: string,
61
+ withSentryConfigOptionsTemplate: string,
62
62
  ): string {
63
63
  return `const { withSentryConfig } = require("@sentry/nextjs");
64
64
 
@@ -67,15 +67,13 @@ const nextConfig = {};
67
67
 
68
68
  module.exports = withSentryConfig(
69
69
  nextConfig,
70
- ${sentryWebpackPluginOptionsTemplate},
71
- ${sentryBuildOptionsTemplate}
70
+ ${withSentryConfigOptionsTemplate}
72
71
  );
73
72
  `;
74
73
  }
75
74
 
76
75
  export function getNextjsConfigCjsAppendix(
77
- sentryWebpackPluginOptionsTemplate: string,
78
- sentryBuildOptionsTemplate: string,
76
+ withSentryConfigOptionsTemplate: string,
79
77
  ): string {
80
78
  return `
81
79
 
@@ -85,15 +83,13 @@ const { withSentryConfig } = require("@sentry/nextjs");
85
83
 
86
84
  module.exports = withSentryConfig(
87
85
  module.exports,
88
- ${sentryWebpackPluginOptionsTemplate},
89
- ${sentryBuildOptionsTemplate}
86
+ ${withSentryConfigOptionsTemplate}
90
87
  );
91
88
  `;
92
89
  }
93
90
 
94
91
  export function getNextjsConfigEsmCopyPasteSnippet(
95
- sentryWebpackPluginOptionsTemplate: string,
96
- sentryBuildOptionsTemplate: string,
92
+ withSentryConfigOptionsTemplate: string,
97
93
  ): string {
98
94
  return `
99
95
 
@@ -102,8 +98,7 @@ import { withSentryConfig } from "@sentry/nextjs";
102
98
 
103
99
  export default withSentryConfig(
104
100
  yourNextConfig,
105
- ${sentryWebpackPluginOptionsTemplate},
106
- ${sentryBuildOptionsTemplate}
101
+ ${withSentryConfigOptionsTemplate}
107
102
  );
108
103
  `;
109
104
  }
@@ -152,7 +147,7 @@ export function getSentryConfigContents(
152
147
  if (config === 'server') {
153
148
  spotlightOption = `
154
149
 
155
- // uncomment the line below to enable Spotlight (https://spotlightjs.com)
150
+ // Uncomment the line below to enable Spotlight (https://spotlightjs.com)
156
151
  // spotlight: process.env.NODE_ENV === 'development',
157
152
  `;
158
153
  }
@@ -344,6 +339,45 @@ YourCustomErrorComponent.getInitialProps = async (contextData${
344
339
  `;
345
340
  }
346
341
 
342
+ export function getInstrumentationHookContent(
343
+ instrumentationHookLocation: 'src' | 'root',
344
+ ) {
345
+ return `export async function register() {
346
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
347
+ await import('${
348
+ instrumentationHookLocation === 'root' ? '.' : '..'
349
+ }/sentry.server.config');
350
+ }
351
+
352
+ if (process.env.NEXT_RUNTIME === 'edge') {
353
+ await import('${
354
+ instrumentationHookLocation === 'root' ? '.' : '..'
355
+ }/sentry.edge.config');
356
+ }
357
+ }
358
+ `;
359
+ }
360
+
361
+ export function getInstrumentationHookCopyPasteSnippet(
362
+ instrumentationHookLocation: 'src' | 'root',
363
+ ) {
364
+ return makeCodeSnippet(true, (unchanged, plus) => {
365
+ return unchanged(`export ${plus('async')} function register() {
366
+ ${plus(`if (process.env.NEXT_RUNTIME === 'nodejs') {
367
+ await import('${
368
+ instrumentationHookLocation === 'root' ? '.' : '..'
369
+ }/sentry.server.config');
370
+ }
371
+
372
+ if (process.env.NEXT_RUNTIME === 'edge') {
373
+ await import('${
374
+ instrumentationHookLocation === 'root' ? '.' : '..'
375
+ }/sentry.edge.config');
376
+ }`)}
377
+ }`);
378
+ });
379
+ }
380
+
347
381
  export function getSentryDefaultGlobalErrorPage() {
348
382
  return `"use client";
349
383
 
@@ -3,7 +3,10 @@ import * as clack from '@clack/prompts';
3
3
  import chalk from 'chalk';
4
4
  import { runNextjsWizard } from '../../nextjs/nextjs-wizard';
5
5
  import { traceStep } from '../../telemetry';
6
- import { abortIfCancelled, addSentryCliConfig } from '../../utils/clack-utils';
6
+ import {
7
+ abortIfCancelled,
8
+ addDotEnvSentryBuildPluginFile,
9
+ } from '../../utils/clack-utils';
7
10
  import { WizardOptions } from '../../utils/types';
8
11
 
9
12
  import { SourceMapUploadToolConfigurationOptions } from './types';
@@ -99,7 +102,7 @@ In case you already tried the wizard, we can also show you how to configure your
99
102
  );
100
103
 
101
104
  await traceStep('nextjs-manual-sentryclirc', () =>
102
- addSentryCliConfig({ authToken: options.authToken }),
105
+ addDotEnvSentryBuildPluginFile(options.authToken),
103
106
  );
104
107
  }
105
108
 
@@ -800,7 +800,7 @@ ${chalk.cyan('https://github.com/getsentry/sentry-wizard/issues')}`);
800
800
  clack.log.info(`In the meantime, we'll add a dummy auth token (${chalk.cyan(
801
801
  `"${DUMMY_AUTH_TOKEN}"`,
802
802
  )}) for you to replace later.
803
- Create your auth token here:
803
+ Create your auth token here:
804
804
  ${chalk.cyan(
805
805
  selfHosted
806
806
  ? `${sentryUrl}organizations/${selectedProject.organization.slug}/settings/auth-tokens`