chore(vscode): separate backend and frontend debug configs

Backend configs run Vite directly (not via npm run dev) so the Node
inspector owns the process that executes SSR code, with source maps
enabled for .ts server files. Frontend configs launch/attach Chrome.
Adds a Node-inspector dev task, a Full Stack compound, and DEBUGGING.md
documenting the Safari limitation and workflows.
This commit is contained in:
NeilClack
2026-09-29 13:29:55 -05:00
parent bbf4ac12ee
commit 04e1f79f37
4 changed files with 229 additions and 24 deletions
+73
View File
@@ -0,0 +1,73 @@
# Debugging the Portal in VS Code
Two independent debug targets: **backend** (server-side SvelteKit code) and
**frontend** (client-side Svelte/browser code). Pick from the Run and Debug
panel (⇧⌘D).
## Backend (server-side code)
Breakpoints work in `+page.server.ts`, `+server.ts`, `$lib/server/**`,
`hooks.server.ts`, `src/lib/db.ts`, etc.
1. Run and Debug → **"Backend: Attach to Dev Server"** (or press F5 with it
selected). It starts the dev server (Vite with the Node inspector on
`:9229`) and attaches automatically.
2. Open a server file, click in the gutter to set a breakpoint, then interact
with the app — the breakpoint hits.
Alternatives:
- **"Backend: Launch Dev Server"** — VS Code starts Vite itself and debugs it
directly (no separate task). Good if you want to stop/restart cleanly.
- **"Backend: Attach (no server start)"** — attach when the server is already
running with the inspector (used by the Full Stack compound).
> **Why not `npm run dev`?** `NODE_OPTIONS=--inspect` on `npm` puts the
> inspector on the _npm_ process, not Vite, so your breakpoints never hit. The
> tasks/configs run `./node_modules/.bin/vite` **directly** so the inspector owns
> the process that actually executes the SSR code.
## Frontend (client-side code)
Breakpoints work in `.svelte` `<script>` blocks and `$lib/**`.
1. Run and Debug → **"Frontend: Chrome"**. This launches Google Chrome, starts
the dev server if needed, and maps Vite's served modules back to your source
files. Breakpoints in the Sources panel and in VS Code both work.
2. If Chrome is already open with your logged-in session, start it once with a
debug port and use **"Frontend: Chrome (attach)"**:
```sh
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 --user-data-dir=/tmp/rw-chrome
```
### Safari
VS Code has **no Safari debugger**, so client-side breakpoints in the VS Code
editor require a Chromium browser (Chrome/Edge/Brave). You can still debug
Safari, just in Safari's own tools:
1. Safari → Settings → Advanced → enable **"Show features for web developers"**.
2. Develop menu → **Show Web Inspector** (⌥⌘I) → Sources tab.
3. Set breakpoints there. Source maps are served by Vite, so you'll see your
`.svelte` and `.ts` files, not compiled output.
For the best editor-integrated experience, install Chrome:
```sh
brew install --cask google-chrome
```
## Full stack
**"Full Stack (Backend + Chrome)"** in the compounds list starts exactly one
dev server, attaches the Node debugger, and opens Chrome — debug both sides at
once.
## Notes
- Only one process can bind `:9229` (inspector) and one `:5173` (dev server).
If a launch fails with "address already in use", stop the other server or run
`pkill -f "vite dev"`.
- The dev server task keeps its terminal open ("Terminal" panel) so you can see
Vite output and the inspector URL.
+107 -9
View File
@@ -2,26 +2,124 @@
"version": "0.2.0",
"configurations": [
{
"name": "Backend (server-side code)",
// ── BACKEND (server-side SvelteKit code) ─────────────────────────
// Recommended. Starts the Vite dev server (with the Node inspector on
// :9229) and attaches. Set breakpoints in +page.server.ts,
// +server.ts, $lib/server/**, hooks.server.ts, etc.
"name": "Backend: Attach to Dev Server",
"type": "node",
"request": "launch",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"request": "attach",
"port": 9229,
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"],
"autoAttachChildProcesses": true
"preLaunchTask": "Start Dev Server (Node Inspector)",
"restart": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"sourceMaps": true,
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
},
{
"name": "Frontend (client-side code)",
// Same, but without a preLaunchTask — used by the Full Stack compound
// so only one dev server is started.
"name": "Backend: Attach (no server start)",
"type": "node",
"request": "attach",
"port": 9229,
"cwd": "${workspaceFolder}",
"restart": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"sourceMaps": true,
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
},
{
// Starts the server itself (no separate task). Runs Vite directly so
// the inspector owns the SSR process (running via `npm run dev` puts
// the inspector on npm instead, and your breakpoints never hit).
"name": "Backend: Launch Dev Server",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/vite/bin/vite.js",
"args": ["dev", "--port", "5173", "--strictPort"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"NODE_OPTIONS": "--enable-source-maps",
"NODE_EXTRA_CA_CERTS": "./digital-ocean.crt"
},
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"sourceMaps": true
},
{
// ── FRONTEND (client-side Svelte/browser code) ───────────────────
// Launches Google Chrome wired to the Vite dev server (starts it for
// you). Set breakpoints in .svelte <script> blocks and $lib/**.
// Requires Chrome (or another Chromium browser) to be installed.
"name": "Frontend: Chrome",
"type": "chrome",
"request": "launch",
"preLaunchTask": "Start Dev Server",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"vite:///src/*": "${webRoot}/src/*"
"vite:///src/*": "${webRoot}/src/*",
"/@fs/*": "*"
},
"skipFiles": ["<node_internals>/**"]
},
{
// Chrome only, no dev-server start — used by the Full Stack compound.
"name": "Frontend: Chrome (no server start)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"vite:///src/*": "${webRoot}/src/*",
"/@fs/*": "*"
},
"skipFiles": ["<node_internals>/**"]
},
{
// Attach to a Chrome you started yourself with:
// /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
// --remote-debugging-port=9222 --user-data-dir=/tmp/rw-chrome
// Handy when Chrome is already open with your logged-in session.
"name": "Frontend: Chrome (attach)",
"type": "chrome",
"request": "attach",
"port": 9222,
"url": "http://localhost:5173/*",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"vite:///src/*": "${webRoot}/src/*",
"/@fs/*": "*"
}
},
{
// If you specifically want Edge instead of Chrome.
"name": "Frontend: Edge",
"type": "msedge",
"request": "launch",
"preLaunchTask": "Start Dev Server",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}",
"sourceMaps": true,
"sourceMapPathOverrides": {
"vite:///src/*": "${webRoot}/src/*",
"/@fs/*": "*"
}
}
],
"compounds": [
{
// Backend + frontend together, starting exactly one dev server.
"name": "Full Stack (Backend + Chrome)",
"configurations": ["Backend: Attach (no server start)", "Frontend: Chrome (no server start)"],
"preLaunchTask": "Start Dev Server (Node Inspector)",
"stopAll": true
}
]
}
+10 -1
View File
@@ -1 +1,10 @@
{}
{
// Keeps the JS debugger from stepping into node_modules / internal frames.
"debug.javascript.autoAttachFilter": "onlyWithFlag",
"debug.javascript.terminalOptions": {
"skipFiles": ["<node_internals>/**", "**/node_modules/**"]
},
// Lets the Node debugger map Vite's served modules back to your source files.
"debug.javascript.usePreview": true,
"debug.javascript.breakOnConditionalError": false
}
+39 -14
View File
@@ -3,24 +3,49 @@
"tasks": [
{
"label": "Start Dev Server",
"detail": "Vite dev server, no inspector (use with the Frontend launch configs).",
"type": "shell",
"command": "npm run dev -- --port 5173 --strictPort",
"command": "NODE_EXTRA_CA_CERTS=./digital-ocean.crt ./node_modules/.bin/vite dev --port 5173 --strictPort",
"isBackground": true,
"problemMatcher": {
"pattern": [
{
"regexp": "^.*$",
"file": 0,
"location": 0,
"message": 0
"presentation": {
"reveal": "always",
"panel": "dedicated",
"clear": true
},
"problemMatcher": [
{
"owner": "vite",
"pattern": [{ "regexp": "^.*$", "file": 0, "location": 0, "message": 0 }],
"background": {
"activeOnStart": true,
"beginsPattern": "^.*VITE.*v",
"endsPattern": "^.*Local:.*$"
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "^.*VITE.*v",
"endsPattern": "^.*localhost:.*$"
}
}
]
},
{
"label": "Start Dev Server (Node Inspector)",
"detail": "Vite dev server with the Node inspector on :9229. Run Vite directly (not via npm) so the inspector owns the process that actually executes the SSR code.",
"type": "shell",
"command": "NODE_OPTIONS='--inspect=9229 --enable-source-maps' NODE_EXTRA_CA_CERTS=./digital-ocean.crt ./node_modules/.bin/vite dev --port 5173 --strictPort",
"isBackground": true,
"presentation": {
"reveal": "always",
"panel": "dedicated",
"clear": true
},
"problemMatcher": [
{
"owner": "vite",
"pattern": [{ "regexp": "^.*$", "file": 0, "location": 0, "message": 0 }],
"background": {
"activeOnStart": true,
"beginsPattern": "^.*VITE.*v",
"endsPattern": "^.*Local:.*$"
}
}
]
}
]
}