Skip to content

Commit e3c6967

Browse files
s1gr1dandreiborza
andcommitted
feat(solidstart): Default to --import setup and add autoInjectServerSentry (#14862)
This PR adds a `withSentry` wrapper for SolidStart's config to build and place `instrument.server.ts` alongside the server build output so that it doesn't have to be placed in `/public` anymore to be discoverable. The setup is changed to be aligned with Nuxt. First, the `instrument.server.ts` file is added to the build output (the sentry release injection file needs to be copied as well - this is not ideal at the moment as there **could** be other imports as well, but it's okay for now) Then, there are two options to set up the SDK: 1. Users provide an `--import` CLI flag to their start command like this: ```node --import ./.output/server/instrument.server.mjs .output/server/index.mjs``` 2. Users can add `autoInjectServerSentry: 'top-level-import'` and the Sentry config will be imported at the top of the server entry ```typescript // app.config.ts import { defineConfig } from '@solidjs/start/config'; import { withSentry } from '@sentry/solidstart'; export default defineConfig(withSentry( { /* ... */ }, { autoInjectServerSentry: 'top-level-import' // optional }) ); ``` --- builds on top of the idea in this PR: #13784 --------- Co-authored-by: Andrei Borza <[email protected]>
1 parent d5f80af commit e3c6967

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1651
-7
lines changed

CHANGELOG.md

+44
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,50 @@
1010

1111
- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott
1212

13+
- **feat(solidstart): Default to `--import` setup and add `autoInjectServerSentry` ([#14862](https://github.com/getsentry/sentry-javascript/pull/14862))**
14+
15+
To enable the SolidStart SDK, wrap your SolidStart Config with `withSentry`. The `sentrySolidStartVite` plugin is now automatically
16+
added by `withSentry` and you can pass the Sentry build-time options like this:
17+
18+
```js
19+
import { defineConfig } from '@solidjs/start/config';
20+
import { withSentry } from '@sentry/solidstart';
21+
22+
export default defineConfig(
23+
withSentry(
24+
{
25+
/* Your SolidStart config options... */
26+
},
27+
{
28+
// Options for setting up source maps
29+
org: process.env.SENTRY_ORG,
30+
project: process.env.SENTRY_PROJECT,
31+
authToken: process.env.SENTRY_AUTH_TOKEN,
32+
},
33+
),
34+
);
35+
```
36+
37+
With the `withSentry` wrapper, the Sentry server config should not be added to the `public` directory anymore.
38+
Add the Sentry server config in `src/instrument.server.ts`. Then, the server config will be placed inside the server build output as `instrument.server.mjs`.
39+
40+
Now, there are two options to set up the SDK:
41+
42+
1. **(recommended)** Provide an `--import` CLI flag to the start command like this (path depends on your server setup):
43+
`node --import ./.output/server/instrument.server.mjs .output/server/index.mjs`
44+
2. Add `autoInjectServerSentry: 'top-level-import'` and the Sentry config will be imported at the top of the server entry (comes with tracing limitations)
45+
```js
46+
withSentry(
47+
{
48+
/* Your SolidStart config options... */
49+
},
50+
{
51+
// Optional: Install Sentry with a top-level import
52+
autoInjectServerSentry: 'top-level-import',
53+
},
54+
);
55+
```
56+
1357
## 8.51.0
1458

1559
### Important Changes
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
dist
3+
.solid
4+
.output
5+
.vercel
6+
.netlify
7+
.vinxi
8+
9+
# Environment
10+
.env
11+
.env*.local
12+
13+
# dependencies
14+
/node_modules
15+
/.pnp
16+
.pnp.js
17+
18+
# IDEs and editors
19+
/.idea
20+
.project
21+
.classpath
22+
*.launch
23+
.settings/
24+
25+
# Temp
26+
gitignore
27+
28+
# testing
29+
/coverage
30+
31+
# misc
32+
.DS_Store
33+
.env.local
34+
.env.development.local
35+
.env.test.local
36+
.env.production.local
37+
38+
npm-debug.log*
39+
yarn-debug.log*
40+
yarn-error.log*
41+
42+
/test-results/
43+
/playwright-report/
44+
/playwright/.cache/
45+
46+
!*.d.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# SolidStart
2+
3+
Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
4+
5+
## Creating a project
6+
7+
```bash
8+
# create a new project in the current directory
9+
npm init solid@latest
10+
11+
# create a new project in my-app
12+
npm init solid@latest my-app
13+
```
14+
15+
## Developing
16+
17+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a
18+
development server:
19+
20+
```bash
21+
npm run dev
22+
23+
# or start the server and open the app in a new browser tab
24+
npm run dev -- --open
25+
```
26+
27+
## Building
28+
29+
Solid apps are built with _presets_, which optimise your project for deployment to different environments.
30+
31+
By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add
32+
it to the `devDependencies` in `package.json` and specify in your `app.config.js`.
33+
34+
## Testing
35+
36+
Tests are written with `vitest`, `@solidjs/testing-library` and `@testing-library/jest-dom` to extend expect with some
37+
helpful custom matchers.
38+
39+
To run them, simply start:
40+
41+
```sh
42+
npm test
43+
```
44+
45+
## This project was created with the [Solid CLI](https://solid-cli.netlify.app)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { withSentry } from '@sentry/solidstart';
2+
import { defineConfig } from '@solidjs/start/config';
3+
4+
export default defineConfig(
5+
withSentry(
6+
{},
7+
{
8+
autoInjectServerSentry: 'top-level-import',
9+
},
10+
),
11+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "solidstart-top-level-import-e2e-testapp",
3+
"version": "0.0.0",
4+
"scripts": {
5+
"clean": "pnpx rimraf node_modules pnpm-lock.yaml .vinxi .output",
6+
"dev": "vinxi dev",
7+
"build": "vinxi build && sh ./post_build.sh",
8+
"preview": "HOST=localhost PORT=3030 vinxi start",
9+
"test:prod": "TEST_ENV=production playwright test",
10+
"test:build": "pnpm install && pnpm build",
11+
"test:assert": "pnpm test:prod"
12+
},
13+
"type": "module",
14+
"dependencies": {
15+
"@sentry/solidstart": "latest || *"
16+
},
17+
"devDependencies": {
18+
"@playwright/test": "^1.44.1",
19+
"@solidjs/meta": "^0.29.4",
20+
"@solidjs/router": "^0.13.4",
21+
"@solidjs/start": "^1.0.2",
22+
"@solidjs/testing-library": "^0.8.7",
23+
"@testing-library/jest-dom": "^6.4.2",
24+
"@testing-library/user-event": "^14.5.2",
25+
"@vitest/ui": "^1.5.0",
26+
"jsdom": "^24.0.0",
27+
"solid-js": "1.8.17",
28+
"typescript": "^5.4.5",
29+
"vinxi": "^0.4.0",
30+
"vite": "^5.4.10",
31+
"vite-plugin-solid": "^2.10.2",
32+
"vitest": "^1.5.0"
33+
},
34+
"overrides": {
35+
"@vercel/nft": "0.27.4"
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const config = getPlaywrightConfig({
4+
startCommand: 'pnpm preview',
5+
port: 3030,
6+
});
7+
8+
export default config;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# TODO: Investigate the need for this script periodically and remove once these modules are correctly resolved.
2+
3+
# This script copies `import-in-the-middle` and `@sentry/solidstart` from the E2E test project root `node_modules`
4+
# to the nitro server build output `node_modules` as these are not properly resolved in our yarn workspace/pnpm
5+
# e2e structure. Some files like `hook.mjs` and `@sentry/solidstart/solidrouter.server.js` are missing. This is
6+
# not reproducible in an external project (when pinning `@vercel/nft` to `v0.27.0` and higher).
7+
cp -r node_modules/.pnpm/import-in-the-middle@1.*/node_modules/import-in-the-middle .output/server/node_modules
8+
cp -rL node_modules/@sentry/solidstart .output/server/node_modules/@sentry
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { withSentryRouterRouting } from '@sentry/solidstart/solidrouter';
2+
import { MetaProvider, Title } from '@solidjs/meta';
3+
import { Router } from '@solidjs/router';
4+
import { FileRoutes } from '@solidjs/start/router';
5+
import { Suspense } from 'solid-js';
6+
7+
const SentryRouter = withSentryRouterRouting(Router);
8+
9+
export default function App() {
10+
return (
11+
<SentryRouter
12+
root={props => (
13+
<MetaProvider>
14+
<Title>SolidStart - with Vitest</Title>
15+
<Suspense>{props.children}</Suspense>
16+
</MetaProvider>
17+
)}
18+
>
19+
<FileRoutes />
20+
</SentryRouter>
21+
);
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @refresh reload
2+
import * as Sentry from '@sentry/solidstart';
3+
import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidrouter';
4+
import { StartClient, mount } from '@solidjs/start/client';
5+
6+
Sentry.init({
7+
// We can't use env variables here, seems like they are stripped
8+
// out in production builds.
9+
dsn: 'https://[email protected]/1337',
10+
environment: 'qa', // dynamic sampling bias to keep transactions
11+
integrations: [solidRouterBrowserTracingIntegration()],
12+
tunnel: 'http://localhost:3031/', // proxy server
13+
// Performance Monitoring
14+
tracesSampleRate: 1.0, // Capture 100% of the transactions
15+
debug: !!import.meta.env.DEBUG,
16+
});
17+
18+
mount(() => <StartClient />, document.getElementById('app')!);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @refresh reload
2+
import { StartServer, createHandler } from '@solidjs/start/server';
3+
4+
export default createHandler(() => (
5+
<StartServer
6+
document={({ assets, children, scripts }) => (
7+
<html lang="en">
8+
<head>
9+
<meta charset="utf-8" />
10+
<meta name="viewport" content="width=device-width, initial-scale=1" />
11+
<link rel="icon" href="/favicon.ico" />
12+
{assets}
13+
</head>
14+
<body>
15+
<div id="app">{children}</div>
16+
{scripts}
17+
</body>
18+
</html>
19+
)}
20+
/>
21+
));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/solidstart';
2+
3+
Sentry.init({
4+
dsn: process.env.E2E_TEST_DSN,
5+
environment: 'qa', // dynamic sampling bias to keep transactions
6+
tracesSampleRate: 1.0, // Capture 100% of the transactions
7+
tunnel: 'http://localhost:3031/', // proxy server
8+
debug: !!process.env.DEBUG,
9+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { A } from '@solidjs/router';
2+
3+
export default function BackNavigation() {
4+
return (
5+
<A id="navLink" href="/users/6">
6+
User 6
7+
</A>
8+
);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default function ClientErrorPage() {
2+
return (
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>
14+
);
15+
}
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+
}

0 commit comments

Comments
 (0)