Juniper provides a development server with hot reload capabilities. Start it with:
deno task devThe development server:
- Watches for file changes
- Automatically rebuilds the application
- Notifies connected browsers to reload
- Runs your application in a managed child process
When you save a file, the development server automatically:
- Detects the file change
- Rebuilds only the necessary parts (server, client, or both)
- Restarts the application server
- Sends a reload signal to connected browsers via Server-Sent Events (SSE)
The browser receives the reload signal through a development client script that's automatically injected in development mode.
The development server watches your project directory for changes. By default, it ignores:
- Files in
public/build/(generated output) - Files containing
.test.(test files) - Temporary files (
.tmp,.lock,.log,~) - Build configuration files (
build.ts,dev.ts)
Note: Files starting with _ (like routes/_utils.ts) are not treated as
routes, but changes to them will still trigger rebuilds since they may be
imported by route files.
You can customize file watching behavior using ignorePaths to exclude specific
directories, or watchPaths to explicitly list directories to watch:
// build.ts - Recommended: exclude specific directories
export const builder = new Builder({
projectRoot,
ignorePaths: ["./docker", "./data"],
});
// Alternative: explicitly list directories to watch
export const builder = new Builder({
projectRoot,
watchPaths: ["./routes", "./components", "./utils"],
});See Watch Paths Configuration for detailed information.
Permission Denied errors during file watching
If you see an error like:
❌ Dev server error: PermissionDenied: Permission denied (os error 13)
about ["/path/to/restricted/directory"]
This occurs when the dev server tries to watch a directory it doesn't have permission to access (commonly Docker volume directories or other system folders).
Solution: Use ignorePaths to exclude the problematic directory:
export const builder = new Builder({
projectRoot,
ignorePaths: ["./docker"],
});See Watch Paths Configuration for a complete example.
Juniper projects use Deno tasks for common operations. These are defined in your
deno.json:
| Task | Description |
|---|---|
deno task dev |
Start the development server with hot reload |
deno task build |
Build the application for development |
deno task build:prod |
Build the application for production |
deno task serve |
Run the built application |
deno task serve:prod |
Run the production build |
deno task test |
Run tests |
deno task check |
Run type checking, linting, and format checking |
Example task configuration:
{
"tasks": {
"dev": "deno run -A @udibo/juniper/dev --project-root .",
"build": "deno run -A ./build.ts",
"build:prod": "export APP_ENV=production && deno run -A ./build.ts",
"serve": "deno run -A --env-file ./main.ts",
"serve:prod": "deno run -A --env-file --env-file=.env.production ./main.ts",
"test": "deno test -A --env-file --env-file=.env.test",
"check": "deno check && deno lint && deno fmt --check"
}
}Use Deno's built-in debugger with the --inspect or --inspect-brk flags:
# Start with debugger (doesn't wait)
deno run --inspect -A ./main.ts
# Start and wait for debugger to connect
deno run --inspect-brk -A ./main.tsConnect using Chrome DevTools:
- Open
chrome://inspectin Chrome - Click "Configure" and add
localhost:9229 - Your Deno process should appear under "Remote Target"
- Click "inspect" to open DevTools
Or use VS Code's built-in debugger (see IDE Integration below).
Use console.log for debugging. The Hono logger middleware logs all requests:
// routes/main.ts
import { Hono } from "hono";
import { logger } from "hono/logger";
const app = new Hono();
app.use(logger()); // Logs all requests
export default app;In development, Juniper preserves error stack traces and includes them in error
responses. The HttpError class supports an expose option to control what
information is shown to users:
import { HttpError } from "@udibo/juniper";
throw new HttpError(500, "Internal error details", {
expose: false, // Don't expose this message to users
exposedMessage: "Something went wrong", // Show this instead
});Install the React Developer Tools browser extension to:
- Inspect the React component tree
- View and edit component props and state
- Profile component render performance
- Debug component hierarchies
Use the browser's Network tab to:
- Monitor API requests and responses
- Debug loader and action data
- Check for failed requests
- Analyze request timing
In development, Juniper generates source maps for easier debugging. You can:
- Set breakpoints in your TypeScript source files
- Step through original code (not bundled output)
- View original variable names and function names
Source maps are disabled in production for smaller bundle sizes.
Recommended Extensions:
- Deno - Official Deno extension for IntelliSense, linting, and formatting
Enable Deno for your workspace:
Create .vscode/settings.json:
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"editor.formatOnSave": true,
"editor.defaultFormatter": "denoland.vscode-deno"
}Debugging configuration:
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Deno: Run",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": ["run", "-A", "--inspect-brk", "./main.ts"],
"attachSimplePort": 9229
},
{
"name": "Deno: Test",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "deno",
"runtimeArgs": ["test", "-A", "--inspect-brk"],
"attachSimplePort": 9229
}
]
}Tasks configuration:
Create .vscode/tasks.json:
{
"version": "2.0.0",
"tasks": [
{
"label": "dev",
"type": "shell",
"command": "deno task dev",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "test",
"type": "shell",
"command": "deno task test",
"problemMatcher": [],
"group": "test"
}
]
}JetBrains IDEs (WebStorm, IntelliJ IDEA) have built-in Deno support:
- Go to Settings/Preferences > Languages & Frameworks > Deno
- Check Enable Deno for this project
- Set the Deno executable path if not auto-detected
Run Configurations:
- Go to Run > Edit Configurations
- Add a new Deno configuration
- Set the script to
main.ts - Add arguments:
-A --env-file
Debugging:
- Create a Deno run configuration
- Add
--inspect-brkto the arguments - Set breakpoints in your code
- Click the Debug button
Next: Routing - File-based routing and data loading
Related topics: