capacitor.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout';
  2. import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout';
  3. import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage';
  4. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  5. import type {
  6. Docs,
  7. DocsParams,
  8. OnboardingConfig,
  9. PlatformOption,
  10. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  11. import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
  12. import {
  13. getCrashReportJavaScriptInstallStep,
  14. getCrashReportModalConfigDescription,
  15. getCrashReportModalIntroduction,
  16. getFeedbackConfigOptions,
  17. getFeedbackConfigureDescription,
  18. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  19. import {
  20. getReplayConfigOptions,
  21. getReplayConfigureDescription,
  22. getReplayVerifyStep,
  23. } from 'sentry/components/onboarding/gettingStartedDoc/utils/replayOnboarding';
  24. import {t, tct} from 'sentry/locale';
  25. export enum SiblingOption {
  26. ANGULARV10 = 'angularV10',
  27. ANGULARV12 = 'angularV12',
  28. REACT = 'react',
  29. VUE3 = 'vue3',
  30. VUE2 = 'vue2',
  31. }
  32. type PlatformOptionKey = 'siblingOption';
  33. const platformOptions: Record<PlatformOptionKey, PlatformOption> = {
  34. siblingOption: {
  35. label: t('Sibling Package'),
  36. items: [
  37. {
  38. label: t('Angular 12+'),
  39. value: SiblingOption.ANGULARV12,
  40. },
  41. {
  42. label: t('Angular 10 & 11'),
  43. value: SiblingOption.ANGULARV10,
  44. },
  45. {
  46. label: t('React'),
  47. value: SiblingOption.REACT,
  48. },
  49. {
  50. label: t('Vue 3'),
  51. value: SiblingOption.VUE3,
  52. },
  53. {
  54. label: t('Vue 2'),
  55. value: SiblingOption.VUE2,
  56. },
  57. ],
  58. },
  59. };
  60. type PlatformOptions = typeof platformOptions;
  61. type Params = DocsParams<PlatformOptions>;
  62. function getIntegrations(params: Params, siblingOption: string) {
  63. const integrations: string[] = ['SentrySibling.browserTracingIntegration()'];
  64. if (params.isPerformanceSelected) {
  65. integrations.push(`
  66. new ${getSiblingImportName(siblingOption)}.BrowserTracing({
  67. // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
  68. tracePropagationTargets: ["localhost", /^https:\\/\\/yourserver\\.io\\/api/],
  69. ${
  70. params.isPerformanceSelected ? getPerformanceIntegration(siblingOption) : ''
  71. }})`);
  72. }
  73. if (params.isFeedbackSelected) {
  74. const feedbackIntegration: string[] = [
  75. `// Additional SDK configuration goes in here, for example:
  76. colorScheme: "system"`,
  77. ];
  78. const feedbackConfigOptions = getFeedbackConfigOptions(params.feedbackOptions);
  79. if (feedbackConfigOptions) {
  80. feedbackIntegration.push(feedbackConfigOptions);
  81. }
  82. integrations.push(
  83. `
  84. Sentry.feedbackIntegration({
  85. ${feedbackIntegration.join(',')}
  86. }),`
  87. );
  88. }
  89. if (params.isReplaySelected) {
  90. integrations.push(
  91. `
  92. new ${getSiblingImportName(siblingOption)}.Replay(${getReplayConfigOptions(
  93. params.replayOptions
  94. )}),`
  95. );
  96. }
  97. return integrations.join(',');
  98. }
  99. const getSentryInitLayout = (params: Params, siblingOption: string): string => {
  100. return `${
  101. siblingOption === SiblingOption.VUE2
  102. ? `Vue,`
  103. : siblingOption === SiblingOption.VUE3
  104. ? 'app,'
  105. : ''
  106. }dsn: "${params.dsn.public}",
  107. integrations: [
  108. ${getIntegrations(params, siblingOption)}
  109. ],
  110. ${
  111. params.isPerformanceSelected
  112. ? `
  113. // Tracing
  114. tracesSampleRate: 1.0, // Capture 100% of the transactions`
  115. : ''
  116. }${
  117. params.isReplaySelected
  118. ? `
  119. // Session Replay
  120. replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
  121. replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.`
  122. : ''
  123. }`;
  124. };
  125. const isAngular = (siblingOption: string): boolean =>
  126. siblingOption === SiblingOption.ANGULARV10 ||
  127. siblingOption === SiblingOption.ANGULARV12;
  128. const isVue = (siblingOption: string): boolean =>
  129. siblingOption === SiblingOption.VUE2 || siblingOption === SiblingOption.VUE3;
  130. function getPerformanceIntegration(siblingOption: string): string {
  131. return `${
  132. isVue(siblingOption)
  133. ? `routingInstrumentation: SentryVue.vueRouterInstrumentation(router),`
  134. : isAngular(siblingOption)
  135. ? `routingInstrumentation: SentryAngular.routingInstrumentation,`
  136. : ''
  137. }`;
  138. }
  139. const performanceAngularErrorHandler = `,
  140. {
  141. provide: SentryAngular.TraceService,
  142. deps: [Router],
  143. },
  144. {
  145. provide: APP_INITIALIZER,
  146. useFactory: () => () => {},
  147. deps: [SentryAngular.TraceService],
  148. multi: true,
  149. },`;
  150. const onboarding: OnboardingConfig<PlatformOptions> = {
  151. install: (params: Params) => [
  152. {
  153. type: StepType.INSTALL,
  154. description: (
  155. <p>
  156. {tct(
  157. `Install the Sentry Capacitor SDK as a dependency using [code:npm] or [code:yarn], alongside the Sentry [siblingName:] SDK:`,
  158. {
  159. code: <code />,
  160. siblingName: getSiblingName(params.platformOptions.siblingOption),
  161. }
  162. )}
  163. </p>
  164. ),
  165. configurations: [
  166. {
  167. language: 'bash',
  168. code: [
  169. {
  170. label: 'npm',
  171. value: 'npm',
  172. language: 'bash',
  173. code: `npm install --save @sentry/capacitor ${getNpmPackage(
  174. params.platformOptions.siblingOption
  175. )}@^7`,
  176. },
  177. {
  178. label: 'yarn',
  179. value: 'yarn',
  180. language: 'bash',
  181. code: `yarn add @sentry/capacitor ${getNpmPackage(
  182. params.platformOptions.siblingOption
  183. )}@^7 --exact`,
  184. },
  185. ],
  186. },
  187. {
  188. additionalInfo: (
  189. <p>
  190. {tct(
  191. `The version of the Sentry [siblingName:] SDK must match with the version referred by Sentry Capacitor. To check which version of the Sentry [siblingName:] SDK is installed, use the following command: [code:npm info @sentry/capacitor peerDependencies]`,
  192. {
  193. code: <code />,
  194. siblingName: getSiblingName(params.platformOptions.siblingOption),
  195. }
  196. )}
  197. </p>
  198. ),
  199. },
  200. ],
  201. },
  202. ],
  203. configure: params => [
  204. {
  205. type: StepType.CONFIGURE,
  206. configurations: getSetupConfiguration({params, showExtraStep: true}),
  207. },
  208. getUploadSourceMapsStep({
  209. guideLink:
  210. 'https://docs.sentry.io/platforms/javascript/guides/capacitor/sourcemaps/',
  211. ...params,
  212. }),
  213. ],
  214. verify: _ => [
  215. {
  216. type: StepType.VERIFY,
  217. description: t(
  218. "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
  219. ),
  220. configurations: [
  221. {
  222. language: 'javascript',
  223. code: `myUndefinedFunction();`,
  224. },
  225. ],
  226. },
  227. ],
  228. nextSteps: () => [
  229. {
  230. id: 'capacitor-android-setup',
  231. name: t('Capacitor 2 Setup'),
  232. description: t(
  233. 'If you are using Capacitor 2 or older, follow this step to add required changes in order to initialize the Capacitor SDK on Android.'
  234. ),
  235. link: 'https://docs.sentry.io/platforms/javascript/guides/capacitor/?#capacitor-2---android-specifics',
  236. },
  237. ],
  238. };
  239. function getSiblingImportsSetupConfiguration(siblingOption: string): string {
  240. switch (siblingOption) {
  241. case SiblingOption.VUE3:
  242. return `import {createApp} from "vue";
  243. import {createRouter} from "vue-router";`;
  244. case SiblingOption.VUE2:
  245. return `import Vue from "vue";
  246. import Router from "vue-router";`;
  247. default:
  248. return '';
  249. }
  250. }
  251. function getVueConstSetup(siblingOption: string): string {
  252. switch (siblingOption) {
  253. case SiblingOption.VUE3:
  254. return `
  255. const app = createApp({
  256. // ...
  257. });
  258. const router = createRouter({
  259. // ...
  260. });
  261. `;
  262. case SiblingOption.VUE2:
  263. return `
  264. Vue.use(Router);
  265. const router = new Router({
  266. // ...
  267. });
  268. `;
  269. default:
  270. return '';
  271. }
  272. }
  273. function getSetupConfiguration({
  274. params,
  275. showExtraStep,
  276. showDescription,
  277. }: {
  278. params: Params;
  279. showExtraStep: boolean;
  280. showDescription?: boolean;
  281. }) {
  282. const siblingOption = params.platformOptions.siblingOption;
  283. const sentryInitLayout = getSentryInitLayout(params, siblingOption);
  284. const configuration = [
  285. {
  286. description: showDescription
  287. ? tct(
  288. `You should init the Sentry capacitor SDK in your [code:main.ts] file as soon as possible during application load up, before initializing Sentry [siblingName:]:`,
  289. {
  290. siblingName: getSiblingName(siblingOption),
  291. code: <code />,
  292. }
  293. )
  294. : null,
  295. language: 'javascript',
  296. code: `${getSiblingImportsSetupConfiguration(siblingOption)}
  297. import * as Sentry from '@sentry/capacitor';
  298. import * as ${getSiblingImportName(siblingOption)} from '${getNpmPackage(
  299. siblingOption
  300. )}';
  301. ${getVueConstSetup(siblingOption)}
  302. Sentry.init({
  303. ${sentryInitLayout}
  304. },
  305. // Forward the init method from ${getNpmPackage(params.platformOptions.siblingOption)}
  306. ${getSiblingImportName(siblingOption)}.init
  307. );`,
  308. },
  309. ];
  310. if (isAngular(siblingOption) && showExtraStep) {
  311. configuration.push({
  312. description: tct(
  313. "The Sentry Angular SDK exports a function to instantiate ErrorHandler provider that will automatically send JavaScript errors captured by the Angular's error handler.",
  314. {}
  315. ),
  316. language: 'javascript',
  317. code: `
  318. import { APP_INITIALIZER, ErrorHandler, NgModule } from "@angular/core";
  319. import { Router } from "@angular/router";
  320. import * as SentryAngular from "${getNpmPackage(siblingOption)}";
  321. @NgModule({
  322. // ...
  323. providers: [
  324. {
  325. provide: ErrorHandler,
  326. useValue: SentryAngular.createErrorHandler(),
  327. }${params.isPerformanceSelected ? performanceAngularErrorHandler : ' '}
  328. ],
  329. // ...
  330. })
  331. export class AppModule {}`,
  332. });
  333. }
  334. return configuration;
  335. }
  336. function getNpmPackage(siblingOption: string): string {
  337. const packages: Record<SiblingOption, string> = {
  338. [SiblingOption.ANGULARV10]: '@sentry/angular',
  339. [SiblingOption.ANGULARV12]: '@sentry/angular-ivy',
  340. [SiblingOption.REACT]: '@sentry/react',
  341. [SiblingOption.VUE3]: '@sentry/vue',
  342. [SiblingOption.VUE2]: '@sentry/vue',
  343. };
  344. return packages[siblingOption];
  345. }
  346. function getSiblingName(siblingOption: string): string {
  347. siblingOption;
  348. switch (siblingOption) {
  349. case SiblingOption.ANGULARV10:
  350. case SiblingOption.ANGULARV12:
  351. return 'Angular';
  352. case SiblingOption.REACT:
  353. return 'React';
  354. case SiblingOption.VUE2:
  355. case SiblingOption.VUE3:
  356. return 'Vue';
  357. default:
  358. return '';
  359. }
  360. }
  361. function getSiblingImportName(siblingOption: string): string {
  362. siblingOption;
  363. switch (siblingOption) {
  364. case SiblingOption.ANGULARV10:
  365. case SiblingOption.ANGULARV12:
  366. return 'SentryAngular';
  367. case SiblingOption.REACT:
  368. return 'SentryReact';
  369. case SiblingOption.VUE2:
  370. case SiblingOption.VUE3:
  371. return 'SentryVue';
  372. default:
  373. return '';
  374. }
  375. }
  376. const replayOnboarding: OnboardingConfig<PlatformOptions> = {
  377. install: params => onboarding.install(params),
  378. configure: params => [
  379. {
  380. type: StepType.CONFIGURE,
  381. description: getReplayConfigureDescription({
  382. link: 'https://docs.sentry.io/platforms/javascript/guides/capacitor/session-replay/',
  383. }),
  384. configurations: getSetupConfiguration({
  385. params,
  386. showExtraStep: false,
  387. showDescription: false,
  388. }),
  389. additionalInfo: <TracePropagationMessage />,
  390. },
  391. ],
  392. verify: getReplayVerifyStep(),
  393. nextSteps: () => [],
  394. };
  395. const feedbackOnboarding: OnboardingConfig<PlatformOptions> = {
  396. install: (params: Params) => onboarding.install(params),
  397. configure: (params: Params) => [
  398. {
  399. type: StepType.CONFIGURE,
  400. description: getFeedbackConfigureDescription({
  401. linkConfig:
  402. 'https://docs.sentry.io/platforms/javascript/guides/capacitor/user-feedback/configuration/',
  403. linkButton:
  404. 'https://docs.sentry.io/platforms/javascript/guides/capacitor/user-feedback/configuration/#bring-your-own-button',
  405. }),
  406. configurations: getSetupConfiguration({
  407. params,
  408. showExtraStep: false,
  409. showDescription: false,
  410. }),
  411. additionalInfo: crashReportCallout({
  412. link: 'https://docs.sentry.io/platforms/javascript/guides/capacitor/user-feedback/#crash-report-modal',
  413. }),
  414. },
  415. ],
  416. verify: () => [],
  417. nextSteps: () => [],
  418. };
  419. const crashReportOnboarding: OnboardingConfig<PlatformOptions> = {
  420. introduction: () => getCrashReportModalIntroduction(),
  421. install: (params: Params) => getCrashReportJavaScriptInstallStep(params),
  422. configure: () => [
  423. {
  424. type: StepType.CONFIGURE,
  425. description: getCrashReportModalConfigDescription({
  426. link: 'https://docs.sentry.io/platforms/javascript/guides/capacitor/user-feedback/configuration/#crash-report-modal',
  427. }),
  428. additionalInfo: widgetCallout({
  429. link: 'https://docs.sentry.io/platforms/javascript/guides/capacitor/user-feedback/#user-feedback-widget',
  430. }),
  431. },
  432. ],
  433. verify: () => [],
  434. nextSteps: () => [],
  435. };
  436. const docs: Docs<PlatformOptions> = {
  437. onboarding,
  438. platformOptions,
  439. feedbackOnboardingNpm: feedbackOnboarding,
  440. replayOnboarding,
  441. crashReportOnboarding,
  442. };
  443. export default docs;