sourceMapsDebuggerModal.tsx 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. import type {PropsWithChildren, ReactNode} from 'react';
  2. import {Fragment, useState} from 'react';
  3. import {useTheme} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import type {ModalRenderProps} from 'sentry/actionCreators/modal';
  6. import {openModal} from 'sentry/actionCreators/modal';
  7. import Alert from 'sentry/components/alert';
  8. import {CodeSnippet} from 'sentry/components/codeSnippet';
  9. import FeatureBadge from 'sentry/components/featureBadge';
  10. import {FeedbackModal} from 'sentry/components/featureFeedback/feedbackModal';
  11. import ExternalLink from 'sentry/components/links/externalLink';
  12. import Link from 'sentry/components/links/link';
  13. import ProgressRing from 'sentry/components/progressRing';
  14. import {TabPanels, Tabs} from 'sentry/components/tabs';
  15. import {TabList} from 'sentry/components/tabs/tabList';
  16. import {
  17. IconCheckmark,
  18. IconCircle,
  19. IconMegaphone,
  20. IconOpen,
  21. IconQuestion,
  22. IconRefresh,
  23. IconWarning,
  24. } from 'sentry/icons';
  25. import {t, tct} from 'sentry/locale';
  26. import {space} from 'sentry/styles/space';
  27. import type {Organization} from 'sentry/types';
  28. import {trackAnalytics} from 'sentry/utils/analytics';
  29. import type {SourceMapWizardBlueThunderAnalyticsParams} from 'sentry/utils/analytics/stackTraceAnalyticsEvents';
  30. const SOURCE_MAP_SCRAPING_REASON_MAP = {
  31. not_found: {
  32. shortName: t('Not Found'),
  33. explanation: t('The source map could not be found at its defined location.'),
  34. },
  35. disabled: {
  36. shortName: t('Disabled'),
  37. explanation: t('JavaScript source fetching is disabled in your project settings.'),
  38. },
  39. invalid_host: {
  40. shortName: t('Invalid Host'),
  41. explanation: t(
  42. 'The source map location was not in the list of allowed domains in your project settings, or the URL was malformed.'
  43. ),
  44. },
  45. permission_denied: {
  46. shortName: t('Permission Denied'),
  47. explanation: t(
  48. 'Permission to access the source map was denied by the server hosting the source map. This means that the server hosting the source map returned a 401 Unauthorized or a 403 Forbidden response code.'
  49. ),
  50. },
  51. timeout: {
  52. shortName: t('Timeout'),
  53. explanation: t('The request to download the source map timed out.'),
  54. },
  55. download_error: {
  56. shortName: t('Download Error'),
  57. explanation: t('There was an error while downloading the source map.'),
  58. },
  59. other: {
  60. shortName: t('Unknown'),
  61. explanation: t('Fetching the source map failed for an unknown reason.'),
  62. },
  63. } as const;
  64. const SOURCE_FILE_SCRAPING_REASON_MAP = {
  65. not_found: {
  66. shortName: t('Not Found'),
  67. explanation: t('The source file could not be found at its defined location.'),
  68. },
  69. disabled: {
  70. shortName: t('Disabled'),
  71. explanation: t('JavaScript source fetching is disabled in your project settings.'),
  72. },
  73. invalid_host: {
  74. shortName: t('Invalid Host'),
  75. explanation: t(
  76. 'The source file location was not in the list of allowed domains in your project settings, or the URL was malformed.'
  77. ),
  78. },
  79. permission_denied: {
  80. shortName: t('Permission Denied'),
  81. explanation: t(
  82. 'Permission to access the source file was denied by the server hosting it. This means that the server hosting the source file returned a 401 Unauthorized or a 403 Forbidden response code.'
  83. ),
  84. },
  85. timeout: {
  86. shortName: t('Timeout'),
  87. explanation: t('The request to download the source file timed out.'),
  88. },
  89. download_error: {
  90. shortName: t('Download Error'),
  91. explanation: t('There was an error while downloading the source file.'),
  92. },
  93. other: {
  94. shortName: t('Unknown'),
  95. explanation: t('Fetching the source file failed for an unknown reason.'),
  96. },
  97. } as const;
  98. export interface FrameSourceMapDebuggerData {
  99. debugIdProgress: number;
  100. debugIdProgressPercent: number;
  101. dist: string | null;
  102. eventHasDebugIds: boolean;
  103. frameIsResolved: boolean;
  104. hasScrapingData: boolean;
  105. matchingSourceFileNames: string[];
  106. matchingSourceMapName: string | null;
  107. minDebugIdSdkVersion: string | null;
  108. release: string | null;
  109. releaseHasSomeArtifact: boolean;
  110. releaseProgress: number;
  111. releaseProgressPercent: number;
  112. releaseSourceMapReference: string | null;
  113. scrapingProgress: number;
  114. scrapingProgressPercent: number;
  115. sdkDebugIdSupport: 'full' | 'needs-upgrade' | 'not-supported' | 'unofficial-sdk';
  116. sdkName: string | null;
  117. sdkVersion: string | null;
  118. sourceFileReleaseNameFetchingResult: 'found' | 'wrong-dist' | 'unsuccessful';
  119. sourceFileScrapingStatus:
  120. | {status: 'success'; url: string}
  121. | {reason: string; status: 'failure'; url: string; details?: string}
  122. | {status: 'not_attempted'; url: string}
  123. | null;
  124. sourceMapReleaseNameFetchingResult: 'found' | 'wrong-dist' | 'unsuccessful';
  125. sourceMapScrapingStatus:
  126. | {status: 'success'; url: string}
  127. | {reason: string; status: 'failure'; url: string; details?: string}
  128. | {status: 'not_attempted'; url: string}
  129. | null;
  130. stackFrameDebugId: string | null;
  131. stackFramePath: string | null;
  132. uploadedSomeArtifactWithDebugId: boolean;
  133. uploadedSourceFileWithCorrectDebugId: boolean;
  134. uploadedSourceMapWithCorrectDebugId: boolean;
  135. }
  136. interface SourceMapsDebuggerModalProps extends ModalRenderProps {
  137. analyticsParams: SourceMapWizardBlueThunderAnalyticsParams & {
  138. organization: Organization | null;
  139. };
  140. sourceResolutionResults: FrameSourceMapDebuggerData;
  141. }
  142. export function SourceMapsDebuggerModal({
  143. Body,
  144. Header,
  145. Footer,
  146. sourceResolutionResults,
  147. analyticsParams,
  148. }: SourceMapsDebuggerModalProps) {
  149. const theme = useTheme();
  150. const [activeTab, setActiveTab] = useState<'debug-ids' | 'release' | 'fetching'>(() => {
  151. const possibleTabs = [
  152. {tab: 'debug-ids', progress: sourceResolutionResults.debugIdProgressPercent},
  153. {tab: 'release', progress: sourceResolutionResults.releaseProgressPercent},
  154. {tab: 'fetching', progress: sourceResolutionResults.scrapingProgressPercent},
  155. ] as const;
  156. // Get the tab with the most progress
  157. return possibleTabs.reduce(
  158. (prev, curr) => (curr.progress > prev.progress ? curr : prev),
  159. possibleTabs[sourceResolutionResults.sdkDebugIdSupport === 'not-supported' ? 1 : 0]
  160. ).tab;
  161. });
  162. return (
  163. <Fragment>
  164. <Header closeButton>
  165. <ModalHeadingContainer>
  166. <h4>{t('Make Your Stack Traces Readable')}</h4>
  167. <FeatureBadge type="beta" tooltipProps={{position: 'right'}} />
  168. </ModalHeadingContainer>
  169. </Header>
  170. <Body>
  171. <p>
  172. {t(
  173. "It looks like the original source code for this stack frame couldn't be determined when this error was captured. To get the original code for this stack frame, Sentry needs source maps to be configured."
  174. )}
  175. </p>
  176. <WizardInstructionParagraph>
  177. {t(
  178. 'The easiest way to get started with source maps is by running the Sentry Source Map Wizard in the terminal inside your project:'
  179. )}
  180. </WizardInstructionParagraph>
  181. <InstructionCodeSnippet
  182. language="bash"
  183. dark
  184. onCopy={() => {
  185. trackAnalytics(
  186. 'source_map_debug_blue_thunder.source_map_wizard_command_copied',
  187. analyticsParams
  188. );
  189. }}
  190. onSelectAndCopy={() => {
  191. trackAnalytics(
  192. 'source_map_debug_blue_thunder.source_map_wizard_command_copied',
  193. analyticsParams
  194. );
  195. }}
  196. >
  197. {'npx @sentry/wizard@latest -i sourcemaps'}
  198. </InstructionCodeSnippet>
  199. <p>
  200. {t(
  201. 'There are multiple ways to configure source maps. The checklists below will help you set them up correctly. Choose one of the following processes:'
  202. )}
  203. </p>
  204. <Tabs<'debug-ids' | 'release' | 'fetching'>
  205. value={activeTab}
  206. onChange={tab => {
  207. setActiveTab(tab);
  208. }}
  209. >
  210. <TabList>
  211. <TabList.Item
  212. key="debug-ids"
  213. textValue={`${t('Debug IDs')} (${
  214. sourceResolutionResults.debugIdProgress
  215. }/4)`}
  216. >
  217. <StyledProgressRing
  218. progressColor={
  219. activeTab === 'debug-ids' ? theme.purple300 : theme.gray300
  220. }
  221. backgroundColor={theme.gray200}
  222. value={sourceResolutionResults.debugIdProgressPercent * 100}
  223. size={16}
  224. barWidth={4}
  225. />
  226. {`${t('Debug IDs')}${
  227. sourceResolutionResults.sdkDebugIdSupport !== 'not-supported'
  228. ? ' ' + t('(recommended)')
  229. : ''
  230. }`}
  231. </TabList.Item>
  232. <TabList.Item
  233. key="release"
  234. textValue={`${t('Releases')} (${
  235. sourceResolutionResults.releaseProgress
  236. }/4)`}
  237. >
  238. <StyledProgressRing
  239. progressColor={activeTab === 'release' ? theme.purple300 : theme.gray300}
  240. backgroundColor={theme.gray200}
  241. value={sourceResolutionResults.releaseProgressPercent * 100}
  242. size={16}
  243. barWidth={4}
  244. />
  245. {t('Releases')}
  246. </TabList.Item>
  247. <TabList.Item
  248. key="fetching"
  249. textValue={`${t('Hosting Publicly')} (${
  250. sourceResolutionResults.scrapingProgress
  251. }/4)`}
  252. hidden={
  253. !sourceResolutionResults.hasScrapingData ||
  254. !sourceResolutionResults.sdkName?.startsWith(
  255. 'sentry.javascript.react-native'
  256. )
  257. }
  258. >
  259. <StyledProgressRing
  260. progressColor={activeTab === 'fetching' ? theme.purple300 : theme.gray300}
  261. backgroundColor={theme.gray200}
  262. value={sourceResolutionResults.scrapingProgressPercent * 100}
  263. size={16}
  264. barWidth={4}
  265. />
  266. {t('Hosting Publicly')}
  267. </TabList.Item>
  268. </TabList>
  269. <StyledTabPanels>
  270. <TabPanels.Item key="debug-ids">
  271. <p>
  272. {tct(
  273. '[link:Debug IDs] are a way of matching your source files to source maps. Follow all of the steps below to get a readable stack trace:',
  274. {
  275. link: (
  276. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js/artifact-bundles/" />
  277. ),
  278. }
  279. )}
  280. </p>
  281. <CheckList>
  282. <InstalledSdkChecklistItem
  283. setActiveTab={setActiveTab}
  284. sourceResolutionResults={sourceResolutionResults}
  285. />
  286. <HasDebugIdChecklistItem
  287. shouldValidate={
  288. sourceResolutionResults.sdkDebugIdSupport === 'full' ||
  289. sourceResolutionResults.sdkDebugIdSupport === 'unofficial-sdk' ||
  290. sourceResolutionResults.eventHasDebugIds
  291. }
  292. sourceResolutionResults={sourceResolutionResults}
  293. />
  294. <UploadedSourceFileWithCorrectDebugIdChecklistItem
  295. shouldValidate={sourceResolutionResults.stackFrameDebugId !== null}
  296. sourceResolutionResults={sourceResolutionResults}
  297. />
  298. <UploadedSourceMapWithCorrectDebugIdChecklistItem
  299. shouldValidate={
  300. sourceResolutionResults.uploadedSourceFileWithCorrectDebugId
  301. }
  302. sourceResolutionResults={sourceResolutionResults}
  303. />
  304. </CheckList>
  305. {sourceResolutionResults.debugIdProgressPercent === 1 ? (
  306. <ChecklistDoneNote />
  307. ) : (
  308. <VerifyAgainNote />
  309. )}
  310. </TabPanels.Item>
  311. <TabPanels.Item key="release">
  312. <p>
  313. {tct(
  314. 'You can match your stack trace to your source code based on [link:Releases] and artifact names. Follow all of the steps below to get a readable stack trace:',
  315. {
  316. link: (
  317. <ExternalLinkWithIcon href="https://docs.sentry.io/product/releases/" />
  318. ),
  319. }
  320. )}
  321. </p>
  322. <CheckList>
  323. <EventHasReleaseNameChecklistItem
  324. sourceResolutionResults={sourceResolutionResults}
  325. />
  326. <ReleaseHasUploadedArtifactsChecklistItem
  327. shouldValidate={sourceResolutionResults.release !== null}
  328. sourceResolutionResults={sourceResolutionResults}
  329. />
  330. <ReleaseSourceFileMatchingChecklistItem
  331. shouldValidate={sourceResolutionResults.releaseHasSomeArtifact}
  332. sourceResolutionResults={sourceResolutionResults}
  333. />
  334. <ReleaseSourceMapMatchingChecklistItem
  335. shouldValidate={
  336. sourceResolutionResults.sourceFileReleaseNameFetchingResult ===
  337. 'found'
  338. }
  339. sourceResolutionResults={sourceResolutionResults}
  340. />
  341. </CheckList>
  342. {sourceResolutionResults.releaseProgressPercent === 1 ? (
  343. <ChecklistDoneNote />
  344. ) : (
  345. <VerifyAgainNote />
  346. )}
  347. </TabPanels.Item>
  348. <TabPanels.Item key="fetching">
  349. <p>
  350. {tct(
  351. 'Sentry will fetch your source files and source maps if you [link:host them publicly].',
  352. {
  353. link: (
  354. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/hosting-publicly/" />
  355. ),
  356. }
  357. )}
  358. </p>
  359. <CheckList>
  360. <ScrapingSourceFileAvailableChecklistItem
  361. sourceResolutionResults={sourceResolutionResults}
  362. />
  363. <ScrapingSourceMapAvailableChecklistItem
  364. sourceResolutionResults={sourceResolutionResults}
  365. />
  366. </CheckList>
  367. {sourceResolutionResults.scrapingProgressPercent === 1 ? (
  368. <ChecklistDoneNote />
  369. ) : (
  370. <VerifyAgainNote />
  371. )}
  372. </TabPanels.Item>
  373. </StyledTabPanels>
  374. </Tabs>
  375. </Body>
  376. <Footer>
  377. <Link
  378. to=""
  379. onClick={e => {
  380. e.stopPropagation();
  381. openModal(modalProps => (
  382. <FeedbackModal
  383. featureName="sourcemaps-debugger"
  384. feedbackTypes={[t('This was helpful'), t('This was not helpful')]}
  385. {...modalProps}
  386. />
  387. ));
  388. }}
  389. >
  390. {t('Was this helpful? Give us feedback!')} <IconMegaphone size="xs" />
  391. </Link>
  392. </Footer>
  393. </Fragment>
  394. );
  395. }
  396. function CheckListItem({children, title, status}: PropsWithChildren<CheckListItemProps>) {
  397. return (
  398. <ListItemContainer>
  399. <CheckMarkContainer>
  400. {
  401. {
  402. none: <IconCircle size="md" color="gray200" />,
  403. checked: <IconCheckmark size="md" color="green300" isCircled />,
  404. alert: <IconWarning size="md" color="yellow300" />,
  405. question: <IconQuestion size="md" color="gray300" />,
  406. }[status]
  407. }
  408. <Line className="source-map-debugger-modal-checklist-line" />
  409. </CheckMarkContainer>
  410. <ListItemContentContainer>
  411. <ListItemTitleWrapper>
  412. <ListItemTitle status={status}>{title}</ListItemTitle>
  413. </ListItemTitleWrapper>
  414. {children}
  415. </ListItemContentContainer>
  416. </ListItemContainer>
  417. );
  418. }
  419. function InstalledSdkChecklistItem({
  420. sourceResolutionResults,
  421. setActiveTab,
  422. }: {
  423. setActiveTab: React.Dispatch<
  424. React.SetStateAction<'release' | 'debug-ids' | 'fetching'>
  425. >;
  426. sourceResolutionResults: FrameSourceMapDebuggerData;
  427. }) {
  428. const successMessage = t('Installed SDK supports Debug IDs');
  429. const errorMessage = t('Installed SDK does not support Debug IDs');
  430. const maybeErrorMessage = t("Installed SDK potentially doesn't support Debug IDs");
  431. if (
  432. sourceResolutionResults.eventHasDebugIds ||
  433. sourceResolutionResults.sdkDebugIdSupport === 'full'
  434. ) {
  435. return <CheckListItem status="checked" title={successMessage} />;
  436. }
  437. if (sourceResolutionResults.sdkDebugIdSupport === 'needs-upgrade') {
  438. return (
  439. <CheckListItem status="alert" title={errorMessage}>
  440. <CheckListInstruction type="muted">
  441. <h6>{t('Outdated SDK')}</h6>
  442. <p>
  443. {sourceResolutionResults.sdkVersion !== null
  444. ? tct(
  445. 'You are using version [currentVersion] of the Sentry SDK which does not support debug IDs.',
  446. {
  447. currentVersion: (
  448. <MonoBlock>{sourceResolutionResults.sdkVersion}</MonoBlock>
  449. ),
  450. }
  451. )
  452. : t(
  453. 'You are using an outdated version of the Sentry SDK which does not support debug IDs.'
  454. )}{' '}
  455. {sourceResolutionResults.minDebugIdSdkVersion !== null
  456. ? tct('You should upgrade to version [targetVersion] or higher.', {
  457. targetVersion: (
  458. <MonoBlock>{sourceResolutionResults.minDebugIdSdkVersion}</MonoBlock>
  459. ),
  460. })
  461. : t('You should upgrade to the latest version.')}
  462. </p>
  463. <p>
  464. {tct(
  465. 'If upgrading the SDK is not an option for you, you can use the [link:Release] process instead.',
  466. {
  467. link: <Link to="" onClick={() => setActiveTab('release')} />,
  468. }
  469. )}
  470. </p>
  471. </CheckListInstruction>
  472. </CheckListItem>
  473. );
  474. }
  475. if (sourceResolutionResults.sdkDebugIdSupport === 'not-supported') {
  476. return (
  477. <CheckListItem status="alert" title={errorMessage}>
  478. <CheckListInstruction type="muted">
  479. <h6>{t("SDK Doesn't Support Debug IDs")}</h6>
  480. <p>
  481. {tct(
  482. 'The SDK you are using does not support debug IDs yet. We recommend using the [link:Release] process instead.',
  483. {
  484. link: <Link to="" onClick={() => setActiveTab('release')} />,
  485. }
  486. )}
  487. </p>
  488. </CheckListInstruction>
  489. </CheckListItem>
  490. );
  491. }
  492. return (
  493. <CheckListItem status="question" title={maybeErrorMessage}>
  494. <CheckListInstruction type="muted">
  495. <h6>{t('Unofficial SDK')}</h6>
  496. <p>
  497. {tct(
  498. "You are using an unofficial Sentry SDK. Please check whether this SDK already supports Debug IDs. It's possible that this SDK supports debug IDs but you may be better off using the [link:Release Name] method of uploading source maps.",
  499. {
  500. link: <Link to="" onClick={() => setActiveTab('release')} />,
  501. }
  502. )}
  503. </p>
  504. <p>
  505. {t(
  506. 'If this SDK depends on an official Sentry SDK, the earliest version that supports Debug IDs is version 7.56.0'
  507. )}
  508. </p>
  509. </CheckListInstruction>
  510. </CheckListItem>
  511. );
  512. }
  513. function HasDebugIdChecklistItem({
  514. sourceResolutionResults,
  515. shouldValidate,
  516. }: {
  517. shouldValidate: boolean;
  518. sourceResolutionResults: FrameSourceMapDebuggerData;
  519. }) {
  520. const successMessage = t('Stack frame has Debug IDs');
  521. const errorMessage = t("Stack frame doesn't have Debug IDs");
  522. if (!shouldValidate) {
  523. return <CheckListItem status="none" title={successMessage} />;
  524. }
  525. if (sourceResolutionResults.stackFrameDebugId !== null) {
  526. return <CheckListItem status="checked" title={successMessage} />;
  527. }
  528. if (sourceResolutionResults.eventHasDebugIds) {
  529. return (
  530. <CheckListItem status="alert" title={errorMessage}>
  531. <CheckListInstruction type="muted">
  532. <h6>{t('Source Is Missing Injection')}</h6>
  533. <p>
  534. {tct(
  535. 'The event already has debug IDs for some stack frames but not for this one. Please configure the tool you are using to upload source maps to inject debug IDs into [bold:all] of your build artifacts.',
  536. {bold: <b />}
  537. )}
  538. </p>
  539. </CheckListInstruction>
  540. </CheckListItem>
  541. );
  542. }
  543. if (sourceResolutionResults.uploadedSomeArtifactWithDebugId) {
  544. return (
  545. <CheckListItem status="alert" title={errorMessage}>
  546. <CheckListInstruction type="muted">
  547. <h6>Uploaded Files Not Deployed</h6>
  548. <p>
  549. {t(
  550. "It seems you already uploaded artifacts with Debug IDs, however, this event doesn't contain any Debug IDs yet. Generally this means that you didn't deploy the same files you injected the Debug IDs into. For Sentry to be able to show your original source code, it is required that you deploy the exact same files that you uploaded to Sentry."
  551. )}
  552. </p>
  553. <p>
  554. {tct(
  555. 'If you are using a [bundlerPluginRepoLink:Sentry Plugin for your Bundler], the plugin needs to be active when building your production app. You cannot do two separate builds, for example, one for uploading to Sentry with the plugin being active and one for deploying without the plugin. The plugin needs to be active for every build.',
  556. {
  557. bundlerPluginRepoLink: (
  558. <ExternalLinkWithIcon href="https://github.com/getsentry/sentry-javascript-bundler-plugins" />
  559. ),
  560. }
  561. )}
  562. </p>
  563. <p>
  564. {tct(
  565. 'If you are utilizing [sentryCliLink:Sentry CLI], ensure that you deploy the exact files that the [injectCommand] command has modified!',
  566. {
  567. sentryCliLink: (
  568. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/cli/" />
  569. ),
  570. injectCommand: <MonoBlock>sentry-cli sourcemaps inject</MonoBlock>,
  571. }
  572. )}
  573. </p>
  574. <p>
  575. {tct(
  576. 'Read the [link:Sentry Source Maps Documentation] to learn how to inject Debug IDs into your build artifacts and how to upload them to Sentry.',
  577. {
  578. link: (
  579. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/" />
  580. ),
  581. }
  582. )}
  583. </p>
  584. </CheckListInstruction>
  585. </CheckListItem>
  586. );
  587. }
  588. return (
  589. <CheckListItem status="alert" title={errorMessage}>
  590. <CheckListInstruction type="muted">
  591. <h6>{t('No Debug ID Tooling Used')}</h6>
  592. <p>
  593. {tct(
  594. "This event doesn't contain any Debug IDs. Read the [link:Sentry Source Maps Documentation] to learn how to inject Debug IDs into your build artifacts and how to upload them to Sentry.",
  595. {
  596. link: (
  597. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/" />
  598. ),
  599. }
  600. )}
  601. </p>
  602. </CheckListInstruction>
  603. </CheckListItem>
  604. );
  605. }
  606. function UploadedSourceFileWithCorrectDebugIdChecklistItem({
  607. sourceResolutionResults,
  608. shouldValidate,
  609. }: {
  610. shouldValidate: boolean;
  611. sourceResolutionResults: FrameSourceMapDebuggerData;
  612. }) {
  613. const successMessage = t('Source file with a matching Debug ID was uploaded');
  614. const errorMessage = t('Missing source file with a matching Debug ID');
  615. if (!shouldValidate) {
  616. return <CheckListItem status="none" title={successMessage} />;
  617. }
  618. if (sourceResolutionResults.uploadedSourceFileWithCorrectDebugId) {
  619. return <CheckListItem status="checked" title={successMessage} />;
  620. }
  621. if (sourceResolutionResults.uploadedSomeArtifactWithDebugId) {
  622. return (
  623. <CheckListItem status="alert" title={errorMessage}>
  624. <CheckListInstruction type="muted">
  625. <h6>{t('No Source File With Matching Debug ID')}</h6>
  626. <p>
  627. {tct(
  628. "You already uploaded artifacts with Debug IDs but none of the uploaded source files had a Debug ID matching this stack frame's Debug ID: [debugId]",
  629. {
  630. debugId: (
  631. <MonoBlock>{sourceResolutionResults.stackFrameDebugId}</MonoBlock>
  632. ),
  633. }
  634. )}
  635. </p>
  636. <p>
  637. {t(
  638. 'Make sure to inject Debug IDs into all of your source files and to upload all of them to Sentry.'
  639. )}
  640. </p>
  641. {/* TODO: Link to Uploaded Artifacts */}
  642. </CheckListInstruction>
  643. </CheckListItem>
  644. );
  645. }
  646. return (
  647. <CheckListItem status="alert" title={errorMessage}>
  648. <CheckListInstruction type="muted">
  649. <h6>{t('No Artifacts With Debug IDs Uploaded')}</h6>
  650. <p>
  651. {tct(
  652. "You didn't upload any artifacts with debug IDs yet. Read the [link:Sentry Source Maps Documentation] to learn how to inject Debug IDs into your build artifacts and how to upload them to Sentry.",
  653. {
  654. link: (
  655. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/" />
  656. ),
  657. }
  658. )}
  659. </p>
  660. {/* TODO: Link to Uploaded Artifacts */}
  661. </CheckListInstruction>
  662. </CheckListItem>
  663. );
  664. }
  665. function UploadedSourceMapWithCorrectDebugIdChecklistItem({
  666. sourceResolutionResults,
  667. shouldValidate,
  668. }: {
  669. shouldValidate: boolean;
  670. sourceResolutionResults: FrameSourceMapDebuggerData;
  671. }) {
  672. const successMessage = t('Uploaded source map with a matching Debug ID');
  673. const errorMessage = t('Missing source map with a matching Debug ID');
  674. if (!shouldValidate) {
  675. return <CheckListItem status="none" title={successMessage} />;
  676. }
  677. if (sourceResolutionResults.uploadedSourceMapWithCorrectDebugId) {
  678. return <CheckListItem status="checked" title={successMessage} />;
  679. }
  680. if (sourceResolutionResults.uploadedSomeArtifactWithDebugId) {
  681. return (
  682. <CheckListItem status="alert" title={errorMessage}>
  683. <CheckListInstruction type="muted">
  684. <h6>{t('No Source Map With Matching Debug ID')}</h6>
  685. <p>
  686. {tct(
  687. "You already uploaded artifacts with Debug IDs but none of the uploaded source maps had a Debug ID matching this stack frame's Debug ID: [debugId]",
  688. {
  689. debugId: (
  690. <MonoBlock>{sourceResolutionResults.stackFrameDebugId}</MonoBlock>
  691. ),
  692. }
  693. )}
  694. </p>
  695. <p>
  696. {t(
  697. 'Make sure to inject Debug IDs into all of your source files and to upload all of them to Sentry.'
  698. )}
  699. </p>
  700. {/* TODO: Link to Uploaded Artifacts */}
  701. </CheckListInstruction>
  702. <SourceMapStepNotRequiredNote />
  703. </CheckListItem>
  704. );
  705. }
  706. return (
  707. <CheckListItem status="alert" title={errorMessage}>
  708. <CheckListInstruction type="muted">
  709. <h6>{t('No Artifacts Uploaded')}</h6>
  710. <p>
  711. {tct(
  712. "You didn't upload any artifacts with debug IDs yet. Read the [link:Sentry Source Maps Documentation] to learn how to inject Debug IDs into your build artifacts and how to upload them to Sentry.",
  713. {
  714. link: (
  715. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/" />
  716. ),
  717. }
  718. )}
  719. </p>
  720. {/* TODO: Link to Uploaded Artifacts */}
  721. </CheckListInstruction>
  722. <SourceMapStepNotRequiredNote />
  723. </CheckListItem>
  724. );
  725. }
  726. function EventHasReleaseNameChecklistItem({
  727. sourceResolutionResults,
  728. }: {
  729. sourceResolutionResults: FrameSourceMapDebuggerData;
  730. }) {
  731. const successMessage = t('Event has release value');
  732. const errorMessage = t("Event doesn't have a release value");
  733. if (sourceResolutionResults.release !== null) {
  734. return <CheckListItem status="checked" title={successMessage} />;
  735. }
  736. return (
  737. <CheckListItem status="alert" title={errorMessage}>
  738. <CheckListInstruction type="muted">
  739. <h6>{t('No Release Value')}</h6>
  740. <p>
  741. {tct(
  742. 'The captured event does not have a [release] value. Configure a [release] value in the SDK:',
  743. {release: <MonoBlock>release</MonoBlock>}
  744. )}
  745. </p>
  746. <InstructionCodeSnippet language="javascript" dark hideCopyButton>
  747. {`Sentry.init({
  748. release: 'your-release-name'
  749. })`}
  750. </InstructionCodeSnippet>
  751. <p>
  752. {tct(
  753. 'Alternatively, you can configure one of our build tools to automatically inject a release value into your code: [link:Sentry Bundler Support]',
  754. {
  755. link: (
  756. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/sourcemaps/#sentry-bundler-support" />
  757. ),
  758. }
  759. )}
  760. </p>
  761. </CheckListInstruction>
  762. </CheckListItem>
  763. );
  764. }
  765. function ReleaseHasUploadedArtifactsChecklistItem({
  766. sourceResolutionResults,
  767. shouldValidate,
  768. }: {
  769. shouldValidate: boolean;
  770. sourceResolutionResults: FrameSourceMapDebuggerData;
  771. }) {
  772. const successMessage = t('Release has uploaded artifacts');
  773. const errorMessage = t("Release doesn't have uploaded artifacts");
  774. const docsLink = sourceResolutionResults.sdkName?.startsWith(
  775. 'sentry.javascript.react-native'
  776. )
  777. ? 'https://docs.sentry.io/platforms/react-native/sourcemaps/'
  778. : 'https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js/legacy-uploading-methods/';
  779. if (!shouldValidate) {
  780. return <CheckListItem status="none" title={successMessage} />;
  781. }
  782. if (sourceResolutionResults.releaseHasSomeArtifact) {
  783. return <CheckListItem status="checked" title={successMessage} />;
  784. }
  785. return (
  786. <CheckListItem status="alert" title={errorMessage}>
  787. <CheckListInstruction type="muted">
  788. <h6>{t('No Uploaded Artifacts')}</h6>
  789. <p>
  790. {t(
  791. "The release this event belongs to doesn't have any uploaded artifacts. Upload your build artifacts to Sentry using the release:"
  792. )}{' '}
  793. <MonoBlock>{sourceResolutionResults.release}</MonoBlock>
  794. </p>
  795. <p>
  796. {tct(
  797. 'Read the [link:Sentry Source Maps Documentation] to learn how to to upload your build artifacts to Sentry.',
  798. {
  799. link: <ExternalLinkWithIcon href={docsLink} />,
  800. }
  801. )}
  802. </p>
  803. </CheckListInstruction>
  804. </CheckListItem>
  805. );
  806. }
  807. function ReleaseSourceFileMatchingChecklistItem({
  808. sourceResolutionResults,
  809. shouldValidate,
  810. }: {
  811. shouldValidate: boolean;
  812. sourceResolutionResults: FrameSourceMapDebuggerData;
  813. }) {
  814. const successMessage = t('Stack frame path matches a source file artifact name');
  815. const errorMessage = t("Stack frame path doesn't match a source file artifact name");
  816. if (!shouldValidate) {
  817. return <CheckListItem status="none" title={successMessage} />;
  818. }
  819. if (sourceResolutionResults.sourceFileReleaseNameFetchingResult === 'found') {
  820. return <CheckListItem status="checked" title={successMessage} />;
  821. }
  822. if (sourceResolutionResults.sourceFileReleaseNameFetchingResult === 'wrong-dist') {
  823. return (
  824. <CheckListItem status="alert" title={errorMessage}>
  825. <CheckListInstruction type="muted">
  826. <h6>{t('Dist Value Not Matching')}</h6>
  827. <p>
  828. {t(
  829. 'You uploaded a source file artifact with the right name, however the dist value on this event does not match the dist value on the artifact.'
  830. )}
  831. </p>
  832. {sourceResolutionResults.dist !== null ? (
  833. <p>
  834. {tct(
  835. 'Upload your build artifacts to Sentry using the dist [dist] or adjust the dist value in your SDK options.',
  836. {dist: <MonoBlock>{sourceResolutionResults.dist}</MonoBlock>}
  837. )}
  838. </p>
  839. ) : (
  840. <p>
  841. {tct(
  842. 'Upload your build artifacts to Sentry using a matching [dist] value or adjust the [dist] value in your SDK options.',
  843. {dist: <MonoBlock>dist</MonoBlock>}
  844. )}
  845. </p>
  846. )}
  847. <DistCodeSnippet />
  848. {/* TODO: Link to Uploaded Artifacts */}
  849. </CheckListInstruction>
  850. </CheckListItem>
  851. );
  852. }
  853. if (sourceResolutionResults.stackFramePath === null) {
  854. <CheckListItem status="alert" title={errorMessage}>
  855. <CheckListInstruction type="muted">
  856. <h6>{t('Stack Frame Without Path')}</h6>
  857. <p>
  858. {t(
  859. "This stack frame doesn't have a path. Check your SDK configuration to send a stack frame path!"
  860. )}
  861. </p>
  862. </CheckListInstruction>
  863. </CheckListItem>;
  864. }
  865. return (
  866. <CheckListItem status="alert" title={errorMessage}>
  867. <CheckListInstruction type="muted">
  868. <h6>{t('Stack Frame Not Matching Artifact Name')}</h6>
  869. <p>
  870. {tct(
  871. 'The path for this stack frame is [stackFramePath] and the release value for this event is [release].',
  872. {
  873. stackFramePath: (
  874. <MonoBlock>{sourceResolutionResults.stackFramePath}</MonoBlock>
  875. ),
  876. release: <MonoBlock>{sourceResolutionResults.release}</MonoBlock>,
  877. }
  878. )}
  879. </p>
  880. <p>
  881. {t(
  882. "Sentry was not able to find a file in the release's artifacts that matches one of the following paths:"
  883. )}
  884. </p>
  885. <InstructionList>
  886. {sourceResolutionResults.matchingSourceFileNames.map(mathingSourceFileName => (
  887. <li key={mathingSourceFileName}>
  888. <MonoBlock>{mathingSourceFileName}</MonoBlock>
  889. </li>
  890. ))}
  891. </InstructionList>
  892. <p>
  893. {/* wrong-dist is not deterministically returned in the case of wrong dist values because of database restrictions */}
  894. {sourceResolutionResults.dist !== null
  895. ? tct(
  896. 'This event has a dist value [dist]. Please check that you uploaded your artifacts with dist [dist].',
  897. {
  898. dist: <MonoBlock>{sourceResolutionResults.dist}</MonoBlock>,
  899. }
  900. )
  901. : t(
  902. "This event doesn't have a dist value. Please check that you uploaded your artifacts without dist value."
  903. )}
  904. </p>
  905. {/* TODO: Link to uploaded files for this release. */}
  906. <p>
  907. {tct(
  908. 'If the stack frame path is changing based on runtime parameters, you can use the [link:RewriteFrames integration] to dynamically change the the stack frame path.',
  909. {
  910. link: (
  911. <ExternalLinkWithIcon href="https://docs.sentry.io/platforms/javascript/configuration/integrations/rewriteframes/" />
  912. ),
  913. }
  914. )}
  915. </p>
  916. </CheckListInstruction>
  917. </CheckListItem>
  918. );
  919. }
  920. function ReleaseSourceMapMatchingChecklistItem({
  921. sourceResolutionResults,
  922. shouldValidate,
  923. }: {
  924. shouldValidate: boolean;
  925. sourceResolutionResults: FrameSourceMapDebuggerData;
  926. }) {
  927. const successMessage = t('Source map reference matches a source map artifact name');
  928. const errorMessage = t("Source map reference doesn't match a source map artifact name");
  929. if (!shouldValidate) {
  930. return <CheckListItem status="none" title={successMessage} />;
  931. }
  932. if (sourceResolutionResults.sourceMapReleaseNameFetchingResult === 'found') {
  933. return <CheckListItem status="checked" title={successMessage} />;
  934. }
  935. if (sourceResolutionResults.releaseSourceMapReference === null) {
  936. return (
  937. <CheckListItem status="alert" title={errorMessage}>
  938. <CheckListInstruction type="muted">
  939. <h6>{t('Missing Source Map Reference')}</h6>
  940. <p>
  941. {tct(
  942. 'The source file for this stack frame is missing a source map reference. A source map reference is usually represented by a [sourceMappingUrl] comment at the bottom of your source file.',
  943. {sourceMappingUrl: <MonoBlock>//# sourceMappingURL=...</MonoBlock>}
  944. )}
  945. </p>
  946. <p>
  947. {tct(
  948. 'You can fix this by configuring your build tool to emit a [sourceMappingUrl] comment.',
  949. {sourceMappingUrl: <MonoBlock>sourceMappingURL</MonoBlock>}
  950. )}
  951. </p>
  952. </CheckListInstruction>
  953. <SourceMapStepNotRequiredNote />
  954. </CheckListItem>
  955. );
  956. }
  957. if (sourceResolutionResults.sourceMapReleaseNameFetchingResult === 'wrong-dist') {
  958. return (
  959. <CheckListItem status="alert" title={errorMessage}>
  960. <CheckListInstruction type="muted">
  961. <h6>{t('Dist Value Not Matching')}</h6>
  962. <p>
  963. {t(
  964. 'You uploaded a source map artifact with the right name, however the dist value on this event does not match the dist value on the artifact.'
  965. )}
  966. </p>
  967. {sourceResolutionResults.dist !== null ? (
  968. <p>
  969. {tct(
  970. 'Upload your build artifacts to Sentry using the dist [dist] or adjust the dist value in your SDK options.',
  971. {dist: <MonoBlock>{sourceResolutionResults.dist}</MonoBlock>}
  972. )}
  973. </p>
  974. ) : (
  975. <p>
  976. {tct(
  977. 'Upload your build artifacts to Sentry using a matching [dist] value or adjust the [dist] value in your SDK options.',
  978. {dist: <MonoBlock>dist</MonoBlock>}
  979. )}
  980. </p>
  981. )}
  982. <DistCodeSnippet />
  983. {/* TODO: Link to Uploaded Artifacts */}
  984. </CheckListInstruction>
  985. </CheckListItem>
  986. );
  987. }
  988. return (
  989. <CheckListItem status="alert" title={errorMessage}>
  990. <CheckListInstruction type="muted">
  991. <h6>{t('Not Found')}</h6>
  992. <p>
  993. {tct(
  994. 'The source file had a source map reference [sourceMapReference], but there was no source map artifact uploaded at that location. Make sure to generate and upload a source map named [matchingSourceMap] to symbolicate this stack frame!',
  995. {
  996. sourceMapReference: (
  997. <MonoBlock>{sourceResolutionResults.releaseSourceMapReference}</MonoBlock>
  998. ),
  999. matchingSourceMap: (
  1000. <MonoBlock>{sourceResolutionResults.matchingSourceMapName}</MonoBlock>
  1001. ),
  1002. }
  1003. )}
  1004. </p>
  1005. <p>
  1006. {/* wrong-dist is not deterministically returned in the case of wrong dist values because of database restrictions */}
  1007. {sourceResolutionResults.dist !== null
  1008. ? tct(
  1009. 'This event has a dist value [dist]. Please check that you uploaded your sourcemaps with dist [dist].',
  1010. {
  1011. dist: <MonoBlock>{sourceResolutionResults.dist}</MonoBlock>,
  1012. }
  1013. )
  1014. : t(
  1015. "This event doesn't have a dist value. Please check that you uploaded your sourcemaps without dist value."
  1016. )}
  1017. </p>
  1018. {/* TODO: Link to Uploaded Artifacts */}
  1019. </CheckListInstruction>
  1020. <SourceMapStepNotRequiredNote />
  1021. </CheckListItem>
  1022. );
  1023. }
  1024. function ScrapingSourceFileAvailableChecklistItem({
  1025. sourceResolutionResults,
  1026. }: {
  1027. sourceResolutionResults: FrameSourceMapDebuggerData;
  1028. }) {
  1029. if (sourceResolutionResults.sourceFileScrapingStatus === null) {
  1030. return (
  1031. <CheckListItem status="alert" title={t('Source file was not fetched')}>
  1032. <CheckListInstruction type="muted">
  1033. <h6>{t('Missing Information')}</h6>
  1034. <p>
  1035. {t(
  1036. 'This stack frame is missing information to attempt fetching the source file.'
  1037. )}
  1038. </p>
  1039. </CheckListInstruction>
  1040. <SourceMapStepNotRequiredNote />
  1041. </CheckListItem>
  1042. );
  1043. }
  1044. if (sourceResolutionResults.sourceFileScrapingStatus.status === 'success') {
  1045. return (
  1046. <CheckListItem status="checked" title={t('Source file available to Sentry')} />
  1047. );
  1048. }
  1049. if (sourceResolutionResults.sourceFileScrapingStatus.status === 'not_attempted') {
  1050. return (
  1051. <CheckListItem status="alert" title={t('Source file was not fetched')}>
  1052. <CheckListInstruction type="muted">
  1053. <h6>{t('Fetching Was Not Attempted')}</h6>
  1054. <p>
  1055. {t(
  1056. 'The source file was already located via Debug IDs or Releases. Sentry will only attempt to fetch the source file from your servers as a fallback mechanism.'
  1057. )}
  1058. </p>
  1059. </CheckListInstruction>
  1060. <SourceMapStepNotRequiredNote />
  1061. </CheckListItem>
  1062. );
  1063. }
  1064. const failureReasonTexts =
  1065. SOURCE_FILE_SCRAPING_REASON_MAP[
  1066. sourceResolutionResults.sourceFileScrapingStatus.reason
  1067. ] ?? SOURCE_FILE_SCRAPING_REASON_MAP.other;
  1068. return (
  1069. <CheckListItem status="alert" title={t('Source file is not available to Sentry')}>
  1070. <CheckListInstruction type="muted">
  1071. <h6>
  1072. {t('Error While Fetching The Source File:')} {failureReasonTexts.shortName}
  1073. </h6>
  1074. <p>{failureReasonTexts.explanation}</p>
  1075. <p>
  1076. {t('Sentry looked for the source file at this location:')}{' '}
  1077. <MonoBlock>{sourceResolutionResults.sourceFileScrapingStatus.url}</MonoBlock>
  1078. </p>
  1079. {sourceResolutionResults.sourceFileScrapingStatus.details && (
  1080. <Fragment>
  1081. <p>{t('Sentry symbolification error message:')}</p>
  1082. <ScrapingSymbolificationErrorMessage>
  1083. "{sourceResolutionResults.sourceFileScrapingStatus.details}"
  1084. </ScrapingSymbolificationErrorMessage>
  1085. </Fragment>
  1086. )}
  1087. </CheckListInstruction>
  1088. </CheckListItem>
  1089. );
  1090. }
  1091. function ScrapingSourceMapAvailableChecklistItem({
  1092. sourceResolutionResults,
  1093. }: {
  1094. sourceResolutionResults: FrameSourceMapDebuggerData;
  1095. }) {
  1096. if (sourceResolutionResults.sourceMapScrapingStatus?.status === 'success') {
  1097. return <CheckListItem status="checked" title={t('Source map available to Sentry')} />;
  1098. }
  1099. if (sourceResolutionResults.sourceFileScrapingStatus?.status !== 'success') {
  1100. return <CheckListItem status="none" title={t('Source map available to Sentry')} />;
  1101. }
  1102. if (sourceResolutionResults.sourceMapScrapingStatus === null) {
  1103. return (
  1104. <CheckListItem status="none" title={t('Source map was not fetched')}>
  1105. <CheckListInstruction type="muted">
  1106. <h6>{t('No Source Map Reference')}</h6>
  1107. <p>{t('There was no source map reference on the source file.')}</p>
  1108. </CheckListInstruction>
  1109. <SourceMapStepNotRequiredNote />
  1110. </CheckListItem>
  1111. );
  1112. }
  1113. if (sourceResolutionResults.sourceMapScrapingStatus.status === 'not_attempted') {
  1114. return (
  1115. <CheckListItem status="alert" title={t('Source map was not fetched')}>
  1116. <CheckListInstruction type="muted">
  1117. <h6>{t('Fetching Was Not Attempted')}</h6>
  1118. <p>
  1119. {t(
  1120. 'The source map was already located via Debug IDs or Releases. Sentry will only attempt to fetch the source map from your servers as a fallback mechanism.'
  1121. )}
  1122. </p>
  1123. </CheckListInstruction>
  1124. <SourceMapStepNotRequiredNote />
  1125. </CheckListItem>
  1126. );
  1127. }
  1128. const failureReasonTexts =
  1129. SOURCE_MAP_SCRAPING_REASON_MAP[
  1130. sourceResolutionResults.sourceMapScrapingStatus.reason
  1131. ] ?? SOURCE_MAP_SCRAPING_REASON_MAP.other;
  1132. return (
  1133. <CheckListItem status="alert" title={t('Source map is not available to Sentry')}>
  1134. <CheckListInstruction type="muted">
  1135. <h6>
  1136. {t('Error While Fetching The Source Map:')} {failureReasonTexts.shortName}
  1137. </h6>
  1138. <p>{failureReasonTexts.explanation}</p>
  1139. <p>
  1140. {t('Sentry looked for the source map at this location:')}{' '}
  1141. <MonoBlock>{sourceResolutionResults.sourceMapScrapingStatus.url}</MonoBlock>
  1142. </p>
  1143. {sourceResolutionResults.sourceMapScrapingStatus.details && (
  1144. <Fragment>
  1145. <p>{t('Sentry symbolification error message:')}</p>
  1146. <ScrapingSymbolificationErrorMessage>
  1147. "{sourceResolutionResults.sourceMapScrapingStatus.details}"
  1148. </ScrapingSymbolificationErrorMessage>
  1149. </Fragment>
  1150. )}
  1151. </CheckListInstruction>
  1152. </CheckListItem>
  1153. );
  1154. }
  1155. function ExternalLinkWithIcon({href, children}: PropsWithChildren<{href: string}>) {
  1156. return (
  1157. <ExternalLink href={href}>
  1158. {children} <IconOpen size="xs" />
  1159. </ExternalLink>
  1160. );
  1161. }
  1162. function DistCodeSnippet() {
  1163. return (
  1164. <InstructionCodeSnippet language="javascript" dark hideCopyButton>
  1165. {`Sentry.init({
  1166. dist: 'your-dist-name'
  1167. })`}
  1168. </InstructionCodeSnippet>
  1169. );
  1170. }
  1171. function VerifyAgainNote() {
  1172. return (
  1173. <CompletionNoteContainer>
  1174. <IconRefresh size="lg" color="gray300" />
  1175. <p>
  1176. {t(
  1177. 'Once you changed your configuration, redeploy your app and capture a new event to verify your changes!'
  1178. )}
  1179. </p>
  1180. </CompletionNoteContainer>
  1181. );
  1182. }
  1183. function ChecklistDoneNote() {
  1184. return (
  1185. <CompletionNoteContainer>
  1186. <IconCheckmark size="md" color="green200" />
  1187. <p>
  1188. {t(
  1189. 'You completed all of the steps above. Capture a new event to verify your setup!'
  1190. )}
  1191. </p>
  1192. </CompletionNoteContainer>
  1193. );
  1194. }
  1195. function SourceMapStepNotRequiredNote() {
  1196. return (
  1197. <CheckListInstruction type="muted" showIcon>
  1198. {
  1199. "You can safely ignore this step if you don't do any transformations to your code before deploying."
  1200. }
  1201. </CheckListInstruction>
  1202. );
  1203. }
  1204. const StyledTabPanels = styled(TabPanels)`
  1205. padding-top: ${space(2)};
  1206. `;
  1207. const CheckList = styled('ul')`
  1208. margin: 0;
  1209. padding: 0 ${space(1.5)};
  1210. list-style-type: none;
  1211. `;
  1212. interface CheckListItemProps {
  1213. status: 'none' | 'checked' | 'alert' | 'question';
  1214. title: ReactNode;
  1215. }
  1216. const ListItemContainer = styled('li')`
  1217. display: flex;
  1218. &:last-of-type {
  1219. .source-map-debugger-modal-checklist-line {
  1220. display: none;
  1221. }
  1222. }
  1223. `;
  1224. const CheckMarkContainer = styled('div')`
  1225. display: flex;
  1226. flex-direction: column;
  1227. align-items: center;
  1228. `;
  1229. const Line = styled('div')`
  1230. margin: ${space(0.5)} 0;
  1231. flex-grow: 1;
  1232. width: ${space(0.25)};
  1233. background-color: ${p => p.theme.gray200};
  1234. border-radius: ${space(0.25)};
  1235. `;
  1236. const ListItemContentContainer = styled('div')`
  1237. flex-grow: 1;
  1238. margin-left: ${space(1.5)};
  1239. padding-bottom: ${space(2)};
  1240. `;
  1241. const CompletionNoteContainer = styled('div')`
  1242. display: flex;
  1243. align-items: center;
  1244. gap: ${space(2)};
  1245. margin-top: ${space(1)};
  1246. margin-bottom: ${space(0.5)};
  1247. padding: 0 ${space(2)} 0 0;
  1248. `;
  1249. const ListItemTitleWrapper = styled('div')`
  1250. min-height: 20px;
  1251. display: flex;
  1252. align-items: center;
  1253. `;
  1254. const ListItemTitle = styled('p')<{status: 'none' | 'checked' | 'alert' | 'question'}>`
  1255. font-weight: 600;
  1256. color: ${p =>
  1257. ({
  1258. none: p.theme.gray300,
  1259. question: p.theme.gray300,
  1260. checked: p.theme.green300,
  1261. alert: p.theme.yellow400,
  1262. })[p.status]};
  1263. `;
  1264. const CheckListInstruction = styled(Alert)`
  1265. width: 100%;
  1266. margin-top: ${space(1)};
  1267. margin-bottom: 0;
  1268. overflow-x: auto;
  1269. h6 {
  1270. font-size: 1rem;
  1271. margin-bottom: ${space(1)};
  1272. }
  1273. p {
  1274. margin-bottom: ${space(1.5)};
  1275. }
  1276. `;
  1277. const MonoBlock = styled('code')`
  1278. padding: ${space(0.25)} ${space(0.5)};
  1279. color: ${p => p.theme.gray400};
  1280. background: ${p => p.theme.gray100};
  1281. border: 1px solid ${p => p.theme.gray200};
  1282. font-family: ${p => p.theme.text.familyMono};
  1283. font-size: ${p => p.theme.fontSizeExtraSmall};
  1284. font-weight: 400;
  1285. white-space: nowrap;
  1286. `;
  1287. const StyledProgressRing = styled(ProgressRing)`
  1288. margin-right: ${space(0.5)};
  1289. `;
  1290. const WizardInstructionParagraph = styled('p')`
  1291. margin-bottom: ${space(1)};
  1292. `;
  1293. const InstructionCodeSnippet = styled(CodeSnippet)`
  1294. margin: ${space(1)} 0 ${space(2)};
  1295. `;
  1296. const InstructionList = styled('ul')`
  1297. margin-bottom: ${space(1.5)};
  1298. li {
  1299. margin-bottom: ${space(0.5)};
  1300. }
  1301. `;
  1302. const ModalHeadingContainer = styled('div')`
  1303. display: flex;
  1304. align-items: center;
  1305. `;
  1306. const ScrapingSymbolificationErrorMessage = styled('p')`
  1307. color: ${p => p.theme.gray300};
  1308. border-left: 2px solid ${p => p.theme.gray200};
  1309. padding-left: ${space(1)};
  1310. margin-top: -${space(1)};
  1311. `;