@sentry/wizard 3.42.1 → 4.0.1

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 (585) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/bin.js +44 -35
  3. package/dist/bin.js.map +1 -1
  4. package/dist/e2e-tests/jest.config.js +1 -1
  5. package/dist/e2e-tests/jest.config.js.map +1 -1
  6. package/dist/e2e-tests/tests/flutter.test.js +62 -147
  7. package/dist/e2e-tests/tests/flutter.test.js.map +1 -1
  8. package/dist/e2e-tests/tests/nextjs.test.js +77 -175
  9. package/dist/e2e-tests/tests/nextjs.test.js.map +1 -1
  10. package/dist/e2e-tests/tests/nuxt-3.test.js +61 -162
  11. package/dist/e2e-tests/tests/nuxt-3.test.js.map +1 -1
  12. package/dist/e2e-tests/tests/nuxt-4.test.js +62 -163
  13. package/dist/e2e-tests/tests/nuxt-4.test.js.map +1 -1
  14. package/dist/e2e-tests/tests/remix.test.js +147 -189
  15. package/dist/e2e-tests/tests/remix.test.js.map +1 -1
  16. package/dist/e2e-tests/tests/sveltekit.test.js +133 -187
  17. package/dist/e2e-tests/tests/sveltekit.test.js.map +1 -1
  18. package/dist/e2e-tests/utils/index.d.ts +1 -1
  19. package/dist/e2e-tests/utils/index.js +113 -195
  20. package/dist/e2e-tests/utils/index.js.map +1 -1
  21. package/dist/lib/Constants.js +5 -5
  22. package/dist/lib/Constants.js.map +1 -1
  23. package/dist/lib/Helper/BottomBar.d.ts +1 -1
  24. package/dist/lib/Helper/BottomBar.js +14 -15
  25. package/dist/lib/Helper/BottomBar.js.map +1 -1
  26. package/dist/lib/Helper/Env.js +5 -2
  27. package/dist/lib/Helper/Env.js.map +1 -1
  28. package/dist/lib/Helper/File.js +14 -27
  29. package/dist/lib/Helper/File.js.map +1 -1
  30. package/dist/lib/Helper/Git.js +24 -59
  31. package/dist/lib/Helper/Git.js.map +1 -1
  32. package/dist/lib/Helper/Logging.js +2 -2
  33. package/dist/lib/Helper/Logging.js.map +1 -1
  34. package/dist/lib/Helper/MergeConfig.js +4 -4
  35. package/dist/lib/Helper/MergeConfig.js.map +1 -1
  36. package/dist/lib/Helper/Package.d.ts +4 -1
  37. package/dist/lib/Helper/Package.js +12 -38
  38. package/dist/lib/Helper/Package.js.map +1 -1
  39. package/dist/lib/Helper/SentryCli.js +77 -149
  40. package/dist/lib/Helper/SentryCli.js.map +1 -1
  41. package/dist/lib/Helper/Wizard.js +29 -124
  42. package/dist/lib/Helper/Wizard.js.map +1 -1
  43. package/dist/lib/Helper/__tests__/File.js +4 -4
  44. package/dist/lib/Helper/__tests__/File.js.map +1 -1
  45. package/dist/lib/Helper/__tests__/MergeConfig.js +24 -24
  46. package/dist/lib/Helper/__tests__/MergeConfig.js.map +1 -1
  47. package/dist/lib/Helper/__tests__/SentryCli.js +33 -23
  48. package/dist/lib/Helper/__tests__/SentryCli.js.map +1 -1
  49. package/dist/lib/Setup.js +23 -85
  50. package/dist/lib/Setup.js.map +1 -1
  51. package/dist/lib/Steps/BaseStep.js +8 -8
  52. package/dist/lib/Steps/BaseStep.js.map +1 -1
  53. package/dist/lib/Steps/ChooseIntegration.js +80 -125
  54. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  55. package/dist/lib/Steps/ConfigureProject.js +6 -66
  56. package/dist/lib/Steps/ConfigureProject.js.map +1 -1
  57. package/dist/lib/Steps/Initial.js +16 -104
  58. package/dist/lib/Steps/Initial.js.map +1 -1
  59. package/dist/lib/Steps/Integrations/BaseIntegration.js +24 -118
  60. package/dist/lib/Steps/Integrations/BaseIntegration.js.map +1 -1
  61. package/dist/lib/Steps/Integrations/Cordova.js +95 -183
  62. package/dist/lib/Steps/Integrations/Cordova.js.map +1 -1
  63. package/dist/lib/Steps/Integrations/Electron.js +87 -145
  64. package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
  65. package/dist/lib/Steps/Integrations/MobileProject.js +40 -148
  66. package/dist/lib/Steps/Integrations/MobileProject.js.map +1 -1
  67. package/dist/lib/Steps/OpenSentry.js +63 -126
  68. package/dist/lib/Steps/OpenSentry.js.map +1 -1
  69. package/dist/lib/Steps/PromptForParameters.js +110 -206
  70. package/dist/lib/Steps/PromptForParameters.js.map +1 -1
  71. package/dist/lib/Steps/Result.js +19 -79
  72. package/dist/lib/Steps/Result.js.map +1 -1
  73. package/dist/lib/Steps/SentryProjectSelector.js +57 -148
  74. package/dist/lib/Steps/SentryProjectSelector.js.map +1 -1
  75. package/dist/lib/Steps/ShouldConfigure.js +6 -66
  76. package/dist/lib/Steps/ShouldConfigure.js.map +1 -1
  77. package/dist/lib/Steps/WaitForSentry.js +43 -120
  78. package/dist/lib/Steps/WaitForSentry.js.map +1 -1
  79. package/dist/lib/Steps/Welcome.js +17 -76
  80. package/dist/lib/Steps/Welcome.js.map +1 -1
  81. package/dist/lib/__tests__/Env.js +3 -3
  82. package/dist/lib/__tests__/Env.js.map +1 -1
  83. package/dist/src/android/android-wizard.js +100 -176
  84. package/dist/src/android/android-wizard.js.map +1 -1
  85. package/dist/src/android/code-tools.js +24 -33
  86. package/dist/src/android/code-tools.js.map +1 -1
  87. package/dist/src/android/gradle.js +106 -165
  88. package/dist/src/android/gradle.js.map +1 -1
  89. package/dist/src/android/manifest.js +26 -27
  90. package/dist/src/android/manifest.js.map +1 -1
  91. package/dist/src/android/templates.js +76 -23
  92. package/dist/src/android/templates.js.map +1 -1
  93. package/dist/src/apple/apple-wizard.js +129 -225
  94. package/dist/src/apple/apple-wizard.js.map +1 -1
  95. package/dist/src/apple/cocoapod.js +47 -109
  96. package/dist/src/apple/cocoapod.js.map +1 -1
  97. package/dist/src/apple/code-tools.js +32 -35
  98. package/dist/src/apple/code-tools.js.map +1 -1
  99. package/dist/src/apple/fastlane.js +51 -97
  100. package/dist/src/apple/fastlane.js.map +1 -1
  101. package/dist/src/apple/templates.js +41 -5
  102. package/dist/src/apple/templates.js.map +1 -1
  103. package/dist/src/apple/xcode-manager.d.ts +2 -2
  104. package/dist/src/apple/xcode-manager.js +93 -108
  105. package/dist/src/apple/xcode-manager.js.map +1 -1
  106. package/dist/src/flutter/code-tools.js +79 -127
  107. package/dist/src/flutter/code-tools.js.map +1 -1
  108. package/dist/src/flutter/flutter-wizard.js +75 -136
  109. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  110. package/dist/src/flutter/templates.js +48 -12
  111. package/dist/src/flutter/templates.js.map +1 -1
  112. package/dist/src/nextjs/nextjs-wizard.js +527 -805
  113. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  114. package/dist/src/nextjs/templates.js +380 -40
  115. package/dist/src/nextjs/templates.js.map +1 -1
  116. package/dist/src/nextjs/utils.js +5 -5
  117. package/dist/src/nextjs/utils.js.map +1 -1
  118. package/dist/src/nuxt/nuxt-wizard.js +91 -188
  119. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  120. package/dist/src/nuxt/sdk-example.js +68 -137
  121. package/dist/src/nuxt/sdk-example.js.map +1 -1
  122. package/dist/src/nuxt/sdk-setup.d.ts +4 -4
  123. package/dist/src/nuxt/sdk-setup.js +180 -336
  124. package/dist/src/nuxt/sdk-setup.js.map +1 -1
  125. package/dist/src/nuxt/templates.js +195 -18
  126. package/dist/src/nuxt/templates.js.map +1 -1
  127. package/dist/src/nuxt/utils.js +29 -76
  128. package/dist/src/nuxt/utils.js.map +1 -1
  129. package/dist/src/react-native/expo-env-file.js +41 -94
  130. package/dist/src/react-native/expo-env-file.js.map +1 -1
  131. package/dist/src/react-native/expo-metro.d.ts +1 -1
  132. package/dist/src/react-native/expo-metro.js +75 -130
  133. package/dist/src/react-native/expo-metro.js.map +1 -1
  134. package/dist/src/react-native/expo.js +61 -114
  135. package/dist/src/react-native/expo.js.map +1 -1
  136. package/dist/src/react-native/git.js +23 -56
  137. package/dist/src/react-native/git.js.map +1 -1
  138. package/dist/src/react-native/glob.js +3 -3
  139. package/dist/src/react-native/glob.js.map +1 -1
  140. package/dist/src/react-native/gradle.js +4 -4
  141. package/dist/src/react-native/gradle.js.map +1 -1
  142. package/dist/src/react-native/javascript.js +51 -95
  143. package/dist/src/react-native/javascript.js.map +1 -1
  144. package/dist/src/react-native/metro.js +176 -296
  145. package/dist/src/react-native/metro.js.map +1 -1
  146. package/dist/src/react-native/react-native-wizard.js +267 -418
  147. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  148. package/dist/src/react-native/uninstall.js +37 -80
  149. package/dist/src/react-native/uninstall.js.map +1 -1
  150. package/dist/src/react-native/xcode.js +57 -55
  151. package/dist/src/react-native/xcode.js.map +1 -1
  152. package/dist/src/remix/codemods/express-server.js +27 -82
  153. package/dist/src/remix/codemods/express-server.js.map +1 -1
  154. package/dist/src/remix/codemods/handle-error.js +28 -31
  155. package/dist/src/remix/codemods/handle-error.js.map +1 -1
  156. package/dist/src/remix/codemods/root-common.js +11 -11
  157. package/dist/src/remix/codemods/root-common.js.map +1 -1
  158. package/dist/src/remix/codemods/root-v1.js +17 -67
  159. package/dist/src/remix/codemods/root-v1.js.map +1 -1
  160. package/dist/src/remix/codemods/root-v2.js +89 -140
  161. package/dist/src/remix/codemods/root-v2.js.map +1 -1
  162. package/dist/src/remix/remix-wizard.js +181 -343
  163. package/dist/src/remix/remix-wizard.js.map +1 -1
  164. package/dist/src/remix/sdk-example.js +84 -66
  165. package/dist/src/remix/sdk-example.js.map +1 -1
  166. package/dist/src/remix/sdk-setup.js +161 -293
  167. package/dist/src/remix/sdk-setup.js.map +1 -1
  168. package/dist/src/remix/templates.js +10 -2
  169. package/dist/src/remix/templates.js.map +1 -1
  170. package/dist/src/remix/utils.js +13 -14
  171. package/dist/src/remix/utils.js.map +1 -1
  172. package/dist/src/run.js +106 -178
  173. package/dist/src/run.js.map +1 -1
  174. package/dist/src/sourcemaps/sourcemaps-wizard.js +248 -372
  175. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  176. package/dist/src/sourcemaps/tools/angular.js +32 -66
  177. package/dist/src/sourcemaps/tools/angular.js.map +1 -1
  178. package/dist/src/sourcemaps/tools/create-react-app.js +14 -59
  179. package/dist/src/sourcemaps/tools/create-react-app.js.map +1 -1
  180. package/dist/src/sourcemaps/tools/esbuild.js +34 -77
  181. package/dist/src/sourcemaps/tools/esbuild.js.map +1 -1
  182. package/dist/src/sourcemaps/tools/nextjs.d.ts +2 -2
  183. package/dist/src/sourcemaps/tools/nextjs.js +75 -102
  184. package/dist/src/sourcemaps/tools/nextjs.js.map +1 -1
  185. package/dist/src/sourcemaps/tools/remix.js +53 -93
  186. package/dist/src/sourcemaps/tools/remix.js.map +1 -1
  187. package/dist/src/sourcemaps/tools/rollup.js +35 -76
  188. package/dist/src/sourcemaps/tools/rollup.js.map +1 -1
  189. package/dist/src/sourcemaps/tools/sentry-cli.js +157 -289
  190. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  191. package/dist/src/sourcemaps/tools/tsc.js +68 -134
  192. package/dist/src/sourcemaps/tools/tsc.js.map +1 -1
  193. package/dist/src/sourcemaps/tools/vite.js +111 -196
  194. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  195. package/dist/src/sourcemaps/tools/webpack.d.ts +1 -1
  196. package/dist/src/sourcemaps/tools/webpack.js +158 -260
  197. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  198. package/dist/src/sourcemaps/utils/detect-tool.js +9 -54
  199. package/dist/src/sourcemaps/utils/detect-tool.js.map +1 -1
  200. package/dist/src/sourcemaps/utils/other-wizards.js +77 -144
  201. package/dist/src/sourcemaps/utils/other-wizards.js.map +1 -1
  202. package/dist/src/sourcemaps/utils/sdk-version.js +112 -191
  203. package/dist/src/sourcemaps/utils/sdk-version.js.map +1 -1
  204. package/dist/src/sveltekit/sdk-example.js +20 -70
  205. package/dist/src/sveltekit/sdk-example.js.map +1 -1
  206. package/dist/src/sveltekit/sdk-setup.js +250 -411
  207. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  208. package/dist/src/sveltekit/sveltekit-wizard.js +107 -191
  209. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  210. package/dist/src/sveltekit/templates.js +147 -13
  211. package/dist/src/sveltekit/templates.js.map +1 -1
  212. package/dist/src/sveltekit/utils.js +3 -3
  213. package/dist/src/sveltekit/utils.js.map +1 -1
  214. package/dist/src/telemetry.d.ts +2 -2
  215. package/dist/src/telemetry.js +58 -108
  216. package/dist/src/telemetry.js.map +1 -1
  217. package/dist/src/utils/ast-utils.js +39 -54
  218. package/dist/src/utils/ast-utils.js.map +1 -1
  219. package/dist/src/utils/bash.js +28 -75
  220. package/dist/src/utils/bash.js.map +1 -1
  221. package/dist/src/utils/clack-utils.d.ts +3 -7
  222. package/dist/src/utils/clack-utils.js +688 -1083
  223. package/dist/src/utils/clack-utils.js.map +1 -1
  224. package/dist/src/utils/debug.js +6 -10
  225. package/dist/src/utils/debug.js.map +1 -1
  226. package/dist/src/utils/package-json.js +5 -6
  227. package/dist/src/utils/package-json.js.map +1 -1
  228. package/dist/src/utils/package-manager.js +65 -139
  229. package/dist/src/utils/package-manager.js.map +1 -1
  230. package/dist/src/utils/release-registry.js +12 -59
  231. package/dist/src/utils/release-registry.js.map +1 -1
  232. package/dist/src/utils/semver.js +3 -4
  233. package/dist/src/utils/semver.js.map +1 -1
  234. package/dist/src/utils/sentrycli-utils.js +4 -4
  235. package/dist/src/utils/sentrycli-utils.js.map +1 -1
  236. package/dist/src/utils/url.js +5 -6
  237. package/dist/src/utils/url.js.map +1 -1
  238. package/dist/test/android/code-tools.test.js +18 -18
  239. package/dist/test/android/code-tools.test.js.map +1 -1
  240. package/dist/test/apple/cocoapod.test.js +154 -299
  241. package/dist/test/apple/cocoapod.test.js.map +1 -1
  242. package/dist/test/apple/code-tools.test.js +375 -263
  243. package/dist/test/apple/code-tools.test.js.map +1 -1
  244. package/dist/test/apple/fastfile.test.js +319 -271
  245. package/dist/test/apple/fastfile.test.js.map +1 -1
  246. package/dist/test/apple/templates.test.js +114 -30
  247. package/dist/test/apple/templates.test.js.map +1 -1
  248. package/dist/test/apple/xcode-manager.test.js +230 -234
  249. package/dist/test/apple/xcode-manager.test.js.map +1 -1
  250. package/dist/test/flutter/code-tools.test.js +119 -48
  251. package/dist/test/flutter/code-tools.test.js.map +1 -1
  252. package/dist/test/flutter/templates.test.js +63 -20
  253. package/dist/test/flutter/templates.test.js.map +1 -1
  254. package/dist/test/nextjs/templates.test.js +332 -43
  255. package/dist/test/nextjs/templates.test.js.map +1 -1
  256. package/dist/test/nuxt/templates.test.js +161 -33
  257. package/dist/test/nuxt/templates.test.js.map +1 -1
  258. package/dist/test/react-native/expo-metro.test.js +61 -15
  259. package/dist/test/react-native/expo-metro.test.js.map +1 -1
  260. package/dist/test/react-native/expo.test.js +37 -17
  261. package/dist/test/react-native/expo.test.js.map +1 -1
  262. package/dist/test/react-native/gradle.test.js +260 -28
  263. package/dist/test/react-native/gradle.test.js.map +1 -1
  264. package/dist/test/react-native/javascript.test.js +85 -19
  265. package/dist/test/react-native/javascript.test.js.map +1 -1
  266. package/dist/test/react-native/metro.test.js +261 -187
  267. package/dist/test/react-native/metro.test.js.map +1 -1
  268. package/dist/test/react-native/xcode.test.js +243 -62
  269. package/dist/test/react-native/xcode.test.js.map +1 -1
  270. package/dist/test/remix/client-entry.test.js +81 -21
  271. package/dist/test/remix/client-entry.test.js.map +1 -1
  272. package/dist/test/remix/server-instrumentation.test.js +21 -8
  273. package/dist/test/remix/server-instrumentation.test.js.map +1 -1
  274. package/dist/test/sourcemaps/tools/sentry-cli.test.js +20 -72
  275. package/dist/test/sourcemaps/tools/sentry-cli.test.js.map +1 -1
  276. package/dist/test/sourcemaps/tools/tsc.test.js +142 -68
  277. package/dist/test/sourcemaps/tools/tsc.test.js.map +1 -1
  278. package/dist/test/sourcemaps/tools/vite.test.js +106 -75
  279. package/dist/test/sourcemaps/tools/vite.test.js.map +1 -1
  280. package/dist/test/sourcemaps/tools/webpack.test.js +226 -102
  281. package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -1
  282. package/dist/test/sveltekit/templates.test.js +117 -18
  283. package/dist/test/sveltekit/templates.test.js.map +1 -1
  284. package/dist/test/utils/ast-utils.test.js +99 -58
  285. package/dist/test/utils/ast-utils.test.js.map +1 -1
  286. package/dist/test/utils/clack-utils.test.js +142 -247
  287. package/dist/test/utils/clack-utils.test.js.map +1 -1
  288. package/package.json +16 -9
  289. package/bin.ts +0 -138
  290. package/codecov.yml +0 -15
  291. package/dist/package.json +0 -128
  292. package/e2e-tests/.env.example +0 -11
  293. package/e2e-tests/README.md +0 -63
  294. package/e2e-tests/jest.config.ts +0 -22
  295. package/e2e-tests/package.json +0 -14
  296. package/e2e-tests/run.sh +0 -15
  297. package/e2e-tests/test-applications/apple/damaged-missing-configuration-list/Project.xcodeproj/project.pbxproj +0 -52
  298. package/e2e-tests/test-applications/apple/damaged-missing-configuration-list/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +0 -78
  299. package/e2e-tests/test-applications/apple/no-targets/Project.xcodeproj/project.pbxproj +0 -62
  300. package/e2e-tests/test-applications/apple/no-targets/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +0 -78
  301. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project.xcodeproj/project.pbxproj +0 -470
  302. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +0 -78
  303. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project1/ContentView.swift +0 -7
  304. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project1/Project1App.swift +0 -10
  305. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project2/ContentView.swift +0 -7
  306. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project2/Project2App.swift +0 -10
  307. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Project.xcodeproj/project.pbxproj +0 -382
  308. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Project.xcodeproj/xcshareddata/xcschemes/Project.xcscheme +0 -78
  309. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Sources/ContentView.swift +0 -7
  310. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Sources/MainApp.swift +0 -10
  311. package/e2e-tests/test-applications/flutter-test-app/.metadata +0 -45
  312. package/e2e-tests/test-applications/flutter-test-app/README.md +0 -16
  313. package/e2e-tests/test-applications/flutter-test-app/analysis_options.yaml +0 -28
  314. package/e2e-tests/test-applications/flutter-test-app/android/app/build.gradle +0 -44
  315. package/e2e-tests/test-applications/flutter-test-app/android/app/src/debug/AndroidManifest.xml +0 -7
  316. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/AndroidManifest.xml +0 -45
  317. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/kotlin/com/example/flutter_magic/MainActivity.kt +0 -5
  318. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable/launch_background.xml +0 -12
  319. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable-v21/launch_background.xml +0 -12
  320. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  321. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  322. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  323. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  324. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  325. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values/styles.xml +0 -18
  326. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values-night/styles.xml +0 -18
  327. package/e2e-tests/test-applications/flutter-test-app/android/app/src/profile/AndroidManifest.xml +0 -7
  328. package/e2e-tests/test-applications/flutter-test-app/android/build.gradle +0 -18
  329. package/e2e-tests/test-applications/flutter-test-app/android/gradle/wrapper/gradle-wrapper.properties +0 -5
  330. package/e2e-tests/test-applications/flutter-test-app/android/gradle.properties +0 -3
  331. package/e2e-tests/test-applications/flutter-test-app/android/settings.gradle +0 -25
  332. package/e2e-tests/test-applications/flutter-test-app/lib/main.dart +0 -125
  333. package/e2e-tests/test-applications/flutter-test-app/linux/CMakeLists.txt +0 -145
  334. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/CMakeLists.txt +0 -88
  335. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.cc +0 -11
  336. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.h +0 -15
  337. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugins.cmake +0 -23
  338. package/e2e-tests/test-applications/flutter-test-app/linux/main.cc +0 -6
  339. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.cc +0 -124
  340. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.h +0 -18
  341. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Debug.xcconfig +0 -2
  342. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Release.xcconfig +0 -2
  343. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/GeneratedPluginRegistrant.swift +0 -10
  344. package/e2e-tests/test-applications/flutter-test-app/macos/Podfile +0 -43
  345. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/AppDelegate.swift +0 -9
  346. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -68
  347. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  348. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  349. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  350. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  351. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  352. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  353. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  354. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Base.lproj/MainMenu.xib +0 -343
  355. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/AppInfo.xcconfig +0 -14
  356. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Debug.xcconfig +0 -2
  357. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Release.xcconfig +0 -2
  358. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Warnings.xcconfig +0 -13
  359. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/DebugProfile.entitlements +0 -12
  360. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Info.plist +0 -32
  361. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/MainFlutterWindow.swift +0 -15
  362. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Release.entitlements +0 -8
  363. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.pbxproj +0 -705
  364. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  365. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +0 -98
  366. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/contents.xcworkspacedata +0 -7
  367. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  368. package/e2e-tests/test-applications/flutter-test-app/macos/RunnerTests/RunnerTests.swift +0 -12
  369. package/e2e-tests/test-applications/flutter-test-app/pubspec.lock +0 -213
  370. package/e2e-tests/test-applications/flutter-test-app/pubspec.yaml +0 -89
  371. package/e2e-tests/test-applications/flutter-test-app/test/widget_test.dart +0 -30
  372. package/e2e-tests/test-applications/flutter-test-app/web/favicon.png +0 -0
  373. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-192.png +0 -0
  374. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-512.png +0 -0
  375. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-192.png +0 -0
  376. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-512.png +0 -0
  377. package/e2e-tests/test-applications/flutter-test-app/web/index.html +0 -38
  378. package/e2e-tests/test-applications/flutter-test-app/web/manifest.json +0 -35
  379. package/e2e-tests/test-applications/flutter-test-app/windows/CMakeLists.txt +0 -108
  380. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/CMakeLists.txt +0 -109
  381. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.cc +0 -11
  382. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.h +0 -15
  383. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugins.cmake +0 -23
  384. package/e2e-tests/test-applications/flutter-test-app/windows/runner/CMakeLists.txt +0 -40
  385. package/e2e-tests/test-applications/flutter-test-app/windows/runner/Runner.rc +0 -121
  386. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.cpp +0 -71
  387. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.h +0 -33
  388. package/e2e-tests/test-applications/flutter-test-app/windows/runner/main.cpp +0 -43
  389. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resource.h +0 -16
  390. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resources/app_icon.ico +0 -0
  391. package/e2e-tests/test-applications/flutter-test-app/windows/runner/runner.exe.manifest +0 -14
  392. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.cpp +0 -65
  393. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.h +0 -19
  394. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.cpp +0 -288
  395. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.h +0 -102
  396. package/e2e-tests/test-applications/nextjs-test-app/next.config.mjs +0 -4
  397. package/e2e-tests/test-applications/nextjs-test-app/package.json +0 -22
  398. package/e2e-tests/test-applications/nextjs-test-app/src/app/layout.tsx +0 -20
  399. package/e2e-tests/test-applications/nextjs-test-app/src/app/page.tsx +0 -90
  400. package/e2e-tests/test-applications/nuxt-3-test-app/README.md +0 -75
  401. package/e2e-tests/test-applications/nuxt-3-test-app/nuxt.config.ts +0 -5
  402. package/e2e-tests/test-applications/nuxt-3-test-app/package.json +0 -18
  403. package/e2e-tests/test-applications/nuxt-3-test-app/public/favicon.ico +0 -0
  404. package/e2e-tests/test-applications/nuxt-3-test-app/public/robots.txt +0 -1
  405. package/e2e-tests/test-applications/nuxt-4-test-app/README.md +0 -75
  406. package/e2e-tests/test-applications/nuxt-4-test-app/nuxt.config.ts +0 -6
  407. package/e2e-tests/test-applications/nuxt-4-test-app/package.json +0 -18
  408. package/e2e-tests/test-applications/nuxt-4-test-app/public/favicon.ico +0 -0
  409. package/e2e-tests/test-applications/nuxt-4-test-app/public/robots.txt +0 -1
  410. package/e2e-tests/test-applications/remix-test-app/app/entry.client.tsx +0 -18
  411. package/e2e-tests/test-applications/remix-test-app/app/entry.server.tsx +0 -140
  412. package/e2e-tests/test-applications/remix-test-app/app/root.tsx +0 -30
  413. package/e2e-tests/test-applications/remix-test-app/app/routes/_index.tsx +0 -48
  414. package/e2e-tests/test-applications/remix-test-app/app/tailwind.css +0 -3
  415. package/e2e-tests/test-applications/remix-test-app/package.json +0 -37
  416. package/e2e-tests/test-applications/remix-test-app/postcss.config.js +0 -6
  417. package/e2e-tests/test-applications/remix-test-app/tailwind.config.ts +0 -9
  418. package/e2e-tests/test-applications/remix-test-app/vite.config.ts +0 -16
  419. package/e2e-tests/test-applications/sveltekit-test-app/package.json +0 -21
  420. package/e2e-tests/test-applications/sveltekit-test-app/src/app.d.ts +0 -13
  421. package/e2e-tests/test-applications/sveltekit-test-app/src/app.html +0 -11
  422. package/e2e-tests/test-applications/sveltekit-test-app/src/lib/index.ts +0 -1
  423. package/e2e-tests/test-applications/sveltekit-test-app/src/routes/+page.svelte +0 -2
  424. package/e2e-tests/test-applications/sveltekit-test-app/svelte.config.js +0 -18
  425. package/e2e-tests/test-applications/sveltekit-test-app/vite.config.ts +0 -6
  426. package/e2e-tests/tests/flutter.test.ts +0 -127
  427. package/e2e-tests/tests/nextjs.test.ts +0 -161
  428. package/e2e-tests/tests/nuxt-3.test.ts +0 -189
  429. package/e2e-tests/tests/nuxt-4.test.ts +0 -188
  430. package/e2e-tests/tests/remix.test.ts +0 -277
  431. package/e2e-tests/tests/sveltekit.test.ts +0 -284
  432. package/e2e-tests/utils/index.ts +0 -438
  433. package/index.ts +0 -2
  434. package/lib/Constants.ts +0 -118
  435. package/lib/Helper/BottomBar.ts +0 -28
  436. package/lib/Helper/Env.ts +0 -7
  437. package/lib/Helper/File.ts +0 -65
  438. package/lib/Helper/Git.ts +0 -39
  439. package/lib/Helper/Logging.ts +0 -44
  440. package/lib/Helper/MergeConfig.ts +0 -19
  441. package/lib/Helper/Package.ts +0 -78
  442. package/lib/Helper/SentryCli.ts +0 -148
  443. package/lib/Helper/Wizard.ts +0 -59
  444. package/lib/Helper/__tests__/File.ts +0 -15
  445. package/lib/Helper/__tests__/MergeConfig.ts +0 -98
  446. package/lib/Helper/__tests__/SentryCli.ts +0 -86
  447. package/lib/Helper/test-fixtures/next.config.1-merged.js +0 -18
  448. package/lib/Helper/test-fixtures/next.config.1.js +0 -6
  449. package/lib/Helper/test-fixtures/next.config.2.js +0 -8
  450. package/lib/Helper/test-fixtures/next.config.3-merged.js +0 -21
  451. package/lib/Helper/test-fixtures/next.config.3.js +0 -9
  452. package/lib/Helper/test-fixtures/next.config.4-merged.js +0 -21
  453. package/lib/Helper/test-fixtures/next.config.4.js +0 -9
  454. package/lib/Setup.ts +0 -41
  455. package/lib/Steps/BaseStep.ts +0 -25
  456. package/lib/Steps/ChooseIntegration.ts +0 -112
  457. package/lib/Steps/ConfigureProject.ts +0 -10
  458. package/lib/Steps/Initial.ts +0 -44
  459. package/lib/Steps/Integrations/BaseIntegration.ts +0 -48
  460. package/lib/Steps/Integrations/Cordova.ts +0 -289
  461. package/lib/Steps/Integrations/Electron.ts +0 -161
  462. package/lib/Steps/Integrations/MobileProject.ts +0 -69
  463. package/lib/Steps/OpenSentry.ts +0 -75
  464. package/lib/Steps/PromptForParameters.ts +0 -207
  465. package/lib/Steps/Result.ts +0 -22
  466. package/lib/Steps/SentryProjectSelector.ts +0 -82
  467. package/lib/Steps/ShouldConfigure.ts +0 -10
  468. package/lib/Steps/WaitForSentry.ts +0 -50
  469. package/lib/Steps/Welcome.ts +0 -21
  470. package/lib/Steps/index.ts +0 -10
  471. package/lib/__tests__/Env.ts +0 -29
  472. package/scripts/NextJs/configs/_error.js +0 -39
  473. package/scripts/NextJs/configs/next.config.js +0 -36
  474. package/scripts/NextJs/configs/next.config.template.js +0 -12
  475. package/scripts/NextJs/configs/sentry.client.config.js +0 -17
  476. package/scripts/NextJs/configs/sentry.edge.config.js +0 -17
  477. package/scripts/NextJs/configs/sentry.server.config.js +0 -17
  478. package/scripts/NextJs/sentry_sample_error.js +0 -47
  479. package/scripts/craft-pre-release.sh +0 -10
  480. package/src/android/android-wizard.ts +0 -192
  481. package/src/android/code-tools.ts +0 -170
  482. package/src/android/gradle.ts +0 -250
  483. package/src/android/manifest.ts +0 -180
  484. package/src/android/templates.ts +0 -86
  485. package/src/apple/apple-wizard.ts +0 -269
  486. package/src/apple/cocoapod.ts +0 -73
  487. package/src/apple/code-tools.ts +0 -147
  488. package/src/apple/fastlane.ts +0 -170
  489. package/src/apple/templates.ts +0 -65
  490. package/src/apple/xcode-manager.ts +0 -404
  491. package/src/flutter/code-tools.ts +0 -284
  492. package/src/flutter/flutter-wizard.ts +0 -164
  493. package/src/flutter/templates.ts +0 -90
  494. package/src/nextjs/nextjs-wizard.ts +0 -1007
  495. package/src/nextjs/templates.ts +0 -526
  496. package/src/nextjs/utils.ts +0 -21
  497. package/src/nuxt/nuxt-wizard.ts +0 -188
  498. package/src/nuxt/sdk-example.ts +0 -135
  499. package/src/nuxt/sdk-setup.ts +0 -349
  500. package/src/nuxt/templates.ts +0 -303
  501. package/src/nuxt/types.ts +0 -8
  502. package/src/nuxt/utils.ts +0 -42
  503. package/src/react-native/expo-env-file.ts +0 -55
  504. package/src/react-native/expo-metro.ts +0 -212
  505. package/src/react-native/expo.ts +0 -175
  506. package/src/react-native/git.ts +0 -25
  507. package/src/react-native/glob.ts +0 -13
  508. package/src/react-native/gradle.ts +0 -26
  509. package/src/react-native/javascript.ts +0 -103
  510. package/src/react-native/metro.ts +0 -599
  511. package/src/react-native/options.ts +0 -5
  512. package/src/react-native/react-native-wizard.ts +0 -513
  513. package/src/react-native/uninstall.ts +0 -110
  514. package/src/react-native/xcode.ts +0 -302
  515. package/src/remix/codemods/express-server.ts +0 -44
  516. package/src/remix/codemods/handle-error.ts +0 -118
  517. package/src/remix/codemods/root-common.ts +0 -63
  518. package/src/remix/codemods/root-v1.ts +0 -41
  519. package/src/remix/codemods/root-v2.ts +0 -153
  520. package/src/remix/remix-wizard.ts +0 -261
  521. package/src/remix/sdk-example.ts +0 -120
  522. package/src/remix/sdk-setup.ts +0 -546
  523. package/src/remix/templates.ts +0 -11
  524. package/src/remix/utils.ts +0 -96
  525. package/src/run.ts +0 -204
  526. package/src/sourcemaps/sourcemaps-wizard.ts +0 -364
  527. package/src/sourcemaps/tools/angular.ts +0 -42
  528. package/src/sourcemaps/tools/create-react-app.ts +0 -19
  529. package/src/sourcemaps/tools/esbuild.ts +0 -65
  530. package/src/sourcemaps/tools/nextjs.ts +0 -114
  531. package/src/sourcemaps/tools/remix.ts +0 -90
  532. package/src/sourcemaps/tools/rollup.ts +0 -67
  533. package/src/sourcemaps/tools/sentry-cli.ts +0 -287
  534. package/src/sourcemaps/tools/tsc.ts +0 -144
  535. package/src/sourcemaps/tools/types.ts +0 -11
  536. package/src/sourcemaps/tools/vite.ts +0 -300
  537. package/src/sourcemaps/tools/webpack.ts +0 -383
  538. package/src/sourcemaps/utils/detect-tool.ts +0 -46
  539. package/src/sourcemaps/utils/other-wizards.ts +0 -167
  540. package/src/sourcemaps/utils/sdk-version.ts +0 -266
  541. package/src/sveltekit/sdk-example.ts +0 -56
  542. package/src/sveltekit/sdk-setup.ts +0 -667
  543. package/src/sveltekit/sveltekit-wizard.ts +0 -192
  544. package/src/sveltekit/templates.ts +0 -185
  545. package/src/sveltekit/utils.ts +0 -50
  546. package/src/telemetry.ts +0 -124
  547. package/src/utils/ast-utils.ts +0 -270
  548. package/src/utils/bash.ts +0 -57
  549. package/src/utils/clack-utils.ts +0 -1533
  550. package/src/utils/debug.ts +0 -20
  551. package/src/utils/package-json.ts +0 -51
  552. package/src/utils/package-manager.ts +0 -172
  553. package/src/utils/release-registry.ts +0 -19
  554. package/src/utils/semver.ts +0 -33
  555. package/src/utils/sentrycli-utils.ts +0 -24
  556. package/src/utils/string.ts +0 -7
  557. package/src/utils/types.ts +0 -77
  558. package/src/utils/url.ts +0 -27
  559. package/src/utils/vendor/is-unicorn-supported.ts +0 -29
  560. package/test/android/code-tools.test.ts +0 -49
  561. package/test/apple/cocoapod.test.ts +0 -306
  562. package/test/apple/code-tools.test.ts +0 -1042
  563. package/test/apple/fastfile.test.ts +0 -550
  564. package/test/apple/templates.test.ts +0 -191
  565. package/test/apple/xcode-manager.test.ts +0 -1066
  566. package/test/flutter/code-tools.test.ts +0 -212
  567. package/test/flutter/templates.test.ts +0 -100
  568. package/test/nextjs/templates.test.ts +0 -429
  569. package/test/nuxt/templates.test.ts +0 -255
  570. package/test/react-native/expo-metro.test.ts +0 -81
  571. package/test/react-native/expo.test.ts +0 -86
  572. package/test/react-native/gradle.test.ts +0 -310
  573. package/test/react-native/javascript.test.ts +0 -134
  574. package/test/react-native/metro.test.ts +0 -396
  575. package/test/react-native/xcode.test.ts +0 -401
  576. package/test/remix/client-entry.test.ts +0 -122
  577. package/test/remix/server-instrumentation.test.ts +0 -36
  578. package/test/sourcemaps/tools/sentry-cli.test.ts +0 -57
  579. package/test/sourcemaps/tools/tsc.test.ts +0 -181
  580. package/test/sourcemaps/tools/vite.test.ts +0 -149
  581. package/test/sourcemaps/tools/webpack.test.ts +0 -303
  582. package/test/sveltekit/templates.test.ts +0 -152
  583. package/test/utils/ast-utils.test.ts +0 -264
  584. package/test/utils/clack-utils.test.ts +0 -234
  585. package/types/xcode.d.ts +0 -526
@@ -1,1533 +0,0 @@
1
- // @ts-ignore - clack is ESM and TS complains about that. It works though
2
- import * as clack from '@clack/prompts';
3
- import axios from 'axios';
4
- import chalk from 'chalk';
5
- import * as childProcess from 'child_process';
6
- import * as fs from 'fs';
7
- import * as path from 'path';
8
- import * as os from 'os';
9
- import { setInterval } from 'timers';
10
- import { URL } from 'url';
11
- import * as Sentry from '@sentry/node';
12
- import { hasPackageInstalled, PackageDotJson } from './package-json';
13
- import { Feature, SentryProjectData, WizardOptions } from './types';
14
- import { traceStep } from '../telemetry';
15
- import {
16
- detectPackageManger,
17
- PackageManager,
18
- packageManagers,
19
- } from './package-manager';
20
- import { debug } from './debug';
21
- import { fulfillsVersionRange } from './semver';
22
-
23
- export const opn = require('opn') as (
24
- url: string,
25
- options?: {
26
- wait?: boolean;
27
- },
28
- ) => Promise<childProcess.ChildProcess>;
29
-
30
- export const SENTRY_DOT_ENV_FILE = '.env.sentry-build-plugin';
31
- export const SENTRY_CLI_RC_FILE = '.sentryclirc';
32
- export const SENTRY_PROPERTIES_FILE = 'sentry.properties';
33
-
34
- const SAAS_URL = 'https://sentry.io/';
35
-
36
- const DUMMY_AUTH_TOKEN = '_YOUR_SENTRY_AUTH_TOKEN_';
37
-
38
- interface WizardProjectData {
39
- apiKeys?: {
40
- token?: string;
41
- };
42
- projects?: SentryProjectData[];
43
- }
44
-
45
- export interface CliSetupConfig {
46
- filename: string;
47
- name: string;
48
- gitignore: boolean;
49
-
50
- likelyAlreadyHasAuthToken(contents: string): boolean;
51
- tokenContent(authToken: string): string;
52
-
53
- likelyAlreadyHasOrgAndProject(contents: string): boolean;
54
- orgAndProjContent(org: string, project: string): string;
55
-
56
- likelyAlreadyHasUrl?(contents: string): boolean;
57
- urlContent?(url: string): string;
58
- }
59
-
60
- export interface CliSetupConfigContent {
61
- authToken: string;
62
- org?: string;
63
- project?: string;
64
- url?: string;
65
- }
66
-
67
- export const rcCliSetupConfig: CliSetupConfig = {
68
- filename: SENTRY_CLI_RC_FILE,
69
- name: 'source maps',
70
- gitignore: true,
71
- likelyAlreadyHasAuthToken: function (contents: string): boolean {
72
- return !!(contents.includes('[auth]') && contents.match(/token=./g));
73
- },
74
- tokenContent: function (authToken: string): string {
75
- return `[auth]\ntoken=${authToken}`;
76
- },
77
- likelyAlreadyHasOrgAndProject: function (contents: string): boolean {
78
- return !!(
79
- contents.includes('[defaults]') &&
80
- contents.match(/org=./g) &&
81
- contents.match(/project=./g)
82
- );
83
- },
84
- orgAndProjContent: function (org: string, project: string): string {
85
- return `[defaults]\norg=${org}\nproject=${project}`;
86
- },
87
- };
88
-
89
- export const propertiesCliSetupConfig: Required<CliSetupConfig> = {
90
- filename: SENTRY_PROPERTIES_FILE,
91
- gitignore: true,
92
- name: 'debug files',
93
- likelyAlreadyHasAuthToken(contents: string): boolean {
94
- return !!contents.match(/auth\.token=./g);
95
- },
96
- tokenContent(authToken: string): string {
97
- return `auth.token=${authToken}`;
98
- },
99
- likelyAlreadyHasOrgAndProject(contents: string): boolean {
100
- return !!(
101
- contents.match(/defaults\.org=./g) &&
102
- contents.match(/defaults\.project=./g)
103
- );
104
- },
105
- orgAndProjContent(org: string, project: string): string {
106
- return `defaults.org=${org}\ndefaults.project=${project}`;
107
- },
108
- likelyAlreadyHasUrl(contents: string): boolean {
109
- return !!contents.match(/defaults\.url=./g);
110
- },
111
- urlContent(url: string): string {
112
- return `defaults.url=${url}`;
113
- },
114
- };
115
-
116
- export async function abort(message?: string, status?: number): Promise<never> {
117
- clack.outro(message ?? 'Wizard setup cancelled.');
118
- const sentryHub = Sentry.getCurrentHub();
119
- const sentryTransaction = sentryHub.getScope().getTransaction();
120
- sentryTransaction?.setStatus('aborted');
121
- sentryTransaction?.finish();
122
- const sentrySession = sentryHub.getScope().getSession();
123
- if (sentrySession) {
124
- sentrySession.status = status === 0 ? 'abnormal' : 'crashed';
125
- sentryHub.captureSession(true);
126
- }
127
- await Sentry.flush(3000);
128
- return process.exit(status ?? 1);
129
- }
130
-
131
- export async function abortIfCancelled<T>(
132
- input: T | Promise<T>,
133
- ): Promise<Exclude<T, symbol>> {
134
- if (clack.isCancel(await input)) {
135
- clack.cancel('Wizard setup cancelled.');
136
- const sentryHub = Sentry.getCurrentHub();
137
- const sentryTransaction = sentryHub.getScope().getTransaction();
138
- sentryTransaction?.setStatus('cancelled');
139
- sentryTransaction?.finish();
140
- sentryHub.captureSession(true);
141
- await Sentry.flush(3000);
142
- process.exit(0);
143
- } else {
144
- return input as Exclude<T, symbol>;
145
- }
146
- }
147
-
148
- export function printWelcome(options: {
149
- wizardName: string;
150
- promoCode?: string;
151
- message?: string;
152
- telemetryEnabled?: boolean;
153
- }): void {
154
- let wizardPackage: { version?: string } = {};
155
-
156
- try {
157
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
158
- wizardPackage = require(path.join(
159
- path.dirname(require.resolve('@sentry/wizard')),
160
- '..',
161
- 'package.json',
162
- ));
163
- } catch {
164
- // We don't need to have this
165
- }
166
-
167
- // eslint-disable-next-line no-console
168
- console.log('');
169
- clack.intro(chalk.inverse(` ${options.wizardName} `));
170
-
171
- let welcomeText =
172
- options.message ||
173
- `The ${options.wizardName} will help you set up Sentry for your application.\nThank you for using Sentry :)`;
174
-
175
- if (options.promoCode) {
176
- welcomeText = `${welcomeText}\n\nUsing promo-code: ${options.promoCode}`;
177
- }
178
-
179
- if (wizardPackage.version) {
180
- welcomeText = `${welcomeText}\n\nVersion: ${wizardPackage.version}`;
181
- }
182
-
183
- if (options.telemetryEnabled) {
184
- welcomeText = `${welcomeText}
185
-
186
- This wizard sends telemetry data and crash reports to Sentry. This helps us improve the Wizard.
187
- You can turn this off at any time by running ${chalk.cyanBright(
188
- 'sentry-wizard --disable-telemetry',
189
- )}.`;
190
- }
191
-
192
- clack.note(welcomeText);
193
- }
194
-
195
- export async function confirmContinueIfNoOrDirtyGitRepo(): Promise<void> {
196
- return traceStep('check-git-status', async () => {
197
- if (!isInGitRepo()) {
198
- const continueWithoutGit = await abortIfCancelled(
199
- clack.confirm({
200
- message:
201
- 'You are not inside a git repository. The wizard will create and update files. Do you want to continue anyway?',
202
- }),
203
- );
204
-
205
- Sentry.setTag('continue-without-git', continueWithoutGit);
206
-
207
- if (!continueWithoutGit) {
208
- await abort(undefined, 0);
209
- }
210
- // return early to avoid checking for uncommitted files
211
- return;
212
- }
213
-
214
- const uncommittedOrUntrackedFiles = getUncommittedOrUntrackedFiles();
215
- if (uncommittedOrUntrackedFiles.length) {
216
- clack.log.warn(
217
- `You have uncommitted or untracked files in your repo:
218
-
219
- ${uncommittedOrUntrackedFiles.join('\n')}
220
-
221
- The wizard will create and update files.`,
222
- );
223
- const continueWithDirtyRepo = await abortIfCancelled(
224
- clack.confirm({
225
- message: 'Do you want to continue anyway?',
226
- }),
227
- );
228
-
229
- Sentry.setTag('continue-with-dirty-repo', continueWithDirtyRepo);
230
-
231
- if (!continueWithDirtyRepo) {
232
- await abort(undefined, 0);
233
- }
234
- }
235
- });
236
- }
237
-
238
- export function isInGitRepo() {
239
- try {
240
- childProcess.execSync('git rev-parse --is-inside-work-tree', {
241
- stdio: 'ignore',
242
- });
243
- return true;
244
- } catch {
245
- return false;
246
- }
247
- }
248
-
249
- export function getUncommittedOrUntrackedFiles(): string[] {
250
- try {
251
- const gitStatus = childProcess
252
- .execSync('git status --porcelain=v1', {
253
- // we only care about stdout
254
- stdio: ['ignore', 'pipe', 'ignore'],
255
- })
256
- .toString();
257
-
258
- const files = gitStatus
259
- .split(os.EOL)
260
- .map((line) => line.trim())
261
- .filter(Boolean)
262
- .map((f) => `- ${f.split(/\s+/)[1]}`);
263
-
264
- return files;
265
- } catch {
266
- return [];
267
- }
268
- }
269
-
270
- export async function askToInstallSentryCLI(): Promise<boolean> {
271
- return await abortIfCancelled(
272
- clack.confirm({
273
- message:
274
- "You don't have Sentry CLI installed. Do you want to install it?",
275
- }),
276
- );
277
- }
278
-
279
- export async function askForItemSelection(
280
- items: string[],
281
- message: string,
282
- ): Promise<{ value: string; index: number }> {
283
- const selection: { value: string; index: number } | symbol =
284
- await abortIfCancelled(
285
- clack.select({
286
- maxItems: 12,
287
- message: message,
288
- options: items.map((item, index) => {
289
- return {
290
- value: { value: item, index: index },
291
- label: item,
292
- };
293
- }),
294
- }),
295
- );
296
-
297
- return selection;
298
- }
299
-
300
- export async function confirmContinueIfPackageVersionNotSupported({
301
- packageId,
302
- packageName,
303
- packageVersion,
304
- acceptableVersions,
305
- note,
306
- }: {
307
- packageId: string;
308
- packageName: string;
309
- packageVersion: string;
310
- acceptableVersions: string;
311
- note?: string;
312
- }): Promise<void> {
313
- return traceStep(`check-package-version`, async () => {
314
- Sentry.setTag(`${packageName.toLowerCase()}-version`, packageVersion);
315
- const isSupportedVersion = fulfillsVersionRange({
316
- acceptableVersions,
317
- version: packageVersion,
318
- canBeLatest: true,
319
- });
320
-
321
- if (isSupportedVersion) {
322
- Sentry.setTag(`${packageName.toLowerCase()}-supported`, true);
323
- return;
324
- }
325
-
326
- clack.log.warn(
327
- `You have an unsupported version of ${packageName} installed:
328
-
329
- ${packageId}@${packageVersion}`,
330
- );
331
-
332
- clack.note(
333
- note ??
334
- `Please upgrade to ${acceptableVersions} if you wish to use the Sentry Wizard.`,
335
- );
336
- const continueWithUnsupportedVersion = await abortIfCancelled(
337
- clack.confirm({
338
- message: 'Do you want to continue anyway?',
339
- }),
340
- );
341
- Sentry.setTag(
342
- `${packageName.toLowerCase()}-continue-with-unsupported-version`,
343
- continueWithUnsupportedVersion,
344
- );
345
-
346
- if (!continueWithUnsupportedVersion) {
347
- await abort(undefined, 0);
348
- }
349
- });
350
- }
351
-
352
- /**
353
- * Installs or updates a package with the user's package manager.
354
- *
355
- * IMPORTANT: This function modifies the `package.json`! Be sure to re-read
356
- * it if you make additional modifications to it after calling this function!
357
- */
358
- export async function installPackage({
359
- packageName,
360
- alreadyInstalled,
361
- askBeforeUpdating = true,
362
- packageNameDisplayLabel,
363
- packageManager,
364
- forceInstall = false,
365
- }: {
366
- /** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
367
- packageName: string;
368
- alreadyInstalled: boolean;
369
- askBeforeUpdating?: boolean;
370
- /** Overrides what is shown in the installation logs in place of the `packageName` option. Useful if the `packageName` is ugly (e.g. `@sentry/nextjs@^8`) */
371
- packageNameDisplayLabel?: string;
372
- packageManager?: PackageManager;
373
- /** Add force install flag to command to skip install precondition fails */
374
- forceInstall?: boolean;
375
- }): Promise<{ packageManager?: PackageManager }> {
376
- return traceStep('install-package', async () => {
377
- if (alreadyInstalled && askBeforeUpdating) {
378
- const shouldUpdatePackage = await abortIfCancelled(
379
- clack.confirm({
380
- message: `The ${chalk.bold.cyan(
381
- packageNameDisplayLabel ?? packageName,
382
- )} package is already installed. Do you want to update it to the latest version?`,
383
- }),
384
- );
385
-
386
- if (!shouldUpdatePackage) {
387
- return {};
388
- }
389
- }
390
-
391
- const sdkInstallSpinner = clack.spinner();
392
-
393
- const pkgManager = packageManager || (await getPackageManager());
394
-
395
- sdkInstallSpinner.start(
396
- `${alreadyInstalled ? 'Updating' : 'Installing'} ${chalk.bold.cyan(
397
- packageNameDisplayLabel ?? packageName,
398
- )} with ${chalk.bold(pkgManager.label)}.`,
399
- );
400
-
401
- try {
402
- await new Promise<void>((resolve, reject) => {
403
- childProcess.exec(
404
- `${pkgManager.installCommand} ${packageName} ${pkgManager.flags} ${
405
- forceInstall ? pkgManager.forceInstallFlag : ''
406
- }`,
407
- (err, stdout, stderr) => {
408
- if (err) {
409
- // Write a log file so we can better troubleshoot issues
410
- fs.writeFileSync(
411
- path.join(
412
- process.cwd(),
413
- `sentry-wizard-installation-error-${Date.now()}.log`,
414
- ),
415
- JSON.stringify({
416
- stdout,
417
- stderr,
418
- }),
419
- { encoding: 'utf8' },
420
- );
421
-
422
- reject(err);
423
- } else {
424
- resolve();
425
- }
426
- },
427
- );
428
- });
429
- } catch (e) {
430
- sdkInstallSpinner.stop('Installation failed.');
431
- clack.log.error(
432
- `${chalk.red(
433
- 'Encountered the following error during installation:',
434
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
435
- )}\n\n${e}\n\n${chalk.dim(
436
- "The wizard has created a `sentry-wizard-installation-error-*.log` file. If you think this issue is caused by the Sentry wizard, create an issue on GitHub and include the log file's content:\nhttps://github.com/getsentry/sentry-wizard/issues",
437
- )}`,
438
- );
439
- await abort();
440
- }
441
-
442
- sdkInstallSpinner.stop(
443
- `${alreadyInstalled ? 'Updated' : 'Installed'} ${chalk.bold.cyan(
444
- packageNameDisplayLabel ?? packageName,
445
- )} with ${chalk.bold(pkgManager.label)}.`,
446
- );
447
-
448
- return { packageManager: pkgManager };
449
- });
450
- }
451
-
452
- export async function addSentryCliConfig(
453
- { authToken, org, project, url }: CliSetupConfigContent,
454
- setupConfig: CliSetupConfig = rcCliSetupConfig,
455
- ): Promise<void> {
456
- return traceStep('add-sentry-cli-config', async () => {
457
- const configPath = path.join(process.cwd(), setupConfig.filename);
458
- const configExists = fs.existsSync(configPath);
459
-
460
- let configContents =
461
- (configExists && fs.readFileSync(configPath, 'utf8')) || '';
462
- configContents = addAuthTokenToSentryConfig(
463
- configContents,
464
- authToken,
465
- setupConfig,
466
- );
467
- configContents = addOrgAndProjectToSentryConfig(
468
- configContents,
469
- org,
470
- project,
471
- setupConfig,
472
- );
473
- configContents = addUrlToSentryConfig(configContents, url, setupConfig);
474
-
475
- try {
476
- await fs.promises.writeFile(configPath, configContents, {
477
- encoding: 'utf8',
478
- flag: 'w',
479
- });
480
- clack.log.success(
481
- `${configExists ? 'Saved' : 'Created'} ${chalk.cyan(
482
- setupConfig.filename,
483
- )}.`,
484
- );
485
- } catch {
486
- clack.log.warning(
487
- `Failed to add auth token to ${chalk.cyan(
488
- setupConfig.filename,
489
- )}. Uploading ${
490
- setupConfig.name
491
- } during build will likely not work locally.`,
492
- );
493
- }
494
-
495
- if (setupConfig.gitignore) {
496
- await addCliConfigFileToGitIgnore(setupConfig.filename);
497
- } else {
498
- clack.log.warn(
499
- chalk.yellow('DO NOT commit auth token to your repository!'),
500
- );
501
- }
502
- });
503
- }
504
-
505
- function addAuthTokenToSentryConfig(
506
- configContents: string,
507
- authToken: string | undefined,
508
- setupConfig: CliSetupConfig,
509
- ): string {
510
- if (!authToken) {
511
- return configContents;
512
- }
513
-
514
- if (setupConfig.likelyAlreadyHasAuthToken(configContents)) {
515
- clack.log.warn(
516
- `${chalk.cyan(
517
- setupConfig.filename,
518
- )} already has auth token. Will not add one.`,
519
- );
520
- return configContents;
521
- }
522
-
523
- const newContents = `${configContents}\n${setupConfig.tokenContent(
524
- authToken,
525
- )}\n`;
526
- clack.log.success(
527
- `Added auth token to ${chalk.cyan(
528
- setupConfig.filename,
529
- )} for you to test uploading ${setupConfig.name} locally.`,
530
- );
531
- return newContents;
532
- }
533
-
534
- function addOrgAndProjectToSentryConfig(
535
- configContents: string,
536
- org: string | undefined,
537
- project: string | undefined,
538
- setupConfig: CliSetupConfig,
539
- ): string {
540
- if (!org || !project) {
541
- return configContents;
542
- }
543
-
544
- if (setupConfig.likelyAlreadyHasOrgAndProject(configContents)) {
545
- clack.log.warn(
546
- `${chalk.cyan(
547
- setupConfig.filename,
548
- )} already has org and project. Will not add them.`,
549
- );
550
- return configContents;
551
- }
552
-
553
- const newContents = `${configContents}\n${setupConfig.orgAndProjContent(
554
- org,
555
- project,
556
- )}\n`;
557
- clack.log.success(
558
- `Added default org and project to ${chalk.cyan(
559
- setupConfig.filename,
560
- )} for you to test uploading ${setupConfig.name} locally.`,
561
- );
562
- return newContents;
563
- }
564
-
565
- function addUrlToSentryConfig(
566
- configContents: string,
567
- url: string | undefined,
568
- setupConfig: CliSetupConfig,
569
- ): string {
570
- if (!url || !setupConfig.urlContent || !setupConfig.likelyAlreadyHasUrl) {
571
- return configContents;
572
- }
573
-
574
- if (setupConfig.likelyAlreadyHasUrl(configContents)) {
575
- clack.log.warn(
576
- `${chalk.cyan(setupConfig.filename)} already has url. Will not add one.`,
577
- );
578
- return configContents;
579
- }
580
-
581
- const newContents = `${configContents}\n${setupConfig.urlContent(url)}\n`;
582
- clack.log.success(
583
- `Added default url to ${chalk.cyan(
584
- setupConfig.filename,
585
- )} for you to test uploading ${setupConfig.name} locally.`,
586
- );
587
- return newContents;
588
- }
589
-
590
- export async function addDotEnvSentryBuildPluginFile(
591
- authToken: string,
592
- ): Promise<void> {
593
- const envVarContent = `# DO NOT commit this file to your repository!
594
- # The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
595
- # It's used for authentication when uploading source maps.
596
- # You can also set this env variable in your own \`.env\` files and remove this file.
597
- SENTRY_AUTH_TOKEN=${authToken}
598
- `;
599
-
600
- const dotEnvFilePath = path.join(process.cwd(), SENTRY_DOT_ENV_FILE);
601
- const dotEnvFileExists = fs.existsSync(dotEnvFilePath);
602
-
603
- if (dotEnvFileExists) {
604
- const dotEnvFileContent = fs.readFileSync(dotEnvFilePath, 'utf8');
605
-
606
- const hasAuthToken = !!dotEnvFileContent.match(
607
- /^\s*SENTRY_AUTH_TOKEN\s*=/g,
608
- );
609
-
610
- if (hasAuthToken) {
611
- clack.log.warn(
612
- `${chalk.bold.cyan(
613
- SENTRY_DOT_ENV_FILE,
614
- )} already has auth token. Will not add one.`,
615
- );
616
- } else {
617
- try {
618
- await fs.promises.writeFile(
619
- dotEnvFilePath,
620
- `${dotEnvFileContent}\n${envVarContent}`,
621
- {
622
- encoding: 'utf8',
623
- flag: 'w',
624
- },
625
- );
626
- clack.log.success(
627
- `Added auth token to ${chalk.bold.cyan(SENTRY_DOT_ENV_FILE)}`,
628
- );
629
- } catch {
630
- clack.log.warning(
631
- `Failed to add auth token to ${chalk.bold.cyan(
632
- SENTRY_DOT_ENV_FILE,
633
- )}. Uploading source maps during build will likely not work locally.`,
634
- );
635
- }
636
- }
637
- } else {
638
- try {
639
- await fs.promises.writeFile(dotEnvFilePath, envVarContent, {
640
- encoding: 'utf8',
641
- flag: 'w',
642
- });
643
- clack.log.success(
644
- `Created ${chalk.bold.cyan(
645
- SENTRY_DOT_ENV_FILE,
646
- )} with auth token for you to test source map uploading locally.`,
647
- );
648
- } catch {
649
- clack.log.warning(
650
- `Failed to create ${chalk.bold.cyan(
651
- SENTRY_DOT_ENV_FILE,
652
- )} with auth token. Uploading source maps during build will likely not work locally.`,
653
- );
654
- }
655
- }
656
-
657
- await addCliConfigFileToGitIgnore(SENTRY_DOT_ENV_FILE);
658
- }
659
-
660
- async function addCliConfigFileToGitIgnore(filename: string): Promise<void> {
661
- const gitignorePath = path.join(process.cwd(), '.gitignore');
662
-
663
- try {
664
- const gitignoreContent = await fs.promises.readFile(gitignorePath, 'utf8');
665
- if (gitignoreContent.split(/\r?\n/).includes(filename)) {
666
- clack.log.info(
667
- `${chalk.bold('.gitignore')} already has ${chalk.bold(
668
- filename,
669
- )}. Will not add it again.`,
670
- );
671
- return;
672
- }
673
-
674
- await fs.promises.appendFile(
675
- gitignorePath,
676
- `\n# Sentry Config File\n${filename}\n`,
677
- { encoding: 'utf8' },
678
- );
679
- clack.log.success(
680
- `Added ${chalk.cyan(filename)} to ${chalk.cyan('.gitignore')}.`,
681
- );
682
- } catch {
683
- clack.log.error(
684
- `Failed adding ${chalk.cyan(filename)} to ${chalk.cyan(
685
- '.gitignore',
686
- )}. Please add it manually!`,
687
- );
688
- }
689
- }
690
-
691
- export async function runPrettierIfInstalled(): Promise<void> {
692
- return traceStep('run-prettier', async () => {
693
- if (!isInGitRepo()) {
694
- // We only run formatting on changed files. If we're not in a git repo, we can't find
695
- // changed files. So let's early-return without showing any formatting-related messages.
696
- return;
697
- }
698
-
699
- const changedOrUntrackedFiles = getUncommittedOrUntrackedFiles()
700
- .map((filename) => {
701
- return filename.startsWith('- ') ? filename.slice(2) : filename;
702
- })
703
- .join(' ');
704
-
705
- if (!changedOrUntrackedFiles.length) {
706
- // Likewise, if we can't find changed or untracked files, there's no point in running Prettier.
707
- return;
708
- }
709
-
710
- const packageJson = await getPackageDotJson();
711
- const prettierInstalled = hasPackageInstalled('prettier', packageJson);
712
-
713
- Sentry.setTag('prettier-installed', prettierInstalled);
714
-
715
- if (!prettierInstalled) {
716
- return;
717
- }
718
-
719
- // prompt the user if they want to run prettier
720
- const shouldRunPrettier = await abortIfCancelled(
721
- clack.confirm({
722
- message:
723
- 'Looks like you have Prettier in your project. Do you want to run it on your files?',
724
- }),
725
- );
726
-
727
- if (!shouldRunPrettier) {
728
- return;
729
- }
730
-
731
- const prettierSpinner = clack.spinner();
732
- prettierSpinner.start('Running Prettier on your files.');
733
-
734
- try {
735
- await new Promise<void>((resolve, reject) => {
736
- childProcess.exec(
737
- `npx prettier --ignore-unknown --write ${changedOrUntrackedFiles}`,
738
- (err) => {
739
- if (err) {
740
- reject(err);
741
- } else {
742
- resolve();
743
- }
744
- },
745
- );
746
- });
747
- } catch (e) {
748
- prettierSpinner.stop('Prettier failed to run.');
749
- clack.log.warn(
750
- 'Prettier failed to run. There may be formatting issues in your updated files.',
751
- );
752
- return;
753
- }
754
-
755
- prettierSpinner.stop('Prettier has formatted your files.');
756
- });
757
- }
758
-
759
- /**
760
- * Checks if @param packageId is listed as a dependency in @param packageJson.
761
- * If not, it will ask users if they want to continue without the package.
762
- *
763
- * Use this function to check if e.g. a the framework of the SDK is installed
764
- *
765
- * @param packageJson the package.json object
766
- * @param packageId the npm name of the package
767
- * @param packageName a human readable name of the package
768
- */
769
- export async function ensurePackageIsInstalled(
770
- packageJson: PackageDotJson,
771
- packageId: string,
772
- packageName: string,
773
- ): Promise<void> {
774
- return traceStep('ensure-package-installed', async () => {
775
- const installed = hasPackageInstalled(packageId, packageJson);
776
-
777
- Sentry.setTag(`${packageName.toLowerCase()}-installed`, installed);
778
-
779
- if (!installed) {
780
- Sentry.setTag(`${packageName.toLowerCase()}-installed`, false);
781
- const continueWithoutPackage = await abortIfCancelled(
782
- clack.confirm({
783
- message: `${packageName} does not seem to be installed. Do you still want to continue?`,
784
- initialValue: false,
785
- }),
786
- );
787
-
788
- if (!continueWithoutPackage) {
789
- await abort(undefined, 0);
790
- }
791
- }
792
- });
793
- }
794
-
795
- export async function getPackageDotJson(): Promise<PackageDotJson> {
796
- const packageJsonFileContents = await fs.promises
797
- .readFile(path.join(process.cwd(), 'package.json'), 'utf8')
798
- .catch(() => {
799
- clack.log.error(
800
- 'Could not find package.json. Make sure to run the wizard in the root of your app!',
801
- );
802
- return abort();
803
- });
804
-
805
- let packageJson: PackageDotJson | undefined = undefined;
806
-
807
- try {
808
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
809
- packageJson = JSON.parse(packageJsonFileContents);
810
- } catch {
811
- clack.log.error(
812
- `Unable to parse your ${chalk.cyan(
813
- 'package.json',
814
- )}. Make sure it has a valid format!`,
815
- );
816
-
817
- await abort();
818
- }
819
-
820
- return packageJson || {};
821
- }
822
-
823
- export async function updatePackageDotJson(
824
- packageDotJson: PackageDotJson,
825
- ): Promise<void> {
826
- try {
827
- await fs.promises.writeFile(
828
- path.join(process.cwd(), 'package.json'),
829
- // TODO: maybe figure out the original indentation
830
- JSON.stringify(packageDotJson, null, 2),
831
- {
832
- encoding: 'utf8',
833
- flag: 'w',
834
- },
835
- );
836
- } catch {
837
- clack.log.error(`Unable to update your ${chalk.cyan('package.json')}.`);
838
-
839
- await abort();
840
- }
841
- }
842
-
843
- export async function getPackageManager(): Promise<PackageManager> {
844
- const detectedPackageManager = detectPackageManger();
845
-
846
- if (detectedPackageManager) {
847
- return detectedPackageManager;
848
- }
849
-
850
- const selectedPackageManager: PackageManager | symbol =
851
- await abortIfCancelled(
852
- clack.select({
853
- message: 'Please select your package manager.',
854
- options: packageManagers.map((packageManager) => ({
855
- value: packageManager,
856
- label: packageManager.label,
857
- })),
858
- }),
859
- );
860
-
861
- Sentry.setTag('package-manager', selectedPackageManager.name);
862
-
863
- return selectedPackageManager;
864
- }
865
-
866
- export function isUsingTypeScript() {
867
- try {
868
- return fs.existsSync(path.join(process.cwd(), 'tsconfig.json'));
869
- } catch {
870
- return false;
871
- }
872
- }
873
-
874
- /**
875
- * Checks if we already got project data from a previous wizard invocation.
876
- * If yes, this data is returned.
877
- * Otherwise, we start the login flow and ask the user to select a project.
878
- *
879
- * Use this function to get project data for the wizard.
880
- *
881
- * @param options wizard options
882
- * @param platform the platform of the wizard
883
- * @returns project data (org, project, token, url)
884
- */
885
- export async function getOrAskForProjectData(
886
- options: WizardOptions,
887
- platform?:
888
- | 'javascript-nextjs'
889
- | 'javascript-nuxt'
890
- | 'javascript-remix'
891
- | 'javascript-sveltekit'
892
- | 'apple-ios'
893
- | 'android'
894
- | 'react-native'
895
- | 'flutter',
896
- ): Promise<{
897
- sentryUrl: string;
898
- selfHosted: boolean;
899
- selectedProject: SentryProjectData;
900
- authToken: string;
901
- }> {
902
- if (options.preSelectedProject) {
903
- return {
904
- selfHosted: options.preSelectedProject.selfHosted,
905
- sentryUrl: options.url ?? SAAS_URL,
906
- authToken: options.preSelectedProject.authToken,
907
- selectedProject: options.preSelectedProject.project,
908
- };
909
- }
910
- const { url: sentryUrl, selfHosted } = await traceStep(
911
- 'ask-self-hosted',
912
- () => askForSelfHosted(options.url, options.saas),
913
- );
914
-
915
- const { projects, apiKeys } = await traceStep('login', () =>
916
- askForWizardLogin({
917
- promoCode: options.promoCode,
918
- url: sentryUrl,
919
- platform: platform,
920
- orgSlug: options.orgSlug,
921
- projectSlug: options.projectSlug,
922
- }),
923
- );
924
-
925
- if (!projects || !projects.length) {
926
- clack.log.error(
927
- 'No projects found. Please create a project in Sentry and try again.',
928
- );
929
- Sentry.setTag('no-projects-found', true);
930
- await abort();
931
- // This rejection won't return due to the abort call but TS doesn't know that
932
- return Promise.reject();
933
- }
934
-
935
- const selectedProject = await traceStep('select-project', () =>
936
- askForProjectSelection(projects, options.orgSlug, options.projectSlug),
937
- );
938
-
939
- const { token } = apiKeys ?? {};
940
-
941
- if (!token) {
942
- clack.log.error(`Didn't receive an auth token. This shouldn't happen :(
943
-
944
- Please let us know if you think this is a bug in the wizard:
945
- ${chalk.cyan('https://github.com/getsentry/sentry-wizard/issues')}`);
946
-
947
- clack.log.info(`In the meantime, we'll add a dummy auth token (${chalk.cyan(
948
- `"${DUMMY_AUTH_TOKEN}"`,
949
- )}) for you to replace later.
950
- Create your auth token here:
951
- ${chalk.cyan(
952
- selfHosted
953
- ? `${sentryUrl}organizations/${selectedProject.organization.slug}/settings/auth-tokens`
954
- : `https://${selectedProject.organization.slug}.sentry.io/settings/auth-tokens`,
955
- )}`);
956
- }
957
-
958
- return {
959
- sentryUrl,
960
- selfHosted,
961
- authToken: apiKeys?.token || DUMMY_AUTH_TOKEN,
962
- selectedProject,
963
- };
964
- }
965
-
966
- /**
967
- * Asks users if they are using SaaS or self-hosted Sentry and returns the validated URL.
968
- *
969
- * If users started the wizard with a --url arg, that URL is used as the default and we skip
970
- * the self-hosted question. However, the passed url is still validated and in case it's
971
- * invalid, users are asked to enter a new one until it is valid.
972
- *
973
- * @param urlFromArgs the url passed via the --url arg
974
- */
975
- async function askForSelfHosted(
976
- urlFromArgs?: string,
977
- saas?: boolean,
978
- ): Promise<{
979
- url: string;
980
- selfHosted: boolean;
981
- }> {
982
- if (saas) {
983
- Sentry.setTag('url', SAAS_URL);
984
- Sentry.setTag('self-hosted', false);
985
- return { url: SAAS_URL, selfHosted: false };
986
- }
987
-
988
- if (!urlFromArgs) {
989
- const choice: 'saas' | 'self-hosted' | symbol = await abortIfCancelled(
990
- clack.select({
991
- message: 'Are you using Sentry SaaS or self-hosted Sentry?',
992
- options: [
993
- { value: 'saas', label: 'Sentry SaaS (sentry.io)' },
994
- {
995
- value: 'self-hosted',
996
- label: 'Self-hosted/on-premise/single-tenant',
997
- },
998
- ],
999
- }),
1000
- );
1001
-
1002
- if (choice === 'saas') {
1003
- Sentry.setTag('url', SAAS_URL);
1004
- Sentry.setTag('self-hosted', false);
1005
- return { url: SAAS_URL, selfHosted: false };
1006
- }
1007
- }
1008
-
1009
- let validUrl: string | undefined;
1010
- let tmpUrlFromArgs = urlFromArgs;
1011
-
1012
- while (validUrl === undefined) {
1013
- const url =
1014
- tmpUrlFromArgs ||
1015
- (await abortIfCancelled(
1016
- clack.text({
1017
- message: `Please enter the URL of your ${
1018
- urlFromArgs ? '' : 'self-hosted '
1019
- }Sentry instance.`,
1020
- placeholder: 'https://sentry.io/',
1021
- }),
1022
- ));
1023
- tmpUrlFromArgs = undefined;
1024
-
1025
- try {
1026
- validUrl = new URL(url).toString();
1027
-
1028
- // We assume everywhere else that the URL ends in a slash
1029
- if (!validUrl.endsWith('/')) {
1030
- validUrl += '/';
1031
- }
1032
- } catch {
1033
- clack.log.error(
1034
- `Please enter a valid URL. (It should look something like "https://sentry.mydomain.com/", got ${url})`,
1035
- );
1036
- }
1037
- }
1038
-
1039
- const isSelfHostedUrl = new URL(validUrl).host !== new URL(SAAS_URL).host;
1040
-
1041
- Sentry.setTag('url', validUrl);
1042
- Sentry.setTag('self-hosted', isSelfHostedUrl);
1043
-
1044
- return { url: validUrl, selfHosted: true };
1045
- }
1046
-
1047
- async function askForWizardLogin(options: {
1048
- url: string;
1049
- promoCode?: string;
1050
- platform?:
1051
- | 'javascript-nextjs'
1052
- | 'javascript-nuxt'
1053
- | 'javascript-remix'
1054
- | 'javascript-sveltekit'
1055
- | 'apple-ios'
1056
- | 'android'
1057
- | 'react-native'
1058
- | 'flutter';
1059
- orgSlug?: string;
1060
- projectSlug?: string;
1061
- }): Promise<WizardProjectData> {
1062
- Sentry.setTag('has-promo-code', !!options.promoCode);
1063
-
1064
- let hasSentryAccount = await clack.confirm({
1065
- message: 'Do you already have a Sentry account?',
1066
- });
1067
-
1068
- hasSentryAccount = await abortIfCancelled(hasSentryAccount);
1069
-
1070
- Sentry.setTag('already-has-sentry-account', hasSentryAccount);
1071
-
1072
- let wizardHash: string;
1073
- try {
1074
- wizardHash = (
1075
- await axios.get<{ hash: string }>(`${options.url}api/0/wizard/`)
1076
- ).data.hash;
1077
- } catch (e: unknown) {
1078
- if (options.url !== SAAS_URL) {
1079
- clack.log.error('Loading Wizard failed. Did you provide the right URL?');
1080
- clack.log.info(JSON.stringify(e, null, 2));
1081
- await abort(
1082
- chalk.red(
1083
- 'Please check your configuration and try again.\n\n Let us know if you think this is an issue with the wizard or Sentry: https://github.com/getsentry/sentry-wizard/issues',
1084
- ),
1085
- );
1086
- } else {
1087
- clack.log.error('Loading Wizard failed.');
1088
- clack.log.info(JSON.stringify(e, null, 2));
1089
- await abort(
1090
- chalk.red(
1091
- 'Please try again in a few minutes and let us know if this issue persists: https://github.com/getsentry/sentry-wizard/issues',
1092
- ),
1093
- );
1094
- }
1095
- }
1096
-
1097
- const loginUrl = new URL(
1098
- `${options.url}account/settings/wizard/${wizardHash!}/`,
1099
- );
1100
-
1101
- if (options.orgSlug) {
1102
- loginUrl.searchParams.set('org_slug', options.orgSlug);
1103
- }
1104
-
1105
- if (options.projectSlug) {
1106
- loginUrl.searchParams.set('project_slug', options.projectSlug);
1107
- }
1108
-
1109
- if (!hasSentryAccount) {
1110
- loginUrl.searchParams.set('signup', '1');
1111
- }
1112
-
1113
- if (options.platform) {
1114
- loginUrl.searchParams.set('project_platform', options.platform);
1115
- }
1116
-
1117
- if (options.promoCode) {
1118
- loginUrl.searchParams.set('code', options.promoCode);
1119
- }
1120
-
1121
- const urlToOpen = loginUrl.toString();
1122
- clack.log.info(
1123
- `${chalk.bold(
1124
- `If the browser window didn't open automatically, please open the following link to ${
1125
- hasSentryAccount ? 'log' : 'sign'
1126
- } into Sentry:`,
1127
- )}\n\n${chalk.cyan(urlToOpen)}`,
1128
- );
1129
-
1130
- opn(urlToOpen, { wait: false }).catch(() => {
1131
- // opn throws in environments that don't have a browser (e.g. remote shells) so we just noop here
1132
- });
1133
-
1134
- const loginSpinner = clack.spinner();
1135
-
1136
- loginSpinner.start('Waiting for you to log in using the link above');
1137
-
1138
- const data = await new Promise<WizardProjectData>((resolve) => {
1139
- const pollingInterval = setInterval(() => {
1140
- axios
1141
- .get<WizardProjectData>(`${options.url}api/0/wizard/${wizardHash}/`, {
1142
- headers: {
1143
- 'Accept-Encoding': 'deflate',
1144
- },
1145
- })
1146
- .then((result) => {
1147
- resolve(result.data);
1148
- clearTimeout(timeout);
1149
- clearInterval(pollingInterval);
1150
- void axios.delete(`${options.url}api/0/wizard/${wizardHash}/`);
1151
- })
1152
- .catch(() => {
1153
- // noop - just try again
1154
- });
1155
- }, 500);
1156
-
1157
- const timeout = setTimeout(() => {
1158
- clearInterval(pollingInterval);
1159
- loginSpinner.stop(
1160
- 'Login timed out. No worries - it happens to the best of us.',
1161
- );
1162
-
1163
- Sentry.setTag('opened-wizard-link', false);
1164
- void abort('Please restart the Wizard and log in to complete the setup.');
1165
- }, 180_000);
1166
- });
1167
-
1168
- loginSpinner.stop('Login complete.');
1169
- Sentry.setTag('opened-wizard-link', true);
1170
-
1171
- return data;
1172
- }
1173
-
1174
- async function askForProjectSelection(
1175
- projects: SentryProjectData[],
1176
- orgSlug?: string,
1177
- projectSlug?: string,
1178
- ): Promise<SentryProjectData> {
1179
- const label = (project: SentryProjectData): string => {
1180
- return `${project.organization.slug}/${project.slug}`;
1181
- };
1182
-
1183
- const filteredProjects = filterProjectsBySlugs(
1184
- projects,
1185
- orgSlug,
1186
- projectSlug,
1187
- );
1188
-
1189
- if (filteredProjects.length === 1) {
1190
- const selection = filteredProjects[0];
1191
-
1192
- Sentry.setTag('project', selection.slug);
1193
- Sentry.setUser({ id: selection.organization.slug });
1194
- clack.log.step(`Selected project ${label(selection)}`);
1195
-
1196
- return selection;
1197
- }
1198
-
1199
- if (filteredProjects.length === 0) {
1200
- clack.log.warn('Could not find a project with the provided slugs.');
1201
- }
1202
-
1203
- const sortedProjects = filteredProjects.length ? filteredProjects : projects;
1204
- sortedProjects.sort((a: SentryProjectData, b: SentryProjectData) => {
1205
- return label(a).localeCompare(label(b));
1206
- });
1207
-
1208
- const selection: SentryProjectData | symbol = await abortIfCancelled(
1209
- clack.select({
1210
- maxItems: 12,
1211
- message: 'Select your Sentry project.',
1212
- options: sortedProjects.map((project) => {
1213
- return {
1214
- value: project,
1215
- label: label(project),
1216
- };
1217
- }),
1218
- }),
1219
- );
1220
-
1221
- Sentry.setTag('project', selection.slug);
1222
- Sentry.setUser({ id: selection.organization.slug });
1223
-
1224
- return selection;
1225
- }
1226
-
1227
- function filterProjectsBySlugs(
1228
- projects: SentryProjectData[],
1229
- orgSlug?: string,
1230
- projectSlug?: string,
1231
- ): SentryProjectData[] {
1232
- if (!orgSlug && !projectSlug) {
1233
- return projects;
1234
- }
1235
- if (orgSlug && !projectSlug) {
1236
- return projects.filter((p) => p.organization.slug === orgSlug);
1237
- }
1238
- if (!orgSlug && projectSlug) {
1239
- return projects.filter((p) => p.slug === projectSlug);
1240
- }
1241
-
1242
- return projects.filter(
1243
- (p) => p.organization.slug === orgSlug && p.slug === projectSlug,
1244
- );
1245
- }
1246
-
1247
- /**
1248
- * Asks users if they have a config file for @param tool (e.g. Vite).
1249
- * If yes, asks users to specify the path to their config file.
1250
- *
1251
- * Use this helper function as a fallback mechanism if the lookup for
1252
- * a config file with its most usual location/name fails.
1253
- *
1254
- * @param toolName Name of the tool for which we're looking for the config file
1255
- * @param configFileName Name of the most common config file name (e.g. vite.config.js)
1256
- *
1257
- * @returns a user path to the config file or undefined if the user doesn't have a config file
1258
- */
1259
- export async function askForToolConfigPath(
1260
- toolName: string,
1261
- configFileName: string,
1262
- ): Promise<string | undefined> {
1263
- const hasConfig = await abortIfCancelled(
1264
- clack.confirm({
1265
- message: `Do you have a ${toolName} config file (e.g. ${chalk.cyan(
1266
- configFileName,
1267
- )})?`,
1268
- initialValue: true,
1269
- }),
1270
- );
1271
-
1272
- if (!hasConfig) {
1273
- return undefined;
1274
- }
1275
-
1276
- return await abortIfCancelled(
1277
- clack.text({
1278
- message: `Please enter the path to your ${toolName} config file:`,
1279
- placeholder: path.join('.', configFileName),
1280
- validate: (value) => {
1281
- if (!value) {
1282
- return 'Please enter a path.';
1283
- }
1284
-
1285
- try {
1286
- fs.accessSync(value);
1287
- } catch {
1288
- return 'Could not access the file at this path.';
1289
- }
1290
- },
1291
- }),
1292
- );
1293
- }
1294
-
1295
- /**
1296
- * Prints copy/paste-able instructions to the console.
1297
- * Afterwards asks the user if they added the code snippet to their file.
1298
- *
1299
- * While there's no point in providing a "no" answer here, it gives users time to fulfill the
1300
- * task before the wizard continues with additional steps.
1301
- *
1302
- * Use this function if you want to show users instructions on how to add/modify
1303
- * code in their file. This is helpful if automatic insertion failed or is not possible/feasible.
1304
- *
1305
- * @param filename the name of the file to which the code snippet should be applied.
1306
- * If a path is provided, only the filename will be used.
1307
- *
1308
- * @param codeSnippet the snippet to be printed. Use {@link makeCodeSnippet} to create the
1309
- * diff-like format for visually highlighting unchanged or modified lines of code.
1310
- *
1311
- * @param hint (optional) a hint to be printed after the main instruction to add
1312
- * the code from @param codeSnippet to their @param filename.
1313
- *
1314
- * More guidelines on copy/paste instructions:
1315
- * @see {@link https://develop.sentry.dev/sdk/setup-wizards/#copy--paste-snippets}
1316
- *
1317
- * TODO: refactor copy paste instructions across different wizards to use this function.
1318
- * this might require adding a custom message parameter to the function
1319
- */
1320
- export async function showCopyPasteInstructions(
1321
- filename: string,
1322
- codeSnippet: string,
1323
- hint?: string,
1324
- ): Promise<void> {
1325
- clack.log.step(
1326
- `Add the following code to your ${chalk.cyan(
1327
- path.basename(filename),
1328
- )} file:${hint ? chalk.dim(` (${chalk.dim(hint)})`) : ''}`,
1329
- );
1330
-
1331
- // Padding the code snippet to be printed with a \n at the beginning and end
1332
- // This makes it easier to distinguish the snippet from the rest of the output
1333
- // Intentionally logging directly to console here so that the code can be copied/pasted directly
1334
- // eslint-disable-next-line no-console
1335
- console.log(`\n${codeSnippet}\n`);
1336
-
1337
- await abortIfCancelled(
1338
- clack.select({
1339
- message: 'Did you apply the snippet above?',
1340
- options: [{ label: 'Yes, continue!', value: true }],
1341
- initialValue: true,
1342
- }),
1343
- );
1344
- }
1345
-
1346
- /**
1347
- * Callback that exposes formatting helpers for a code snippet.
1348
- * @param unchanged - Formats text as old code.
1349
- * @param plus - Formats text as new code.
1350
- * @param minus - Formats text as removed code.
1351
- */
1352
- type CodeSnippetFormatter = (
1353
- unchanged: (txt: string) => string,
1354
- plus: (txt: string) => string,
1355
- minus: (txt: string) => string,
1356
- ) => string;
1357
-
1358
- /**
1359
- * Crafts a code snippet that can be used to e.g.
1360
- * - print copy/paste instructions to the console
1361
- * - create a new config file.
1362
- *
1363
- * @param colors set this to true if you want the final snippet to be colored.
1364
- * This is useful for printing the snippet to the console as part of copy/paste instructions.
1365
- *
1366
- * @param callback the callback that returns the formatted code snippet.
1367
- * It exposes takes the helper functions for marking code as unchanged, new or removed.
1368
- * These functions no-op if no special formatting should be applied
1369
- * and otherwise apply the appropriate formatting/coloring.
1370
- * (@see {@link CodeSnippetFormatter})
1371
- *
1372
- * @see {@link showCopyPasteInstructions} for the helper with which to display the snippet in the console.
1373
- *
1374
- * @returns a string containing the final, formatted code snippet.
1375
- */
1376
- export function makeCodeSnippet(
1377
- colors: boolean,
1378
- callback: CodeSnippetFormatter,
1379
- ): string {
1380
- const unchanged = (txt: string) => (colors ? chalk.grey(txt) : txt);
1381
- const plus = (txt: string) => (colors ? chalk.greenBright(txt) : txt);
1382
- const minus = (txt: string) => (colors ? chalk.redBright(txt) : txt);
1383
-
1384
- return callback(unchanged, plus, minus);
1385
- }
1386
-
1387
- /**
1388
- * Creates a new config file with the given @param filepath and @param codeSnippet.
1389
- *
1390
- * Use this function to create a new config file for users. This is useful
1391
- * when users answered that they don't yet have a config file for a tool.
1392
- *
1393
- * (This doesn't mean that they don't yet have some other way of configuring
1394
- * their tool but we can leave it up to them to figure out how to merge configs
1395
- * here.)
1396
- *
1397
- * @param filepath absolute path to the new config file
1398
- * @param codeSnippet the snippet to be inserted into the file
1399
- * @param moreInformation (optional) the message to be printed after the file was created
1400
- * For example, this can be a link to more information about configuring the tool.
1401
- *
1402
- * @returns true on success, false otherwise
1403
- */
1404
- export async function createNewConfigFile(
1405
- filepath: string,
1406
- codeSnippet: string,
1407
- moreInformation?: string,
1408
- ): Promise<boolean> {
1409
- if (!path.isAbsolute(filepath)) {
1410
- debug(`createNewConfigFile: filepath is not absolute: ${filepath}`);
1411
- return false;
1412
- }
1413
-
1414
- const prettyFilename = chalk.cyan(path.relative(process.cwd(), filepath));
1415
-
1416
- try {
1417
- await fs.promises.writeFile(filepath, codeSnippet);
1418
-
1419
- clack.log.success(`Added new ${prettyFilename} file.`);
1420
-
1421
- if (moreInformation) {
1422
- clack.log.info(chalk.gray(moreInformation));
1423
- }
1424
-
1425
- return true;
1426
- } catch (e) {
1427
- debug(e);
1428
- clack.log.warn(
1429
- `Could not create a new ${prettyFilename} file. Please create one manually and follow the instructions below.`,
1430
- );
1431
- }
1432
-
1433
- return false;
1434
- }
1435
-
1436
- export async function askShouldCreateExamplePage(
1437
- customRoute?: string,
1438
- ): Promise<boolean> {
1439
- const route = chalk.cyan(customRoute ?? '/sentry-example-page');
1440
- return traceStep('ask-create-example-page', () =>
1441
- abortIfCancelled(
1442
- clack.select({
1443
- message: `Do you want to create an example page ("${route}") to test your Sentry setup?`,
1444
- options: [
1445
- {
1446
- value: true,
1447
- label: 'Yes',
1448
- hint: 'Recommended - Check your git status before committing!',
1449
- },
1450
- { value: false, label: 'No' },
1451
- ],
1452
- }),
1453
- ),
1454
- );
1455
- }
1456
-
1457
- export async function askShouldCreateExampleComponent(): Promise<boolean> {
1458
- return traceStep('ask-create-example-component', () =>
1459
- abortIfCancelled(
1460
- clack.select({
1461
- message: `Do you want to create an example component to test your Sentry setup?`,
1462
- options: [
1463
- {
1464
- value: true,
1465
- label: 'Yes',
1466
- hint: 'Recommended - Check your git status before committing!',
1467
- },
1468
- { value: false, label: 'No' },
1469
- ],
1470
- }),
1471
- ),
1472
- );
1473
- }
1474
-
1475
- export async function featureSelectionPrompt<F extends ReadonlyArray<Feature>>(
1476
- features: F,
1477
- ): Promise<{ [key in F[number]['id']]: boolean }> {
1478
- return traceStep('feature-selection', async () => {
1479
- const selectedFeatures: Record<string, boolean> = {};
1480
-
1481
- for (const feature of features) {
1482
- const selected = await abortIfCancelled(
1483
- clack.select({
1484
- message: feature.prompt,
1485
- initialValue: true,
1486
- options: [
1487
- {
1488
- value: true,
1489
- label: 'Yes',
1490
- hint: feature.enabledHint,
1491
- },
1492
- {
1493
- value: false,
1494
- label: 'No',
1495
- hint: feature.disabledHint,
1496
- },
1497
- ],
1498
- }),
1499
- );
1500
-
1501
- selectedFeatures[feature.id] = selected;
1502
- }
1503
-
1504
- return selectedFeatures as { [key in F[number]['id']]: boolean };
1505
- });
1506
- }
1507
-
1508
- export async function askShouldInstallPackage(
1509
- pkgName: string,
1510
- ): Promise<boolean> {
1511
- return traceStep(`ask-install-package`, () =>
1512
- abortIfCancelled(
1513
- clack.confirm({
1514
- message: `Do you want to install ${chalk.cyan(pkgName)}?`,
1515
- }),
1516
- ),
1517
- );
1518
- }
1519
-
1520
- export async function askShouldAddPackageOverride(
1521
- pkgName: string,
1522
- pkgVersion: string,
1523
- ): Promise<boolean> {
1524
- return traceStep(`ask-add-package-override`, () =>
1525
- abortIfCancelled(
1526
- clack.confirm({
1527
- message: `Do you want to add an override for ${chalk.cyan(
1528
- pkgName,
1529
- )} version ${chalk.cyan(pkgVersion)}?`,
1530
- }),
1531
- ),
1532
- );
1533
- }