UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

285 lines 15 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.confirmReadImportDocs = exports.addNuxtOverrides = exports.createConfigFiles = exports.addSDKModule = exports.askDeploymentPlatform = exports.getNuxtConfig = void 0; const node_fs_1 = __importDefault(require("node:fs")); const node_path_1 = __importDefault(require("node:path")); // @ts-expect-error - clack is ESM and TS complains about that. It works though const clack = __importStar(require("@clack/prompts")); const Sentry = __importStar(require("@sentry/node")); const chalk_1 = __importDefault(require("chalk")); // @ts-expect-error - magicast is ESM and TS complains about that. It works though const magicast_1 = require("magicast"); // @ts-expect-error - magicast is ESM and TS complains about that. It works though const helpers_1 = require("magicast/helpers"); const opn_1 = __importDefault(require("opn")); const semver_1 = require("semver"); const telemetry_1 = require("../telemetry"); const clack_1 = require("../utils/clack"); const package_json_1 = require("../utils/package-json"); const package_manager_1 = require("../utils/package-manager"); const templates_1 = require("./templates"); const types_1 = require("./types"); const possibleNuxtConfig = [ 'nuxt.config.js', 'nuxt.config.mjs', 'nuxt.config.cjs', 'nuxt.config.ts', 'nuxt.config.mts', 'nuxt.config.cts', ]; async function getNuxtConfig() { let configFile = possibleNuxtConfig.find((fileName) => node_fs_1.default.existsSync(node_path_1.default.join(process.cwd(), fileName))); if (!configFile) { clack.log.info('No Nuxt config file found, creating a new one.'); Sentry.setTag('nuxt-config-strategy', 'create'); // nuxt recommends its config to be .ts by default configFile = 'nuxt.config.ts'; await node_fs_1.default.promises.writeFile(node_path_1.default.join(process.cwd(), configFile), (0, templates_1.getDefaultNuxtConfig)(), { encoding: 'utf-8', flag: 'w' }); clack.log.success(`Created ${chalk_1.default.cyan('nuxt.config.ts')}.`); } return node_path_1.default.join(process.cwd(), configFile); } exports.getNuxtConfig = getNuxtConfig; async function askDeploymentPlatform() { return await (0, clack_1.abortIfCancelled)(clack.select({ message: 'Please select your deployment platform.', options: types_1.deploymentPlatforms.map((platform) => ({ value: platform, label: `${platform.charAt(0).toUpperCase()}${platform.slice(1)}`, })), })); } exports.askDeploymentPlatform = askDeploymentPlatform; async function addSDKModule(config, options, deploymentPlatform) { const failureTagKey = 'modify-nuxt-config-error'; const shouldTopLevelImport = deploymentPlatform === 'vercel' || deploymentPlatform === 'netlify'; if (shouldTopLevelImport) { clack.log.warn(`Sentry needs to be initialized before the application starts. ${chalk_1.default.cyan(`${deploymentPlatform .charAt(0) .toUpperCase()}${deploymentPlatform.slice(1)}`)} does not support this yet.\n\nWe will inject the Sentry server-side config at the top of your Nuxt server entry file instead.\n\nThis comes with some restrictions, for more info see:\n\n${chalk_1.default.underline('https://docs.sentry.io/platforms/javascript/guides/nuxt/install/top-level-import/')} `); } let module; try { module = await (0, magicast_1.loadFile)(config); } catch (e) { if (e instanceof Error) { if (e instanceof SyntaxError || e.message.includes('Unexpected token')) { Sentry.setTag(failureTagKey, 'loadFile-failed-syntax-error'); } else if (e.message.includes('ENOENT') || e.message.includes('no such file')) { Sentry.setTag(failureTagKey, 'loadFile-failed-file-not-found'); } } else { Sentry.setTag(failureTagKey, 'loadFile-failed'); } clack.log.error(`Error while loading Nuxt config file: ${e instanceof Error ? e.message : 'Unknown'}`); showFallbackInstructions(config, options, shouldTopLevelImport); throw e; } try { (0, helpers_1.addNuxtModule)(module, '@sentry/nuxt/module', 'sentry', { sourceMapsUploadOptions: { org: options.org, project: options.project, ...(options.selfHosted && { url: options.url }), }, ...(shouldTopLevelImport && { autoInjectServerSentry: 'top-level-import', }), }); } catch (e) { Sentry.setTag(failureTagKey, 'adding-sentry-options-failed'); clack.log.error(`Error while modifying 'sentry' in Nuxt config: ${e instanceof Error ? e.message : 'Unknown'}`); showFallbackInstructions(config, options, shouldTopLevelImport); throw e; } try { (0, helpers_1.addNuxtModule)(module, '@sentry/nuxt/module', 'sourcemap', { client: 'hidden', }); } catch (e) { Sentry.setTag(failureTagKey, 'adding-sourcemap-options-failed'); clack.log.error(`Error while modifying 'sourcemap' in Nuxt config: ${e instanceof Error ? e.message : 'Unknown'}`); showFallbackInstructions(config, options, shouldTopLevelImport); throw e; } let code; try { ({ code } = (0, magicast_1.generateCode)(module)); } catch (e) { Sentry.setTag(failureTagKey, 'generateCode-failed'); clack.log.error(`Error while generating module code: ${e instanceof Error ? e.message : 'Unknown'}`); showFallbackInstructions(config, options, shouldTopLevelImport); throw e; } try { await node_fs_1.default.promises.writeFile(config, code, { encoding: 'utf-8', flag: 'w' }); clack.log.success(`Added Sentry Nuxt Module to ${chalk_1.default.cyan(node_path_1.default.basename(config))}.`); } catch (e) { Sentry.setTag(failureTagKey, 'writeFile-failed'); clack.log.error(`Error while writing Nuxt config: ${e instanceof Error ? e.message : 'Unknown'}`); showFallbackInstructions(config, options, shouldTopLevelImport); throw e; } } exports.addSDKModule = addSDKModule; function showFallbackInstructions(config, options, shouldTopLevelImport) { clack.log.warn(`Please add the following settings to ${chalk_1.default.cyan(node_path_1.default.basename(config))}:`); // eslint-disable-next-line no-console console.log(`\n\n${(0, templates_1.getNuxtModuleFallbackTemplate)(options, shouldTopLevelImport)}\n\n`); } async function createConfigFiles(dsn) { const selectedFeatures = 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', }, ]); const typeScriptDetected = (0, clack_1.isUsingTypeScript)(); const configVariants = ['server', 'client']; for (const configVariant of configVariants) { await (0, telemetry_1.traceStep)(`create-sentry-${configVariant}-config`, async () => { const jsConfig = `sentry.${configVariant}.config.js`; const tsConfig = `sentry.${configVariant}.config.ts`; const jsConfigExists = node_fs_1.default.existsSync(node_path_1.default.join(process.cwd(), jsConfig)); const tsConfigExists = node_fs_1.default.existsSync(node_path_1.default.join(process.cwd(), tsConfig)); let shouldWriteFile = true; if (jsConfigExists || tsConfigExists) { const existingConfigs = []; if (jsConfigExists) { existingConfigs.push(jsConfig); } if (tsConfigExists) { existingConfigs.push(tsConfig); } const overwriteExistingConfigs = await (0, clack_1.abortIfCancelled)(clack.confirm({ message: `Found existing Sentry ${configVariant} config (${existingConfigs.join(', ')}). Overwrite ${existingConfigs.length > 1 ? 'them' : 'it'}?`, })); Sentry.setTag(`overwrite-${configVariant}-config`, overwriteExistingConfigs); shouldWriteFile = overwriteExistingConfigs; if (overwriteExistingConfigs) { if (jsConfigExists) { node_fs_1.default.unlinkSync(node_path_1.default.join(process.cwd(), jsConfig)); clack.log.warn(`Removed existing ${chalk_1.default.cyan(jsConfig)}.`); } if (tsConfigExists) { node_fs_1.default.unlinkSync(node_path_1.default.join(process.cwd(), tsConfig)); clack.log.warn(`Removed existing ${chalk_1.default.cyan(tsConfig)}.`); } } } if (shouldWriteFile) { await node_fs_1.default.promises.writeFile(node_path_1.default.join(process.cwd(), typeScriptDetected ? tsConfig : jsConfig), (0, templates_1.getSentryConfigContents)(dsn, configVariant, selectedFeatures), { encoding: 'utf8', flag: 'w' }); clack.log.success(`Created new ${chalk_1.default.cyan(typeScriptDetected ? tsConfig : jsConfig)}.`); Sentry.setTag(`created-${configVariant}-config`, true); } else { clack.log.info(`Okay, here are the changes your ${chalk_1.default.cyan(typeScriptDetected ? tsConfig : jsConfig)} should contain:`); // eslint-disable-next-line no-console console.log('\n\n ' + (0, templates_1.getConfigBody)(dsn, configVariant, selectedFeatures) + '\n\n'); } }); } } exports.createConfigFiles = createConfigFiles; async function addNuxtOverrides(packageJson, packageManager, nuxtMinVer, forceInstall) { const isPNPM = package_manager_1.PNPM.detect(); const overrides = [ { pkgName: '@vercel/nft', pkgVersion: '^0.27.4', }, ...(nuxtMinVer && (0, semver_1.lt)(nuxtMinVer, '3.14.0') ? [{ pkgName: 'ofetch', pkgVersion: '^1.4.0' }] : []), ]; clack.log.warn(`To ensure Sentry can properly instrument your code it needs to add version overrides for some Nuxt dependencies${isPNPM ? ` and install ${chalk_1.default.cyan('import-in-the-middle')}.` : '.'}\n\nFor more info see: ${chalk_1.default.underline('https://github.com/getsentry/sentry-javascript/issues/14514')}${isPNPM ? `\n\nand ${chalk_1.default.underline('https://docs.sentry.io/platforms/javascript/guides/nuxt/troubleshooting/#pnpm-dev-cannot-find-package-import-in-the-middle')}` : ''}`); for (const { pkgName, pkgVersion } of overrides) { const shouldAddOverride = await (0, clack_1.askShouldAddPackageOverride)(pkgName, pkgVersion); if (shouldAddOverride) { await packageManager.addOverride(pkgName, pkgVersion); } } if (package_manager_1.PNPM.detect()) { // For pnpm, we want to install iitm // See: https://docs.sentry.io/platforms/javascript/guides/nuxt/troubleshooting/#pnpm-dev-cannot-find-package-import-in-the-middle const iitmAlreadyInstalled = (0, package_json_1.hasPackageInstalled)('import-in-the-middle', packageJson); Sentry.setTag('iitm-already-installed', iitmAlreadyInstalled); const shouldInstallIitm = await (0, clack_1.askShouldInstallPackage)('import-in-the-middle'); if (shouldInstallIitm) { await (0, clack_1.installPackage)({ packageName: 'import-in-the-middle', alreadyInstalled: iitmAlreadyInstalled, packageManager, forceInstall, }); } } } exports.addNuxtOverrides = addNuxtOverrides; async function confirmReadImportDocs(deploymentPlatform) { const canImportSentryServerConfigFile = deploymentPlatform !== 'vercel' && deploymentPlatform !== 'netlify'; if (!canImportSentryServerConfigFile) { // Nothing to do, users have been set up with automatic top-level-import instead return; } const docsUrl = 'https://docs.sentry.io/platforms/javascript/guides/nuxt/install/cli-import/#initializing-sentry-with---import'; clack.log.info(`After building your Nuxt app, you need to ${chalk_1.default.bold('--import')} the Sentry server config file when running your app.\n\nFor more info, see:\n\n${chalk_1.default.underline(docsUrl)}`); const shouldOpenDocs = await (0, clack_1.abortIfCancelled)(clack.confirm({ message: 'Do you want to open the docs?' })); Sentry.setTag('init-with-import-docs-opened', shouldOpenDocs); if (shouldOpenDocs) { // opn throws in environments that don't have a browser (e.g. remote shells) so we just noop here const noop = () => { }; // eslint-disable-line @typescript-eslint/no-empty-function (0, opn_1.default)(docsUrl, { wait: false }).then((cp) => cp.on('error', noop), noop); } } exports.confirmReadImportDocs = confirmReadImportDocs; //# sourceMappingURL=sdk-setup.js.map