UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

178 lines (176 loc) 7.36 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.instrumentReactRouterConfig = exports.hasReactRouterSentryContent = exports.addSentryBuildEndToReactRouterConfig = void 0; const recast = __importStar(require("recast")); const path = __importStar(require("path")); const fs = __importStar(require("fs")); // @ts-expect-error - magicast is ESM and TS complains about that. It works though const magicast_1 = require("magicast"); // @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 ast_utils_1 = require("../../utils/ast-utils"); /** * Extracts the ObjectExpression from various export patterns. * Supports: direct object, `satisfies Config`, and `as Config` patterns. */ function extractConfigObject(declaration) { if (declaration.type === 'ObjectExpression') { return declaration; } if (declaration.type === 'TSSatisfiesExpression' || declaration.type === 'TSAsExpression') { const expr = declaration .expression; return expr.type === 'ObjectExpression' ? expr : undefined; } return undefined; } /** * Creates an identifier property for object literals. */ function createIdentifierProperty(name) { const b = recast.types.builders; return b.property('init', b.identifier(name), b.identifier(name)); } function addSentryBuildEndToReactRouterConfig(program) { const b = recast.types.builders; let ssrWasChanged = false; const defaultExport = program.body.find((node) => node.type === 'ExportDefaultDeclaration'); if (!defaultExport) { return { success: false, ssrWasChanged: false }; } const configObj = extractConfigObject(defaultExport.declaration); if (!configObj) { return { success: false, ssrWasChanged: false }; } const buildEndProp = (0, ast_utils_1.findProperty)(configObj, 'buildEnd'); if (buildEndProp) { throw new Error('A buildEnd hook already exists in your React Router config.'); } const ssrProp = (0, ast_utils_1.findProperty)(configObj, 'ssr'); if (!ssrProp) { const ssrProperty = b.objectProperty(b.identifier('ssr'), b.booleanLiteral(true)); ssrProperty.comments = [ { type: 'CommentLine', value: ' SSR is required for Sentry sourcemap uploads to work correctly', }, ]; configObj.properties.unshift(ssrProperty); ssrWasChanged = true; } else if (ssrProp.value.type === 'BooleanLiteral' || ssrProp.value.type === 'Literal') { const wasExplicitlyFalse = ssrProp.value.value === false; if (wasExplicitlyFalse) { ssrWasChanged = true; } ssrProp.value = b.booleanLiteral(true); if (wasExplicitlyFalse) { ssrProp.comments = [ { type: 'CommentLine', value: ' Changed to true - SSR is required for Sentry sourcemap uploads', }, ]; } } const paramNames = ['viteConfig', 'reactRouterConfig', 'buildManifest']; const buildEndFunction = b.arrowFunctionExpression([b.objectPattern(paramNames.map(createIdentifierProperty))], b.blockStatement([ b.expressionStatement(b.awaitExpression(b.callExpression(b.identifier('sentryOnBuildEnd'), [ b.objectExpression(paramNames.map(createIdentifierProperty)), ]))), ])); buildEndFunction.async = true; configObj.properties.push(b.objectProperty(b.identifier('buildEnd'), buildEndFunction)); return { success: true, ssrWasChanged }; } exports.addSentryBuildEndToReactRouterConfig = addSentryBuildEndToReactRouterConfig; function hasReactRouterSentryContent(program) { let hasSentry = false; recast.visit(program, { visitIdentifier(path) { if (path.node.name === 'sentryOnBuildEnd') { hasSentry = true; return false; // stop traversal } this.traverse(path); }, }); return hasSentry; } exports.hasReactRouterSentryContent = hasReactRouterSentryContent; async function instrumentReactRouterConfig(isTS) { const configFilename = `react-router.config.${isTS ? 'ts' : 'js'}`; const configPath = path.join(process.cwd(), configFilename); if (!fs.existsSync(configPath)) { const defaultConfig = isTS ? `import type { Config } from "@react-router/dev/config"; import { sentryOnBuildEnd } from "@sentry/react-router"; export default { ssr: true, buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => { await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest }); }, } satisfies Config; ` : `import { sentryOnBuildEnd } from "@sentry/react-router"; export default { ssr: true, buildEnd: async ({ viteConfig, reactRouterConfig, buildManifest }) => { await sentryOnBuildEnd({ viteConfig, reactRouterConfig, buildManifest }); }, }; `; await fs.promises.writeFile(configPath, defaultConfig); return { ssrWasChanged: false }; } const configContent = await fs.promises.readFile(configPath, 'utf-8'); const filename = chalk_1.default.cyan(configFilename); const mod = (0, magicast_1.parseModule)(configContent); if (hasReactRouterSentryContent(mod.$ast)) { prompts_1.default.log.info(`${filename} already contains sentryOnBuildEnd.`); return { ssrWasChanged: false }; } mod.imports.$add({ from: '@sentry/react-router', imported: 'sentryOnBuildEnd', local: 'sentryOnBuildEnd', }); const { success, ssrWasChanged } = addSentryBuildEndToReactRouterConfig(mod.$ast); if (!success) { throw new Error('Failed to modify React Router config structure'); } const code = (0, magicast_1.generateCode)(mod.$ast).code; await fs.promises.writeFile(configPath, code); return { ssrWasChanged }; } exports.instrumentReactRouterConfig = instrumentReactRouterConfig; //# sourceMappingURL=react-router-config.js.map