@sentry/wizard 3.13.0 → 3.14.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.
- package/CHANGELOG.md +11 -1
- package/dist/package.json +2 -2
- package/dist/src/android/android-wizard.js +2 -4
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/apple/apple-wizard.js +1 -1
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.d.ts +1 -0
- package/dist/src/nextjs/nextjs-wizard.js +252 -161
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/utils.d.ts +1 -0
- package/dist/src/nextjs/utils.js +25 -0
- package/dist/src/nextjs/utils.js.map +1 -0
- package/dist/src/remix/remix-wizard.js +5 -7
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-setup.js +10 -4
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.js +1 -1
- package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.js +7 -3
- package/dist/src/sveltekit/sdk-setup.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +6 -10
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/telemetry.d.ts +1 -0
- package/dist/src/telemetry.js +27 -12
- package/dist/src/telemetry.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +11 -1
- package/dist/src/utils/clack-utils.js +190 -126
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/package-manager.js +12 -7
- package/dist/src/utils/package-manager.js.map +1 -1
- package/package.json +2 -2
- package/src/android/android-wizard.ts +4 -5
- package/src/apple/apple-wizard.ts +2 -2
- package/src/nextjs/nextjs-wizard.ts +262 -195
- package/src/nextjs/utils.ts +21 -0
- package/src/remix/remix-wizard.ts +7 -9
- package/src/remix/sdk-setup.ts +12 -3
- package/src/sourcemaps/sourcemaps-wizard.ts +2 -2
- package/src/sveltekit/sdk-setup.ts +6 -3
- package/src/sveltekit/sveltekit-wizard.ts +9 -12
- package/src/telemetry.ts +22 -11
- package/src/utils/clack-utils.ts +177 -107
- package/src/utils/package-manager.ts +12 -6
|
@@ -7,11 +7,13 @@ import * as fs from 'fs';
|
|
|
7
7
|
import { builders, generateCode, parseModule } from 'magicast';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
|
|
10
|
+
import * as Sentry from '@sentry/node';
|
|
11
|
+
|
|
10
12
|
import {
|
|
11
13
|
abort,
|
|
12
14
|
abortIfCancelled,
|
|
13
15
|
addSentryCliConfig,
|
|
14
|
-
|
|
16
|
+
confirmContinueIfNoOrDirtyGitRepo,
|
|
15
17
|
ensurePackageIsInstalled,
|
|
16
18
|
getOrAskForProjectData,
|
|
17
19
|
getPackageDotJson,
|
|
@@ -19,7 +21,7 @@ import {
|
|
|
19
21
|
isUsingTypeScript,
|
|
20
22
|
printWelcome,
|
|
21
23
|
} from '../utils/clack-utils';
|
|
22
|
-
import { WizardOptions } from '../utils/types';
|
|
24
|
+
import { SentryProjectData, WizardOptions } from '../utils/types';
|
|
23
25
|
import {
|
|
24
26
|
getNextjsConfigCjsAppendix,
|
|
25
27
|
getNextjsConfigCjsTemplate,
|
|
@@ -31,88 +33,158 @@ import {
|
|
|
31
33
|
getSentryExampleAppDirApiRoute,
|
|
32
34
|
getSentryExamplePageContents,
|
|
33
35
|
} from './templates';
|
|
36
|
+
import { traceStep, withTelemetry } from '../telemetry';
|
|
37
|
+
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
|
|
38
|
+
import { getNextJsVersionBucket } from './utils';
|
|
39
|
+
|
|
40
|
+
export function runNextjsWizard(options: WizardOptions) {
|
|
41
|
+
return withTelemetry(
|
|
42
|
+
{
|
|
43
|
+
enabled: options.telemetryEnabled,
|
|
44
|
+
integration: 'nextjs',
|
|
45
|
+
},
|
|
46
|
+
() => runNextjsWizardWithTelemetry(options),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
export async function runNextjsWizardWithTelemetry(
|
|
51
|
+
options: WizardOptions,
|
|
52
|
+
): Promise<void> {
|
|
37
53
|
printWelcome({
|
|
38
54
|
wizardName: 'Sentry Next.js Wizard',
|
|
39
55
|
promoCode: options.promoCode,
|
|
56
|
+
telemetryEnabled: options.telemetryEnabled,
|
|
40
57
|
});
|
|
41
58
|
|
|
42
|
-
await
|
|
59
|
+
await confirmContinueIfNoOrDirtyGitRepo();
|
|
43
60
|
|
|
44
61
|
const packageJson = await getPackageDotJson();
|
|
62
|
+
|
|
45
63
|
await ensurePackageIsInstalled(packageJson, 'next', 'Next.js');
|
|
46
64
|
|
|
65
|
+
const nextVersion = getPackageVersion('next', packageJson);
|
|
66
|
+
Sentry.setTag('nextjs-version', getNextJsVersionBucket(nextVersion));
|
|
67
|
+
|
|
47
68
|
const { selectedProject, authToken, selfHosted, sentryUrl } =
|
|
48
69
|
await getOrAskForProjectData(options, 'javascript-nextjs');
|
|
49
70
|
|
|
71
|
+
const sdkAlreadyInstalled = hasPackageInstalled(
|
|
72
|
+
'@sentry/nextjs',
|
|
73
|
+
packageJson,
|
|
74
|
+
);
|
|
75
|
+
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
|
|
76
|
+
|
|
50
77
|
await installPackage({
|
|
51
78
|
packageName: '@sentry/nextjs',
|
|
52
79
|
alreadyInstalled: !!packageJson?.dependencies?.['@sentry/nextjs'],
|
|
53
80
|
});
|
|
54
81
|
|
|
55
|
-
|
|
82
|
+
await traceStep('configure-sdk', async () =>
|
|
83
|
+
createOrMergeNextJsFiles(selectedProject, selfHosted, sentryUrl),
|
|
84
|
+
);
|
|
56
85
|
|
|
57
|
-
|
|
86
|
+
await traceStep('create-example-page', async () =>
|
|
87
|
+
createExamplePage(selfHosted, selectedProject, sentryUrl),
|
|
88
|
+
);
|
|
58
89
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
90
|
+
await addSentryCliConfig(authToken);
|
|
91
|
+
|
|
92
|
+
const mightBeUsingVercel = fs.existsSync(
|
|
93
|
+
path.join(process.cwd(), 'vercel.json'),
|
|
94
|
+
);
|
|
62
95
|
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
clack.outro(
|
|
97
|
+
`${chalk.green('Everything is set up!')}
|
|
65
98
|
|
|
66
|
-
|
|
99
|
+
${chalk.cyan(
|
|
100
|
+
'You can validate your setup by starting your dev environment (`next dev`) and visiting "/sentry-example-page".',
|
|
101
|
+
)}
|
|
102
|
+
${
|
|
103
|
+
mightBeUsingVercel
|
|
104
|
+
? `
|
|
105
|
+
▲ It seems like you're using Vercel. We recommend using the Sentry Vercel integration: https://vercel.com/integrations/sentry
|
|
106
|
+
`
|
|
107
|
+
: ''
|
|
108
|
+
}
|
|
109
|
+
${chalk.dim(
|
|
110
|
+
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
|
|
111
|
+
)}`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
67
114
|
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
async function createOrMergeNextJsFiles(
|
|
116
|
+
selectedProject: SentryProjectData,
|
|
117
|
+
selfHosted: boolean,
|
|
118
|
+
sentryUrl: string,
|
|
119
|
+
) {
|
|
120
|
+
const typeScriptDetected = isUsingTypeScript();
|
|
70
121
|
|
|
71
|
-
|
|
72
|
-
existingConfigs.push(jsConfig);
|
|
73
|
-
}
|
|
122
|
+
const configVariants = ['server', 'client', 'edge'] as const;
|
|
74
123
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
124
|
+
for (const configVariant of configVariants) {
|
|
125
|
+
await traceStep(`create-sentry-${configVariant}-config`, async () => {
|
|
126
|
+
const jsConfig = `sentry.${configVariant}.config.js`;
|
|
127
|
+
const tsConfig = `sentry.${configVariant}.config.ts`;
|
|
78
128
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
message: `Found existing Sentry ${configVariant} config (${existingConfigs.join(
|
|
82
|
-
', ',
|
|
83
|
-
)}). Overwrite ${existingConfigs.length > 1 ? 'them' : 'it'}?`,
|
|
84
|
-
}),
|
|
85
|
-
);
|
|
129
|
+
const jsConfigExists = fs.existsSync(path.join(process.cwd(), jsConfig));
|
|
130
|
+
const tsConfigExists = fs.existsSync(path.join(process.cwd(), tsConfig));
|
|
86
131
|
|
|
87
|
-
shouldWriteFile =
|
|
132
|
+
let shouldWriteFile = true;
|
|
133
|
+
|
|
134
|
+
if (jsConfigExists || tsConfigExists) {
|
|
135
|
+
const existingConfigs = [];
|
|
88
136
|
|
|
89
|
-
if (overwriteExistingConfigs) {
|
|
90
137
|
if (jsConfigExists) {
|
|
91
|
-
|
|
92
|
-
clack.log.warn(`Removed existing ${chalk.bold(jsConfig)}.`);
|
|
138
|
+
existingConfigs.push(jsConfig);
|
|
93
139
|
}
|
|
140
|
+
|
|
94
141
|
if (tsConfigExists) {
|
|
95
|
-
|
|
96
|
-
|
|
142
|
+
existingConfigs.push(tsConfig);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const overwriteExistingConfigs = await abortIfCancelled(
|
|
146
|
+
clack.confirm({
|
|
147
|
+
message: `Found existing Sentry ${configVariant} config (${existingConfigs.join(
|
|
148
|
+
', ',
|
|
149
|
+
)}). Overwrite ${existingConfigs.length > 1 ? 'them' : 'it'}?`,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
Sentry.setTag(
|
|
153
|
+
`overwrite-${configVariant}-config`,
|
|
154
|
+
overwriteExistingConfigs,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
shouldWriteFile = overwriteExistingConfigs;
|
|
158
|
+
|
|
159
|
+
if (overwriteExistingConfigs) {
|
|
160
|
+
if (jsConfigExists) {
|
|
161
|
+
fs.unlinkSync(path.join(process.cwd(), jsConfig));
|
|
162
|
+
clack.log.warn(`Removed existing ${chalk.bold(jsConfig)}.`);
|
|
163
|
+
}
|
|
164
|
+
if (tsConfigExists) {
|
|
165
|
+
fs.unlinkSync(path.join(process.cwd(), tsConfig));
|
|
166
|
+
clack.log.warn(`Removed existing ${chalk.bold(tsConfig)}.`);
|
|
167
|
+
}
|
|
97
168
|
}
|
|
98
169
|
}
|
|
99
|
-
}
|
|
100
170
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
171
|
+
if (shouldWriteFile) {
|
|
172
|
+
await fs.promises.writeFile(
|
|
173
|
+
path.join(process.cwd(), typeScriptDetected ? tsConfig : jsConfig),
|
|
174
|
+
getSentryConfigContents(
|
|
175
|
+
selectedProject.keys[0].dsn.public,
|
|
176
|
+
configVariant,
|
|
177
|
+
),
|
|
178
|
+
{ encoding: 'utf8', flag: 'w' },
|
|
179
|
+
);
|
|
180
|
+
clack.log.success(
|
|
181
|
+
`Created fresh ${chalk.bold(
|
|
182
|
+
typeScriptDetected ? tsConfig : jsConfig,
|
|
183
|
+
)}.`,
|
|
184
|
+
);
|
|
185
|
+
Sentry.setTag(`created-${configVariant}-config`, true);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
116
188
|
}
|
|
117
189
|
|
|
118
190
|
const sentryWebpackOptionsTemplate = getNextjsWebpackPluginOptionsTemplate(
|
|
@@ -126,162 +198,179 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
|
|
|
126
198
|
const nextConfigJs = 'next.config.js';
|
|
127
199
|
const nextConfigMjs = 'next.config.mjs';
|
|
128
200
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
);
|
|
132
|
-
const nextConfigMjsExists = fs.existsSync(
|
|
133
|
-
path.join(process.cwd(), nextConfigMjs),
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (!nextConfigJsExists && !nextConfigMjsExists) {
|
|
137
|
-
await fs.promises.writeFile(
|
|
201
|
+
await traceStep('setup-next-config', async () => {
|
|
202
|
+
const nextConfigJsExists = fs.existsSync(
|
|
138
203
|
path.join(process.cwd(), nextConfigJs),
|
|
139
|
-
getNextjsConfigCjsTemplate(
|
|
140
|
-
sentryWebpackOptionsTemplate,
|
|
141
|
-
sentryBuildOptionsTemplate,
|
|
142
|
-
),
|
|
143
|
-
{ encoding: 'utf8', flag: 'w' },
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
clack.log.success(
|
|
147
|
-
`Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
|
|
148
204
|
);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (nextConfigJsExists) {
|
|
152
|
-
const nextConfgiJsContent = fs.readFileSync(
|
|
153
|
-
path.join(process.cwd(), nextConfigJs),
|
|
154
|
-
'utf8',
|
|
205
|
+
const nextConfigMjsExists = fs.existsSync(
|
|
206
|
+
path.join(process.cwd(), nextConfigMjs),
|
|
155
207
|
);
|
|
156
208
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
nextConfgiJsContent.includes('withSentryConfig');
|
|
160
|
-
|
|
161
|
-
let shouldInject = true;
|
|
162
|
-
|
|
163
|
-
if (probablyIncludesSdk) {
|
|
164
|
-
const injectAnyhow = await abortIfCancelled(
|
|
165
|
-
clack.confirm({
|
|
166
|
-
message: `${chalk.bold(
|
|
167
|
-
nextConfigJs,
|
|
168
|
-
)} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
shouldInject = injectAnyhow;
|
|
173
|
-
}
|
|
209
|
+
if (!nextConfigJsExists && !nextConfigMjsExists) {
|
|
210
|
+
Sentry.setTag('next-config-strategy', 'create');
|
|
174
211
|
|
|
175
|
-
|
|
176
|
-
await fs.promises.appendFile(
|
|
212
|
+
await fs.promises.writeFile(
|
|
177
213
|
path.join(process.cwd(), nextConfigJs),
|
|
178
|
-
|
|
214
|
+
getNextjsConfigCjsTemplate(
|
|
179
215
|
sentryWebpackOptionsTemplate,
|
|
180
216
|
sentryBuildOptionsTemplate,
|
|
181
217
|
),
|
|
182
|
-
'utf8',
|
|
218
|
+
{ encoding: 'utf8', flag: 'w' },
|
|
183
219
|
);
|
|
184
220
|
|
|
185
221
|
clack.log.success(
|
|
186
|
-
`
|
|
187
|
-
'(you probably want to clean this up a bit!)',
|
|
188
|
-
)}`,
|
|
222
|
+
`Created ${chalk.bold('next.config.js')} with Sentry configuration.`,
|
|
189
223
|
);
|
|
190
224
|
}
|
|
191
|
-
}
|
|
192
225
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
path.join(process.cwd(), nextConfigMjs),
|
|
196
|
-
'utf8',
|
|
197
|
-
);
|
|
226
|
+
if (nextConfigJsExists) {
|
|
227
|
+
Sentry.setTag('next-config-strategy', 'modify');
|
|
198
228
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
229
|
+
const nextConfgiJsContent = fs.readFileSync(
|
|
230
|
+
path.join(process.cwd(), nextConfigJs),
|
|
231
|
+
'utf8',
|
|
232
|
+
);
|
|
202
233
|
|
|
203
|
-
|
|
234
|
+
const probablyIncludesSdk =
|
|
235
|
+
nextConfgiJsContent.includes('@sentry/nextjs') &&
|
|
236
|
+
nextConfgiJsContent.includes('withSentryConfig');
|
|
204
237
|
|
|
205
|
-
|
|
206
|
-
const injectAnyhow = await abortIfCancelled(
|
|
207
|
-
clack.confirm({
|
|
208
|
-
message: `${chalk.bold(
|
|
209
|
-
nextConfigMjs,
|
|
210
|
-
)} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
|
|
211
|
-
}),
|
|
212
|
-
);
|
|
238
|
+
let shouldInject = true;
|
|
213
239
|
|
|
214
|
-
|
|
215
|
-
|
|
240
|
+
if (probablyIncludesSdk) {
|
|
241
|
+
const injectAnyhow = await abortIfCancelled(
|
|
242
|
+
clack.confirm({
|
|
243
|
+
message: `${chalk.bold(
|
|
244
|
+
nextConfigJs,
|
|
245
|
+
)} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
|
|
246
|
+
}),
|
|
247
|
+
);
|
|
216
248
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const mod = parseModule(nextConfgiMjsContent);
|
|
220
|
-
mod.imports.$add({
|
|
221
|
-
from: '@sentry/nextjs',
|
|
222
|
-
imported: 'withSentryConfig',
|
|
223
|
-
local: 'withSentryConfig',
|
|
224
|
-
});
|
|
225
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
226
|
-
const expressionToWrap = generateCode(mod.exports.default.$ast).code;
|
|
227
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
228
|
-
mod.exports.default = builders.raw(`withSentryConfig(
|
|
229
|
-
${expressionToWrap},
|
|
230
|
-
${sentryWebpackOptionsTemplate},
|
|
231
|
-
${sentryBuildOptionsTemplate}
|
|
232
|
-
)`);
|
|
233
|
-
const newCode = mod.generate().code;
|
|
249
|
+
shouldInject = injectAnyhow;
|
|
250
|
+
}
|
|
234
251
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
252
|
+
if (shouldInject) {
|
|
253
|
+
await fs.promises.appendFile(
|
|
254
|
+
path.join(process.cwd(), nextConfigJs),
|
|
255
|
+
getNextjsConfigCjsAppendix(
|
|
256
|
+
sentryWebpackOptionsTemplate,
|
|
257
|
+
sentryBuildOptionsTemplate,
|
|
258
|
+
),
|
|
259
|
+
'utf8',
|
|
242
260
|
);
|
|
261
|
+
|
|
243
262
|
clack.log.success(
|
|
244
263
|
`Added Sentry configuration to ${chalk.bold(
|
|
245
|
-
|
|
264
|
+
nextConfigJs,
|
|
246
265
|
)}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
|
|
247
266
|
);
|
|
248
267
|
}
|
|
249
|
-
} catch {
|
|
250
|
-
clack.log.warn(
|
|
251
|
-
chalk.yellow(
|
|
252
|
-
`Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
|
|
253
|
-
),
|
|
254
|
-
);
|
|
255
|
-
clack.log.info(
|
|
256
|
-
`Please put the following code snippet into ${chalk.bold(
|
|
257
|
-
nextConfigMjs,
|
|
258
|
-
)}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
|
|
259
|
-
);
|
|
260
268
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
),
|
|
269
|
+
Sentry.setTag('next-config-mod-result', 'success');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (nextConfigMjsExists) {
|
|
273
|
+
const nextConfgiMjsContent = fs.readFileSync(
|
|
274
|
+
path.join(process.cwd(), nextConfigMjs),
|
|
275
|
+
'utf8',
|
|
267
276
|
);
|
|
268
277
|
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
278
|
+
const probablyIncludesSdk =
|
|
279
|
+
nextConfgiMjsContent.includes('@sentry/nextjs') &&
|
|
280
|
+
nextConfgiMjsContent.includes('withSentryConfig');
|
|
281
|
+
|
|
282
|
+
let shouldInject = true;
|
|
283
|
+
|
|
284
|
+
if (probablyIncludesSdk) {
|
|
285
|
+
const injectAnyhow = await abortIfCancelled(
|
|
286
|
+
clack.confirm({
|
|
287
|
+
message: `${chalk.bold(
|
|
288
|
+
nextConfigMjs,
|
|
289
|
+
)} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
shouldInject = injectAnyhow;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
if (shouldInject) {
|
|
298
|
+
const mod = parseModule(nextConfgiMjsContent);
|
|
299
|
+
mod.imports.$add({
|
|
300
|
+
from: '@sentry/nextjs',
|
|
301
|
+
imported: 'withSentryConfig',
|
|
302
|
+
local: 'withSentryConfig',
|
|
303
|
+
});
|
|
304
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
305
|
+
const expressionToWrap = generateCode(mod.exports.default.$ast).code;
|
|
306
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
307
|
+
mod.exports.default = builders.raw(`withSentryConfig(
|
|
308
|
+
${expressionToWrap},
|
|
309
|
+
${sentryWebpackOptionsTemplate},
|
|
310
|
+
${sentryBuildOptionsTemplate}
|
|
311
|
+
)`);
|
|
312
|
+
const newCode = mod.generate().code;
|
|
313
|
+
|
|
314
|
+
await fs.promises.writeFile(
|
|
315
|
+
path.join(process.cwd(), nextConfigMjs),
|
|
316
|
+
newCode,
|
|
317
|
+
{
|
|
318
|
+
encoding: 'utf8',
|
|
319
|
+
flag: 'w',
|
|
320
|
+
},
|
|
321
|
+
);
|
|
322
|
+
clack.log.success(
|
|
323
|
+
`Added Sentry configuration to ${chalk.bold(
|
|
324
|
+
nextConfigMjs,
|
|
325
|
+
)}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
Sentry.setTag('next-config-mod-result', 'success');
|
|
329
|
+
}
|
|
330
|
+
} catch {
|
|
331
|
+
Sentry.setTag('next-config-mod-result', 'fail');
|
|
332
|
+
clack.log.warn(
|
|
333
|
+
chalk.yellow(
|
|
334
|
+
`Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
|
|
335
|
+
),
|
|
336
|
+
);
|
|
337
|
+
clack.log.info(
|
|
338
|
+
`Please put the following code snippet into ${chalk.bold(
|
|
272
339
|
nextConfigMjs,
|
|
273
|
-
)}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
340
|
+
)}: ${chalk.dim('You probably have to clean it up a bit.')}\n`,
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
// eslint-disable-next-line no-console
|
|
344
|
+
console.log(
|
|
345
|
+
getNextjsConfigEsmCopyPasteSnippet(
|
|
346
|
+
sentryWebpackOptionsTemplate,
|
|
347
|
+
sentryBuildOptionsTemplate,
|
|
348
|
+
),
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const shouldContinue = await abortIfCancelled(
|
|
352
|
+
clack.confirm({
|
|
353
|
+
message: `Are you done putting the snippet above into ${chalk.bold(
|
|
354
|
+
nextConfigMjs,
|
|
355
|
+
)}?`,
|
|
356
|
+
active: 'Yes',
|
|
357
|
+
inactive: 'No, get me out of here',
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
278
360
|
|
|
279
|
-
|
|
280
|
-
|
|
361
|
+
if (!shouldContinue) {
|
|
362
|
+
await abort();
|
|
363
|
+
}
|
|
281
364
|
}
|
|
282
365
|
}
|
|
283
|
-
}
|
|
366
|
+
});
|
|
367
|
+
}
|
|
284
368
|
|
|
369
|
+
async function createExamplePage(
|
|
370
|
+
selfHosted: boolean,
|
|
371
|
+
selectedProject: SentryProjectData,
|
|
372
|
+
sentryUrl: string,
|
|
373
|
+
): Promise<void> {
|
|
285
374
|
const srcDir = path.join(process.cwd(), 'src');
|
|
286
375
|
const maybePagesDirPath = path.join(process.cwd(), 'pages');
|
|
287
376
|
const maybeSrcPagesDirPath = path.join(srcDir, 'pages');
|
|
@@ -316,6 +405,8 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
|
|
|
316
405
|
});
|
|
317
406
|
}
|
|
318
407
|
|
|
408
|
+
Sentry.setTag('nextjs-app-dir', !!appLocation);
|
|
409
|
+
|
|
319
410
|
if (appLocation) {
|
|
320
411
|
const examplePageContents = getSentryExamplePageContents({
|
|
321
412
|
selfHosted,
|
|
@@ -415,28 +506,4 @@ export async function runNextjsWizard(options: WizardOptions): Promise<void> {
|
|
|
415
506
|
)}.`,
|
|
416
507
|
);
|
|
417
508
|
}
|
|
418
|
-
|
|
419
|
-
await addSentryCliConfig(authToken);
|
|
420
|
-
|
|
421
|
-
const mightBeUsingVercel = fs.existsSync(
|
|
422
|
-
path.join(process.cwd(), 'vercel.json'),
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
clack.outro(
|
|
426
|
-
`${chalk.green('Everything is set up!')}
|
|
427
|
-
|
|
428
|
-
${chalk.cyan(
|
|
429
|
-
'You can validate your setup by starting your dev environment (`next dev`) and visiting "/sentry-example-page".',
|
|
430
|
-
)}
|
|
431
|
-
${
|
|
432
|
-
mightBeUsingVercel
|
|
433
|
-
? `
|
|
434
|
-
▲ It seems like you're using Vercel. We recommend using the Sentry Vercel integration: https://vercel.com/integrations/sentry
|
|
435
|
-
`
|
|
436
|
-
: ''
|
|
437
|
-
}
|
|
438
|
-
${chalk.dim(
|
|
439
|
-
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-javascript/issues',
|
|
440
|
-
)}`,
|
|
441
|
-
);
|
|
442
509
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { major, minVersion } from 'semver';
|
|
2
|
+
|
|
3
|
+
export function getNextJsVersionBucket(version: string | undefined) {
|
|
4
|
+
if (!version) {
|
|
5
|
+
return 'none';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const minVer = minVersion(version);
|
|
10
|
+
if (!minVer) {
|
|
11
|
+
return 'invalid';
|
|
12
|
+
}
|
|
13
|
+
const majorVersion = major(minVer);
|
|
14
|
+
if (majorVersion >= 11) {
|
|
15
|
+
return `${majorVersion}.x`;
|
|
16
|
+
}
|
|
17
|
+
return '<11.0.0';
|
|
18
|
+
} catch {
|
|
19
|
+
return 'unknown';
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -4,7 +4,7 @@ import chalk from 'chalk';
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
addSentryCliConfig,
|
|
7
|
-
|
|
7
|
+
confirmContinueIfNoOrDirtyGitRepo,
|
|
8
8
|
ensurePackageIsInstalled,
|
|
9
9
|
getOrAskForProjectData,
|
|
10
10
|
getPackageDotJson,
|
|
@@ -45,7 +45,7 @@ async function runRemixWizardWithTelemetry(
|
|
|
45
45
|
telemetryEnabled: options.telemetryEnabled,
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
await
|
|
48
|
+
await confirmContinueIfNoOrDirtyGitRepo();
|
|
49
49
|
|
|
50
50
|
const remixConfig = await loadRemixConfig();
|
|
51
51
|
const packageJson = await getPackageDotJson();
|
|
@@ -56,12 +56,10 @@ async function runRemixWizardWithTelemetry(
|
|
|
56
56
|
const { selectedProject, authToken, sentryUrl } =
|
|
57
57
|
await getOrAskForProjectData(options, 'javascript-remix');
|
|
58
58
|
|
|
59
|
-
await
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}),
|
|
64
|
-
);
|
|
59
|
+
await installPackage({
|
|
60
|
+
packageName: '@sentry/remix',
|
|
61
|
+
alreadyInstalled: hasPackageInstalled('@sentry/remix', packageJson),
|
|
62
|
+
});
|
|
65
63
|
|
|
66
64
|
const dsn = selectedProject.keys[0].dsn.public;
|
|
67
65
|
|
|
@@ -107,7 +105,7 @@ async function runRemixWizardWithTelemetry(
|
|
|
107
105
|
|
|
108
106
|
await traceStep('Initialize Sentry on server entry', async () => {
|
|
109
107
|
try {
|
|
110
|
-
await initializeSentryOnEntryServer(dsn,
|
|
108
|
+
await initializeSentryOnEntryServer(dsn, isV2, isTS);
|
|
111
109
|
} catch (e) {
|
|
112
110
|
clack.log.warn(`Could not initialize Sentry on server entry.
|
|
113
111
|
Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/`);
|
package/src/remix/sdk-setup.ts
CHANGED
|
@@ -12,7 +12,7 @@ import * as url from 'url';
|
|
|
12
12
|
// @ts-expect-error - clack is ESM and TS complains about that. It works though
|
|
13
13
|
import clack from '@clack/prompts';
|
|
14
14
|
import chalk from 'chalk';
|
|
15
|
-
import {
|
|
15
|
+
import { gte, minVersion } from 'semver';
|
|
16
16
|
|
|
17
17
|
// @ts-expect-error - magicast is ESM and TS complains about that. It works though
|
|
18
18
|
import { builders, generateCode, loadFile, writeFile } from 'magicast';
|
|
@@ -97,8 +97,17 @@ export function isRemixV2(
|
|
|
97
97
|
packageJson: PackageDotJson,
|
|
98
98
|
): boolean {
|
|
99
99
|
const remixVersion = getPackageVersion('@remix-run/react', packageJson);
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
if (!remixVersion) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const minVer = minVersion(remixVersion);
|
|
105
|
+
|
|
106
|
+
if (!minVer) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const isV2Remix = gte(minVer, '2.0.0');
|
|
102
111
|
|
|
103
112
|
return isV2Remix || remixConfig?.future?.v2_errorBoundary || false;
|
|
104
113
|
}
|
|
@@ -6,7 +6,7 @@ import * as Sentry from '@sentry/node';
|
|
|
6
6
|
import {
|
|
7
7
|
abort,
|
|
8
8
|
abortIfCancelled,
|
|
9
|
-
|
|
9
|
+
confirmContinueIfNoOrDirtyGitRepo,
|
|
10
10
|
SENTRY_DOT_ENV_FILE,
|
|
11
11
|
printWelcome,
|
|
12
12
|
SENTRY_CLI_RC_FILE,
|
|
@@ -70,7 +70,7 @@ You can turn this off by running the wizard with the '--disable-telemetry' flag.
|
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
await
|
|
73
|
+
await confirmContinueIfNoOrDirtyGitRepo();
|
|
74
74
|
|
|
75
75
|
await traceStep('check-sdk-version', ensureMinimumSdkVersionIsInstalled);
|
|
76
76
|
|