@sentry/wizard 3.1.0 → 3.2.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 +15 -0
- package/bin.ts +5 -1
- package/dist/bin.js +6 -1
- package/dist/bin.js.map +1 -1
- package/dist/lib/Constants.d.ts +2 -1
- package/dist/lib/Constants.js +5 -0
- package/dist/lib/Constants.js.map +1 -1
- package/dist/lib/Helper/File.js +25 -2
- package/dist/lib/Helper/File.js.map +1 -1
- package/dist/lib/Helper/Git.d.ts +7 -0
- package/dist/lib/Helper/Git.js +94 -0
- package/dist/lib/Helper/Git.js.map +1 -0
- package/dist/lib/Helper/Logging.d.ts +1 -0
- package/dist/lib/Helper/Logging.js +9 -2
- package/dist/lib/Helper/Logging.js.map +1 -1
- package/dist/lib/Helper/MergeConfig.js +24 -1
- package/dist/lib/Helper/MergeConfig.js.map +1 -1
- package/dist/lib/Helper/Package.d.ts +9 -0
- package/dist/lib/Helper/Package.js +39 -2
- package/dist/lib/Helper/Package.js.map +1 -1
- package/dist/lib/Helper/PackageManager.d.ts +1 -1
- package/dist/lib/Helper/PackageManager.js +32 -11
- package/dist/lib/Helper/PackageManager.js.map +1 -1
- package/dist/lib/Helper/SentryCli.d.ts +11 -0
- package/dist/lib/Helper/SentryCli.js +141 -2
- package/dist/lib/Helper/SentryCli.js.map +1 -1
- package/dist/lib/Helper/Wizard.js +24 -1
- package/dist/lib/Helper/Wizard.js.map +1 -1
- package/dist/lib/Helper/__tests__/MergeConfig.js +25 -2
- package/dist/lib/Helper/__tests__/MergeConfig.js.map +1 -1
- package/dist/lib/Setup.js +25 -2
- package/dist/lib/Setup.js.map +1 -1
- package/dist/lib/Steps/ChooseIntegration.js +28 -1
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/lib/Steps/Initial.js +25 -2
- package/dist/lib/Steps/Initial.js.map +1 -1
- package/dist/lib/Steps/Integrations/BaseIntegration.js +24 -1
- package/dist/lib/Steps/Integrations/BaseIntegration.js.map +1 -1
- package/dist/lib/Steps/Integrations/Cordova.js +25 -2
- package/dist/lib/Steps/Integrations/Cordova.js.map +1 -1
- package/dist/lib/Steps/Integrations/Electron.js +26 -3
- package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
- package/dist/lib/Steps/Integrations/MobileProject.js +24 -1
- package/dist/lib/Steps/Integrations/MobileProject.js.map +1 -1
- package/dist/lib/Steps/Integrations/NextJs.d.ts +5 -11
- package/dist/lib/Steps/Integrations/NextJs.js +14 -343
- package/dist/lib/Steps/Integrations/NextJs.js.map +1 -1
- package/dist/lib/Steps/Integrations/ReactNative.d.ts +1 -0
- package/dist/lib/Steps/Integrations/ReactNative.js +67 -6
- package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
- package/dist/lib/Steps/Integrations/SvelteKit.d.ts +13 -0
- package/dist/lib/Steps/Integrations/SvelteKit.js +95 -0
- package/dist/lib/Steps/Integrations/SvelteKit.js.map +1 -0
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js +28 -5
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js.map +1 -1
- package/dist/lib/Steps/PromptForParameters.js +24 -1
- package/dist/lib/Steps/PromptForParameters.js.map +1 -1
- package/dist/lib/Steps/SentryProjectSelector.js +25 -1
- package/dist/lib/Steps/SentryProjectSelector.js.map +1 -1
- package/dist/lib/__tests__/Setup.js +24 -1
- package/dist/lib/__tests__/Setup.js.map +1 -1
- package/dist/src/{nextjs-wizard.js → nextjs/nextjs-wizard.js} +113 -108
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -0
- package/dist/src/sveltekit/sdk-example.d.ts +10 -0
- package/dist/src/sveltekit/sdk-example.js +106 -0
- package/dist/src/sveltekit/sdk-example.js.map +1 -0
- package/dist/src/sveltekit/sdk-setup.d.ts +13 -0
- package/dist/src/sveltekit/sdk-setup.js +451 -0
- package/dist/src/sveltekit/sdk-setup.js.map +1 -0
- package/dist/src/sveltekit/sentry-cli-setup.d.ts +2 -0
- package/dist/src/sveltekit/sentry-cli-setup.js +71 -0
- package/dist/src/sveltekit/sentry-cli-setup.js.map +1 -0
- package/dist/src/sveltekit/sveltekit-wizard.d.ts +5 -0
- package/dist/src/sveltekit/sveltekit-wizard.js +147 -0
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -0
- package/dist/src/templates/nextjs-templates.js +2 -2
- package/dist/src/templates/nextjs-templates.js.map +1 -1
- package/dist/src/templates/sveltekit-templates.d.ts +12 -0
- package/dist/src/templates/sveltekit-templates.js +26 -0
- package/dist/src/templates/sveltekit-templates.js.map +1 -0
- package/dist/src/{clack-utils.d.ts → utils/clack-utils.d.ts} +7 -0
- package/dist/src/{clack-utils.js → utils/clack-utils.js} +127 -42
- package/dist/src/utils/clack-utils.js.map +1 -0
- package/lib/Constants.ts +5 -0
- package/lib/Helper/Git.ts +39 -0
- package/lib/Helper/Logging.ts +4 -0
- package/lib/Helper/Package.ts +17 -0
- package/lib/Helper/PackageManager.ts +4 -9
- package/lib/Helper/SentryCli.ts +74 -0
- package/lib/Steps/ChooseIntegration.ts +4 -0
- package/lib/Steps/Integrations/NextJs.ts +7 -397
- package/lib/Steps/Integrations/ReactNative.ts +49 -3
- package/lib/Steps/Integrations/SvelteKit.ts +29 -0
- package/lib/Steps/SentryProjectSelector.ts +1 -0
- package/package.json +1 -1
- package/src/{nextjs-wizard.ts → nextjs/nextjs-wizard.ts} +13 -44
- package/src/sveltekit/sdk-example.ts +56 -0
- package/src/sveltekit/sdk-setup.ts +430 -0
- package/src/sveltekit/sentry-cli-setup.ts +27 -0
- package/src/sveltekit/sveltekit-wizard.ts +116 -0
- package/src/templates/nextjs-templates.ts +2 -2
- package/src/templates/sveltekit-templates.ts +172 -0
- package/src/{clack-utils.ts → utils/clack-utils.ts} +73 -11
- package/dist/src/clack-utils.js.map +0 -1
- package/dist/src/nextjs-wizard.js.map +0 -1
- /package/dist/src/{nextjs-wizard.d.ts → nextjs/nextjs-wizard.d.ts} +0 -0
|
@@ -1,103 +1,20 @@
|
|
|
1
|
-
/* eslint-disable max-lines */
|
|
2
|
-
import Chalk from 'chalk';
|
|
3
|
-
import * as fs from 'fs';
|
|
4
1
|
import type { Answers } from 'inquirer';
|
|
5
|
-
import {
|
|
6
|
-
import * as _ from 'lodash';
|
|
7
|
-
import * as path from 'path';
|
|
2
|
+
import { runNextjsWizard } from '../../../src/nextjs/nextjs-wizard';
|
|
8
3
|
|
|
9
4
|
import type { Args } from '../../Constants';
|
|
10
|
-
import { debug, green, l, nl, red } from '../../Helper/Logging';
|
|
11
|
-
import { mergeConfigFile } from '../../Helper/MergeConfig';
|
|
12
|
-
import { checkPackageVersion } from '../../Helper/Package';
|
|
13
|
-
import { getPackageMangerChoice } from '../../Helper/PackageManager';
|
|
14
|
-
import type { SentryCliProps } from '../../Helper/SentryCli';
|
|
15
|
-
import { SentryCli } from '../../Helper/SentryCli';
|
|
16
5
|
import { BaseIntegration } from './BaseIntegration';
|
|
17
6
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const GITIGNORE_FILENAME = '.gitignore';
|
|
23
|
-
const CONFIG_DIR = 'configs/';
|
|
24
|
-
const MERGEABLE_CONFIG_INFIX = 'wizardcopy';
|
|
25
|
-
|
|
26
|
-
// for those files which can go in more than one place, the list of places they
|
|
27
|
-
// could go (the first one which works will be used)
|
|
28
|
-
const TEMPLATE_DESTINATIONS: { [key: string]: string[] } = {
|
|
29
|
-
'_error.js': ['pages', 'src/pages'],
|
|
30
|
-
'next.config.js': ['.'],
|
|
31
|
-
'sentry.server.config.js': ['.'],
|
|
32
|
-
'sentry.client.config.js': ['.'],
|
|
33
|
-
'sentry.edge.config.js': ['.'],
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
let appPackage: any = {};
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
appPackage = require(path.join(process.cwd(), 'package.json'));
|
|
40
|
-
} catch {
|
|
41
|
-
// We don't need to have this
|
|
42
|
-
}
|
|
43
|
-
|
|
7
|
+
/**
|
|
8
|
+
* This class just redirects to the new `nextjs-wizard.ts` flow
|
|
9
|
+
* for anyone calling the wizard without the '-i nextjs' flag.
|
|
10
|
+
*/
|
|
44
11
|
export class NextJs extends BaseIntegration {
|
|
45
|
-
protected _sentryCli: SentryCli;
|
|
46
|
-
|
|
47
12
|
public constructor(protected _argv: Args) {
|
|
48
13
|
super(_argv);
|
|
49
|
-
this._sentryCli = new SentryCli(this._argv);
|
|
50
14
|
}
|
|
51
15
|
|
|
52
|
-
public async emit(
|
|
53
|
-
|
|
54
|
-
nl();
|
|
55
|
-
|
|
56
|
-
const sentryCliProps = this._sentryCli.convertAnswersToProperties(answers);
|
|
57
|
-
await this._createSentryCliConfig(sentryCliProps);
|
|
58
|
-
|
|
59
|
-
const templateDirectory = path.join(__dirname, '..', '..', '..', 'NextJs');
|
|
60
|
-
const configDirectory = path.join(templateDirectory, CONFIG_DIR);
|
|
61
|
-
|
|
62
|
-
if (fs.existsSync(configDirectory)) {
|
|
63
|
-
await this._createNextConfig(configDirectory, dsn);
|
|
64
|
-
} else {
|
|
65
|
-
debug(
|
|
66
|
-
`Couldn't find ${configDirectory}, probably because you ran this from inside of \`/lib\` rather than \`/dist\``,
|
|
67
|
-
);
|
|
68
|
-
nl();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const selectedProjectSlug: string | null = answers.config?.project?.slug;
|
|
72
|
-
if (selectedProjectSlug) {
|
|
73
|
-
const hasFirstEvent = answers.wizard?.projects?.find?.(
|
|
74
|
-
(p: { slug: string }) => p.slug === selectedProjectSlug,
|
|
75
|
-
)?.firstEvent;
|
|
76
|
-
if (!hasFirstEvent) {
|
|
77
|
-
await this._setTemplate(
|
|
78
|
-
templateDirectory,
|
|
79
|
-
'sentry_sample_error.js',
|
|
80
|
-
['pages', 'src/pages'],
|
|
81
|
-
dsn,
|
|
82
|
-
);
|
|
83
|
-
l(
|
|
84
|
-
Chalk.bgYellowBright(`
|
|
85
|
-
|------------------------------------------------------------------------|
|
|
86
|
-
| Installation Complete |
|
|
87
|
-
| To verify your installation and finish onboarding, launch your Next.js |
|
|
88
|
-
| application, navigate to http://localhost:3000/sentry_sample_error |
|
|
89
|
-
| and send us a sample error. |
|
|
90
|
-
|------------------------------------------------------------------------|
|
|
91
|
-
`),
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
l(
|
|
97
|
-
'For more information, see https://docs.sentry.io/platforms/javascript/guides/nextjs/',
|
|
98
|
-
);
|
|
99
|
-
nl();
|
|
100
|
-
|
|
16
|
+
public async emit(_answers: Answers): Promise<Answers> {
|
|
17
|
+
await runNextjsWizard({ promoCode: this._argv.promoCode });
|
|
101
18
|
return {};
|
|
102
19
|
}
|
|
103
20
|
|
|
@@ -106,314 +23,7 @@ export class NextJs extends BaseIntegration {
|
|
|
106
23
|
if (this._shouldConfigure) {
|
|
107
24
|
return this._shouldConfigure;
|
|
108
25
|
}
|
|
109
|
-
|
|
110
|
-
nl();
|
|
111
|
-
|
|
112
|
-
let userAnswers: Answers = { continue: true };
|
|
113
|
-
const hasCompatibleNextjsVersion = checkPackageVersion(
|
|
114
|
-
appPackage,
|
|
115
|
-
'next',
|
|
116
|
-
COMPATIBLE_NEXTJS_VERSIONS,
|
|
117
|
-
true,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const packageManager = getPackageMangerChoice();
|
|
121
|
-
const hasSdkInstalled = this._hasPackageInstalled('@sentry/nextjs');
|
|
122
|
-
|
|
123
|
-
let hasCompatibleSdkVersion = false;
|
|
124
|
-
// if no package but we have nextjs, let's add it if we can
|
|
125
|
-
if (!hasSdkInstalled && packageManager && hasCompatibleNextjsVersion) {
|
|
126
|
-
await packageManager.installPackage('@sentry/nextjs');
|
|
127
|
-
// can assume it's compatible since we just installed it
|
|
128
|
-
hasCompatibleSdkVersion = true;
|
|
129
|
-
} else {
|
|
130
|
-
// otherwise, let's check the version and spit out the appropriate error
|
|
131
|
-
hasCompatibleSdkVersion = checkPackageVersion(
|
|
132
|
-
appPackage,
|
|
133
|
-
'@sentry/nextjs',
|
|
134
|
-
COMPATIBLE_SDK_VERSIONS,
|
|
135
|
-
true,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
const hasAllPackagesCompatible =
|
|
139
|
-
hasCompatibleNextjsVersion && hasCompatibleSdkVersion;
|
|
140
|
-
|
|
141
|
-
if (!hasAllPackagesCompatible && !this._argv.quiet) {
|
|
142
|
-
userAnswers = await prompt({
|
|
143
|
-
message:
|
|
144
|
-
'There were errors during your project checkup, do you still want to continue?',
|
|
145
|
-
name: 'continue',
|
|
146
|
-
default: false,
|
|
147
|
-
type: 'confirm',
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
nl();
|
|
152
|
-
|
|
153
|
-
if (!userAnswers['continue']) {
|
|
154
|
-
throw new Error('Please install the required dependencies to continue.');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this._shouldConfigure = Promise.resolve({ nextjs: true });
|
|
158
26
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
159
27
|
return this.shouldConfigure;
|
|
160
28
|
}
|
|
161
|
-
|
|
162
|
-
private async _createSentryCliConfig(
|
|
163
|
-
cliProps: SentryCliProps,
|
|
164
|
-
): Promise<void> {
|
|
165
|
-
const { 'auth/token': authToken, ...cliPropsToWrite } = cliProps;
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* To not commit the auth token to the VCS, instead of adding it to the
|
|
169
|
-
* properties file (like the rest of props), it's added to the Sentry CLI
|
|
170
|
-
* config, which is added to the gitignore. This way makes the properties
|
|
171
|
-
* file safe to commit without exposing any auth tokens.
|
|
172
|
-
*/
|
|
173
|
-
if (authToken) {
|
|
174
|
-
try {
|
|
175
|
-
await fs.promises.appendFile(
|
|
176
|
-
SENTRYCLIRC_FILENAME,
|
|
177
|
-
this._sentryCli.dumpConfig({ auth: { token: authToken } }),
|
|
178
|
-
);
|
|
179
|
-
green(`✓ Successfully added the auth token to ${SENTRYCLIRC_FILENAME}`);
|
|
180
|
-
} catch {
|
|
181
|
-
red(
|
|
182
|
-
`⚠ Could not add the auth token to ${SENTRYCLIRC_FILENAME}, ` +
|
|
183
|
-
`please add it to identify your user account:\n${authToken}`,
|
|
184
|
-
);
|
|
185
|
-
nl();
|
|
186
|
-
}
|
|
187
|
-
} else {
|
|
188
|
-
red(
|
|
189
|
-
`⚠ Did not find an auth token, please add your token to ${SENTRYCLIRC_FILENAME}`,
|
|
190
|
-
);
|
|
191
|
-
l(
|
|
192
|
-
'To generate an auth token, visit https://sentry.io/settings/account/api/auth-tokens/',
|
|
193
|
-
);
|
|
194
|
-
l(
|
|
195
|
-
'To learn how to configure Sentry CLI, visit ' +
|
|
196
|
-
'https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#configure-sentry-cli',
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
await this._addToGitignore(
|
|
201
|
-
SENTRYCLIRC_FILENAME,
|
|
202
|
-
`⚠ Could not add ${SENTRYCLIRC_FILENAME} to ${GITIGNORE_FILENAME}, ` +
|
|
203
|
-
'please add it to not commit your auth key.',
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
await fs.promises.writeFile(
|
|
208
|
-
`./${PROPERTIES_FILENAME}`,
|
|
209
|
-
this._sentryCli.dumpProperties(cliPropsToWrite),
|
|
210
|
-
);
|
|
211
|
-
green('✓ Successfully created sentry.properties');
|
|
212
|
-
} catch {
|
|
213
|
-
red(`⚠ Could not add org and project data to ${PROPERTIES_FILENAME}`);
|
|
214
|
-
l(
|
|
215
|
-
'See docs for a manual setup: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#configure-sentry-cli',
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
nl();
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
private async _addToGitignore(
|
|
222
|
-
filepath: string,
|
|
223
|
-
errorMsg: string,
|
|
224
|
-
): Promise<void> {
|
|
225
|
-
/**
|
|
226
|
-
* Don't check whether the given file is ignored because:
|
|
227
|
-
* 1. It's tricky to check it without git.
|
|
228
|
-
* 2. Git might not be installed or accessible.
|
|
229
|
-
* 3. It's convenient to use a module to interact with git, but it would
|
|
230
|
-
* increase the size x2 approximately. Docs say to run the Wizard without
|
|
231
|
-
* installing it, and duplicating the size would slow the set-up down.
|
|
232
|
-
* 4. The Wizard is meant to be run once.
|
|
233
|
-
* 5. A message is logged informing users it's been added to the gitignore.
|
|
234
|
-
* 6. It will be added to the gitignore as many times as it runs - not a big
|
|
235
|
-
* deal.
|
|
236
|
-
* 7. It's straightforward to remove it from the gitignore.
|
|
237
|
-
*/
|
|
238
|
-
try {
|
|
239
|
-
await fs.promises.appendFile(
|
|
240
|
-
GITIGNORE_FILENAME,
|
|
241
|
-
`\n# Sentry\n${filepath}\n`,
|
|
242
|
-
);
|
|
243
|
-
green(`✓ ${filepath} added to ${GITIGNORE_FILENAME}`);
|
|
244
|
-
} catch {
|
|
245
|
-
red(errorMsg);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private async _createNextConfig(
|
|
250
|
-
configDirectory: string,
|
|
251
|
-
dsn: any,
|
|
252
|
-
): Promise<void> {
|
|
253
|
-
const templates = fs.readdirSync(configDirectory);
|
|
254
|
-
// next.config.template.js used for merging next.config.js , not its own template,
|
|
255
|
-
// so it shouldn't have a setTemplate call
|
|
256
|
-
const filteredTemplates = templates.filter(
|
|
257
|
-
(template) => template !== 'next.config.template.js',
|
|
258
|
-
);
|
|
259
|
-
for (const template of filteredTemplates) {
|
|
260
|
-
await this._setTemplate(
|
|
261
|
-
configDirectory,
|
|
262
|
-
template,
|
|
263
|
-
TEMPLATE_DESTINATIONS[template],
|
|
264
|
-
dsn,
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
red(
|
|
268
|
-
'⚠ Performance monitoring is enabled capturing 100% of transactions.\n' +
|
|
269
|
-
' Learn more in https://docs.sentry.io/product/performance/',
|
|
270
|
-
);
|
|
271
|
-
nl();
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
private async _setTemplate(
|
|
275
|
-
configDirectory: string,
|
|
276
|
-
templateFile: string,
|
|
277
|
-
destinationOptions: string[],
|
|
278
|
-
dsn: string,
|
|
279
|
-
): Promise<void> {
|
|
280
|
-
const templatePath = path.join(configDirectory, templateFile);
|
|
281
|
-
|
|
282
|
-
for (const destinationDir of destinationOptions) {
|
|
283
|
-
if (!fs.existsSync(destinationDir)) {
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
const destinationPath = path.join(destinationDir, templateFile);
|
|
287
|
-
// in case the file in question already exists, we'll make a copy with
|
|
288
|
-
// `MERGEABLE_CONFIG_INFIX` inserted just before the extension, so as not
|
|
289
|
-
// to overwrite the existing file
|
|
290
|
-
const mergeableFilePath = path.join(
|
|
291
|
-
destinationDir,
|
|
292
|
-
this._spliceInPlace(
|
|
293
|
-
templateFile.split('.'),
|
|
294
|
-
-1,
|
|
295
|
-
0,
|
|
296
|
-
MERGEABLE_CONFIG_INFIX,
|
|
297
|
-
).join('.'),
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
if (templateFile === 'next.config.js') {
|
|
301
|
-
await this._mergeNextConfig(
|
|
302
|
-
destinationPath,
|
|
303
|
-
templatePath,
|
|
304
|
-
destinationDir,
|
|
305
|
-
templateFile,
|
|
306
|
-
configDirectory,
|
|
307
|
-
mergeableFilePath,
|
|
308
|
-
);
|
|
309
|
-
return;
|
|
310
|
-
} else {
|
|
311
|
-
if (!fs.existsSync(destinationPath)) {
|
|
312
|
-
this._fillAndCopyTemplate(templatePath, destinationPath, dsn);
|
|
313
|
-
} else if (!fs.existsSync(mergeableFilePath)) {
|
|
314
|
-
this._fillAndCopyTemplate(templatePath, mergeableFilePath, dsn);
|
|
315
|
-
red(
|
|
316
|
-
`File \`${templateFile}\` already exists, so created \`${mergeableFilePath}\`.\n` +
|
|
317
|
-
'Please merge those files.',
|
|
318
|
-
);
|
|
319
|
-
nl();
|
|
320
|
-
} else {
|
|
321
|
-
red(
|
|
322
|
-
`Both \`${templateFile}\` and \`${mergeableFilePath}\` already exist.\n` +
|
|
323
|
-
'Please merge those files.',
|
|
324
|
-
);
|
|
325
|
-
nl();
|
|
326
|
-
}
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
red(
|
|
332
|
-
`Could not find appropriate destination for \`${templateFile}\`. Tried: ${destinationOptions}.`,
|
|
333
|
-
);
|
|
334
|
-
nl();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
private _fillAndCopyTemplate(
|
|
338
|
-
sourcePath: string,
|
|
339
|
-
targetPath: string,
|
|
340
|
-
dsn: string,
|
|
341
|
-
): void {
|
|
342
|
-
const templateContent = fs.readFileSync(sourcePath).toString();
|
|
343
|
-
const filledTemplate = templateContent.replace('___DSN___', dsn);
|
|
344
|
-
fs.writeFileSync(targetPath, filledTemplate);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
private _hasPackageInstalled(packageName: string): boolean {
|
|
348
|
-
const depsVersion = _.get(appPackage, ['dependencies', packageName]);
|
|
349
|
-
const devDepsVersion = _.get(appPackage, ['devDependencies', packageName]);
|
|
350
|
-
return !!depsVersion || !!devDepsVersion;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private _spliceInPlace(
|
|
354
|
-
arr: Array<any>,
|
|
355
|
-
start: number,
|
|
356
|
-
deleteCount: number,
|
|
357
|
-
...inserts: any[]
|
|
358
|
-
): Array<any> {
|
|
359
|
-
arr.splice(start, deleteCount, ...inserts);
|
|
360
|
-
return arr;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
private async _mergeNextConfig(
|
|
364
|
-
destinationPath: string,
|
|
365
|
-
templatePath: string,
|
|
366
|
-
destinationDir: string,
|
|
367
|
-
templateFile: string,
|
|
368
|
-
configDirectory: string,
|
|
369
|
-
mergeableFilePath: string,
|
|
370
|
-
): Promise<void> {
|
|
371
|
-
// if no next.config.js exists, we'll create one
|
|
372
|
-
if (!fs.existsSync(destinationPath)) {
|
|
373
|
-
fs.copyFileSync(templatePath, destinationPath);
|
|
374
|
-
green('Created File `next.config.js`');
|
|
375
|
-
nl();
|
|
376
|
-
} else {
|
|
377
|
-
// creates a file name for the copy of the original next.config.js file
|
|
378
|
-
// with the name `next.config.original.js`
|
|
379
|
-
const originalFileName = this._spliceInPlace(
|
|
380
|
-
templateFile.split('.'),
|
|
381
|
-
-1,
|
|
382
|
-
0,
|
|
383
|
-
'original',
|
|
384
|
-
).join('.');
|
|
385
|
-
const originalFilePath = path.join(destinationDir, originalFileName);
|
|
386
|
-
// makes copy of original next.config.js
|
|
387
|
-
fs.writeFileSync(originalFilePath, fs.readFileSync(destinationPath));
|
|
388
|
-
await this._addToGitignore(
|
|
389
|
-
originalFilePath,
|
|
390
|
-
'Unable to add next.config.original.js to gitignore',
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
const mergedTemplatePath = path.join(
|
|
394
|
-
configDirectory,
|
|
395
|
-
'next.config.template.js',
|
|
396
|
-
);
|
|
397
|
-
// attempts to merge with existing next.config.js, if true -> success
|
|
398
|
-
if (mergeConfigFile(destinationPath, mergedTemplatePath)) {
|
|
399
|
-
green(
|
|
400
|
-
`Updated \`${templateFile}\` with Sentry. The original ${templateFile} was saved as \`next.config.original.js\`.\n` +
|
|
401
|
-
'Information on the changes made to the Next.js configuration file an be found at https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/',
|
|
402
|
-
);
|
|
403
|
-
nl();
|
|
404
|
-
} else {
|
|
405
|
-
// if merge fails, we'll create a copy of the `next.config.js` template and ask them to merge
|
|
406
|
-
fs.copyFileSync(templatePath, mergeableFilePath);
|
|
407
|
-
await this._addToGitignore(
|
|
408
|
-
mergeableFilePath,
|
|
409
|
-
'Unable to add next.config.wizard.js template to gitignore',
|
|
410
|
-
);
|
|
411
|
-
red(
|
|
412
|
-
`Unable to merge \`${templateFile}\`, so created \`${mergeableFilePath}\`.\n` +
|
|
413
|
-
'Please integrate next.config.wizardcopy.js into your next.config.js or next.config.ts file',
|
|
414
|
-
);
|
|
415
|
-
nl();
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
29
|
}
|
|
@@ -14,11 +14,13 @@ import {
|
|
|
14
14
|
matchFiles,
|
|
15
15
|
patchMatchingFile,
|
|
16
16
|
} from '../../Helper/File';
|
|
17
|
-
import { dim, green, nl, red } from '../../Helper/Logging';
|
|
17
|
+
import { dim, green, l, nl, red } from '../../Helper/Logging';
|
|
18
18
|
import { checkPackageVersion } from '../../Helper/Package';
|
|
19
|
-
import {
|
|
19
|
+
import { getPackageManagerChoice } from '../../Helper/PackageManager';
|
|
20
20
|
import { SentryCli } from '../../Helper/SentryCli';
|
|
21
21
|
import { MobileProject } from './MobileProject';
|
|
22
|
+
import { BottomBar } from '../../Helper/BottomBar';
|
|
23
|
+
import { URL } from 'url';
|
|
22
24
|
|
|
23
25
|
const xcode = require('xcode');
|
|
24
26
|
|
|
@@ -37,11 +39,15 @@ export class ReactNative extends MobileProject {
|
|
|
37
39
|
*/
|
|
38
40
|
private static _buildGradleAndroidSectionBeginning = /^android {/m;
|
|
39
41
|
|
|
42
|
+
private url: string | undefined;
|
|
43
|
+
|
|
40
44
|
protected _answers: Answers;
|
|
41
45
|
protected _sentryCli: SentryCli;
|
|
42
46
|
|
|
47
|
+
|
|
43
48
|
public constructor(protected _argv: Args) {
|
|
44
49
|
super(_argv);
|
|
50
|
+
this.url = _argv.url;
|
|
45
51
|
this._sentryCli = new SentryCli(this._argv);
|
|
46
52
|
}
|
|
47
53
|
|
|
@@ -55,7 +61,7 @@ export class ReactNative extends MobileProject {
|
|
|
55
61
|
nl();
|
|
56
62
|
|
|
57
63
|
let userAnswers: Answers = { continue: true };
|
|
58
|
-
const packageManager =
|
|
64
|
+
const packageManager = getPackageManagerChoice();
|
|
59
65
|
|
|
60
66
|
const hasCompatibleReactNativeVersion = checkPackageVersion(
|
|
61
67
|
this._readAppPackage(),
|
|
@@ -79,7 +85,10 @@ export class ReactNative extends MobileProject {
|
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
if (packageManager) {
|
|
88
|
+
BottomBar.show(`Adding ${SENTRY_REACT_NATIVE_PACKAGE}...`);
|
|
82
89
|
await packageManager.installPackage(SENTRY_REACT_NATIVE_PACKAGE);
|
|
90
|
+
BottomBar.hide();
|
|
91
|
+
green(`✓ Added \`${SENTRY_REACT_NATIVE_PACKAGE}\``);
|
|
83
92
|
}
|
|
84
93
|
const hasCompatibleSentryReactNativeVersion = checkPackageVersion(
|
|
85
94
|
this._readAppPackage(),
|
|
@@ -115,7 +124,9 @@ export class ReactNative extends MobileProject {
|
|
|
115
124
|
this._patchXcodeProj.bind(this),
|
|
116
125
|
);
|
|
117
126
|
green('✓ Patched build script in Xcode project.');
|
|
127
|
+
BottomBar.show('Adding Sentry pods...');
|
|
118
128
|
await this._podInstall();
|
|
129
|
+
BottomBar.hide();
|
|
119
130
|
green('✓ Pods installed.');
|
|
120
131
|
} else {
|
|
121
132
|
await patchMatchingFile(
|
|
@@ -135,6 +146,41 @@ export class ReactNative extends MobileProject {
|
|
|
135
146
|
|
|
136
147
|
await Promise.all(promises);
|
|
137
148
|
|
|
149
|
+
let host: string | null = null
|
|
150
|
+
try {
|
|
151
|
+
host = (new URL(this.url || '')).host;
|
|
152
|
+
} catch (_error) {
|
|
153
|
+
// ignore
|
|
154
|
+
}
|
|
155
|
+
const orgSlug = _.get(answers, 'config.organization.slug', null);
|
|
156
|
+
const projectId = _.get(answers, 'config.project.id', null);
|
|
157
|
+
const projectIssuesUrl = host && orgSlug && projectId
|
|
158
|
+
? `https://${orgSlug}.${host}/issues/?project=${projectId}`
|
|
159
|
+
: null;
|
|
160
|
+
|
|
161
|
+
l(`
|
|
162
|
+
To make sure everything is set up correctly, put the following code snippet into your application.
|
|
163
|
+
The snippet will create a button that, when tapped, sends a test event to Sentry.
|
|
164
|
+
`);
|
|
165
|
+
|
|
166
|
+
if (projectIssuesUrl) {
|
|
167
|
+
l(`After that check your project issues:`);
|
|
168
|
+
l(projectIssuesUrl);
|
|
169
|
+
nl();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
l(`<Button title='Try!' onPress={ () => { Sentry.captureException(new Error('First error')) }}/>`);
|
|
173
|
+
nl();
|
|
174
|
+
|
|
175
|
+
if (!this._argv.quiet) {
|
|
176
|
+
await prompt({
|
|
177
|
+
message: 'Have you successfully sent a test event?',
|
|
178
|
+
name: 'snippet',
|
|
179
|
+
default: true,
|
|
180
|
+
type: 'confirm',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
138
184
|
return answers;
|
|
139
185
|
}
|
|
140
186
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Answers } from 'inquirer';
|
|
2
|
+
import { runSvelteKitWizard } from '../../../src/sveltekit/sveltekit-wizard';
|
|
3
|
+
|
|
4
|
+
import type { Args } from '../../Constants';
|
|
5
|
+
import { BaseIntegration } from './BaseIntegration';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This class just redirects to the new `sveltekit-wizard.ts` flow
|
|
9
|
+
* for anyone calling the wizard without the '-i sveltekit' flag.
|
|
10
|
+
*/
|
|
11
|
+
export class SvelteKit extends BaseIntegration {
|
|
12
|
+
public constructor(protected _argv: Args) {
|
|
13
|
+
super(_argv);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public async emit(_answers: Answers): Promise<Answers> {
|
|
17
|
+
await runSvelteKitWizard({ promoCode: this._argv.promoCode });
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public async shouldConfigure(_answers: Answers): Promise<Answers> {
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
23
|
+
if (this._shouldConfigure) {
|
|
24
|
+
return this._shouldConfigure;
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
27
|
+
return this.shouldConfigure;
|
|
28
|
+
}
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
|
-
|
|
2
|
+
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
3
|
+
import clack from '@clack/prompts';
|
|
3
4
|
import chalk from 'chalk';
|
|
4
5
|
import * as fs from 'fs';
|
|
6
|
+
// @ts-ignore - magicast is ESM and TS complains about that. It works though
|
|
5
7
|
import { builders, generateCode, parseModule } from 'magicast';
|
|
6
8
|
import * as path from 'path';
|
|
7
9
|
|
|
@@ -12,10 +14,12 @@ import {
|
|
|
12
14
|
askForSelfHosted,
|
|
13
15
|
askForWizardLogin,
|
|
14
16
|
confirmContinueEvenThoughNoGitRepo,
|
|
17
|
+
ensurePackageIsInstalled,
|
|
18
|
+
getPackageDotJson,
|
|
15
19
|
installPackage,
|
|
16
20
|
printWelcome,
|
|
17
21
|
SentryProjectData,
|
|
18
|
-
} from '
|
|
22
|
+
} from '../utils/clack-utils';
|
|
19
23
|
import {
|
|
20
24
|
getNextjsConfigCjsAppendix,
|
|
21
25
|
getNextjsConfigCjsTemplate,
|
|
@@ -25,7 +29,7 @@ import {
|
|
|
25
29
|
getSentryConfigContents,
|
|
26
30
|
getSentryExampleApiRoute,
|
|
27
31
|
getSentryExamplePageContents,
|
|
28
|
-
} from '
|
|
32
|
+
} from '../templates/nextjs-templates';
|
|
29
33
|
|
|
30
34
|
interface NextjsWizardOptions {
|
|
31
35
|
promoCode?: string;
|
|
@@ -42,43 +46,8 @@ export async function runNextjsWizard(
|
|
|
42
46
|
|
|
43
47
|
await confirmContinueEvenThoughNoGitRepo();
|
|
44
48
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
.catch(() => {
|
|
48
|
-
clack.log.error(
|
|
49
|
-
'Could not find package.json. Make sure to run the wizard in the root of your Next.js app!',
|
|
50
|
-
);
|
|
51
|
-
abort();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
let packageJson:
|
|
55
|
-
| { dependencies?: { ['@sentry/nextjs']: string; ['next']: string } }
|
|
56
|
-
| undefined = undefined;
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
60
|
-
packageJson = JSON.parse(packageJsonFileContents);
|
|
61
|
-
} catch (e) {
|
|
62
|
-
clack.log.error(
|
|
63
|
-
'Unable to parse your package.json. Make sure it has a valid format!',
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
abort();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!packageJson?.dependencies?.['next']) {
|
|
70
|
-
const continueWithoutNext = await clack.confirm({
|
|
71
|
-
message:
|
|
72
|
-
'Next.js does not seem to be installed. Do you still want to continue?',
|
|
73
|
-
initialValue: false,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
abortIfCancelled(continueWithoutNext);
|
|
77
|
-
|
|
78
|
-
if (!continueWithoutNext) {
|
|
79
|
-
abort();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
49
|
+
const packageJson = await getPackageDotJson();
|
|
50
|
+
await ensurePackageIsInstalled(packageJson, 'next', 'Next.js');
|
|
82
51
|
|
|
83
52
|
const { url: sentryUrl, selfHosted } = await askForSelfHosted();
|
|
84
53
|
|
|
@@ -89,7 +58,7 @@ export async function runNextjsWizard(
|
|
|
89
58
|
|
|
90
59
|
const selectedProject: SentryProjectData | symbol = await clack.select({
|
|
91
60
|
message: 'Select your Sentry project.',
|
|
92
|
-
options: projects.map(project => {
|
|
61
|
+
options: projects.map((project) => {
|
|
93
62
|
return {
|
|
94
63
|
value: project,
|
|
95
64
|
label: `${project.organization.slug}/${project.slug}`,
|
|
@@ -109,7 +78,7 @@ export async function runNextjsWizard(
|
|
|
109
78
|
isUsingTypescript = fs.existsSync(
|
|
110
79
|
path.join(process.cwd(), 'tsconfig.json'),
|
|
111
80
|
);
|
|
112
|
-
} catch
|
|
81
|
+
} catch {
|
|
113
82
|
// noop - Default to assuming user is not using typescript
|
|
114
83
|
}
|
|
115
84
|
|
|
@@ -218,7 +187,7 @@ export async function runNextjsWizard(
|
|
|
218
187
|
if (probablyIncludesSdk) {
|
|
219
188
|
const injectAnyhow = await clack.confirm({
|
|
220
189
|
message: `${chalk.bold(
|
|
221
|
-
|
|
190
|
+
nextConfigJs,
|
|
222
191
|
)} already contains Sentry SDK configuration. Should the wizard modify it anyways?`,
|
|
223
192
|
});
|
|
224
193
|
|
|
@@ -300,7 +269,7 @@ export async function runNextjsWizard(
|
|
|
300
269
|
)}. ${chalk.dim('(you probably want to clean this up a bit!)')}`,
|
|
301
270
|
);
|
|
302
271
|
}
|
|
303
|
-
} catch
|
|
272
|
+
} catch {
|
|
304
273
|
clack.log.warn(
|
|
305
274
|
chalk.yellow(
|
|
306
275
|
`Something went wrong writing to ${chalk.bold(nextConfigMjs)}`,
|