Skip to content
Merged
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

## Unreleased

### Breaking Changes

- feat(js)!: Install SDK version `^11` in the angular, cloudflare, nextjs, nuxt, react-router, remix and sveltekit wizards ([#1344](https://github.com/getsentry/sentry-wizard/pull/1344))

The wizards now install v11 of the JavaScript SDKs. If a version below v11 is already installed, the wizard prints a link to the [v10 to v11 migration guide](https://docs.sentry.io/platforms/javascript/migration/v10-to-v11/).
- ref(nuxt)!: Remove `--import` guidance and outdated prompts for v11 ([#1347](https://github.com/getsentry/sentry-wizard/pull/1347))

### Other Changes

- feat(nextjs): Import `withSentryConfig` from `@sentry/nextjs/config` and warn on Next.js < 14 ([#1346](https://github.com/getsentry/sentry-wizard/pull/1346))
- fix(nextjs): Use `connection()` instead of `force-dynamic` in the example API route on Next.js 15+ so it builds with `cacheComponents` ([#1346](https://github.com/getsentry/sentry-wizard/pull/1346))
- feat(react-router): Import `sentryReactRouter` and `sentryOnBuildEnd` from `@sentry/react-router/vite` on SDK v11, use session profiling options, warn on React Router < 7.15 ([#1352](https://github.com/getsentry/sentry-wizard/pull/1352))
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/pnpm-workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('pnpm workspace', () => {
path.resolve(projectDir, 'vite.config.ts'),
);
expect(viteConfig.toString()).toMatchInlineSnapshot(`
"import { sentrySvelteKit } from "@sentry/sveltekit";
"import { sentrySvelteKit } from "@sentry/sveltekit/vite";
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/sveltekit-tracing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('Sveltekit with instrumentation and tracing', () => {
path.resolve(projectDir, 'vite.config.ts'),
);
expect(viteConfig.toString()).toMatchInlineSnapshot(`
"import { sentrySvelteKit } from "@sentry/sveltekit";
"import { sentrySvelteKit } from "@sentry/sveltekit/vite";
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

Expand Down
5 changes: 4 additions & 1 deletion src/angular/angular-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getOrAskForProjectData,
getPackageDotJson,
installPackage,
printSdkV11MigrationGuideIfOutdated,
printWelcome,
runPrettierIfInstalled,
abort,
Expand Down Expand Up @@ -164,8 +165,10 @@ Apologies for the inconvenience!`,

Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

printSdkV11MigrationGuideIfOutdated('@sentry/angular', packageJson);

await installPackage({
packageName: '@sentry/angular@^10',
packageName: '@sentry/angular@^11',
packageNameDisplayLabel: '@sentry/angular',
alreadyInstalled: sdkAlreadyInstalled,
});
Expand Down
5 changes: 4 additions & 1 deletion src/cloudflare/cloudflare-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
getOrAskForProjectData,
getPackageDotJson,
installPackage,
printSdkV11MigrationGuideIfOutdated,
printWelcome,
runPrettierIfInstalled,
} from '../utils/clack';
Expand Down Expand Up @@ -77,8 +78,10 @@ async function runCloudflareWizardWithTelemetry(

const { selectedProject } = projectData;

printSdkV11MigrationGuideIfOutdated('@sentry/cloudflare', packageJson);

await installPackage({
packageName: '@sentry/cloudflare@^10',
packageName: '@sentry/cloudflare@^11',
packageNameDisplayLabel: '@sentry/cloudflare',
alreadyInstalled: hasPackageInstalled('@sentry/cloudflare', packageJson),
forceInstall,
Expand Down
5 changes: 4 additions & 1 deletion src/nextjs/nextjs-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
getPackageDotJson,
getPackageManager,
installPackage,
printSdkV11MigrationGuideIfOutdated,
isUsingTypeScript,
printWelcome,
runFormatters,
Expand Down Expand Up @@ -131,9 +132,11 @@ The wizard will continue, but you may need to upgrade Next.js for the SDK to wor
);
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

printSdkV11MigrationGuideIfOutdated('@sentry/nextjs', packageJson);

const { packageManager: packageManagerFromInstallStep } =
await installPackage({
packageName: '@sentry/nextjs@^10.73.0',
packageName: '@sentry/nextjs@^11',
packageNameDisplayLabel: '@sentry/nextjs',
alreadyInstalled: !!packageJson?.dependencies?.['@sentry/nextjs'],
forceInstall,
Expand Down
4 changes: 2 additions & 2 deletions src/nextjs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export const MIN_SDK_VERSION_WITH_CONFIG_SUBPATH = '10.73.0';

/**
* Returns the module `withSentryConfig` should be imported from for the
* installed SDK version. The wizard installs `>=10.73.0`, but a user can keep
* an older SDK by declining the update prompt. Those releases only expose
* installed SDK version. The wizard installs `^11`, but a user can keep an
* older SDK by declining the update prompt. Those releases only expose
* `withSentryConfig` on the root export, so fall back to it there. Unknown or
* unparseable versions get the subpath, which is the only option on v11.
*/
Expand Down
5 changes: 4 additions & 1 deletion src/nuxt/nuxt-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getPackageDotJson,
getPackageManager,
installPackage,
printSdkV11MigrationGuideIfOutdated,
printWelcome,
runPrettierIfInstalled,
} from '../utils/clack';
Expand Down Expand Up @@ -117,8 +118,10 @@ export async function runNuxtWizardWithTelemetry(
const sdkAlreadyInstalled = hasPackageInstalled('@sentry/nuxt', packageJson);
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

printSdkV11MigrationGuideIfOutdated('@sentry/nuxt', packageJson);

await installPackage({
packageName: '@sentry/nuxt@^10',
packageName: '@sentry/nuxt@^11',
alreadyInstalled: sdkAlreadyInstalled,
packageManager,
forceInstall,
Expand Down
3 changes: 3 additions & 0 deletions src/react-router/react-router-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isUsingTypeScript,
printWelcome,
installPackage,
printSdkV11MigrationGuideIfOutdated,
addDotEnvSentryBuildPluginFile,
showCopyPasteInstructions,
makeCodeSnippet,
Expand Down Expand Up @@ -113,6 +114,8 @@ async function runReactRouterWizardWithTelemetry(

const { selectedProject, authToken, selfHosted, sentryUrl } = projectData;

printSdkV11MigrationGuideIfOutdated('@sentry/react-router', packageJson);

await installPackage({
packageName: `@sentry/react-router@${SENTRY_REACT_ROUTER_SDK_RANGE}`,
packageNameDisplayLabel: '@sentry/react-router',
Expand Down
2 changes: 1 addition & 1 deletion src/react-router/sdk-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getMajorVersion } from '../utils/semver';
* wizard installs. Also decides the default import path of the Vite plugin
* helpers when the installed version is unknown.
*/
export const SENTRY_REACT_ROUTER_SDK_RANGE = '^10';
export const SENTRY_REACT_ROUTER_SDK_RANGE = '^11';

/** Root export. Exposes `sentryReactRouter` and `sentryOnBuildEnd` up to SDK v10 only. */
export const SENTRY_REACT_ROUTER_ROOT_IMPORT_PATH = '@sentry/react-router';
Expand Down
5 changes: 4 additions & 1 deletion src/remix/remix-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
getOrAskForProjectData,
getPackageDotJson,
installPackage,
printSdkV11MigrationGuideIfOutdated,
isUsingTypeScript,
printWelcome,
rcCliSetupConfig,
Expand Down Expand Up @@ -89,8 +90,10 @@ async function runRemixWizardWithTelemetry(

const { selectedProject, authToken, sentryUrl, selfHosted } = projectData;

printSdkV11MigrationGuideIfOutdated('@sentry/remix', packageJson);

await installPackage({
packageName: '@sentry/remix@^10',
packageName: '@sentry/remix@^11',
packageNameDisplayLabel: '@sentry/remix',
alreadyInstalled: hasPackageInstalled('@sentry/remix', packageJson),
forceInstall,
Expand Down
3 changes: 3 additions & 0 deletions src/sveltekit/sveltekit-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getPackageDotJson,
getPackageManager,
installPackage,
printSdkV11MigrationGuideIfOutdated,
printWelcome,
runPrettierIfInstalled,
} from '../utils/clack';
Expand Down Expand Up @@ -171,6 +172,8 @@ without SvelteKit's builtin observability.`,
);
Sentry.setTag('sdk-already-installed', sdkAlreadyInstalled);

printSdkV11MigrationGuideIfOutdated('@sentry/sveltekit', packageJson);

await installPackage({
packageName: `@sentry/sveltekit@${SENTRY_SVELTEKIT_SDK_RANGE}`,
packageNameDisplayLabel: '@sentry/sveltekit',
Expand Down
2 changes: 1 addition & 1 deletion src/sveltekit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getMajorVersion } from '../utils/semver';
* Version range of `@sentry/sveltekit` the wizard installs. Also decides the
* default import path of the Vite plugin when the installed version is unknown.
*/
export const SENTRY_SVELTEKIT_SDK_RANGE = '^10';
export const SENTRY_SVELTEKIT_SDK_RANGE = '^11';

/** Root export. Exposes `sentrySvelteKit` up to SDK v10 only. */
export const SENTRY_SVELTEKIT_ROOT_IMPORT_PATH = '@sentry/sveltekit';
Expand Down
58 changes: 57 additions & 1 deletion src/utils/clack/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ import * as clack from '@clack/prompts';
import * as Sentry from '@sentry/node';
import axios from 'axios';
import chalk from 'chalk';
import { gte, minVersion, validRange } from 'semver';
import opn from 'opn';
import { traceStep } from '../../telemetry';
import { WIZARD_VERSION } from '../../version';
import { debug } from '../debug';
import { type PackageDotJson, hasPackageInstalled } from '../package-json';
import {
type PackageDotJson,
getPackageVersion,
hasPackageInstalled,
} from '../package-json';
import {
type PackageManager,
_detectPackageManger,
Expand Down Expand Up @@ -360,6 +365,57 @@ export async function confirmContinueIfPackageVersionNotSupported({
});
}

const JS_SDK_V11_MIGRATION_GUIDE_URL =
'https://docs.sentry.io/platforms/javascript/migration/v10-to-v11/';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: This one goes only to the browser migration guide, should we rather point to the exact migration guide given that we have platform specific sections?


/** Docs guide slugs of the SDKs that have a platform-specific migration guide. */
const JS_SDK_V11_MIGRATION_GUIDE_SLUGS: Record<string, string> = {
'@sentry/angular': 'angular',
'@sentry/cloudflare': 'cloudflare',
'@sentry/nextjs': 'nextjs',
'@sentry/nuxt': 'nuxt',
'@sentry/react-router': 'react-router',
'@sentry/remix': 'remix',
'@sentry/sveltekit': 'sveltekit',
};

function getSdkV11MigrationGuideUrl(packageName: string): string {
const slug = JS_SDK_V11_MIGRATION_GUIDE_SLUGS[packageName];

return slug
? `https://docs.sentry.io/platforms/javascript/guides/${slug}/migration/v10-to-v11/`
: JS_SDK_V11_MIGRATION_GUIDE_URL;
}

/**
* Logs a link to the v10 to v11 migration guide if @param packageName is
* already installed at a version below v11. The wizard updates the SDK to v11,
* which contains breaking changes the user must apply to their code.
*/
export function printSdkV11MigrationGuideIfOutdated(
packageName: string,
packageJson: PackageDotJson,
): void {
const installedRange = getPackageVersion(packageName, packageJson);
const installedVersion =
installedRange && validRange(installedRange)
? minVersion(installedRange)
: null;

if (!installedVersion || gte(installedVersion, '11.0.0')) {
return;
Comment thread
andreiborza marked this conversation as resolved.
Resolved
}

clack.log.warn(
`The wizard updates ${chalk.cyan(
packageName,
)} to version 11, which has ${chalk.bold('breaking changes')}.
Follow the migration guide to update your code: ${chalk.cyan(
getSdkV11MigrationGuideUrl(packageName),
)}`,
);
}

type InstallPackageOptions = {
/** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
packageName: string;
Expand Down
73 changes: 73 additions & 0 deletions test/utils/clack/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createNewConfigFile,
getPackageManager,
installPackage,
printSdkV11MigrationGuideIfOutdated,
} from '../../../src/utils/clack/';

import * as fs from 'node:fs';
Expand Down Expand Up @@ -415,6 +416,78 @@ describe('installPackage', () => {
});
});

describe('printSdkV11MigrationGuideIfOutdated', () => {
afterEach(() => {
vi.clearAllMocks();
});

it.each(['^10.0.0', '10.5.2', '~9.1.0', '>=8.0.0 <11.0.0'])(
'warns and links the migration guide for the installed range %s',
(version) => {
printSdkV11MigrationGuideIfOutdated('@sentry/nextjs', {
dependencies: { '@sentry/nextjs': version },
});

expect(clackMock.log.warn).toHaveBeenCalledTimes(1);
expect(clackMock.log.warn).toHaveBeenCalledWith(
expect.stringContaining(
'https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v10-to-v11/',
),
);
},
);

it.each([
['@sentry/angular', 'angular'],
['@sentry/cloudflare', 'cloudflare'],
['@sentry/nextjs', 'nextjs'],
['@sentry/nuxt', 'nuxt'],
['@sentry/react-router', 'react-router'],
['@sentry/remix', 'remix'],
['@sentry/sveltekit', 'sveltekit'],
])('links the %s migration guide', (packageName, guide) => {
printSdkV11MigrationGuideIfOutdated(packageName, {
dependencies: { [packageName]: '^10.2.0' },
});

expect(clackMock.log.warn).toHaveBeenCalledWith(
expect.stringContaining(
`https://docs.sentry.io/platforms/javascript/guides/${guide}/migration/v10-to-v11/`,
),
);
});

it('falls back to the generic migration guide for other SDKs', () => {
printSdkV11MigrationGuideIfOutdated('@sentry/browser', {
dependencies: { '@sentry/browser': '^10.0.0' },
});

expect(clackMock.log.warn).toHaveBeenCalledWith(
expect.stringContaining(
'https://docs.sentry.io/platforms/javascript/migration/v10-to-v11/',
),
);
});

it.each([
['the SDK is not installed', {}],
[
'the SDK is already on v11',
{ dependencies: { '@sentry/nextjs': '^11.0.0' } },
],
[
'the installed range is not a valid semver range',
{
dependencies: { '@sentry/nextjs': 'workspace:*' },
},
],
])('stays silent when %s', (_case, packageJson) => {
printSdkV11MigrationGuideIfOutdated('@sentry/nextjs', packageJson);

expect(clackMock.log.warn).not.toHaveBeenCalled();
});
});

describe('askForWizardLogin', () => {
beforeEach(() => {
vi.clearAllMocks();
Expand Down
Loading