@sentry/wizard
Version:
Sentry wizard helping you to configure your project
285 lines • 15 kB
JavaScript
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
;