@sentry/wizard
Version:
Sentry wizard helping you to configure your project
231 lines (204 loc) • 7.26 kB
text/typescript
// @ts-expect-error - clack is ESM and TS complains about that. It works though
import clack from '@clack/prompts';
import chalk from 'chalk';
import {
addSentryCliConfig,
askShouldCreateExamplePage,
confirmContinueIfNoOrDirtyGitRepo,
ensurePackageIsInstalled,
getOrAskForProjectData,
getPackageDotJson,
installPackage,
isUsingTypeScript,
printWelcome,
rcCliSetupConfig,
} from '../utils/clack-utils';
import { hasPackageInstalled } from '../utils/package-json';
import { WizardOptions } from '../utils/types';
import {
initializeSentryOnEntryClient,
instrumentSentryOnEntryServer,
updateBuildScript,
instrumentRootRoute,
isRemixV2,
loadRemixConfig,
runRemixReveal,
insertServerInstrumentationFile,
createServerInstrumentationFile,
updateStartScript,
} from './sdk-setup';
import { debug } from '../utils/debug';
import { traceStep, withTelemetry } from '../telemetry';
import { isHydrogenApp } from './utils';
import { DEFAULT_URL } from '../../lib/Constants';
import { findFile } from '../utils/ast-utils';
import { configureVitePlugin } from '../sourcemaps/tools/vite';
import { createExamplePage } from './sdk-example';
export async function runRemixWizard(options: WizardOptions): Promise<void> {
return withTelemetry(
{
enabled: options.telemetryEnabled,
integration: 'remix',
},
() => runRemixWizardWithTelemetry(options),
);
}
async function runRemixWizardWithTelemetry(
options: WizardOptions,
): Promise<void> {
printWelcome({
wizardName: 'Sentry Remix Wizard',
promoCode: options.promoCode,
telemetryEnabled: options.telemetryEnabled,
});
await confirmContinueIfNoOrDirtyGitRepo();
const remixConfig = await loadRemixConfig();
const packageJson = await getPackageDotJson();
// We expect `@remix-run/dev` to be installed for every Remix project
await ensurePackageIsInstalled(packageJson, '@remix-run/dev', 'Remix');
const { selectedProject, authToken, sentryUrl, selfHosted } =
await getOrAskForProjectData(options, 'javascript-remix');
await installPackage({
packageName: '@sentry/remix',
alreadyInstalled: hasPackageInstalled('@sentry/remix', packageJson),
});
const dsn = selectedProject.keys[0].dsn.public;
const isTS = isUsingTypeScript();
const isV2 = isRemixV2(remixConfig, packageJson);
const viteConfig = findFile('vite.config');
await addSentryCliConfig({ authToken }, rcCliSetupConfig);
if (viteConfig) {
await traceStep(
'Update vite configuration for sourcemap uploads',
async () => {
try {
await configureVitePlugin({
orgSlug: selectedProject.organization.slug,
projectSlug: selectedProject.slug,
url: sentryUrl,
selfHosted,
authToken,
});
} catch (e) {
clack.log
.warn(`Could not update vite configuration to generate and upload sourcemaps.
Please update your vite configuration manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/sourcemaps/`);
debug(e);
}
},
);
} else {
await traceStep('Update build script for sourcemap uploads', async () => {
try {
await updateBuildScript({
org: selectedProject.organization.slug,
project: selectedProject.slug,
url: sentryUrl === DEFAULT_URL ? undefined : sentryUrl,
isHydrogen: isHydrogenApp(packageJson),
});
} catch (e) {
clack.log
.warn(`Could not update build script to generate and upload sourcemaps.
Please update your build script manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/sourcemaps/`);
debug(e);
}
});
}
await traceStep('Instrument root route', async () => {
try {
await instrumentRootRoute(isV2, isTS);
} catch (e) {
clack.log.warn(`Could not instrument root route.
Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/`);
debug(e);
}
});
traceStep('Reveal missing entry files', () => {
try {
runRemixReveal(isTS);
} catch (e) {
clack.log.warn(`Could not run 'npx remix reveal'.
Please create your entry files manually`);
debug(e);
}
});
await traceStep('Initialize Sentry on client entry', async () => {
try {
await initializeSentryOnEntryClient(dsn, isTS);
} catch (e) {
clack.log.warn(`Could not initialize Sentry on client entry.
Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/`);
debug(e);
}
});
let instrumentationFile = '';
await traceStep('Create server instrumentation file', async () => {
try {
instrumentationFile = await createServerInstrumentationFile(dsn);
} catch (e) {
clack.log.warn(
'Could not create a server instrumentation file. Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/',
);
debug(e);
}
});
let serverFileInstrumented = false;
await traceStep(
'Create server instrumentation file and import it',
async () => {
try {
serverFileInstrumented = await insertServerInstrumentationFile(dsn);
} catch (e) {
clack.log.warn(
'Could not create a server instrumentation file. Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/',
);
debug(e);
}
},
);
if (!serverFileInstrumented && instrumentationFile) {
await traceStep(
'Update `start` script to import instrumentation file.',
async () => {
try {
await updateStartScript(instrumentationFile);
} catch (e) {
clack.log
.warn(`Could not automatically add Sentry initialization to server entry.
Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/`);
debug(e);
}
},
);
}
await traceStep('Instrument server `handleError`', async () => {
try {
await instrumentSentryOnEntryServer(isV2, isTS);
} catch (e) {
clack.log.warn(`Could not initialize Sentry on server entry.
Please do it manually using instructions from https://docs.sentry.io/platforms/javascript/guides/remix/manual-setup/`);
debug(e);
}
});
const shouldCreateExamplePage = await askShouldCreateExamplePage();
if (shouldCreateExamplePage) {
await traceStep('Create example page', async () => {
await createExamplePage({
isTS,
selfHosted,
orgSlug: selectedProject.organization.slug,
projectId: selectedProject.id,
url: sentryUrl,
});
});
}
clack.outro(`
${chalk.green(
'Sentry has been successfully configured for your Remix project.',
)}
${chalk.cyan('You can now deploy your project to see Sentry in action.')}
${chalk.cyan(
`To learn more about how to use Sentry with Remix, visit our documentation:
https://docs.sentry.io/platforms/javascript/guides/remix/`,
)}`);
}