@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.
- package/CHANGELOG.md +20 -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 +14 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/android/code-tools.d.ts +8 -0
- package/dist/src/android/code-tools.js +20 -8
- package/dist/src/android/code-tools.js.map +1 -1
- package/dist/src/android/gradle.js +6 -1
- package/dist/src/android/gradle.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 +39 -124
- package/dist/src/sourcemaps/tools/vite.js.map +1 -1
- package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
- package/dist/src/sourcemaps/tools/webpack.js +280 -25
- package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.js +123 -49
- 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 +77 -3
- package/dist/src/utils/ast-utils.js +172 -6
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +85 -1
- package/dist/src/utils/clack-utils.js +214 -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/android/code-tools.test.d.ts +1 -0
- package/dist/test/android/code-tools.test.js +34 -0
- package/dist/test/android/code-tools.test.js.map +1 -0
- 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/sourcemaps/tools/webpack.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
- package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
- package/dist/test/utils/ast-utils.test.js +181 -15
- package/dist/test/utils/ast-utils.test.js.map +1 -1
- package/dist/test/utils/clack-utils.test.d.ts +1 -0
- package/dist/test/utils/clack-utils.test.js +200 -0
- package/dist/test/utils/clack-utils.test.js.map +1 -0
- package/lib/Steps/ChooseIntegration.ts +1 -0
- package/package.json +1 -1
- package/src/android/android-wizard.ts +16 -5
- package/src/android/code-tools.ts +21 -7
- package/src/android/gradle.ts +6 -1
- 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 +37 -127
- package/src/sourcemaps/tools/webpack.ts +343 -27
- package/src/sveltekit/sdk-setup.ts +115 -39
- package/src/sveltekit/sveltekit-wizard.ts +93 -25
- package/src/sveltekit/utils.ts +50 -0
- package/src/utils/ast-utils.ts +203 -7
- package/src/utils/clack-utils.ts +211 -44
- package/src/utils/package-manager.ts +12 -6
- package/test/android/code-tools.test.ts +49 -0
- package/test/sourcemaps/tools/sentry-cli.test.ts +51 -0
- package/test/sourcemaps/tools/tsc.test.ts +181 -0
- package/test/sourcemaps/tools/webpack.test.ts +303 -0
- package/test/utils/ast-utils.test.ts +240 -20
- package/test/utils/clack-utils.test.ts +142 -0
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
|
|
1
4
|
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
2
|
-
import
|
|
5
|
+
import * as clack from '@clack/prompts';
|
|
3
6
|
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
import * as recast from 'recast';
|
|
9
|
+
import x = recast.types;
|
|
10
|
+
import t = x.namedTypes;
|
|
11
|
+
|
|
12
|
+
import * as Sentry from '@sentry/node';
|
|
13
|
+
|
|
4
14
|
import {
|
|
5
15
|
abortIfCancelled,
|
|
6
16
|
addDotEnvSentryBuildPluginFile,
|
|
17
|
+
askForToolConfigPath,
|
|
18
|
+
createNewConfigFile,
|
|
7
19
|
getPackageDotJson,
|
|
8
20
|
installPackage,
|
|
21
|
+
makeCodeSnippet,
|
|
22
|
+
showCopyPasteInstructions,
|
|
9
23
|
} from '../../utils/clack-utils';
|
|
10
24
|
import { hasPackageInstalled } from '../../utils/package-json';
|
|
11
25
|
|
|
@@ -14,28 +28,33 @@ import {
|
|
|
14
28
|
SourceMapUploadToolConfigurationOptions,
|
|
15
29
|
} from './types';
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
import { findFile, hasSentryContent } from '../../utils/ast-utils';
|
|
32
|
+
import { debug } from '../../utils/debug';
|
|
33
|
+
|
|
34
|
+
const getCodeSnippet = (
|
|
35
|
+
options: SourceMapUploadToolConfigurationOptions,
|
|
36
|
+
colors: boolean,
|
|
37
|
+
) =>
|
|
38
|
+
makeCodeSnippet(colors, (unchanged, plus) =>
|
|
39
|
+
unchanged(`${plus(
|
|
40
|
+
'const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");',
|
|
41
|
+
)}
|
|
22
42
|
|
|
23
43
|
module.exports = {
|
|
24
|
-
// ... other
|
|
25
|
-
${
|
|
26
|
-
'devtool: "source-map", // Source map generation must be turned on',
|
|
27
|
-
)}
|
|
44
|
+
// ... other options
|
|
45
|
+
${plus('devtool: "source-map", // Source map generation must be turned on')}
|
|
28
46
|
plugins: [
|
|
29
|
-
|
|
47
|
+
// Put the Sentry Webpack plugin after all other plugins
|
|
48
|
+
${plus(`sentryWebpackPlugin({
|
|
30
49
|
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
31
50
|
org: "${options.orgSlug}",
|
|
32
51
|
project: "${options.projectSlug}",${
|
|
33
52
|
options.selfHosted ? `\n url: "${options.url}",` : ''
|
|
34
|
-
}
|
|
35
|
-
})
|
|
53
|
+
}
|
|
54
|
+
}),`)}
|
|
36
55
|
],
|
|
37
|
-
}
|
|
38
|
-
|
|
56
|
+
}`),
|
|
57
|
+
);
|
|
39
58
|
|
|
40
59
|
export const configureWebPackPlugin: SourceMapUploadToolConfigurationFunction =
|
|
41
60
|
async (options) => {
|
|
@@ -47,21 +66,318 @@ export const configureWebPackPlugin: SourceMapUploadToolConfigurationFunction =
|
|
|
47
66
|
),
|
|
48
67
|
});
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
const webpackConfigPath =
|
|
70
|
+
findFile(path.resolve(process.cwd(), 'webpack.config')) ??
|
|
71
|
+
(await askForToolConfigPath('Webpack', 'webpack.config.js'));
|
|
72
|
+
|
|
73
|
+
let successfullyAdded = false;
|
|
74
|
+
if (webpackConfigPath) {
|
|
75
|
+
successfullyAdded = await modifyWebpackConfig(webpackConfigPath, options);
|
|
76
|
+
} else {
|
|
77
|
+
successfullyAdded = await createNewConfigFile(
|
|
78
|
+
path.join(process.cwd(), 'webpack.config.js'),
|
|
79
|
+
getCodeSnippet(options, false),
|
|
80
|
+
'More information about Webpack configs: https://vitejs.dev/config/',
|
|
81
|
+
);
|
|
82
|
+
Sentry.setTag(
|
|
83
|
+
'created-new-config',
|
|
84
|
+
successfullyAdded ? 'success' : 'fail',
|
|
85
|
+
);
|
|
86
|
+
}
|
|
53
87
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
if (successfullyAdded) {
|
|
89
|
+
clack.log.info(
|
|
90
|
+
`We recommend checking the ${
|
|
91
|
+
webpackConfigPath ? 'modified' : 'added'
|
|
92
|
+
} file after the wizard finished to ensure it works with your build setup.`,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
Sentry.setTag('ast-mod', 'success');
|
|
96
|
+
} else {
|
|
97
|
+
Sentry.setTag('ast-mod', 'fail');
|
|
98
|
+
await showCopyPasteInstructions(
|
|
99
|
+
path.basename(webpackConfigPath || 'webpack.config.js'),
|
|
100
|
+
getCodeSnippet(options, true),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await addDotEnvSentryBuildPluginFile(options.authToken);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Modifies a webpack config file to enable source map generation and add the Sentry webpack plugin
|
|
109
|
+
* exported only for testing
|
|
110
|
+
*/
|
|
111
|
+
export async function modifyWebpackConfig(
|
|
112
|
+
webpackConfigPath: string,
|
|
113
|
+
options: SourceMapUploadToolConfigurationOptions,
|
|
114
|
+
): Promise<boolean> {
|
|
115
|
+
try {
|
|
116
|
+
const webpackConfig = await fs.promises.readFile(webpackConfigPath, {
|
|
117
|
+
encoding: 'utf-8',
|
|
118
|
+
});
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
120
|
+
const prettyConfigFilename = chalk.cyan(path.basename(webpackConfigPath));
|
|
121
|
+
|
|
122
|
+
// no idea why recast returns any here, this is dumb :/
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
124
|
+
const program = recast.parse(webpackConfig.toString()).program as t.Program;
|
|
125
|
+
|
|
126
|
+
if (!(await shouldModifyWebpackConfig(program, prettyConfigFilename))) {
|
|
127
|
+
// Sentry tag is set in shouldModifyWebpackConfig
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const exportStmt = getCjsModuleExports(program);
|
|
132
|
+
if (!exportStmt) {
|
|
133
|
+
// We only care about CJS at the moment since it's probably the most widely used format for webpack configs.
|
|
134
|
+
debug(`Could not find module.exports = {...} in ${webpackConfigPath}.`);
|
|
135
|
+
Sentry.setTag('ast-mod-fail-reason', 'config-object-not-found');
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const configObject = getWebpackConfigObject(exportStmt, program);
|
|
140
|
+
|
|
141
|
+
if (!configObject) {
|
|
142
|
+
debug(`Couldn't find config object in ${webpackConfigPath}`);
|
|
143
|
+
Sentry.setTag('ast-mod-fail-reason', 'config-object-not-found');
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const enabledSourcemaps = enableSourcemapsGeneration(configObject);
|
|
148
|
+
|
|
149
|
+
if (enabledSourcemaps) {
|
|
150
|
+
clack.log.success(
|
|
151
|
+
`Enabled source map generation in ${prettyConfigFilename}.`,
|
|
152
|
+
);
|
|
153
|
+
} else {
|
|
154
|
+
clack.log.warn(
|
|
155
|
+
`Couldn't enable source maps generation in ${prettyConfigFilename} Please follow the instructions below.`,
|
|
156
|
+
);
|
|
157
|
+
Sentry.setTag('ast-mod-fail-reason', 'insertion-fail');
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const addedPlugin = addSentryWebpackPlugin(program, configObject, options);
|
|
162
|
+
if (addedPlugin) {
|
|
163
|
+
clack.log.success(
|
|
164
|
+
`Added Sentry webpack plugin to ${prettyConfigFilename}.`,
|
|
165
|
+
);
|
|
166
|
+
} else {
|
|
167
|
+
clack.log.warn(
|
|
168
|
+
`Couldn't add Sentry webpack plugin to ${prettyConfigFilename}. Please follow the instructions below.`,
|
|
169
|
+
);
|
|
170
|
+
Sentry.setTag('ast-mod-fail-reason', 'insertion-fail');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const code = recast.print(program).code;
|
|
175
|
+
await fs.promises.writeFile(webpackConfigPath, code);
|
|
176
|
+
|
|
177
|
+
return true;
|
|
178
|
+
} catch (e) {
|
|
179
|
+
Sentry.setTag('ast-mod-fail-reason', 'insertion-fail');
|
|
180
|
+
debug(e);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function shouldModifyWebpackConfig(
|
|
186
|
+
program: t.Program,
|
|
187
|
+
prettyConfigFilename: string,
|
|
188
|
+
) {
|
|
189
|
+
if (hasSentryContent(program)) {
|
|
190
|
+
const shouldContinue = await abortIfCancelled(
|
|
191
|
+
clack.select({
|
|
192
|
+
message: `Seems like ${prettyConfigFilename} already contains Sentry-related code. Should the wizard modify it anyway?`,
|
|
193
|
+
options: [
|
|
194
|
+
{
|
|
195
|
+
label: 'Yes, add the Sentry Webpack plugin',
|
|
196
|
+
value: true,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
label: 'No, show me instructions to manually add the plugin',
|
|
200
|
+
value: false,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
62
203
|
initialValue: true,
|
|
63
204
|
}),
|
|
64
205
|
);
|
|
65
206
|
|
|
66
|
-
|
|
67
|
-
|
|
207
|
+
if (!shouldContinue) {
|
|
208
|
+
Sentry.setTag('ast-mod-fail-reason', 'has-sentry-content');
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function addSentryWebpackPlugin(
|
|
217
|
+
program: t.Program,
|
|
218
|
+
configObj: t.ObjectExpression,
|
|
219
|
+
options: SourceMapUploadToolConfigurationOptions,
|
|
220
|
+
) {
|
|
221
|
+
const b = addSentryWebpackPluginImport(program);
|
|
222
|
+
|
|
223
|
+
const sentryPluginCall = b.callExpression(
|
|
224
|
+
b.identifier('sentryWebpackPlugin'),
|
|
225
|
+
[
|
|
226
|
+
b.objectExpression([
|
|
227
|
+
b.objectProperty(
|
|
228
|
+
b.identifier('authToken'),
|
|
229
|
+
b.identifier('process.env.SENTRY_AUTH_TOKEN'),
|
|
230
|
+
),
|
|
231
|
+
b.objectProperty(b.identifier('org'), b.stringLiteral(options.orgSlug)),
|
|
232
|
+
b.objectProperty(
|
|
233
|
+
b.identifier('project'),
|
|
234
|
+
b.stringLiteral(options.projectSlug),
|
|
235
|
+
),
|
|
236
|
+
...(options.selfHosted
|
|
237
|
+
? [
|
|
238
|
+
b.objectProperty(
|
|
239
|
+
b.identifier('url'),
|
|
240
|
+
b.stringLiteral(options.url),
|
|
241
|
+
),
|
|
242
|
+
]
|
|
243
|
+
: []),
|
|
244
|
+
]),
|
|
245
|
+
],
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const pluginsProp = configObj.properties.find(
|
|
249
|
+
(p): p is t.Property =>
|
|
250
|
+
p.type === 'Property' &&
|
|
251
|
+
p.key.type === 'Identifier' &&
|
|
252
|
+
p.key.name === 'plugins',
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
if (pluginsProp) {
|
|
256
|
+
if (pluginsProp.value.type === 'ArrayExpression') {
|
|
257
|
+
pluginsProp.value.elements.push(sentryPluginCall);
|
|
258
|
+
} else {
|
|
259
|
+
pluginsProp.value = b.arrayExpression([sentryPluginCall]);
|
|
260
|
+
}
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
configObj.properties.push(
|
|
265
|
+
b.objectProperty(
|
|
266
|
+
b.identifier('plugins'),
|
|
267
|
+
b.arrayExpression([sentryPluginCall]),
|
|
268
|
+
),
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function addSentryWebpackPluginImport(program: t.Program) {
|
|
275
|
+
const b = recast.types.builders;
|
|
276
|
+
|
|
277
|
+
const sentryPluginRequireStmt = b.variableDeclaration('const', [
|
|
278
|
+
b.variableDeclarator(
|
|
279
|
+
b.objectPattern([
|
|
280
|
+
b.objectProperty.from({
|
|
281
|
+
key: b.identifier('sentryWebpackPlugin'),
|
|
282
|
+
value: b.identifier('sentryWebpackPlugin'),
|
|
283
|
+
shorthand: true,
|
|
284
|
+
}),
|
|
285
|
+
]),
|
|
286
|
+
b.callExpression(b.identifier('require'), [
|
|
287
|
+
b.stringLiteral('@sentry/webpack-plugin'),
|
|
288
|
+
]),
|
|
289
|
+
),
|
|
290
|
+
]);
|
|
291
|
+
|
|
292
|
+
program.body.unshift(sentryPluginRequireStmt);
|
|
293
|
+
return b;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function enableSourcemapsGeneration(configObj: t.ObjectExpression): boolean {
|
|
297
|
+
const b = recast.types.builders;
|
|
298
|
+
|
|
299
|
+
const devtoolProp = configObj.properties.find(
|
|
300
|
+
(p): p is t.Property =>
|
|
301
|
+
p.type === 'Property' &&
|
|
302
|
+
p.key.type === 'Identifier' &&
|
|
303
|
+
p.key.name === 'devtool',
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
if (devtoolProp) {
|
|
307
|
+
// devtool can have quite a lot of source maps values.
|
|
308
|
+
// see: https://webpack.js.org/configuration/devtool/#devtool
|
|
309
|
+
// For Sentry to work best, we should set it to "source-map" or "hidden-source-map"
|
|
310
|
+
// Heuristic:
|
|
311
|
+
// - all values that contain "hidden" will be set to "hidden-source-map"
|
|
312
|
+
// - all other values will be set to "source-map"
|
|
313
|
+
if (
|
|
314
|
+
(devtoolProp.value.type === 'Literal' ||
|
|
315
|
+
devtoolProp.value.type === 'StringLiteral') &&
|
|
316
|
+
devtoolProp.value.value?.toString().startsWith('hidden-')
|
|
317
|
+
) {
|
|
318
|
+
devtoolProp.value = b.stringLiteral('hidden-source-map');
|
|
319
|
+
} else {
|
|
320
|
+
devtoolProp.value = b.stringLiteral('source-map');
|
|
321
|
+
}
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
configObj.properties.push(
|
|
326
|
+
b.objectProperty(b.identifier('devtool'), b.stringLiteral('source-map')),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function getWebpackConfigObject(
|
|
333
|
+
moduleExports: t.AssignmentExpression,
|
|
334
|
+
program: t.Program,
|
|
335
|
+
): t.ObjectExpression | undefined {
|
|
336
|
+
const rhs = moduleExports.right;
|
|
337
|
+
if (rhs.type === 'ObjectExpression') {
|
|
338
|
+
return rhs;
|
|
339
|
+
}
|
|
340
|
+
if (rhs.type === 'Identifier') {
|
|
341
|
+
const configId = rhs.name;
|
|
342
|
+
|
|
343
|
+
const configDeclaration = program.body.find(
|
|
344
|
+
(s): s is t.VariableDeclaration =>
|
|
345
|
+
s.type === 'VariableDeclaration' &&
|
|
346
|
+
!!s.declarations.find(
|
|
347
|
+
(d) =>
|
|
348
|
+
d.type === 'VariableDeclarator' &&
|
|
349
|
+
d.id.type === 'Identifier' &&
|
|
350
|
+
d.id.name === configId,
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
const declarator = configDeclaration?.declarations.find(
|
|
355
|
+
(d): d is t.VariableDeclarator =>
|
|
356
|
+
d.type === 'VariableDeclarator' &&
|
|
357
|
+
d.id.type === 'Identifier' &&
|
|
358
|
+
d.id.name === configId,
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
return declarator?.init?.type === 'ObjectExpression'
|
|
362
|
+
? declarator.init
|
|
363
|
+
: undefined;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function getCjsModuleExports(
|
|
370
|
+
program: t.Program,
|
|
371
|
+
): t.AssignmentExpression | undefined {
|
|
372
|
+
const moduleExports = program.body.find(
|
|
373
|
+
(s): s is t.ExpressionStatement =>
|
|
374
|
+
s.type === 'ExpressionStatement' &&
|
|
375
|
+
s.expression.type === 'AssignmentExpression' &&
|
|
376
|
+
s.expression.left.type === 'MemberExpression' &&
|
|
377
|
+
s.expression.left.object.type === 'Identifier' &&
|
|
378
|
+
s.expression.left.object.name === 'module' &&
|
|
379
|
+
s.expression.left.property.type === 'Identifier' &&
|
|
380
|
+
s.expression.left.property.name === 'exports',
|
|
381
|
+
);
|
|
382
|
+
return moduleExports?.expression as t.AssignmentExpression;
|
|
383
|
+
}
|
|
@@ -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
|
|
@@ -17,6 +19,11 @@ import { abortIfCancelled, isUsingTypeScript } from '../utils/clack-utils';
|
|
|
17
19
|
import { debug } from '../utils/debug';
|
|
18
20
|
import { findFile, hasSentryContent } from '../utils/ast-utils';
|
|
19
21
|
|
|
22
|
+
import * as recast from 'recast';
|
|
23
|
+
import x = recast.types;
|
|
24
|
+
import t = x.namedTypes;
|
|
25
|
+
import { traceStep } from '../telemetry';
|
|
26
|
+
|
|
20
27
|
const SVELTE_CONFIG_FILE = 'svelte.config.js';
|
|
21
28
|
|
|
22
29
|
export type PartialSvelteConfig = {
|
|
@@ -55,19 +62,25 @@ export async function createOrMergeSvelteKitFiles(
|
|
|
55
62
|
|
|
56
63
|
const { dsn } = projectInfo;
|
|
57
64
|
|
|
65
|
+
Sentry.setTag(
|
|
66
|
+
'client-hooks-file-strategy',
|
|
67
|
+
originalClientHooksFile ? 'merge' : 'create',
|
|
68
|
+
);
|
|
58
69
|
if (!originalClientHooksFile) {
|
|
59
70
|
clack.log.info('No client hooks file found, creating a new one.');
|
|
60
71
|
await createNewHooksFile(`${clientHooksPath}.${fileEnding}`, 'client', dsn);
|
|
72
|
+
} else {
|
|
73
|
+
await mergeHooksFile(originalClientHooksFile, 'client', dsn);
|
|
61
74
|
}
|
|
75
|
+
|
|
76
|
+
Sentry.setTag(
|
|
77
|
+
'server-hooks-file-strategy',
|
|
78
|
+
originalServerHooksFile ? 'merge' : 'create',
|
|
79
|
+
);
|
|
62
80
|
if (!originalServerHooksFile) {
|
|
63
81
|
clack.log.info('No server hooks file found, creating a new one.');
|
|
64
82
|
await createNewHooksFile(`${serverHooksPath}.${fileEnding}`, 'server', dsn);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (originalClientHooksFile) {
|
|
68
|
-
await mergeHooksFile(originalClientHooksFile, 'client', dsn);
|
|
69
|
-
}
|
|
70
|
-
if (originalServerHooksFile) {
|
|
83
|
+
} else {
|
|
71
84
|
await mergeHooksFile(originalServerHooksFile, 'server', dsn);
|
|
72
85
|
}
|
|
73
86
|
|
|
@@ -120,6 +133,7 @@ async function createNewHooksFile(
|
|
|
120
133
|
await fs.promises.writeFile(hooksFileDest, filledTemplate);
|
|
121
134
|
|
|
122
135
|
clack.log.success(`Created ${hooksFileDest}`);
|
|
136
|
+
Sentry.setTag(`created-${hooktype}-hooks`, 'success');
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
/**
|
|
@@ -139,7 +153,10 @@ async function mergeHooksFile(
|
|
|
139
153
|
dsn: string,
|
|
140
154
|
): Promise<void> {
|
|
141
155
|
const originalHooksMod = await loadFile(hooksFile);
|
|
142
|
-
|
|
156
|
+
|
|
157
|
+
const file: 'server-hooks' | 'client-hooks' = `${hookType}-hooks`;
|
|
158
|
+
|
|
159
|
+
if (hasSentryContent(originalHooksMod.$ast as t.Program)) {
|
|
143
160
|
// We don't want to mess with files that already have Sentry content.
|
|
144
161
|
// Let's just bail out at this point.
|
|
145
162
|
clack.log.warn(
|
|
@@ -148,32 +165,59 @@ async function mergeHooksFile(
|
|
|
148
165
|
)} already contains Sentry code.
|
|
149
166
|
Skipping adding Sentry functionality to.`,
|
|
150
167
|
);
|
|
168
|
+
Sentry.setTag(`modified-${file}`, 'fail');
|
|
169
|
+
Sentry.setTag(`${file}-fail-reason`, 'has-sentry-content');
|
|
151
170
|
return;
|
|
152
171
|
}
|
|
153
172
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
await modifyAndRecordFail(
|
|
174
|
+
() =>
|
|
175
|
+
originalHooksMod.imports.$add({
|
|
176
|
+
from: '@sentry/sveltekit',
|
|
177
|
+
imported: '*',
|
|
178
|
+
local: 'Sentry',
|
|
179
|
+
}),
|
|
180
|
+
'import-injection',
|
|
181
|
+
file,
|
|
182
|
+
);
|
|
159
183
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
+
);
|
|
165
195
|
|
|
166
|
-
|
|
196
|
+
await modifyAndRecordFail(
|
|
197
|
+
() => wrapHandleError(originalHooksMod),
|
|
198
|
+
'wrap-handle-error',
|
|
199
|
+
file,
|
|
200
|
+
);
|
|
167
201
|
|
|
168
202
|
if (hookType === 'server') {
|
|
169
|
-
|
|
203
|
+
await modifyAndRecordFail(
|
|
204
|
+
() => wrapHandle(originalHooksMod),
|
|
205
|
+
'wrap-handle',
|
|
206
|
+
'server-hooks',
|
|
207
|
+
);
|
|
170
208
|
}
|
|
171
209
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
+
);
|
|
175
218
|
|
|
176
219
|
clack.log.success(`Added Sentry code to ${hooksFile}`);
|
|
220
|
+
Sentry.setTag(`modified-${hookType}-hooks`, 'success');
|
|
177
221
|
}
|
|
178
222
|
|
|
179
223
|
function insertClientInitCall(
|
|
@@ -399,39 +443,52 @@ async function modifyViteConfig(
|
|
|
399
443
|
try {
|
|
400
444
|
const viteModule = parseModule(viteConfigContent);
|
|
401
445
|
|
|
402
|
-
if (hasSentryContent(viteModule)) {
|
|
446
|
+
if (hasSentryContent(viteModule.$ast as t.Program)) {
|
|
403
447
|
clack.log.warn(
|
|
404
448
|
`File ${chalk.cyan(
|
|
405
449
|
path.basename(viteConfigPath),
|
|
406
450
|
)} already contains Sentry code.
|
|
407
451
|
Skipping adding Sentry functionality to.`,
|
|
408
452
|
);
|
|
453
|
+
Sentry.setTag(`modified-vite-cfg`, 'fail');
|
|
454
|
+
Sentry.setTag(`vite-cfg-fail-reason`, 'has-sentry-content');
|
|
409
455
|
return;
|
|
410
456
|
}
|
|
411
457
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
+
);
|
|
427
476
|
|
|
428
|
-
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
|
+
);
|
|
429
485
|
} catch (e) {
|
|
430
486
|
debug(e);
|
|
431
487
|
await showFallbackViteCopyPasteSnippet(
|
|
432
488
|
viteConfigPath,
|
|
433
489
|
getViteConfigCodeSnippet(org, project, selfHosted, url),
|
|
434
490
|
);
|
|
491
|
+
Sentry.captureException('Sveltekit Vite Config Modification Fail');
|
|
435
492
|
}
|
|
436
493
|
}
|
|
437
494
|
|
|
@@ -508,3 +565,22 @@ function getInitCallInsertionIndex(originalHooksModAST: Program): number {
|
|
|
508
565
|
: 0;
|
|
509
566
|
return initCallInsertionIndex;
|
|
510
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
|
+
}
|