Skip to content

Commit

Permalink
feat(solidstart): Add solidstart sentry vite plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
andreiborza committed Aug 30, 2024
1 parent f452423 commit 3627b54
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -1,75 +1,15 @@
import * as Sentry from '@sentry/solidstart';
import type { ParentProps } from 'solid-js';
import { ErrorBoundary, createSignal, onMount } from 'solid-js';

const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary);

const [count, setCount] = createSignal(1);
const [caughtError, setCaughtError] = createSignal(false);

export default function ClientErrorPage() {
return (
<SampleErrorBoundary>
{caughtError() && (
<Throw error={`Error ${count()} thrown from Sentry ErrorBoundary in Solid Start E2E test app`} />
)}
<section class="bg-gray-100 text-gray-700 p-8">
<div class="flex flex-col items-start space-x-2">
<button
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
id="caughtErrorBtn"
onClick={() => setCaughtError(true)}
>
Throw caught error
</button>
</div>
<div class="flex flex-col items-start space-x-2">
<button
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
id="errorBtn"
onClick={() => {
throw new Error('Error thrown from Solid Start E2E test app');
}}
>
Throw uncaught error
</button>
</div>
</section>
</SampleErrorBoundary>
);
}

function Throw(props: { error: string }) {
onMount(() => {
throw new Error(props.error);
});
return null;
}

function SampleErrorBoundary(props: ParentProps) {
return (
<SentryErrorBoundary
fallback={(error, reset) => (
<section class="bg-gray-100 text-gray-700 p-8">
<h1 class="text-2xl font-bold">Error Boundary Fallback</h1>
<div class="flex items-center space-x-2 mb-4">
<code>{error.message}</code>
</div>
<button
id="errorBoundaryResetBtn"
class="border rounded-lg px-2 border-gray-900"
onClick={() => {
setCount(count() + 1);
setCaughtError(false);
reset();
}}
>
Reset
</button>
</section>
)}
>
{props.children}
</SentryErrorBoundary>
<div class="flex flex-col items-start space-x-2">
<button
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
id="errorBtn"
onClick={() => {
throw new Error('Uncaught error thrown from Solid Start E2E test app');
}}
>
Throw uncaught error
</button>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as Sentry from '@sentry/solidstart';
import type { ParentProps } from 'solid-js';
import { ErrorBoundary, createSignal, onMount } from 'solid-js';

const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary);

const [count, setCount] = createSignal(1);
const [caughtError, setCaughtError] = createSignal(false);

export default function ErrorBoundaryTestPage() {
return (
<SampleErrorBoundary>
{caughtError() && (
<Throw error={`Error ${count()} thrown from Sentry ErrorBoundary in Solid Start E2E test app`} />
)}
<section class="bg-gray-100 text-gray-700 p-8">
<div class="flex flex-col items-start space-x-2">
<button
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
id="caughtErrorBtn"
onClick={() => setCaughtError(true)}
>
Throw caught error
</button>
</div>
</section>
</SampleErrorBoundary>
);
}

function Throw(props: { error: string }) {
onMount(() => {
throw new Error(props.error);
});
return null;
}

function SampleErrorBoundary(props: ParentProps) {
return (
<SentryErrorBoundary
fallback={(error, reset) => (
<section class="bg-gray-100 text-gray-700 p-8">
<h1 class="text-2xl font-bold">Error Boundary Fallback</h1>
<div class="flex items-center space-x-2 mb-4">
<code>{error.message}</code>
</div>
<button
id="errorBoundaryResetBtn"
class="border rounded-lg px-2 border-gray-900"
onClick={() => {
setCount(count() + 1);
setCaughtError(false);
reset();
}}
>
Reset
</button>
</section>
)}
>
{props.children}
</SentryErrorBoundary>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ export default function Home() {
<li>
<A href="/server-error">Server error</A>
</li>
<li>
<A href="/error-boundary">Error Boundary</A>
</li>
<li>
<A id="navLink" href="/users/5">
User 5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ test('captures an exception', async ({ page }) => {
);
});

await page.goto('/client-error');
await page.goto('/error-boundary');
await page.locator('#caughtErrorBtn').click();
const errorEvent = await errorEventPromise;

Expand All @@ -27,7 +27,7 @@ test('captures an exception', async ({ page }) => {
},
],
},
transaction: '/client-error',
transaction: '/error-boundary',
});
});

Expand All @@ -40,7 +40,8 @@ test('captures a second exception after resetting the boundary', async ({ page }
);
});

await page.goto('/client-error');
await page.waitForTimeout(5000);
await page.goto('/error-boundary');
await page.locator('#caughtErrorBtn').click();
const firstErrorEvent = await firstErrorEventPromise;

Expand All @@ -57,7 +58,7 @@ test('captures a second exception after resetting the boundary', async ({ page }
},
],
},
transaction: '/client-error',
transaction: '/error-boundary',
});

const secondErrorEventPromise = waitForError('solidstart', errorEvent => {
Expand Down Expand Up @@ -85,6 +86,6 @@ test('captures a second exception after resetting the boundary', async ({ page }
},
],
},
transaction: '/client-error',
transaction: '/error-boundary',
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { waitForError } from '@sentry-internal/test-utils';
test.describe('client-side errors', () => {
test('captures error thrown on click', async ({ page }) => {
const errorPromise = waitForError('solidstart', async errorEvent => {
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Solid Start E2E test app';
return errorEvent?.exception?.values?.[0]?.value === 'Uncaught error thrown from Solid Start E2E test app';
});

await page.goto(`/client-error`);
Expand All @@ -16,7 +16,7 @@ test.describe('client-side errors', () => {
values: [
{
type: 'Error',
value: 'Error thrown from Solid Start E2E test app',
value: 'Uncaught error thrown from Solid Start E2E test app',
mechanism: {
type: 'instrument',
handled: false,
Expand Down
1 change: 1 addition & 0 deletions packages/solidstart/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
files: ['src/vite/**', 'src/server/**'],
rules: {
'@sentry-internal/sdk/no-optional-chaining': 'off',
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
},
},
],
Expand Down
46 changes: 11 additions & 35 deletions packages/solidstart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,58 +157,34 @@ render(
);
```

# Sourcemaps and Releases
## Uploading Source Maps

To generate and upload source maps of your Solid Start app use our Vite bundler plugin.

1. Install the Sentry Vite plugin

```bash
# Using npm
npm install @sentry/vite-plugin --save-dev

# Using yarn
yarn add @sentry/vite-plugin --dev
```

2. Configure the vite plugin

To upload source maps you have to configure an auth token. Auth tokens can be passed to the plugin explicitly with the
`authToken` option, with a `SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the
working directory when building your project. We recommend you add the auth token to your CI/CD environment as an
environment variable.
To upload source maps, add the `sentrySolidStartVite` plugin from `@sentry/solidstart` to your `app.config.ts` and
configure an auth token. Auth tokens can be passed to the plugin explicitly with the `authToken` option, with a
`SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the working directory when
building your project. We recommend you add the auth token to your CI/CD environment as an environment variable.

Learn more about configuring the plugin in our
[Sentry Vite Plugin documentation](https://www.npmjs.com/package/@sentry/vite-plugin).

```bash
// .env.sentry-build-plugin
SENTRY_AUTH_TOKEN=<your auth token>
SENTRY_ORG=<your org>
SENTRY_PROJECT=<your project name>
```

3. Finally, add the plugin to your `app.config.ts` file.

```javascript
```typescript
// app.config.ts
import { defineConfig } from '@solidjs/start/config';
import { sentryVitePlugin } from '@sentry/vite-plugin';
import { sentrySolidStartVite } from '@sentry/solidstart';

export default defineConfig({
// rest of your config
// ...

vite: {
build: {
sourcemap: true,
},
plugins: [
sentryVitePlugin({
sentrySolidStartVite({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
debug: true,
}),
],
},
// ...
});
```
1 change: 1 addition & 0 deletions packages/solidstart/src/index.server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './server';
export * from './vite';
1 change: 1 addition & 0 deletions packages/solidstart/src/index.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// exports in this file - which we do below.
export * from './client';
export * from './server';
export * from './vite';

import type { Integration, Options, StackParser } from '@sentry/types';

Expand Down
1 change: 1 addition & 0 deletions packages/solidstart/src/vite/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './sentrySolidStartVite';
18 changes: 18 additions & 0 deletions packages/solidstart/src/vite/sentrySolidStartVite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Plugin } from 'vite';
import { makeSourceMapsVitePlugin } from './sourceMaps';
import type { SentrySolidStartPluginOptions } from './types';

/**
* Various Sentry vite plugins to be used for SolidStart.
*/
export const sentrySolidStartVite = (options: SentrySolidStartPluginOptions): Plugin[] => {
const sentryPlugins: Plugin[] = [];

if (process.env.NODE_ENV !== 'development') {
if (options.sourceMapsUploadOptions?.enabled ?? true) {
sentryPlugins.push(...makeSourceMapsVitePlugin(options));
}
}

return sentryPlugins;
};
57 changes: 57 additions & 0 deletions packages/solidstart/src/vite/sourceMaps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { sentryVitePlugin } from '@sentry/vite-plugin';
import type { Plugin } from 'vite';
import type { SentrySolidStartPluginOptions } from './types';

/**
* A Sentry plugin for SolidStart to enable source maps and use
* @sentry/vite-plugin to automatically upload source maps to Sentry.
* @param {SourceMapsOptions} options
*/
export function makeSourceMapsVitePlugin(options: SentrySolidStartPluginOptions): Plugin[] {
const { authToken, debug, org, project, sourceMapsUploadOptions } = options;
return [
{
name: 'sentry-solidstart-source-maps',
apply: 'build',
enforce: 'post',
config(config) {
const sourceMapsPreviouslyNotEnabled = !config.build?.sourcemap;
if (debug && sourceMapsPreviouslyNotEnabled) {
// eslint-disable-next-line no-console
console.log('[Sentry SolidStart Plugin] Enabling source map generation');
if (!sourceMapsUploadOptions?.filesToDeleteAfterUpload) {
// eslint-disable-next-line no-console
console.warn(
`[Sentry SolidStart PLugin] We recommend setting the \`sourceMapsUploadOptions.filesToDeleteAfterUpload\` option to clean up source maps after uploading.
[Sentry SolidStart Plugin] Otherwise, source maps might be deployed to production, depending on your configuration`,
);
}
}
return {
...config,
build: {
...config.build,
sourcemap: true,
},
};
},
},
...sentryVitePlugin({
org: org ?? process.env.SENTRY_ORG,
project: project ?? process.env.SENTRY_PROJECT,
authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN,
telemetry: sourceMapsUploadOptions?.telemetry ?? true,
sourcemaps: {
filesToDeleteAfterUpload: sourceMapsUploadOptions?.filesToDeleteAfterUpload ?? undefined,
...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions?.sourcemaps,
},
_metaOptions: {
telemetry: {
metaFramework: 'solidstart',
},
},
debug: debug ?? false,
...sourceMapsUploadOptions?.unstable_sentryVitePluginOptions,
}),
];
}
Loading

0 comments on commit 3627b54

Please sign in to comment.