@sentry/wizard 3.28.0 → 3.30.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 +12 -0
- package/dist/package.json +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +9 -4
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/nextjs/templates.js +2 -2
- package/dist/src/nextjs/templates.js.map +1 -1
- package/dist/src/react-native/react-native-wizard.js +4 -1
- package/dist/src/react-native/react-native-wizard.js.map +1 -1
- package/dist/src/remix/remix-wizard.d.ts +1 -1
- package/dist/src/remix/remix-wizard.js +27 -11
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-setup.d.ts +24 -3
- package/dist/src/remix/sdk-setup.js +95 -61
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/run.d.ts +2 -0
- package/dist/src/run.js +2 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +3 -1
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +3 -0
- package/dist/src/utils/clack-utils.js +86 -14
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/types.d.ts +12 -0
- package/dist/src/utils/types.js.map +1 -1
- package/dist/test/remix/client-entry.test.d.ts +1 -0
- package/dist/test/remix/client-entry.test.js +41 -0
- package/dist/test/remix/client-entry.test.js.map +1 -0
- package/dist/test/remix/server-instrumentation.test.d.ts +1 -0
- package/dist/test/remix/server-instrumentation.test.js +22 -0
- package/dist/test/remix/server-instrumentation.test.js.map +1 -0
- package/package.json +1 -1
- package/src/nextjs/nextjs-wizard.ts +9 -5
- package/src/nextjs/templates.ts +12 -3
- package/src/react-native/react-native-wizard.ts +4 -0
- package/src/remix/remix-wizard.ts +32 -6
- package/src/remix/sdk-setup.ts +145 -48
- package/src/run.ts +4 -0
- package/src/sveltekit/sveltekit-wizard.ts +3 -0
- package/src/utils/clack-utils.ts +93 -3
- package/src/utils/types.ts +14 -0
- package/test/remix/client-entry.test.ts +122 -0
- package/test/remix/server-instrumentation.test.ts +38 -0
package/src/utils/clack-utils.ts
CHANGED
|
@@ -670,6 +670,52 @@ async function addCliConfigFileToGitIgnore(filename: string): Promise<void> {
|
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
+
export async function runPrettierIfInstalled(): Promise<void> {
|
|
674
|
+
return traceStep('run-prettier', async () => {
|
|
675
|
+
const packageJson = await getPackageDotJson();
|
|
676
|
+
const prettierInstalled = hasPackageInstalled('prettier', packageJson);
|
|
677
|
+
|
|
678
|
+
if (prettierInstalled) {
|
|
679
|
+
// prompt the user if they want to run prettier
|
|
680
|
+
const shouldRunPrettier = await abortIfCancelled(
|
|
681
|
+
clack.confirm({
|
|
682
|
+
message:
|
|
683
|
+
'Looks like you have Prettier in your project. Do you want to run it on your files?',
|
|
684
|
+
}),
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
if (!shouldRunPrettier) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
} else {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const prettierSpinner = clack.spinner();
|
|
695
|
+
prettierSpinner.start('Running Prettier on your files.');
|
|
696
|
+
|
|
697
|
+
try {
|
|
698
|
+
await new Promise<void>((resolve, reject) => {
|
|
699
|
+
childProcess.exec('npx prettier --write .', (err) => {
|
|
700
|
+
if (err) {
|
|
701
|
+
reject(err);
|
|
702
|
+
} else {
|
|
703
|
+
resolve();
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
} catch {
|
|
708
|
+
prettierSpinner.stop('Prettier failed to run.');
|
|
709
|
+
clack.log.error(
|
|
710
|
+
'Prettier failed to run. There may be formatting issues in your updated files.',
|
|
711
|
+
);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
prettierSpinner.stop('Prettier has formatted your files.');
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
673
719
|
/**
|
|
674
720
|
* Checks if @param packageId is listed as a dependency in @param packageJson.
|
|
675
721
|
* If not, it will ask users if they want to continue without the package.
|
|
@@ -734,7 +780,7 @@ export async function getPackageDotJson(): Promise<PackageDotJson> {
|
|
|
734
780
|
return packageJson || {};
|
|
735
781
|
}
|
|
736
782
|
|
|
737
|
-
async function getPackageManager(): Promise<PackageManager> {
|
|
783
|
+
export async function getPackageManager(): Promise<PackageManager> {
|
|
738
784
|
const detectedPackageManager = detectPackageManger();
|
|
739
785
|
|
|
740
786
|
if (detectedPackageManager) {
|
|
@@ -823,7 +869,7 @@ export async function getOrAskForProjectData(
|
|
|
823
869
|
}
|
|
824
870
|
|
|
825
871
|
const selectedProject = await traceStep('select-project', () =>
|
|
826
|
-
askForProjectSelection(projects),
|
|
872
|
+
askForProjectSelection(projects, options.orgSlug, options.projectSlug),
|
|
827
873
|
);
|
|
828
874
|
|
|
829
875
|
const { token } = apiKeys ?? {};
|
|
@@ -1041,14 +1087,38 @@ async function askForWizardLogin(options: {
|
|
|
1041
1087
|
|
|
1042
1088
|
async function askForProjectSelection(
|
|
1043
1089
|
projects: SentryProjectData[],
|
|
1090
|
+
orgSlug?: string,
|
|
1091
|
+
projectSlug?: string,
|
|
1044
1092
|
): Promise<SentryProjectData> {
|
|
1045
1093
|
const label = (project: SentryProjectData): string => {
|
|
1046
1094
|
return `${project.organization.slug}/${project.slug}`;
|
|
1047
1095
|
};
|
|
1048
|
-
|
|
1096
|
+
|
|
1097
|
+
const filteredProjects = filterProjectsBySlugs(
|
|
1098
|
+
projects,
|
|
1099
|
+
orgSlug,
|
|
1100
|
+
projectSlug,
|
|
1101
|
+
);
|
|
1102
|
+
|
|
1103
|
+
if (filteredProjects.length === 1) {
|
|
1104
|
+
const selection = filteredProjects[0];
|
|
1105
|
+
|
|
1106
|
+
Sentry.setTag('project', selection.slug);
|
|
1107
|
+
Sentry.setUser({ id: selection.organization.slug });
|
|
1108
|
+
clack.log.step(`Selected project ${label(selection)}`);
|
|
1109
|
+
|
|
1110
|
+
return selection;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if (filteredProjects.length === 0) {
|
|
1114
|
+
clack.log.warn('Could not find a project with the provided slugs.');
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const sortedProjects = filteredProjects.length ? filteredProjects : projects;
|
|
1049
1118
|
sortedProjects.sort((a: SentryProjectData, b: SentryProjectData) => {
|
|
1050
1119
|
return label(a).localeCompare(label(b));
|
|
1051
1120
|
});
|
|
1121
|
+
|
|
1052
1122
|
const selection: SentryProjectData | symbol = await abortIfCancelled(
|
|
1053
1123
|
clack.select({
|
|
1054
1124
|
maxItems: 12,
|
|
@@ -1068,6 +1138,26 @@ async function askForProjectSelection(
|
|
|
1068
1138
|
return selection;
|
|
1069
1139
|
}
|
|
1070
1140
|
|
|
1141
|
+
function filterProjectsBySlugs(
|
|
1142
|
+
projects: SentryProjectData[],
|
|
1143
|
+
orgSlug?: string,
|
|
1144
|
+
projectSlug?: string,
|
|
1145
|
+
): SentryProjectData[] {
|
|
1146
|
+
if (!orgSlug && !projectSlug) {
|
|
1147
|
+
return projects;
|
|
1148
|
+
}
|
|
1149
|
+
if (orgSlug && !projectSlug) {
|
|
1150
|
+
return projects.filter((p) => p.organization.slug === orgSlug);
|
|
1151
|
+
}
|
|
1152
|
+
if (!orgSlug && projectSlug) {
|
|
1153
|
+
return projects.filter((p) => p.slug === projectSlug);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
return projects.filter(
|
|
1157
|
+
(p) => p.organization.slug === orgSlug && p.slug === projectSlug,
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1071
1161
|
/**
|
|
1072
1162
|
* Asks users if they have a config file for @param tool (e.g. Vite).
|
|
1073
1163
|
* If yes, asks users to specify the path to their config file.
|
package/src/utils/types.ts
CHANGED
|
@@ -33,6 +33,20 @@ export type WizardOptions = {
|
|
|
33
33
|
*/
|
|
34
34
|
url?: string;
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The org to pre-select in the wizard.
|
|
38
|
+
* This can be passed via the `--org` arg.
|
|
39
|
+
* Example: `--org my-org`
|
|
40
|
+
*/
|
|
41
|
+
orgSlug?: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Project slug to pre-select in the wizard.
|
|
45
|
+
* This can be passed via the `--project` arg.
|
|
46
|
+
* Example: `--project my-project`
|
|
47
|
+
*/
|
|
48
|
+
projectSlug?: string;
|
|
49
|
+
|
|
36
50
|
/**
|
|
37
51
|
* If this is set, the wizard will skip the login and project selection step.
|
|
38
52
|
* (This can not yet be set externally but for example when redirecting from
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// @ts-expect-error - magicast is ESM and TS complains about that. It works though
|
|
2
|
+
import { parseModule } from 'magicast';
|
|
3
|
+
import { updateEntryClientMod } from '../../src/remix/sdk-setup';
|
|
4
|
+
|
|
5
|
+
describe('initializeSentryOnEntryClient', () => {
|
|
6
|
+
it('should initialize Sentry on client entry with all features enabled', () => {
|
|
7
|
+
// Empty entry.client.tsx file for testing
|
|
8
|
+
const originalEntryClientMod = parseModule('');
|
|
9
|
+
|
|
10
|
+
const dsn = 'https://sentry.io/123';
|
|
11
|
+
const selectedFeatures = {
|
|
12
|
+
performance: true,
|
|
13
|
+
replay: true,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const result = updateEntryClientMod(
|
|
17
|
+
originalEntryClientMod,
|
|
18
|
+
dsn,
|
|
19
|
+
selectedFeatures,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
expect(result.generate().code).toMatchInlineSnapshot(`
|
|
23
|
+
"import { useEffect,} from "react";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
useLocation,
|
|
27
|
+
useMatches,
|
|
28
|
+
} from "@remix-run/react";
|
|
29
|
+
|
|
30
|
+
import * as Sentry from "@sentry/remix";
|
|
31
|
+
|
|
32
|
+
Sentry.init({
|
|
33
|
+
dsn: "https://sentry.io/123",
|
|
34
|
+
tracesSampleRate: 1,
|
|
35
|
+
|
|
36
|
+
integrations: [Sentry.browserTracingIntegration({
|
|
37
|
+
useEffect,
|
|
38
|
+
useLocation,
|
|
39
|
+
useMatches
|
|
40
|
+
}), Sentry.replayIntegration({
|
|
41
|
+
maskAllText: true,
|
|
42
|
+
blockAllMedia: true
|
|
43
|
+
})],
|
|
44
|
+
|
|
45
|
+
replaysSessionSampleRate: 0.1,
|
|
46
|
+
replaysOnErrorSampleRate: 1
|
|
47
|
+
})"
|
|
48
|
+
`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should initialize Sentry on client entry when performance disabled', () => {
|
|
52
|
+
// Empty entry.client.tsx file for testing
|
|
53
|
+
const originalEntryClientMod = parseModule('');
|
|
54
|
+
|
|
55
|
+
const dsn = 'https://sentry.io/123';
|
|
56
|
+
const selectedFeatures = {
|
|
57
|
+
performance: false,
|
|
58
|
+
replay: true,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const result = updateEntryClientMod(
|
|
62
|
+
originalEntryClientMod,
|
|
63
|
+
dsn,
|
|
64
|
+
selectedFeatures,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(result.generate().code).toMatchInlineSnapshot(`
|
|
68
|
+
"import * as Sentry from "@sentry/remix";
|
|
69
|
+
|
|
70
|
+
Sentry.init({
|
|
71
|
+
dsn: "https://sentry.io/123",
|
|
72
|
+
|
|
73
|
+
integrations: [Sentry.replayIntegration({
|
|
74
|
+
maskAllText: true,
|
|
75
|
+
blockAllMedia: true
|
|
76
|
+
})],
|
|
77
|
+
|
|
78
|
+
replaysSessionSampleRate: 0.1,
|
|
79
|
+
replaysOnErrorSampleRate: 1
|
|
80
|
+
})"
|
|
81
|
+
`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should initialize Sentry on client entry when replay disabled', () => {
|
|
85
|
+
// Empty entry.client.tsx file for testing
|
|
86
|
+
const originalEntryClientMod = parseModule('');
|
|
87
|
+
|
|
88
|
+
const dsn = 'https://sentry.io/123';
|
|
89
|
+
const selectedFeatures = {
|
|
90
|
+
performance: true,
|
|
91
|
+
replay: false,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const result = updateEntryClientMod(
|
|
95
|
+
originalEntryClientMod,
|
|
96
|
+
dsn,
|
|
97
|
+
selectedFeatures,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(result.generate().code).toMatchInlineSnapshot(`
|
|
101
|
+
"import { useEffect,} from "react";
|
|
102
|
+
|
|
103
|
+
import {
|
|
104
|
+
useLocation,
|
|
105
|
+
useMatches,
|
|
106
|
+
} from "@remix-run/react";
|
|
107
|
+
|
|
108
|
+
import * as Sentry from "@sentry/remix";
|
|
109
|
+
|
|
110
|
+
Sentry.init({
|
|
111
|
+
dsn: "https://sentry.io/123",
|
|
112
|
+
tracesSampleRate: 1,
|
|
113
|
+
|
|
114
|
+
integrations: [Sentry.browserTracingIntegration({
|
|
115
|
+
useEffect,
|
|
116
|
+
useLocation,
|
|
117
|
+
useMatches
|
|
118
|
+
})]
|
|
119
|
+
})"
|
|
120
|
+
`);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { generateServerInstrumentationFile } from '../../src/remix/sdk-setup';
|
|
2
|
+
|
|
3
|
+
describe('generateServerInstrumentationFile', () => {
|
|
4
|
+
it('should generate server instrumentation file', () => {
|
|
5
|
+
const result = generateServerInstrumentationFile('https://sentry.io/123', {
|
|
6
|
+
performance: true,
|
|
7
|
+
replay: true,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
expect(result.instrumentationFileMod.generate().code)
|
|
11
|
+
.toMatchInlineSnapshot(`
|
|
12
|
+
"import * as Sentry from "@sentry/remix";
|
|
13
|
+
|
|
14
|
+
Sentry.init({
|
|
15
|
+
dsn: "https://sentry.io/123",
|
|
16
|
+
tracesSampleRate: 1,
|
|
17
|
+
autoInstrumentRemix: true
|
|
18
|
+
})"
|
|
19
|
+
`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should generate server instrumentation file when performance is disabled', () => {
|
|
23
|
+
const result = generateServerInstrumentationFile('https://sentry.io/123', {
|
|
24
|
+
performance: false,
|
|
25
|
+
replay: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result.instrumentationFileMod.generate().code)
|
|
29
|
+
.toMatchInlineSnapshot(`
|
|
30
|
+
"import * as Sentry from "@sentry/remix";
|
|
31
|
+
|
|
32
|
+
Sentry.init({
|
|
33
|
+
dsn: "https://sentry.io/123",
|
|
34
|
+
autoInstrumentRemix: true
|
|
35
|
+
})"
|
|
36
|
+
`);
|
|
37
|
+
});
|
|
38
|
+
});
|