From 23dde17ce8ee4f9c97079b23988a4d79ef4a8d47 Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Fri, 18 Jul 2025 12:57:37 +0200 Subject: [PATCH 1/3] ref(getting-started): svelte content blocks --- .../gettingStartedDocs/javascript/svelte.tsx | 75 ++++++++++++++----- 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/static/app/gettingStartedDocs/javascript/svelte.tsx b/static/app/gettingStartedDocs/javascript/svelte.tsx index edd7574f578582..fd6050cfa65152 100644 --- a/static/app/gettingStartedDocs/javascript/svelte.tsx +++ b/static/app/gettingStartedDocs/javascript/svelte.tsx @@ -5,6 +5,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { + ContentBlock, Docs, DocsParams, OnboardingConfig, @@ -160,6 +161,27 @@ const getInstallConfig = () => [ }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/svelte', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/svelte', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/svelte', + }, + ], +}; + const onboarding: OnboardingConfig = { introduction: params => ( @@ -177,23 +199,32 @@ const onboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, - description: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', - {code: } - ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', + {code: } + ), + }, + installSnippetBlock, + ], }, ], configure: (params: Params) => [ { type: StepType.CONFIGURE, - description: tct( - "Initialize Sentry as early as possible in your application's lifecycle, usually your Svelte app's entry point ([code:main.ts/js]):", - {code: } - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: tct( + "Initialize Sentry as early as possible in your application's lifecycle, usually your Svelte app's entry point ([code:main.ts/js]):", + {code: } + ), + }, + { + type: 'code', + tabs: [ { label: 'Svelte v5', value: 'svelte v5', @@ -208,9 +239,11 @@ const onboarding: OnboardingConfig = { }, ], }, - ...(params.isProfilingSelected - ? [getProfilingDocumentHeaderConfigurationStep()] - : []), + { + type: 'conditional', + condition: params.isProfilingSelected, + content: getProfilingDocumentHeaderConfigurationStep().content!, + }, ], }, getUploadSourceMapsStep({ @@ -221,12 +254,16 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: t( + "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." + ), + }, + { + type: 'code', + tabs: [ { label: 'Svelte v5', value: 'svelte v5', From f292b64a6b11e266c53ab14edfa4d4239003ae00 Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:36:24 +0200 Subject: [PATCH 2/3] remove profiling --- static/app/gettingStartedDocs/javascript/svelte.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/static/app/gettingStartedDocs/javascript/svelte.tsx b/static/app/gettingStartedDocs/javascript/svelte.tsx index fd6050cfa65152..899be1e7e31cf7 100644 --- a/static/app/gettingStartedDocs/javascript/svelte.tsx +++ b/static/app/gettingStartedDocs/javascript/svelte.tsx @@ -19,10 +19,7 @@ import { getFeedbackConfigOptions, getFeedbackConfigureDescription, } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding'; -import { - getProfilingDocumentHeaderConfigurationStep, - MaybeBrowserProfilingBetaWarning, -} from 'sentry/components/onboarding/gettingStartedDoc/utils/profilingOnboarding'; +import {MaybeBrowserProfilingBetaWarning} from 'sentry/components/onboarding/gettingStartedDoc/utils/profilingOnboarding'; import { getReplayConfigOptions, getReplayConfigureDescription, @@ -239,11 +236,6 @@ const onboarding: OnboardingConfig = { }, ], }, - { - type: 'conditional', - condition: params.isProfilingSelected, - content: getProfilingDocumentHeaderConfigurationStep().content!, - }, ], }, getUploadSourceMapsStep({ From df0ac78a4b9e168048ecc370b88c90b873bc258e Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:45:21 +0200 Subject: [PATCH 3/3] other platforms --- .../gettingStartedDocs/javascript/angular.tsx | 165 ++++++++------ .../gettingStartedDocs/javascript/astro.tsx | 81 +++++-- .../gettingStartedDocs/javascript/ember.tsx | 64 +++++- .../gettingStartedDocs/javascript/gatsby.tsx | 75 +++++-- .../javascript/javascript.tsx | 80 +++++-- .../gettingStartedDocs/javascript/nuxt.tsx | 73 +++++-- .../javascript/react-router.spec.tsx | 168 ++++++++++++++ .../gettingStartedDocs/javascript/remix.tsx | 58 ++--- .../gettingStartedDocs/javascript/solid.tsx | 70 ++++-- .../javascript/solidstart.spec.tsx | 116 ++++++++++ .../javascript/tanstackstart-react.spec.tsx | 127 +++++++++++ .../javascript/tanstackstart-react.tsx | 206 +++++++++++------- .../app/gettingStartedDocs/javascript/vue.tsx | 83 +++++-- 13 files changed, 1072 insertions(+), 294 deletions(-) create mode 100644 static/app/gettingStartedDocs/javascript/react-router.spec.tsx create mode 100644 static/app/gettingStartedDocs/javascript/solidstart.spec.tsx create mode 100644 static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx diff --git a/static/app/gettingStartedDocs/javascript/angular.tsx b/static/app/gettingStartedDocs/javascript/angular.tsx index 1f6c028fc1a4ff..b8405c0420d77c 100644 --- a/static/app/gettingStartedDocs/javascript/angular.tsx +++ b/static/app/gettingStartedDocs/javascript/angular.tsx @@ -4,7 +4,7 @@ import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedba import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import { type BasePlatformOptions, - type Configuration, + type ContentBlock, type Docs, type DocsParams, type OnboardingConfig, @@ -214,54 +214,11 @@ export const appConfig: ApplicationConfig = { }; `; -const getVerifySnippetTemplate = () => ` - -`; - const getVerifySnippetComponent = () => ` public throwTestError(): void { throw new Error("Sentry Test Error"); }`; -function getVerifyConfiguration(): Configuration { - return { - description: t( - 'To verify that everything is working as expected, you can trigger a test error in your app. As an example we will add a button that throws an error when being clicked to your main app component.' - ), - configurations: [ - { - description: tct( - 'First add the button element to your [code:app.component.html]:', - {code: } - ), - code: [ - { - label: 'HTML', - value: 'html', - language: 'html', - filename: 'app.component.html', - code: getVerifySnippetTemplate(), - }, - ], - }, - { - description: tct('Then, in your [code:app.component.ts] add the event handler:', { - code: , - }), - code: [ - { - label: 'TypeScript', - value: 'typescript', - language: 'typescript', - filename: 'app.component.ts', - code: getVerifySnippetComponent(), - }, - ], - }, - ], - }; -} - const getInstallConfig = () => [ { language: 'bash', @@ -288,6 +245,27 @@ const getInstallConfig = () => [ }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/angular', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/angular', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm install @sentry/angular', + }, + ], +}; + const onboarding: OnboardingConfig = { introduction: () => tct( @@ -299,29 +277,44 @@ const onboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, - description: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:', - {code: } - ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:', + {code: } + ), + }, + installSnippetBlock, + ], }, ], configure: (params: Params) => [ { type: StepType.CONFIGURE, - configurations: [ + content: [ { - description: tct( + type: 'text', + text: tct( `Initialize the Sentry Angular SDK in your [code:main.ts] file as early as possible, before initializing Angular:`, { code: , } ), - language: 'javascript', - code: getSdkSetupSnippet(params), }, { - description: isModuleConfig(params) + type: 'code', + tabs: [ + { + label: 'JavaScript', + language: 'javascript', + code: getSdkSetupSnippet(params), + }, + ], + }, + { + type: 'text', + text: isModuleConfig(params) ? tct( "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.module.ts] file:", {code: } @@ -330,10 +323,18 @@ const onboarding: OnboardingConfig = { "Register the Sentry Angular SDK's ErrorHandler and Tracing providers in your [code:app.config.ts] file:", {code: } ), - language: 'javascript', - code: isModuleConfig(params) - ? getConfigureAppModuleSnippet() - : getConfigureAppConfigSnippet(), + }, + { + type: 'code', + tabs: [ + { + label: 'JavaScript', + language: 'javascript', + code: isModuleConfig(params) + ? getConfigureAppModuleSnippet() + : getConfigureAppConfigSnippet(), + }, + ], }, ], }, @@ -345,10 +346,52 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - configurations: [ - getVerifyConfiguration(), + content: [ + { + type: 'text', + text: t( + 'To verify that everything is working as expected, you can trigger a test error in your app. As an example we will add a button that throws an error when being clicked to your main app component.' + ), + }, + { + type: 'text', + text: tct('First add the button element to your [code:app.component.html]:', { + code: , + }), + }, + { + type: 'code', + tabs: [ + { + label: 'HTML', + value: 'html', + language: 'html', + filename: 'app.component.html', + code: '', + }, + ], + }, + { + type: 'text', + text: tct('Then, in your [code:app.component.ts] add the event handler:', { + code: , + }), + }, + { + type: 'code', + tabs: [ + { + label: 'TypeScript', + value: 'typescript', + language: 'typescript', + filename: 'app.component.ts', + code: getVerifySnippetComponent(), + }, + ], + }, { - description: t( + type: 'text', + text: t( "After clicking the button, you should see the error on Sentry's Issues page." ), }, diff --git a/static/app/gettingStartedDocs/javascript/astro.tsx b/static/app/gettingStartedDocs/javascript/astro.tsx index 19271002c2f6f9..7388d6b1a50672 100644 --- a/static/app/gettingStartedDocs/javascript/astro.tsx +++ b/static/app/gettingStartedDocs/javascript/astro.tsx @@ -5,6 +5,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { + ContentBlock, Docs, DocsParams, OnboardingConfig, @@ -70,7 +71,7 @@ const getVerifySnippet = () => ` document.querySelector("#error-button").addEventListener("click", handleClick); `; - +// TODO: Remove once the other product areas support content blocks const getInstallConfig = () => [ { type: StepType.INSTALL, @@ -96,6 +97,17 @@ const getInstallConfig = () => [ }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'bash', + language: 'bash', + code: 'npx astro add @sentry/astro', + }, + ], +}; + const onboarding: OnboardingConfig = { introduction: () => ( @@ -114,19 +126,39 @@ const onboarding: OnboardingConfig = {

), - install: () => getInstallConfig(), + install: () => [ + { + type: StepType.INSTALL, + content: [ + { + type: 'text', + text: tct( + 'Install the [code:@sentry/astro] package with the [code:astro] CLI:', + { + code: , + } + ), + }, + installSnippetBlock, + ], + }, + ], configure: (params: Params) => [ { type: StepType.CONFIGURE, - description: tct( - 'Open up your [astroConfig:astro.config.mjs] file and configure the DSN, and any other settings you need:', + content: [ { - astroConfig: , - } - ), - configurations: [ + type: 'text', + text: tct( + 'Open up your [astroConfig:astro.config.mjs] file and configure the DSN, and any other settings you need:', + { + astroConfig: , + } + ), + }, { - code: [ + type: 'code', + tabs: [ { label: 'JavaScript', value: 'javascript', @@ -136,24 +168,27 @@ const onboarding: OnboardingConfig = { ], }, { - description: tct( + type: 'text', + text: tct( 'Add your Sentry auth token to the [authTokenEnvVar:SENTRY_AUTH_TOKEN] environment variable:', { authTokenEnvVar: , } ), - language: 'bash', - code: [ + }, + { + type: 'code', + tabs: [ { - value: 'bash', - language: 'bash', label: 'bash', - code: `SENTRY_AUTH_TOKEN=___ORG_AUTH_TOKEN___`, + language: 'bash', + code: 'SENTRY_AUTH_TOKEN=___ORG_AUTH_TOKEN___', }, ], }, { - description: tct( + type: 'text', + text: tct( 'You can further customize your SDK by [manualSetupLink:manually initializing the SDK].', { manualSetupLink: ( @@ -168,12 +203,16 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: t( - 'Then throw a test error anywhere in your app, so you can test that everything is working:' - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: t( + 'Then throw a test error anywhere in your app, so you can test that everything is working:' + ), + }, + { + type: 'code', + tabs: [ { label: 'Astro', value: 'html', diff --git a/static/app/gettingStartedDocs/javascript/ember.tsx b/static/app/gettingStartedDocs/javascript/ember.tsx index fea25e1c794ea3..a2fc266fd563ff 100644 --- a/static/app/gettingStartedDocs/javascript/ember.tsx +++ b/static/app/gettingStartedDocs/javascript/ember.tsx @@ -3,6 +3,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { + ContentBlock, Docs, DocsParams, OnboardingConfig, @@ -115,6 +116,7 @@ export default class App extends Application { `; }; +// TODO: Remove once the other product areas support content blocks const getInstallConfig = () => [ { language: 'bash', @@ -125,6 +127,17 @@ ember install @sentry/ember }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'ember-cli', + language: 'bash', + code: 'ember install @sentry/ember', + }, + ], +}; + const getVerifyEmberSnippet = () => ` myUndefinedFunction();`; @@ -139,21 +152,33 @@ const onboarding: OnboardingConfig = { description: t( 'Sentry captures data by using an SDK within your application’s runtime.' ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: t( + 'Sentry captures data by using an SDK within your application’s runtime.' + ), + }, + installSnippetBlock, + ], }, ], configure: (params: Params) => [ { type: StepType.CONFIGURE, - description: tct( - 'You should [code:init] the Sentry SDK as soon as possible during your application load up in [code:app.js], before initializing Ember:', + content: [ { - code: , - } - ), - configurations: [ + type: 'text', + text: tct( + 'You should [code:init] the Sentry SDK as soon as possible during your application load up in [code:app.js], before initializing Ember:', + { + code: , + } + ), + }, { - code: [ + type: 'code', + tabs: [ { label: 'JavaScript', value: 'javascript', @@ -172,9 +197,26 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), + content: [ + { + type: 'text', + text: t( + "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." + ), + }, + { + type: 'code', + tabs: [ + { + label: 'JavaScript', + value: 'javascript', + language: 'javascript', + code: getVerifyEmberSnippet(), + }, + ], + }, + ], + configurations: [ { code: [ diff --git a/static/app/gettingStartedDocs/javascript/gatsby.tsx b/static/app/gettingStartedDocs/javascript/gatsby.tsx index 36d23d60d3f2c4..91844dd3bbf91d 100644 --- a/static/app/gettingStartedDocs/javascript/gatsby.tsx +++ b/static/app/gettingStartedDocs/javascript/gatsby.tsx @@ -5,9 +5,11 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { + ContentBlock, Docs, DocsParams, OnboardingConfig, + OnboardingStep, } from 'sentry/components/onboarding/gettingStartedDoc/types'; import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils'; @@ -116,19 +118,22 @@ root.render(); const getVerifySnippet = () => ` myUndefinedFunction();`; -const getConfigureStep = (params: Params) => { +const getConfigureStep = (params: Params): OnboardingStep => { return { type: StepType.CONFIGURE, - configurations: [ + content: [ { - description: tct( + type: 'text' as const, + text: tct( 'Register the [code:Sentry@sentry/gatsby] plugin in your Gatsby configuration file (typically [code:gatsby-config.js]).', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', - value: 'javascript', language: 'javascript', code: `module.exports = { plugins: [{ @@ -139,14 +144,17 @@ const getConfigureStep = (params: Params) => { ], }, { - description: tct( + type: 'text', + text: tct( 'Then, configure your [codeSentry:Sentry.init:]. For this, create a new file called [codeSentry:sentry.config.js] in the root of your project and add the following code:', {codeSentry: } ), - code: [ + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', - value: 'javascript', language: 'javascript', filename: 'sentry.config.js', code: getSdkSetupSnippet(params), @@ -157,6 +165,7 @@ const getConfigureStep = (params: Params) => { }; }; +// TODO: Remove once the other product areas support content blocks const getInstallConfig = () => [ { language: 'bash', @@ -173,6 +182,27 @@ const getInstallConfig = () => [ }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/gatsby', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/gatsby', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/gatsby', + }, + ], +}; + const onboarding: OnboardingConfig = { introduction: () => tct( @@ -184,11 +214,16 @@ const onboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, - description: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', - {code: } - ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', + {code: } + ), + }, + installSnippetBlock, + ], }, ], configure: (params: Params) => [ @@ -201,12 +236,16 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: t( + "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." + ), + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', value: 'javascript', diff --git a/static/app/gettingStartedDocs/javascript/javascript.tsx b/static/app/gettingStartedDocs/javascript/javascript.tsx index 0a3f641714aba6..39aa2ab56cec7c 100644 --- a/static/app/gettingStartedDocs/javascript/javascript.tsx +++ b/static/app/gettingStartedDocs/javascript/javascript.tsx @@ -9,6 +9,7 @@ import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedba import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { BasePlatformOptions, + ContentBlock, Docs, DocsParams, OnboardingConfig, @@ -275,6 +276,7 @@ Sentry.init({ const getVerifyJSSnippet = () => ` myUndefinedFunction();`; +// TODO: Remove once the other product areas support content blocks const getInstallConfig = () => [ { language: 'bash', @@ -301,27 +303,53 @@ const getInstallConfig = () => [ }, ]; -const getVerifyConfig = () => [ +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/browser', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/browser', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/browser', + }, + ], +}; + +const verifySnippetBlock: ContentBlock[] = [ { - type: StepType.VERIFY, - description: t( + type: 'text', + text: t( "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." ), - configurations: [ + }, + { + type: 'code', + tabs: [ { - code: [ - { - label: 'Javascript', - value: 'javascript', - language: 'javascript', - code: getVerifyJSSnippet(), - }, - ], + label: 'Javascript', + language: 'javascript', + code: getVerifyJSSnippet(), }, ], }, ]; +const getVerifyConfig = () => [ + { + type: StepType.VERIFY, + content: verifySnippetBlock, + }, +]; + const getAiRulesConfig = (params: Params) => getAIRulesForCodeEditorStep({ rules: ` @@ -639,24 +667,32 @@ const packageManagerOnboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, - description: t( - "Sentry captures data by using an SDK within your application's runtime." - ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: t( + "Sentry captures data by using an SDK within your application's runtime." + ), + }, + installSnippetBlock, + ], }, ], configure: params => [ { type: StepType.CONFIGURE, - description: t( - "Initialize Sentry as early as possible in your application's lifecycle." - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: t( + "Initialize Sentry as early as possible in your application's lifecycle." + ), + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', - value: 'javascript', language: 'javascript', code: getSdkSetupSnippet(params), }, diff --git a/static/app/gettingStartedDocs/javascript/nuxt.tsx b/static/app/gettingStartedDocs/javascript/nuxt.tsx index 648a844a289708..e34af5997d736e 100644 --- a/static/app/gettingStartedDocs/javascript/nuxt.tsx +++ b/static/app/gettingStartedDocs/javascript/nuxt.tsx @@ -1,5 +1,3 @@ -import {Fragment} from 'react'; - import ExternalLink from 'sentry/components/links/externalLink'; import {CopyDsnField} from 'sentry/components/onboarding/gettingStartedDoc/copyDsnField'; import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/crashReportCallout'; @@ -53,6 +51,7 @@ const getConfigStep = ({isSelfHosted, organization, projectSlug}: Params) => { ]; }; +// TODO: Refactor once the other product areas support content blocks const getInstallConfig = (params: Params) => [ { type: StepType.INSTALL, @@ -75,24 +74,50 @@ const onboarding: OnboardingConfig = { install: (params: Params) => [ { title: t('Automatic Configuration (Recommended)'), - configurations: getConfigStep(params), + content: [ + { + type: 'text', + text: tct( + 'Configure your app automatically by running the [wizardLink:Sentry wizard] in the root of your project.', + { + wizardLink: ( + + ), + } + ), + }, + { + type: 'code', + tabs: [ + { + label: 'bash', + language: 'bash', + code: `npx @sentry/wizard@latest -i nuxt ${params.isSelfHosted ? '' : '--saas'} --org ${params.organization.slug} --project ${params.projectSlug}`, + }, + ], + }, + ], }, ], configure: params => [ { collapsible: true, title: t('Manual Configuration'), - description: tct( - 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].', + content: [ { - manualSetupLink: ( - + type: 'text', + text: tct( + 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].', + { + manualSetupLink: ( + + ), + } ), - } - ), - configurations: [ + }, { - description: , + type: 'text', + text: , }, ], }, @@ -100,22 +125,22 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: ( - -

- {tctCode( - 'Build and run your application and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.' - )} -

-

{t('Or, throw an error in a simple vue component.')}

-
- ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: tctCode( + 'Build and run your application and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.' + ), + }, + { + type: 'text', + text: t('Or, throw an error in a simple vue component.'), + }, + { + type: 'code', + tabs: [ { label: 'Vue', - value: 'vue', language: 'html', code: getVerifyNuxtSnippet(), }, diff --git a/static/app/gettingStartedDocs/javascript/react-router.spec.tsx b/static/app/gettingStartedDocs/javascript/react-router.spec.tsx new file mode 100644 index 00000000000000..0d6e4e0029457b --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/react-router.spec.tsx @@ -0,0 +1,168 @@ +import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout'; +import {screen} from 'sentry-test/reactTestingLibrary'; +import {textWithMarkupMatcher} from 'sentry-test/utils'; + +import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types'; + +import docs from './react-router'; + +describe('javascript-react-router onboarding docs', function () { + it('renders onboarding docs correctly', () => { + renderWithOnboardingLayout(docs); + + // Renders main headings + expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Configure'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument(); + + // Includes import statement + expect( + screen.getByText( + textWithMarkupMatcher(/import \* as Sentry from "@sentry\/react-router"/) + ) + ).toBeInTheDocument(); + }); + + it('displays sample rates by default', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/)) + ).toBeInTheDocument(); + }); + + it('enables performance setting the tracesSampleRate to 1', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/reactRouterTracingIntegration\(\)/)) + ).toBeInTheDocument(); + }); + + it('enables replay by setting replay samplerates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replayIntegration\(/)) + ).toBeInTheDocument(); + }); + + it('enables profiling when selected', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/nodeProfilingIntegration\(\)/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + }); + + it('includes React Router specific configurations', () => { + renderWithOnboardingLayout(docs); + + // Check for React Router specific setup + expect( + screen.getByText(textWithMarkupMatcher(/createSentryHandleRequest/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/handleError: HandleErrorFunction/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/captureException\(error\)/)) + ).toBeInTheDocument(); + }); + + it('includes server and client setup', () => { + renderWithOnboardingLayout(docs); + + // Check for client setup + expect( + screen.getByText(textWithMarkupMatcher(/entry\.client\.tsx/)) + ).toBeInTheDocument(); + expect(screen.getByText(textWithMarkupMatcher(/HydratedRouter/))).toBeInTheDocument(); + + // Check for server setup + expect( + screen.getByText(textWithMarkupMatcher(/instrument\.server\.mjs/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/entry\.server\.tsx/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/react-router reveal/)) + ).toBeInTheDocument(); + }); + + it('includes error boundary setup', () => { + renderWithOnboardingLayout(docs); + + expect(screen.getByText(textWithMarkupMatcher(/ErrorBoundary/))).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/Route\.ErrorBoundaryProps/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/isRouteErrorResponse/)) + ).toBeInTheDocument(); + }); + + it('includes package.json scripts update', () => { + renderWithOnboardingLayout(docs); + + expect( + screen.getByText( + textWithMarkupMatcher(/NODE_OPTIONS.*--import.*instrument\.server\.mjs/) + ) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/react-router dev/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/react-router-serve/)) + ).toBeInTheDocument(); + }); + + it('displays test error route in verify section', () => { + renderWithOnboardingLayout(docs); + + expect( + screen.getByText(textWithMarkupMatcher(/Sentry Test Error/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/This page will throw an error!/)) + ).toBeInTheDocument(); + }); +}); diff --git a/static/app/gettingStartedDocs/javascript/remix.tsx b/static/app/gettingStartedDocs/javascript/remix.tsx index bdf80fff7d199c..dbbc95a234bfc1 100644 --- a/static/app/gettingStartedDocs/javascript/remix.tsx +++ b/static/app/gettingStartedDocs/javascript/remix.tsx @@ -49,6 +49,7 @@ const getConfigStep = ({isSelfHosted, organization, projectSlug}: Params) => { ]; }; +// TODO: Refactor once the other product areas support content blocks const getInstallConfig = (params: Params) => [ { type: StepType.INSTALL, @@ -77,17 +78,21 @@ const onboarding: OnboardingConfig = { { collapsible: true, title: t('Manual Configuration'), - description: tct( - 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].', + content: [ { - manualSetupLink: ( - + type: 'text', + text: tct( + 'Alternatively, you can also set up the SDK manually, by following the [manualSetupLink:manual setup docs].', + { + manualSetupLink: ( + + ), + } ), - } - ), - configurations: [ + }, { - description: , + type: 'text', + text: , }, ], }, @@ -95,26 +100,25 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: ( - -

- {tct( - 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.', - { - code: , - } - )} -

-

- {t( - 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.' - )} -

-
- ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: tct( + 'Start your development server and visit [code:/sentry-example-page] if you have set it up. Click the button to trigger a test error.', + { + code: , + } + ), + }, + { + type: 'text', + text: t( + 'Or, trigger a sample error by calling a function that does not exist somewhere in your application.' + ), + }, + { + type: 'code', + tabs: [ { label: 'Javascript', value: 'javascript', diff --git a/static/app/gettingStartedDocs/javascript/solid.tsx b/static/app/gettingStartedDocs/javascript/solid.tsx index 4e6285607e6e79..62888db8006af1 100644 --- a/static/app/gettingStartedDocs/javascript/solid.tsx +++ b/static/app/gettingStartedDocs/javascript/solid.tsx @@ -5,6 +5,7 @@ import crashReportCallout from 'sentry/components/onboarding/gettingStartedDoc/f import widgetCallout from 'sentry/components/onboarding/gettingStartedDoc/feedback/widgetCallout'; import TracePropagationMessage from 'sentry/components/onboarding/gettingStartedDoc/replay/tracePropagationMessage'; import type { + ContentBlock, Docs, DocsParams, OnboardingConfig, @@ -134,6 +135,7 @@ const getVerifySnippet = () => ` Throw error `; +// TODO: Remove once the other product areas support content blocks const getInstallConfig = () => [ { language: 'bash', @@ -160,6 +162,27 @@ const getInstallConfig = () => [ }, ]; +const installSnippetBlock: ContentBlock = { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/solid', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/solid', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/solid', + }, + ], +}; + const onboarding: OnboardingConfig = { introduction: () => ( @@ -176,25 +199,34 @@ const onboarding: OnboardingConfig = { install: () => [ { type: StepType.INSTALL, - description: tct( - 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', + content: [ { - code: , - } - ), - configurations: getInstallConfig(), + type: 'text', + text: tct( + 'Add the Sentry SDK as a dependency using [code:npm], [code:yarn], or [code:pnpm]:', + { + code: , + } + ), + }, + installSnippetBlock, + ], }, ], configure: (params: Params) => [ { type: StepType.CONFIGURE, - description: tct( - "Initialize Sentry as early as possible in your application's lifecycle, usually your solid app's entry point ([code:main.ts/js]):", - {code: } - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: tct( + "Initialize Sentry as early as possible in your application's lifecycle, usually your solid app's entry point ([code:main.ts/js]):", + {code: } + ), + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', value: 'javascript', @@ -214,12 +246,16 @@ const onboarding: OnboardingConfig = { verify: () => [ { type: StepType.VERIFY, - description: t( - "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." - ), - configurations: [ + content: [ { - code: [ + type: 'text', + text: t( + "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected." + ), + }, + { + type: 'code', + tabs: [ { label: 'JavaScript', value: 'javascript', diff --git a/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx b/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx new file mode 100644 index 00000000000000..c78cfc56af0a98 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/solidstart.spec.tsx @@ -0,0 +1,116 @@ +import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout'; +import {screen} from 'sentry-test/reactTestingLibrary'; +import {textWithMarkupMatcher} from 'sentry-test/utils'; + +import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types'; + +import docs from './solidstart'; + +describe('javascript-solidstart onboarding docs', function () { + it('renders onboarding docs correctly', () => { + renderWithOnboardingLayout(docs); + + // Renders main headings + expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Configure'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument(); + + // Includes import statement + expect( + screen.getByText( + textWithMarkupMatcher(/import \* as Sentry from "@sentry\/solidstart"/) + ) + ).toBeInTheDocument(); + }); + + it('enables performance setting the tracesSampleRate to 1', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/solidRouterBrowserTracingIntegration\(\)/)) + ).toBeInTheDocument(); + }); + + it('enables replay by setting replay samplerates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replayIntegration\(/)) + ).toBeInTheDocument(); + }); + + it('enables profiling by setting profiling sample rates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ProductSolution.ERROR_MONITORING, ProductSolution.PROFILING], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/Sentry.browserProfilingIntegration\(\)/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/profilesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + }); + + it('includes SolidStart specific configurations', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + // Check for SolidStart specific setup + expect( + screen.getByText(textWithMarkupMatcher(/sentryBeforeResponseMiddleware/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/withSentryRouterRouting/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/instrument\.server\.mjs/)) + ).toBeInTheDocument(); + }); + + it('includes server instrumentation setup', () => { + renderWithOnboardingLayout(docs); + + expect( + screen.getByText( + textWithMarkupMatcher(/NODE_OPTIONS.*--import.*instrument\.server\.mjs/) + ) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/entry-client\.tsx/)) + ).toBeInTheDocument(); + expect(screen.getByText(textWithMarkupMatcher(/middleware\.ts/))).toBeInTheDocument(); + }); + + it('displays test error button in verify section', () => { + renderWithOnboardingLayout(docs); + + expect(screen.getByText(textWithMarkupMatcher(/Throw error/))).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/Sentry Frontend Error/)) + ).toBeInTheDocument(); + }); +}); diff --git a/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx b/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx new file mode 100644 index 00000000000000..93aef3025adb24 --- /dev/null +++ b/static/app/gettingStartedDocs/javascript/tanstackstart-react.spec.tsx @@ -0,0 +1,127 @@ +import {renderWithOnboardingLayout} from 'sentry-test/onboarding/renderWithOnboardingLayout'; +import {screen} from 'sentry-test/reactTestingLibrary'; +import {textWithMarkupMatcher} from 'sentry-test/utils'; + +import {ProductSolution} from 'sentry/components/onboarding/gettingStartedDoc/types'; + +import docs from './tanstackstart-react'; + +describe('javascript-tanstackstart-react onboarding docs', function () { + it('renders onboarding docs correctly', () => { + renderWithOnboardingLayout(docs); + + // Renders main headings + expect(screen.getByRole('heading', {name: 'Install'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Set up the SDK'})).toBeInTheDocument(); + expect(screen.getByRole('heading', {name: 'Verify'})).toBeInTheDocument(); + + // Includes import statement + expect( + screen.getByText( + textWithMarkupMatcher(/import \* as Sentry from "@sentry\/tanstackstart-react"/) + ) + ).toBeInTheDocument(); + + // Includes configuration wrapper + expect( + screen.getByText(textWithMarkupMatcher(/wrapVinxiConfigWithSentry/)) + ).toBeInTheDocument(); + }); + + it('displays sample rates by default', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate/)) + ).toBeInTheDocument(); + }); + + it('enables performance setting the tracesSampleRate to 1', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.PERFORMANCE_MONITORING, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/tracesSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText( + textWithMarkupMatcher(/tanstackRouterBrowserTracingIntegration\(router\)/) + ) + ).toBeInTheDocument(); + }); + + it('enables replay by setting replay samplerates', () => { + renderWithOnboardingLayout(docs, { + selectedProducts: [ + ProductSolution.ERROR_MONITORING, + ProductSolution.SESSION_REPLAY, + ], + }); + + expect( + screen.getByText(textWithMarkupMatcher(/replaysSessionSampleRate: 0\.1/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replaysOnErrorSampleRate: 1\.0/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/replayIntegration\(\)/)) + ).toBeInTheDocument(); + }); + + it('includes TanStack Start specific configurations', () => { + renderWithOnboardingLayout(docs); + + // Check for TanStack Start specific setup + expect( + screen.getByText(textWithMarkupMatcher(/wrapCreateRootRouteWithSentry/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/wrapStreamHandlerWithSentry/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/sentryGlobalServerMiddlewareHandler/)) + ).toBeInTheDocument(); + }); + + it('includes error boundary configuration', () => { + renderWithOnboardingLayout(docs); + + expect( + screen.getByText(textWithMarkupMatcher(/withErrorBoundary/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/captureException/)) + ).toBeInTheDocument(); + }); + + it('displays test error button in verify section', () => { + renderWithOnboardingLayout(docs); + + expect( + screen.getByText(textWithMarkupMatcher(/Break the world/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/Sentry Test Error/)) + ).toBeInTheDocument(); + expect( + screen.getByText(textWithMarkupMatcher(/sentry-example-api/)) + ).toBeInTheDocument(); + }); +}); diff --git a/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx b/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx index 799c76a9365d46..996b73b6331635 100644 --- a/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx +++ b/static/app/gettingStartedDocs/javascript/tanstackstart-react.tsx @@ -12,61 +12,65 @@ import {getNodeAgentMonitoringOnboarding} from 'sentry/utils/gettingStartedDocs/ type Params = DocsParams; -const getInstallConfig = () => [ - { - language: 'bash', - code: [ - { - label: 'npm', - value: 'npm', - language: 'bash', - code: 'npm install --save @sentry/tanstackstart-react', - }, - { - label: 'yarn', - value: 'yarn', - language: 'bash', - code: 'yarn add @sentry/tanstackstart-react', - }, - { - label: 'pnpm', - value: 'pnpm', - language: 'bash', - code: 'pnpm add @sentry/tanstackstart-react', - }, - ], - }, -]; - const onboarding: OnboardingConfig = { introduction: () => t("In this guide you'll set up the Sentry TanStack Start React SDK"), install: () => [ { type: StepType.INSTALL, - description: tct( - 'Add the Sentry TanStack Start SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:', - {code: } - ), - configurations: getInstallConfig(), + content: [ + { + type: 'text', + text: tct( + 'Add the Sentry TanStack Start SDK as a dependency using [code:npm], [code:yarn] or [code:pnpm]:', + {code: } + ), + }, + { + type: 'code', + tabs: [ + { + label: 'npm', + language: 'bash', + code: 'npm install --save @sentry/tanstackstart-react', + }, + { + label: 'yarn', + language: 'bash', + code: 'yarn add @sentry/tanstackstart-react', + }, + { + label: 'pnpm', + language: 'bash', + code: 'pnpm add @sentry/tanstackstart-react', + }, + ], + }, + ], }, ], configure: (params: Params) => [ { title: t('Set up the SDK'), - description: t( - 'In the following steps we will set up the SDK and instrument various parts of your application.' - ), - configurations: [ + content: [ { - description: tct( + type: 'text', + text: t( + 'In the following steps we will set up the SDK and instrument various parts of your application.' + ), + }, + { + type: 'text', + text: tct( "First, extend your app's default TanStack Start configuration by adding [code:wrapVinxiConfigWithSentry] to your [code:app.config.ts] file:", {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { label: 'TypeScript', - value: 'typescript', language: 'typescript', filename: 'app.config.ts', code: `import { defineConfig } from "@tanstack/react-start/config"; @@ -90,11 +94,15 @@ export default wrapVinxiConfigWithSentry(config, { ], }, { - description: tct( + type: 'text', + text: tct( 'In future versions of this SDK, setting the [code:SENTRY_AUTH_TOKEN] environment variable during your build will upload sourcemaps for you to see unminified errors in Sentry.', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { value: 'bash', language: 'bash', @@ -104,13 +112,17 @@ export default wrapVinxiConfigWithSentry(config, { ], }, { - description: tct( + type: 'text', + text: tct( 'Add the following initialization code to your [code:app/client.tsx] file to initialize Sentry on the client:', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', filename: 'app/client.tsx', @@ -168,13 +180,17 @@ hydrateRoot(document, );`, ], }, { - description: tct( + type: 'text', + text: tct( 'Add the following initialization code anywhere in your [code:app/ssr.tsx] file to initialize Sentry on the server:', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', filename: 'app/ssr.tsx', @@ -202,13 +218,17 @@ Sentry.init({ ], }, { - description: tct( + type: 'text', + text: tct( "Wrap TanStack Start's [code:createRootRoute] function using [code:wrapCreateRootRouteWithSentry] to apply tracing to Server-Side Rendering (SSR):", {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', filename: 'app/routes/__root.tsx', @@ -223,13 +243,17 @@ export const Route = wrapCreateRootRouteWithSentry(createRootRoute)({ ], }, { - description: tct( + type: 'text', + text: tct( "Wrap TanStack Start's [code:defaultStreamHandler] function using [code:wrapStreamHandlerWithSentry] to instrument requests to your server:", {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', filename: 'app/ssr.tsx', @@ -249,11 +273,15 @@ export default createStartHandler({ ], }, { - description: tct( + type: 'text', + text: tct( "Add the [code:sentryGlobalServerMiddlewareHandler] to your global middlewares to instrument your server function invocations. If you haven't done so, create a [code:app/global-middleware.ts] file.", {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { label: 'TypeScript', value: 'typescript', @@ -274,18 +302,23 @@ registerGlobalMiddleware({ ], }, { - description: t( + type: 'text', + text: t( 'The Sentry SDK cannot capture errors that you manually caught yourself with error boundaries.' ), }, { - description: tct( + type: 'text', + text: tct( 'If you have React error boundaries in your app and you want to report errors that these boundaries catch to Sentry, apply the [code:withErrorBoundary] wrapper to your [code:ErrorBoundary]:', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', code: `import React from "react"; @@ -305,13 +338,17 @@ export const MySentryWrappedErrorBoundary = withErrorBoundary( ], }, { - description: tct( + type: 'text', + text: tct( 'If you defined [code:errorComponents] in your Code-Based TanStack Router routes, capture the [code:error] argument with [code:captureException] inside a [code:useEffect] hook:', {code: } ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', + label: 'TypeScript', value: 'tsx', language: 'tsx', code: `import { createRoute } from "@tanstack/react-router"; @@ -337,18 +374,24 @@ const route = createRoute({ verify: () => [ { type: StepType.VERIFY, - description: t( - "Let's test your setup and confirm that Sentry is working correctly and sending data to your Sentry project." - ), - configurations: [ + content: [ { - description: t( + type: 'text', + text: t( + "Let's test your setup and confirm that Sentry is working correctly and sending data to your Sentry project." + ), + }, + { + type: 'text', + text: t( 'To verify that Sentry captures errors and creates issues in your Sentry project, add a test button to an existing page or create a new one:' ), - code: [ + }, + { + type: 'code', + tabs: [ { - label: 'tsx', - value: 'tsx', + label: 'TypeScript', language: 'tsx', code: `