Skip to content

Commit e2213fc

Browse files
authored
feat(solidstart): Add sentrySolidStartVite plugin to simplify source maps upload (#13493)
This plugin simplifies source maps upload by using `@sentry/vite-plugin` and enabling source map generation by default. Usage: ```js import { defineConfig } from '@solidjs/start/config' import { sentrySolidStartVite } from '@sentry/solidstart' export default defineConfig({ vite: { plugins: [ sentrySolidStartVite({ org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, authToken: process.env.SENTRY_AUTH_TOKEN, debug: true, }), ], } }) ``` Closes: #12553
1 parent a69da1b commit e2213fc

19 files changed

+464
-120
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ jobs:
10031003
working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}
10041004
timeout-minutes: 5
10051005
run: pnpm test:assert
1006-
1006+
10071007
- name: Upload Playwright Traces
10081008
uses: actions/upload-artifact@v4
10091009
if: failure()
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { sentrySolidStartVite } from '@sentry/solidstart';
12
import { defineConfig } from '@solidjs/start/config';
23

3-
export default defineConfig({});
4+
export default defineConfig({
5+
vite: {
6+
plugins: [sentrySolidStartVite()],
7+
},
8+
});

dev-packages/e2e-tests/test-applications/solidstart/package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
"version": "0.0.0",
44
"scripts": {
55
"clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output",
6+
"clean:build": "pnpx rimraf .vinxi .output",
67
"dev": "NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
78
"build": "vinxi build",
89
"//": [
910
"We are using `vinxi dev` to start the server because `vinxi start` is experimental and ",
1011
"doesn't correctly resolve modules for @sentry/solidstart/solidrouter.",
11-
"This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177"
12+
"This is currently not an issue outside of our repo. See: https://github.com/nksaraf/vinxi/issues/177",
13+
"We run the build command to ensure building succeeds. However, keeping",
14+
"build output around slows down the vite dev server when using `@sentry/vite-plugin` so we clear it out",
15+
"before actually running the tests.",
16+
"Cleaning the build output should be removed once we can use `vinxi start`."
1217
],
13-
"preview": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
18+
"preview": "pnpm clean:build && HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi dev",
1419
"start": "HOST=localhost PORT=3030 NODE_OPTIONS='--import ./src/instrument.server.mjs' vinxi start",
1520
"test:prod": "TEST_ENV=production playwright test",
1621
"test:build": "pnpm install && npx playwright install && pnpm build",
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,15 @@
1-
import * as Sentry from '@sentry/solidstart';
2-
import type { ParentProps } from 'solid-js';
3-
import { ErrorBoundary, createSignal, onMount } from 'solid-js';
4-
5-
const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary);
6-
7-
const [count, setCount] = createSignal(1);
8-
const [caughtError, setCaughtError] = createSignal(false);
9-
101
export default function ClientErrorPage() {
112
return (
12-
<SampleErrorBoundary>
13-
{caughtError() && (
14-
<Throw error={`Error ${count()} thrown from Sentry ErrorBoundary in Solid Start E2E test app`} />
15-
)}
16-
<section class="bg-gray-100 text-gray-700 p-8">
17-
<div class="flex flex-col items-start space-x-2">
18-
<button
19-
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
20-
id="caughtErrorBtn"
21-
onClick={() => setCaughtError(true)}
22-
>
23-
Throw caught error
24-
</button>
25-
</div>
26-
<div class="flex flex-col items-start space-x-2">
27-
<button
28-
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
29-
id="errorBtn"
30-
onClick={() => {
31-
throw new Error('Error thrown from Solid Start E2E test app');
32-
}}
33-
>
34-
Throw uncaught error
35-
</button>
36-
</div>
37-
</section>
38-
</SampleErrorBoundary>
39-
);
40-
}
41-
42-
function Throw(props: { error: string }) {
43-
onMount(() => {
44-
throw new Error(props.error);
45-
});
46-
return null;
47-
}
48-
49-
function SampleErrorBoundary(props: ParentProps) {
50-
return (
51-
<SentryErrorBoundary
52-
fallback={(error, reset) => (
53-
<section class="bg-gray-100 text-gray-700 p-8">
54-
<h1 class="text-2xl font-bold">Error Boundary Fallback</h1>
55-
<div class="flex items-center space-x-2 mb-4">
56-
<code>{error.message}</code>
57-
</div>
58-
<button
59-
id="errorBoundaryResetBtn"
60-
class="border rounded-lg px-2 border-gray-900"
61-
onClick={() => {
62-
setCount(count() + 1);
63-
setCaughtError(false);
64-
reset();
65-
}}
66-
>
67-
Reset
68-
</button>
69-
</section>
70-
)}
71-
>
72-
{props.children}
73-
</SentryErrorBoundary>
3+
<div class="flex flex-col items-start space-x-2">
4+
<button
5+
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
6+
id="errorBtn"
7+
onClick={() => {
8+
throw new Error('Uncaught error thrown from Solid Start E2E test app');
9+
}}
10+
>
11+
Throw uncaught error
12+
</button>
13+
</div>
7414
);
7515
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as Sentry from '@sentry/solidstart';
2+
import type { ParentProps } from 'solid-js';
3+
import { ErrorBoundary, createSignal, onMount } from 'solid-js';
4+
5+
const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary);
6+
7+
const [count, setCount] = createSignal(1);
8+
const [caughtError, setCaughtError] = createSignal(false);
9+
10+
export default function ErrorBoundaryTestPage() {
11+
return (
12+
<SampleErrorBoundary>
13+
{caughtError() && (
14+
<Throw error={`Error ${count()} thrown from Sentry ErrorBoundary in Solid Start E2E test app`} />
15+
)}
16+
<section class="bg-gray-100 text-gray-700 p-8">
17+
<div class="flex flex-col items-start space-x-2">
18+
<button
19+
class="border rounded-lg px-2 mb-2 border-red-500 text-red-500 cursor-pointer"
20+
id="caughtErrorBtn"
21+
onClick={() => setCaughtError(true)}
22+
>
23+
Throw caught error
24+
</button>
25+
</div>
26+
</section>
27+
</SampleErrorBoundary>
28+
);
29+
}
30+
31+
function Throw(props: { error: string }) {
32+
onMount(() => {
33+
throw new Error(props.error);
34+
});
35+
return null;
36+
}
37+
38+
function SampleErrorBoundary(props: ParentProps) {
39+
return (
40+
<SentryErrorBoundary
41+
fallback={(error, reset) => (
42+
<section class="bg-gray-100 text-gray-700 p-8">
43+
<h1 class="text-2xl font-bold">Error Boundary Fallback</h1>
44+
<div class="flex items-center space-x-2 mb-4">
45+
<code>{error.message}</code>
46+
</div>
47+
<button
48+
id="errorBoundaryResetBtn"
49+
class="border rounded-lg px-2 border-gray-900"
50+
onClick={() => {
51+
setCount(count() + 1);
52+
setCaughtError(false);
53+
reset();
54+
}}
55+
>
56+
Reset
57+
</button>
58+
</section>
59+
)}
60+
>
61+
{props.children}
62+
</SentryErrorBoundary>
63+
);
64+
}

dev-packages/e2e-tests/test-applications/solidstart/src/routes/index.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export default function Home() {
1414
<li>
1515
<A href="/server-error">Server error</A>
1616
</li>
17+
<li>
18+
<A href="/error-boundary">Error Boundary</A>
19+
</li>
1720
<li>
1821
<A id="navLink" href="/users/5">
1922
User 5

dev-packages/e2e-tests/test-applications/solidstart/tests/errorboundary.test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ test('captures an exception', async ({ page }) => {
1010
);
1111
});
1212

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

@@ -27,7 +27,7 @@ test('captures an exception', async ({ page }) => {
2727
},
2828
],
2929
},
30-
transaction: '/client-error',
30+
transaction: '/error-boundary',
3131
});
3232
});
3333

@@ -40,7 +40,7 @@ test('captures a second exception after resetting the boundary', async ({ page }
4040
);
4141
});
4242

43-
await page.goto('/client-error');
43+
await page.goto('/error-boundary');
4444
await page.locator('#caughtErrorBtn').click();
4545
const firstErrorEvent = await firstErrorEventPromise;
4646

@@ -57,7 +57,7 @@ test('captures a second exception after resetting the boundary', async ({ page }
5757
},
5858
],
5959
},
60-
transaction: '/client-error',
60+
transaction: '/error-boundary',
6161
});
6262

6363
const secondErrorEventPromise = waitForError('solidstart', errorEvent => {
@@ -85,6 +85,6 @@ test('captures a second exception after resetting the boundary', async ({ page }
8585
},
8686
],
8787
},
88-
transaction: '/client-error',
88+
transaction: '/error-boundary',
8989
});
9090
});

dev-packages/e2e-tests/test-applications/solidstart/tests/errors.client.test.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { waitForError } from '@sentry-internal/test-utils';
44
test.describe('client-side errors', () => {
55
test('captures error thrown on click', async ({ page }) => {
66
const errorPromise = waitForError('solidstart', async errorEvent => {
7-
return errorEvent?.exception?.values?.[0]?.value === 'Error thrown from Solid Start E2E test app';
7+
return errorEvent?.exception?.values?.[0]?.value === 'Uncaught error thrown from Solid Start E2E test app';
88
});
99

1010
await page.goto(`/client-error`);
@@ -16,9 +16,8 @@ test.describe('client-side errors', () => {
1616
values: [
1717
{
1818
type: 'Error',
19-
value: 'Error thrown from Solid Start E2E test app',
19+
value: 'Uncaught error thrown from Solid Start E2E test app',
2020
mechanism: {
21-
type: 'instrument',
2221
handled: false,
2322
},
2423
},

packages/solidstart/.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
files: ['src/vite/**', 'src/server/**'],
1515
rules: {
1616
'@sentry-internal/sdk/no-optional-chaining': 'off',
17+
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
1718
},
1819
},
1920
],

packages/solidstart/README.md

+11-35
Original file line numberDiff line numberDiff line change
@@ -157,58 +157,34 @@ render(
157157
);
158158
```
159159

160-
# Sourcemaps and Releases
160+
## Uploading Source Maps
161161

162-
To generate and upload source maps of your Solid Start app use our Vite bundler plugin.
163-
164-
1. Install the Sentry Vite plugin
165-
166-
```bash
167-
# Using npm
168-
npm install @sentry/vite-plugin --save-dev
169-
170-
# Using yarn
171-
yarn add @sentry/vite-plugin --dev
172-
```
173-
174-
2. Configure the vite plugin
175-
176-
To upload source maps you have to configure an auth token. Auth tokens can be passed to the plugin explicitly with the
177-
`authToken` option, with a `SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the
178-
working directory when building your project. We recommend you add the auth token to your CI/CD environment as an
179-
environment variable.
162+
To upload source maps, add the `sentrySolidStartVite` plugin from `@sentry/solidstart` to your `app.config.ts` and
163+
configure an auth token. Auth tokens can be passed to the plugin explicitly with the `authToken` option, with a
164+
`SENTRY_AUTH_TOKEN` environment variable, or with an `.env.sentry-build-plugin` file in the working directory when
165+
building your project. We recommend you add the auth token to your CI/CD environment as an environment variable.
180166

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

184-
```bash
185-
// .env.sentry-build-plugin
186-
SENTRY_AUTH_TOKEN=<your auth token>
187-
SENTRY_ORG=<your org>
188-
SENTRY_PROJECT=<your project name>
189-
```
190-
191-
3. Finally, add the plugin to your `app.config.ts` file.
192-
193-
```javascript
170+
```typescript
171+
// app.config.ts
194172
import { defineConfig } from '@solidjs/start/config';
195-
import { sentryVitePlugin } from '@sentry/vite-plugin';
173+
import { sentrySolidStartVite } from '@sentry/solidstart';
196174

197175
export default defineConfig({
198-
// rest of your config
199176
// ...
200177

201178
vite: {
202-
build: {
203-
sourcemap: true,
204-
},
205179
plugins: [
206-
sentryVitePlugin({
180+
sentrySolidStartVite({
207181
org: process.env.SENTRY_ORG,
208182
project: process.env.SENTRY_PROJECT,
209183
authToken: process.env.SENTRY_AUTH_TOKEN,
184+
debug: true,
210185
}),
211186
],
212187
},
188+
// ...
213189
});
214190
```
+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './server';
2+
export * from './vite';

packages/solidstart/src/index.types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// exports in this file - which we do below.
44
export * from './client';
55
export * from './server';
6+
export * from './vite';
67

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

packages/solidstart/src/vite/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './sentrySolidStartVite';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Plugin } from 'vite';
2+
import { makeSourceMapsVitePlugin } from './sourceMaps';
3+
import type { SentrySolidStartPluginOptions } from './types';
4+
5+
/**
6+
* Various Sentry vite plugins to be used for SolidStart.
7+
*/
8+
export const sentrySolidStartVite = (options: SentrySolidStartPluginOptions = {}): Plugin[] => {
9+
const sentryPlugins: Plugin[] = [];
10+
11+
if (process.env.NODE_ENV !== 'development') {
12+
if (options.sourceMapsUploadOptions?.enabled ?? true) {
13+
sentryPlugins.push(...makeSourceMapsVitePlugin(options));
14+
}
15+
}
16+
17+
return sentryPlugins;
18+
};

0 commit comments

Comments
 (0)