UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

273 lines (245 loc) 10.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getManualViteConfigContent = exports.getManualReactRouterConfigContent = exports.getManualServerInstrumentContent = exports.getManualRootContent = exports.getManualHandleRequestContent = exports.getManualServerEntryContent = exports.getManualClientEntryContent = exports.getSentryInstrumentationServerContent = exports.EXAMPLE_PAGE_TEMPLATE_JSX = exports.EXAMPLE_PAGE_TEMPLATE_TSX = exports.ERROR_BOUNDARY_TEMPLATE = void 0; const clack_1 = require("../utils/clack"); function generateErrorBoundaryTemplate(isTypeScript, forManualInstructions = false) { const typeAnnotations = isTypeScript ? { stack: ': string | undefined', props: ': Route.ErrorBoundaryProps' } : { stack: '', props: '' }; const commentLine = forManualInstructions ? '// you only want to capture non 404-errors that reach the boundary\n ' : '// Only capture non-404 errors (all errors here are already non-RouteErrorResponse)\n '; return `function ErrorBoundary({ error }${typeAnnotations.props}) { let message = "Oops!"; let details = "An unexpected error occurred."; let stack${typeAnnotations.stack}; if (isRouteErrorResponse(error)) { message = error.status === 404 ? "404" : "Error"; details = error.status === 404 ? "The requested page could not be found." : error.statusText || details; } else if (error && error instanceof Error) { ${commentLine}Sentry.captureException(error); details = error.message; stack = error.stack; } return ( <main> <h1>{message}</h1> <p>{details}</p> {stack && ( <pre> <code>{stack}</code> </pre> )} </main> ); }`; } exports.ERROR_BOUNDARY_TEMPLATE = generateErrorBoundaryTemplate(false); exports.EXAMPLE_PAGE_TEMPLATE_TSX = `import type { Route } from "./+types/sentry-example-page"; export async function loader() { throw new Error("some error thrown in a loader"); } export default function SentryExamplePage() { return <div>Loading this page will throw an error</div>; }`; exports.EXAMPLE_PAGE_TEMPLATE_JSX = `export async function loader() { throw new Error("some error thrown in a loader"); } export default function SentryExamplePage() { return <div>Loading this page will throw an error</div>; }`; function generateServerInstrumentationCode(dsn, enableTracing, enableProfiling, enableLogs) { return `import * as Sentry from '@sentry/react-router';${enableProfiling ? `\nimport { nodeProfilingIntegration } from '@sentry/profiling-node';` : ''} Sentry.init({ dsn: "${dsn}", // Adds request headers and IP for users, for more info visit: // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii sendDefaultPii: true,${enableLogs ? '\n\n // Enable logs to be sent to Sentry\n enableLogs: true,' : ''}${enableProfiling ? '\n\n integrations: [nodeProfilingIntegration()],' : ''} tracesSampleRate: ${enableTracing ? '1.0' : '0'}, ${enableTracing ? '// Capture 100% of the transactions' : ''}${enableProfiling ? '\n profilesSampleRate: 1.0, // profile every transaction' : ''}${enableTracing ? ` // Set up performance monitoring beforeSend(event) { // Filter out 404s from error reporting if (event.exception) { const error = event.exception.values?.[0]; if (error?.type === "NotFoundException" || error?.value?.includes("404")) { return null; } } return event; },` : ''} });`; } const getSentryInstrumentationServerContent = (dsn, enableTracing, enableProfiling = false, enableLogs = false) => { return generateServerInstrumentationCode(dsn, enableTracing, enableProfiling, enableLogs); }; exports.getSentryInstrumentationServerContent = getSentryInstrumentationServerContent; const getManualClientEntryContent = (dsn, enableTracing, enableReplay, enableLogs) => { const integrations = []; if (enableTracing) { integrations.push('Sentry.reactRouterTracingIntegration()'); } if (enableReplay) { integrations.push('Sentry.replayIntegration()'); } const integrationsStr = integrations.length > 0 ? integrations.join(',\n ') : ''; return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => unchanged(`${plus("import * as Sentry from '@sentry/react-router';")} import { startTransition, StrictMode } from 'react'; import { hydrateRoot } from 'react-dom/client'; import { HydratedRouter } from 'react-router/dom'; ${plus(`Sentry.init({ dsn: "${dsn}", // Adds request headers and IP for users, for more info visit: // https://docs.sentry.io/platforms/javascript/guides/react-router/configuration/options/#sendDefaultPii sendDefaultPii: true, integrations: [ ${integrationsStr} ], ${enableLogs ? '// Enable logs to be sent to Sentry\n enableLogs: true,\n\n ' : ''}tracesSampleRate: ${enableTracing ? '1.0' : '0'},${enableTracing ? ' // Capture 100% of the transactions' : ''}${enableTracing ? '\n\n // Set `tracePropagationTargets` to declare which URL(s) should have trace propagation enabled\n // In production, replace "yourserver.io" with your actual backend domain\n tracePropagationTargets: [/^\\//, /^https:\\/\\/yourserver\\.io\\/api/],' : ''}${enableReplay ? '\n\n // Capture Replay for 10% of all sessions,\n // plus 100% of sessions with an error\n replaysSessionSampleRate: 0.1,\n replaysOnErrorSampleRate: 1.0,' : ''} });`)} startTransition(() => { hydrateRoot( document, <StrictMode> <HydratedRouter /> </StrictMode> ); });`)); }; exports.getManualClientEntryContent = getManualClientEntryContent; const getManualServerEntryContent = () => { return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => unchanged(`${plus("import * as Sentry from '@sentry/react-router';")} import { createReadableStreamFromReadable } from '@react-router/node'; import { renderToPipeableStream } from 'react-dom/server'; import { ServerRouter } from 'react-router'; ${plus(`const handleRequest = Sentry.createSentryHandleRequest({ ServerRouter, renderToPipeableStream, createReadableStreamFromReadable, });`)} export default handleRequest; ${plus(`export const handleError = Sentry.createSentryHandleError({ logErrors: false });`)} // ... rest of your server entry`)); }; exports.getManualServerEntryContent = getManualServerEntryContent; const getManualHandleRequestContent = () => { return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => unchanged(`${plus("import * as Sentry from '@sentry/react-router';")} import { createReadableStreamFromReadable } from '@react-router/node'; import { renderToPipeableStream } from 'react-dom/server'; import { ServerRouter } from 'react-router'; ${plus(`// Replace your existing handleRequest function with this Sentry-wrapped version: const handleRequest = Sentry.createSentryHandleRequest({ ServerRouter, renderToPipeableStream, createReadableStreamFromReadable, });`)} ${plus(`// If you have a custom handleRequest implementation, wrap it like this: // export default Sentry.wrapSentryHandleRequest(yourCustomHandleRequest);`)} export default handleRequest;`)); }; exports.getManualHandleRequestContent = getManualHandleRequestContent; const getManualRootContent = (isTs) => { const typeAnnotations = isTs ? { stack: ': string | undefined', props: ': Route.ErrorBoundaryProps' } : { stack: '', props: '' }; return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => unchanged(`${plus("import * as Sentry from '@sentry/react-router';")} export function ErrorBoundary({ error }${typeAnnotations.props}) { let message = "Oops!"; let details = "An unexpected error occurred."; let stack${typeAnnotations.stack}; if (isRouteErrorResponse(error)) { message = error.status === 404 ? "404" : "Error"; details = error.status === 404 ? "The requested page could not be found." : error.statusText || details; } else if (error && error instanceof Error) { // you only want to capture non 404-errors that reach the boundary ${plus('Sentry.captureException(error);')} details = error.message; stack = error.stack; } return ( <main> <h1>{message}</h1> <p>{details}</p> {stack && ( <pre> <code>{stack}</code> </pre> )} </main> ); } // ...`)); }; exports.getManualRootContent = getManualRootContent; const getManualServerInstrumentContent = (dsn, enableTracing, enableProfiling, enableLogs = false) => { return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => plus(generateServerInstrumentationCode(dsn, enableTracing, enableProfiling, enableLogs))); }; exports.getManualServerInstrumentContent = getManualServerInstrumentContent; const getManualReactRouterConfigContent = (isTS = true) => { return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => isTS ? unchanged(`${plus('import type { Config } from "@react-router/dev/config";')} ${plus("import { sentryOnBuildEnd } from '@sentry/react-router';")} export default { ${plus('ssr: true,')} ${plus(`buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => { await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest }); },`)} } satisfies Config; // If you already have a buildEnd hook, modify it to call sentryOnBuildEnd: // buildEnd: async (args) => { // await yourExistingLogic(args); // await sentryOnBuildEnd(args); // }`) : unchanged(`${plus("import { sentryOnBuildEnd } from '@sentry/react-router';")} export default { ${plus('ssr: true,')} ${plus(`buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => { await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest }); },`)} }; // If you already have a buildEnd hook, modify it to call sentryOnBuildEnd: // buildEnd: async (args) => { // await yourExistingLogic(args); // await sentryOnBuildEnd(args); // }`)); }; exports.getManualReactRouterConfigContent = getManualReactRouterConfigContent; const getManualViteConfigContent = (orgSlug, projectSlug) => { return (0, clack_1.makeCodeSnippet)(true, (unchanged, plus) => unchanged(`${plus("import { sentryReactRouter } from '@sentry/react-router';")} import { defineConfig } from 'vite'; export default defineConfig(config => { return { plugins: [ // ... your existing plugins ${plus(`sentryReactRouter({ org: "${orgSlug}", project: "${projectSlug}", authToken: process.env.SENTRY_AUTH_TOKEN, }, config),`)} ], }; });`)); }; exports.getManualViteConfigContent = getManualViteConfigContent; //# sourceMappingURL=templates.js.map