Skip to content

Commit 6416fb5

Browse files
committed
Adding Sentry support
1 parent 2bff595 commit 6416fb5

File tree

9 files changed

+116
-20
lines changed

9 files changed

+116
-20
lines changed

src/add-ons.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type AddOn = {
4242
phase: 'setup' | 'add-on'
4343
shadcnComponents?: Array<string>
4444
warning?: string
45+
dependsOn?: Array<string>
4546
}
4647

4748
function isDirectory(path: string): boolean {
@@ -83,3 +84,29 @@ export async function getAllAddOns(): Promise<Array<AddOn>> {
8384
}
8485
return addOns
8586
}
87+
88+
// Turn the list of chosen add-on IDs into a final list of add-ons by resolving dependencies
89+
export async function finalizeAddOns(
90+
chosenAddOnIDs: Array<string>,
91+
): Promise<Array<AddOn>> {
92+
const finalAddOnIDs = new Set(chosenAddOnIDs)
93+
94+
const addOns = await getAllAddOns()
95+
96+
for (const addOnID of finalAddOnIDs) {
97+
const addOn = addOns.find((a) => a.id === addOnID)
98+
if (!addOn) {
99+
throw new Error(`Add-on ${addOnID} not found`)
100+
}
101+
102+
for (const dependsOn of addOn.dependsOn || []) {
103+
const dep = addOns.find((a) => a.id === dependsOn)
104+
if (!dep) {
105+
throw new Error(`Dependency ${dependsOn} not found`)
106+
}
107+
finalAddOnIDs.add(dep.id)
108+
}
109+
}
110+
111+
return [...finalAddOnIDs].map((id) => addOns.find((a) => a.id === id)!)
112+
}

src/options.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
getPackageManager,
1414
} from './package-manager.js'
1515
import { CODE_ROUTER, FILE_ROUTER } from './constants.js'
16-
import { getAllAddOns } from './add-ons.js'
16+
import { finalizeAddOns, getAllAddOns } from './add-ons.js'
1717

1818
import type { CliOptions, Options } from './types.js'
1919

@@ -166,9 +166,7 @@ export async function promptForOptions(
166166
process.exit(0)
167167
}
168168

169-
options.chosenAddOns = addOns.filter((addOn) =>
170-
selectedAddOns.includes(addOn.id),
171-
)
169+
options.chosenAddOns = await finalizeAddOns(selectedAddOns)
172170
options.tailwind = true
173171
} else {
174172
options.chosenAddOns = []
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Your Sentry DSN (from your Sentry account)
2+
VITE_SENTRY_DSN=
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as fs from 'node:fs/promises'
2+
import { createFileRoute } from '@tanstack/react-router'
3+
import { createServerFn } from '@tanstack/start'
4+
5+
export const Route = createFileRoute('/demo/sentry/bad-server-func')({
6+
component: RouteComponent,
7+
})
8+
9+
const badServerFunc = createServerFn({
10+
method: 'GET',
11+
}).handler(async () => {
12+
await fs.readFile('./doesnt-exist', 'utf-8')
13+
return true
14+
})
15+
16+
function RouteComponent() {
17+
return (
18+
<div className="p-4">
19+
<button
20+
onClick={async () => {
21+
await badServerFunc()
22+
}}
23+
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
24+
>
25+
I Don't Work, And That Should Fire an Error to Sentry
26+
</button>
27+
</div>
28+
)
29+
}

templates/add-ons/sentry/info.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"name": "Sentry",
3+
"phase": "setup",
4+
"description": "Add Sentry for error monitoring and crash reporting (requires Start).",
5+
"link": "https://sentry.com/",
6+
"routes": [
7+
{
8+
"url": "/demo/sentry/bad-server-func",
9+
"name": "Sentry"
10+
}
11+
],
12+
"dependsOn": ["start"]
13+
}

templates/add-ons/sentry/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"@sentry/react": "^8.50.0"
4+
}
5+
}

templates/add-ons/start/assets/src/router.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { createRouter as createTanstackRouter } from '@tanstack/react-router'<% if (addOnEnabled.sentry) { %>
2+
import * as Sentry from '@sentry/react'
3+
<% } %>
4+
5+
// Import the generated route tree
6+
import { routeTree } from './routeTree.gen'
7+
8+
import './styles.css'
9+
10+
// Create a new router instance
11+
export const createRouter = () => {
12+
const router = createTanstackRouter({ routeTree })
13+
<% if (addOnEnabled.sentry) { %>
14+
Sentry.init({
15+
dsn: import.meta.env.VITE_SENTRY_DSN,
16+
integrations: [Sentry.replayIntegration(), Sentry.tanstackRouterBrowserTracingIntegration(router)],
17+
18+
// Setting a sample rate is required for sending performance data.
19+
// We recommend adjusting this value in production, or using tracesSampler
20+
// for finer control.
21+
tracesSampleRate: 1.0,
22+
replaysSessionSampleRate: 1.0, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
23+
replaysOnErrorSampleRate: 1.0,
24+
});
25+
<% } %>
26+
return router;
27+
}
28+
29+
// Register the router instance for type safety
30+
declare module '@tanstack/react-router' {
31+
interface Register {
32+
router: ReturnType<typeof createRouter>
33+
}
34+
}

templates/add-ons/start/info.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
{
99
"url": "/demo/start/server-funcs",
1010
"name": "Start - Server Functions"
11+
},
12+
{
13+
"url": "/demo/start/api-request",
14+
"name": "Start - API Request"
1115
}
1216
],
1317
"shadcnComponents": ["button", "input"]

0 commit comments

Comments
 (0)