@sentry/wizard 3.5.0 → 3.7.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 (115) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/bin.ts +1 -34
  3. package/dist/bin.js +1 -28
  4. package/dist/bin.js.map +1 -1
  5. package/dist/lib/Constants.d.ts +2 -0
  6. package/dist/lib/Constants.js +5 -0
  7. package/dist/lib/Constants.js.map +1 -1
  8. package/dist/lib/Helper/Wizard.js +2 -0
  9. package/dist/lib/Helper/Wizard.js.map +1 -1
  10. package/dist/lib/Helper/__tests__/SentryCli.js +1 -0
  11. package/dist/lib/Helper/__tests__/SentryCli.js.map +1 -1
  12. package/dist/lib/Steps/ChooseIntegration.js +4 -0
  13. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  14. package/dist/lib/Steps/Integrations/Apple.d.ts +10 -0
  15. package/dist/lib/Steps/Integrations/Apple.js +92 -0
  16. package/dist/lib/Steps/Integrations/Apple.js.map +1 -0
  17. package/dist/lib/Steps/Integrations/NextJsShim.js +1 -0
  18. package/dist/lib/Steps/Integrations/NextJsShim.js.map +1 -1
  19. package/dist/lib/Steps/Integrations/SourceMapsShim.d.ts +1 -1
  20. package/dist/lib/Steps/Integrations/SourceMapsShim.js +2 -1
  21. package/dist/lib/Steps/Integrations/SourceMapsShim.js.map +1 -1
  22. package/dist/lib/Steps/Integrations/SvelteKitShim.js +1 -0
  23. package/dist/lib/Steps/Integrations/SvelteKitShim.js.map +1 -1
  24. package/dist/package.json +2 -2
  25. package/dist/src/apple/apple-wizard.d.ts +2 -0
  26. package/dist/src/apple/apple-wizard.js +197 -0
  27. package/dist/src/apple/apple-wizard.js.map +1 -0
  28. package/dist/src/apple/code-tools.d.ts +1 -0
  29. package/dist/src/apple/code-tools.js +100 -0
  30. package/dist/src/apple/code-tools.js.map +1 -0
  31. package/dist/src/apple/templates.d.ts +4 -0
  32. package/dist/src/apple/templates.js +19 -0
  33. package/dist/src/apple/templates.js.map +1 -0
  34. package/dist/src/apple/xcode-manager.d.ts +4 -0
  35. package/dist/src/apple/xcode-manager.js +145 -0
  36. package/dist/src/apple/xcode-manager.js.map +1 -0
  37. package/dist/src/nextjs/nextjs-wizard.js +4 -10
  38. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  39. package/dist/src/sourcemaps/sourcemaps-wizard.js +193 -71
  40. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  41. package/dist/src/sourcemaps/tools/angular.d.ts +1 -0
  42. package/dist/src/sourcemaps/tools/angular.js +76 -0
  43. package/dist/src/sourcemaps/tools/angular.js.map +1 -0
  44. package/dist/src/sourcemaps/tools/esbuild.js +2 -1
  45. package/dist/src/sourcemaps/tools/esbuild.js.map +1 -1
  46. package/dist/src/sourcemaps/tools/rollup.js +2 -1
  47. package/dist/src/sourcemaps/tools/rollup.js.map +1 -1
  48. package/dist/src/sourcemaps/tools/sentry-cli.d.ts +1 -0
  49. package/dist/src/sourcemaps/tools/sentry-cli.js +52 -24
  50. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  51. package/dist/src/sourcemaps/tools/vite.js +2 -1
  52. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  53. package/dist/src/sourcemaps/tools/webpack.js +2 -1
  54. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  55. package/dist/src/sourcemaps/utils/detect-tool.d.ts +3 -0
  56. package/dist/src/sourcemaps/utils/detect-tool.js +73 -0
  57. package/dist/src/sourcemaps/utils/detect-tool.js.map +1 -0
  58. package/dist/src/sourcemaps/utils/other-wizards.d.ts +4 -0
  59. package/dist/src/sourcemaps/utils/other-wizards.js +183 -0
  60. package/dist/src/sourcemaps/utils/other-wizards.js.map +1 -0
  61. package/dist/src/sourcemaps/utils/sdk-version.js +14 -13
  62. package/dist/src/sourcemaps/utils/sdk-version.js.map +1 -1
  63. package/dist/src/sveltekit/sdk-setup.js +5 -3
  64. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  65. package/dist/src/sveltekit/sveltekit-wizard.js +2 -1
  66. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  67. package/dist/src/sveltekit/templates.js +1 -1
  68. package/dist/src/sveltekit/templates.js.map +1 -1
  69. package/dist/src/telemetry.d.ts +1 -0
  70. package/dist/src/telemetry.js +7 -1
  71. package/dist/src/telemetry.js.map +1 -1
  72. package/dist/src/utils/bash.d.ts +3 -0
  73. package/dist/src/utils/bash.js +118 -0
  74. package/dist/src/utils/bash.js.map +1 -0
  75. package/dist/src/utils/clack-utils.d.ts +12 -8
  76. package/dist/src/utils/clack-utils.js +83 -40
  77. package/dist/src/utils/clack-utils.js.map +1 -1
  78. package/dist/src/utils/package-json.d.ts +19 -0
  79. package/dist/src/utils/package-json.js +29 -0
  80. package/dist/src/utils/package-json.js.map +1 -0
  81. package/dist/src/utils/types.d.ts +4 -0
  82. package/dist/src/utils/types.js.map +1 -1
  83. package/lib/Constants.ts +6 -0
  84. package/lib/Helper/Wizard.ts +3 -0
  85. package/lib/Helper/__tests__/SentryCli.ts +2 -1
  86. package/lib/Steps/ChooseIntegration.ts +4 -0
  87. package/lib/Steps/Integrations/Apple.ts +27 -0
  88. package/lib/Steps/Integrations/NextJsShim.ts +1 -0
  89. package/lib/Steps/Integrations/SourceMapsShim.ts +2 -1
  90. package/lib/Steps/Integrations/SvelteKitShim.ts +1 -0
  91. package/package-lock.json +8910 -0
  92. package/package.json +2 -2
  93. package/src/apple/apple-wizard.ts +150 -0
  94. package/src/apple/code-tools.ts +81 -0
  95. package/src/apple/templates.ts +39 -0
  96. package/src/apple/xcode-manager.ts +147 -0
  97. package/src/nextjs/nextjs-wizard.ts +6 -10
  98. package/src/sourcemaps/sourcemaps-wizard.ts +211 -92
  99. package/src/sourcemaps/tools/angular.ts +42 -0
  100. package/src/sourcemaps/tools/esbuild.ts +1 -1
  101. package/src/sourcemaps/tools/rollup.ts +2 -4
  102. package/src/sourcemaps/tools/sentry-cli.ts +45 -27
  103. package/src/sourcemaps/tools/vite.ts +1 -1
  104. package/src/sourcemaps/tools/webpack.ts +1 -1
  105. package/src/sourcemaps/utils/detect-tool.ts +41 -0
  106. package/src/sourcemaps/utils/other-wizards.ts +148 -0
  107. package/src/sourcemaps/utils/sdk-version.ts +9 -16
  108. package/src/sveltekit/sdk-setup.ts +5 -2
  109. package/src/sveltekit/sveltekit-wizard.ts +1 -1
  110. package/src/sveltekit/templates.ts +1 -1
  111. package/src/telemetry.ts +8 -0
  112. package/src/utils/bash.ts +44 -0
  113. package/src/utils/clack-utils.ts +82 -51
  114. package/src/utils/package-json.ts +45 -0
  115. package/src/utils/types.ts +5 -0
@@ -0,0 +1,148 @@
1
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
2
+ import clack from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import { runNextjsWizard } from '../../nextjs/nextjs-wizard';
5
+ import { runSvelteKitWizard } from '../../sveltekit/sveltekit-wizard';
6
+
7
+ import {
8
+ abort,
9
+ abortIfCancelled,
10
+ getPackageDotJson,
11
+ } from '../../utils/clack-utils';
12
+ import {
13
+ findInstalledPackageFromList,
14
+ hasPackageInstalled,
15
+ } from '../../utils/package-json';
16
+
17
+ import * as Sentry from '@sentry/node';
18
+ import { WizardOptions } from '../../utils/types';
19
+
20
+ type WizardFunction = (options: WizardOptions) => Promise<void>;
21
+
22
+ type FrameworkInfo = {
23
+ frameworkName: string;
24
+ frameworkSlug: string;
25
+ frameworkPackage: string;
26
+ sourcemapsDocsLink: string;
27
+ wizard: WizardFunction;
28
+ };
29
+
30
+ const sdkMap: Record<string, FrameworkInfo> = {
31
+ '@sentry/nextjs': {
32
+ frameworkName: 'Next.js',
33
+ frameworkSlug: 'nextjs',
34
+ frameworkPackage: 'next',
35
+ sourcemapsDocsLink:
36
+ 'https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#configure-source-maps',
37
+ wizard: runNextjsWizard,
38
+ },
39
+ '@sentry/sveltekit': {
40
+ frameworkName: 'SvelteKit',
41
+ frameworkSlug: 'sveltekit',
42
+ frameworkPackage: '@sveltejs/kit',
43
+ sourcemapsDocsLink:
44
+ 'https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/#configure-source-maps-upload',
45
+ wizard: runSvelteKitWizard,
46
+ },
47
+ };
48
+
49
+ export async function checkIfMoreSuitableWizardExistsAndAskForRedirect(): Promise<
50
+ WizardFunction | undefined
51
+ > {
52
+ const sdkName = await checkIfMoreSuitableWizardExists();
53
+
54
+ if (!sdkName) {
55
+ return undefined;
56
+ }
57
+
58
+ return await askForRedirect(sdkName);
59
+ }
60
+
61
+ async function checkIfMoreSuitableWizardExists(): Promise<string | undefined> {
62
+ Sentry.setTag('using-wrong-wizard', false);
63
+
64
+ const packageJson = await getPackageDotJson();
65
+
66
+ const installedSdkPackage = findInstalledPackageFromList(
67
+ Object.keys(sdkMap),
68
+ packageJson,
69
+ );
70
+
71
+ if (!installedSdkPackage) {
72
+ return undefined;
73
+ }
74
+
75
+ const sdkPackageName = installedSdkPackage.name;
76
+
77
+ const { frameworkPackage } = sdkMap[sdkPackageName];
78
+
79
+ if (!hasPackageInstalled(frameworkPackage, packageJson)) {
80
+ // The user has installed the SDK but not the framework.
81
+ // Maybe it's a false positive and the user is using a different framework.
82
+ // Let's not redirect them to the framework wizard in that case.
83
+ return undefined;
84
+ }
85
+
86
+ Sentry.setTag('using-wrong-wizard', true);
87
+
88
+ return sdkPackageName;
89
+ }
90
+
91
+ async function askForRedirect(
92
+ sdkName: string,
93
+ ): Promise<WizardFunction | undefined> {
94
+ const { frameworkName, sourcemapsDocsLink, frameworkSlug, wizard } =
95
+ sdkMap[sdkName];
96
+
97
+ clack.log.warn(
98
+ `${chalk.yellow(
99
+ `It seems like you're using this wizard in a ${frameworkName} project.`,
100
+ )}
101
+
102
+ We recommend using our dedicated ${frameworkName} wizard instead of this wizard.
103
+ The ${frameworkName} wizard will set up our ${sdkName} SDK and also configure uploading source maps for you.
104
+
105
+ If you already tried the ${frameworkName} wizard and it didn't work for you, check out the following guides:
106
+
107
+ Manual source maps configuration for ${frameworkName}:
108
+ ${sourcemapsDocsLink}
109
+
110
+ Troubleshooting Source Maps:
111
+ https://docs.sentry.io/platforms/javascript/guides/${frameworkSlug}/sourcemaps/troubleshooting_js/
112
+ `,
113
+ );
114
+
115
+ const nextStep: 'redirect' | 'continue' | 'stop' = await abortIfCancelled(
116
+ clack.select({
117
+ message: `Do you want to run the ${frameworkName} wizard now?`,
118
+ options: [
119
+ {
120
+ label: 'Yes',
121
+ value: 'redirect',
122
+ hint: `${chalk.green('Recommended')}`,
123
+ },
124
+ {
125
+ label: 'No, continue with this wizard',
126
+ value: 'continue',
127
+ },
128
+ {
129
+ label: "No, I'll check out the guides ",
130
+ value: 'stop',
131
+ hint: 'Exit this wizard',
132
+ },
133
+ ],
134
+ }),
135
+ );
136
+
137
+ Sentry.setTag('wrong-wizard-decision', nextStep);
138
+
139
+ switch (nextStep) {
140
+ case 'redirect':
141
+ return wizard;
142
+ case 'stop':
143
+ await abort('Exiting Wizard', 0);
144
+ break;
145
+ default:
146
+ return undefined;
147
+ }
148
+ }
@@ -5,11 +5,11 @@ import { minVersion, satisfies } from 'semver';
5
5
  import {
6
6
  abortIfCancelled,
7
7
  getPackageDotJson,
8
- getPackageVersion,
9
8
  installPackage,
10
9
  } from '../../utils/clack-utils';
11
10
 
12
11
  import * as Sentry from '@sentry/node';
12
+ import { findInstalledPackageFromList } from '../../utils/package-json';
13
13
 
14
14
  const MINIMUM_DEBUG_ID_SDK_VERSION = '7.47.0';
15
15
 
@@ -36,12 +36,7 @@ const SENTRY_SDK_PACKAGE_NAMES = [
36
36
  // Base SDKs
37
37
  '@sentry/browser',
38
38
  '@sentry/node',
39
- ] as const;
40
-
41
- type SdkPackage = {
42
- name: (typeof SENTRY_SDK_PACKAGE_NAMES)[number];
43
- version: string;
44
- };
39
+ ];
45
40
 
46
41
  /**
47
42
  * Check for a minimum SDK version and prompt the user to upgrade if necessary.
@@ -57,15 +52,10 @@ type SdkPackage = {
57
52
  * -> We tell users to manually upgrade (migrate between majors)
58
53
  */
59
54
  export async function ensureMinimumSdkVersionIsInstalled(): Promise<void> {
60
- const packageJson = await getPackageDotJson();
61
-
62
- const installedSdkPackages = SENTRY_SDK_PACKAGE_NAMES.map((packageName) => ({
63
- name: packageName,
64
- version: getPackageVersion(packageName, packageJson),
65
- })).filter((sdkPackage): sdkPackage is SdkPackage => !!sdkPackage.version);
66
-
67
- const installedSdkPackage =
68
- installedSdkPackages.length > 0 && installedSdkPackages[0];
55
+ const installedSdkPackage = findInstalledPackageFromList(
56
+ SENTRY_SDK_PACKAGE_NAMES,
57
+ await getPackageDotJson(),
58
+ );
69
59
 
70
60
  // Case 1:
71
61
  if (!installedSdkPackage) {
@@ -75,6 +65,8 @@ export async function ensureMinimumSdkVersionIsInstalled(): Promise<void> {
75
65
  const { name: installedSdkName, version: installedSdkVersionOrRange } =
76
66
  installedSdkPackage;
77
67
 
68
+ Sentry.setTag('installed-sdk', installedSdkName);
69
+
78
70
  const minInstalledVersion = getMinInstalledVersion(
79
71
  installedSdkVersionOrRange,
80
72
  installedSdkName,
@@ -198,6 +190,7 @@ async function handleAutoUpdateSdk(packageName: string) {
198
190
 
199
191
  async function handleNoSdkInstalled(): Promise<void> {
200
192
  Sentry.setTag('initial-sdk-version', 'none');
193
+ Sentry.setTag('installed-sdk', 'none');
201
194
 
202
195
  clack.log.warn(
203
196
  `${chalk.yellowBright(
@@ -13,6 +13,7 @@ import { builders, generateCode, loadFile, parseModule } from 'magicast';
13
13
  // @ts-ignore - magicast is ESM and TS complains about that. It works though
14
14
  import { addVitePlugin } from 'magicast/helpers';
15
15
  import { getClientHooksTemplate, getServerHooksTemplate } from './templates';
16
+ import { isUsingTypeScript } from '../utils/clack-utils';
16
17
 
17
18
  const SVELTE_CONFIG_FILE = 'svelte.config.js';
18
19
 
@@ -40,13 +41,15 @@ export async function createOrMergeSvelteKitFiles(
40
41
 
41
42
  const viteConfig = findHooksFile(path.resolve(process.cwd(), 'vite.config'));
42
43
 
44
+ const fileEnding = isUsingTypeScript() ? 'ts' : 'js';
45
+
43
46
  if (!originalClientHooksFile) {
44
47
  clack.log.info('No client hooks file found, creating a new one.');
45
- await createNewHooksFile(`${clientHooksPath}.js`, 'client', dsn);
48
+ await createNewHooksFile(`${clientHooksPath}.${fileEnding}`, 'client', dsn);
46
49
  }
47
50
  if (!originalServerHooksFile) {
48
51
  clack.log.info('No server hooks file found, creating a new one.');
49
- await createNewHooksFile(`${serverHooksPath}.js`, 'server', dsn);
52
+ await createNewHooksFile(`${serverHooksPath}.${fileEnding}`, 'server', dsn);
50
53
  }
51
54
 
52
55
  if (originalClientHooksFile) {
@@ -9,10 +9,10 @@ import {
9
9
  confirmContinueEvenThoughNoGitRepo,
10
10
  ensurePackageIsInstalled,
11
11
  getPackageDotJson,
12
- hasPackageInstalled,
13
12
  installPackage,
14
13
  printWelcome,
15
14
  } from '../utils/clack-utils';
15
+ import { hasPackageInstalled } from '../utils/package-json';
16
16
  import { WizardOptions } from '../utils/types';
17
17
  import { createExamplePage } from './sdk-example';
18
18
  import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup';
@@ -72,7 +72,7 @@ Feel free to delete this file and the entire sentry route.
72
72
  });
73
73
 
74
74
  try {
75
- const res = await fetch('/test-sentry');
75
+ const res = await fetch('/sentry-example');
76
76
  if (!res.ok) {
77
77
  throw new Error('Sentry Example Frontend Error');
78
78
  }
package/src/telemetry.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  makeNodeTransport,
7
7
  NodeClient,
8
8
  runWithAsyncContext,
9
+ trace,
9
10
  } from '@sentry/node';
10
11
  import packageJson from '../package.json';
11
12
 
@@ -26,6 +27,7 @@ export async function withTelemetry<F>(
26
27
  const transaction = sentryHub.startTransaction({
27
28
  name: 'sentry-wizard-execution',
28
29
  status: 'ok',
30
+ op: 'wizard.flow',
29
31
  });
30
32
  sentryHub.getScope().setSpan(transaction);
31
33
  const sentrySession = sentryHub.startSession();
@@ -74,6 +76,8 @@ function createSentryInstance(enabled: boolean, integration: string) {
74
76
  },
75
77
 
76
78
  transport: makeNodeTransport,
79
+
80
+ debug: true,
77
81
  });
78
82
 
79
83
  const hub = new Hub(client);
@@ -84,3 +88,7 @@ function createSentryInstance(enabled: boolean, integration: string) {
84
88
 
85
89
  return { sentryHub: hub, sentryClient: client };
86
90
  }
91
+
92
+ export function traceStep<T>(step: string, callback: () => T): T {
93
+ return trace({ name: step, op: 'wizard.step' }, () => callback());
94
+ }
@@ -0,0 +1,44 @@
1
+ import * as child_process from 'child_process';
2
+ import * as https from 'https';
3
+ import * as fs from 'fs';
4
+
5
+ export function hasSentryCLI(): boolean {
6
+ try {
7
+ child_process.execSync('sentry-cli --version');
8
+ return true;
9
+ } catch (e) {
10
+ return false;
11
+ }
12
+ }
13
+
14
+ export async function installSentryCLI(): Promise<void> {
15
+ const httpAsync = new Promise((resolve, reject) => {
16
+ const file = fs.createWriteStream('installcli.sh');
17
+ https.get('https://sentry.io/get-cli/', (response) => {
18
+ response.pipe(file);
19
+ file.on('finish', () => {
20
+ file.close();
21
+ try {
22
+ child_process.execSync('bash ./installcli.sh');
23
+ } catch (e) {
24
+ reject(e);
25
+ return
26
+ }
27
+ fs.unlinkSync('installcli.sh');
28
+ resolve(null);
29
+ });
30
+
31
+ file.on('error', (err) => {
32
+ fs.unlinkSync('installcli.sh');
33
+ reject(err);
34
+ });
35
+ });
36
+ });
37
+
38
+ await httpAsync;
39
+ }
40
+
41
+ export function execute(command: string): string {
42
+ const output = child_process.execSync(command);
43
+ return output.toString();
44
+ }
@@ -10,11 +10,15 @@ import { URL } from 'url';
10
10
  import { promisify } from 'util';
11
11
  import * as Sentry from '@sentry/node';
12
12
  import { windowedSelect } from './vendor/clack-custom-select';
13
+ import { hasPackageInstalled, PackageDotJson } from './package-json';
13
14
 
14
15
  const opn = require('opn') as (
15
16
  url: string,
16
17
  ) => Promise<childProcess.ChildProcess>;
17
18
 
19
+ export const SENTRY_DOT_ENV_FILE = '.env.sentry-build-plugin';
20
+ export const SENTRY_CLI_RC_FILE = '.sentryclirc';
21
+
18
22
  const SAAS_URL = 'https://sentry.io/';
19
23
 
20
24
  interface WizardProjectData {
@@ -24,16 +28,11 @@ interface WizardProjectData {
24
28
  projects: SentryProjectData[];
25
29
  }
26
30
 
27
- export type PackageDotJson = {
28
- scripts?: Record<string, string>;
29
- dependencies?: Record<string, string>;
30
- devDependencies?: Record<string, string>;
31
- };
32
-
33
31
  export interface SentryProjectData {
34
32
  id: string;
35
33
  slug: string;
36
34
  name: string;
35
+ platform: string;
37
36
  organization: {
38
37
  slug: string;
39
38
  };
@@ -130,10 +129,19 @@ export async function confirmContinueEvenThoughNoGitRepo(): Promise<void> {
130
129
  }
131
130
  }
132
131
 
132
+ export async function askToInstallSentryCLI(): Promise<boolean> {
133
+ return await abortIfCancelled(
134
+ clack.confirm({
135
+ message:
136
+ "You don't have Sentry CLI installed. Do you want to install it?",
137
+ }),
138
+ );
139
+ }
140
+
133
141
  export async function askForWizardLogin(options: {
134
142
  url: string;
135
143
  promoCode?: string;
136
- platform?: 'javascript-nextjs' | 'javascript-sveltekit';
144
+ platform?: 'javascript-nextjs' | 'javascript-sveltekit' | 'apple-ios';
137
145
  }): Promise<WizardProjectData> {
138
146
  Sentry.setTag('has-promo-code', !!options.promoCode);
139
147
 
@@ -234,6 +242,27 @@ export async function askForWizardLogin(options: {
234
242
  return data;
235
243
  }
236
244
 
245
+ export async function askForItemSelection(
246
+ items: string[],
247
+ message: string,
248
+ ): Promise<{ value: string; index: number }> {
249
+ const selection: { value: string; index: number } | symbol =
250
+ await abortIfCancelled(
251
+ windowedSelect({
252
+ maxItems: 12,
253
+ message: message,
254
+ options: items.map((item, index) => {
255
+ return {
256
+ value: { value: item, index: index },
257
+ label: item,
258
+ };
259
+ }),
260
+ }),
261
+ );
262
+
263
+ return selection;
264
+ }
265
+
237
266
  export async function askForProjectSelection(
238
267
  projects: SentryProjectData[],
239
268
  ): Promise<SentryProjectData> {
@@ -251,6 +280,7 @@ export async function askForProjectSelection(
251
280
  );
252
281
 
253
282
  Sentry.setTag('project', selection.slug);
283
+ Sentry.setTag('project-platform', selection.platform);
254
284
  Sentry.setUser({ id: selection.organization.slug });
255
285
 
256
286
  return selection;
@@ -390,10 +420,12 @@ export async function askForSelfHosted(urlFromArgs?: string): Promise<{
390
420
  }
391
421
 
392
422
  export async function addSentryCliRc(authToken: string): Promise<void> {
393
- const clircExists = fs.existsSync(path.join(process.cwd(), '.sentryclirc'));
423
+ const clircExists = fs.existsSync(
424
+ path.join(process.cwd(), SENTRY_CLI_RC_FILE),
425
+ );
394
426
  if (clircExists) {
395
427
  const clircContents = fs.readFileSync(
396
- path.join(process.cwd(), '.sentryclirc'),
428
+ path.join(process.cwd(), SENTRY_CLI_RC_FILE),
397
429
  'utf8',
398
430
  );
399
431
 
@@ -404,25 +436,25 @@ export async function addSentryCliRc(authToken: string): Promise<void> {
404
436
  if (likelyAlreadyHasAuthToken) {
405
437
  clack.log.warn(
406
438
  `${chalk.bold(
407
- '.sentryclirc',
439
+ SENTRY_CLI_RC_FILE,
408
440
  )} already has auth token. Will not add one.`,
409
441
  );
410
442
  } else {
411
443
  try {
412
444
  await fs.promises.writeFile(
413
- path.join(process.cwd(), '.sentryclirc'),
445
+ path.join(process.cwd(), SENTRY_CLI_RC_FILE),
414
446
  `${clircContents}\n[auth]\ntoken=${authToken}\n`,
415
447
  { encoding: 'utf8', flag: 'w' },
416
448
  );
417
449
  clack.log.success(
418
450
  `Added auth token to ${chalk.bold(
419
- '.sentryclirc',
451
+ SENTRY_CLI_RC_FILE,
420
452
  )} for you to test uploading source maps locally.`,
421
453
  );
422
454
  } catch {
423
455
  clack.log.warning(
424
456
  `Failed to add auth token to ${chalk.bold(
425
- '.sentryclirc',
457
+ SENTRY_CLI_RC_FILE,
426
458
  )}. Uploading source maps during build will likely not work locally.`,
427
459
  );
428
460
  }
@@ -430,32 +462,30 @@ export async function addSentryCliRc(authToken: string): Promise<void> {
430
462
  } else {
431
463
  try {
432
464
  await fs.promises.writeFile(
433
- path.join(process.cwd(), '.sentryclirc'),
465
+ path.join(process.cwd(), SENTRY_CLI_RC_FILE),
434
466
  `[auth]\ntoken=${authToken}\n`,
435
467
  { encoding: 'utf8', flag: 'w' },
436
468
  );
437
469
  clack.log.success(
438
470
  `Created ${chalk.bold(
439
- '.sentryclirc',
471
+ SENTRY_CLI_RC_FILE,
440
472
  )} with auth token for you to test uploading source maps locally.`,
441
473
  );
442
474
  } catch {
443
475
  clack.log.warning(
444
476
  `Failed to create ${chalk.bold(
445
- '.sentryclirc',
477
+ SENTRY_CLI_RC_FILE,
446
478
  )} with auth token. Uploading source maps during build will likely not work locally.`,
447
479
  );
448
480
  }
449
481
  }
450
482
 
451
- await addAuthTokenFileToGitIgnore('.sentryclirc');
483
+ await addAuthTokenFileToGitIgnore(SENTRY_CLI_RC_FILE);
452
484
  }
453
485
 
454
486
  export async function addDotEnvSentryBuildPluginFile(
455
487
  authToken: string,
456
488
  ): Promise<void> {
457
- const DOT_ENV_FILE = '.env.sentry-build-plugin';
458
-
459
489
  const envVarContent = `# DO NOT commit this file to your repository!
460
490
  # The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
461
491
  # It's used for authentication when uploading source maps.
@@ -463,7 +493,7 @@ export async function addDotEnvSentryBuildPluginFile(
463
493
  SENTRY_AUTH_TOKEN="${authToken}"
464
494
  `;
465
495
 
466
- const dotEnvFilePath = path.join(process.cwd(), DOT_ENV_FILE);
496
+ const dotEnvFilePath = path.join(process.cwd(), SENTRY_DOT_ENV_FILE);
467
497
  const dotEnvFileExists = fs.existsSync(dotEnvFilePath);
468
498
 
469
499
  if (dotEnvFileExists) {
@@ -475,7 +505,9 @@ SENTRY_AUTH_TOKEN="${authToken}"
475
505
 
476
506
  if (hasAuthToken) {
477
507
  clack.log.warn(
478
- `${chalk.bold(DOT_ENV_FILE)} already has auth token. Will not add one.`,
508
+ `${chalk.bold(
509
+ SENTRY_DOT_ENV_FILE,
510
+ )} already has auth token. Will not add one.`,
479
511
  );
480
512
  } else {
481
513
  try {
@@ -487,11 +519,13 @@ SENTRY_AUTH_TOKEN="${authToken}"
487
519
  flag: 'w',
488
520
  },
489
521
  );
490
- clack.log.success(`Added auth token to ${chalk.bold(DOT_ENV_FILE)}`);
522
+ clack.log.success(
523
+ `Added auth token to ${chalk.bold(SENTRY_DOT_ENV_FILE)}`,
524
+ );
491
525
  } catch {
492
526
  clack.log.warning(
493
527
  `Failed to add auth token to ${chalk.bold(
494
- DOT_ENV_FILE,
528
+ SENTRY_DOT_ENV_FILE,
495
529
  )}. Uploading source maps during build will likely not work locally.`,
496
530
  );
497
531
  }
@@ -504,19 +538,19 @@ SENTRY_AUTH_TOKEN="${authToken}"
504
538
  });
505
539
  clack.log.success(
506
540
  `Created ${chalk.bold(
507
- DOT_ENV_FILE,
541
+ SENTRY_DOT_ENV_FILE,
508
542
  )} with auth token for you to test source map uploading locally.`,
509
543
  );
510
544
  } catch {
511
545
  clack.log.warning(
512
546
  `Failed to create ${chalk.bold(
513
- DOT_ENV_FILE,
547
+ SENTRY_DOT_ENV_FILE,
514
548
  )} with auth token. Uploading source maps during build will likely not work locally.`,
515
549
  );
516
550
  }
517
551
  }
518
552
 
519
- await addAuthTokenFileToGitIgnore(DOT_ENV_FILE);
553
+ await addAuthTokenFileToGitIgnore(SENTRY_DOT_ENV_FILE);
520
554
  }
521
555
 
522
556
  async function addAuthTokenFileToGitIgnore(filename: string): Promise<void> {
@@ -584,32 +618,8 @@ export async function getPackageDotJson(): Promise<PackageDotJson> {
584
618
  return packageJson || {};
585
619
  }
586
620
 
587
- export function hasPackageInstalled(
588
- packageName: string,
589
- packageJson: PackageDotJson,
590
- ): boolean {
591
- return getPackageVersion(packageName, packageJson) !== undefined;
592
- }
593
-
594
- export function getPackageVersion(
595
- packageName: string,
596
- packageJson: PackageDotJson,
597
- ): string | undefined {
598
- return (
599
- packageJson?.dependencies?.[packageName] ||
600
- packageJson?.devDependencies?.[packageName]
601
- );
602
- }
603
-
604
621
  async function getPackageManager(): Promise<string> {
605
- let detectedPackageManager;
606
- if (fs.existsSync(path.join(process.cwd(), 'yarn.lock'))) {
607
- detectedPackageManager = 'yarn';
608
- } else if (fs.existsSync(path.join(process.cwd(), 'package-lock.json'))) {
609
- detectedPackageManager = 'npm';
610
- } else if (fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'))) {
611
- detectedPackageManager = 'pnpm';
612
- }
622
+ const detectedPackageManager = detectPackageManager();
613
623
 
614
624
  if (detectedPackageManager) {
615
625
  return detectedPackageManager;
@@ -630,3 +640,24 @@ async function getPackageManager(): Promise<string> {
630
640
 
631
641
  return selectedPackageManager;
632
642
  }
643
+
644
+ export function detectPackageManager(): 'yarn' | 'npm' | 'pnpm' | undefined {
645
+ if (fs.existsSync(path.join(process.cwd(), 'yarn.lock'))) {
646
+ return 'yarn';
647
+ }
648
+ if (fs.existsSync(path.join(process.cwd(), 'package-lock.json'))) {
649
+ return 'npm';
650
+ }
651
+ if (fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'))) {
652
+ return 'pnpm';
653
+ }
654
+ return undefined;
655
+ }
656
+
657
+ export function isUsingTypeScript() {
658
+ try {
659
+ return fs.existsSync(path.join(process.cwd(), 'tsconfig.json'));
660
+ } catch {
661
+ return false;
662
+ }
663
+ }
@@ -0,0 +1,45 @@
1
+ export type PackageDotJson = {
2
+ scripts?: Record<string, string>;
3
+ dependencies?: Record<string, string>;
4
+ devDependencies?: Record<string, string>;
5
+ };
6
+
7
+ type NpmPackage = {
8
+ name: string;
9
+ version: string;
10
+ };
11
+
12
+ /**
13
+ * Checks if @param packageJson has any of the @param packageNamesList package names
14
+ * listed as a dependency or devDependency.
15
+ * If so, it returns the first package name that is found, including the
16
+ * version (range) specified in the package.json.
17
+ */
18
+ export function findInstalledPackageFromList(
19
+ packageNamesList: string[],
20
+ packageJson: PackageDotJson,
21
+ ): NpmPackage | undefined {
22
+ return packageNamesList
23
+ .map((packageName) => ({
24
+ name: packageName,
25
+ version: getPackageVersion(packageName, packageJson),
26
+ }))
27
+ .find((sdkPackage): sdkPackage is NpmPackage => !!sdkPackage.version);
28
+ }
29
+
30
+ export function hasPackageInstalled(
31
+ packageName: string,
32
+ packageJson: PackageDotJson,
33
+ ): boolean {
34
+ return getPackageVersion(packageName, packageJson) !== undefined;
35
+ }
36
+
37
+ export function getPackageVersion(
38
+ packageName: string,
39
+ packageJson: PackageDotJson,
40
+ ): string | undefined {
41
+ return (
42
+ packageJson?.dependencies?.[packageName] ||
43
+ packageJson?.devDependencies?.[packageName]
44
+ );
45
+ }
@@ -1,4 +1,9 @@
1
1
  export type WizardOptions = {
2
+ /**
3
+ * Controls whether the wizard should send telemetry data to Sentry.
4
+ */
5
+ telemetryEnabled: boolean;
6
+
2
7
  /**
3
8
  * The promo code to use while signing up for Sentry.
4
9
  * This can be passed via the --promo-code arg.