UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

202 lines (198 loc) 8.95 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.addSentryExpoConfigRequire = exports.patchMetroInMemory = exports.addSentryToExpoMetroConfig = void 0; const fs = __importStar(require("node:fs")); // @ts-expect-error - clack is ESM and TS complains about that. It works though const clack = __importStar(require("@clack/prompts")); const chalk_1 = __importDefault(require("chalk")); const Sentry = __importStar(require("@sentry/node")); const ast_utils_1 = require("../utils/ast-utils"); const clack_1 = require("../utils/clack"); const metro_1 = require("./metro"); const recast = __importStar(require("recast")); const b = recast.types.builders; async function addSentryToExpoMetroConfig() { let metroConfigPath = (0, metro_1.findMetroConfigPath)(); if (!metroConfigPath) { // No existing metro config found, create metro.config.js (Expo default) metroConfigPath = 'metro.config.js'; } if (!fs.existsSync(metroConfigPath)) { const success = await createSentryExpoMetroConfig(metroConfigPath); if (!success) { Sentry.setTag('expo-metro-config', 'create-new-error'); return await showInstructions(metroConfigPath); } Sentry.setTag('expo-metro-config', 'created-new'); return undefined; } Sentry.setTag('expo-metro-config', 'exists'); clack.log.info(`Updating existing ${metroConfigPath}.`); const mod = await (0, metro_1.parseMetroConfig)(metroConfigPath); if (!mod) { return await showInstructions(metroConfigPath); } let didPatch = false; try { didPatch = patchMetroInMemory(mod, metroConfigPath); } catch (e) { Sentry.captureException('Unable to patch expo metro config'); } if (!didPatch) { Sentry.setTag('expo-metro-config', 'patch-error'); clack.log.error(`Could not patch ${chalk_1.default.cyan(metroConfigPath)} with Sentry configuration.`); return await showInstructions(metroConfigPath); } const saved = await (0, metro_1.writeMetroConfig)(mod, metroConfigPath); if (saved) { Sentry.setTag('expo-metro-config', 'patch-saved'); clack.log.success(chalk_1.default.green(`${chalk_1.default.cyan(metroConfigPath)} changes saved.`)); } else { Sentry.setTag('expo-metro-config', 'patch-save-error'); clack.log.error(`Could not save changes to ${chalk_1.default.cyan(metroConfigPath)}, please follow the manual steps.`); return await showInstructions(metroConfigPath); } } exports.addSentryToExpoMetroConfig = addSentryToExpoMetroConfig; function patchMetroInMemory(mod, metroConfigPath) { const ast = mod.$ast; if ((0, ast_utils_1.hasSentryContent)(ast)) { clack.log.warn(`The ${chalk_1.default.cyan(metroConfigPath)} file already has Sentry configuration.`); return false; } let didReplaceDefaultConfigCall = false; recast.visit(ast, { visitVariableDeclaration(path) { const { node } = path; if ( // path is require("expo/metro-config") // and only getDefaultConfig is being destructured // then remove the entire declaration node.declarations.length > 0 && node.declarations[0].type === 'VariableDeclarator' && node.declarations[0].init && node.declarations[0].init.type === 'CallExpression' && node.declarations[0].init.callee && node.declarations[0].init.callee.type === 'Identifier' && node.declarations[0].init.callee.name === 'require' && node.declarations[0].init.arguments[0].type === 'StringLiteral' && node.declarations[0].init.arguments[0].value === 'expo/metro-config' && node.declarations[0].id.type === 'ObjectPattern' && node.declarations[0].id.properties.length === 1 && node.declarations[0].id.properties[0].type === 'ObjectProperty' && node.declarations[0].id.properties[0].key.type === 'Identifier' && node.declarations[0].id.properties[0].key.name === 'getDefaultConfig') { path.prune(); return false; } this.traverse(path); }, visitCallExpression(path) { const { node } = path; if ( // path is getDefaultConfig // then rename it to getSentryExpoConfig node.callee.type === 'Identifier' && node.callee.name === 'getDefaultConfig') { node.callee.name = 'getSentryExpoConfig'; didReplaceDefaultConfigCall = true; return false; } this.traverse(path); }, }); if (!didReplaceDefaultConfigCall) { clack.log.warn(`Could not find \`getDefaultConfig\` in ${chalk_1.default.cyan(metroConfigPath)}.`); return false; } addSentryExpoConfigRequire(ast, metroConfigPath); return true; } exports.patchMetroInMemory = patchMetroInMemory; function addSentryExpoConfigRequire(program, metroConfigPath) { try { const lastRequireIndex = (0, ast_utils_1.getLastRequireIndex)(program); const sentryExpoConfigRequire = createSentryExpoConfigRequire(); // Add the require statement after the last require or at the beginning program.body.splice(lastRequireIndex + 1, 0, sentryExpoConfigRequire); } catch (error) { clack.log.error(`Could not add Sentry Expo config require statement to ${chalk_1.default.cyan(metroConfigPath)}.`); Sentry.captureException(`Could not add Sentry Expo config require statement to ${metroConfigPath}.`); } } exports.addSentryExpoConfigRequire = addSentryExpoConfigRequire; /** * Creates const { getSentryExpoConfig } = require("@sentry/react-native/metro"); */ function createSentryExpoConfigRequire() { return b.variableDeclaration('const', [ b.variableDeclarator(b.objectPattern([ b.objectProperty.from({ key: b.identifier('getSentryExpoConfig'), value: b.identifier('getSentryExpoConfig'), shorthand: true, }), ]), b.callExpression(b.identifier('require'), [ b.literal('@sentry/react-native/metro'), ])), ]); } async function createSentryExpoMetroConfig(metroConfigPath) { const snippet = `const { getSentryExpoConfig } = require("@sentry/react-native/metro"); const config = getSentryExpoConfig(__dirname); module.exports = config; `; try { await fs.promises.writeFile(metroConfigPath, snippet); } catch (e) { clack.log.error(`Could not create ${chalk_1.default.cyan(metroConfigPath)} with Sentry configuration.`); Sentry.captureException(`Could not create ${metroConfigPath} with Sentry configuration.`); return false; } clack.log.success(`Created ${chalk_1.default.cyan(metroConfigPath)} with Sentry configuration.`); return true; } function showInstructions(metroConfigPath) { return (0, clack_1.showCopyPasteInstructions)({ filename: metroConfigPath, codeSnippet: getMetroWithSentryExpoConfigSnippet(true), }); } function getMetroWithSentryExpoConfigSnippet(colors) { return (0, clack_1.makeCodeSnippet)(colors, (unchanged, plus, minus) => unchanged(`${minus(`// const { getDefaultConfig } = require("expo/metro-config");`)} ${plus(`const { getSentryExpoConfig } = require("@sentry/react-native/metro");`)} ${minus(`// const config = getDefaultConfig(__dirname);`)} ${plus(`const config = getSentryExpoConfig(__dirname);`)} module.exports = config;`)); } //# sourceMappingURL=expo-metro.js.map