UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

254 lines 13.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runReactRouterWizard = void 0; // @ts-expect-error - clack is ESM and TS complains about that. It works though const prompts_1 = __importDefault(require("@clack/prompts")); const chalk_1 = __importDefault(require("chalk")); const telemetry_1 = require("../telemetry"); const clack_1 = require("../utils/clack"); const mcp_config_1 = require("../utils/clack/mcp-config"); const package_json_1 = require("../utils/package-json"); const debug_1 = require("../utils/debug"); const sdk_example_1 = require("./sdk-example"); const sdk_setup_1 = require("./sdk-setup"); const templates_1 = require("./templates"); async function runReactRouterWizard(options) { return (0, telemetry_1.withTelemetry)({ enabled: options.telemetryEnabled, integration: 'reactRouter', wizardOptions: options, }, () => runReactRouterWizardWithTelemetry(options)); } exports.runReactRouterWizard = runReactRouterWizard; async function runReactRouterWizardWithTelemetry(options) { (0, clack_1.printWelcome)({ wizardName: 'Sentry React Router Wizard', promoCode: options.promoCode, }); const packageJson = await (0, clack_1.getPackageDotJson)(); if (!packageJson) { prompts_1.default.log.error('Could not find a package.json file in the current directory'); return; } const typeScriptDetected = (0, clack_1.isUsingTypeScript)(); if (!(0, sdk_setup_1.isReactRouterV7)(packageJson)) { prompts_1.default.log.error('This wizard requires React Router v7. Please upgrade your React Router version to v7.0.0 or higher.\n\nFor upgrade instructions, visit: https://react-router.dev/upgrade/v7'); return; } await (0, clack_1.confirmContinueIfNoOrDirtyGitRepo)({ ignoreGitChanges: options.ignoreGitChanges, cwd: undefined, }); const sentryAlreadyInstalled = (0, package_json_1.hasPackageInstalled)('@sentry/react-router', packageJson); const projectData = await (0, clack_1.getOrAskForProjectData)(options, 'javascript-react-router'); if (projectData.spotlight) { prompts_1.default.log.warn('Spotlight mode is not yet supported for React Router.'); prompts_1.default.log.info('Spotlight is currently only available for Next.js.'); await (0, clack_1.abort)('Exiting wizard', 0); return; } const { selectedProject, authToken, selfHosted, sentryUrl } = projectData; await (0, clack_1.installPackage)({ packageName: '@sentry/react-router', alreadyInstalled: sentryAlreadyInstalled, }); const featureSelection = await (0, clack_1.featureSelectionPrompt)([ { id: 'performance', prompt: `Do you want to enable ${chalk_1.default.bold('Tracing')} to track the performance of your application?`, enabledHint: 'recommended', }, { id: 'replay', prompt: `Do you want to enable ${chalk_1.default.bold('Session Replay')} to get a video-like reproduction of errors during a user session?`, enabledHint: 'recommended, but increases bundle size', }, { id: 'logs', prompt: `Do you want to enable ${chalk_1.default.bold('Logs')} to send your application logs to Sentry?`, enabledHint: 'recommended', }, { id: 'profiling', prompt: `Do you want to enable ${chalk_1.default.bold('Profiling')} to track application performance in detail?`, enabledHint: 'recommended for production debugging', }, ]); if (featureSelection.profiling) { const profilingAlreadyInstalled = (0, package_json_1.hasPackageInstalled)('@sentry/profiling-node', packageJson); await (0, clack_1.installPackage)({ packageName: '@sentry/profiling-node', alreadyInstalled: profilingAlreadyInstalled, }); } const createExamplePageSelection = await (0, clack_1.askShouldCreateExamplePage)(); (0, telemetry_1.traceStep)('Reveal missing entry files', () => { try { (0, sdk_setup_1.runReactRouterReveal)(typeScriptDetected); prompts_1.default.log.success('Entry files are ready for instrumentation'); } catch (e) { prompts_1.default.log.warn(`Could not run 'npx react-router reveal'. Please create your entry files manually using React Router v7 commands.`); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Initialize Sentry on client entry', async () => { try { await (0, sdk_setup_1.initializeSentryOnEntryClient)(selectedProject.keys[0].dsn.public, featureSelection.performance, featureSelection.replay, featureSelection.logs, typeScriptDetected); } catch (e) { prompts_1.default.log.warn(`Could not initialize Sentry on client entry automatically.`); const clientEntryFilename = `entry.client.${typeScriptDetected ? 'tsx' : 'jsx'}`; const manualClientContent = (0, templates_1.getManualClientEntryContent)(selectedProject.keys[0].dsn.public, featureSelection.performance, featureSelection.replay, featureSelection.logs); await (0, clack_1.showCopyPasteInstructions)({ filename: clientEntryFilename, codeSnippet: manualClientContent, hint: 'This enables error tracking and performance monitoring for your React Router app', }); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Instrument root route', async () => { try { await (0, sdk_setup_1.instrumentRootRoute)(typeScriptDetected); } catch (e) { prompts_1.default.log.warn(`Could not instrument root route automatically.`); const rootFilename = `app/root.${typeScriptDetected ? 'tsx' : 'jsx'}`; const manualRootContent = (0, templates_1.getManualRootContent)(typeScriptDetected); await (0, clack_1.showCopyPasteInstructions)({ filename: rootFilename, codeSnippet: manualRootContent, hint: 'This adds error boundary integration to capture exceptions in your React Router app', }); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Instrument server entry', async () => { try { await (0, sdk_setup_1.instrumentSentryOnEntryServer)(typeScriptDetected); } catch (e) { prompts_1.default.log.warn(`Could not initialize Sentry on server entry automatically.`); const serverEntryFilename = `entry.server.${typeScriptDetected ? 'tsx' : 'jsx'}`; const manualServerContent = (0, templates_1.getManualServerEntryContent)(); await (0, clack_1.showCopyPasteInstructions)({ filename: serverEntryFilename, codeSnippet: manualServerContent, hint: 'This configures server-side request handling and error tracking', }); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Create server instrumentation file', async () => { try { (0, sdk_setup_1.createServerInstrumentationFile)(selectedProject.keys[0].dsn.public, { performance: featureSelection.performance, replay: featureSelection.replay, logs: featureSelection.logs, profiling: featureSelection.profiling, }); } catch (e) { prompts_1.default.log.warn('Could not create a server instrumentation file automatically.'); const manualServerInstrumentContent = (0, templates_1.getManualServerInstrumentContent)(selectedProject.keys[0].dsn.public, featureSelection.performance, featureSelection.profiling, featureSelection.logs); await (0, clack_1.showCopyPasteInstructions)({ filename: 'instrument.server.mjs', codeSnippet: manualServerInstrumentContent, hint: 'Create the file if it does not exist - this initializes Sentry before your application starts', }); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Update package.json scripts', async () => { try { await (0, sdk_setup_1.updatePackageJsonScripts)(); } catch (e) { prompts_1.default.log.warn('Could not update start script automatically.'); await (0, clack_1.showCopyPasteInstructions)({ filename: 'package.json', codeSnippet: (0, clack_1.makeCodeSnippet)(true, (unchanged, plus, minus) => { return unchanged(`{ scripts: { ${minus('"start": "react-router dev"')} ${plus('"start": "NODE_OPTIONS=\'--import ./instrument.server.mjs\' react-router-serve ./build/server/index.js"')} ${minus('"dev": "react-router dev"')} ${plus('"dev": "NODE_OPTIONS=\'--import ./instrument.server.mjs\' react-router dev"')} }, // ... rest of your package.json }`); }), }); (0, debug_1.debug)(e); } }); await (0, telemetry_1.traceStep)('Create build plugin env file', async () => { try { await (0, clack_1.addDotEnvSentryBuildPluginFile)(authToken); } catch (e) { prompts_1.default.log.warn('Could not create .env.sentry-build-plugin file. Please create it manually.'); (0, debug_1.debug)(e); } }); // Validate auth token before configuring sourcemap uploads if (!authToken) { prompts_1.default.log.warn(`${chalk_1.default.yellow('Warning:')} No auth token found. Sourcemap uploads will not work without ${chalk_1.default.cyan('SENTRY_AUTH_TOKEN')}.\n` + `Please set ${chalk_1.default.cyan('SENTRY_AUTH_TOKEN')} in your ${chalk_1.default.cyan('.env.sentry-build-plugin')} file or environment variables.`); } // Configure Vite plugin for sourcemap uploads await (0, telemetry_1.traceStep)('Configure Vite plugin for sourcemap uploads', async () => { try { await (0, sdk_setup_1.configureReactRouterVitePlugin)(selectedProject.organization.slug, selectedProject.slug); } catch (e) { prompts_1.default.log.warn(`Could not configure Vite plugin for sourcemap uploads automatically.`); await (0, clack_1.showCopyPasteInstructions)({ filename: `vite.config.${typeScriptDetected ? 'ts' : 'js'}`, codeSnippet: (0, templates_1.getManualViteConfigContent)(selectedProject.organization.slug, selectedProject.slug), hint: 'This enables automatic sourcemap uploads during build for better error tracking', }); (0, debug_1.debug)(e); } }); // Configure React Router config for build hook await (0, telemetry_1.traceStep)('Configure React Router build hook', async () => { try { await (0, sdk_setup_1.configureReactRouterConfig)(typeScriptDetected); } catch (e) { prompts_1.default.log.warn(`Could not configure React Router build hook automatically.`); await (0, clack_1.showCopyPasteInstructions)({ filename: `react-router.config.${typeScriptDetected ? 'ts' : 'js'}`, codeSnippet: (0, templates_1.getManualReactRouterConfigContent)(typeScriptDetected), hint: 'This enables automatic sourcemap uploads at the end of the build process', }); (0, debug_1.debug)(e); } }); // Create example page if requested if (createExamplePageSelection) { await (0, telemetry_1.traceStep)('Create example page', async () => { await (0, sdk_example_1.createExamplePage)({ selfHosted, orgSlug: selectedProject.organization.slug, projectId: selectedProject.id, url: sentryUrl, isTS: typeScriptDetected, projectDir: process.cwd(), }); }); } await (0, clack_1.runPrettierIfInstalled)({ cwd: undefined }); // Offer optional project-scoped MCP config for Sentry with org and project scope await (0, mcp_config_1.offerProjectScopedMcpConfig)(selectedProject.organization.slug, selectedProject.slug); prompts_1.default.outro(`${chalk_1.default.green('Successfully installed the Sentry React Router SDK!')}${createExamplePageSelection ? `\n\nYou can validate your setup by visiting ${chalk_1.default.cyan('"/sentry-example-page"')} in your application.` : ''}`); } //# sourceMappingURL=react-router-wizard.js.map