@sentry/wizard 3.25.2 → 3.27.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 (43) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/lib/Steps/OpenSentry.js +1 -1
  3. package/dist/lib/Steps/OpenSentry.js.map +1 -1
  4. package/dist/package.json +2 -1
  5. package/dist/src/nextjs/nextjs-wizard.d.ts +1 -1
  6. package/dist/src/nextjs/nextjs-wizard.js +194 -99
  7. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  8. package/dist/src/nextjs/templates.d.ts +7 -3
  9. package/dist/src/nextjs/templates.js +27 -14
  10. package/dist/src/nextjs/templates.js.map +1 -1
  11. package/dist/src/run.js +1 -1
  12. package/dist/src/run.js.map +1 -1
  13. package/dist/src/sourcemaps/sourcemaps-wizard.js +1 -1
  14. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  15. package/dist/src/telemetry.js +10 -3
  16. package/dist/src/telemetry.js.map +1 -1
  17. package/dist/src/utils/clack-utils.d.ts +4 -1
  18. package/dist/src/utils/clack-utils.js +81 -11
  19. package/dist/src/utils/clack-utils.js.map +1 -1
  20. package/dist/src/utils/package-manager.d.ts +5 -2
  21. package/dist/src/utils/package-manager.js +44 -56
  22. package/dist/src/utils/package-manager.js.map +1 -1
  23. package/dist/src/utils/types.d.ts +6 -0
  24. package/dist/src/utils/types.js.map +1 -1
  25. package/dist/src/utils/url.js +7 -2
  26. package/dist/src/utils/url.js.map +1 -1
  27. package/dist/test/nextjs/templates.test.js +79 -1
  28. package/dist/test/nextjs/templates.test.js.map +1 -1
  29. package/dist/test/sourcemaps/tools/sentry-cli.test.js +2 -1
  30. package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -1
  31. package/lib/Steps/OpenSentry.ts +1 -1
  32. package/package.json +2 -1
  33. package/src/nextjs/nextjs-wizard.ts +209 -101
  34. package/src/nextjs/templates.ts +54 -26
  35. package/src/run.ts +1 -1
  36. package/src/sourcemaps/sourcemaps-wizard.ts +1 -1
  37. package/src/telemetry.ts +7 -1
  38. package/src/utils/clack-utils.ts +77 -7
  39. package/src/utils/package-manager.ts +43 -13
  40. package/src/utils/types.ts +7 -0
  41. package/src/utils/url.ts +6 -2
  42. package/test/nextjs/templates.test.ts +296 -2
  43. package/test/sourcemaps/tools/sentry-cli.test.ts +2 -1
@@ -17,6 +17,7 @@ import {
17
17
  confirmContinueIfNoOrDirtyGitRepo,
18
18
  createNewConfigFile,
19
19
  ensurePackageIsInstalled,
20
+ featureSelectionPrompt,
20
21
  getOrAskForProjectData,
21
22
  getPackageDotJson,
22
23
  installPackage,
@@ -24,7 +25,7 @@ import {
24
25
  printWelcome,
25
26
  showCopyPasteInstructions,
26
27
  } from '../utils/clack-utils';
27
- import { SentryProjectData, WizardOptions } from '../utils/types';
28
+ import type { SentryProjectData, WizardOptions } from '../utils/types';
28
29
  import {
29
30
  getFullUnderscoreErrorCopyPasteSnippet,
30
31
  getGlobalErrorCopyPasteSnippet,
@@ -36,7 +37,7 @@ import {
36
37
  getSentryConfigContents,
37
38
  getSentryDefaultGlobalErrorPage,
38
39
  getSentryDefaultUnderscoreErrorPage,
39
- getSentryExampleApiRoute,
40
+ getSentryExamplePagesDirApiRoute,
40
41
  getSentryExampleAppDirApiRoute,
41
42
  getSentryExamplePageContents,
42
43
  getSimpleUnderscoreErrorCopyPasteSnippet,
@@ -93,9 +94,12 @@ export async function runNextjsWizardWithTelemetry(
93
94
 
94
95
  await traceStep('configure-sdk', async () => {
95
96
  const tunnelRoute = await askShouldSetTunnelRoute();
97
+ const reactComponentAnnotation =
98
+ await askShouldEnableReactComponentAnnotation();
96
99
 
97
100
  await createOrMergeNextJsFiles(selectedProject, selfHosted, sentryUrl, {
98
101
  tunnelRoute,
102
+ reactComponentAnnotation,
99
103
  });
100
104
  });
101
105
 
@@ -321,6 +325,7 @@ ${chalk.dim(
321
325
 
322
326
  type SDKConfigOptions = {
323
327
  tunnelRoute: boolean;
328
+ reactComponentAnnotation: boolean;
324
329
  };
325
330
 
326
331
  async function createOrMergeNextJsFiles(
@@ -329,6 +334,23 @@ async function createOrMergeNextJsFiles(
329
334
  sentryUrl: string,
330
335
  sdkConfigOptions: SDKConfigOptions,
331
336
  ) {
337
+ const selectedFeatures = await featureSelectionPrompt([
338
+ {
339
+ id: 'performance',
340
+ prompt: `Do you want to enable ${chalk.bold(
341
+ 'Tracing',
342
+ )} to track the performance of your application?`,
343
+ enabledHint: 'recommended',
344
+ },
345
+ {
346
+ id: 'replay',
347
+ prompt: `Do you want to enable ${chalk.bold(
348
+ 'Sentry Session Replay',
349
+ )} to get reproduction of frontend errors via user sessions?`,
350
+ enabledHint: 'recommended, but increases bundle size',
351
+ },
352
+ ] as const);
353
+
332
354
  const typeScriptDetected = isUsingTypeScript();
333
355
 
334
356
  const configVariants = ['server', 'client', 'edge'] as const;
@@ -386,6 +408,7 @@ async function createOrMergeNextJsFiles(
386
408
  getSentryConfigContents(
387
409
  selectedProject.keys[0].dsn.public,
388
410
  configVariant,
411
+ selectedFeatures,
389
412
  ),
390
413
  { encoding: 'utf8', flag: 'w' },
391
414
  );
@@ -400,60 +423,85 @@ async function createOrMergeNextJsFiles(
400
423
  }
401
424
 
402
425
  await traceStep('setup-instrumentation-hook', async () => {
403
- const srcInstrumentationTsExists = fs.existsSync(
404
- path.join(process.cwd(), 'src', 'instrumentation.ts'),
405
- );
406
- const srcInstrumentationJsExists = fs.existsSync(
407
- path.join(process.cwd(), 'src', 'instrumentation.js'),
408
- );
426
+ const hasRootAppDirectory = hasDirectoryPathFromRoot('app');
427
+ const hasRootPagesDirectory = hasDirectoryPathFromRoot('pages');
428
+ const hasSrcDirectory = hasDirectoryPathFromRoot('src');
429
+
430
+ let instrumentationHookLocation: 'src' | 'root' | 'does-not-exist';
431
+
409
432
  const instrumentationTsExists = fs.existsSync(
410
433
  path.join(process.cwd(), 'instrumentation.ts'),
411
434
  );
412
435
  const instrumentationJsExists = fs.existsSync(
413
436
  path.join(process.cwd(), 'instrumentation.js'),
414
437
  );
438
+ const srcInstrumentationTsExists = fs.existsSync(
439
+ path.join(process.cwd(), 'src', 'instrumentation.ts'),
440
+ );
441
+ const srcInstrumentationJsExists = fs.existsSync(
442
+ path.join(process.cwd(), 'src', 'instrumentation.js'),
443
+ );
415
444
 
416
- let instrumentationHookLocation: 'src' | 'root' | 'does-not-exist';
417
- if (srcInstrumentationTsExists || srcInstrumentationJsExists) {
418
- instrumentationHookLocation = 'src';
419
- } else if (instrumentationTsExists || instrumentationJsExists) {
420
- instrumentationHookLocation = 'root';
445
+ // https://nextjs.org/docs/app/building-your-application/configuring/src-directory
446
+ // https://nextjs.org/docs/app/api-reference/file-conventions/instrumentation
447
+ // The logic for where Next.js picks up the instrumentation file is as follows:
448
+ // - If there is either an `app` folder or a `pages` folder in the root directory of your Next.js app, Next.js looks
449
+ // for an `instrumentation.ts` file in the root of the Next.js app.
450
+ // - Otherwise, if there is neither an `app` folder or a `pages` folder in the rood directory of your Next.js app,
451
+ // AND if there is an `src` folder, Next.js will look for the `instrumentation.ts` file in the `src` folder.
452
+ if (hasRootPagesDirectory || hasRootAppDirectory) {
453
+ if (instrumentationJsExists || instrumentationTsExists) {
454
+ instrumentationHookLocation = 'root';
455
+ } else {
456
+ instrumentationHookLocation = 'does-not-exist';
457
+ }
421
458
  } else {
422
- instrumentationHookLocation = 'does-not-exist';
459
+ if (srcInstrumentationTsExists || srcInstrumentationJsExists) {
460
+ instrumentationHookLocation = 'src';
461
+ } else {
462
+ instrumentationHookLocation = 'does-not-exist';
463
+ }
423
464
  }
424
465
 
466
+ const newInstrumentationFileName = `instrumentation.${
467
+ typeScriptDetected ? 'ts' : 'js'
468
+ }`;
469
+
425
470
  if (instrumentationHookLocation === 'does-not-exist') {
426
- const newInstrumentationFileName = `instrumentation.${
427
- typeScriptDetected ? 'ts' : 'js'
428
- }`;
429
- const srcFolderExists = fs.existsSync(path.join(process.cwd(), 'src'));
471
+ let newInstrumentationHookLocation: 'root' | 'src';
472
+ if (hasRootPagesDirectory || hasRootAppDirectory) {
473
+ newInstrumentationHookLocation = 'root';
474
+ } else if (hasSrcDirectory) {
475
+ newInstrumentationHookLocation = 'src';
476
+ } else {
477
+ newInstrumentationHookLocation = 'root';
478
+ }
430
479
 
431
- const instrumentationHookPath = srcFolderExists
432
- ? path.join(process.cwd(), 'src', newInstrumentationFileName)
433
- : path.join(process.cwd(), newInstrumentationFileName);
480
+ const newInstrumentationHookPath =
481
+ newInstrumentationHookLocation === 'root'
482
+ ? path.join(process.cwd(), newInstrumentationFileName)
483
+ : path.join(process.cwd(), 'src', newInstrumentationFileName);
434
484
 
435
485
  const successfullyCreated = await createNewConfigFile(
436
- instrumentationHookPath,
437
- getInstrumentationHookContent(srcFolderExists ? 'src' : 'root'),
486
+ newInstrumentationHookPath,
487
+ getInstrumentationHookContent(newInstrumentationHookLocation),
438
488
  );
439
489
 
440
490
  if (!successfullyCreated) {
441
491
  await showCopyPasteInstructions(
442
492
  newInstrumentationFileName,
443
493
  getInstrumentationHookCopyPasteSnippet(
444
- srcFolderExists ? 'src' : 'root',
494
+ newInstrumentationHookLocation,
445
495
  ),
446
496
  );
447
497
  }
448
498
  } else {
449
499
  await showCopyPasteInstructions(
450
- srcInstrumentationTsExists
500
+ srcInstrumentationTsExists || instrumentationTsExists
451
501
  ? 'instrumentation.ts'
452
- : srcInstrumentationJsExists
502
+ : srcInstrumentationJsExists || instrumentationJsExists
453
503
  ? 'instrumentation.js'
454
- : instrumentationTsExists
455
- ? 'instrumentation.ts'
456
- : 'instrumentation.js',
504
+ : newInstrumentationFileName,
457
505
  getInstrumentationHookCopyPasteSnippet(instrumentationHookLocation),
458
506
  );
459
507
  }
@@ -466,23 +514,28 @@ async function createOrMergeNextJsFiles(
466
514
  selfHosted,
467
515
  sentryUrl,
468
516
  tunnelRoute: sdkConfigOptions.tunnelRoute,
517
+ reactComponentAnnotation: sdkConfigOptions.reactComponentAnnotation,
469
518
  });
470
519
 
471
- const nextConfigJs = 'next.config.js';
472
- const nextConfigMjs = 'next.config.mjs';
473
-
474
- const nextConfigJsExists = fs.existsSync(
475
- path.join(process.cwd(), nextConfigJs),
476
- );
477
- const nextConfigMjsExists = fs.existsSync(
478
- path.join(process.cwd(), nextConfigMjs),
520
+ const nextConfigPossibleFilesMap = {
521
+ js: 'next.config.js',
522
+ mjs: 'next.config.mjs',
523
+ cjs: 'next.config.cjs',
524
+ ts: 'next.config.ts',
525
+ mts: 'next.config.mts',
526
+ cts: 'next.config.cts',
527
+ };
528
+
529
+ const foundNextConfigFile = Object.entries(nextConfigPossibleFilesMap).find(
530
+ ([, fileName]) => fs.existsSync(path.join(process.cwd(), fileName)),
479
531
  );
480
532
 
481
- if (!nextConfigJsExists && !nextConfigMjsExists) {
533
+ if (!foundNextConfigFile) {
482
534
  Sentry.setTag('next-config-strategy', 'create');
483
535
 
484
536
  await fs.promises.writeFile(
485
- path.join(process.cwd(), nextConfigJs),
537
+ // We are creating a `next.config.js` file by default as it is supported by the most Next.js versions
538
+ path.join(process.cwd(), nextConfigPossibleFilesMap.js),
486
539
  getNextjsConfigCjsTemplate(withSentryConfigOptionsTemplate),
487
540
  { encoding: 'utf8', flag: 'w' },
488
541
  );
@@ -490,19 +543,24 @@ async function createOrMergeNextJsFiles(
490
543
  clack.log.success(
491
544
  `Created ${chalk.cyan('next.config.js')} with Sentry configuration.`,
492
545
  );
546
+
547
+ return;
493
548
  }
494
549
 
495
- if (nextConfigJsExists) {
550
+ const [foundNextConfigFileType, foundNextConfigFileFilename] =
551
+ foundNextConfigFile;
552
+
553
+ if (foundNextConfigFileType === 'js' || foundNextConfigFileType === 'cjs') {
496
554
  Sentry.setTag('next-config-strategy', 'modify');
497
555
 
498
- const nextConfigJsContent = fs.readFileSync(
499
- path.join(process.cwd(), nextConfigJs),
556
+ const nextConfigCjsContent = fs.readFileSync(
557
+ path.join(process.cwd(), foundNextConfigFileFilename),
500
558
  'utf8',
501
559
  );
502
560
 
503
561
  const probablyIncludesSdk =
504
- nextConfigJsContent.includes('@sentry/nextjs') &&
505
- nextConfigJsContent.includes('withSentryConfig');
562
+ nextConfigCjsContent.includes('@sentry/nextjs') &&
563
+ nextConfigCjsContent.includes('withSentryConfig');
506
564
 
507
565
  let shouldInject = true;
508
566
 
@@ -510,7 +568,7 @@ async function createOrMergeNextJsFiles(
510
568
  const injectAnyhow = await abortIfCancelled(
511
569
  clack.confirm({
512
570
  message: `${chalk.cyan(
513
- nextConfigJs,
571
+ foundNextConfigFileFilename,
514
572
  )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
515
573
  }),
516
574
  );
@@ -520,14 +578,14 @@ async function createOrMergeNextJsFiles(
520
578
 
521
579
  if (shouldInject) {
522
580
  await fs.promises.appendFile(
523
- path.join(process.cwd(), nextConfigJs),
581
+ path.join(process.cwd(), foundNextConfigFileFilename),
524
582
  getNextjsConfigCjsAppendix(withSentryConfigOptionsTemplate),
525
583
  'utf8',
526
584
  );
527
585
 
528
586
  clack.log.success(
529
587
  `Added Sentry configuration to ${chalk.cyan(
530
- nextConfigJs,
588
+ foundNextConfigFileFilename,
531
589
  )}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
532
590
  );
533
591
  }
@@ -535,9 +593,14 @@ async function createOrMergeNextJsFiles(
535
593
  Sentry.setTag('next-config-mod-result', 'success');
536
594
  }
537
595
 
538
- if (nextConfigMjsExists) {
596
+ if (
597
+ foundNextConfigFileType === 'mjs' ||
598
+ foundNextConfigFileType === 'mts' ||
599
+ foundNextConfigFileType === 'cts' ||
600
+ foundNextConfigFileType === 'ts'
601
+ ) {
539
602
  const nextConfigMjsContent = fs.readFileSync(
540
- path.join(process.cwd(), nextConfigMjs),
603
+ path.join(process.cwd(), foundNextConfigFileFilename),
541
604
  'utf8',
542
605
  );
543
606
 
@@ -551,7 +614,7 @@ async function createOrMergeNextJsFiles(
551
614
  const injectAnyhow = await abortIfCancelled(
552
615
  clack.confirm({
553
616
  message: `${chalk.cyan(
554
- nextConfigMjs,
617
+ foundNextConfigFileFilename,
555
618
  )} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
556
619
  }),
557
620
  );
@@ -577,7 +640,7 @@ async function createOrMergeNextJsFiles(
577
640
  const newCode = mod.generate().code;
578
641
 
579
642
  await fs.promises.writeFile(
580
- path.join(process.cwd(), nextConfigMjs),
643
+ path.join(process.cwd(), foundNextConfigFileFilename),
581
644
  newCode,
582
645
  {
583
646
  encoding: 'utf8',
@@ -586,7 +649,7 @@ async function createOrMergeNextJsFiles(
586
649
  );
587
650
  clack.log.success(
588
651
  `Added Sentry configuration to ${chalk.cyan(
589
- nextConfigMjs,
652
+ foundNextConfigFileFilename,
590
653
  )}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
591
654
  );
592
655
 
@@ -596,12 +659,14 @@ async function createOrMergeNextJsFiles(
596
659
  Sentry.setTag('next-config-mod-result', 'fail');
597
660
  clack.log.warn(
598
661
  chalk.yellow(
599
- `Something went wrong writing to ${chalk.cyan(nextConfigMjs)}`,
662
+ `Something went wrong writing to ${chalk.cyan(
663
+ foundNextConfigFileFilename,
664
+ )}.`,
600
665
  ),
601
666
  );
602
667
  clack.log.info(
603
668
  `Please put the following code snippet into ${chalk.cyan(
604
- nextConfigMjs,
669
+ foundNextConfigFileFilename,
605
670
  )}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
606
671
  );
607
672
 
@@ -613,7 +678,7 @@ async function createOrMergeNextJsFiles(
613
678
  const shouldContinue = await abortIfCancelled(
614
679
  clack.confirm({
615
680
  message: `Are you done putting the snippet above into ${chalk.cyan(
616
- nextConfigMjs,
681
+ foundNextConfigFileFilename,
617
682
  )}?`,
618
683
  active: 'Yes',
619
684
  inactive: 'No, get me out of here',
@@ -628,50 +693,58 @@ async function createOrMergeNextJsFiles(
628
693
  });
629
694
  }
630
695
 
696
+ function hasDirectoryPathFromRoot(dirnameOrDirs: string | string[]): boolean {
697
+ const dirPath = Array.isArray(dirnameOrDirs)
698
+ ? path.join(process.cwd(), ...dirnameOrDirs)
699
+ : path.join(process.cwd(), dirnameOrDirs);
700
+
701
+ return fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();
702
+ }
703
+
631
704
  async function createExamplePage(
632
705
  selfHosted: boolean,
633
706
  selectedProject: SentryProjectData,
634
707
  sentryUrl: string,
635
708
  ): Promise<void> {
636
- const srcDir = path.join(process.cwd(), 'src');
637
- const maybePagesDirPath = path.join(process.cwd(), 'pages');
638
- const maybeSrcPagesDirPath = path.join(srcDir, 'pages');
639
- const maybeAppDirPath = path.join(process.cwd(), 'app');
640
- const maybeSrcAppDirPath = path.join(srcDir, 'app');
709
+ const hasSrcDirectory = hasDirectoryPathFromRoot('src');
710
+ const hasRootAppDirectory = hasDirectoryPathFromRoot('app');
711
+ const hasRootPagesDirectory = hasDirectoryPathFromRoot('pages');
712
+ const hasSrcAppDirectory = hasDirectoryPathFromRoot(['src', 'app']);
713
+ const hasSrcPagesDirectory = hasDirectoryPathFromRoot(['src', 'pages']);
714
+
715
+ Sentry.setTag('nextjs-app-dir', hasRootAppDirectory || hasSrcAppDirectory);
641
716
 
642
717
  const typeScriptDetected = isUsingTypeScript();
643
718
 
644
- let pagesLocation =
645
- fs.existsSync(maybePagesDirPath) &&
646
- fs.lstatSync(maybePagesDirPath).isDirectory()
647
- ? ['pages']
648
- : fs.existsSync(maybeSrcPagesDirPath) &&
649
- fs.lstatSync(maybeSrcPagesDirPath).isDirectory()
719
+ // If `pages` or an `app` directory exists in the root, we'll put the example page there.
720
+ // `app` directory takes priority over `pages` directory when they coexist, so we prioritize that.
721
+ // https://nextjs.org/docs/app/building-your-application/routing#the-app-router
722
+
723
+ const appFolderLocation = hasRootAppDirectory
724
+ ? ['app']
725
+ : hasSrcAppDirectory
726
+ ? ['src', 'app']
727
+ : undefined;
728
+
729
+ let pagesFolderLocation = hasRootPagesDirectory
730
+ ? ['pages']
731
+ : hasSrcPagesDirectory
732
+ ? ['src', 'pages']
733
+ : undefined;
734
+
735
+ // If the user has neither pages nor app directory we create a pages folder for them
736
+ if (!appFolderLocation && !pagesFolderLocation) {
737
+ const newPagesFolderLocation = hasSrcDirectory
650
738
  ? ['src', 'pages']
651
- : undefined;
652
-
653
- const appLocation =
654
- fs.existsSync(maybeAppDirPath) &&
655
- fs.lstatSync(maybeAppDirPath).isDirectory()
656
- ? ['app']
657
- : fs.existsSync(maybeSrcAppDirPath) &&
658
- fs.lstatSync(maybeSrcAppDirPath).isDirectory()
659
- ? ['src', 'app']
660
- : undefined;
661
-
662
- if (!pagesLocation && !appLocation) {
663
- pagesLocation =
664
- fs.existsSync(srcDir) && fs.lstatSync(srcDir).isDirectory()
665
- ? ['src', 'pages']
666
- : ['pages'];
667
- fs.mkdirSync(path.join(process.cwd(), ...pagesLocation), {
739
+ : ['pages'];
740
+ fs.mkdirSync(path.join(process.cwd(), ...newPagesFolderLocation), {
668
741
  recursive: true,
669
742
  });
670
- }
671
743
 
672
- Sentry.setTag('nextjs-app-dir', !!appLocation);
744
+ pagesFolderLocation = newPagesFolderLocation;
745
+ }
673
746
 
674
- if (appLocation) {
747
+ if (appFolderLocation) {
675
748
  const examplePageContents = getSentryExamplePageContents({
676
749
  selfHosted,
677
750
  orgSlug: selectedProject.organization.slug,
@@ -681,7 +754,7 @@ async function createExamplePage(
681
754
  });
682
755
 
683
756
  fs.mkdirSync(
684
- path.join(process.cwd(), ...appLocation, 'sentry-example-page'),
757
+ path.join(process.cwd(), ...appFolderLocation, 'sentry-example-page'),
685
758
  {
686
759
  recursive: true,
687
760
  },
@@ -692,7 +765,7 @@ async function createExamplePage(
692
765
  await fs.promises.writeFile(
693
766
  path.join(
694
767
  process.cwd(),
695
- ...appLocation,
768
+ ...appFolderLocation,
696
769
  'sentry-example-page',
697
770
  newPageFileName,
698
771
  ),
@@ -702,12 +775,17 @@ async function createExamplePage(
702
775
 
703
776
  clack.log.success(
704
777
  `Created ${chalk.cyan(
705
- path.join(...appLocation, 'sentry-example-page', newPageFileName),
778
+ path.join(...appFolderLocation, 'sentry-example-page', newPageFileName),
706
779
  )}.`,
707
780
  );
708
781
 
709
782
  fs.mkdirSync(
710
- path.join(process.cwd(), ...appLocation, 'api', 'sentry-example-api'),
783
+ path.join(
784
+ process.cwd(),
785
+ ...appFolderLocation,
786
+ 'api',
787
+ 'sentry-example-api',
788
+ ),
711
789
  {
712
790
  recursive: true,
713
791
  },
@@ -718,7 +796,7 @@ async function createExamplePage(
718
796
  await fs.promises.writeFile(
719
797
  path.join(
720
798
  process.cwd(),
721
- ...appLocation,
799
+ ...appFolderLocation,
722
800
  'api',
723
801
  'sentry-example-api',
724
802
  newRouteFileName,
@@ -730,14 +808,14 @@ async function createExamplePage(
730
808
  clack.log.success(
731
809
  `Created ${chalk.cyan(
732
810
  path.join(
733
- ...appLocation,
811
+ ...appFolderLocation,
734
812
  'api',
735
813
  'sentry-example-api',
736
814
  newRouteFileName,
737
815
  ),
738
816
  )}.`,
739
817
  );
740
- } else if (pagesLocation) {
818
+ } else if (pagesFolderLocation) {
741
819
  const examplePageContents = getSentryExamplePageContents({
742
820
  selfHosted,
743
821
  orgSlug: selectedProject.organization.slug,
@@ -747,35 +825,39 @@ async function createExamplePage(
747
825
  });
748
826
 
749
827
  await fs.promises.writeFile(
750
- path.join(process.cwd(), ...pagesLocation, 'sentry-example-page.jsx'),
828
+ path.join(
829
+ process.cwd(),
830
+ ...pagesFolderLocation,
831
+ 'sentry-example-page.jsx',
832
+ ),
751
833
  examplePageContents,
752
834
  { encoding: 'utf8', flag: 'w' },
753
835
  );
754
836
 
755
837
  clack.log.success(
756
838
  `Created ${chalk.cyan(
757
- path.join(...pagesLocation, 'sentry-example-page.js'),
839
+ path.join(...pagesFolderLocation, 'sentry-example-page.js'),
758
840
  )}.`,
759
841
  );
760
842
 
761
- fs.mkdirSync(path.join(process.cwd(), ...pagesLocation, 'api'), {
843
+ fs.mkdirSync(path.join(process.cwd(), ...pagesFolderLocation, 'api'), {
762
844
  recursive: true,
763
845
  });
764
846
 
765
847
  await fs.promises.writeFile(
766
848
  path.join(
767
849
  process.cwd(),
768
- ...pagesLocation,
850
+ ...pagesFolderLocation,
769
851
  'api',
770
852
  'sentry-example-api.js',
771
853
  ),
772
- getSentryExampleApiRoute(),
854
+ getSentryExamplePagesDirApiRoute(),
773
855
  { encoding: 'utf8', flag: 'w' },
774
856
  );
775
857
 
776
858
  clack.log.success(
777
859
  `Created ${chalk.cyan(
778
- path.join(...pagesLocation, 'api', 'sentry-example-api.js'),
860
+ path.join(...pagesFolderLocation, 'api', 'sentry-example-api.js'),
779
861
  )}.`,
780
862
  );
781
863
  }
@@ -791,7 +873,7 @@ async function askShouldSetTunnelRoute() {
791
873
  const shouldSetTunnelRoute = await abortIfCancelled(
792
874
  clack.select({
793
875
  message:
794
- 'Do you want to route Sentry requests in the browser through your NextJS server to avoid ad blockers?',
876
+ 'Do you want to route Sentry requests in the browser through your Next.js server to avoid ad blockers?',
795
877
  options: [
796
878
  {
797
879
  label: 'Yes',
@@ -804,7 +886,7 @@ async function askShouldSetTunnelRoute() {
804
886
  hint: 'Browser errors and events might be blocked by ad blockers before being sent to Sentry',
805
887
  },
806
888
  ],
807
- initialValue: false,
889
+ initialValue: true,
808
890
  }),
809
891
  );
810
892
 
@@ -817,3 +899,29 @@ async function askShouldSetTunnelRoute() {
817
899
  return shouldSetTunnelRoute;
818
900
  });
819
901
  }
902
+
903
+ async function askShouldEnableReactComponentAnnotation() {
904
+ return await traceStep('ask-react-component-annotation-option', async () => {
905
+ const shouldEnableReactComponentAnnotation = await abortIfCancelled(
906
+ clack.select({
907
+ message:
908
+ 'Do you want to enable React component annotations to make breadcrumbs and session replays more readable?',
909
+ options: [
910
+ {
911
+ label: 'Yes',
912
+ value: true,
913
+ hint: 'Annotates React component names - increases bundle size',
914
+ },
915
+ {
916
+ label: 'No',
917
+ value: false,
918
+ hint: 'Continue without React component annotations',
919
+ },
920
+ ],
921
+ initialValue: true,
922
+ }),
923
+ );
924
+
925
+ return shouldEnableReactComponentAnnotation;
926
+ });
927
+ }