@sentry/wizard 3.38.0 → 3.40.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.
Files changed (166) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +3 -3
  3. package/dist/e2e-tests/tests/flutter.test.d.ts +1 -0
  4. package/dist/e2e-tests/tests/flutter.test.js +190 -0
  5. package/dist/e2e-tests/tests/flutter.test.js.map +1 -0
  6. package/dist/e2e-tests/tests/nextjs.test.js +1 -1
  7. package/dist/e2e-tests/tests/nextjs.test.js.map +1 -1
  8. package/dist/e2e-tests/tests/nuxt-3.test.js +1 -1
  9. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/nuxt-4.test.js +1 -1
  11. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  12. package/dist/e2e-tests/tests/remix.test.js +1 -2
  13. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  14. package/dist/e2e-tests/tests/sveltekit.test.js +17 -5
  15. package/dist/e2e-tests/tests/sveltekit.test.js.map +1 -1
  16. package/dist/e2e-tests/utils/index.d.ts +22 -1
  17. package/dist/e2e-tests/utils/index.js +58 -3
  18. package/dist/e2e-tests/utils/index.js.map +1 -1
  19. package/dist/lib/Constants.d.ts +1 -0
  20. package/dist/lib/Constants.js +5 -0
  21. package/dist/lib/Constants.js.map +1 -1
  22. package/dist/package.json +1 -1
  23. package/dist/src/apple/apple-wizard.js +1 -1
  24. package/dist/src/apple/apple-wizard.js.map +1 -1
  25. package/dist/src/apple/xcode-manager.d.ts +4 -4
  26. package/dist/src/apple/xcode-manager.js.map +1 -1
  27. package/dist/src/flutter/code-tools.d.ts +17 -0
  28. package/dist/src/flutter/code-tools.js +263 -0
  29. package/dist/src/flutter/code-tools.js.map +1 -0
  30. package/dist/src/flutter/flutter-wizard.d.ts +2 -0
  31. package/dist/src/flutter/flutter-wizard.js +171 -0
  32. package/dist/src/flutter/flutter-wizard.js.map +1 -0
  33. package/dist/src/flutter/templates.d.ts +9 -0
  34. package/dist/src/flutter/templates.js +40 -0
  35. package/dist/src/flutter/templates.js.map +1 -0
  36. package/dist/src/nextjs/nextjs-wizard.js +3 -1
  37. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  38. package/dist/src/run.d.ts +1 -1
  39. package/dist/src/run.js +39 -32
  40. package/dist/src/run.js.map +1 -1
  41. package/dist/src/sourcemaps/sourcemaps-wizard.js +2 -3
  42. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  43. package/dist/src/telemetry.d.ts +2 -1
  44. package/dist/src/telemetry.js +1 -1
  45. package/dist/src/telemetry.js.map +1 -1
  46. package/dist/src/utils/clack-utils.d.ts +1 -1
  47. package/dist/src/utils/clack-utils.js +3 -3
  48. package/dist/src/utils/clack-utils.js.map +1 -1
  49. package/dist/src/utils/package-manager.d.ts +0 -1
  50. package/dist/src/utils/package-manager.js +9 -10
  51. package/dist/src/utils/package-manager.js.map +1 -1
  52. package/dist/test/flutter/code-tools.test.d.ts +1 -0
  53. package/dist/test/flutter/code-tools.test.js +84 -0
  54. package/dist/test/flutter/code-tools.test.js.map +1 -0
  55. package/dist/test/flutter/templates.test.d.ts +1 -0
  56. package/dist/test/flutter/templates.test.js +41 -0
  57. package/dist/test/flutter/templates.test.js.map +1 -0
  58. package/e2e-tests/README.md +5 -1
  59. package/e2e-tests/test-applications/flutter-test-app/.metadata +45 -0
  60. package/e2e-tests/test-applications/flutter-test-app/README.md +16 -0
  61. package/e2e-tests/test-applications/flutter-test-app/analysis_options.yaml +28 -0
  62. package/e2e-tests/test-applications/flutter-test-app/android/app/build.gradle +44 -0
  63. package/e2e-tests/test-applications/flutter-test-app/android/app/src/debug/AndroidManifest.xml +7 -0
  64. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/AndroidManifest.xml +45 -0
  65. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/kotlin/com/example/flutter_magic/MainActivity.kt +5 -0
  66. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  67. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  68. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  69. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  70. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  71. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  72. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  73. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values/styles.xml +18 -0
  74. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values-night/styles.xml +18 -0
  75. package/e2e-tests/test-applications/flutter-test-app/android/app/src/profile/AndroidManifest.xml +7 -0
  76. package/e2e-tests/test-applications/flutter-test-app/android/build.gradle +18 -0
  77. package/e2e-tests/test-applications/flutter-test-app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  78. package/e2e-tests/test-applications/flutter-test-app/android/gradle.properties +3 -0
  79. package/e2e-tests/test-applications/flutter-test-app/android/settings.gradle +25 -0
  80. package/e2e-tests/test-applications/flutter-test-app/lib/main.dart +125 -0
  81. package/e2e-tests/test-applications/flutter-test-app/linux/CMakeLists.txt +145 -0
  82. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/CMakeLists.txt +88 -0
  83. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.cc +11 -0
  84. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.h +15 -0
  85. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugins.cmake +23 -0
  86. package/e2e-tests/test-applications/flutter-test-app/linux/main.cc +6 -0
  87. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.cc +124 -0
  88. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.h +18 -0
  89. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  90. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  91. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/GeneratedPluginRegistrant.swift +10 -0
  92. package/e2e-tests/test-applications/flutter-test-app/macos/Podfile +43 -0
  93. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/AppDelegate.swift +9 -0
  94. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  95. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  96. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  97. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  98. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  99. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  100. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  101. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  102. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Base.lproj/MainMenu.xib +343 -0
  103. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  104. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Debug.xcconfig +2 -0
  105. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Release.xcconfig +2 -0
  106. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  107. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/DebugProfile.entitlements +12 -0
  108. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Info.plist +32 -0
  109. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/MainFlutterWindow.swift +15 -0
  110. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Release.entitlements +8 -0
  111. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.pbxproj +705 -0
  112. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  113. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +98 -0
  114. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  115. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  116. package/e2e-tests/test-applications/flutter-test-app/macos/RunnerTests/RunnerTests.swift +12 -0
  117. package/e2e-tests/test-applications/flutter-test-app/pubspec.lock +213 -0
  118. package/e2e-tests/test-applications/flutter-test-app/pubspec.yaml +89 -0
  119. package/e2e-tests/test-applications/flutter-test-app/test/widget_test.dart +30 -0
  120. package/e2e-tests/test-applications/flutter-test-app/web/favicon.png +0 -0
  121. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-192.png +0 -0
  122. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-512.png +0 -0
  123. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-192.png +0 -0
  124. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-512.png +0 -0
  125. package/e2e-tests/test-applications/flutter-test-app/web/index.html +38 -0
  126. package/e2e-tests/test-applications/flutter-test-app/web/manifest.json +35 -0
  127. package/e2e-tests/test-applications/flutter-test-app/windows/CMakeLists.txt +108 -0
  128. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/CMakeLists.txt +109 -0
  129. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.cc +11 -0
  130. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.h +15 -0
  131. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugins.cmake +23 -0
  132. package/e2e-tests/test-applications/flutter-test-app/windows/runner/CMakeLists.txt +40 -0
  133. package/e2e-tests/test-applications/flutter-test-app/windows/runner/Runner.rc +121 -0
  134. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.cpp +71 -0
  135. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.h +33 -0
  136. package/e2e-tests/test-applications/flutter-test-app/windows/runner/main.cpp +43 -0
  137. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resource.h +16 -0
  138. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resources/app_icon.ico +0 -0
  139. package/e2e-tests/test-applications/flutter-test-app/windows/runner/runner.exe.manifest +14 -0
  140. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.cpp +65 -0
  141. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.h +19 -0
  142. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.cpp +288 -0
  143. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.h +102 -0
  144. package/e2e-tests/test-applications/nextjs-test-app/package.json +1 -1
  145. package/e2e-tests/tests/flutter.test.ts +127 -0
  146. package/e2e-tests/tests/nextjs.test.ts +1 -1
  147. package/e2e-tests/tests/nuxt-3.test.ts +1 -1
  148. package/e2e-tests/tests/nuxt-4.test.ts +1 -1
  149. package/e2e-tests/tests/remix.test.ts +30 -19
  150. package/e2e-tests/tests/sveltekit.test.ts +79 -50
  151. package/e2e-tests/utils/index.ts +62 -2
  152. package/lib/Constants.ts +5 -0
  153. package/package.json +1 -1
  154. package/src/apple/apple-wizard.ts +1 -1
  155. package/src/apple/xcode-manager.ts +5 -5
  156. package/src/flutter/code-tools.ts +284 -0
  157. package/src/flutter/flutter-wizard.ts +164 -0
  158. package/src/flutter/templates.ts +90 -0
  159. package/src/nextjs/nextjs-wizard.ts +4 -1
  160. package/src/run.ts +7 -0
  161. package/src/sourcemaps/sourcemaps-wizard.ts +3 -7
  162. package/src/telemetry.ts +6 -2
  163. package/src/utils/clack-utils.ts +8 -5
  164. package/src/utils/package-manager.ts +8 -11
  165. package/test/flutter/code-tools.test.ts +212 -0
  166. package/test/flutter/templates.test.ts +100 -0
@@ -41,7 +41,14 @@ export async function handleError({ error, event }) {
41
41
  }
42
42
  `;
43
43
 
44
- async function runWizardOnSvelteKitProject(projectDir: string, integration: Integration, fileModificationFn?: (projectDir: string, integration: Integration) => unknown) {
44
+ async function runWizardOnSvelteKitProject(
45
+ projectDir: string,
46
+ integration: Integration,
47
+ fileModificationFn?: (
48
+ projectDir: string,
49
+ integration: Integration,
50
+ ) => unknown,
51
+ ) {
45
52
  const wizardInstance = startWizardInstance(integration, projectDir);
46
53
  let packageManagerPrompted = false;
47
54
 
@@ -49,9 +56,7 @@ async function runWizardOnSvelteKitProject(projectDir: string, integration: Inte
49
56
  fileModificationFn(projectDir, integration);
50
57
 
51
58
  // As we modified project, we have a warning prompt before we get the package manager prompt
52
- await wizardInstance.waitForOutput(
53
- 'Do you want to continue anyway?',
54
- );
59
+ await wizardInstance.waitForOutput('Do you want to continue anyway?');
55
60
 
56
61
  packageManagerPrompted = await wizardInstance.sendStdinAndWaitForOutput(
57
62
  [KEYS.ENTER],
@@ -59,7 +64,7 @@ async function runWizardOnSvelteKitProject(projectDir: string, integration: Inte
59
64
  );
60
65
  } else {
61
66
  packageManagerPrompted = await wizardInstance.waitForOutput(
62
- 'Please select your package manager'
67
+ 'Please select your package manager',
63
68
  );
64
69
  }
65
70
 
@@ -72,7 +77,7 @@ async function runWizardOnSvelteKitProject(projectDir: string, integration: Inte
72
77
  'to track the performance of your application?',
73
78
  {
74
79
  timeout: 240_000,
75
- }
80
+ },
76
81
  ));
77
82
 
78
83
  const replayOptionPrompted =
@@ -100,10 +105,14 @@ async function runWizardOnSvelteKitProject(projectDir: string, integration: Inte
100
105
  wizardInstance.kill();
101
106
  }
102
107
 
103
- function checkSvelteKitProject(projectDir: string, integration: Integration, options?: {
104
- devModeExpectedOutput: string;
105
- prodModeExpectedOutput: string;
106
- }) {
108
+ function checkSvelteKitProject(
109
+ projectDir: string,
110
+ integration: Integration,
111
+ options?: {
112
+ devModeExpectedOutput: string;
113
+ prodModeExpectedOutput: string;
114
+ },
115
+ ) {
107
116
  test('should have the correct package.json', () => {
108
117
  checkPackageJson(projectDir, integration);
109
118
  });
@@ -113,14 +122,21 @@ function checkSvelteKitProject(projectDir: string, integration: Integration, opt
113
122
  });
114
123
 
115
124
  test('example page exists', () => {
116
- checkFileExists(path.resolve(projectDir, 'src/routes/sentry-example/+page.svelte'));
117
- checkFileExists(path.resolve(projectDir, 'src/routes/sentry-example/+server.js'));
125
+ checkFileExists(
126
+ path.resolve(projectDir, 'src/routes/sentry-example/+page.svelte'),
127
+ );
128
+ checkFileExists(
129
+ path.resolve(projectDir, 'src/routes/sentry-example/+server.js'),
130
+ );
118
131
  });
119
132
 
120
133
  test('vite.config contains sentry plugin', () => {
121
- checkFileContents(path.resolve(projectDir, 'vite.config.ts'), `plugins: [sentrySvelteKit({
134
+ checkFileContents(
135
+ path.resolve(projectDir, 'vite.config.ts'),
136
+ `plugins: [sentrySvelteKit({
122
137
  sourceMapsUploadOptions: {
123
- `);
138
+ `,
139
+ );
124
140
  });
125
141
 
126
142
  test('hook files created', () => {
@@ -129,15 +145,22 @@ function checkSvelteKitProject(projectDir: string, integration: Integration, opt
129
145
  });
130
146
 
131
147
  test('builds successfully', async () => {
132
- await checkIfBuilds(projectDir, 'Successfully uploaded source maps to Sentry');
148
+ await checkIfBuilds(projectDir);
133
149
  });
134
150
 
135
151
  test('runs on dev mode correctly', async () => {
136
- await checkIfRunsOnDevMode(projectDir, options?.devModeExpectedOutput || 'ready in');
152
+ await checkIfRunsOnDevMode(
153
+ projectDir,
154
+ options?.devModeExpectedOutput || 'ready in',
155
+ );
137
156
  });
138
157
 
139
158
  test('runs on prod mode correctly', async () => {
140
- await checkIfRunsOnProdMode(projectDir, options?.prodModeExpectedOutput || 'to expose', 'preview');
159
+ await checkIfRunsOnProdMode(
160
+ projectDir,
161
+ options?.prodModeExpectedOutput || 'to expose',
162
+ 'preview',
163
+ );
141
164
  });
142
165
  }
143
166
 
@@ -161,10 +184,9 @@ describe('Sveltekit', () => {
161
184
  checkSvelteKitProject(projectDir, integration);
162
185
 
163
186
  test('hooks.client.ts contains sentry', () => {
164
- checkFileContents(
165
- path.resolve(projectDir, 'src/hooks.client.ts'),
166
- [`import * as Sentry from '@sentry/sveltekit';`,
167
- `Sentry.init({
187
+ checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [
188
+ `import * as Sentry from '@sentry/sveltekit';`,
189
+ `Sentry.init({
168
190
  dsn: '${TEST_ARGS.PROJECT_DSN}',
169
191
 
170
192
  tracesSampleRate: 1.0,
@@ -179,22 +201,24 @@ describe('Sveltekit', () => {
179
201
 
180
202
  // If you don't want to use Session Replay, just remove the line below:
181
203
  integrations: [replayIntegration()],
182
- });`, 'export const handleError = handleErrorWithSentry(']);
204
+ });`,
205
+ 'export const handleError = handleErrorWithSentry(',
206
+ ]);
183
207
  });
184
208
 
185
209
  test('hooks.server.ts contains sentry', () => {
186
- checkFileContents(
187
- path.resolve(projectDir, 'src/hooks.server.ts'),
188
- [
189
- `import * as Sentry from '@sentry/sveltekit';`,
190
- `Sentry.init({
210
+ checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [
211
+ `import * as Sentry from '@sentry/sveltekit';`,
212
+ `Sentry.init({
191
213
  dsn: '${TEST_ARGS.PROJECT_DSN}',
192
214
 
193
215
  tracesSampleRate: 1.0,
194
216
 
195
217
  // uncomment the line below to enable Spotlight (https://spotlightjs.com)
196
218
  // spotlight: import.meta.env.DEV,
197
- });`, 'export const handleError = handleErrorWithSentry();']);
219
+ });`,
220
+ 'export const handleError = handleErrorWithSentry();',
221
+ ]);
198
222
  });
199
223
  });
200
224
 
@@ -206,17 +230,21 @@ describe('Sveltekit', () => {
206
230
  );
207
231
 
208
232
  beforeAll(async () => {
209
- await runWizardOnSvelteKitProject(projectDir, integration, (projectDir) => {
210
- createFile(
211
- path.resolve(projectDir, 'src/hooks.server.ts'),
212
- SERVER_HOOK_TEMPLATE,
213
- )
214
-
215
- createFile(
216
- path.resolve(projectDir, 'src/hooks.client.ts'),
217
- CLIENT_HOOK_TEMPLATE,
218
- )
219
- });
233
+ await runWizardOnSvelteKitProject(
234
+ projectDir,
235
+ integration,
236
+ (projectDir) => {
237
+ createFile(
238
+ path.resolve(projectDir, 'src/hooks.server.ts'),
239
+ SERVER_HOOK_TEMPLATE,
240
+ );
241
+
242
+ createFile(
243
+ path.resolve(projectDir, 'src/hooks.client.ts'),
244
+ CLIENT_HOOK_TEMPLATE,
245
+ );
246
+ },
247
+ );
220
248
  });
221
249
 
222
250
  afterAll(() => {
@@ -229,27 +257,28 @@ describe('Sveltekit', () => {
229
257
  // These are removed from the common tests as the content is different
230
258
  // when the hooks are merged instead of created from the template
231
259
  test('hooks.client.ts contains sentry instrumentation', () => {
232
- checkFileContents(
233
- path.resolve(projectDir, 'src/hooks.client.ts'),
234
- [`import * as Sentry from '@sentry/sveltekit';`,
235
- `Sentry.init({
260
+ checkFileContents(path.resolve(projectDir, 'src/hooks.client.ts'), [
261
+ `import * as Sentry from '@sentry/sveltekit';`,
262
+ `Sentry.init({
236
263
  dsn: "${TEST_ARGS.PROJECT_DSN}",
237
264
  tracesSampleRate: 1,
238
265
  replaysSessionSampleRate: 0.1,
239
266
  replaysOnErrorSampleRate: 1,
240
267
  integrations: [Sentry.replayIntegration()]
241
- })`, 'export const handleError = Sentry.handleErrorWithSentry(']);
268
+ })`,
269
+ 'export const handleError = Sentry.handleErrorWithSentry(',
270
+ ]);
242
271
  });
243
272
 
244
273
  test('hooks.server.ts contains sentry init', () => {
245
- checkFileContents(
246
- path.resolve(projectDir, 'src/hooks.server.ts'),
247
- [`import * as Sentry from '@sentry/sveltekit';`,
248
- `Sentry.init({
274
+ checkFileContents(path.resolve(projectDir, 'src/hooks.server.ts'), [
275
+ `import * as Sentry from '@sentry/sveltekit';`,
276
+ `Sentry.init({
249
277
  dsn: "${TEST_ARGS.PROJECT_DSN}",
250
278
  tracesSampleRate: 1
251
- })`, 'export const handleError = Sentry.handleErrorWithSentry();']);
279
+ })`,
280
+ 'export const handleError = Sentry.handleErrorWithSentry();',
281
+ ]);
252
282
  });
253
283
  });
254
284
  });
255
-
@@ -80,6 +80,36 @@ export class WizardTestEnv {
80
80
  return outputPromise;
81
81
  }
82
82
 
83
+ /**
84
+ * Waits for the task to exit with a given `statusCode`.
85
+ *
86
+ * @returns a promise that resolves to `true` if the run ends with the status
87
+ * code, or it rejects when the `timeout` was reached.
88
+ */
89
+ waitForStatusCode(
90
+ statusCode: number | null,
91
+ options: {
92
+ /** Timeout in ms */
93
+ timeout?: number;
94
+ } = {},
95
+ ) {
96
+ const { timeout } = {
97
+ timeout: 60_000,
98
+ ...options,
99
+ };
100
+
101
+ return new Promise<boolean>((resolve, reject) => {
102
+ const timeoutId = setTimeout(() => {
103
+ reject(new Error(`Timeout waiting for status code: ${statusCode}`));
104
+ }, timeout);
105
+
106
+ this.taskHandle.on('exit', (code: number | null) => {
107
+ clearTimeout(timeoutId);
108
+ resolve(code === statusCode);
109
+ });
110
+ });
111
+ }
112
+
83
113
  /**
84
114
  * Waits for the provided output with `.includes()` logic.
85
115
  *
@@ -317,16 +347,46 @@ export function checkEnvBuildPlugin(projectDir: string) {
317
347
  );
318
348
  }
319
349
 
350
+ /**
351
+ * Check if the sentry.properties contains the auth token
352
+ * @param projectDir
353
+ */
354
+ export function checkSentryProperties(projectDir: string) {
355
+ checkFileContents(
356
+ `${projectDir}/sentry.properties`,
357
+ `auth_token=${TEST_ARGS.AUTH_TOKEN}`,
358
+ );
359
+ }
360
+
320
361
  /**
321
362
  * Check if the project builds
363
+ * Check if the project builds and ends with status code 0.
322
364
  * @param projectDir
323
365
  */
324
- export async function checkIfBuilds(
366
+ export async function checkIfBuilds(projectDir: string) {
367
+ const testEnv = new WizardTestEnv('npm', ['run', 'build'], {
368
+ cwd: projectDir,
369
+ });
370
+
371
+ await expect(
372
+ testEnv.waitForStatusCode(0, {
373
+ timeout: 120_000,
374
+ }),
375
+ ).resolves.toBe(true);
376
+ }
377
+
378
+ /**
379
+ * Check if the flutter project builds
380
+ * @param projectDir
381
+ */
382
+ export async function checkIfFlutterBuilds(
325
383
  projectDir: string,
326
384
  expectedOutput: string,
385
+ debug = false,
327
386
  ) {
328
- const testEnv = new WizardTestEnv('npm', ['run', 'build'], {
387
+ const testEnv = new WizardTestEnv('flutter', ['build', 'web'], {
329
388
  cwd: projectDir,
389
+ debug: debug,
330
390
  });
331
391
 
332
392
  await expect(
package/lib/Constants.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /** Key value should be the same here */
2
2
  export enum Integration {
3
3
  reactNative = 'reactNative',
4
+ flutter = 'flutter',
4
5
  ios = 'ios',
5
6
  android = 'android',
6
7
  cordova = 'cordova',
@@ -41,6 +42,8 @@ export function getIntegrationDescription(type: string): string {
41
42
  return 'Android';
42
43
  case Integration.reactNative:
43
44
  return 'React Native';
45
+ case Integration.flutter:
46
+ return 'Flutter';
44
47
  case Integration.cordova:
45
48
  return 'Cordova';
46
49
  case Integration.electron:
@@ -66,6 +69,8 @@ export function mapIntegrationToPlatform(type: string): string | undefined {
66
69
  return 'android';
67
70
  case Integration.reactNative:
68
71
  return 'react-native';
72
+ case Integration.flutter:
73
+ return 'flutter';
69
74
  case Integration.cordova:
70
75
  return 'cordova';
71
76
  case Integration.electron:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/wizard",
3
- "version": "3.38.0",
3
+ "version": "3.40.0",
4
4
  "homepage": "https://github.com/getsentry/sentry-wizard",
5
5
  "repository": "https://github.com/getsentry/sentry-wizard",
6
6
  "description": "Sentry wizard helping you to configure your project",
@@ -109,7 +109,7 @@ async function runAppleWizardWithTelementry(
109
109
  const availableTargets = xcProject.getAllTargets();
110
110
 
111
111
  if (availableTargets.length == 0) {
112
- clack.log.error(`No suttable target found in ${xcodeProjFile}`);
112
+ clack.log.error(`No suitable target found in ${xcodeProjFile}`);
113
113
  Sentry.setTag('No-Target', true);
114
114
  await abort();
115
115
  return;
@@ -10,7 +10,7 @@ import * as templates from './templates';
10
10
  import * as path from 'path';
11
11
  const xcode = require('xcode');
12
12
 
13
- interface ProjetFile {
13
+ interface ProjectFile {
14
14
  key: string;
15
15
  path: string;
16
16
  }
@@ -190,7 +190,7 @@ export class XcodeProject {
190
190
  projectPath: string;
191
191
  project: any;
192
192
  objects: any;
193
- files: ProjetFile[] | undefined;
193
+ files: ProjectFile[] | undefined;
194
194
 
195
195
  public constructor(projectPath: string) {
196
196
  this.projectPath = projectPath;
@@ -282,7 +282,7 @@ export class XcodeProject {
282
282
  .filter((f: string) => f.length > 0) as string[];
283
283
  }
284
284
 
285
- projectFiles(): ProjetFile[] {
285
+ projectFiles(): ProjectFile[] {
286
286
  if (this.files === undefined) {
287
287
  const proj = this.project.getFirstProject();
288
288
  const mainGroupKey = proj.firstProject.mainGroup;
@@ -292,8 +292,8 @@ export class XcodeProject {
292
292
  return this.files;
293
293
  }
294
294
 
295
- buildGroup(group: any, path = ''): ProjetFile[] {
296
- const result: ProjetFile[] = [];
295
+ buildGroup(group: any, path = ''): ProjectFile[] {
296
+ const result: ProjectFile[] = [];
297
297
  for (const child of group.children) {
298
298
  if (this.objects.PBXFileReference[child.value]) {
299
299
  const fileReference = this.objects.PBXFileReference[child.value];
@@ -0,0 +1,284 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as Sentry from '@sentry/node';
4
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
5
+ import * as clack from '@clack/prompts';
6
+ import chalk from 'chalk';
7
+ import {
8
+ sentryImport,
9
+ pubspecOptions,
10
+ sentryProperties,
11
+ initSnippet,
12
+ } from './templates';
13
+ import { featureSelectionPrompt } from '../utils/clack-utils';
14
+
15
+ /**
16
+ * Recursively finds a file per name in subfolders.
17
+ * @param dir - The directory to start searching.
18
+ * @param name - The name of the file including path extension.
19
+ * @returns The path to the main.dart file or null if not found.
20
+ */
21
+ export function findFile(dir: string, name: string): string | null {
22
+ const files: string[] = fs.readdirSync(dir);
23
+
24
+ for (const file of files) {
25
+ const fullPath: string = path.join(dir, file);
26
+ const stats: fs.Stats = fs.statSync(fullPath);
27
+
28
+ if (stats.isDirectory()) {
29
+ const result: string | null = findFile(fullPath, name);
30
+ if (result) {
31
+ return result;
32
+ }
33
+ } else if (file === name) {
34
+ return fullPath;
35
+ }
36
+ }
37
+
38
+ return null;
39
+ }
40
+
41
+ export function patchPubspec(
42
+ pubspecFile: string | null,
43
+ sentryDartFlutterVersion: string,
44
+ sentryDartPluginVersion: string,
45
+ project: string,
46
+ org: string,
47
+ ): boolean {
48
+ try {
49
+ if (!pubspecFile) {
50
+ throw new Error('pubspec.yaml is not provided or invalid.');
51
+ }
52
+
53
+ let pubspecContent = fs.readFileSync(pubspecFile, 'utf8');
54
+
55
+ if (!pubspecContent.includes('sentry_flutter:')) {
56
+ const dependenciesIndex = getDependenciesLocation(pubspecContent);
57
+
58
+ pubspecContent =
59
+ pubspecContent.slice(0, dependenciesIndex) +
60
+ ` sentry_flutter: ${sentryDartFlutterVersion}\n` +
61
+ pubspecContent.slice(dependenciesIndex);
62
+
63
+ clack.log.success(
64
+ chalk.greenBright(
65
+ `${chalk.bold('sentry_flutter')} added to pubspec.yaml`,
66
+ ),
67
+ );
68
+ } else {
69
+ clack.log.success(
70
+ chalk.greenBright(
71
+ `${chalk.bold('sentry_flutter')} is already included in pubspec.yaml`,
72
+ ),
73
+ );
74
+ }
75
+
76
+ if (!pubspecContent.includes('sentry_dart_plugin:')) {
77
+ const devDependenciesIndex = getDevDependenciesLocation(pubspecContent);
78
+ pubspecContent =
79
+ pubspecContent.slice(0, devDependenciesIndex) +
80
+ ` sentry_dart_plugin: ${sentryDartPluginVersion}\n` +
81
+ pubspecContent.slice(devDependenciesIndex);
82
+
83
+ clack.log.success(
84
+ chalk.greenBright(
85
+ `${chalk.bold('sentry_dart_plugin')} added to pubspec.yaml`,
86
+ ),
87
+ );
88
+ } else {
89
+ clack.log.success(
90
+ chalk.greenBright(
91
+ `${chalk.bold(
92
+ 'sentry_dart_plugin',
93
+ )} is already included in pubspec.yaml`,
94
+ ),
95
+ );
96
+ }
97
+
98
+ if (!pubspecContent.includes('sentry:')) {
99
+ pubspecContent += '\n';
100
+ pubspecContent += pubspecOptions(project, org);
101
+
102
+ clack.log.success(
103
+ chalk.greenBright(
104
+ `${chalk.bold('sentry plugin configuration')} added to pubspec.yaml`,
105
+ ),
106
+ );
107
+ } else {
108
+ clack.log.success(
109
+ chalk.greenBright(
110
+ `${chalk.bold(
111
+ 'sentry plugin configuration',
112
+ )} is already included in pubspec.yaml`,
113
+ ),
114
+ );
115
+ }
116
+
117
+ fs.writeFileSync(pubspecFile, pubspecContent, 'utf8');
118
+
119
+ return true;
120
+ } catch (error) {
121
+ clack.log.warn(`Failed to read/write ${chalk.cyan('pubspec.yaml')} file.`);
122
+ Sentry.captureException(error);
123
+ return false;
124
+ }
125
+ }
126
+
127
+ export function addProperties(pubspecFile: string | null, authToken: string) {
128
+ try {
129
+ if (!pubspecFile) {
130
+ throw new Error('pubspec.yaml is not provided or invalid.');
131
+ }
132
+
133
+ const pubspecDir = path.dirname(pubspecFile);
134
+ const sentryPropertiesFileName = 'sentry.properties';
135
+ const sentryPropertiesFile = path.join(
136
+ pubspecDir,
137
+ sentryPropertiesFileName,
138
+ );
139
+ const sentryPropertiesContent = sentryProperties(authToken);
140
+
141
+ fs.writeFileSync(sentryPropertiesFile, sentryPropertiesContent, 'utf8');
142
+
143
+ const gitignoreFile = path.join(pubspecDir, '.gitignore');
144
+ if (fs.existsSync(gitignoreFile)) {
145
+ fs.appendFileSync(gitignoreFile, `\n${sentryPropertiesFileName}\n`);
146
+ } else {
147
+ fs.writeFileSync(gitignoreFile, `${sentryPropertiesFileName}\n`, 'utf8');
148
+ }
149
+ return true;
150
+ } catch (error) {
151
+ clack.log.warn(`Failed to read/write ${chalk.cyan('pubspec.yaml')} file.`);
152
+ Sentry.captureException(error);
153
+ return false;
154
+ }
155
+ }
156
+
157
+ export async function patchMain(
158
+ mainFile: string | null,
159
+ dsn: string,
160
+ canEnableProfiling: boolean,
161
+ ): Promise<boolean> {
162
+ try {
163
+ if (!mainFile) {
164
+ throw new Error('pubspec.yaml is not provided or invalid.');
165
+ }
166
+
167
+ let mainContent = fs.readFileSync(mainFile, 'utf8');
168
+ if (
169
+ /import\s+['"]package[:]sentry_flutter\/sentry_flutter\.dart['"];?/i.test(
170
+ mainContent,
171
+ )
172
+ ) {
173
+ // sentry is already configured
174
+ clack.log.success(
175
+ chalk.greenBright(
176
+ `${chalk.bold('main.dart')} already has Sentry configured.`,
177
+ ),
178
+ );
179
+ return true;
180
+ }
181
+
182
+ const features = [
183
+ {
184
+ id: 'tracing',
185
+ prompt: `Do you want to enable ${chalk.bold(
186
+ 'Tracing',
187
+ )} to track the performance of your application?`,
188
+ enabledHint: 'recommended',
189
+ },
190
+ ];
191
+ if (canEnableProfiling) {
192
+ features.push({
193
+ id: 'profiling',
194
+ prompt: `Do you want to enable ${chalk.bold(
195
+ 'Profiling',
196
+ )} to analyze CPU usage and optimize performance-critical code on iOS & macOS?`,
197
+ enabledHint: 'recommended, tracing must be enabled',
198
+ });
199
+ }
200
+
201
+ const selectedFeatures = await featureSelectionPrompt(features);
202
+ const normalizedSelectedFeatures = {
203
+ tracing: selectedFeatures.tracing ?? false,
204
+ profiling: selectedFeatures.profiling ?? false,
205
+ };
206
+ mainContent = patchMainContent(
207
+ dsn,
208
+ mainContent,
209
+ normalizedSelectedFeatures,
210
+ );
211
+
212
+ fs.writeFileSync(mainFile, mainContent, 'utf8');
213
+
214
+ clack.log.success(
215
+ chalk.greenBright(
216
+ `Patched ${chalk.bold(
217
+ 'main.dart',
218
+ )} with the Sentry setup and test error snippet.`,
219
+ ),
220
+ );
221
+
222
+ return true;
223
+ } catch (error) {
224
+ clack.log.warn(`Failed to read/write ${chalk.cyan('main.dart')} file.`);
225
+ Sentry.captureException(error);
226
+ return false;
227
+ }
228
+ }
229
+
230
+ export function patchMainContent(
231
+ dsn: string,
232
+ mainContent: string,
233
+ selectedFeatures: {
234
+ tracing: boolean;
235
+ profiling: boolean;
236
+ },
237
+ ): string {
238
+ const importIndex = getLastImportLineLocation(mainContent);
239
+ mainContent =
240
+ mainContent.slice(0, importIndex) +
241
+ sentryImport +
242
+ mainContent.slice(importIndex);
243
+
244
+ // Find and replace `runApp(...)`
245
+ mainContent = mainContent.replace(
246
+ /runApp\(([\s\S]*?)\);/g, // Match the `runApp(...)` invocation
247
+ (_, runAppArgs) => initSnippet(dsn, selectedFeatures, runAppArgs as string),
248
+ );
249
+
250
+ // Make the `main` function async if it's not already
251
+ mainContent = mainContent.replace(
252
+ /void\s+main\(\)\s*\{/g,
253
+ 'Future<void> main() async {',
254
+ );
255
+
256
+ return mainContent;
257
+ }
258
+
259
+ export function getLastImportLineLocation(sourceCode: string): number {
260
+ const importRegex = /import\s+['"].*['"].*;/gim;
261
+ return getLastReqExpLocation(sourceCode, importRegex);
262
+ }
263
+
264
+ export function getDependenciesLocation(sourceCode: string): number {
265
+ const dependencyRegex = /^dependencies:\s*$/gim;
266
+ return getLastReqExpLocation(sourceCode, dependencyRegex);
267
+ }
268
+
269
+ export function getDevDependenciesLocation(sourceCode: string): number {
270
+ const dependencyRegex = /^dev_dependencies:\s*$/gim;
271
+ return getLastReqExpLocation(sourceCode, dependencyRegex);
272
+ }
273
+
274
+ // Helper
275
+
276
+ function getLastReqExpLocation(sourceCode: string, regExp: RegExp): number {
277
+ let match = regExp.exec(sourceCode);
278
+ let importIndex = 0;
279
+ while (match) {
280
+ importIndex = match.index + match[0].length + 1;
281
+ match = regExp.exec(sourceCode);
282
+ }
283
+ return importIndex;
284
+ }