@sentry/wizard 3.11.0 → 3.13.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 (91) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/lib/Steps/ChooseIntegration.js +1 -0
  3. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  4. package/dist/package.json +1 -1
  5. package/dist/src/android/android-wizard.js +14 -4
  6. package/dist/src/android/android-wizard.js.map +1 -1
  7. package/dist/src/android/code-tools.d.ts +8 -0
  8. package/dist/src/android/code-tools.js +20 -8
  9. package/dist/src/android/code-tools.js.map +1 -1
  10. package/dist/src/android/gradle.js +6 -1
  11. package/dist/src/android/gradle.js.map +1 -1
  12. package/dist/src/nextjs/nextjs-wizard.js +5 -2
  13. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  14. package/dist/src/nextjs/templates.d.ts +1 -1
  15. package/dist/src/nextjs/templates.js +2 -2
  16. package/dist/src/nextjs/templates.js.map +1 -1
  17. package/dist/src/remix/remix-wizard.js +8 -4
  18. package/dist/src/remix/remix-wizard.js.map +1 -1
  19. package/dist/src/remix/sdk-setup.d.ts +5 -1
  20. package/dist/src/remix/sdk-setup.js +3 -2
  21. package/dist/src/remix/sdk-setup.js.map +1 -1
  22. package/dist/src/sourcemaps/tools/sentry-cli.d.ts +9 -0
  23. package/dist/src/sourcemaps/tools/sentry-cli.js +26 -22
  24. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  25. package/dist/src/sourcemaps/tools/tsc.d.ts +6 -0
  26. package/dist/src/sourcemaps/tools/tsc.js +98 -17
  27. package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
  28. package/dist/src/sourcemaps/tools/vite.js +39 -124
  29. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  30. package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
  31. package/dist/src/sourcemaps/tools/webpack.js +280 -25
  32. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  33. package/dist/src/sveltekit/sdk-setup.js +123 -49
  34. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  35. package/dist/src/sveltekit/sveltekit-wizard.d.ts +1 -0
  36. package/dist/src/sveltekit/sveltekit-wizard.js +119 -44
  37. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  38. package/dist/src/sveltekit/utils.d.ts +2 -0
  39. package/dist/src/sveltekit/utils.js +48 -0
  40. package/dist/src/sveltekit/utils.js.map +1 -0
  41. package/dist/src/utils/ast-utils.d.ts +77 -3
  42. package/dist/src/utils/ast-utils.js +172 -6
  43. package/dist/src/utils/ast-utils.js.map +1 -1
  44. package/dist/src/utils/clack-utils.d.ts +85 -1
  45. package/dist/src/utils/clack-utils.js +214 -51
  46. package/dist/src/utils/clack-utils.js.map +1 -1
  47. package/dist/src/utils/package-manager.d.ts +5 -0
  48. package/dist/src/utils/package-manager.js +11 -7
  49. package/dist/src/utils/package-manager.js.map +1 -1
  50. package/dist/test/android/code-tools.test.d.ts +1 -0
  51. package/dist/test/android/code-tools.test.js +34 -0
  52. package/dist/test/android/code-tools.test.js.map +1 -0
  53. package/dist/test/sourcemaps/tools/sentry-cli.test.d.ts +1 -0
  54. package/dist/test/sourcemaps/tools/sentry-cli.test.js +112 -0
  55. package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -0
  56. package/dist/test/sourcemaps/tools/tsc.test.d.ts +1 -0
  57. package/dist/test/sourcemaps/tools/tsc.test.js +121 -0
  58. package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -0
  59. package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
  60. package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
  61. package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
  62. package/dist/test/utils/ast-utils.test.js +181 -15
  63. package/dist/test/utils/ast-utils.test.js.map +1 -1
  64. package/dist/test/utils/clack-utils.test.d.ts +1 -0
  65. package/dist/test/utils/clack-utils.test.js +200 -0
  66. package/dist/test/utils/clack-utils.test.js.map +1 -0
  67. package/lib/Steps/ChooseIntegration.ts +1 -0
  68. package/package.json +1 -1
  69. package/src/android/android-wizard.ts +16 -5
  70. package/src/android/code-tools.ts +21 -7
  71. package/src/android/gradle.ts +6 -1
  72. package/src/nextjs/nextjs-wizard.ts +15 -3
  73. package/src/nextjs/templates.ts +3 -2
  74. package/src/remix/remix-wizard.ts +8 -11
  75. package/src/remix/sdk-setup.ts +8 -2
  76. package/src/sourcemaps/tools/sentry-cli.ts +16 -9
  77. package/src/sourcemaps/tools/tsc.ts +133 -28
  78. package/src/sourcemaps/tools/vite.ts +37 -127
  79. package/src/sourcemaps/tools/webpack.ts +343 -27
  80. package/src/sveltekit/sdk-setup.ts +115 -39
  81. package/src/sveltekit/sveltekit-wizard.ts +93 -25
  82. package/src/sveltekit/utils.ts +50 -0
  83. package/src/utils/ast-utils.ts +203 -7
  84. package/src/utils/clack-utils.ts +211 -44
  85. package/src/utils/package-manager.ts +12 -6
  86. package/test/android/code-tools.test.ts +49 -0
  87. package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
  88. package/test/sourcemaps/tools/tsc.test.ts +181 -0
  89. package/test/sourcemaps/tools/webpack.test.ts +303 -0
  90. package/test/utils/ast-utils.test.ts +240 -20
  91. package/test/utils/clack-utils.test.ts +142 -0
@@ -1,39 +1,144 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+
4
+ import * as recast from 'recast';
5
+
6
+ import * as Sentry from '@sentry/node';
7
+
1
8
  // @ts-ignore - clack is ESM and TS complains about that. It works though
2
- import clack, { select } from '@clack/prompts';
9
+ import * as clack from '@clack/prompts';
3
10
  import chalk from 'chalk';
4
- import { abortIfCancelled } from '../../utils/clack-utils';
5
-
6
- export async function configureTscSourcemapGenerationFlow(): Promise<void> {
7
- clack.log.step(
8
- `Add the following code to your ${chalk.bold(
9
- 'tsconfig.json',
10
- )} file: ${chalk.dim(
11
- '(This ensures that source maps are generated correctly)',
12
- )}`,
13
- );
14
11
 
15
- // Intentially logging directly to console here so that the code can be copied/pasted directly
16
- // eslint-disable-next-line no-console
17
- console.log(codeSnippet);
12
+ import {
13
+ askForToolConfigPath,
14
+ createNewConfigFile,
15
+ makeCodeSnippet,
16
+ showCopyPasteInstructions,
17
+ } from '../../utils/clack-utils';
18
+ import {
19
+ findFile,
20
+ getOrSetObjectProperty,
21
+ parseJsonC,
22
+ printJsonC,
23
+ setOrUpdateObjectProperty,
24
+ } from '../../utils/ast-utils';
25
+ import { debug } from '../../utils/debug';
18
26
 
19
- await abortIfCancelled(
20
- select({
21
- message: 'Did you update your config as shown in the snippet above?',
22
- options: [{ label: 'Yes, continue!', value: true }],
23
- initialValue: true,
24
- }),
25
- );
26
- }
27
+ const b = recast.types.builders;
27
28
 
28
- const codeSnippet = chalk.gray(`
29
- {
29
+ const getCodeSnippet = (colors: boolean) =>
30
+ makeCodeSnippet(colors, (unchanged, plus, _) =>
31
+ unchanged(
32
+ `{
30
33
  "compilerOptions": {
31
- ${chalk.greenBright('"sourceMap": true,')}
32
- ${chalk.greenBright('"inlineSources": true,')}
34
+ ${plus('"sourceMap": true,')}
35
+ ${plus('"inlineSources": true,')}
33
36
 
34
37
  // Set \`sourceRoot\` to "/" to strip the build path prefix from
35
38
  // generated source code references. This will improve issue grouping in Sentry.
36
- ${chalk.greenBright('"sourceRoot": "/"')}
39
+ ${plus('"sourceRoot": "/"')}
40
+ }
41
+ }`,
42
+ ),
43
+ );
44
+
45
+ export async function configureTscSourcemapGenerationFlow(): Promise<void> {
46
+ const tsConfigPath =
47
+ findFile(path.join(process.cwd(), 'tsconfig'), ['.json']) ??
48
+ (await askForToolConfigPath('TypeScript', 'tsconfig.json'));
49
+
50
+ let successfullyAdded = false;
51
+ if (tsConfigPath) {
52
+ successfullyAdded = await enableSourcemaps(tsConfigPath);
53
+ } else {
54
+ successfullyAdded = await createNewConfigFile(
55
+ path.join(process.cwd(), 'tsconfig.json'),
56
+ getCodeSnippet(false),
57
+ );
58
+ Sentry.setTag('created-new-config', successfullyAdded ? 'success' : 'fail');
59
+ }
60
+
61
+ if (successfullyAdded) {
62
+ Sentry.setTag('ast-mod', 'success');
63
+ clack.log.info(
64
+ `We recommend checking the ${
65
+ tsConfigPath ? 'modified' : 'added'
66
+ } file after the wizard finished to ensure it works with your build setup.`,
67
+ );
68
+ } else {
69
+ Sentry.setTag('ast-mod', 'fail');
70
+ await showCopyPasteInstructions(
71
+ 'tsconfig.json',
72
+ getCodeSnippet(true),
73
+ 'This ensures that source maps are generated correctly',
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Modifies tsconfig.json (@param tsConfigPath) to enable source maps generation.
80
+ *
81
+ * Exported only for testing
82
+ */
83
+ export async function enableSourcemaps(tsConfigPath: string): Promise<boolean> {
84
+ try {
85
+ const tsConfig = await fs.promises.readFile(tsConfigPath, 'utf-8');
86
+
87
+ const { ast, jsonObject } = parseJsonC(tsConfig.toString());
88
+
89
+ if (!jsonObject || !ast) {
90
+ // this will only happen if the input file isn't valid JSON-C
91
+ Sentry.setTag('ast-mod-fail-reason', 'original-file-invalid');
92
+ return false;
93
+ }
94
+
95
+ const compilerOptionsProp = getOrSetObjectProperty(
96
+ jsonObject,
97
+ 'compilerOptions',
98
+ b.objectExpression([]),
99
+ );
100
+
101
+ const compilerOptionsObj = compilerOptionsProp.value;
102
+
103
+ if (!compilerOptionsObj || compilerOptionsObj.type !== 'ObjectExpression') {
104
+ // a valid compilerOptions prop should always be an object expression
105
+ Sentry.setTag('ast-mod-fail-reason', 'original-file-invalid');
106
+ return false;
107
+ }
108
+
109
+ setOrUpdateObjectProperty(
110
+ compilerOptionsObj,
111
+ 'sourceMap',
112
+ b.booleanLiteral(true),
113
+ );
114
+
115
+ setOrUpdateObjectProperty(
116
+ compilerOptionsObj,
117
+ 'inlineSources',
118
+ b.booleanLiteral(true),
119
+ );
120
+
121
+ setOrUpdateObjectProperty(
122
+ compilerOptionsObj,
123
+ 'sourceRoot',
124
+ b.stringLiteral('/'),
125
+ 'Set `sourceRoot` to "/" to strip the build path prefix\nfrom generated source code references.\nThis improves issue grouping in Sentry.',
126
+ );
127
+
128
+ const code = printJsonC(ast);
129
+
130
+ await fs.promises.writeFile(tsConfigPath, code);
131
+
132
+ clack.log.success(
133
+ `Enabled source maps generation in ${chalk.cyan(
134
+ path.basename(tsConfigPath || 'tsconfig.json'),
135
+ )}.`,
136
+ );
137
+
138
+ return true;
139
+ } catch (e) {
140
+ debug(e);
141
+ Sentry.setTag('ast-mod-fail-reason', 'insertion-fail');
142
+ return false;
37
143
  }
38
144
  }
39
- `);
@@ -15,8 +15,12 @@ import chalk from 'chalk';
15
15
  import {
16
16
  abortIfCancelled,
17
17
  addDotEnvSentryBuildPluginFile,
18
+ askForToolConfigPath,
19
+ createNewConfigFile,
18
20
  getPackageDotJson,
19
21
  installPackage,
22
+ makeCodeSnippet,
23
+ showCopyPasteInstructions,
20
24
  } from '../../utils/clack-utils';
21
25
  import { hasPackageInstalled } from '../../utils/package-json';
22
26
 
@@ -33,52 +37,27 @@ import { debug } from '../../utils/debug';
33
37
  const getViteConfigSnippet = (
34
38
  options: SourceMapUploadToolConfigurationOptions,
35
39
  colors: boolean,
36
- ) => {
37
- const rawImportStmt =
38
- 'import { sentryVitePlugin } from "@sentry/vite-plugin";';
39
- const rawGenerateSourceMapsOption =
40
- 'sourcemap: true, // Source map generation must be turned on';
41
- const rawSentryVitePluginFunction = `sentryVitePlugin({
42
- authToken: process.env.SENTRY_AUTH_TOKEN,
43
- org: "${options.orgSlug}",
44
- project: "${options.projectSlug}",${
45
- options.selfHosted ? `\n url: "${options.url}",` : ''
46
- }
47
- }),`;
48
-
49
- const importStmt = colors ? chalk.greenBright(rawImportStmt) : rawImportStmt;
50
- const generateSourceMapsOption = colors
51
- ? chalk.greenBright(rawGenerateSourceMapsOption)
52
- : rawGenerateSourceMapsOption;
53
- const sentryVitePluginFunction = colors
54
- ? chalk.greenBright(rawSentryVitePluginFunction)
55
- : rawSentryVitePluginFunction;
56
-
57
- const code = getViteConfigContent(
58
- importStmt,
59
- generateSourceMapsOption,
60
- sentryVitePluginFunction,
61
- );
62
- return colors ? chalk.gray(code) : code;
63
- };
64
-
65
- const getViteConfigContent = (
66
- importStmt: string,
67
- generateSourceMapsOption: string,
68
- sentryVitePluginFunction: string,
69
- ) => `import { defineConfig } from "vite";
70
- ${importStmt}
40
+ ) =>
41
+ makeCodeSnippet(colors, (unchanged, plus, _) =>
42
+ unchanged(`import { defineConfig } from "vite";
43
+ ${plus('import { sentryVitePlugin } from "@sentry/vite-plugin";')}
71
44
 
72
45
  export default defineConfig({
73
46
  build: {
74
- ${generateSourceMapsOption}
47
+ ${plus('sourcemap: true, // Source map generation must be turned on')}
75
48
  },
76
49
  plugins: [
77
50
  // Put the Sentry vite plugin after all other plugins
78
- ${sentryVitePluginFunction}
51
+ ${plus(`sentryVitePlugin({
52
+ authToken: process.env.SENTRY_AUTH_TOKEN,
53
+ org: "${options.orgSlug}",
54
+ project: "${options.projectSlug}",${
55
+ options.selfHosted ? `\n url: "${options.url}",` : ''
56
+ }
57
+ }),`)}
79
58
  ],
80
- });
81
- `;
59
+ });`),
60
+ );
82
61
 
83
62
  export const configureVitePlugin: SourceMapUploadToolConfigurationFunction =
84
63
  async (options) => {
@@ -91,58 +70,43 @@ export const configureVitePlugin: SourceMapUploadToolConfigurationFunction =
91
70
  });
92
71
 
93
72
  const viteConfigPath =
94
- findFile(path.resolve(process.cwd(), 'vite.config')) ||
95
- (await askForViteConfigPath());
73
+ findFile(path.resolve(process.cwd(), 'vite.config')) ??
74
+ (await askForToolConfigPath('Vite', 'vite.config.js'));
96
75
 
97
76
  let successfullyAdded = false;
98
77
  if (viteConfigPath) {
99
78
  successfullyAdded = await addVitePluginToConfig(viteConfigPath, options);
100
79
  } else {
101
- successfullyAdded = await createNewViteConfig(options);
80
+ successfullyAdded = await createNewConfigFile(
81
+ path.join(process.cwd(), 'vite.config.js'),
82
+ getViteConfigSnippet(options, false),
83
+ 'More information about vite configs: https://vitejs.dev/config/',
84
+ );
85
+ Sentry.setTag(
86
+ 'created-new-config',
87
+ successfullyAdded ? 'success' : 'fail',
88
+ );
102
89
  }
103
90
 
104
91
  if (successfullyAdded) {
92
+ clack.log.info(
93
+ `We recommend checking the ${
94
+ viteConfigPath ? 'modified' : 'added'
95
+ } file after the wizard finished to ensure it works with your build setup.`,
96
+ );
97
+
105
98
  Sentry.setTag('ast-mod', 'success');
106
99
  } else {
107
100
  Sentry.setTag('ast-mod', 'fail');
108
101
  await showCopyPasteInstructions(
109
102
  path.basename(viteConfigPath || 'vite.config.js'),
110
- options,
103
+ getViteConfigSnippet(options, true),
111
104
  );
112
105
  }
113
106
 
114
107
  await addDotEnvSentryBuildPluginFile(options.authToken);
115
108
  };
116
109
 
117
- async function createNewViteConfig(
118
- options: SourceMapUploadToolConfigurationOptions,
119
- ): Promise<boolean> {
120
- try {
121
- await fs.promises.writeFile(
122
- 'vite.config.js',
123
- getViteConfigSnippet(options, false),
124
- );
125
- Sentry.setTag('created-new-config', 'success');
126
- return true;
127
- } catch (e) {
128
- debug(e);
129
- Sentry.setTag('created-new-config', 'fail');
130
- clack.log.warn(
131
- `Could not create a new ${chalk.cyan(
132
- 'vite.config.js',
133
- )} file. Please create one manually and follow the instructions below.`,
134
- );
135
-
136
- clack.log.info(
137
- chalk.gray(
138
- 'More information about vite configs: https://vitejs.dev/config/',
139
- ),
140
- );
141
-
142
- return false;
143
- }
144
- }
145
-
146
110
  export async function addVitePluginToConfig(
147
111
  viteConfigPath: string,
148
112
  options: SourceMapUploadToolConfigurationOptions,
@@ -156,7 +120,7 @@ export async function addVitePluginToConfig(
156
120
 
157
121
  const mod = parseModule(viteConfigContent);
158
122
 
159
- if (hasSentryContent(mod)) {
123
+ if (hasSentryContent(mod.$ast as t.Program)) {
160
124
  const shouldContinue = await abortIfCancelled(
161
125
  clack.select({
162
126
  message: `${prettyViteConfigFilename} already contains Sentry-related code. Should the wizard modify it anyway?`,
@@ -215,60 +179,6 @@ export async function addVitePluginToConfig(
215
179
  }
216
180
  }
217
181
 
218
- async function showCopyPasteInstructions(
219
- viteConfigFilename: string,
220
- options: SourceMapUploadToolConfigurationOptions,
221
- ) {
222
- clack.log.step(
223
- `Add the following code to your ${chalk.cyan(viteConfigFilename)} file:`,
224
- );
225
-
226
- // Intentionally logging directly to console here so that the code can be copied/pasted directly
227
- // eslint-disable-next-line no-console
228
- console.log(`\n${getViteConfigSnippet(options, true)}`);
229
-
230
- await abortIfCancelled(
231
- clack.select({
232
- message: 'Did you copy the snippet above?',
233
- options: [{ label: 'Yes, continue!', value: true }],
234
- initialValue: true,
235
- }),
236
- );
237
- }
238
-
239
- async function askForViteConfigPath(): Promise<string | undefined> {
240
- const hasViteConfig = await abortIfCancelled(
241
- clack.confirm({
242
- message: `Do you have a vite config file (e.g. ${chalk.cyan(
243
- 'vite.config.js',
244
- )}?`,
245
- initialValue: true,
246
- }),
247
- );
248
-
249
- if (!hasViteConfig) {
250
- return undefined;
251
- }
252
-
253
- return await abortIfCancelled(
254
- clack.text({
255
- message: 'Please enter the path to your vite config file:',
256
- placeholder: `.${path.sep}vite.config.js`,
257
- validate: (value) => {
258
- if (!value) {
259
- return 'Please enter a path.';
260
- }
261
-
262
- try {
263
- fs.accessSync(value);
264
- } catch {
265
- return 'Could not access the file at this path.';
266
- }
267
- },
268
- }),
269
- );
270
- }
271
-
272
182
  function enableSourcemapGeneration(program: t.Program): boolean {
273
183
  const configObj = getViteConfigObject(program);
274
184