@sentry/wizard 3.7.1 → 3.9.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.
- package/CHANGELOG.md +21 -0
- package/bin.ts +14 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -1
- package/dist/lib/Helper/Logging.d.ts +1 -0
- package/dist/lib/Helper/Logging.js +2 -1
- package/dist/lib/Helper/Logging.js.map +1 -1
- package/dist/lib/Helper/__tests__/MergeConfig.js.map +1 -1
- package/dist/lib/Setup.js +4 -0
- package/dist/lib/Setup.js.map +1 -1
- package/dist/lib/Steps/ChooseIntegration.js +12 -26
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/lib/Steps/Integrations/Cordova.js.map +1 -1
- package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
- package/dist/lib/Steps/Integrations/MobileProject.js.map +1 -1
- package/dist/lib/Steps/Integrations/ReactNative.js +5 -5
- package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js +5 -6
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js.map +1 -1
- package/dist/package.json +11 -7
- package/dist/src/apple/apple-wizard.js +31 -2
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/apple/cocoapod.d.ts +2 -0
- package/dist/src/apple/cocoapod.js +122 -0
- package/dist/src/apple/cocoapod.js.map +1 -0
- package/dist/src/apple/code-tools.js +22 -12
- package/dist/src/apple/code-tools.js.map +1 -1
- package/dist/src/apple/fastlane.d.ts +2 -0
- package/dist/src/apple/fastlane.js +179 -0
- package/dist/src/apple/fastlane.js.map +1 -0
- package/dist/src/apple/templates.d.ts +1 -0
- package/dist/src/apple/templates.js +7 -3
- package/dist/src/apple/templates.js.map +1 -1
- package/dist/src/apple/xcode-manager.d.ts +1 -1
- package/dist/src/apple/xcode-manager.js +35 -28
- package/dist/src/apple/xcode-manager.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +71 -81
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.js +61 -46
- package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
- package/dist/src/sourcemaps/tools/nextjs.d.ts +3 -0
- package/dist/src/sourcemaps/tools/nextjs.js +135 -0
- package/dist/src/sourcemaps/tools/nextjs.js.map +1 -0
- package/dist/src/sourcemaps/tools/sentry-cli.js +120 -16
- package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
- package/dist/src/sourcemaps/tools/vite.js +102 -12
- package/dist/src/sourcemaps/tools/vite.js.map +1 -1
- package/dist/src/sourcemaps/utils/detect-tool.d.ts +1 -1
- package/dist/src/sourcemaps/utils/detect-tool.js +1 -0
- package/dist/src/sourcemaps/utils/detect-tool.js.map +1 -1
- package/dist/src/sourcemaps/utils/other-wizards.js +35 -12
- package/dist/src/sourcemaps/utils/other-wizards.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.d.ts +9 -1
- package/dist/src/sveltekit/sdk-setup.js +73 -29
- package/dist/src/sveltekit/sdk-setup.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +23 -13
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/ast-utils.d.ts +8 -0
- package/dist/src/utils/ast-utils.js +45 -0
- package/dist/src/utils/ast-utils.js.map +1 -0
- package/dist/src/utils/bash.d.ts +2 -1
- package/dist/src/utils/bash.js +14 -2
- package/dist/src/utils/bash.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +7 -14
- package/dist/src/utils/clack-utils.js +46 -2
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/debug.d.ts +2 -0
- package/dist/src/utils/debug.js +51 -0
- package/dist/src/utils/debug.js.map +1 -0
- package/dist/src/utils/package-json.d.ts +1 -1
- package/dist/src/utils/package-json.js.map +1 -1
- package/dist/src/utils/types.d.ts +24 -0
- package/dist/src/utils/types.js.map +1 -1
- package/dist/test/utils/ast-utils.test.d.ts +1 -0
- package/dist/test/utils/ast-utils.test.js +21 -0
- package/dist/test/utils/ast-utils.test.js.map +1 -0
- package/lib/Helper/Logging.ts +1 -1
- package/lib/Helper/__tests__/MergeConfig.ts +9 -4
- package/lib/Setup.ts +5 -0
- package/lib/Steps/ChooseIntegration.ts +13 -3
- package/lib/Steps/Integrations/Cordova.ts +3 -3
- package/lib/Steps/Integrations/Electron.ts +1 -2
- package/lib/Steps/Integrations/MobileProject.ts +1 -1
- package/lib/Steps/Integrations/ReactNative.ts +23 -17
- package/lib/Steps/Integrations/__tests__/ReactNative.ts +24 -15
- package/package.json +11 -7
- package/src/apple/apple-wizard.ts +35 -3
- package/src/apple/cocoapod.ts +57 -0
- package/src/apple/code-tools.ts +80 -57
- package/src/apple/fastlane.ts +160 -0
- package/src/apple/templates.ts +26 -10
- package/src/apple/xcode-manager.ts +137 -120
- package/src/nextjs/nextjs-wizard.ts +4 -13
- package/src/sourcemaps/sourcemaps-wizard.ts +40 -28
- package/src/sourcemaps/tools/nextjs.ts +114 -0
- package/src/sourcemaps/tools/sentry-cli.ts +134 -8
- package/src/sourcemaps/tools/vite.ts +101 -12
- package/src/sourcemaps/utils/detect-tool.ts +3 -1
- package/src/sourcemaps/utils/other-wizards.ts +32 -13
- package/src/sveltekit/sdk-setup.ts +122 -43
- package/src/sveltekit/sveltekit-wizard.ts +15 -6
- package/src/utils/ast-utils.ts +20 -0
- package/src/utils/bash.ts +43 -30
- package/src/utils/clack-utils.ts +42 -14
- package/src/utils/debug.ts +20 -0
- package/src/utils/package-json.ts +1 -1
- package/src/utils/types.ts +22 -0
- package/test/utils/ast-utils.test.ts +44 -0
- package/dist/src/sveltekit/sentry-cli-setup.d.ts +0 -2
- package/dist/src/sveltekit/sentry-cli-setup.js +0 -71
- package/dist/src/sveltekit/sentry-cli-setup.js.map +0 -1
- package/package-lock.json +0 -8910
- package/src/sveltekit/sentry-cli-setup.ts +0 -27
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
2
2
|
import clack from '@clack/prompts';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { runNextjsWizard } from '../../nextjs/nextjs-wizard';
|
|
5
4
|
import { runSvelteKitWizard } from '../../sveltekit/sveltekit-wizard';
|
|
6
5
|
|
|
7
6
|
import {
|
|
@@ -17,33 +16,37 @@ import {
|
|
|
17
16
|
import * as Sentry from '@sentry/node';
|
|
18
17
|
import { WizardOptions } from '../../utils/types';
|
|
19
18
|
|
|
19
|
+
import * as childProcess from 'child_process';
|
|
20
|
+
|
|
20
21
|
type WizardFunction = (options: WizardOptions) => Promise<void>;
|
|
21
22
|
|
|
22
23
|
type FrameworkInfo = {
|
|
23
24
|
frameworkName: string;
|
|
24
|
-
frameworkSlug: string;
|
|
25
25
|
frameworkPackage: string;
|
|
26
|
+
troubleshootingDocsLink: string;
|
|
26
27
|
sourcemapsDocsLink: string;
|
|
27
28
|
wizard: WizardFunction;
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
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
32
|
'@sentry/sveltekit': {
|
|
40
33
|
frameworkName: 'SvelteKit',
|
|
41
|
-
frameworkSlug: 'sveltekit',
|
|
42
34
|
frameworkPackage: '@sveltejs/kit',
|
|
43
35
|
sourcemapsDocsLink:
|
|
44
36
|
'https://docs.sentry.io/platforms/javascript/guides/sveltekit/manual-setup/#configure-source-maps-upload',
|
|
37
|
+
troubleshootingDocsLink:
|
|
38
|
+
'https://docs.sentry.io/platforms/javascript/guides/sveltekit/sourcemaps/troubleshooting_js/',
|
|
45
39
|
wizard: runSvelteKitWizard,
|
|
46
40
|
},
|
|
41
|
+
'@sentry/react-native': {
|
|
42
|
+
frameworkName: 'React Native',
|
|
43
|
+
frameworkPackage: 'react-native',
|
|
44
|
+
sourcemapsDocsLink:
|
|
45
|
+
'https://docs.sentry.io/platforms/react-native/sourcemaps/',
|
|
46
|
+
troubleshootingDocsLink:
|
|
47
|
+
'https://docs.sentry.io/platforms/react-native/troubleshooting/#source-maps',
|
|
48
|
+
wizard: runReactNativeWizard,
|
|
49
|
+
},
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
export async function checkIfMoreSuitableWizardExistsAndAskForRedirect(): Promise<
|
|
@@ -91,7 +94,7 @@ async function checkIfMoreSuitableWizardExists(): Promise<string | undefined> {
|
|
|
91
94
|
async function askForRedirect(
|
|
92
95
|
sdkName: string,
|
|
93
96
|
): Promise<WizardFunction | undefined> {
|
|
94
|
-
const { frameworkName, sourcemapsDocsLink,
|
|
97
|
+
const { frameworkName, sourcemapsDocsLink, troubleshootingDocsLink, wizard } =
|
|
95
98
|
sdkMap[sdkName];
|
|
96
99
|
|
|
97
100
|
clack.log.warn(
|
|
@@ -108,7 +111,7 @@ Manual source maps configuration for ${frameworkName}:
|
|
|
108
111
|
${sourcemapsDocsLink}
|
|
109
112
|
|
|
110
113
|
Troubleshooting Source Maps:
|
|
111
|
-
|
|
114
|
+
${troubleshootingDocsLink}
|
|
112
115
|
`,
|
|
113
116
|
);
|
|
114
117
|
|
|
@@ -146,3 +149,19 @@ https://docs.sentry.io/platforms/javascript/guides/${frameworkSlug}/sourcemaps/t
|
|
|
146
149
|
return undefined;
|
|
147
150
|
}
|
|
148
151
|
}
|
|
152
|
+
|
|
153
|
+
function runReactNativeWizard(): Promise<void> {
|
|
154
|
+
const [runner, ...wizardArgs] = [...process.argv];
|
|
155
|
+
wizardArgs.push('--integration', 'reactNative');
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
childProcess.spawnSync(runner, wizardArgs, {
|
|
159
|
+
cwd: process.cwd(),
|
|
160
|
+
stdio: 'inherit',
|
|
161
|
+
});
|
|
162
|
+
} catch {
|
|
163
|
+
return Promise.reject();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return Promise.resolve();
|
|
167
|
+
}
|
|
@@ -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
|
-
|
|
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 =
|
|
40
|
-
const originalServerHooksFile =
|
|
49
|
+
const originalClientHooksFile = findScriptFile(clientHooksPath);
|
|
50
|
+
const originalServerHooksFile = findScriptFile(serverHooksPath);
|
|
41
51
|
|
|
42
|
-
const viteConfig =
|
|
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(
|
|
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(
|
|
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
|
-
|
|
401
|
-
|
|
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
|
-
|
|
438
|
+
async function showFallbackViteCopyPasteSnippet(
|
|
439
|
+
viteConfigPath: string,
|
|
440
|
+
codeSnippet: string,
|
|
441
|
+
) {
|
|
442
|
+
const viteConfigFilename = path.basename(viteConfigPath);
|
|
405
443
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
414
|
-
|
|
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
|
*/
|
|
@@ -3,6 +3,8 @@ import clack from '@clack/prompts';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
abort,
|
|
7
|
+
addSentryCliRc,
|
|
6
8
|
askForProjectSelection,
|
|
7
9
|
askForSelfHosted,
|
|
8
10
|
askForWizardLogin,
|
|
@@ -17,8 +19,6 @@ import { WizardOptions } from '../utils/types';
|
|
|
17
19
|
import { createExamplePage } from './sdk-example';
|
|
18
20
|
import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup';
|
|
19
21
|
|
|
20
|
-
import { setupCLIConfig } from './sentry-cli-setup';
|
|
21
|
-
|
|
22
22
|
export async function runSvelteKitWizard(
|
|
23
23
|
options: WizardOptions,
|
|
24
24
|
): Promise<void> {
|
|
@@ -47,14 +47,21 @@ export async function runSvelteKitWizard(
|
|
|
47
47
|
alreadyInstalled: hasPackageInstalled('@sentry/sveltekit', packageJson),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
await
|
|
51
|
-
|
|
52
|
-
const dsn = selectedProject.keys[0].dsn.public;
|
|
50
|
+
await addSentryCliRc(apiKeys.token);
|
|
53
51
|
|
|
54
52
|
const svelteConfig = await loadSvelteConfig();
|
|
55
53
|
|
|
56
54
|
try {
|
|
57
|
-
await createOrMergeSvelteKitFiles(
|
|
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
|
+
);
|
|
58
65
|
} catch (e: unknown) {
|
|
59
66
|
clack.log.error('Error while setting up the SvelteKit SDK:');
|
|
60
67
|
clack.log.info(
|
|
@@ -66,6 +73,7 @@ export async function runSvelteKitWizard(
|
|
|
66
73
|
: 'Unknown error',
|
|
67
74
|
),
|
|
68
75
|
);
|
|
76
|
+
await abort('Exiting Wizard');
|
|
69
77
|
return;
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -87,6 +95,7 @@ export async function runSvelteKitWizard(
|
|
|
87
95
|
: 'Unknown error',
|
|
88
96
|
),
|
|
89
97
|
);
|
|
98
|
+
await abort('Exiting Wizard');
|
|
90
99
|
return;
|
|
91
100
|
}
|
|
92
101
|
|
|
@@ -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
|
+
}
|
package/src/utils/bash.ts
CHANGED
|
@@ -3,42 +3,55 @@ import * as https from 'https';
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
|
|
5
5
|
export function hasSentryCLI(): boolean {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
try {
|
|
7
|
+
child_process.execSync('sentry-cli --version');
|
|
8
|
+
return true;
|
|
9
|
+
} catch (e) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export async function installSentryCLI(): Promise<void> {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
31
|
+
file.on('error', (err) => {
|
|
32
|
+
fs.unlinkSync('installcli.sh');
|
|
33
|
+
reject(err);
|
|
34
|
+
});
|
|
36
35
|
});
|
|
36
|
+
});
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
await httpAsync;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export function
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
export function executeSync(command: string): string {
|
|
42
|
+
const output = child_process.execSync(command);
|
|
43
|
+
return output.toString();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function execute(command: string): Promise<string> {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
child_process.exec(command, (error, stdout, _) => {
|
|
49
|
+
if (error) {
|
|
50
|
+
reject(error);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
resolve(stdout);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
44
57
|
}
|
package/src/utils/clack-utils.ts
CHANGED
|
@@ -11,6 +11,8 @@ import { promisify } from 'util';
|
|
|
11
11
|
import * as Sentry from '@sentry/node';
|
|
12
12
|
import { windowedSelect } from './vendor/clack-custom-select';
|
|
13
13
|
import { hasPackageInstalled, PackageDotJson } from './package-json';
|
|
14
|
+
import { SentryProjectData, WizardOptions } from './types';
|
|
15
|
+
import { traceStep } from '../telemetry';
|
|
14
16
|
|
|
15
17
|
const opn = require('opn') as (
|
|
16
18
|
url: string,
|
|
@@ -28,17 +30,6 @@ interface WizardProjectData {
|
|
|
28
30
|
projects: SentryProjectData[];
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
export interface SentryProjectData {
|
|
32
|
-
id: string;
|
|
33
|
-
slug: string;
|
|
34
|
-
name: string;
|
|
35
|
-
platform: string;
|
|
36
|
-
organization: {
|
|
37
|
-
slug: string;
|
|
38
|
-
};
|
|
39
|
-
keys: [{ dsn: { public: string } }];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
33
|
export async function abort(message?: string, status?: number): Promise<never> {
|
|
43
34
|
clack.outro(message ?? 'Wizard setup cancelled.');
|
|
44
35
|
const sentryHub = Sentry.getCurrentHub();
|
|
@@ -206,9 +197,7 @@ export async function askForWizardLogin(options: {
|
|
|
206
197
|
|
|
207
198
|
const loginSpinner = clack.spinner();
|
|
208
199
|
|
|
209
|
-
loginSpinner.start(
|
|
210
|
-
"Waiting for you to log in using the link above. Once you're logged in, return to this wizard.",
|
|
211
|
-
);
|
|
200
|
+
loginSpinner.start('Waiting for you to log in using the link above');
|
|
212
201
|
|
|
213
202
|
const data = await new Promise<WizardProjectData>((resolve) => {
|
|
214
203
|
const pollingInterval = setInterval(() => {
|
|
@@ -661,3 +650,42 @@ export function isUsingTypeScript() {
|
|
|
661
650
|
return false;
|
|
662
651
|
}
|
|
663
652
|
}
|
|
653
|
+
|
|
654
|
+
export async function getOrAskForProjectData(options: WizardOptions): Promise<{
|
|
655
|
+
sentryUrl: string;
|
|
656
|
+
selfHosted: boolean;
|
|
657
|
+
selectedProject: SentryProjectData;
|
|
658
|
+
authToken: string;
|
|
659
|
+
}> {
|
|
660
|
+
if (options.preSelectedProject) {
|
|
661
|
+
return {
|
|
662
|
+
selfHosted: options.preSelectedProject.selfHosted,
|
|
663
|
+
sentryUrl: options.url ?? SAAS_URL,
|
|
664
|
+
authToken: options.preSelectedProject.authToken,
|
|
665
|
+
selectedProject: options.preSelectedProject.project,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
const { url: sentryUrl, selfHosted } = await traceStep(
|
|
669
|
+
'ask-self-hosted',
|
|
670
|
+
() => askForSelfHosted(options.url),
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
const { projects, apiKeys } = await traceStep('login', () =>
|
|
674
|
+
askForWizardLogin({
|
|
675
|
+
promoCode: options.promoCode,
|
|
676
|
+
url: sentryUrl,
|
|
677
|
+
platform: 'javascript-nextjs',
|
|
678
|
+
}),
|
|
679
|
+
);
|
|
680
|
+
|
|
681
|
+
const selectedProject = await traceStep('select-project', () =>
|
|
682
|
+
askForProjectSelection(projects),
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
return {
|
|
686
|
+
sentryUrl,
|
|
687
|
+
selfHosted,
|
|
688
|
+
authToken: apiKeys.token,
|
|
689
|
+
selectedProject,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
@@ -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
|
+
}
|
package/src/utils/types.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
export interface SentryProjectData {
|
|
2
|
+
id: string;
|
|
3
|
+
slug: string;
|
|
4
|
+
name: string;
|
|
5
|
+
platform: string;
|
|
6
|
+
organization: {
|
|
7
|
+
slug: string;
|
|
8
|
+
};
|
|
9
|
+
keys: [{ dsn: { public: string } }];
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
export type WizardOptions = {
|
|
2
13
|
/**
|
|
3
14
|
* Controls whether the wizard should send telemetry data to Sentry.
|
|
@@ -15,4 +26,15 @@ export type WizardOptions = {
|
|
|
15
26
|
* This can be passed via the `-u` or `--url` arg.
|
|
16
27
|
*/
|
|
17
28
|
url?: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* If this is set, the wizard will skip the login and project selection step.
|
|
32
|
+
* (This can not yet be set externally but for example when redirecting from
|
|
33
|
+
* one wizard to another when the project was already selected)
|
|
34
|
+
*/
|
|
35
|
+
preSelectedProject?: {
|
|
36
|
+
project: SentryProjectData;
|
|
37
|
+
authToken: string;
|
|
38
|
+
selfHosted: boolean;
|
|
39
|
+
};
|
|
18
40
|
};
|
|
@@ -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
|
+
});
|