@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,9 +1,12 @@
1
1
  // @ts-ignore - clack is ESM and TS complains about that. It works though
2
- import clack from '@clack/prompts';
2
+ import * as clack from '@clack/prompts';
3
3
  import chalk from 'chalk';
4
4
 
5
+ import * as Sentry from '@sentry/node';
6
+
5
7
  import {
6
8
  abort,
9
+ abortIfCancelled,
7
10
  addSentryCliConfig,
8
11
  confirmContinueEvenThoughNoGitRepo,
9
12
  ensurePackageIsInstalled,
@@ -12,46 +15,105 @@ import {
12
15
  installPackage,
13
16
  printWelcome,
14
17
  } from '../utils/clack-utils';
15
- import { hasPackageInstalled } from '../utils/package-json';
18
+ import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
16
19
  import { WizardOptions } from '../utils/types';
17
20
  import { createExamplePage } from './sdk-example';
18
21
  import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup';
22
+ import { traceStep, withTelemetry } from '../telemetry';
23
+ import { getKitVersionBucket, getSvelteVersionBucket } from './utils';
19
24
 
20
25
  export async function runSvelteKitWizard(
21
26
  options: WizardOptions,
27
+ ): Promise<void> {
28
+ return withTelemetry(
29
+ {
30
+ enabled: options.telemetryEnabled,
31
+ integration: 'sveltekit',
32
+ },
33
+ () => runSvelteKitWizardWithTelemetry(options),
34
+ );
35
+ }
36
+
37
+ export async function runSvelteKitWizardWithTelemetry(
38
+ options: WizardOptions,
22
39
  ): Promise<void> {
23
40
  printWelcome({
24
41
  wizardName: 'Sentry SvelteKit Wizard',
25
42
  promoCode: options.promoCode,
43
+ telemetryEnabled: options.telemetryEnabled,
26
44
  });
27
45
 
28
- await confirmContinueEvenThoughNoGitRepo();
46
+ await traceStep('detect-git', confirmContinueEvenThoughNoGitRepo);
29
47
 
30
48
  const packageJson = await getPackageDotJson();
31
- await ensurePackageIsInstalled(packageJson, '@sveltejs/kit', 'Sveltekit');
49
+ await traceStep('detect-framework-version', () =>
50
+ ensurePackageIsInstalled(packageJson, '@sveltejs/kit', 'Sveltekit'),
51
+ );
52
+
53
+ const kitVersion = getPackageVersion('@sveltejs/kit', packageJson);
54
+ const kitVersionBucket = getKitVersionBucket(kitVersion);
55
+ Sentry.setTag('sveltekit-version', kitVersionBucket);
56
+
57
+ if (kitVersionBucket === '0.x') {
58
+ clack.log.warn(
59
+ "It seems you're using a SvelteKit version <1.0.0 which is not supported by Sentry.\nWe recommend upgrading to the latest 1.x version before you continue.",
60
+ );
61
+ const shouldContinue = await abortIfCancelled(
62
+ clack.select({
63
+ message: 'Do you want to continue anyway?',
64
+ options: [
65
+ {
66
+ label: 'Yes, continue',
67
+ hint: 'The SDK might not work correctly',
68
+ value: true,
69
+ },
70
+ { label: "No, I'll upgrade first", value: false },
71
+ ],
72
+ }),
73
+ );
74
+ if (!shouldContinue) {
75
+ await abort('Exiting Wizard', 0);
76
+ return;
77
+ }
78
+ }
79
+
80
+ Sentry.setTag(
81
+ 'svelte-version',
82
+ getSvelteVersionBucket(getPackageVersion('svelte', packageJson)),
83
+ );
32
84
 
33
85
  const { selectedProject, selfHosted, sentryUrl, authToken } =
34
86
  await getOrAskForProjectData(options, 'javascript-sveltekit');
35
87
 
36
- await installPackage({
37
- packageName: '@sentry/sveltekit',
38
- alreadyInstalled: hasPackageInstalled('@sentry/sveltekit', packageJson),
39
- });
88
+ const sdkAlreadyInstalled = hasPackageInstalled(
89
+ '@sentry/sveltekit',
90
+ packageJson,
91
+ );
92
+ Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
93
+
94
+ await traceStep('install-sdk', () =>
95
+ installPackage({
96
+ packageName: '@sentry/sveltekit',
97
+ alreadyInstalled: sdkAlreadyInstalled,
98
+ }),
99
+ );
40
100
 
41
- await addSentryCliConfig(authToken);
101
+ await traceStep('add-cli-config', () => addSentryCliConfig(authToken));
42
102
 
43
- const svelteConfig = await loadSvelteConfig();
103
+ const svelteConfig = await traceStep('load-svelte-config', loadSvelteConfig);
44
104
 
45
105
  try {
46
- await createOrMergeSvelteKitFiles(
47
- {
48
- dsn: selectedProject.keys[0].dsn.public,
49
- org: selectedProject.organization.slug,
50
- project: selectedProject.slug,
51
- selfHosted,
52
- url: sentryUrl,
53
- },
54
- svelteConfig,
106
+ await traceStep('configure-sdk', () =>
107
+ createOrMergeSvelteKitFiles(
108
+ {
109
+ dsn: selectedProject.keys[0].dsn.public,
110
+ org: selectedProject.organization.slug,
111
+ project: selectedProject.slug,
112
+ selfHosted,
113
+ url: sentryUrl,
114
+ },
115
+ svelteConfig,
116
+ ),
55
117
  );
56
118
  } catch (e: unknown) {
57
119
  clack.log.error('Error while setting up the SvelteKit SDK:');
@@ -64,17 +126,20 @@ export async function runSvelteKitWizard(
64
126
  : 'Unknown error',
65
127
  ),
66
128
  );
129
+ Sentry.captureException('Error while setting up the SvelteKit SDK');
67
130
  await abort('Exiting Wizard');
68
131
  return;
69
132
  }
70
133
 
71
134
  try {
72
- await createExamplePage(svelteConfig, {
73
- selfHosted,
74
- url: sentryUrl,
75
- orgSlug: selectedProject.organization.slug,
76
- projectId: selectedProject.id,
77
- });
135
+ await traceStep('create-example-page', () =>
136
+ createExamplePage(svelteConfig, {
137
+ selfHosted,
138
+ url: sentryUrl,
139
+ orgSlug: selectedProject.organization.slug,
140
+ projectId: selectedProject.id,
141
+ }),
142
+ );
78
143
  } catch (e: unknown) {
79
144
  clack.log.error('Error while creating an example page to test Sentry:');
80
145
  clack.log.info(
@@ -86,6 +151,9 @@ export async function runSvelteKitWizard(
86
151
  : 'Unknown error',
87
152
  ),
88
153
  );
154
+ Sentry.captureException(
155
+ 'Error while creating an example Svelte page to test Sentry',
156
+ );
89
157
  await abort('Exiting Wizard');
90
158
  return;
91
159
  }
@@ -0,0 +1,50 @@
1
+ import { lt, minVersion } from 'semver';
2
+
3
+ export function getKitVersionBucket(
4
+ version: string | undefined,
5
+ ): 'none' | 'invalid' | '0.x' | '>=1.0.0 <1.24.0' | '>=1.24.0' {
6
+ if (!version) {
7
+ return 'none';
8
+ }
9
+
10
+ const minVer = minVersion(version);
11
+ if (!minVer) {
12
+ return 'invalid';
13
+ }
14
+
15
+ if (lt(minVer, '1.0.0')) {
16
+ return '0.x';
17
+ } else if (lt(minVer, '1.24.0')) {
18
+ return '>=1.0.0 <1.24.0';
19
+ } else {
20
+ // This is the version when the client-side invalidation fix was released
21
+ // https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%401.24.0
22
+ // https://github.com/sveltejs/kit/pull/10576
23
+ return '>=1.24.0';
24
+ }
25
+ }
26
+
27
+ export function getSvelteVersionBucket(
28
+ version: string | undefined,
29
+ ): 'none' | 'invalid' | '<3.0.0' | '3.x' | '4.x' | '>4.x' {
30
+ if (!version) {
31
+ return 'none';
32
+ }
33
+
34
+ const minVer = minVersion(version);
35
+ if (!minVer) {
36
+ return 'invalid';
37
+ }
38
+
39
+ if (lt(minVer, '3.0.0')) {
40
+ return '<3.0.0';
41
+ }
42
+ if (lt(minVer, '4.0.0')) {
43
+ return '3.x';
44
+ }
45
+ if (lt(minVer, '5.0.0')) {
46
+ return '4.x';
47
+ }
48
+ // Svelte 5 isn't released yet but it's being worked on
49
+ return '>4.x';
50
+ }
@@ -1,6 +1,10 @@
1
1
  import * as fs from 'fs';
2
- // @ts-ignore - magicast is ESM and TS complains about that. It works though
3
- import { ProxifiedModule } from 'magicast';
2
+
3
+ import * as recast from 'recast';
4
+ import x = recast.types;
5
+ import t = x.namedTypes;
6
+
7
+ const b = recast.types.builders;
4
8
 
5
9
  /**
6
10
  * Checks if a file where we don't know its concrete file type yet exists
@@ -8,15 +12,207 @@ import { ProxifiedModule } from 'magicast';
8
12
  */
9
13
  export function findFile(
10
14
  filePath: string,
11
- fileTypes: string[] = ['.js', '.ts', '.mjs'],
15
+ fileTypes: string[] = ['.js', '.ts', '.mjs', '.cjs'],
12
16
  ): string | undefined {
13
17
  return fileTypes
14
18
  .map((type) => `${filePath}${type}`)
15
19
  .find((file) => fs.existsSync(file));
16
20
  }
17
21
 
18
- /** Checks if a Sentry package is already mentioned in the file */
19
- export function hasSentryContent(mod: ProxifiedModule<object>): boolean {
20
- const imports = mod.imports.$items.map((i) => i.from);
21
- return !!imports.find((i) => i.startsWith('@sentry/'));
22
+ /**
23
+ * checks for require('@sentry/*') syntax
24
+ */
25
+ export function hasSentryContent(program: t.Program): boolean {
26
+ let foundSentry: boolean | undefined = false;
27
+ recast.visit(program, {
28
+ visitStringLiteral(path) {
29
+ foundSentry = foundSentry || path.node.value.startsWith('@sentry/');
30
+ this.traverse(path);
31
+ },
32
+ visitLiteral(path) {
33
+ foundSentry =
34
+ foundSentry || path.node.value?.toString().startsWith('@sentry/');
35
+ this.traverse(path);
36
+ },
37
+ });
38
+
39
+ return !!foundSentry;
40
+ }
41
+
42
+ /**
43
+ * Searches for a property of an ObjectExpression by name
44
+ *
45
+ * @param object the ObjectExpression to search in
46
+ * @param name the name of the property to search for
47
+ *
48
+ * @returns the property if it exists
49
+ */
50
+ export function getObjectProperty(
51
+ object: t.ObjectExpression,
52
+ name: string,
53
+ ): t.Property | undefined {
54
+ return object.properties.find((p): p is t.Property => {
55
+ const isObjectProp = p.type === 'Property' || p.type === 'ObjectProperty';
56
+
57
+ if (!isObjectProp) {
58
+ return false;
59
+ }
60
+
61
+ const hasMatchingLiteralKey =
62
+ isObjectProp &&
63
+ (p.key.type === 'Literal' || p.key.type === 'StringLiteral') &&
64
+ p.key.value === name;
65
+
66
+ if (hasMatchingLiteralKey) {
67
+ return true;
68
+ }
69
+
70
+ // has matching identifier key
71
+ return isObjectProp && p.key.type === 'Identifier' && p.key.name === name;
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Attempts to find a property of an ObjectExpression by name. If it doesn't exist,
77
+ * the property will be added to the ObjectExpression with the provided default value.
78
+ *
79
+ * @param object the parent object expression to search in
80
+ * @param name the name of the property to search for
81
+ * @param defaultValue the default value to set if the property doesn't exist
82
+ *
83
+ * @returns the
84
+ */
85
+ export function getOrSetObjectProperty(
86
+ object: t.ObjectExpression,
87
+ name: string,
88
+ defaultValue:
89
+ | t.Literal
90
+ | t.BooleanLiteral
91
+ | t.StringLiteral
92
+ | t.ObjectExpression,
93
+ ): t.Property {
94
+ const existingProperty = getObjectProperty(object, name);
95
+
96
+ if (existingProperty) {
97
+ return existingProperty;
98
+ }
99
+
100
+ const newProperty = b.property.from({
101
+ kind: 'init',
102
+ key: b.stringLiteral(name),
103
+ value: defaultValue,
104
+ });
105
+
106
+ object.properties.push(newProperty);
107
+
108
+ return newProperty;
109
+ }
110
+
111
+ /**
112
+ * Sets a property of an ObjectExpression if it exists, otherwise adds it
113
+ * to the ObjectExpression. Optionally, a comment can be added to the
114
+ * property.
115
+ *
116
+ * @param object the ObjectExpression to set the property on
117
+ * @param name the name of the property to set
118
+ * @param value the value of the property to set
119
+ * @param comment (optional) a comment to add to the property
120
+ */
121
+ export function setOrUpdateObjectProperty(
122
+ object: t.ObjectExpression,
123
+ name: string,
124
+ value: t.Literal | t.BooleanLiteral | t.StringLiteral | t.ObjectExpression,
125
+ comment?: string,
126
+ ) {
127
+ const newComments =
128
+ comment &&
129
+ comment.split('\n').map((c) => b.commentLine(` ${c}`, true, false));
130
+
131
+ const existingProperty = getObjectProperty(object, name);
132
+
133
+ if (existingProperty) {
134
+ existingProperty.value = value;
135
+ if (newComments) {
136
+ existingProperty.comments = [
137
+ ...(existingProperty?.comments || []),
138
+ ...newComments,
139
+ ];
140
+ }
141
+ } else {
142
+ object.properties.push(
143
+ b.objectProperty.from({
144
+ key: b.stringLiteral(name),
145
+ value,
146
+ ...(newComments && {
147
+ comments: newComments,
148
+ }),
149
+ }),
150
+ );
151
+ }
152
+ }
153
+
154
+ type JsonCParseResult =
155
+ | {
156
+ jsonObject: t.ObjectExpression;
157
+ ast: t.Program;
158
+ }
159
+ | {
160
+ jsonObject: undefined;
161
+ ast: undefined;
162
+ };
163
+
164
+ /**
165
+ * Parses a JSON string with (potential) comments (JSON-C) and returns the JS AST
166
+ * that can be walked and modified with recast like a normal JS AST.
167
+ *
168
+ * This is done by wrapping the JSON-C string in parentheses, thereby making it
169
+ * a JS `Program` with an `ExpressionStatement` as its body. The expression is then
170
+ * extracted from the AST and returned alongside the AST.
171
+ *
172
+ * To preserve as much original formatting as possible, the returned `ast`
173
+ * property should be passed to {@link `printJsonC`} to get the JSON-C string back.
174
+ *
175
+ * If the input is not valid JSON-C, the result will be undefined.
176
+ *
177
+ * @see {@link JsonCParseResult}
178
+ *
179
+ * @param jsonString a JSON-C string
180
+ *
181
+ * @returns a {@link JsonCParseResult}, containing either the JSON-C object and the AST or undefined in both cases
182
+ */
183
+ export function parseJsonC(jsonString: string): JsonCParseResult {
184
+ try {
185
+ const jsTsConfig = `(${jsonString})`;
186
+ // no idea why recast returns any here, this is dumb :/
187
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
188
+ const ast = recast.parse(jsTsConfig.toString()).program as t.Program;
189
+
190
+ const jsonObject =
191
+ (ast.body[0].type === 'ExpressionStatement' &&
192
+ ast.body[0].expression.type === 'ObjectExpression' &&
193
+ ast.body[0].expression) ||
194
+ undefined;
195
+
196
+ if (jsonObject) {
197
+ return { jsonObject, ast };
198
+ }
199
+ } catch {
200
+ /* empty */
201
+ }
202
+ return { jsonObject: undefined, ast: undefined };
203
+ }
204
+
205
+ /**
206
+ * Takes the AST of a parsed JSON-C "program" and returns the JSON-C string without
207
+ * any of the temporary JS wrapper code that was previously applied.
208
+ *
209
+ * Only use this in conjunction with {@link `parseJsonC`}
210
+ *
211
+ * @param ast the `ast` returned from {@link `parseJsonC`}
212
+ *
213
+ * @returns the JSON-C string
214
+ */
215
+ export function printJsonC(ast: t.Program): string {
216
+ const js = recast.print(ast).code;
217
+ return js.substring(1, js.length - 1);
22
218
  }