@sentry/wizard 3.12.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.
- package/CHANGELOG.md +13 -0
- package/dist/lib/Steps/ChooseIntegration.js +1 -0
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/android/android-wizard.js +6 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +5 -2
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/templates.d.ts +1 -1
- package/dist/src/nextjs/templates.js +2 -2
- package/dist/src/nextjs/templates.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +8 -4
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-setup.d.ts +5 -1
- package/dist/src/remix/sdk-setup.js +3 -2
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/sourcemaps/tools/sentry-cli.d.ts +9 -0
- package/dist/src/sourcemaps/tools/sentry-cli.js +26 -22
- package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
- package/dist/src/sourcemaps/tools/tsc.d.ts +6 -0
- package/dist/src/sourcemaps/tools/tsc.js +98 -17
- package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
- package/dist/src/sourcemaps/tools/vite.js +3 -13
- package/dist/src/sourcemaps/tools/vite.js.map +1 -1
- package/dist/src/sourcemaps/tools/webpack.js +3 -13
- package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.js +122 -48
- package/dist/src/sveltekit/sdk-setup.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.d.ts +1 -0
- package/dist/src/sveltekit/sveltekit-wizard.js +119 -44
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/sveltekit/utils.d.ts +2 -0
- package/dist/src/sveltekit/utils.js +48 -0
- package/dist/src/sveltekit/utils.js.map +1 -0
- package/dist/src/utils/ast-utils.d.ts +70 -0
- package/dist/src/utils/ast-utils.js +152 -1
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +38 -6
- package/dist/src/utils/clack-utils.js +57 -51
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/package-manager.d.ts +5 -0
- package/dist/src/utils/package-manager.js +11 -7
- package/dist/src/utils/package-manager.js.map +1 -1
- package/dist/test/sourcemaps/tools/sentry-cli.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/sentry-cli.test.js +112 -0
- package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -0
- package/dist/test/sourcemaps/tools/tsc.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/tsc.test.js +121 -0
- package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -0
- package/dist/test/utils/ast-utils.test.js +157 -26
- package/dist/test/utils/ast-utils.test.js.map +1 -1
- package/lib/Steps/ChooseIntegration.ts +1 -0
- package/package.json +1 -1
- package/src/android/android-wizard.ts +8 -5
- package/src/nextjs/nextjs-wizard.ts +15 -3
- package/src/nextjs/templates.ts +3 -2
- package/src/remix/remix-wizard.ts +8 -11
- package/src/remix/sdk-setup.ts +8 -2
- package/src/sourcemaps/tools/sentry-cli.ts +16 -9
- package/src/sourcemaps/tools/tsc.ts +133 -28
- package/src/sourcemaps/tools/vite.ts +15 -39
- package/src/sourcemaps/tools/webpack.ts +16 -39
- package/src/sveltekit/sdk-setup.ts +109 -37
- package/src/sveltekit/sveltekit-wizard.ts +93 -25
- package/src/sveltekit/utils.ts +50 -0
- package/src/utils/ast-utils.ts +180 -0
- package/src/utils/clack-utils.ts +68 -49
- package/src/utils/package-manager.ts +12 -6
- package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
- package/test/sourcemaps/tools/tsc.test.ts +181 -0
- package/test/utils/ast-utils.test.ts +233 -32
|
@@ -53,10 +53,8 @@ async function runRemixWizardWithTelemetry(
|
|
|
53
53
|
// We expect `@remix-run/dev` to be installed for every Remix project
|
|
54
54
|
await ensurePackageIsInstalled(packageJson, '@remix-run/dev', 'Remix');
|
|
55
55
|
|
|
56
|
-
const { selectedProject, authToken } =
|
|
57
|
-
options,
|
|
58
|
-
'javascript-remix',
|
|
59
|
-
);
|
|
56
|
+
const { selectedProject, authToken, sentryUrl } =
|
|
57
|
+
await getOrAskForProjectData(options, 'javascript-remix');
|
|
60
58
|
|
|
61
59
|
await traceStep('Install Sentry SDK', () =>
|
|
62
60
|
installPackage({
|
|
@@ -70,16 +68,15 @@ async function runRemixWizardWithTelemetry(
|
|
|
70
68
|
const isTS = isUsingTypeScript();
|
|
71
69
|
const isV2 = isRemixV2(remixConfig, packageJson);
|
|
72
70
|
|
|
73
|
-
await addSentryCliConfig(
|
|
74
|
-
authToken,
|
|
75
|
-
sourceMapsCliSetupConfig,
|
|
76
|
-
selectedProject.organization.slug,
|
|
77
|
-
selectedProject.name,
|
|
78
|
-
);
|
|
71
|
+
await addSentryCliConfig(authToken, sourceMapsCliSetupConfig);
|
|
79
72
|
|
|
80
73
|
await traceStep('Update build script for sourcemap uploads', async () => {
|
|
81
74
|
try {
|
|
82
|
-
await updateBuildScript(
|
|
75
|
+
await updateBuildScript({
|
|
76
|
+
org: selectedProject.organization.slug,
|
|
77
|
+
project: selectedProject.name,
|
|
78
|
+
url: sentryUrl,
|
|
79
|
+
});
|
|
83
80
|
} catch (e) {
|
|
84
81
|
clack.log
|
|
85
82
|
.warn(`Could not update build script to generate and upload sourcemaps.
|
package/src/remix/sdk-setup.ts
CHANGED
|
@@ -151,7 +151,11 @@ export async function instrumentRootRoute(
|
|
|
151
151
|
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
export async function updateBuildScript(
|
|
154
|
+
export async function updateBuildScript(args: {
|
|
155
|
+
org: string;
|
|
156
|
+
project: string;
|
|
157
|
+
url?: string;
|
|
158
|
+
}): Promise<void> {
|
|
155
159
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
156
160
|
// Add sourcemaps option to build script
|
|
157
161
|
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
@@ -166,7 +170,9 @@ export async function updateBuildScript(): Promise<void> {
|
|
|
166
170
|
|
|
167
171
|
if (!packageJson.scripts.build) {
|
|
168
172
|
packageJson.scripts.build =
|
|
169
|
-
|
|
173
|
+
`remix build --sourcemap && sentry-upload-sourcemaps --org ${args.org} --project ${args.project}` +
|
|
174
|
+
(args.url ? ` --url ${args.url}` : '');
|
|
175
|
+
|
|
170
176
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
171
177
|
} else if (packageJson.scripts.build.includes('remix build')) {
|
|
172
178
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
@@ -1,5 +1,5 @@
|
|
|
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
|
import * as Sentry from '@sentry/node';
|
|
5
5
|
import * as path from 'path';
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
import { SourceMapUploadToolConfigurationOptions } from './types';
|
|
15
15
|
import { hasPackageInstalled, PackageDotJson } from '../../utils/package-json';
|
|
16
16
|
import { traceStep } from '../../telemetry';
|
|
17
|
-
import { detectPackageManger } from '../../utils/package-manager';
|
|
17
|
+
import { detectPackageManger, NPM } from '../../utils/package-manager';
|
|
18
18
|
|
|
19
19
|
const SENTRY_NPM_SCRIPT_NAME = 'sentry:sourcemaps';
|
|
20
20
|
|
|
@@ -194,7 +194,7 @@ async function askShouldAddToBuildCommand(): Promise<boolean> {
|
|
|
194
194
|
*
|
|
195
195
|
* @param packageDotJson The package.json which will be modified.
|
|
196
196
|
*/
|
|
197
|
-
async function addSentryCommandToBuildCommand(
|
|
197
|
+
export async function addSentryCommandToBuildCommand(
|
|
198
198
|
packageDotJson: PackageDotJson,
|
|
199
199
|
): Promise<void> {
|
|
200
200
|
// This usually shouldn't happen because earlier we added the
|
|
@@ -205,8 +205,7 @@ async function addSentryCommandToBuildCommand(
|
|
|
205
205
|
(s) => s !== SENTRY_NPM_SCRIPT_NAME,
|
|
206
206
|
);
|
|
207
207
|
|
|
208
|
-
const packageManager = detectPackageManger();
|
|
209
|
-
const packageManagerName = packageManager?.name ?? 'npm';
|
|
208
|
+
const packageManager = detectPackageManger() ?? NPM;
|
|
210
209
|
|
|
211
210
|
// Heuristic to pre-select the build command:
|
|
212
211
|
// Often, 'build' is the prod build command, so we favour it.
|
|
@@ -221,7 +220,7 @@ async function addSentryCommandToBuildCommand(
|
|
|
221
220
|
(await abortIfCancelled(
|
|
222
221
|
clack.confirm({
|
|
223
222
|
message: `Is ${chalk.cyan(
|
|
224
|
-
`${
|
|
223
|
+
`${packageManager.runScriptCommand} ${buildCommand}`,
|
|
225
224
|
)} your production build command?`,
|
|
226
225
|
}),
|
|
227
226
|
));
|
|
@@ -229,7 +228,7 @@ async function addSentryCommandToBuildCommand(
|
|
|
229
228
|
if (allNpmScripts.length && (!buildCommand || !isProdBuildCommand)) {
|
|
230
229
|
buildCommand = await abortIfCancelled(
|
|
231
230
|
clack.select({
|
|
232
|
-
message: `Which ${
|
|
231
|
+
message: `Which ${packageManager.name} command in your ${chalk.cyan(
|
|
233
232
|
'package.json',
|
|
234
233
|
)} builds your application for production?`,
|
|
235
234
|
options: allNpmScripts
|
|
@@ -252,10 +251,18 @@ Please add it manually to your prod build command.`,
|
|
|
252
251
|
return;
|
|
253
252
|
}
|
|
254
253
|
|
|
254
|
+
const oldCommand = packageDotJson.scripts[buildCommand];
|
|
255
|
+
if (!oldCommand) {
|
|
256
|
+
// very unlikely to happen but nevertheless
|
|
257
|
+
clack.log.warn(
|
|
258
|
+
`\`${buildCommand}\` doesn't seem to be part of your package.json scripts`,
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
255
263
|
packageDotJson.scripts[
|
|
256
264
|
buildCommand
|
|
257
|
-
|
|
258
|
-
] = `${packageDotJson.scripts[buildCommand]} && ${packageManager} run ${SENTRY_NPM_SCRIPT_NAME}`;
|
|
265
|
+
] = `${oldCommand} && ${packageManager.runScriptCommand} ${SENTRY_NPM_SCRIPT_NAME}`;
|
|
259
266
|
|
|
260
267
|
await fs.promises.writeFile(
|
|
261
268
|
path.join(process.cwd(), 'package.json'),
|
|
@@ -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
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
|
29
|
-
|
|
29
|
+
const getCodeSnippet = (colors: boolean) =>
|
|
30
|
+
makeCodeSnippet(colors, (unchanged, plus, _) =>
|
|
31
|
+
unchanged(
|
|
32
|
+
`{
|
|
30
33
|
"compilerOptions": {
|
|
31
|
-
${
|
|
32
|
-
${
|
|
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
|
-
${
|
|
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
|
-
`);
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
createNewConfigFile,
|
|
20
20
|
getPackageDotJson,
|
|
21
21
|
installPackage,
|
|
22
|
+
makeCodeSnippet,
|
|
22
23
|
showCopyPasteInstructions,
|
|
23
24
|
} from '../../utils/clack-utils';
|
|
24
25
|
import { hasPackageInstalled } from '../../utils/package-json';
|
|
@@ -36,52 +37,27 @@ import { debug } from '../../utils/debug';
|
|
|
36
37
|
const getViteConfigSnippet = (
|
|
37
38
|
options: SourceMapUploadToolConfigurationOptions,
|
|
38
39
|
colors: boolean,
|
|
39
|
-
) =>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'sourcemap: true, // Source map generation must be turned on';
|
|
44
|
-
const rawSentryVitePluginFunction = `sentryVitePlugin({
|
|
45
|
-
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
46
|
-
org: "${options.orgSlug}",
|
|
47
|
-
project: "${options.projectSlug}",${
|
|
48
|
-
options.selfHosted ? `\n url: "${options.url}",` : ''
|
|
49
|
-
}
|
|
50
|
-
}),`;
|
|
51
|
-
|
|
52
|
-
const importStmt = colors ? chalk.greenBright(rawImportStmt) : rawImportStmt;
|
|
53
|
-
const generateSourceMapsOption = colors
|
|
54
|
-
? chalk.greenBright(rawGenerateSourceMapsOption)
|
|
55
|
-
: rawGenerateSourceMapsOption;
|
|
56
|
-
const sentryVitePluginFunction = colors
|
|
57
|
-
? chalk.greenBright(rawSentryVitePluginFunction)
|
|
58
|
-
: rawSentryVitePluginFunction;
|
|
59
|
-
|
|
60
|
-
const code = getViteConfigContent(
|
|
61
|
-
importStmt,
|
|
62
|
-
generateSourceMapsOption,
|
|
63
|
-
sentryVitePluginFunction,
|
|
64
|
-
);
|
|
65
|
-
return colors ? chalk.gray(code) : code;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const getViteConfigContent = (
|
|
69
|
-
importStmt: string,
|
|
70
|
-
generateSourceMapsOption: string,
|
|
71
|
-
sentryVitePluginFunction: string,
|
|
72
|
-
) => `import { defineConfig } from "vite";
|
|
73
|
-
${importStmt}
|
|
40
|
+
) =>
|
|
41
|
+
makeCodeSnippet(colors, (unchanged, plus, _) =>
|
|
42
|
+
unchanged(`import { defineConfig } from "vite";
|
|
43
|
+
${plus('import { sentryVitePlugin } from "@sentry/vite-plugin";')}
|
|
74
44
|
|
|
75
45
|
export default defineConfig({
|
|
76
46
|
build: {
|
|
77
|
-
${
|
|
47
|
+
${plus('sourcemap: true, // Source map generation must be turned on')}
|
|
78
48
|
},
|
|
79
49
|
plugins: [
|
|
80
50
|
// Put the Sentry vite plugin after all other plugins
|
|
81
|
-
${
|
|
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
|
+
}),`)}
|
|
82
58
|
],
|
|
83
|
-
})
|
|
84
|
-
|
|
59
|
+
});`),
|
|
60
|
+
);
|
|
85
61
|
|
|
86
62
|
export const configureVitePlugin: SourceMapUploadToolConfigurationFunction =
|
|
87
63
|
async (options) => {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
createNewConfigFile,
|
|
19
19
|
getPackageDotJson,
|
|
20
20
|
installPackage,
|
|
21
|
+
makeCodeSnippet,
|
|
21
22
|
showCopyPasteInstructions,
|
|
22
23
|
} from '../../utils/clack-utils';
|
|
23
24
|
import { hasPackageInstalled } from '../../utils/package-json';
|
|
@@ -33,51 +34,27 @@ import { debug } from '../../utils/debug';
|
|
|
33
34
|
const getCodeSnippet = (
|
|
34
35
|
options: SourceMapUploadToolConfigurationOptions,
|
|
35
36
|
colors: boolean,
|
|
36
|
-
) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const rawSentryWebpackPluginFunction = `sentryWebpackPlugin({
|
|
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 sentryWebpackPluginFunction = colors
|
|
54
|
-
? chalk.greenBright(rawSentryWebpackPluginFunction)
|
|
55
|
-
: rawSentryWebpackPluginFunction;
|
|
56
|
-
|
|
57
|
-
const code = getWebpackConfigContent(
|
|
58
|
-
importStmt,
|
|
59
|
-
generateSourceMapsOption,
|
|
60
|
-
sentryWebpackPluginFunction,
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return colors ? chalk.gray(code) : code;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const getWebpackConfigContent = (
|
|
67
|
-
importStmt: string,
|
|
68
|
-
generateSourceMapsOption: string,
|
|
69
|
-
sentryWebpackPluginFunction: string,
|
|
70
|
-
) => `${importStmt}
|
|
37
|
+
) =>
|
|
38
|
+
makeCodeSnippet(colors, (unchanged, plus) =>
|
|
39
|
+
unchanged(`${plus(
|
|
40
|
+
'const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");',
|
|
41
|
+
)}
|
|
71
42
|
|
|
72
43
|
module.exports = {
|
|
73
44
|
// ... other options
|
|
74
|
-
${
|
|
45
|
+
${plus('devtool: "source-map", // Source map generation must be turned on')}
|
|
75
46
|
plugins: [
|
|
76
47
|
// Put the Sentry Webpack plugin after all other plugins
|
|
77
|
-
${
|
|
48
|
+
${plus(`sentryWebpackPlugin({
|
|
49
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
50
|
+
org: "${options.orgSlug}",
|
|
51
|
+
project: "${options.projectSlug}",${
|
|
52
|
+
options.selfHosted ? `\n url: "${options.url}",` : ''
|
|
53
|
+
}
|
|
54
|
+
}),`)}
|
|
78
55
|
],
|
|
79
|
-
}
|
|
80
|
-
|
|
56
|
+
}`),
|
|
57
|
+
);
|
|
81
58
|
|
|
82
59
|
export const configureWebPackPlugin: SourceMapUploadToolConfigurationFunction =
|
|
83
60
|
async (options) => {
|
|
@@ -4,6 +4,8 @@ import * as path from 'path';
|
|
|
4
4
|
import * as url from 'url';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
|
|
7
|
+
import * as Sentry from '@sentry/node';
|
|
8
|
+
|
|
7
9
|
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
8
10
|
import clack from '@clack/prompts';
|
|
9
11
|
// @ts-ignore - magicast is ESM and TS complains about that. It works though
|
|
@@ -20,6 +22,7 @@ import { findFile, hasSentryContent } from '../utils/ast-utils';
|
|
|
20
22
|
import * as recast from 'recast';
|
|
21
23
|
import x = recast.types;
|
|
22
24
|
import t = x.namedTypes;
|
|
25
|
+
import { traceStep } from '../telemetry';
|
|
23
26
|
|
|
24
27
|
const SVELTE_CONFIG_FILE = 'svelte.config.js';
|
|
25
28
|
|
|
@@ -59,19 +62,25 @@ export async function createOrMergeSvelteKitFiles(
|
|
|
59
62
|
|
|
60
63
|
const { dsn } = projectInfo;
|
|
61
64
|
|
|
65
|
+
Sentry.setTag(
|
|
66
|
+
'client-hooks-file-strategy',
|
|
67
|
+
originalClientHooksFile ? 'merge' : 'create',
|
|
68
|
+
);
|
|
62
69
|
if (!originalClientHooksFile) {
|
|
63
70
|
clack.log.info('No client hooks file found, creating a new one.');
|
|
64
71
|
await createNewHooksFile(`${clientHooksPath}.${fileEnding}`, 'client', dsn);
|
|
72
|
+
} else {
|
|
73
|
+
await mergeHooksFile(originalClientHooksFile, 'client', dsn);
|
|
65
74
|
}
|
|
75
|
+
|
|
76
|
+
Sentry.setTag(
|
|
77
|
+
'server-hooks-file-strategy',
|
|
78
|
+
originalServerHooksFile ? 'merge' : 'create',
|
|
79
|
+
);
|
|
66
80
|
if (!originalServerHooksFile) {
|
|
67
81
|
clack.log.info('No server hooks file found, creating a new one.');
|
|
68
82
|
await createNewHooksFile(`${serverHooksPath}.${fileEnding}`, 'server', dsn);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (originalClientHooksFile) {
|
|
72
|
-
await mergeHooksFile(originalClientHooksFile, 'client', dsn);
|
|
73
|
-
}
|
|
74
|
-
if (originalServerHooksFile) {
|
|
83
|
+
} else {
|
|
75
84
|
await mergeHooksFile(originalServerHooksFile, 'server', dsn);
|
|
76
85
|
}
|
|
77
86
|
|
|
@@ -124,6 +133,7 @@ async function createNewHooksFile(
|
|
|
124
133
|
await fs.promises.writeFile(hooksFileDest, filledTemplate);
|
|
125
134
|
|
|
126
135
|
clack.log.success(`Created ${hooksFileDest}`);
|
|
136
|
+
Sentry.setTag(`created-${hooktype}-hooks`, 'success');
|
|
127
137
|
}
|
|
128
138
|
|
|
129
139
|
/**
|
|
@@ -143,6 +153,9 @@ async function mergeHooksFile(
|
|
|
143
153
|
dsn: string,
|
|
144
154
|
): Promise<void> {
|
|
145
155
|
const originalHooksMod = await loadFile(hooksFile);
|
|
156
|
+
|
|
157
|
+
const file: 'server-hooks' | 'client-hooks' = `${hookType}-hooks`;
|
|
158
|
+
|
|
146
159
|
if (hasSentryContent(originalHooksMod.$ast as t.Program)) {
|
|
147
160
|
// We don't want to mess with files that already have Sentry content.
|
|
148
161
|
// Let's just bail out at this point.
|
|
@@ -152,32 +165,59 @@ async function mergeHooksFile(
|
|
|
152
165
|
)} already contains Sentry code.
|
|
153
166
|
Skipping adding Sentry functionality to.`,
|
|
154
167
|
);
|
|
168
|
+
Sentry.setTag(`modified-${file}`, 'fail');
|
|
169
|
+
Sentry.setTag(`${file}-fail-reason`, 'has-sentry-content');
|
|
155
170
|
return;
|
|
156
171
|
}
|
|
157
172
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
173
|
+
await modifyAndRecordFail(
|
|
174
|
+
() =>
|
|
175
|
+
originalHooksMod.imports.$add({
|
|
176
|
+
from: '@sentry/sveltekit',
|
|
177
|
+
imported: '*',
|
|
178
|
+
local: 'Sentry',
|
|
179
|
+
}),
|
|
180
|
+
'import-injection',
|
|
181
|
+
file,
|
|
182
|
+
);
|
|
163
183
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
184
|
+
await modifyAndRecordFail(
|
|
185
|
+
() => {
|
|
186
|
+
if (hookType === 'client') {
|
|
187
|
+
insertClientInitCall(dsn, originalHooksMod);
|
|
188
|
+
} else {
|
|
189
|
+
insertServerInitCall(dsn, originalHooksMod);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
'init-call-injection',
|
|
193
|
+
file,
|
|
194
|
+
);
|
|
169
195
|
|
|
170
|
-
|
|
196
|
+
await modifyAndRecordFail(
|
|
197
|
+
() => wrapHandleError(originalHooksMod),
|
|
198
|
+
'wrap-handle-error',
|
|
199
|
+
file,
|
|
200
|
+
);
|
|
171
201
|
|
|
172
202
|
if (hookType === 'server') {
|
|
173
|
-
|
|
203
|
+
await modifyAndRecordFail(
|
|
204
|
+
() => wrapHandle(originalHooksMod),
|
|
205
|
+
'wrap-handle',
|
|
206
|
+
'server-hooks',
|
|
207
|
+
);
|
|
174
208
|
}
|
|
175
209
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
210
|
+
await modifyAndRecordFail(
|
|
211
|
+
async () => {
|
|
212
|
+
const modifiedCode = originalHooksMod.generate().code;
|
|
213
|
+
await fs.promises.writeFile(hooksFile, modifiedCode);
|
|
214
|
+
},
|
|
215
|
+
'write-file',
|
|
216
|
+
file,
|
|
217
|
+
);
|
|
179
218
|
|
|
180
219
|
clack.log.success(`Added Sentry code to ${hooksFile}`);
|
|
220
|
+
Sentry.setTag(`modified-${hookType}-hooks`, 'success');
|
|
181
221
|
}
|
|
182
222
|
|
|
183
223
|
function insertClientInitCall(
|
|
@@ -410,32 +450,45 @@ async function modifyViteConfig(
|
|
|
410
450
|
)} already contains Sentry code.
|
|
411
451
|
Skipping adding Sentry functionality to.`,
|
|
412
452
|
);
|
|
453
|
+
Sentry.setTag(`modified-vite-cfg`, 'fail');
|
|
454
|
+
Sentry.setTag(`vite-cfg-fail-reason`, 'has-sentry-content');
|
|
413
455
|
return;
|
|
414
456
|
}
|
|
415
457
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
458
|
+
await modifyAndRecordFail(
|
|
459
|
+
() =>
|
|
460
|
+
addVitePlugin(viteModule, {
|
|
461
|
+
imported: 'sentrySvelteKit',
|
|
462
|
+
from: '@sentry/sveltekit',
|
|
463
|
+
constructor: 'sentrySvelteKit',
|
|
464
|
+
options: {
|
|
465
|
+
sourceMapsUploadOptions: {
|
|
466
|
+
org,
|
|
467
|
+
project,
|
|
468
|
+
...(selfHosted && { url }),
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
index: 0,
|
|
472
|
+
}),
|
|
473
|
+
'add-vite-plugin',
|
|
474
|
+
'vite-cfg',
|
|
475
|
+
);
|
|
431
476
|
|
|
432
|
-
await
|
|
477
|
+
await modifyAndRecordFail(
|
|
478
|
+
async () => {
|
|
479
|
+
const code = generateCode(viteModule.$ast).code;
|
|
480
|
+
await fs.promises.writeFile(viteConfigPath, code);
|
|
481
|
+
},
|
|
482
|
+
'write-file',
|
|
483
|
+
'vite-cfg',
|
|
484
|
+
);
|
|
433
485
|
} catch (e) {
|
|
434
486
|
debug(e);
|
|
435
487
|
await showFallbackViteCopyPasteSnippet(
|
|
436
488
|
viteConfigPath,
|
|
437
489
|
getViteConfigCodeSnippet(org, project, selfHosted, url),
|
|
438
490
|
);
|
|
491
|
+
Sentry.captureException('Sveltekit Vite Config Modification Fail');
|
|
439
492
|
}
|
|
440
493
|
}
|
|
441
494
|
|
|
@@ -512,3 +565,22 @@ function getInitCallInsertionIndex(originalHooksModAST: Program): number {
|
|
|
512
565
|
: 0;
|
|
513
566
|
return initCallInsertionIndex;
|
|
514
567
|
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Applies the @param modifyCallback and records Sentry tags if the call failed.
|
|
571
|
+
* In case of a failure, a tag is set with @param reason as a fail reason
|
|
572
|
+
* and the error is rethrown.
|
|
573
|
+
*/
|
|
574
|
+
async function modifyAndRecordFail<T>(
|
|
575
|
+
modifyCallback: () => T | Promise<T>,
|
|
576
|
+
reason: string,
|
|
577
|
+
fileType: 'server-hooks' | 'client-hooks' | 'vite-cfg',
|
|
578
|
+
): Promise<void> {
|
|
579
|
+
try {
|
|
580
|
+
await traceStep(`${fileType}-${reason}`, modifyCallback);
|
|
581
|
+
} catch (e) {
|
|
582
|
+
Sentry.setTag(`modified-${fileType}`, 'fail');
|
|
583
|
+
Sentry.setTag(`${fileType}-mod-fail-reason`, reason);
|
|
584
|
+
throw e;
|
|
585
|
+
}
|
|
586
|
+
}
|