@sentry/wizard 3.8.0 → 3.9.1

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 (53) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/bin.ts +14 -0
  3. package/dist/bin.js +9 -0
  4. package/dist/bin.js.map +1 -1
  5. package/dist/lib/Helper/Logging.d.ts +1 -0
  6. package/dist/lib/Helper/Logging.js +2 -1
  7. package/dist/lib/Helper/Logging.js.map +1 -1
  8. package/dist/lib/Setup.js +4 -0
  9. package/dist/lib/Setup.js.map +1 -1
  10. package/dist/lib/Steps/Integrations/ReactNative.js +3 -3
  11. package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
  12. package/dist/package.json +11 -7
  13. package/dist/src/apple/xcode-manager.js +6 -2
  14. package/dist/src/apple/xcode-manager.js.map +1 -1
  15. package/dist/src/sourcemaps/tools/sentry-cli.js +1 -1
  16. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  17. package/dist/src/sourcemaps/tools/vite.js +208 -22
  18. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  19. package/dist/src/sveltekit/sdk-setup.d.ts +9 -1
  20. package/dist/src/sveltekit/sdk-setup.js +73 -29
  21. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  22. package/dist/src/sveltekit/sveltekit-wizard.js +9 -5
  23. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  24. package/dist/src/utils/ast-utils.d.ts +8 -0
  25. package/dist/src/utils/ast-utils.js +45 -0
  26. package/dist/src/utils/ast-utils.js.map +1 -0
  27. package/dist/src/utils/debug.d.ts +2 -0
  28. package/dist/src/utils/debug.js +51 -0
  29. package/dist/src/utils/debug.js.map +1 -0
  30. package/dist/src/utils/string.d.ts +1 -0
  31. package/dist/src/utils/string.js +10 -0
  32. package/dist/src/utils/string.js.map +1 -0
  33. package/dist/test/utils/ast-utils.test.d.ts +1 -0
  34. package/dist/test/utils/ast-utils.test.js +21 -0
  35. package/dist/test/utils/ast-utils.test.js.map +1 -0
  36. package/lib/Helper/Logging.ts +1 -1
  37. package/lib/Setup.ts +5 -0
  38. package/lib/Steps/Integrations/ReactNative.ts +7 -3
  39. package/package.json +11 -7
  40. package/src/apple/xcode-manager.ts +7 -2
  41. package/src/sourcemaps/tools/sentry-cli.ts +1 -1
  42. package/src/sourcemaps/tools/vite.ts +220 -28
  43. package/src/sveltekit/sdk-setup.ts +122 -43
  44. package/src/sveltekit/sveltekit-wizard.ts +12 -6
  45. package/src/utils/ast-utils.ts +20 -0
  46. package/src/utils/debug.ts +20 -0
  47. package/src/utils/string.ts +7 -0
  48. package/test/utils/ast-utils.test.ts +44 -0
  49. package/dist/src/sveltekit/sentry-cli-setup.d.ts +0 -2
  50. package/dist/src/sveltekit/sentry-cli-setup.js +0 -71
  51. package/dist/src/sveltekit/sentry-cli-setup.js.map +0 -1
  52. package/package-lock.json +0 -8910
  53. package/src/sveltekit/sentry-cli-setup.ts +0 -27
@@ -1,5 +1,12 @@
1
1
  // @ts-ignore - clack is ESM and TS complains about that. It works though
2
2
  import clack, { select } from '@clack/prompts';
3
+ // @ts-ignore - magicast is ESM and TS complains about that. It works though
4
+ import { generateCode, parseModule } from 'magicast';
5
+ // @ts-ignore - magicast is ESM and TS complains about that. It works though
6
+ import { addVitePlugin } from 'magicast/helpers';
7
+
8
+ import * as Sentry from '@sentry/node';
9
+
3
10
  import chalk from 'chalk';
4
11
  import {
5
12
  abortIfCancelled,
@@ -13,30 +20,61 @@ import {
13
20
  SourceMapUploadToolConfigurationFunction,
14
21
  SourceMapUploadToolConfigurationOptions,
15
22
  } from './types';
23
+ import { findScriptFile, hasSentryContent } from '../../utils/ast-utils';
24
+
25
+ import * as path from 'path';
26
+ import * as fs from 'fs';
27
+ import { debug } from '../../utils/debug';
28
+
29
+ const getViteConfigSnippet = (
30
+ options: SourceMapUploadToolConfigurationOptions,
31
+ colors: boolean,
32
+ ) => {
33
+ const rawImportStmt =
34
+ 'import { sentryVitePlugin } from "@sentry/vite-plugin";';
35
+ const rawGenerateSourceMapsOption =
36
+ 'sourcemap: true, // Source map generation must be turned on';
37
+ const rawSentryVitePluginFunction = `sentryVitePlugin({
38
+ authToken: process.env.SENTRY_AUTH_TOKEN,
39
+ org: "${options.orgSlug}",
40
+ project: "${options.projectSlug}",${
41
+ options.selfHosted ? `\n url: "${options.url}",` : ''
42
+ }
43
+ }),`;
44
+
45
+ const importStmt = colors ? chalk.greenBright(rawImportStmt) : rawImportStmt;
46
+ const generateSourceMapsOption = colors
47
+ ? chalk.greenBright(rawGenerateSourceMapsOption)
48
+ : rawGenerateSourceMapsOption;
49
+ const sentryVitePluginFunction = colors
50
+ ? chalk.greenBright(rawSentryVitePluginFunction)
51
+ : rawSentryVitePluginFunction;
52
+
53
+ const code = getViteConfigContent(
54
+ importStmt,
55
+ generateSourceMapsOption,
56
+ sentryVitePluginFunction,
57
+ );
58
+ return colors ? chalk.gray(code) : code;
59
+ };
16
60
 
17
- const getCodeSnippet = (options: SourceMapUploadToolConfigurationOptions) =>
18
- chalk.gray(`
19
- import { defineConfig } from "vite";
20
- ${chalk.greenBright('import { sentryVitePlugin } from "@sentry/vite-plugin"')};
61
+ const getViteConfigContent = (
62
+ importStmt: string,
63
+ generateSourceMapsOption: string,
64
+ sentryVitePluginFunction: string,
65
+ ) => `import { defineConfig } from "vite";
66
+ ${importStmt}
21
67
 
22
68
  export default defineConfig({
23
69
  build: {
24
- ${chalk.greenBright(
25
- 'sourcemap: true, // Source map generation must be turned on',
26
- )}
70
+ ${generateSourceMapsOption}
27
71
  },
28
72
  plugins: [
29
73
  // Put the Sentry vite plugin after all other plugins
30
- ${chalk.greenBright(`sentryVitePlugin({
31
- authToken: process.env.SENTRY_AUTH_TOKEN,
32
- org: "${options.orgSlug}",
33
- project: "${options.projectSlug}",${
34
- options.selfHosted ? `\n url: "${options.url}",` : ''
35
- }
36
- }),`)}
74
+ ${sentryVitePluginFunction}
37
75
  ],
38
76
  });
39
- `);
77
+ `;
40
78
 
41
79
  export const configureVitePlugin: SourceMapUploadToolConfigurationFunction =
42
80
  async (options) => {
@@ -48,21 +86,175 @@ export const configureVitePlugin: SourceMapUploadToolConfigurationFunction =
48
86
  ),
49
87
  });
50
88
 
51
- clack.log.step(
52
- `Add the following code to your ${chalk.bold('vite.config.js')} file:`,
53
- );
89
+ const viteConfigPath =
90
+ findScriptFile(path.resolve(process.cwd(), 'vite.config')) ||
91
+ (await askForViteConfigPath());
54
92
 
55
- // Intentially logging directly to console here so that the code can be copied/pasted directly
56
- // eslint-disable-next-line no-console
57
- console.log(getCodeSnippet(options));
93
+ let successfullyAdded = false;
94
+ if (viteConfigPath) {
95
+ successfullyAdded = await addVitePluginToConfig(viteConfigPath, options);
96
+ } else {
97
+ successfullyAdded = await createNewViteConfig(options);
98
+ }
58
99
 
59
- await abortIfCancelled(
60
- select({
61
- message: 'Did you copy the snippet above?',
62
- options: [{ label: 'Yes, continue!', value: true }],
63
- initialValue: true,
64
- }),
65
- );
100
+ if (successfullyAdded) {
101
+ Sentry.setTag('ast-mod', 'success');
102
+ } else {
103
+ Sentry.setTag('ast-mod', 'fail');
104
+ await showCopyPasteInstructions(
105
+ path.basename(viteConfigPath || 'vite.config.js'),
106
+ options,
107
+ );
108
+ }
66
109
 
67
110
  await addDotEnvSentryBuildPluginFile(options.authToken);
68
111
  };
112
+
113
+ async function createNewViteConfig(
114
+ options: SourceMapUploadToolConfigurationOptions,
115
+ ): Promise<boolean> {
116
+ try {
117
+ await fs.promises.writeFile(
118
+ 'vite.config.js',
119
+ getViteConfigSnippet(options, false),
120
+ );
121
+ Sentry.setTag('created-new-config', 'success');
122
+ return true;
123
+ } catch (e) {
124
+ debug(e);
125
+ Sentry.setTag('created-new-config', 'fail');
126
+ clack.log.warn(
127
+ `Could not create a new ${chalk.cyan(
128
+ 'vite.config.js',
129
+ )} file. Please create one manually and follow the instructions below.`,
130
+ );
131
+
132
+ clack.log.info(
133
+ chalk.gray(
134
+ 'More information about vite configs: https://vitejs.dev/config/',
135
+ ),
136
+ );
137
+
138
+ return false;
139
+ }
140
+ }
141
+
142
+ async function addVitePluginToConfig(
143
+ viteConfigPath: string,
144
+ options: SourceMapUploadToolConfigurationOptions,
145
+ ): Promise<boolean> {
146
+ try {
147
+ const prettyViteConfigFilename = chalk.cyan(path.basename(viteConfigPath));
148
+
149
+ const viteConfigContent = (
150
+ await fs.promises.readFile(viteConfigPath)
151
+ ).toString();
152
+
153
+ const mod = parseModule(viteConfigContent);
154
+
155
+ if (hasSentryContent(mod)) {
156
+ const shouldContinue = await abortIfCancelled(
157
+ clack.select({
158
+ message: `${prettyViteConfigFilename} already contains Sentry-related code. Should the wizard modify it anyway?`,
159
+ options: [
160
+ {
161
+ label: 'Yes, add the Sentry Vite plugin',
162
+ value: true,
163
+ },
164
+ {
165
+ label: 'No, show me instructions to manually add the plugin',
166
+ value: false,
167
+ },
168
+ ],
169
+ initialValue: true,
170
+ }),
171
+ );
172
+
173
+ if (!shouldContinue) {
174
+ Sentry.setTag('ast-mod-fail-reason', 'has-sentry-content');
175
+ return false;
176
+ }
177
+ }
178
+
179
+ const { orgSlug: org, projectSlug: project, selfHosted, url } = options;
180
+
181
+ addVitePlugin(mod, {
182
+ imported: 'sentryVitePlugin',
183
+ from: '@sentry/vite-plugin',
184
+ constructor: 'sentryVitePlugin',
185
+ options: {
186
+ org,
187
+ project,
188
+ ...(selfHosted && { url }),
189
+ },
190
+ });
191
+
192
+ const code = generateCode(mod.$ast).code;
193
+
194
+ await fs.promises.writeFile(viteConfigPath, code);
195
+
196
+ clack.log.success(
197
+ `Added the Sentry Vite plugin to ${prettyViteConfigFilename}`,
198
+ );
199
+
200
+ return true;
201
+ } catch (e) {
202
+ debug(e);
203
+ Sentry.setTag('ast-mod-fail-reason', 'insertion-fail');
204
+ return false;
205
+ }
206
+ }
207
+
208
+ async function showCopyPasteInstructions(
209
+ viteConfigFilename: string,
210
+ options: SourceMapUploadToolConfigurationOptions,
211
+ ) {
212
+ clack.log.step(
213
+ `Add the following code to your ${chalk.cyan(viteConfigFilename)} file:`,
214
+ );
215
+
216
+ // Intentionally logging directly to console here so that the code can be copied/pasted directly
217
+ // eslint-disable-next-line no-console
218
+ console.log(`\n${getViteConfigSnippet(options, true)}`);
219
+
220
+ await abortIfCancelled(
221
+ select({
222
+ message: 'Did you copy the snippet above?',
223
+ options: [{ label: 'Yes, continue!', value: true }],
224
+ initialValue: true,
225
+ }),
226
+ );
227
+ }
228
+
229
+ async function askForViteConfigPath(): Promise<string | undefined> {
230
+ const hasViteConfig = await abortIfCancelled(
231
+ clack.confirm({
232
+ message: `Do you have a vite config file (e.g. ${chalk.cyan(
233
+ 'vite.config.js',
234
+ )}?`,
235
+ initialValue: true,
236
+ }),
237
+ );
238
+
239
+ if (!hasViteConfig) {
240
+ return undefined;
241
+ }
242
+
243
+ return await abortIfCancelled(
244
+ clack.text({
245
+ message: 'Please enter the path to your vite config file:',
246
+ placeholder: `.${path.sep}vite.config.js`,
247
+ validate: (value) => {
248
+ if (!value) {
249
+ return 'Please enter a path.';
250
+ }
251
+
252
+ try {
253
+ fs.accessSync(value);
254
+ } catch {
255
+ return 'Could not access the file at this path.';
256
+ }
257
+ },
258
+ }),
259
+ );
260
+ }
@@ -13,7 +13,9 @@ 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
+ import { abortIfCancelled, isUsingTypeScript } from '../utils/clack-utils';
17
+ import { debug } from '../utils/debug';
18
+ import { findScriptFile, hasSentryContent } from '../utils/ast-utils';
17
19
 
18
20
  const SVELTE_CONFIG_FILE = 'svelte.config.js';
19
21
 
@@ -29,20 +31,30 @@ export type PartialSvelteConfig = {
29
31
  };
30
32
  };
31
33
 
34
+ type ProjectInfo = {
35
+ dsn: string;
36
+ org: string;
37
+ project: string;
38
+ selfHosted: boolean;
39
+ url: string;
40
+ };
41
+
32
42
  export async function createOrMergeSvelteKitFiles(
33
- dsn: string,
43
+ projectInfo: ProjectInfo,
34
44
  svelteConfig: PartialSvelteConfig,
35
45
  ): Promise<void> {
36
46
  const { clientHooksPath, serverHooksPath } = getHooksConfigDirs(svelteConfig);
37
47
 
38
48
  // full file paths with correct file ending (or undefined if not found)
39
- const originalClientHooksFile = findHooksFile(clientHooksPath);
40
- const originalServerHooksFile = findHooksFile(serverHooksPath);
49
+ const originalClientHooksFile = findScriptFile(clientHooksPath);
50
+ const originalServerHooksFile = findScriptFile(serverHooksPath);
41
51
 
42
- const viteConfig = findHooksFile(path.resolve(process.cwd(), 'vite.config'));
52
+ const viteConfig = findScriptFile(path.resolve(process.cwd(), 'vite.config'));
43
53
 
44
54
  const fileEnding = isUsingTypeScript() ? 'ts' : 'js';
45
55
 
56
+ const { dsn } = projectInfo;
57
+
46
58
  if (!originalClientHooksFile) {
47
59
  clack.log.info('No client hooks file found, creating a new one.');
48
60
  await createNewHooksFile(`${clientHooksPath}.${fileEnding}`, 'client', dsn);
@@ -60,7 +72,7 @@ export async function createOrMergeSvelteKitFiles(
60
72
  }
61
73
 
62
74
  if (viteConfig) {
63
- await modifyViteConfig(viteConfig);
75
+ await modifyViteConfig(viteConfig, projectInfo);
64
76
  }
65
77
  }
66
78
 
@@ -91,16 +103,6 @@ function getHooksConfigDirs(svelteConfig: PartialSvelteConfig): {
91
103
  };
92
104
  }
93
105
 
94
- /**
95
- * Checks if a hooks file exists and returns the full path to the file with the correct file type.
96
- */
97
- function findHooksFile(hooksFile: string): string | undefined {
98
- const possibleFileTypes = ['.js', '.ts', '.mjs'];
99
- return possibleFileTypes
100
- .map((type) => `${hooksFile}${type}`)
101
- .find((file) => fs.existsSync(file));
102
- }
103
-
104
106
  /**
105
107
  * Reads the template, replaces the dsn placeholder with the actual dsn and writes the file to @param hooksFileDest
106
108
  */
@@ -137,9 +139,15 @@ async function mergeHooksFile(
137
139
  dsn: string,
138
140
  ): Promise<void> {
139
141
  const originalHooksMod = await loadFile(hooksFile);
140
- if (hasSentryContent(path.basename(hooksFile), originalHooksMod.$code)) {
142
+ if (hasSentryContent(originalHooksMod)) {
141
143
  // We don't want to mess with files that already have Sentry content.
142
144
  // Let's just bail out at this point.
145
+ clack.log.warn(
146
+ `File ${chalk.cyan(
147
+ path.basename(hooksFile),
148
+ )} already contains Sentry code.
149
+ Skipping adding Sentry functionality to.`,
150
+ );
143
151
  return;
144
152
  }
145
153
 
@@ -347,20 +355,6 @@ function wrapHandle(mod: ProxifiedModule<any>): void {
347
355
  }
348
356
  }
349
357
 
350
- /** Checks if the Sentry SvelteKit SDK is already mentioned in the file */
351
- function hasSentryContent(fileName: string, fileContent: string): boolean {
352
- if (fileContent.includes('@sentry/sveltekit')) {
353
- clack.log.warn(
354
- `File ${chalk.cyan(path.basename(fileName))} already contains Sentry code.
355
- Skipping adding Sentry functionality to ${chalk.cyan(
356
- path.basename(fileName),
357
- )}.`,
358
- );
359
- return true;
360
- }
361
- return false;
362
- }
363
-
364
358
  export async function loadSvelteConfig(): Promise<PartialSvelteConfig> {
365
359
  const configFilePath = path.join(process.cwd(), SVELTE_CONFIG_FILE);
366
360
 
@@ -392,28 +386,113 @@ Please make sure, you're running this wizard with Node 16 or newer`);
392
386
  }
393
387
  }
394
388
 
395
- async function modifyViteConfig(viteConfigPath: string): Promise<void> {
389
+ async function modifyViteConfig(
390
+ viteConfigPath: string,
391
+ projectInfo: ProjectInfo,
392
+ ): Promise<void> {
396
393
  const viteConfigContent = (
397
394
  await fs.promises.readFile(viteConfigPath, 'utf-8')
398
395
  ).toString();
399
396
 
400
- if (hasSentryContent(viteConfigPath, viteConfigContent)) {
401
- return;
397
+ const { org, project, url, selfHosted } = projectInfo;
398
+
399
+ try {
400
+ const viteModule = parseModule(viteConfigContent);
401
+
402
+ if (hasSentryContent(viteModule)) {
403
+ clack.log.warn(
404
+ `File ${chalk.cyan(
405
+ path.basename(viteConfigPath),
406
+ )} already contains Sentry code.
407
+ Skipping adding Sentry functionality to.`,
408
+ );
409
+ return;
410
+ }
411
+
412
+ addVitePlugin(viteModule, {
413
+ imported: 'sentrySvelteKit',
414
+ from: '@sentry/sveltekit',
415
+ constructor: 'sentrySvelteKit',
416
+ options: {
417
+ sourceMapsUploadOptions: {
418
+ org,
419
+ project,
420
+ ...(selfHosted && { url }),
421
+ },
422
+ },
423
+ index: 0,
424
+ });
425
+
426
+ const code = generateCode(viteModule.$ast).code;
427
+
428
+ await fs.promises.writeFile(viteConfigPath, code);
429
+ } catch (e) {
430
+ debug(e);
431
+ await showFallbackViteCopyPasteSnippet(
432
+ viteConfigPath,
433
+ getViteConfigCodeSnippet(org, project, selfHosted, url),
434
+ );
402
435
  }
436
+ }
403
437
 
404
- const viteModule = parseModule(viteConfigContent);
438
+ async function showFallbackViteCopyPasteSnippet(
439
+ viteConfigPath: string,
440
+ codeSnippet: string,
441
+ ) {
442
+ const viteConfigFilename = path.basename(viteConfigPath);
405
443
 
406
- addVitePlugin(viteModule, {
407
- imported: 'sentrySvelteKit',
408
- from: '@sentry/sveltekit',
409
- constructor: 'sentrySvelteKit',
410
- index: 0,
411
- });
444
+ clack.log.warning(
445
+ `Couldn't automatically modify your ${chalk.cyan(viteConfigFilename)}
446
+ ${chalk.dim(`This sometimes happens when we encounter more complex vite configs.
447
+ It may not seem like it but sometimes our magical powers are limited ;)`)}`,
448
+ );
449
+
450
+ clack.log.info("But don't worry - it's super easy to do this yourself!");
412
451
 
413
- const code = generateCode(viteModule.$ast).code;
414
- await fs.promises.writeFile(viteConfigPath, code);
452
+ clack.log.step(
453
+ `Add the following code to your ${chalk.cyan(viteConfigFilename)}:`,
454
+ );
455
+
456
+ // Intentionally logging to console here for easier copy/pasting
457
+ // eslint-disable-next-line no-console
458
+ console.log(codeSnippet);
459
+
460
+ await abortIfCancelled(
461
+ clack.select({
462
+ message: 'Did you copy the snippet above?',
463
+ options: [
464
+ { label: 'Yes!', value: true, hint: "Great, that's already it!" },
465
+ ],
466
+ initialValue: true,
467
+ }),
468
+ );
415
469
  }
416
470
 
471
+ const getViteConfigCodeSnippet = (
472
+ org: string,
473
+ project: string,
474
+ selfHosted: boolean,
475
+ url: string,
476
+ ) =>
477
+ chalk.gray(`
478
+ import { sveltekit } from '@sveltejs/kit/vite';
479
+ import { defineConfig } from 'vite';
480
+ ${chalk.greenBright("import { sentrySvelteKit } from '@sentry/sveltekit'")}
481
+
482
+ export default defineConfig({
483
+ plugins: [
484
+ // Make sure \`sentrySvelteKit\` is registered before \`sveltekit\`
485
+ ${chalk.greenBright(`sentrySvelteKit({
486
+ sourceMapsUploadOptions: {
487
+ org: '${org}',
488
+ project: '${project}',${selfHosted ? `\n url: '${url}',` : ''}
489
+ }
490
+ }),`)}
491
+ sveltekit(),
492
+ ]
493
+ });
494
+ `);
495
+
417
496
  /**
418
497
  * We want to insert the init call on top of the file but after all import statements
419
498
  */
@@ -4,6 +4,7 @@ import chalk from 'chalk';
4
4
 
5
5
  import {
6
6
  abort,
7
+ addSentryCliRc,
7
8
  askForProjectSelection,
8
9
  askForSelfHosted,
9
10
  askForWizardLogin,
@@ -18,8 +19,6 @@ import { WizardOptions } from '../utils/types';
18
19
  import { createExamplePage } from './sdk-example';
19
20
  import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup';
20
21
 
21
- import { setupCLIConfig } from './sentry-cli-setup';
22
-
23
22
  export async function runSvelteKitWizard(
24
23
  options: WizardOptions,
25
24
  ): Promise<void> {
@@ -48,14 +47,21 @@ export async function runSvelteKitWizard(
48
47
  alreadyInstalled: hasPackageInstalled('@sentry/sveltekit', packageJson),
49
48
  });
50
49
 
51
- await setupCLIConfig(apiKeys.token, selectedProject, sentryUrl);
52
-
53
- const dsn = selectedProject.keys[0].dsn.public;
50
+ await addSentryCliRc(apiKeys.token);
54
51
 
55
52
  const svelteConfig = await loadSvelteConfig();
56
53
 
57
54
  try {
58
- await createOrMergeSvelteKitFiles(dsn, svelteConfig);
55
+ await createOrMergeSvelteKitFiles(
56
+ {
57
+ dsn: selectedProject.keys[0].dsn.public,
58
+ org: selectedProject.organization.slug,
59
+ project: selectedProject.slug,
60
+ selfHosted,
61
+ url: sentryUrl,
62
+ },
63
+ svelteConfig,
64
+ );
59
65
  } catch (e: unknown) {
60
66
  clack.log.error('Error while setting up the SvelteKit SDK:');
61
67
  clack.log.info(
@@ -0,0 +1,20 @@
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';
4
+
5
+ /**
6
+ * Checks if a JS/TS file where we don't know its concrete file type yet exists
7
+ * and returns the full path to the file with the correct file type.
8
+ */
9
+ export function findScriptFile(hooksFile: string): string | undefined {
10
+ const possibleFileTypes = ['.js', '.ts', '.mjs'];
11
+ return possibleFileTypes
12
+ .map((type) => `${hooksFile}${type}`)
13
+ .find((file) => fs.existsSync(file));
14
+ }
15
+
16
+ /** Checks if a Sentry package is already mentioned in the file */
17
+ export function hasSentryContent(mod: ProxifiedModule<object>): boolean {
18
+ const imports = mod.imports.$items.map((i) => i.from);
19
+ return !!imports.find((i) => i.startsWith('@sentry/'));
20
+ }
@@ -0,0 +1,20 @@
1
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
2
+ import * as clack from '@clack/prompts';
3
+ import chalk from 'chalk';
4
+ import { prepareMessage } from '../../lib/Helper/Logging';
5
+
6
+ let debugEnabled = false;
7
+
8
+ export function debug(...args: unknown[]) {
9
+ if (!debugEnabled) {
10
+ return;
11
+ }
12
+
13
+ const msg = args.map((a) => prepareMessage(a)).join(' ');
14
+
15
+ clack.log.info(chalk.dim(msg));
16
+ }
17
+
18
+ export function enableDebugLogs() {
19
+ debugEnabled = true;
20
+ }
@@ -0,0 +1,7 @@
1
+ export function stripAnsii(str: string): string {
2
+ return str.replace(
3
+ // eslint-disable-next-line no-control-regex
4
+ /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
5
+ '',
6
+ );
7
+ }
@@ -0,0 +1,44 @@
1
+ //@ts-ignore
2
+ import { parseModule } from 'magicast';
3
+ import { hasSentryContent } from '../../src/utils/ast-utils';
4
+
5
+ describe('AST utils', () => {
6
+ describe('hasSentryContent', () => {
7
+ it("returns true if a '@sentry/' import was found in the parsed module", () => {
8
+ const code = `
9
+ import { sentryVitePlugin } from "@sentry/vite-plugin";
10
+ import * as somethingelse from 'gs';
11
+
12
+ export default {
13
+ plugins: [sentryVitePlugin()]
14
+ }
15
+ `;
16
+
17
+ expect(hasSentryContent(parseModule(code))).toBe(true);
18
+ });
19
+ it.each([
20
+ `
21
+ import * as somethingelse from 'gs';
22
+ export default {
23
+ plugins: []
24
+ }
25
+ `,
26
+ `import * as somethingelse from 'gs';
27
+ // import { sentryVitePlugin } from "@sentry/vite-plugin"
28
+ export default {
29
+ plugins: []
30
+ }
31
+ `,
32
+ `import * as thirdPartyVitePlugin from "vite-plugin-@sentry"
33
+ export default {
34
+ plugins: [thirdPartyVitePlugin()]
35
+ }
36
+ `,
37
+ ])(
38
+ "reutrns false for modules without a valid '@sentry/' import",
39
+ (code) => {
40
+ expect(hasSentryContent(parseModule(code))).toBe(false);
41
+ },
42
+ );
43
+ });
44
+ });
@@ -1,2 +0,0 @@
1
- import { SentryProjectData } from '../utils/types';
2
- export declare function setupCLIConfig(authToken: string, selectedProject: SentryProjectData, sentryUrl: string): Promise<void>;