@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
|
@@ -1,9 +1,12 @@
|
|
|
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
|
|
|
5
|
+
import * as Sentry from '@sentry/node';
|
|
6
|
+
|
|
5
7
|
import {
|
|
6
8
|
abort,
|
|
9
|
+
abortIfCancelled,
|
|
7
10
|
addSentryCliConfig,
|
|
8
11
|
confirmContinueEvenThoughNoGitRepo,
|
|
9
12
|
ensurePackageIsInstalled,
|
|
@@ -12,46 +15,105 @@ import {
|
|
|
12
15
|
installPackage,
|
|
13
16
|
printWelcome,
|
|
14
17
|
} from '../utils/clack-utils';
|
|
15
|
-
import { hasPackageInstalled } from '../utils/package-json';
|
|
18
|
+
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
|
|
16
19
|
import { WizardOptions } from '../utils/types';
|
|
17
20
|
import { createExamplePage } from './sdk-example';
|
|
18
21
|
import { createOrMergeSvelteKitFiles, loadSvelteConfig } from './sdk-setup';
|
|
22
|
+
import { traceStep, withTelemetry } from '../telemetry';
|
|
23
|
+
import { getKitVersionBucket, getSvelteVersionBucket } from './utils';
|
|
19
24
|
|
|
20
25
|
export async function runSvelteKitWizard(
|
|
21
26
|
options: WizardOptions,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
return withTelemetry(
|
|
29
|
+
{
|
|
30
|
+
enabled: options.telemetryEnabled,
|
|
31
|
+
integration: 'sveltekit',
|
|
32
|
+
},
|
|
33
|
+
() => runSvelteKitWizardWithTelemetry(options),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runSvelteKitWizardWithTelemetry(
|
|
38
|
+
options: WizardOptions,
|
|
22
39
|
): Promise<void> {
|
|
23
40
|
printWelcome({
|
|
24
41
|
wizardName: 'Sentry SvelteKit Wizard',
|
|
25
42
|
promoCode: options.promoCode,
|
|
43
|
+
telemetryEnabled: options.telemetryEnabled,
|
|
26
44
|
});
|
|
27
45
|
|
|
28
|
-
await confirmContinueEvenThoughNoGitRepo
|
|
46
|
+
await traceStep('detect-git', confirmContinueEvenThoughNoGitRepo);
|
|
29
47
|
|
|
30
48
|
const packageJson = await getPackageDotJson();
|
|
31
|
-
await
|
|
49
|
+
await traceStep('detect-framework-version', () =>
|
|
50
|
+
ensurePackageIsInstalled(packageJson, '@sveltejs/kit', 'Sveltekit'),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const kitVersion = getPackageVersion('@sveltejs/kit', packageJson);
|
|
54
|
+
const kitVersionBucket = getKitVersionBucket(kitVersion);
|
|
55
|
+
Sentry.setTag('sveltekit-version', kitVersionBucket);
|
|
56
|
+
|
|
57
|
+
if (kitVersionBucket === '0.x') {
|
|
58
|
+
clack.log.warn(
|
|
59
|
+
"It seems you're using a SvelteKit version <1.0.0 which is not supported by Sentry.\nWe recommend upgrading to the latest 1.x version before you continue.",
|
|
60
|
+
);
|
|
61
|
+
const shouldContinue = await abortIfCancelled(
|
|
62
|
+
clack.select({
|
|
63
|
+
message: 'Do you want to continue anyway?',
|
|
64
|
+
options: [
|
|
65
|
+
{
|
|
66
|
+
label: 'Yes, continue',
|
|
67
|
+
hint: 'The SDK might not work correctly',
|
|
68
|
+
value: true,
|
|
69
|
+
},
|
|
70
|
+
{ label: "No, I'll upgrade first", value: false },
|
|
71
|
+
],
|
|
72
|
+
}),
|
|
73
|
+
);
|
|
74
|
+
if (!shouldContinue) {
|
|
75
|
+
await abort('Exiting Wizard', 0);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Sentry.setTag(
|
|
81
|
+
'svelte-version',
|
|
82
|
+
getSvelteVersionBucket(getPackageVersion('svelte', packageJson)),
|
|
83
|
+
);
|
|
32
84
|
|
|
33
85
|
const { selectedProject, selfHosted, sentryUrl, authToken } =
|
|
34
86
|
await getOrAskForProjectData(options, 'javascript-sveltekit');
|
|
35
87
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
88
|
+
const sdkAlreadyInstalled = hasPackageInstalled(
|
|
89
|
+
'@sentry/sveltekit',
|
|
90
|
+
packageJson,
|
|
91
|
+
);
|
|
92
|
+
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);
|
|
93
|
+
|
|
94
|
+
await traceStep('install-sdk', () =>
|
|
95
|
+
installPackage({
|
|
96
|
+
packageName: '@sentry/sveltekit',
|
|
97
|
+
alreadyInstalled: sdkAlreadyInstalled,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
40
100
|
|
|
41
|
-
await addSentryCliConfig(authToken);
|
|
101
|
+
await traceStep('add-cli-config', () => addSentryCliConfig(authToken));
|
|
42
102
|
|
|
43
|
-
const svelteConfig = await loadSvelteConfig
|
|
103
|
+
const svelteConfig = await traceStep('load-svelte-config', loadSvelteConfig);
|
|
44
104
|
|
|
45
105
|
try {
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
106
|
+
await traceStep('configure-sdk', () =>
|
|
107
|
+
createOrMergeSvelteKitFiles(
|
|
108
|
+
{
|
|
109
|
+
dsn: selectedProject.keys[0].dsn.public,
|
|
110
|
+
org: selectedProject.organization.slug,
|
|
111
|
+
project: selectedProject.slug,
|
|
112
|
+
selfHosted,
|
|
113
|
+
url: sentryUrl,
|
|
114
|
+
},
|
|
115
|
+
svelteConfig,
|
|
116
|
+
),
|
|
55
117
|
);
|
|
56
118
|
} catch (e: unknown) {
|
|
57
119
|
clack.log.error('Error while setting up the SvelteKit SDK:');
|
|
@@ -64,17 +126,20 @@ export async function runSvelteKitWizard(
|
|
|
64
126
|
: 'Unknown error',
|
|
65
127
|
),
|
|
66
128
|
);
|
|
129
|
+
Sentry.captureException('Error while setting up the SvelteKit SDK');
|
|
67
130
|
await abort('Exiting Wizard');
|
|
68
131
|
return;
|
|
69
132
|
}
|
|
70
133
|
|
|
71
134
|
try {
|
|
72
|
-
await
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
135
|
+
await traceStep('create-example-page', () =>
|
|
136
|
+
createExamplePage(svelteConfig, {
|
|
137
|
+
selfHosted,
|
|
138
|
+
url: sentryUrl,
|
|
139
|
+
orgSlug: selectedProject.organization.slug,
|
|
140
|
+
projectId: selectedProject.id,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
78
143
|
} catch (e: unknown) {
|
|
79
144
|
clack.log.error('Error while creating an example page to test Sentry:');
|
|
80
145
|
clack.log.info(
|
|
@@ -86,6 +151,9 @@ export async function runSvelteKitWizard(
|
|
|
86
151
|
: 'Unknown error',
|
|
87
152
|
),
|
|
88
153
|
);
|
|
154
|
+
Sentry.captureException(
|
|
155
|
+
'Error while creating an example Svelte page to test Sentry',
|
|
156
|
+
);
|
|
89
157
|
await abort('Exiting Wizard');
|
|
90
158
|
return;
|
|
91
159
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { lt, minVersion } from 'semver';
|
|
2
|
+
|
|
3
|
+
export function getKitVersionBucket(
|
|
4
|
+
version: string | undefined,
|
|
5
|
+
): 'none' | 'invalid' | '0.x' | '>=1.0.0 <1.24.0' | '>=1.24.0' {
|
|
6
|
+
if (!version) {
|
|
7
|
+
return 'none';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const minVer = minVersion(version);
|
|
11
|
+
if (!minVer) {
|
|
12
|
+
return 'invalid';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (lt(minVer, '1.0.0')) {
|
|
16
|
+
return '0.x';
|
|
17
|
+
} else if (lt(minVer, '1.24.0')) {
|
|
18
|
+
return '>=1.0.0 <1.24.0';
|
|
19
|
+
} else {
|
|
20
|
+
// This is the version when the client-side invalidation fix was released
|
|
21
|
+
// https://github.com/sveltejs/kit/releases/tag/%40sveltejs%2Fkit%401.24.0
|
|
22
|
+
// https://github.com/sveltejs/kit/pull/10576
|
|
23
|
+
return '>=1.24.0';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getSvelteVersionBucket(
|
|
28
|
+
version: string | undefined,
|
|
29
|
+
): 'none' | 'invalid' | '<3.0.0' | '3.x' | '4.x' | '>4.x' {
|
|
30
|
+
if (!version) {
|
|
31
|
+
return 'none';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const minVer = minVersion(version);
|
|
35
|
+
if (!minVer) {
|
|
36
|
+
return 'invalid';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (lt(minVer, '3.0.0')) {
|
|
40
|
+
return '<3.0.0';
|
|
41
|
+
}
|
|
42
|
+
if (lt(minVer, '4.0.0')) {
|
|
43
|
+
return '3.x';
|
|
44
|
+
}
|
|
45
|
+
if (lt(minVer, '5.0.0')) {
|
|
46
|
+
return '4.x';
|
|
47
|
+
}
|
|
48
|
+
// Svelte 5 isn't released yet but it's being worked on
|
|
49
|
+
return '>4.x';
|
|
50
|
+
}
|
package/src/utils/ast-utils.ts
CHANGED
|
@@ -4,6 +4,8 @@ import * as recast from 'recast';
|
|
|
4
4
|
import x = recast.types;
|
|
5
5
|
import t = x.namedTypes;
|
|
6
6
|
|
|
7
|
+
const b = recast.types.builders;
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Checks if a file where we don't know its concrete file type yet exists
|
|
9
11
|
* and returns the full path to the file with the correct file type.
|
|
@@ -36,3 +38,181 @@ export function hasSentryContent(program: t.Program): boolean {
|
|
|
36
38
|
|
|
37
39
|
return !!foundSentry;
|
|
38
40
|
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Searches for a property of an ObjectExpression by name
|
|
44
|
+
*
|
|
45
|
+
* @param object the ObjectExpression to search in
|
|
46
|
+
* @param name the name of the property to search for
|
|
47
|
+
*
|
|
48
|
+
* @returns the property if it exists
|
|
49
|
+
*/
|
|
50
|
+
export function getObjectProperty(
|
|
51
|
+
object: t.ObjectExpression,
|
|
52
|
+
name: string,
|
|
53
|
+
): t.Property | undefined {
|
|
54
|
+
return object.properties.find((p): p is t.Property => {
|
|
55
|
+
const isObjectProp = p.type === 'Property' || p.type === 'ObjectProperty';
|
|
56
|
+
|
|
57
|
+
if (!isObjectProp) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const hasMatchingLiteralKey =
|
|
62
|
+
isObjectProp &&
|
|
63
|
+
(p.key.type === 'Literal' || p.key.type === 'StringLiteral') &&
|
|
64
|
+
p.key.value === name;
|
|
65
|
+
|
|
66
|
+
if (hasMatchingLiteralKey) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// has matching identifier key
|
|
71
|
+
return isObjectProp && p.key.type === 'Identifier' && p.key.name === name;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Attempts to find a property of an ObjectExpression by name. If it doesn't exist,
|
|
77
|
+
* the property will be added to the ObjectExpression with the provided default value.
|
|
78
|
+
*
|
|
79
|
+
* @param object the parent object expression to search in
|
|
80
|
+
* @param name the name of the property to search for
|
|
81
|
+
* @param defaultValue the default value to set if the property doesn't exist
|
|
82
|
+
*
|
|
83
|
+
* @returns the
|
|
84
|
+
*/
|
|
85
|
+
export function getOrSetObjectProperty(
|
|
86
|
+
object: t.ObjectExpression,
|
|
87
|
+
name: string,
|
|
88
|
+
defaultValue:
|
|
89
|
+
| t.Literal
|
|
90
|
+
| t.BooleanLiteral
|
|
91
|
+
| t.StringLiteral
|
|
92
|
+
| t.ObjectExpression,
|
|
93
|
+
): t.Property {
|
|
94
|
+
const existingProperty = getObjectProperty(object, name);
|
|
95
|
+
|
|
96
|
+
if (existingProperty) {
|
|
97
|
+
return existingProperty;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const newProperty = b.property.from({
|
|
101
|
+
kind: 'init',
|
|
102
|
+
key: b.stringLiteral(name),
|
|
103
|
+
value: defaultValue,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
object.properties.push(newProperty);
|
|
107
|
+
|
|
108
|
+
return newProperty;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Sets a property of an ObjectExpression if it exists, otherwise adds it
|
|
113
|
+
* to the ObjectExpression. Optionally, a comment can be added to the
|
|
114
|
+
* property.
|
|
115
|
+
*
|
|
116
|
+
* @param object the ObjectExpression to set the property on
|
|
117
|
+
* @param name the name of the property to set
|
|
118
|
+
* @param value the value of the property to set
|
|
119
|
+
* @param comment (optional) a comment to add to the property
|
|
120
|
+
*/
|
|
121
|
+
export function setOrUpdateObjectProperty(
|
|
122
|
+
object: t.ObjectExpression,
|
|
123
|
+
name: string,
|
|
124
|
+
value: t.Literal | t.BooleanLiteral | t.StringLiteral | t.ObjectExpression,
|
|
125
|
+
comment?: string,
|
|
126
|
+
) {
|
|
127
|
+
const newComments =
|
|
128
|
+
comment &&
|
|
129
|
+
comment.split('\n').map((c) => b.commentLine(` ${c}`, true, false));
|
|
130
|
+
|
|
131
|
+
const existingProperty = getObjectProperty(object, name);
|
|
132
|
+
|
|
133
|
+
if (existingProperty) {
|
|
134
|
+
existingProperty.value = value;
|
|
135
|
+
if (newComments) {
|
|
136
|
+
existingProperty.comments = [
|
|
137
|
+
...(existingProperty?.comments || []),
|
|
138
|
+
...newComments,
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
object.properties.push(
|
|
143
|
+
b.objectProperty.from({
|
|
144
|
+
key: b.stringLiteral(name),
|
|
145
|
+
value,
|
|
146
|
+
...(newComments && {
|
|
147
|
+
comments: newComments,
|
|
148
|
+
}),
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
type JsonCParseResult =
|
|
155
|
+
| {
|
|
156
|
+
jsonObject: t.ObjectExpression;
|
|
157
|
+
ast: t.Program;
|
|
158
|
+
}
|
|
159
|
+
| {
|
|
160
|
+
jsonObject: undefined;
|
|
161
|
+
ast: undefined;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Parses a JSON string with (potential) comments (JSON-C) and returns the JS AST
|
|
166
|
+
* that can be walked and modified with recast like a normal JS AST.
|
|
167
|
+
*
|
|
168
|
+
* This is done by wrapping the JSON-C string in parentheses, thereby making it
|
|
169
|
+
* a JS `Program` with an `ExpressionStatement` as its body. The expression is then
|
|
170
|
+
* extracted from the AST and returned alongside the AST.
|
|
171
|
+
*
|
|
172
|
+
* To preserve as much original formatting as possible, the returned `ast`
|
|
173
|
+
* property should be passed to {@link `printJsonC`} to get the JSON-C string back.
|
|
174
|
+
*
|
|
175
|
+
* If the input is not valid JSON-C, the result will be undefined.
|
|
176
|
+
*
|
|
177
|
+
* @see {@link JsonCParseResult}
|
|
178
|
+
*
|
|
179
|
+
* @param jsonString a JSON-C string
|
|
180
|
+
*
|
|
181
|
+
* @returns a {@link JsonCParseResult}, containing either the JSON-C object and the AST or undefined in both cases
|
|
182
|
+
*/
|
|
183
|
+
export function parseJsonC(jsonString: string): JsonCParseResult {
|
|
184
|
+
try {
|
|
185
|
+
const jsTsConfig = `(${jsonString})`;
|
|
186
|
+
// no idea why recast returns any here, this is dumb :/
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
188
|
+
const ast = recast.parse(jsTsConfig.toString()).program as t.Program;
|
|
189
|
+
|
|
190
|
+
const jsonObject =
|
|
191
|
+
(ast.body[0].type === 'ExpressionStatement' &&
|
|
192
|
+
ast.body[0].expression.type === 'ObjectExpression' &&
|
|
193
|
+
ast.body[0].expression) ||
|
|
194
|
+
undefined;
|
|
195
|
+
|
|
196
|
+
if (jsonObject) {
|
|
197
|
+
return { jsonObject, ast };
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
/* empty */
|
|
201
|
+
}
|
|
202
|
+
return { jsonObject: undefined, ast: undefined };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Takes the AST of a parsed JSON-C "program" and returns the JSON-C string without
|
|
207
|
+
* any of the temporary JS wrapper code that was previously applied.
|
|
208
|
+
*
|
|
209
|
+
* Only use this in conjunction with {@link `parseJsonC`}
|
|
210
|
+
*
|
|
211
|
+
* @param ast the `ast` returned from {@link `parseJsonC`}
|
|
212
|
+
*
|
|
213
|
+
* @returns the JSON-C string
|
|
214
|
+
*/
|
|
215
|
+
export function printJsonC(ast: t.Program): string {
|
|
216
|
+
const js = recast.print(ast).code;
|
|
217
|
+
return js.substring(1, js.length - 1);
|
|
218
|
+
}
|
package/src/utils/clack-utils.ts
CHANGED
|
@@ -125,18 +125,23 @@ export function printWelcome(options: {
|
|
|
125
125
|
|
|
126
126
|
let welcomeText =
|
|
127
127
|
options.message ||
|
|
128
|
-
|
|
128
|
+
`The ${options.wizardName} will help you set up Sentry for your application.\nThank you for using Sentry :)`;
|
|
129
129
|
|
|
130
130
|
if (options.promoCode) {
|
|
131
|
-
welcomeText
|
|
131
|
+
welcomeText = `${welcomeText}\n\nUsing promo-code: ${options.promoCode}`;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
if (wizardPackage.version) {
|
|
135
|
-
welcomeText
|
|
135
|
+
welcomeText = `${welcomeText}\n\nVersion: ${wizardPackage.version}`;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (options.telemetryEnabled) {
|
|
139
|
-
welcomeText
|
|
139
|
+
welcomeText = `${welcomeText}
|
|
140
|
+
|
|
141
|
+
This wizard sends telemetry data and crash reports to Sentry. This helps us improve the Wizard.
|
|
142
|
+
You can turn this off at any time by running ${chalk.cyanBright(
|
|
143
|
+
'sentry-wizard --disable-telemetry',
|
|
144
|
+
)}.`;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
clack.note(welcomeText);
|
|
@@ -248,43 +253,9 @@ export async function installPackage({
|
|
|
248
253
|
);
|
|
249
254
|
}
|
|
250
255
|
|
|
251
|
-
async function addOrgAndProjectToSentryCliRc(
|
|
252
|
-
org: string,
|
|
253
|
-
project: string,
|
|
254
|
-
setupConfig: CliSetupConfig,
|
|
255
|
-
): Promise<void> {
|
|
256
|
-
const configContents = fs.readFileSync(
|
|
257
|
-
path.join(process.cwd(), setupConfig.filename),
|
|
258
|
-
'utf8',
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
if (setupConfig.likelyAlreadyHasOrgAndProject(configContents)) {
|
|
262
|
-
clack.log.warn(
|
|
263
|
-
`${chalk.bold(
|
|
264
|
-
setupConfig.filename,
|
|
265
|
-
)} already has org and project. Will not add them.`,
|
|
266
|
-
);
|
|
267
|
-
} else {
|
|
268
|
-
try {
|
|
269
|
-
await fs.promises.appendFile(
|
|
270
|
-
path.join(process.cwd(), setupConfig.filename),
|
|
271
|
-
`\n${setupConfig.orgAndProjContent(org, project)}\n`,
|
|
272
|
-
);
|
|
273
|
-
} catch (e) {
|
|
274
|
-
clack.log.warn(
|
|
275
|
-
`${chalk.bold(
|
|
276
|
-
setupConfig.filename,
|
|
277
|
-
)} could not be updated with org and project.`,
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
256
|
export async function addSentryCliConfig(
|
|
284
257
|
authToken: string,
|
|
285
258
|
setupConfig: CliSetupConfig = sourceMapsCliSetupConfig,
|
|
286
|
-
orgSlug?: string,
|
|
287
|
-
projectSlug?: string,
|
|
288
259
|
): Promise<void> {
|
|
289
260
|
const configExists = fs.existsSync(
|
|
290
261
|
path.join(process.cwd(), setupConfig.filename),
|
|
@@ -352,10 +323,6 @@ export async function addSentryCliConfig(
|
|
|
352
323
|
}
|
|
353
324
|
}
|
|
354
325
|
|
|
355
|
-
if (orgSlug && projectSlug) {
|
|
356
|
-
await addOrgAndProjectToSentryCliRc(orgSlug, projectSlug, setupConfig);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
326
|
await addAuthTokenFileToGitIgnore(setupConfig.filename);
|
|
360
327
|
}
|
|
361
328
|
|
|
@@ -457,6 +424,7 @@ export async function ensurePackageIsInstalled(
|
|
|
457
424
|
packageName: string,
|
|
458
425
|
) {
|
|
459
426
|
if (!hasPackageInstalled(packageId, packageJson)) {
|
|
427
|
+
Sentry.setTag('package-installed', false);
|
|
460
428
|
const continueWithoutPackage = await abortIfCancelled(
|
|
461
429
|
clack.confirm({
|
|
462
430
|
message: `${packageName} does not seem to be installed. Do you still want to continue?`,
|
|
@@ -467,6 +435,8 @@ export async function ensurePackageIsInstalled(
|
|
|
467
435
|
if (!continueWithoutPackage) {
|
|
468
436
|
await abort(undefined, 0);
|
|
469
437
|
}
|
|
438
|
+
} else {
|
|
439
|
+
Sentry.setTag('package-installed', true);
|
|
470
440
|
}
|
|
471
441
|
}
|
|
472
442
|
|
|
@@ -826,7 +796,7 @@ export async function askForToolConfigPath(
|
|
|
826
796
|
clack.confirm({
|
|
827
797
|
message: `Do you have a ${toolName} config file (e.g. ${chalk.cyan(
|
|
828
798
|
configFileName,
|
|
829
|
-
)}?`,
|
|
799
|
+
)})?`,
|
|
830
800
|
initialValue: true,
|
|
831
801
|
}),
|
|
832
802
|
);
|
|
@@ -866,27 +836,35 @@ export async function askForToolConfigPath(
|
|
|
866
836
|
*
|
|
867
837
|
* @param filename the name of the file to which the code snippet should be applied.
|
|
868
838
|
* If a path is provided, only the filename will be used.
|
|
869
|
-
* @param codeSnippet the snippet to be printed.
|
|
870
|
-
* Make sure to follow the diff-like format of highlighting lines that require changes
|
|
871
|
-
* and showing unchanged lines in gray.
|
|
872
839
|
*
|
|
873
|
-
*
|
|
840
|
+
* @param codeSnippet the snippet to be printed. Use {@link makeCodeSnippet} to create the
|
|
841
|
+
* diff-like format for visually highlighting unchanged or modified lines of code.
|
|
842
|
+
*
|
|
843
|
+
* @param hint (optional) a hint to be printed after the main instruction to add
|
|
844
|
+
* the code from @param codeSnippet to their @param filename.
|
|
845
|
+
*
|
|
846
|
+
* More guidelines on copy/paste instructions:
|
|
847
|
+
* @see {@link https://develop.sentry.dev/sdk/setup-wizards/#copy--paste-snippets}
|
|
848
|
+
*
|
|
874
849
|
* TODO: refactor copy paste instructions across different wizards to use this function.
|
|
875
850
|
* this might require adding a custom message parameter to the function
|
|
876
851
|
*/
|
|
877
852
|
export async function showCopyPasteInstructions(
|
|
878
853
|
filename: string,
|
|
879
854
|
codeSnippet: string,
|
|
855
|
+
hint?: string,
|
|
880
856
|
): Promise<void> {
|
|
881
857
|
clack.log.step(
|
|
882
858
|
`Add the following code to your ${chalk.cyan(
|
|
883
859
|
path.basename(filename),
|
|
884
|
-
)} file
|
|
860
|
+
)} file:${hint ? chalk.dim(` (${chalk.dim(hint)})`) : ''}`,
|
|
885
861
|
);
|
|
886
862
|
|
|
863
|
+
// Padding the code snippet to be printed with a \n at the beginning and end
|
|
864
|
+
// This makes it easier to distinguish the snippet from the rest of the output
|
|
887
865
|
// Intentionally logging directly to console here so that the code can be copied/pasted directly
|
|
888
866
|
// eslint-disable-next-line no-console
|
|
889
|
-
console.log(`\n${codeSnippet}`);
|
|
867
|
+
console.log(`\n${codeSnippet}\n`);
|
|
890
868
|
|
|
891
869
|
await abortIfCancelled(
|
|
892
870
|
clack.select({
|
|
@@ -897,6 +875,47 @@ export async function showCopyPasteInstructions(
|
|
|
897
875
|
);
|
|
898
876
|
}
|
|
899
877
|
|
|
878
|
+
/**
|
|
879
|
+
* Callback that exposes formatting helpers for a code snippet.
|
|
880
|
+
* @param unchanged - Formats text as old code.
|
|
881
|
+
* @param plus - Formats text as new code.
|
|
882
|
+
* @param minus - Formats text as removed code.
|
|
883
|
+
*/
|
|
884
|
+
type CodeSnippetFormatter = (
|
|
885
|
+
unchanged: (txt: string) => string,
|
|
886
|
+
plus: (txt: string) => string,
|
|
887
|
+
minus: (txt: string) => string,
|
|
888
|
+
) => string;
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Crafts a code snippet that can be used to e.g.
|
|
892
|
+
* - print copy/paste instructions to the console
|
|
893
|
+
* - create a new config file.
|
|
894
|
+
*
|
|
895
|
+
* @param colors set this to true if you want the final snippet to be colored.
|
|
896
|
+
* This is useful for printing the snippet to the console as part of copy/paste instructions.
|
|
897
|
+
*
|
|
898
|
+
* @param callback the callback that returns the formatted code snippet.
|
|
899
|
+
* It exposes takes the helper functions for marking code as unchaned, new or removed.
|
|
900
|
+
* These functions no-op if no special formatting should be applied
|
|
901
|
+
* and otherwise apply the appropriate formatting/coloring.
|
|
902
|
+
* (@see {@link CodeSnippetFormatter})
|
|
903
|
+
*
|
|
904
|
+
* @see {@link showCopyPasteInstructions} for the helper with which to display the snippet in the console.
|
|
905
|
+
*
|
|
906
|
+
* @returns a string containing the final, formatted code snippet.
|
|
907
|
+
*/
|
|
908
|
+
export function makeCodeSnippet(
|
|
909
|
+
colors: boolean,
|
|
910
|
+
callback: CodeSnippetFormatter,
|
|
911
|
+
): string {
|
|
912
|
+
const unchanged = (txt: string) => (colors ? chalk.grey(txt) : txt);
|
|
913
|
+
const plus = (txt: string) => (colors ? chalk.greenBright(txt) : txt);
|
|
914
|
+
const minus = (txt: string) => (colors ? chalk.redBright(txt) : txt);
|
|
915
|
+
|
|
916
|
+
return callback(unchanged, plus, minus);
|
|
917
|
+
}
|
|
918
|
+
|
|
900
919
|
/**
|
|
901
920
|
* Creates a new config file with the given @param filepath and @param codeSnippet.
|
|
902
921
|
*
|
|
@@ -10,38 +10,44 @@ export interface PackageManager {
|
|
|
10
10
|
lockFile: string;
|
|
11
11
|
installCommand: string;
|
|
12
12
|
buildCommand: string;
|
|
13
|
+
/* The command that the package manager uses to run a script from package.json */
|
|
14
|
+
runScriptCommand: string;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
const
|
|
17
|
+
export const BUN: PackageManager = {
|
|
16
18
|
name: 'bun',
|
|
17
19
|
label: 'Bun',
|
|
18
20
|
lockFile: 'bun.lockb',
|
|
19
21
|
installCommand: 'bun add',
|
|
20
|
-
buildCommand: 'bun build',
|
|
22
|
+
buildCommand: 'bun run build',
|
|
23
|
+
runScriptCommand: 'bun run',
|
|
21
24
|
};
|
|
22
|
-
const
|
|
25
|
+
export const YARN: PackageManager = {
|
|
23
26
|
name: 'yarn',
|
|
24
27
|
label: 'Yarn',
|
|
25
28
|
lockFile: 'yarn.lock',
|
|
26
29
|
installCommand: 'yarn add',
|
|
27
30
|
buildCommand: 'yarn build',
|
|
31
|
+
runScriptCommand: 'yarn',
|
|
28
32
|
};
|
|
29
|
-
const
|
|
33
|
+
export const PNPM: PackageManager = {
|
|
30
34
|
name: 'pnpm',
|
|
31
35
|
label: 'PNPM',
|
|
32
36
|
lockFile: 'pnpm-lock.yaml',
|
|
33
37
|
installCommand: 'pnpm add',
|
|
34
38
|
buildCommand: 'pnpm build',
|
|
39
|
+
runScriptCommand: 'pnpm',
|
|
35
40
|
};
|
|
36
|
-
const
|
|
41
|
+
export const NPM: PackageManager = {
|
|
37
42
|
name: 'npm',
|
|
38
43
|
label: 'NPM',
|
|
39
44
|
lockFile: 'package-lock.json',
|
|
40
45
|
installCommand: 'npm add',
|
|
41
46
|
buildCommand: 'npm run build',
|
|
47
|
+
runScriptCommand: 'npm run',
|
|
42
48
|
};
|
|
43
49
|
|
|
44
|
-
export const packageManagers = [
|
|
50
|
+
export const packageManagers = [BUN, YARN, PNPM, NPM];
|
|
45
51
|
|
|
46
52
|
export function detectPackageManger(): PackageManager | null {
|
|
47
53
|
for (const packageManager of packageManagers) {
|