@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
@@ -0,0 +1,164 @@
1
+ import { WizardOptions } from '../utils/types';
2
+ import * as Sentry from '@sentry/node';
3
+ import * as codetools from './code-tools';
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { showCopyPasteInstructions } from '../utils/clack-utils';
7
+ import { pubspecSnippetColored, initSnippetColored } from './templates';
8
+ import { fetchSdkVersion } from '../utils/release-registry';
9
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
10
+ import * as clack from '@clack/prompts';
11
+ import chalk from 'chalk';
12
+
13
+ import {
14
+ confirmContinueIfNoOrDirtyGitRepo,
15
+ getOrAskForProjectData,
16
+ printWelcome,
17
+ } from '../utils/clack-utils';
18
+
19
+ import { traceStep, withTelemetry } from '../telemetry';
20
+ import { findFile } from './code-tools';
21
+
22
+ export async function runFlutterWizard(options: WizardOptions): Promise<void> {
23
+ return withTelemetry(
24
+ {
25
+ enabled: options.telemetryEnabled,
26
+ integration: 'flutter',
27
+ wizardOptions: options,
28
+ },
29
+ () => runFlutterWizardWithTelemetry(options),
30
+ );
31
+ }
32
+
33
+ async function runFlutterWizardWithTelemetry(
34
+ options: WizardOptions,
35
+ ): Promise<void> {
36
+ printWelcome({
37
+ wizardName: 'Sentry Flutter Wizard',
38
+ promoCode: options.promoCode,
39
+ });
40
+
41
+ await confirmContinueIfNoOrDirtyGitRepo();
42
+
43
+ const { selectedProject, selfHosted, sentryUrl, authToken } =
44
+ await getOrAskForProjectData(options, 'flutter');
45
+
46
+ const projectDir = process.cwd();
47
+ const pubspecFile = path.join(projectDir, 'pubspec.yaml');
48
+ if (!fs.existsSync(pubspecFile)) {
49
+ clack.log.error(
50
+ `Could not find ${chalk.cyan(
51
+ 'pubspec.yaml',
52
+ )}. Make sure you run the wizard in the projects root folder.`,
53
+ );
54
+ return;
55
+ }
56
+
57
+ // ======== STEP 1. Add sentry_flutter and sentry_dart_plugin to pubspec.yaml ============
58
+
59
+ clack.log.step(
60
+ `Adding ${chalk.bold('Sentry')} to your apps ${chalk.cyan(
61
+ 'pubspec.yaml',
62
+ )} file.`,
63
+ );
64
+
65
+ const flutterVersion = await fetchSdkVersion('sentry.dart.flutter');
66
+ const flutterVersionOrAny = flutterVersion ? `^${flutterVersion}` : 'any';
67
+
68
+ const pluginVersion = await fetchSdkVersion('sentry.dart.plugin');
69
+ const pluginVersionOrAny = pluginVersion ? `^${pluginVersion}` : 'any';
70
+
71
+ const pubspecPatched = traceStep('Patch pubspec.yaml', () =>
72
+ codetools.patchPubspec(
73
+ pubspecFile,
74
+ flutterVersionOrAny,
75
+ pluginVersionOrAny,
76
+ selectedProject.slug,
77
+ selectedProject.organization.slug,
78
+ ),
79
+ );
80
+ if (!pubspecPatched) {
81
+ clack.log.warn(
82
+ `Could not patch ${chalk.cyan(
83
+ 'pubspec.yaml',
84
+ )}. Add the dependencies to it.`,
85
+ );
86
+ await showCopyPasteInstructions(
87
+ 'pubspec.yaml',
88
+ pubspecSnippetColored(
89
+ flutterVersionOrAny,
90
+ pluginVersionOrAny,
91
+ selectedProject.slug,
92
+ selectedProject.organization.slug,
93
+ ),
94
+ 'This ensures the Sentry SDK and plugin can be imported.',
95
+ );
96
+ }
97
+ Sentry.setTag('pubspec-patched', pubspecPatched);
98
+
99
+ // ======== STEP 2. Add sentry.properties with auth token ============
100
+
101
+ const propertiesAdded = traceStep('Add sentry.properties', () =>
102
+ codetools.addProperties(pubspecFile, authToken),
103
+ );
104
+ if (!propertiesAdded) {
105
+ clack.log.warn(
106
+ `We could not add ${chalk.cyan(
107
+ 'sentry.properties',
108
+ )} file in your project directory in order to provide an auth token for Sentry CLI. You'll have to add it manually, or you can set the SENTRY_AUTH_TOKEN environment variable instead. See https://docs.sentry.io/cli/configuration/#auth-token for more information.`,
109
+ );
110
+ } else {
111
+ clack.log.info(
112
+ `We created ${chalk.cyan(
113
+ 'sentry.properties',
114
+ )} file in your project directory in order to provide an auth token for Sentry CLI.\nIt was also added to your ".gitignore" file.\nAt your CI enviroment, you can set the SENTRY_AUTH_TOKEN environment variable instead. See https://docs.sentry.io/cli/configuration/#auth-token for more information.`,
115
+ );
116
+ }
117
+ Sentry.setTag('sentry-properties-added', pubspecPatched);
118
+
119
+ // ======== STEP 3. Patch main.dart with setup and a test error snippet ============
120
+
121
+ clack.log.step(
122
+ `Patching ${chalk.cyan('main.dart')} with setup and test error snippet.`,
123
+ );
124
+
125
+ const mainFile = findFile(`${projectDir}/lib`, 'main.dart');
126
+ const dsn = selectedProject.keys[0].dsn.public;
127
+ const canEnableProfiling =
128
+ fs.existsSync(`${projectDir}/ios`) || fs.existsSync(`${projectDir}/macos`);
129
+
130
+ const mainPatched = await traceStep('Patch main.dart', () =>
131
+ codetools.patchMain(mainFile, dsn, canEnableProfiling),
132
+ );
133
+ if (!mainPatched) {
134
+ clack.log.warn(
135
+ `Could not patch ${chalk.cyan(
136
+ 'main.dart',
137
+ )} file. Place the following code snippet within the apps main function.`,
138
+ );
139
+ await showCopyPasteInstructions(
140
+ 'main.dart',
141
+ initSnippetColored(dsn),
142
+ 'This ensures the Sentry SDK is ready to capture errors.',
143
+ );
144
+ }
145
+ Sentry.setTag('main-patched', mainPatched);
146
+
147
+ // ======== OUTRO ========
148
+
149
+ const issuesPageLink = selfHosted
150
+ ? `${sentryUrl}organizations/${selectedProject.organization.slug}/issues/?project=${selectedProject.id}`
151
+ : `https://${selectedProject.organization.slug}.sentry.io/issues/?project=${selectedProject.id}`;
152
+
153
+ clack.outro(`
154
+ ${chalk.greenBright('Successfully installed the Sentry Flutter SDK!')}
155
+
156
+ ${chalk.cyan(
157
+ `You can validate your setup by launching your application and checking Sentry issues page afterwards
158
+ ${issuesPageLink}`,
159
+ )}
160
+
161
+ Check out the SDK documentation for further configuration:
162
+ https://docs.sentry.io/platforms/flutter/
163
+ `);
164
+ }
@@ -0,0 +1,90 @@
1
+ import { makeCodeSnippet } from '../utils/clack-utils';
2
+
3
+ export const sentryImport = `import 'package:sentry_flutter/sentry_flutter.dart';\n`;
4
+
5
+ export function pubspecOptions(project: string, org: string): string {
6
+ return `sentry:
7
+ upload_debug_symbols: true
8
+ upload_source_maps: true
9
+ project: ${project}
10
+ org: ${org}
11
+ `;
12
+ }
13
+
14
+ export function sentryProperties(authToken: string): string {
15
+ return `auth_token=${authToken}`;
16
+ }
17
+
18
+ export function initSnippet(
19
+ dsn: string,
20
+ selectedFeaturesMap: {
21
+ tracing: boolean;
22
+ profiling: boolean;
23
+ },
24
+ runApp: string,
25
+ ): string {
26
+ let snippet = `await SentryFlutter.init(
27
+ (options) {
28
+ options.dsn = '${dsn}';`;
29
+
30
+ if (selectedFeaturesMap.tracing) {
31
+ snippet += `
32
+ // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
33
+ // We recommend adjusting this value in production.
34
+ options.tracesSampleRate = 1.0;`;
35
+ }
36
+
37
+ if (selectedFeaturesMap.profiling && selectedFeaturesMap.tracing) {
38
+ snippet += `
39
+ // The sampling rate for profiling is relative to tracesSampleRate
40
+ // Setting to 1.0 will profile 100% of sampled transactions:
41
+ options.profilesSampleRate = 1.0;`;
42
+ }
43
+
44
+ snippet += `
45
+ },
46
+ appRunner: () => runApp(SentryWidget(child: ${runApp})),
47
+ );
48
+ // TODO: Remove this line after sending the first sample event to sentry.
49
+ await Sentry.captureException(Exception('This is a sample exception.'));`;
50
+
51
+ return snippet;
52
+ }
53
+
54
+ export function pubspecSnippetColored(
55
+ sentryVersion: string,
56
+ pluginVersion: string,
57
+ project: string,
58
+ org: string,
59
+ ): string {
60
+ const snippet = `dependencies:
61
+ sentry_flutter: ${sentryVersion}
62
+
63
+ dev_dependencies:
64
+ sentry_dart_plugin: ${pluginVersion}
65
+
66
+ ${pubspecOptions(project, org)}`;
67
+
68
+ return makeCodeSnippet(true, (_unchanged, plus, _minus) => {
69
+ return plus(snippet);
70
+ });
71
+ }
72
+
73
+ export function initSnippetColored(dsn: string): string {
74
+ const snippet = `import 'package:sentry_flutter/sentry_flutter.dart';
75
+
76
+ Future<void>main() async {
77
+ await SentryFlutter.init(
78
+ (options) {
79
+ options.dsn = '${dsn}';
80
+ // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
81
+ // We recommend adjusting this value in production.
82
+ options.tracesSampleRate = 1.0;
83
+ },
84
+ appRunner: () => runApp(SentryWidget(child: YourApp())),
85
+ )
86
+ }`;
87
+ return makeCodeSnippet(true, (_unchanged, plus, _minus) => {
88
+ return plus(snippet);
89
+ });
90
+ }
@@ -926,7 +926,7 @@ async function createExamplePage(
926
926
  * It's valuable enough to for users to justify asking the additional question.
927
927
  */
928
928
  async function askShouldSetTunnelRoute() {
929
- return await traceStep('ask-tunnelRoute-option', async () => {
929
+ return await traceStep('ask-tunnelRoute-option', async (span) => {
930
930
  const shouldSetTunnelRoute = await abortIfCancelled(
931
931
  clack.select({
932
932
  message:
@@ -953,6 +953,9 @@ async function askShouldSetTunnelRoute() {
953
953
  );
954
954
  }
955
955
 
956
+ span?.setAttribute('tunnelRoute', shouldSetTunnelRoute);
957
+ Sentry.setTag('tunnelRoute', shouldSetTunnelRoute);
958
+
956
959
  return shouldSetTunnelRoute;
957
960
  });
958
961
  }
package/src/run.ts CHANGED
@@ -5,6 +5,7 @@ import { runReactNativeWizard } from './react-native/react-native-wizard';
5
5
 
6
6
  import { run as legacyRun } from '../lib/Setup';
7
7
  import type { PreselectedProject, WizardOptions } from './utils/types';
8
+ import { runFlutterWizard } from './flutter/flutter-wizard';
8
9
  import { runAndroidWizard } from './android/android-wizard';
9
10
  import { runAppleWizard } from './apple/apple-wizard';
10
11
  import { runNextjsWizard } from './nextjs/nextjs-wizard';
@@ -18,6 +19,7 @@ import type { PackageDotJson } from './utils/package-json';
18
19
 
19
20
  type WizardIntegration =
20
21
  | 'reactNative'
22
+ | 'flutter'
21
23
  | 'ios'
22
24
  | 'android'
23
25
  | 'cordova'
@@ -100,6 +102,7 @@ export async function run(argv: Args) {
100
102
  message: 'What do you want to set up?',
101
103
  options: [
102
104
  { value: 'reactNative', label: 'React Native' },
105
+ { value: 'flutter', label: 'Flutter' },
103
106
  { value: 'ios', label: 'iOS' },
104
107
  { value: 'android', label: 'Android' },
105
108
  { value: 'cordova', label: 'Cordova' },
@@ -139,6 +142,10 @@ export async function run(argv: Args) {
139
142
  });
140
143
  break;
141
144
 
145
+ case 'flutter':
146
+ await runFlutterWizard(wizardOptions);
147
+ break;
148
+
142
149
  case 'ios':
143
150
  await runAppleWizard(wizardOptions);
144
151
  break;
@@ -267,13 +267,9 @@ export async function configureCI(
267
267
  'create-react-app',
268
268
  ].includes(selectedTool);
269
269
 
270
- // some non-cli-based flows also use the .sentryclirc file
271
- const usesSentryCliRc = selectedTool === 'nextjs';
272
-
273
- const authTokenFile =
274
- isCliBasedFlowTool || usesSentryCliRc
275
- ? SENTRY_CLI_RC_FILE
276
- : SENTRY_DOT_ENV_FILE;
270
+ const authTokenFile = isCliBasedFlowTool
271
+ ? SENTRY_CLI_RC_FILE
272
+ : SENTRY_DOT_ENV_FILE;
277
273
 
278
274
  if (!isUsingCI) {
279
275
  clack.log.info(
package/src/telemetry.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  setTag,
10
10
  startSpan,
11
11
  flush,
12
+ Span,
12
13
  } from '@sentry/node';
13
14
  import packageJson from '../package.json';
14
15
  import { WizardOptions } from './utils/types';
@@ -110,9 +111,12 @@ function createSentryInstance(enabled: boolean, integration: string) {
110
111
  return { sentryHub: hub, sentryClient: client };
111
112
  }
112
113
 
113
- export function traceStep<T>(step: string, callback: () => T): T {
114
+ export function traceStep<T>(
115
+ step: string,
116
+ callback: (span: Span | undefined) => T,
117
+ ): T {
114
118
  updateProgress(step);
115
- return startSpan({ name: step, op: 'wizard.step' }, () => callback());
119
+ return startSpan({ name: step, op: 'wizard.step' }, (span) => callback(span));
116
120
  }
117
121
 
118
122
  export function updateProgress(step: string) {
@@ -881,7 +881,8 @@ export async function getOrAskForProjectData(
881
881
  | 'javascript-sveltekit'
882
882
  | 'apple-ios'
883
883
  | 'android'
884
- | 'react-native',
884
+ | 'react-native'
885
+ | 'flutter',
885
886
  ): Promise<{
886
887
  sentryUrl: string;
887
888
  selfHosted: boolean;
@@ -1043,7 +1044,8 @@ async function askForWizardLogin(options: {
1043
1044
  | 'javascript-sveltekit'
1044
1045
  | 'apple-ios'
1045
1046
  | 'android'
1046
- | 'react-native';
1047
+ | 'react-native'
1048
+ | 'flutter';
1047
1049
  orgSlug?: string;
1048
1050
  projectSlug?: string;
1049
1051
  }): Promise<WizardProjectData> {
@@ -1096,9 +1098,10 @@ async function askForWizardLogin(options: {
1096
1098
 
1097
1099
  if (!hasSentryAccount) {
1098
1100
  loginUrl.searchParams.set('signup', '1');
1099
- if (options.platform) {
1100
- loginUrl.searchParams.set('project_platform', options.platform);
1101
- }
1101
+ }
1102
+
1103
+ if (options.platform) {
1104
+ loginUrl.searchParams.set('project_platform', options.platform);
1102
1105
  }
1103
1106
 
1104
1107
  if (options.promoCode) {
@@ -9,7 +9,6 @@ import { getPackageDotJson, updatePackageDotJson } from './clack-utils';
9
9
  export interface PackageManager {
10
10
  name: string;
11
11
  label: string;
12
- lockFile: string;
13
12
  installCommand: string;
14
13
  buildCommand: string;
15
14
  /* The command that the package manager uses to run a script from package.json */
@@ -22,12 +21,14 @@ export interface PackageManager {
22
21
  export const BUN: PackageManager = {
23
22
  name: 'bun',
24
23
  label: 'Bun',
25
- lockFile: 'bun.lockb',
26
24
  installCommand: 'bun add',
27
25
  buildCommand: 'bun run build',
28
26
  runScriptCommand: 'bun run',
29
27
  flags: '',
30
- detect: () => fs.existsSync(path.join(process.cwd(), BUN.lockFile)),
28
+ detect: () =>
29
+ ['bun.lockb', 'bun.lock'].some((lockFile) =>
30
+ fs.existsSync(path.join(process.cwd(), lockFile)),
31
+ ),
31
32
  addOverride: async (pkgName, pkgVersion): Promise<void> => {
32
33
  const packageDotJson = await getPackageDotJson();
33
34
  const overrides = packageDotJson.overrides || {};
@@ -44,7 +45,6 @@ export const BUN: PackageManager = {
44
45
  export const YARN_V1: PackageManager = {
45
46
  name: 'yarn',
46
47
  label: 'Yarn V1',
47
- lockFile: 'yarn.lock',
48
48
  installCommand: 'yarn add',
49
49
  buildCommand: 'yarn build',
50
50
  runScriptCommand: 'yarn',
@@ -52,7 +52,7 @@ export const YARN_V1: PackageManager = {
52
52
  detect: () => {
53
53
  try {
54
54
  return fs
55
- .readFileSync(path.join(process.cwd(), YARN_V1.lockFile), 'utf-8')
55
+ .readFileSync(path.join(process.cwd(), 'yarn.lock'), 'utf-8')
56
56
  .slice(0, 500)
57
57
  .includes('yarn lockfile v1');
58
58
  } catch (e) {
@@ -76,7 +76,6 @@ export const YARN_V1: PackageManager = {
76
76
  export const YARN_V2: PackageManager = {
77
77
  name: 'yarn',
78
78
  label: 'Yarn V2/3/4',
79
- lockFile: 'yarn.lock',
80
79
  installCommand: 'yarn add',
81
80
  buildCommand: 'yarn build',
82
81
  runScriptCommand: 'yarn',
@@ -84,7 +83,7 @@ export const YARN_V2: PackageManager = {
84
83
  detect: () => {
85
84
  try {
86
85
  return fs
87
- .readFileSync(path.join(process.cwd(), YARN_V2.lockFile), 'utf-8')
86
+ .readFileSync(path.join(process.cwd(), 'yarn.lock'), 'utf-8')
88
87
  .slice(0, 500)
89
88
  .includes('__metadata');
90
89
  } catch (e) {
@@ -107,12 +106,11 @@ export const YARN_V2: PackageManager = {
107
106
  export const PNPM: PackageManager = {
108
107
  name: 'pnpm',
109
108
  label: 'PNPM',
110
- lockFile: 'pnpm-lock.yaml',
111
109
  installCommand: 'pnpm add',
112
110
  buildCommand: 'pnpm build',
113
111
  runScriptCommand: 'pnpm',
114
112
  flags: '--ignore-workspace-root-check',
115
- detect: () => fs.existsSync(path.join(process.cwd(), PNPM.lockFile)),
113
+ detect: () => fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml')),
116
114
  addOverride: async (pkgName, pkgVersion): Promise<void> => {
117
115
  const packageDotJson = await getPackageDotJson();
118
116
  const pnpm = packageDotJson.pnpm || {};
@@ -133,12 +131,11 @@ export const PNPM: PackageManager = {
133
131
  export const NPM: PackageManager = {
134
132
  name: 'npm',
135
133
  label: 'NPM',
136
- lockFile: 'package-lock.json',
137
134
  installCommand: 'npm add',
138
135
  buildCommand: 'npm run build',
139
136
  runScriptCommand: 'npm run',
140
137
  flags: '',
141
- detect: () => fs.existsSync(path.join(process.cwd(), NPM.lockFile)),
138
+ detect: () => fs.existsSync(path.join(process.cwd(), 'package-lock.json')),
142
139
  addOverride: async (pkgName, pkgVersion): Promise<void> => {
143
140
  const packageDotJson = await getPackageDotJson();
144
141
  const overrides = packageDotJson.overrides || {};
@@ -0,0 +1,212 @@
1
+ //@ts-ignore
2
+ import {
3
+ patchMainContent,
4
+ getDependenciesLocation,
5
+ getDevDependenciesLocation,
6
+ getLastImportLineLocation,
7
+ } from '../../src/flutter/code-tools';
8
+ //@ts-ignore
9
+ import { initSnippet } from '../../src/flutter/templates';
10
+
11
+ describe('code-tools', () => {
12
+ const pubspec = `name: flutter_example
13
+ description: An example flutter app.
14
+ version: 1.0.0
15
+ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16
+
17
+ environment:
18
+ sdk: '>=2.17.0 <4.0.0'
19
+ flutter: '>=3.0.0'
20
+
21
+ dependencies:
22
+ flutter:
23
+ sdk: flutter
24
+
25
+ dev_dependencies:
26
+ flutter_lints: ^2.0.0
27
+ `;
28
+
29
+ const simpleRunApp = `import 'package:flutter/widgets.dart';
30
+
31
+ void main() {
32
+ runApp(const MyApp());
33
+ }
34
+ `;
35
+
36
+ const asyncRunApp = `import 'package:flutter/widgets.dart';
37
+
38
+ void main() {
39
+ runApp(const MyApp());
40
+ }
41
+ `;
42
+
43
+ const selectedFeaturesMap = {
44
+ tracing: true,
45
+ profiling: true,
46
+ };
47
+
48
+ const simpleRunAppPatched = `import 'package:flutter/widgets.dart';
49
+ import 'package:sentry_flutter/sentry_flutter.dart';
50
+
51
+ Future<void> main() async {
52
+ ${initSnippet('dsn', selectedFeaturesMap, 'const MyApp()')}
53
+ }
54
+ `;
55
+
56
+ const paramRunApp = `import 'package:flutter/widgets.dart';
57
+
58
+ Future<void> main() async {
59
+ await someFunction();
60
+ runApp(MyApp(param: SomeParam()));
61
+ await anotherFunction();
62
+ }
63
+ `;
64
+
65
+ const paramRunAppPatched = `import 'package:flutter/widgets.dart';
66
+ import 'package:sentry_flutter/sentry_flutter.dart';
67
+
68
+ Future<void> main() async {
69
+ await someFunction();
70
+ ${initSnippet('dsn', selectedFeaturesMap, 'MyApp(param: SomeParam())')}
71
+ await anotherFunction();
72
+ }
73
+ `;
74
+
75
+ const multilineRunApp = `import 'package:flutter/widgets.dart';
76
+
77
+ void main() {
78
+ runApp(
79
+ MyApp(
80
+ param: Param(),
81
+ multi: Another(1),
82
+ line: await bites(the: "dust"),
83
+ ),
84
+ );
85
+ anotherFunction();
86
+ }
87
+ `;
88
+
89
+ const multilineRunAppPatched = `import 'package:flutter/widgets.dart';
90
+ import 'package:sentry_flutter/sentry_flutter.dart';
91
+
92
+ Future<void> main() async {
93
+ ${initSnippet(
94
+ 'dsn',
95
+ selectedFeaturesMap,
96
+ `
97
+ MyApp(
98
+ param: Param(),
99
+ multi: Another(1),
100
+ line: await bites(the: "dust"),
101
+ ),
102
+ `,
103
+ )}
104
+ anotherFunction();
105
+ }
106
+ `;
107
+
108
+ describe('patchMainContent', () => {
109
+ it('wraps simple runApp', () => {
110
+ expect(patchMainContent('dsn', simpleRunApp, selectedFeaturesMap)).toBe(
111
+ simpleRunAppPatched,
112
+ );
113
+ });
114
+
115
+ it('wraps async runApp', () => {
116
+ expect(patchMainContent('dsn', asyncRunApp, selectedFeaturesMap)).toBe(
117
+ simpleRunAppPatched,
118
+ );
119
+ });
120
+
121
+ it('wraps runApp with parameterized app', () => {
122
+ expect(patchMainContent('dsn', paramRunApp, selectedFeaturesMap)).toBe(
123
+ paramRunAppPatched,
124
+ );
125
+ });
126
+
127
+ it('wraps multiline runApp', () => {
128
+ expect(
129
+ patchMainContent('dsn', multilineRunApp, selectedFeaturesMap),
130
+ ).toBe(multilineRunAppPatched);
131
+ });
132
+ });
133
+
134
+ describe('pubspec', () => {
135
+ it('returns proper line index for dependencies', () => {
136
+ expect(getDependenciesLocation(pubspec)).toBe(
137
+ pubspec.indexOf(' flutter:\n'),
138
+ );
139
+ });
140
+
141
+ it('returns proper line index for dev-dependencies', () => {
142
+ expect(getDevDependenciesLocation(pubspec)).toBe(
143
+ pubspec.indexOf(' flutter_lints: ^2.0.0\n'),
144
+ );
145
+ });
146
+ });
147
+
148
+ describe('getLastImportLineLocation', () => {
149
+ it('returns proper line index', () => {
150
+ const code =
151
+ `import 'foo:bar';\n` + `//<insert-location>\n` + `class X {}`;
152
+ expect(getLastImportLineLocation(code)).toBe(
153
+ code.indexOf('//<insert-location>'),
154
+ );
155
+ });
156
+
157
+ it('returns proper line index when alias import is used', () => {
158
+ const code =
159
+ `import 'package:my_library/utils.dart' as utils;\n` +
160
+ `//<insert-location>\n` +
161
+ `class X {}`;
162
+ expect(getLastImportLineLocation(code)).toBe(
163
+ code.indexOf('//<insert-location>'),
164
+ );
165
+ });
166
+
167
+ it('returns proper line index when specific parts import is used', () => {
168
+ const code =
169
+ `import 'dart:math' show pi, sin;\n` +
170
+ `//<insert-location>\n` +
171
+ `class X {}`;
172
+ expect(getLastImportLineLocation(code)).toBe(
173
+ code.indexOf('//<insert-location>'),
174
+ );
175
+ });
176
+
177
+ it('returns proper line index when hide import is used', () => {
178
+ const code =
179
+ `import 'dart:math' hide Random;\n` +
180
+ `//<insert-location>\n` +
181
+ `class X {}`;
182
+ expect(getLastImportLineLocation(code)).toBe(
183
+ code.indexOf('//<insert-location>'),
184
+ );
185
+ });
186
+
187
+ it('returns proper line index when deferred import is used', () => {
188
+ const code =
189
+ `import 'package:my_library/large_library.dart' deferred as largeLibrary;\n` +
190
+ `//<insert-location>\n` +
191
+ `class X {}`;
192
+ expect(getLastImportLineLocation(code)).toBe(
193
+ code.indexOf('//<insert-location>'),
194
+ );
195
+ });
196
+
197
+ it('returns proper line index when multiple imports (with newlines) are present', () => {
198
+ const code =
199
+ `import 'foo:bar';\n` +
200
+ `import 'package:my_library/utils.dart' as utils;\n` +
201
+ `import 'dart:math' show pi, sin;\n` +
202
+ `import 'dart:math' hide Random;\n` +
203
+ `\n` +
204
+ `import 'package:my_library/large_library.dart' deferred as largeLibrary;\n` +
205
+ `//<insert-location>\n` +
206
+ `class X {}`;
207
+ expect(getLastImportLineLocation(code)).toBe(
208
+ code.indexOf('//<insert-location>'),
209
+ );
210
+ });
211
+ });
212
+ });