-
-
Notifications
You must be signed in to change notification settings - Fork 778
docs: add examples #3984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
docs: add examples #3984
Conversation
|
@RihanArfan is attempting to deploy a commit to the Nitro Team on Vercel. A member of the Team first needs to authorize it. |
π WalkthroughWalkthroughAdds an examples system: Nuxt Content collection for examples, a Sandbox iframe component, examples layouts/pages, a server route to serve raw example markdown, example utilities, ~30+ example README files, and small docs package/workspace dependency updates. Changes
Estimated code review effortπ― 3 (Moderate) | β±οΈ ~25 minutes Possibly related PRs
π₯ Pre-merge checks | β 3β Passed checks (3 passed)
βοΈ Tip: You can configure your own custom pre-merge checks in the settings. β¨ Finishing touchesπ§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
π€ Fix all issues with AI agents
In `@examples/auto-imports/README.md`:
- Around line 33-42: The example contradicts the auto-import claim by explicitly
importing makeGreeting; remove the explicit import line and use makeGreeting
directly in the handler so utilities from server/utils/hello.ts are shown as
auto-imported; update the snippet containing defineHandler and the usage of
makeGreeting("Nitro") accordingly and ensure no import of makeGreeting remains
in that example.
In `@examples/cached-handler/README.md`:
- Around line 22-38: Replace the fragile string-match in shouldBypassCache with
proper query parsing: import getQuery from "nitro/h3" and change the
shouldBypassCache handler (currently using req.url.includes(...)) to accept the
event, call getQuery(event), and return a boolean based on query.skipCache
(e.g., query.skipCache === 'true' or appropriate truthy check). Update any
parameter name from { req } to event where needed and remove the
req.url.includes usage.
In `@examples/custom-error-handler/README.md`:
- Around line 26-31: The current defineErrorHandler returns raw error.message
and always a 500 Response; change it to map known error types to appropriate
status codes (e.g., validation/auth errors β 4xx, others β 5xx), sanitize the
message returned to clients (use a generic message like "Internal Server Error"
or a safe error.code) and log the full error server-side (including stack)
before creating the Response; update the handler in defineErrorHandler and the
Response creation to use the computed status and sanitized client message while
ensuring full error details are written to your logger.
In `@examples/middleware/README.md`:
- Around line 27-35: The example uses an invalid export defineMiddleware from
"nitro/h3"; update the import and wrapper to use defineEventHandler instead:
replace the import of defineMiddleware with defineEventHandler and change the
default export wrapper from defineMiddleware((event) => { ... }) to
defineEventHandler((event) => { ... }) so event.context.auth is set using the
supported Nitro API.
In `@examples/renderer/README.md`:
- Around line 27-44: The renderer function currently interpolates
user-controlled values (url.pathname and apiRes) directly into the HTML string,
creating an XSS risk; fix it by introducing/using an HTML-escape utility to
sanitize url.pathname and apiRes before interpolation (e.g., a helper that
replaces &, <, >, ", ' and / with entities) and replace the direct references in
the template with the escaped values so the Response body uses the sanitized
outputs from renderer.
In `@examples/vite-ssr-preact/README.md`:
- Around line 26-39: The server entry imports App from "./app.jsx" but renders
<Root url={url} />, causing a runtime error; update the render call in export
default.fetch to use the imported component (App) β e.g., replace the <Root
url={url} /> usage with <App url={url} /> β or alternatively change the import
to import Root from "./app.jsx" so the rendered component and import name match
(refer to the renderToReadableStream call and the App/Root identifiers).
π‘ Minor comments (20)
examples/vite-ssr-tsr-react/README.md-11-24 (1)
11-24: Add a language to the project tree code fence.MD040 flags the fence without a language. Use
text(orbash) for clarity and lint compliance.β Suggested change
-``` +```text vite-ssr-tsr-react/ βββ src/ β βββ main.tsx # App entry with router β βββ routes/ β β βββ __root.tsx # Root layout β β βββ index.tsx # Home route β βββ routeTree.gen.ts # Generated route tree β βββ assets/ β βββ main.css βββ index.html βββ vite.config.mjs βββ tsconfig.json</details> </blockquote></details> <details> <summary>examples/plugins/README.md-32-33 (1)</summary><blockquote> `32-33`: **Use a valid Content-Type value.** `html; charset=utf-8` is not a valid MIME type; use `text/html; charset=utf-8`. <details> <summary>π Suggested fix</summary> ```diff - hooks.hook("response", (event) => { - event.headers.set("content-type", "html; charset=utf-8"); - }); + hooks.hook("response", (event) => { + event.headers.set("content-type", "text/html; charset=utf-8"); + });examples/plugins/README.md-11-19 (1)
11-19: Add a language to the fenced block.Markdownlint MD040: the fence at Line 11 should specify a language (e.g.,
text) to avoid lint errors.π Suggested fix
-``` +```text plugins/ βββ server/ β βββ plugins/ β βββ test.ts # Custom plugin βββ server.ts # Main handler βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/cached-handler/README.md-11-16 (1)</summary><blockquote> `11-16`: **Add language specifier to fenced code block.** The fenced code block showing the directory structure should have a language identifier for proper rendering and consistency. <details> <summary>π Proposed fix</summary> ```diff -``` +```txt cached-handler/ βββ server.ts # Cached handler βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-ssr-tss-react/README.md-11-25 (1)</summary><blockquote> `11-25`: **Add language specifier to the project structure code block.** The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance. <details> <summary>π Proposed fix</summary> ```diff -``` +```text vite-ssr-tss-react/ βββ src/ β βββ router.tsx # Router configurationexamples/hello-world/README.md-11-16 (1)
11-16: Add language specifier to the project structure code block.The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance.
π Proposed fix
-``` +```text hello-world/ βββ server.ts # Main server entry βββ nitro.config.ts # Nitro configurationexamples/import-alias/README.md-11-20 (1)
11-20: Add language specifier to the project structure code block.The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance.
π Proposed fix
-``` +```text import-alias/ βββ server/ β βββ routes/examples/shiki/README.md-11-17 (1)
11-17: Add language specifier to the project structure code block.The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance.
π Proposed fix
-``` +```text shiki/ βββ index.html # HTML with server scripts βββ styles.css # Syntax theme stylesexamples/vite-ssr-html/README.md-11-20 (1)
11-20: Add language specifier to the project structure code block.The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance.
π Proposed fix
-``` +```text vite-ssr-html/ βββ app/ β βββ entry-server.ts # Server-side entry pointexamples/auto-imports/README.md-11-19 (1)
11-19: Add language specifier to the project structure code block.The fenced code block showing the project structure should include a language specifier for better rendering and linting compliance.
π Proposed fix
-``` +```text auto-imports/ βββ server/ β βββ utils/examples/import-alias/README.md-26-35 (1)
26-35: Remove.tsextensions from import paths.The import statements include
.tsfile extensions (lines 27-28), which is non-standard for TypeScript. TypeScript module resolution automatically locates.tsfiles without explicit extensions. Update the imports to use extensionless paths:import { sum } from "~server/utils/math"andimport { rand } from "#server/utils/math". This aligns with TypeScript best practices and the approach used in other Nitro examples.examples/server-fetch/README.md-11-18 (1)
11-18: Add a language tag to the project-structure fence.
Markdownlint (MD040) will fail because the fenced block has no language; usetext/plaintext.π‘ Suggested fix
-``` +```text server-fetch/ βββ routes/ β βββ index.ts # Main route using server fetch β βββ hello.ts # Internal API route βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-ssr-solid/README.md-11-20 (1)</summary><blockquote> `11-20`: **Add a language tag to the project-structure fence.** Markdownlint (MD040) will fail because the fenced block has no language; use `text`/`plaintext`. <details> <summary>π‘ Suggested fix</summary> ```diff -``` +```text vite-ssr-solid/ βββ src/ β βββ app.tsx # Solid app component β βββ entry-client.tsx # Client hydration entry β βββ entry-server.tsx # Server render entry β βββ styles.css βββ vite.config.mjs βββ tsconfig.json</details> </blockquote></details> <details> <summary>examples/vite-ssr-vue-router/README.md-11-25 (1)</summary><blockquote> `11-25`: **Add a language tag to the project-structure fence.** Markdownlint (MD040) will fail because the fenced block has no language; use `text`/`plaintext`. <details> <summary>π‘ Suggested fix</summary> ```diff -``` +```text vite-ssr-vue-router/ βββ app/ β βββ app.vue # Root Vue component β βββ entry-client.ts # Client hydration entry β βββ entry-server.ts # Server render entry β βββ routes.ts # Route definitions β βββ pages/ β β βββ index.vue β β βββ about.vue β β βββ not-found.vue β βββ styles.css βββ vite.config.mjs βββ tsconfig.json</details> </blockquote></details> <details> <summary>examples/websocket/README.md-11-18 (1)</summary><blockquote> `11-18`: **Add a language tag to the project-structure fence.** Markdownlint (MD040) will fail because the fenced block has no language; use `text`/`plaintext`. <details> <summary>π‘ Suggested fix</summary> ```diff -``` +```text websocket/ βββ routes/ β βββ _ws.ts # WebSocket handler βββ index.html # Client demo βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-ssr-solid/README.md-26-29 (1)</summary><blockquote> `26-29`: **Fix import path in README example.** The README.md shows `import { App } from "./app.jsx"` but the actual file is `app.tsx`. This mismatch will cause copy-paste failures. <details> <summary>Suggested fix</summary> ```diff -import { App } from "./app.jsx"; +import { App } from "./app.tsx";examples/vite-ssr-vue-router/README.md-31-48 (1)
31-48: The README example code is incomplete and cannot be run as-is.The code snippet on lines 31-48 references
hrefandhtmlTemplatewithout defining them. The actual implementation in the file defines both (lines 15-16 and 49-61), so the README should either show the complete, working code or clearly mark these as placeholders.π‘ Suggested fix
import { createSSRApp } from "vue"; import { renderToString } from "vue/server-renderer"; import { RouterView, createMemoryHistory, createRouter } from "vue-router"; import { routes } from "./routes.ts"; +const htmlTemplate = (body: string): string => `<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Vue Router Custom Framework</title> +</head> +<body> + <div id="root">${body}</div> +</body> +</html>`; + async function handler(request: Request): Promise<Response> { const app = createSSRApp(RouterView); const router = createRouter({ history: createMemoryHistory(), routes }); app.use(router); + const url = new URL(request.url); + const href = url.href.slice(url.origin.length); await router.push(href); await router.isReady(); const html = await renderToString(app); return new Response(htmlTemplate(html), {docs/.docs/pages/examples/[...slug].vue-12-33 (1)
12-33: Normalizeroute.pathbefore querying the collection.If trailing slashes are enabled,
queryCollection(...).path(${route.path}/readme)can miss the document and throw a 404. Normalize once and reuse it for the query andexampleName.π οΈ Proposed fix
-const { data: page } = await useAsyncData(kebabCase(route.path), () => - queryCollection('examples').path(`${route.path}/readme`).first(), -) +const normalizedPath = route.path.replace(/\/$/, '') +const { data: page } = await useAsyncData(kebabCase(normalizedPath), () => + queryCollection('examples').path(`${normalizedPath}/readme`).first(), +) // Extract example name from route (e.g., "/examples/vite-ssr-html" -> "vite-ssr-html") const exampleName = computed(() => { - return route.path.replace(/^\/examples\//, '') + return normalizedPath.replace(/^\/examples\//, '') })docs/.docs/components/Sandbox.vue-12-22 (1)
12-22: Guard against missingrepoto avoid broken embeds.
propsare optional, buturlalways returns a string; ifsrcandrepoare both absent youβll build a.../undefined/...URL and the loading state will never render. Consider returningundefinedwhen the required inputs are missing.π οΈ Proposed fix
const url = computed(() => { if (props.src) { return props.src } + if (!props.repo) { + return undefined + } const base = `https://stackblitz.com/github/${props.repo}/tree/${props.branch || 'main'}/${props.dir || ''}` const params = new URLSearchParams({ embed: '1', file: props.file || 'README.md', theme: colorMode.value, }) return `${base}?${params.toString()}` })docs/.docs/pages/examples/[...slug].vue-104-109 (1)
104-109: Default the Sandbox file to README.md instead of vite.config.ts.Seven examples in the repository do not have
vite.config.ts(vite-nitro-plugin, vite-ssr-preact, vite-ssr-react, vite-ssr-solid, vite-ssr-tsr-react, vite-ssr-tss-react, vite-ssr-vue-router), which will result in a broken or empty initial view in the Sandbox. Additionally, at least one example usesvite.config.mjsinstead of.ts. Since all 31 examples have README.md, default to that and override only for Vite-specific examples that require it.
π§Ή Nitpick comments (19)
examples/vite-rsc/README.md (1)
11-24: Add a language to the fenced code block.Markdownlint MD040 is triggered because the fenced block lacks a language specifier. Use
text(orbashif you want CLI styling).examples/middleware/README.md (1)
11-19: Consider adding language specifier to fenced code block.For the tree structure, you could add
textas the language specifier to silence the markdownlint warning.π Suggested change
-``` +```text middleware/ βββ server/ β βββ middleware/ β βββ auth.ts # Auth middleware βββ server.ts # Main handler βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/express/README.md (1)</summary><blockquote> `11-16`: **Consider specifying a language for the fenced code block.** While directory trees are often left plain, adding a language specifier like `text` or `plaintext` would satisfy the markdownlint rule and improve consistency. <details> <summary>π Proposed fix</summary> ```diff -``` +```text express/ βββ server.node.ts # Express app (Node.js specific) βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/database/README.md (1)</summary><blockquote> `11-19`: **Add a language to the fenced block to satisfy MD040.** Use a plain-text fence for the tree output. <details> <summary>π§ Suggested change</summary> ```diff -``` +```text database/ βββ server.ts # Database operations βββ tasks/ β βββ db/ β βββ migrate.ts # Migration task βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-nitro-plugin/README.md (1)</summary><blockquote> `11-15`: **Add a language to the fenced block to satisfy MD040.** Use a plain-text fence for the tree output. <details> <summary>π§ Suggested change</summary> ```diff -``` +```text vite-nitro-plugin/ βββ vite.config.mjs # Vite config with Nitro plugin βββ tsconfig.json</details> </blockquote></details> <details> <summary>examples/nano-jsx/README.md (1)</summary><blockquote> `11-16`: **Add a language to the fenced block to satisfy MD040.** Use a plain-text fence for the tree output. <details> <summary>π§ Suggested change</summary> ```diff -``` +```text nano-jsx/ βββ server.tsx # JSX server entry βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/fastify/README.md (1)</summary><blockquote> `11-16`: **Add a language to the fenced block to satisfy MD040.** Use a plain-text fence for the tree output. <details> <summary>π§ Suggested change</summary> ```diff -``` +```text fastify/ βββ server.node.ts # Fastify app (Node.js specific) βββ nitro.config.ts βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-trpc/README.md (1)</summary><blockquote> `11-17`: **Add a language to the fenced block to satisfy MD040.** Use a plain-text fence for the tree output. <details> <summary>π§ Suggested change</summary> ```diff -``` +```text vite-trpc/ βββ server/ β βββ trpc.ts # tRPC router and procedures βββ index.html # Frontend entry βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/custom-error-handler/README.md (2)</summary><blockquote> `11-17`: **Add language identifier to fenced code block.** The project structure code block should specify a language for better rendering and accessibility. <details> <summary>π Suggested fix</summary> ```diff -``` +```text custom-error-handler/ βββ error.ts # Custom error handler βββ server.ts # Main handler βββ nitro.config.ts βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. --- `34-36`: **Fix parameter name inconsistency.** The code uses `_event` (line 26) but the documentation refers to it as `event`. Consider updating the documentation to match the code, or vice versa. ```diff The error handler receives: - `error` - The error that was thrown -- `event` - The H3 event object +- `_event` - The H3 event object (prefixed with underscore when unused)examples/elysia/README.md (1)
11-16: Add language identifier to fenced code block.The project structure code block should specify a language for better rendering and accessibility.
π Suggested fix
-``` +```text elysia/ βββ server.ts # Elysia app βββ nitro.config.ts βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. </blockquote></details> <details> <summary>examples/runtime-config/README.md (1)</summary><blockquote> `11-17`: **Add language identifier to fenced code block.** The project structure code block should specify a language for better rendering and accessibility. <details> <summary>π Suggested fix</summary> ```diff -``` +```text runtime-config/ βββ .env # Environment variables βββ server.ts # Access runtime config βββ nitro.config.ts # Define config schema βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. </blockquote></details> <details> <summary>examples/virtual-routes/README.md (1)</summary><blockquote> `11-15`: **Add language identifier to fenced code block.** The project structure code block should specify a language for better rendering and accessibility. <details> <summary>π Suggested fix</summary> ```diff -``` +```text virtual-routes/ βββ nitro.config.ts # Virtual route configuration βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. </blockquote></details> <details> <summary>examples/renderer/README.md (2)</summary><blockquote> `11-18`: **Add language identifier to fenced code block.** The project structure code block should specify a language for better rendering and accessibility. <details> <summary>π Suggested fix</summary> ```diff -``` +```text renderer/ βββ api/ β βββ hello.ts # API route βββ renderer.ts # Custom renderer βββ nitro.config.ts βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. --- `27-27`: **Remove unused `req` parameter from type signature.** The type signature includes `req: Request` but only `url` is destructured and used in the function. ```diff -export default async function renderer({ url }: { req: Request; url: URL }) { +export default async function renderer({ url }: { url: URL }) {examples/api-routes/README.md (2)
11-22: Add language identifier to fenced code block.The project structure code block should specify a language for better rendering and accessibility.
π Suggested fix
-``` +```text api-routes/ βββ api/ β βββ hello.ts # GET /api/hello β βββ hello/ β β βββ [name].ts # GET /api/hello/:name (dynamic route) β βββ test.get.ts # GET /api/test β βββ test.post.ts # POST /api/test βββ index.html βββ nitro.config.ts βββ vite.config.ts</details> As per coding guidelines: static analysis hint MD040 indicates fenced code blocks should have a language specified. --- `44-44`: **Avoid non-null assertion operator for safer error handling.** Using the non-null assertion operator (`!`) on `event.context.params` could cause runtime errors if params are unexpectedly undefined. <details> <summary>β¨ Suggested improvement</summary> ```diff - return `Hello ${event.context.params!.name}!`; + const name = event.context.params?.name ?? 'Guest'; + return `Hello ${name}!`;Or with explicit error handling:
const name = event.context.params?.name; if (!name) { throw createError({ statusCode: 400, message: 'Name parameter required' }); } return `Hello ${name}!`;docs/package.json (1)
7-12: Confirm zod is runtimeβrequired; otherwise move to devDependencies.
If zod is only used for docs build/schema validation, it can live indevDependenciesto minimize runtime surface.Based on learnings: Avoid introducing new dependencies unless strictly necessaryβadd them to `devDependencies` unless required in runtime logic.π‘ Suggested fix
- "dependencies": { - "zod": "^4.3.6" - }, "devDependencies": { "shaders": "^2.2.43", - "undocs": "^0.4.15" + "undocs": "^0.4.15", + "zod": "^4.3.6" }docs/.docs/pages/examples/[...slug].vue (1)
35-38: Use appConfig for StackBlitz and Sandbox like GitHub links already do.GitHub links on lines 93 and 123 already use
appConfig.docs.githubandappConfig.docs.branch. Apply the same pattern to the StackBlitz URL and Sandbox component for consistency across forks and non-mainbranches.β»οΈ Suggested refactor
+const docsRepo = appConfig.docs.github || 'nitrojs/nitro' +const docsBranch = appConfig.docs.branch || 'main' const stackblitzUrl = computed(() => { - return `https://stackblitz.com/fork/github/nitrojs/nitro/tree/main/examples/${exampleName.value}` + return `https://stackblitz.com/fork/github/${docsRepo}/tree/${docsBranch}/examples/${exampleName.value}` })<Sandbox - repo="nitrojs/nitro" - branch="main" + :repo="docsRepo" + :branch="docsBranch" :dir="`examples/${exampleName}`" file="vite.config.ts" class="!mb-6" />
| ```ts [server.ts] | ||
| import { html } from "nitro/h3"; | ||
| import { defineCachedHandler } from "nitro/cache"; | ||
|
|
||
| export default defineCachedHandler( | ||
| async () => { | ||
| await new Promise((resolve) => setTimeout(resolve, 500)); | ||
|
|
||
| return html` | ||
| Response generated at ${new Date().toISOString()} (took 500ms) | ||
| <br />(<a href="?skipCache=true">skip cache</a>) | ||
| `; | ||
| }, | ||
| { | ||
| shouldBypassCache: ({ req }) => req.url.includes("skipCache=true") | ||
| } | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
Use proper query parameter parsing instead of string matching.
The shouldBypassCache implementation uses req.url.includes("skipCache=true") which is fragile and not best practice. This string matching approach will incorrectly match URLs like ?foo=skipCache=true&bar=1 or fail to match ?skipCache=1.
Consider using proper query parameter parsing with h3's getQuery utility.
β»οΈ Proposed refactor using proper query parameter parsing
```ts [server.ts]
-import { html } from "nitro/h3";
+import { html, getQuery } from "nitro/h3";
import { defineCachedHandler } from "nitro/cache";
export default defineCachedHandler(
async () => {
await new Promise((resolve) => setTimeout(resolve, 500));
return html`
Response generated at ${new Date().toISOString()} (took 500ms)
<br />(<a href="?skipCache=true">skip cache</a>)
`;
},
{
- shouldBypassCache: ({ req }) => req.url.includes("skipCache=true")
+ shouldBypassCache: (event) => {
+ const query = getQuery(event);
+ return query.skipCache === 'true';
+ }
}
);</details>
As per coding guidelines: examples should reflect best practices.
<details>
<summary>π€ Prompt for AI Agents</summary>
In @examples/cached-handler/README.md around lines 22 - 38, Replace the fragile
string-match in shouldBypassCache with proper query parsing: import getQuery
from "nitro/h3" and change the shouldBypassCache handler (currently using
req.url.includes(...)) to accept the event, call getQuery(event), and return a
boolean based on query.skipCache (e.g., query.skipCache === 'true' or
appropriate truthy check). Update any parameter name from { req } to event where
needed and remove the req.url.includes usage.
</details>
<!-- fingerprinting:phantom:triton:puma -->
<!-- This is an auto-generated comment by CodeRabbit -->
| export default defineErrorHandler((error, _event) => { | ||
| return new Response(`Custom Error Handler: ${error.message}`, { | ||
| status: 500, | ||
| headers: { "Content-Type": "text/plain" }, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid exposing raw error messages and consider error type handling.
Directly exposing error.message in the response may leak sensitive implementation details or stack traces. Additionally, returning a hardcoded 500 status for all errors doesn't account for client errors (4xx) or other error types.
Consider:
- Sanitizing error messages for production environments
- Using appropriate status codes based on error type
- Logging full error details server-side while returning generic messages to clients
π Suggested improvements
export default defineErrorHandler((error, _event) => {
+ // Determine appropriate status code
+ const statusCode = error.statusCode || error.status || 500;
+
+ // Sanitize message for production
+ const message = process.env.NODE_ENV === 'production'
+ ? 'An error occurred'
+ : `Custom Error Handler: ${error.message}`;
+
- return new Response(`Custom Error Handler: ${error.message}`, {
- status: 500,
+ return new Response(message, {
+ status: statusCode,
headers: { "Content-Type": "text/plain" },
});
});π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export default defineErrorHandler((error, _event) => { | |
| return new Response(`Custom Error Handler: ${error.message}`, { | |
| status: 500, | |
| headers: { "Content-Type": "text/plain" }, | |
| }); | |
| }); | |
| export default defineErrorHandler((error, _event) => { | |
| // Determine appropriate status code | |
| const statusCode = error.statusCode || error.status || 500; | |
| // Sanitize message for production | |
| const message = process.env.NODE_ENV === 'production' | |
| ? 'An error occurred' | |
| : `Custom Error Handler: ${error.message}`; | |
| return new Response(message, { | |
| status: statusCode, | |
| headers: { "Content-Type": "text/plain" }, | |
| }); | |
| }); |
π€ Prompt for AI Agents
In `@examples/custom-error-handler/README.md` around lines 26 - 31, The current
defineErrorHandler returns raw error.message and always a 500 Response; change
it to map known error types to appropriate status codes (e.g., validation/auth
errors β 4xx, others β 5xx), sanitize the message returned to clients (use a
generic message like "Internal Server Error" or a safe error.code) and log the
full error server-side (including stack) before creating the Response; update
the handler in defineErrorHandler and the Response creation to use the computed
status and sanitized client message while ensuring full error details are
written to your logger.
| ```ts [server/middleware/auth.ts] | ||
| import { defineMiddleware } from "nitro/h3"; | ||
|
|
||
| export default defineMiddleware((event) => { | ||
| event.context.auth = { | ||
| name: "User " + Math.round(Math.random() * 100) | ||
| }; | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Search for defineMiddleware in the codebase
rg "defineMiddleware" -i --type ts --type js -C 3Repository: nitrojs/nitro
Length of output: 457
π Script executed:
#!/bin/bash
# Check middleware definitions and exports in the repository
rg "export.*middleware|defineEventHandler|eventHandler|defineMiddleware" --type ts --type js | head -30Repository: nitrojs/nitro
Length of output: 3264
π Script executed:
#!/bin/bash
# Look for examples of middleware usage in the examples directory
fd -e md -e ts -e js "middleware" examples/ 2>/dev/null | xargs ls -la 2>/dev/nullRepository: nitrojs/nitro
Length of output: 2626
π Web query:
Nitro H3 middleware defineMiddleware API 2025
π‘ Result:
In Nitro (Nuxt/Nitro runtime) there isnβt a core defineMiddleware() API in h3/nitro. Middleware is typically defined as an event handler and auto-registered from the middleware directory:
- Nitro v3 docs: put files in
middleware/and export a handler (example usesdefineHandlerfromnitro/h3)βmiddleware should generally not return a value. (v3.nitro.build) - Nitro (stable docs): same idea under
server/middleware/, typically usingdefineEventHandler. (nitro.build)
// server/middleware/auth.ts (Nuxt/Nitro)
export default defineEventHandler((event) => {
event.context.user = { id: "123" }
})Within h3 itself, βmiddlewareβ is usually registered via app.use(...), and for lifecycle-style middleware you can use utilities like onRequest, onResponse, and onError. (h3.dev)
If what you saw was defineMiddleware({ onRequest, onBeforeResponse }), that API is from Vinxi, not Nitro/h3. (vinxi.vercel.app)
Citations:
- 1: https://v3.nitro.build/docs/routing?utm_source=openai
- 2: https://nitro.build/guide/routing?utm_source=openai
- 3: https://h3.dev/guide/basics/middleware?utm_source=openai
- 4: https://vinxi.vercel.app/api/server/response.html?utm_source=openai
Replace defineMiddleware with defineEventHandler in the middleware example.
defineMiddleware is not a valid export from "nitro/h3". The correct API for Nitro middleware is defineEventHandler. Update the import and function wrapper:
import { defineEventHandler } from "nitro/h3";
export default defineEventHandler((event) => {
event.context.auth = {
name: "User " + Math.round(Math.random() * 100)
};
});π€ Prompt for AI Agents
In `@examples/middleware/README.md` around lines 27 - 35, The example uses an
invalid export defineMiddleware from "nitro/h3"; update the import and wrapper
to use defineEventHandler instead: replace the import of defineMiddleware with
defineEventHandler and change the default export wrapper from
defineMiddleware((event) => { ... }) to defineEventHandler((event) => { ... })
so event.context.auth is set using the supported Nitro API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
defineMiddleware is a valid export
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Search for defineMiddleware export in the source code
echo "=== Searching for defineMiddleware export definition ==="
rg "export.*defineMiddleware" --type ts -C 2
echo -e "\n=== Searching for defineMiddleware function definition ==="
rg "function defineMiddleware|const defineMiddleware|export.*defineMiddleware" --type ts -C 3
echo -e "\n=== Check h3 module exports ==="
fd -e ts -e js "index" src/ | xargs rg "defineMiddleware" 2>/dev/null || echo "No matches in index files"
echo -e "\n=== Look for middleware utilities ==="
rg "defineMiddleware" src/ --type ts -A 5 -B 2
Length of output: 1016
π Script executed:
#!/bin/bash
# Look for h3 re-exports in Nitro source
echo "=== Check nitro/h3 module exports ==="
fd "h3" src/ --type f -e ts | xargs cat 2>/dev/null | head -100
echo -e "\n=== Search for all define* exports from h3 ==="
rg "from ['\"]h3['\"]" src/ --type ts -C 2
echo -e "\n=== Check type definitions for defineMiddleware ==="
rg "defineMiddleware" --type-add 'dts:*.d.ts' --type dts -C 3
echo -e "\n=== Look at h3 package itself ==="
cat node_modules/h3/dist/index.d.ts 2>/dev/null | grep -i "middleware" | head -20 || echo "h3 types not found in node_modules"
echo -e "\n=== Check if defineMiddleware is in h3 package ==="
cat node_modules/h3/dist/index.d.ts 2>/dev/null | grep "defineMiddleware" || echo "defineMiddleware not found in h3 types"
Length of output: 11737
π Script executed:
#!/bin/bash
# Check if defineMiddleware is defined anywhere in this PR's changes
echo "=== Check all files in examples/middleware ==="
fd . examples/middleware --type f -x cat {}
echo -e "\n=== Check if there's a local defineMiddleware implementation ==="
rg "defineMiddleware\s*=" examples/ --type ts --type js -C 5
echo -e "\n=== Check package versions being used ==="
cat package.json | grep -A 20 '"h3"' || echo "No h3 in root package.json"
echo -e "\n=== Check for any utils or helpers that define it ==="
fd -e ts -e js . examples/middleware | xargs cat
Length of output: 3319
π Script executed:
#!/bin/bash
# Check what nitro/h3 actually exports
echo "=== Check lib/index exports (the public API) ==="
cat lib/index.d.mts 2>/dev/null | grep -i "middleware" -A 2 -B 2 || echo "lib/index.d.mts not found"
echo -e "\n=== Check for nitro/h3 re-exports ==="
fd -e ts -e mts "h3" lib/ src/runtime/ --exec cat {} | grep -E "export.*from.*h3|defineMiddleware" | head -30
echo -e "\n=== Look for the nitro/h3 module definition ==="
rg "nitro/h3" lib/ src/ --type-add 'mts:*.mts' --type mts --type ts -l
echo -e "\n=== Check what lib/h3 exports ==="
cat lib/h3.d.mts 2>/dev/null || echo "lib/h3.d.mts not found"
echo -e "\n=== Search for middleware-related exports in lib ==="
fd . lib/ --type f | xargs grep -l "middleware" 2>/dev/null
Length of output: 310
π Web query:
h3 v2 defineMiddleware API 2025
π‘ Result:
In h3 v2 (2025 beta / 2.0.0+), defineMiddleware is essentially a type helper: it returns the input middleware unchanged at runtime. The implementation is literally function defineMiddleware(input) { return input; }. [1]
API / usage (h3 v2)
import { defineMiddleware } from "h3";
export const auth = defineMiddleware(async (event, next) => {
// ...pre
const res = await next();
// ...post
return res;
});Middleware in v2 is registered via app.use(...) (optionally with match options like method, route patterns, etc.), and you can also use the built-in middleware factories onRequest, onResponse, and onError. [2]
Related v2 migration note (important for 2025+)
In v2, the older defineRequestMiddleware / defineResponseMiddleware utilities were removed; use v2 middleware / hooks instead. [3]
Timeline context: H3 v2 beta was announced June 10, 2025. [4]
Sources
[1] https://app.unpkg.com/h3%402.0.1-rc.5/files/dist/h3.mjs
[2] https://h3.dev/guide/basics/middleware
[3] https://h3.dev/migration
[4] https://www.h3.dev/blog/v2-beta
@RihanArfan, you're absolutely right, and I apologize for the incorrect comment!
I've confirmed that defineMiddleware is a valid export in h3 v2. Since this repository uses h3 ^2.0.1-rc.11 and nitro/h3 re-exports everything from h3 (export * from "h3"), the defineMiddleware import is correct.
In h3 v2, defineMiddleware is a type helper for defining middleware with proper typing. The example code is using the correct API.
Thank you for the correction!
π°π
/)_/)
(β’α΄β’) Oops! My mistake.
/>π Learning about h3 v2...
| export default async function renderer({ url }: { req: Request; url: URL }) { | ||
| const apiRes = await fetch("/api/hello").then((res) => res.text()); | ||
|
|
||
| return new Response( | ||
| `<!DOCTYPE html> | ||
| <html> | ||
| <head> | ||
| <title>Custom Renderer</title> | ||
| </head> | ||
| <body> | ||
| <h1>Hello from custom renderer!</h1> | ||
| <p>Current path: ${url.pathname}</p> | ||
| <p>API says: ${apiRes}</p> | ||
| </body> | ||
| </html>`, | ||
| { headers: { "content-type": "text/html; charset=utf-8" } } | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Escape user-controlled data to prevent XSS vulnerabilities.
The template directly interpolates url.pathname and apiRes without escaping, which could lead to XSS attacks if these values contain HTML or JavaScript.
π Suggested fix with HTML escaping
import { fetch } from "nitro";
+function escapeHtml(text: string): string {
+ return text
+ .replace(/&/g, '&')
+ .replace(/</g, '<')
+ .replace(/>/g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
export default async function renderer({ url }: { req: Request; url: URL }) {
const apiRes = await fetch("/api/hello").then((res) => res.text());
return new Response(
`<!DOCTYPE html>
<html>
<head>
<title>Custom Renderer</title>
</head>
<body>
<h1>Hello from custom renderer!</h1>
- <p>Current path: ${url.pathname}</p>
- <p>API says: ${apiRes}</p>
+ <p>Current path: ${escapeHtml(url.pathname)}</p>
+ <p>API says: ${escapeHtml(apiRes)}</p>
</body>
</html>`,
{ headers: { "content-type": "text/html; charset=utf-8" } }
);
}π€ Prompt for AI Agents
In `@examples/renderer/README.md` around lines 27 - 44, The renderer function
currently interpolates user-controlled values (url.pathname and apiRes) directly
into the HTML string, creating an XSS risk; fix it by introducing/using an
HTML-escape utility to sanitize url.pathname and apiRes before interpolation
(e.g., a helper that replaces &, <, >, ", ' and / with entities) and replace the
direct references in the template with the escaped values so the Response body
uses the sanitized outputs from renderer.
| ```tsx [src/entry-server.tsx] | ||
| import { renderToReadableStream } from "preact-render-to-string/stream"; | ||
| import { App } from "./app.jsx"; | ||
|
|
||
| export default { | ||
| async fetch(request: Request) { | ||
| const url = new URL(request.url); | ||
| const htmlStream = renderToReadableStream(<Root url={url} />); | ||
| return new Response(htmlStream, { | ||
| headers: { "Content-Type": "text/html;charset=utf-8" }, | ||
| }); | ||
| }, | ||
| }; | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix component name mismatch.
The code imports App from "./app.jsx" but uses <Root url={url} /> in the render call. This will cause a runtime error.
π Proposed fix
import { renderToReadableStream } from "preact-render-to-string/stream";
import { App } from "./app.jsx";
export default {
async fetch(request: Request) {
const url = new URL(request.url);
- const htmlStream = renderToReadableStream(<Root url={url} />);
+ const htmlStream = renderToReadableStream(<App url={url} />);
return new Response(htmlStream, {
headers: { "Content-Type": "text/html;charset=utf-8" },
});
},
};π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```tsx [src/entry-server.tsx] | |
| import { renderToReadableStream } from "preact-render-to-string/stream"; | |
| import { App } from "./app.jsx"; | |
| export default { | |
| async fetch(request: Request) { | |
| const url = new URL(request.url); | |
| const htmlStream = renderToReadableStream(<Root url={url} />); | |
| return new Response(htmlStream, { | |
| headers: { "Content-Type": "text/html;charset=utf-8" }, | |
| }); | |
| }, | |
| }; | |
| ``` |
π€ Prompt for AI Agents
In `@examples/vite-ssr-preact/README.md` around lines 26 - 39, The server entry
imports App from "./app.jsx" but renders <Root url={url} />, causing a runtime
error; update the render call in export default.fetch to use the imported
component (App) β e.g., replace the <Root url={url} /> usage with <App url={url}
/> β or alternatively change the import to import Root from "./app.jsx" so the
rendered component and import name match (refer to the renderToReadableStream
call and the App/Root identifiers).
|
undocs merged to main we can try with nightly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
π€ Fix all issues with AI agents
In `@docs/.docs/pages/examples/`[...slug].vue:
- Around line 12-53: Normalize route.path once and reuse that normalized value
in all content queries and derived computations to avoid double slashes: create
a single normalizedPath (derived from route.path with trailing slash removed)
and use normalizedPath.value wherever the code currently uses route.path
(including inside kebabCase for useAsyncData keys,
queryCollection('examples').path(...)/queryCollectionItemSurroundings calls,
exampleName computation, prerenderRoutes/joinURL, etc.), and update any string
concatenations to use `${normalizedPath.value}/readme` so no queries produce
`//readme`.
In `@docs/.docs/server/routes/raw/examples/`[...slug].md.get.ts:
- Around line 20-27: The code assumes page.body.value, page.title, and
page.description exist and mutates page.body.value directly; fix by creating a
local safe copy of the body before modification (e.g., clone page.body or
page.body.value into a new array), guard that page.body and page.body.value are
arrays (provide empty array fallback), and guard page.title/page.description
with sensible defaults before unshifting the H1/blockquote; then call setHeader
and pass the modified clone into stringify (preserving the { type: 'minimark' }
option) so the original cached page object is not mutated.
In `@examples/cached-handler/README.md`:
- Around line 13-20: Update the README's unlabeled fenced block that contains
the directory tree by adding a language tag (e.g., change the opening "```" to
"```text") so the tree block is recognized by markdownlint; locate the fenced
block showing the lines starting with "βββ nitro.config.ts" and replace its
opening fence accordingly.
In `@examples/database/README.md`:
- Around line 31-57: The handler in server.ts (export default defineHandler) is
performing schema changes (db.sql`DROP TABLE IF EXISTS users` and db.sql`CREATE
TABLE IF NOT EXISTS users`) on every request which is unsafe; remove those
DROP/CREATE calls from the handler and either (a) move the schema setup into a
startup/init script or migration task run once at application bootstrap, or (b)
update the example handler to assume the users table exists and only demonstrate
the runtime operations (using useDatabase(), db.sql`INSERT INTO users ...`,
db.sql`SELECT * FROM users WHERE id = ${userId}`), and add a note in README that
schema creation should be done via migrations or an initialization file rather
than inside defineHandler.
- Around line 65-91: The migration task in migrate.ts uses a destructive DROP
TABLE IF EXISTS which will wipe data; update the task to avoid destructive
operations by removing the DROP TABLE call in the defineTask exported default
(the async run function that uses useDatabase and db.sql) and instead only run
CREATE TABLE IF NOT EXISTS, or rename this file to reset.ts (or add a separate
tasks/db/reset.ts) to make clear it is a destructive reset; ensure the migration
example uses an additive pattern (CREATE TABLE IF NOT EXISTS and ALTER TABLE
statements or a versioned migration approach) within the run function rather
than dropping tables.
In `@examples/fastify/README.md`:
- Around line 11-18: The fenced directory tree in README.md is missing a
language tag causing MD040; update the opening fence for the tree (the triple
backticks that precede the directory list) to include a language like text
(e.g., change ``` to ```text) so the block is recognized by markdownlint.
In `@examples/import-alias/README.md`:
- Line 28: Fix the typo in the README heading by changing the heading text "##
Importingi Using Aliases" to "## Importing Using Aliases" (update the line that
contains the heading string "## Importingi Using Aliases").
- Line 48: Remove the stray closing automd comment <!-- /automd --> that has no
matching opening directive; locate the orphaned token in the README and delete
that comment so only valid automd directives remain.
In `@examples/mono-jsx/README.md`:
- Around line 27-30: The README example text mismatches: change the "mongo-jsx"
typo to "mono-jsx" so the exported JSX component and heading match the project
name β update the snippet returned by the default export (the JSX fragment
containing <html> and <h1>) to read "Nitro + mono-jsx works!" instead of "Nitro
+ mongo-jsx works!".
In `@examples/nano-jsx/README.md`:
- Around line 11-18: Update the generated fenced code block so it includes a
language identifier (e.g., change the opening "```" to "```text") in the README
generation step; modify the automd template/config that emits the directory tree
(the template producing the triple-backtick block containing "βββ
nitro.config.ts ... βββ vite.config.ts") to emit "```text" (or
"```plaintext"/"```tree") as the fence opening so the README.md output includes
the language identifier.
In `@examples/plugins/README.md`:
- Line 38: The content-type header value set via event.headers.set is using an
invalid MIME type ("html; charset=utf-8"); update the call to
event.headers.set("content-type", "text/html; charset=utf-8") so the correct
MIME type for HTML is used (locate the event.headers.set invocation in the
example and replace the value accordingly).
In `@examples/shiki/README.md`:
- Around line 13-23: The code fence containing the directory tree in README.md
is missing a language specifier (MD040); update the opening fence for the tree
block (the triple-backtick that precedes the directory listing) to include a
language token such as text (i.e., change ``` to ```text) so markdownlint stops
flagging it and the block remains rendered the same.
In `@examples/vite-ssr-preact/README.md`:
- Line 89: Update the import for the App component so its extension matches the
actual file name: replace the current import statement importing App from
"./app.jsx" with an import that points to "./app.tsx" (or remove the explicit
extension if your bundler resolves .tsx), ensuring the symbol App imported from
app.tsx is used consistently.
π§Ή Nitpick comments (16)
examples/hono/README.md (1)
11-18: Add a language tag to the directory tree fence.markdownlint flags the fence without a language. Use
text(orplain) for the tree block.β Proposed fix
-``` +```text βββ nitro.config.ts βββ package.json βββ README.md βββ server.ts βββ tsconfig.json βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-ssr-preact/README.md (1)</summary><blockquote> `13-23`: **Add language identifier to fenced code block.** The fenced code block displaying the directory tree should specify a language identifier (e.g., `text` or leave it as plain markdown) to satisfy markdown linting rules. <details> <summary>π Proposed fix</summary> ```diff -``` +```text βββ src/ β βββ app.tsx β βββ entry-client.tsxexamples/database/README.md (1)
46-46: Use a realistic email address in the example.The empty string
''for the email field doesn't demonstrate realistic usage. Consider using'john.doe@example.com'to show proper data insertion.β¨ Suggested change
- await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; + await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', 'john.doe@example.com')`;examples/server-fetch/README.md (1)
13-22: Add a language tag to the dir-tree fence.Markdown lint flags the fenced block without a language; please add one (e.g.,
text).π οΈ Suggested fix
-``` +```text βββ routes/ β βββ hello.ts β βββ index.ts βββ nitro.config.ts βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/vite-ssr-react/README.md (1)</summary><blockquote> `13-23`: **Add a language tag to the dir-tree fence.** Markdown lint flags the fenced block without a language; please add one (e.g., `text`). <details> <summary>π οΈ Suggested fix</summary> ```diff -``` +```text βββ src/ β βββ app.tsx β βββ entry-client.tsx β βββ entry-server.tsx β βββ styles.css βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.mjs</details> </blockquote></details> <details> <summary>examples/vite-nitro-plugin/README.md (1)</summary><blockquote> `13-18`: **Add a language tag to the dir-tree fence.** Markdown lint flags the fenced block without a language; please add one (e.g., `text`). <details> <summary>π οΈ Suggested fix</summary> ```diff -``` +```text βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.mjs</details> </blockquote></details> <details> <summary>examples/vite-ssr-tss-react/README.md (1)</summary><blockquote> `13-28`: **Add a language tag to the dir-tree fence.** Markdown lint flags the fenced block without a language; please add one (e.g., `text`). <details> <summary>π οΈ Suggested fix</summary> ```diff -``` +```text βββ src/ β βββ routes/ β β βββ __root.tsx β β βββ index.tsx β βββ styles/ β β βββ app.css β βββ router.tsx β βββ routeTree.gen.ts βββ .gitignore βββ package.json βββ README.md βββ server.ts βββ tsconfig.json βββ vite.config.mjs</details> </blockquote></details> <details> <summary>examples/auto-imports/README.md (1)</summary><blockquote> `13-23`: **Add a language tag to the dir-tree fence.** Markdown lint flags the fenced block without a language; please add one (e.g., `text`). <details> <summary>π οΈ Suggested fix</summary> ```diff -``` +```text βββ server/ β βββ utils/ β βββ hello.ts βββ nitro.config.ts βββ package.json βββ README.md βββ server.ts βββ tsconfig.json βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/mono-jsx/README.md (1)</summary><blockquote> `11-18`: **Add a language tag to the dir-tree fence.** Markdown lint flags the fenced block without a language; please add one (e.g., `text`). <details> <summary>π οΈ Suggested fix</summary> ```diff -``` +```text βββ nitro.config.ts βββ package.json βββ README.md βββ server.tsx βββ tsconfig.json βββ vite.config.ts</details> </blockquote></details> <details> <summary>examples/middleware/README.md (1)</summary><blockquote> `13-23`: **Add a language to the dir-tree fenced block.** Markdownlint flags this block because it lacks a language tag. Consider using `text` (or `bash`) for consistency. <details> <summary>Suggested fix</summary> ```diff -``` +```text βββ server/ β βββ middleware/ β βββ auth.ts βββ nitro.config.ts βββ package.json βββ README.md βββ server.ts βββ tsconfig.json βββ vite.config.tsexamples/renderer/README.md (1)
13-22: Add a language to the dir-tree fenced block.Markdownlint flags this block because it lacks a language tag. Consider using
text.Suggested fix
-``` +```text βββ api/ β βββ hello.ts βββ nitro.config.ts βββ package.json βββ README.md βββ renderer.ts βββ tsconfig.json βββ vite.config.tsexamples/vite-ssr-tsr-react/README.md (1)
13-27: Add a language to the dir-tree fenced block.Markdownlint flags this block because it lacks a language tag. Consider using
text.Suggested fix
-``` +```text βββ src/ β βββ assets/ β β βββ main.css β βββ routes/ β β βββ __root.tsx β β βββ index.tsx β βββ main.tsx β βββ routeTree.gen.ts βββ index.html βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.mjsexamples/vite-trpc/README.md (1)
13-22: Add a language to the dir-tree fenced block.Markdownlint flags this block because it lacks a language tag. Consider using
text.Suggested fix
-``` +```text βββ server/ β βββ trpc.ts βββ .gitignore βββ index.html βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.tsexamples/vite-rsc/README.md (2)
13-34: Add a language to the dir-tree fenced block.Markdownlint flags this block because it lacks a language tag. Consider using
text.Suggested fix
-``` +```text βββ app/ β βββ assets/ β β βββ nitro.svg β β βββ react.svg β β βββ vite.svg β βββ framework/ β β βββ entry.browser.tsx β β βββ entry.rsc.tsx β β βββ entry.ssr.tsx β β βββ error-boundary.tsx β β βββ request.tsx β βββ action.tsx β βββ client.tsx β βββ index.css β βββ root.tsx βββ .gitignore βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.ts
170-179: Addrel="noopener noreferrer"to external links withtarget="_blank".This keeps the example aligned with security best practices.
Suggested fix
- <a href="https://vite.dev" target="_blank"> + <a href="https://vite.dev" target="_blank" rel="noopener noreferrer"> <img src={viteLogo} className="logo" alt="Vite logo" /> </a> - <a href="https://react.dev/reference/rsc/server-components" target="_blank"> + <a href="https://react.dev/reference/rsc/server-components" target="_blank" rel="noopener noreferrer"> <img src={reactLogo} className="logo react" alt="React logo" /> </a> - <a href="https://v3.nitro.build" target="_blank"> + <a href="https://v3.nitro.build" target="_blank" rel="noopener noreferrer"> <img src={nitroLogo} className="logo" alt="Nitro logo" /> </a>Based on learnings: Add examples in
examples/for new integrations and ensure they reflect best practices.examples/vite-ssr-vue-router/README.md (1)
13-29: Add a language to the dir-tree fenced block.Markdownlint flags this block because it lacks a language tag. Consider using
text.Suggested fix
-``` +```text βββ app/ β βββ pages/ β β βββ about.vue β β βββ index.vue β β βββ not-found.vue β βββ app.vue β βββ entry-client.ts β βββ entry-server.ts β βββ routes.ts β βββ shims.d.ts β βββ styles.css βββ package.json βββ README.md βββ tsconfig.json βββ vite.config.mjs
| const { data: page } = await useAsyncData(kebabCase(route.path), () => | ||
| queryCollection('examples').path(`${route.path}/readme`).first(), | ||
| ) | ||
| if (!page.value) { | ||
| throw createError({ | ||
| statusCode: 404, | ||
| statusMessage: 'Example not found', | ||
| message: `${route.path} does not exist`, | ||
| fatal: true, | ||
| }) | ||
| } | ||
|
|
||
| const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround`, () => { | ||
| return queryCollectionItemSurroundings('examples', `${route.path}/readme`, { | ||
| fields: ['description'], | ||
| }) | ||
| }) | ||
|
|
||
| // Extract example name from route (e.g., "/examples/vite-ssr-html" -> "vite-ssr-html") | ||
| const exampleName = computed(() => { | ||
| return route.path.replace(/^\/examples\//, '') | ||
| }) | ||
|
|
||
| const breadcrumb = computed(() => [ | ||
| { label: 'Examples', icon: 'i-lucide-folder-code', to: '/examples' }, | ||
| { label: page.value?.title || exampleName.value }, | ||
| ]) | ||
|
|
||
| usePageSEO({ | ||
| title: `${page.value?.title} - ${appConfig.site.name}`, | ||
| ogTitle: page.value?.title, | ||
| description: page.value?.description, | ||
| }) | ||
|
|
||
| const path = computed(() => route.path.replace(/\/$/, '')) | ||
| prerenderRoutes([joinURL('/raw', `${path.value}.md`)]) | ||
| useHead({ | ||
| link: [ | ||
| { | ||
| rel: 'alternate', | ||
| href: joinURL(appConfig.site.url, 'raw', `${path.value}.md`), | ||
| type: 'text/markdown', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
fd -t f "\[\.\.\.slug\]\.vue" docs/Repository: nitrojs/nitro
Length of output: 39
π Script executed:
find . -name "*slug*" -type f | head -20Repository: nitrojs/nitro
Length of output: 206
π Script executed:
cat -n ./docs/.docs/pages/examples/[...slug].vueRepository: nitrojs/nitro
Length of output: 4173
π Script executed:
find . -name "nuxt.config.*" -type f | head -5Repository: nitrojs/nitro
Length of output: 39
π Script executed:
fd -t f "nuxt" docs/ | head -20Repository: nitrojs/nitro
Length of output: 39
π Script executed:
fd -t f -e config "nuxt" . | head -20Repository: nitrojs/nitro
Length of output: 39
π Script executed:
ls -la docs/.docs/ | head -30Repository: nitrojs/nitro
Length of output: 510
π Script executed:
cat docs/.docs/content.config.tsRepository: nitrojs/nitro
Length of output: 582
π Script executed:
find . -maxdepth 2 -name "nuxt.config.*" -o -name ".nuxtrc*"Repository: nitrojs/nitro
Length of output: 39
π Script executed:
rg "route\.path" docs/ -A 2 -B 2Repository: nitrojs/nitro
Length of output: 39
π Script executed:
rg "trailing" docs/ -iRepository: nitrojs/nitro
Length of output: 715
π Script executed:
rg "useAsyncData" docs/.docs/pages/ -A 3 -B 1Repository: nitrojs/nitro
Length of output: 1571
π Script executed:
rg "queryCollection.*path.*route\.path" docs/Repository: nitrojs/nitro
Length of output: 39
π Script executed:
cat docs/.docs/content.config.tsRepository: nitrojs/nitro
Length of output: 582
Consolidate trailing slash normalization to avoid double slashes in content queries.
Using route.path directly in content queries (lines 12-14, 24-28) can produce //readme if a trailing slash is present. Line 46 already normalizes for the raw markdown URLβapply the same normalization consistently across all queries and path computations by normalizing once at the top.
Consolidate normalizations
+const normalizedPath = computed(() => route.path.replace(/\/$/, ''))
const { data: page } = await useAsyncData(kebabCase(route.path), () =>
- queryCollection('examples').path(`${route.path}/readme`).first(),
+ queryCollection('examples').path(`${normalizedPath.value}/readme`).first(),
)
if (!page.value) {
throw createError({
statusCode: 404,
statusMessage: 'Example not found',
message: `${route.path} does not exist`,
fatal: true,
})
}
-const { data: surround } = await useAsyncData(`${kebabCase(route.path)}-surround`, () => {
- return queryCollectionItemSurroundings('examples', `${route.path}/readme`, {
+const { data: surround } = await useAsyncData(`${kebabCase(normalizedPath.value)}-surround`, () => {
+ return queryCollectionItemSurroundings('examples', `${normalizedPath.value}/readme`, {
fields: ['description'],
})
})
// Extract example name from route (e.g., "/examples/vite-ssr-html" -> "vite-ssr-html")
const exampleName = computed(() => {
- return route.path.replace(/^\/examples\//, '')
+ return normalizedPath.value.replace(/^\/examples\//, '')
})
const breadcrumb = computed(() => [
{ label: 'Examples', icon: 'i-lucide-folder-code', to: '/examples' },
{ label: page.value?.title || exampleName.value },
])
usePageSEO({
title: `${page.value?.title} - ${appConfig.site.name}`,
ogTitle: page.value?.title,
description: page.value?.description,
})
-const path = computed(() => route.path.replace(/\/$/, ''))
-prerenderRoutes([joinURL('/raw', `${path.value}.md`)])
+prerenderRoutes([joinURL('/raw', `${normalizedPath.value}.md`)])
useHead({
link: [
{
rel: 'alternate',
- href: joinURL(appConfig.site.url, 'raw', `${path.value}.md`),
+ href: joinURL(appConfig.site.url, 'raw', `${normalizedPath.value}.md`),
type: 'text/markdown',
},
],
})π€ Prompt for AI Agents
In `@docs/.docs/pages/examples/`[...slug].vue around lines 12 - 53, Normalize
route.path once and reuse that normalized value in all content queries and
derived computations to avoid double slashes: create a single normalizedPath
(derived from route.path with trailing slash removed) and use
normalizedPath.value wherever the code currently uses route.path (including
inside kebabCase for useAsyncData keys,
queryCollection('examples').path(...)/queryCollectionItemSurroundings calls,
exampleName computation, prerenderRoutes/joinURL, etc.), and update any string
concatenations to use `${normalizedPath.value}/readme` so no queries produce
`//readme`.
| // Add title and description to the top of the page if missing | ||
| if (page.body.value[0]?.[0] !== 'h1') { | ||
| page.body.value.unshift(['blockquote', {}, page.description]) | ||
| page.body.value.unshift(['h1', {}, page.title]) | ||
| } | ||
|
|
||
| setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') | ||
| return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
find . -name "[...slug].md.get.ts" -o -name "*.md.get.ts" | head -20Repository: nitrojs/nitro
Length of output: 116
π Script executed:
git ls-files | grep -E "(routes|examples)" | head -30Repository: nitrojs/nitro
Length of output: 1149
π Script executed:
cat -n "docs/.docs/server/routes/raw/examples/[...slug].md.get.ts"Repository: nitrojs/nitro
Length of output: 1374
π Script executed:
# Search for how queryCollection is used elsewhere
rg "queryCollection" --type ts --type js -A 3 -B 1 | head -60Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if there are other examples of handling page.body
rg "page\.body" --type ts --type js -B 2 -A 2Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Search for any Nuxt Content related documentation or usage
rg "page\.body|page\.title|page\.description" -A 2 -B 2Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check for any TypeScript types or interfaces that define the page structure
rg "body\s*:\s*" --type ts | head -20Repository: nitrojs/nitro
Length of output: 1457
π Script executed:
# Look at the imports and see what queryCollection returns
rg "queryCollection" --type ts --type js -B 5 -A 10Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if there's any TypeScript definition or documentation about the page object
rg "@nuxt/content" --type ts --type jsonRepository: nitrojs/nitro
Length of output: 39
π Script executed:
# Look for any validation or checks on page properties in the codebase
rg "page\?" --type ts -B 2 -A 2Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if page.body.value is accessed elsewhere and if there are null checks
rg "\.body\.value" --type ts -B 1 -A 1Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if there are other similar route handlers in the docs
fd ".get.ts" docs/.docs/server/routes/ | head -20Repository: nitrojs/nitro
Length of output: 114
π Script executed:
# Look at the package.json to understand the `@nuxt/content` version being used
cat docs/package.json 2>/dev/null || cat package.json | grep -A 5 -B 5 "@nuxt/content"Repository: nitrojs/nitro
Length of output: 292
π Script executed:
# Check if there are defensive patterns used in other docs routes
fd ".get.ts" docs/ -x cat {} \; | head -100Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check root package.json for dependencies
cat package.json | grep -A 100 "dependencies" | head -50Repository: nitrojs/nitro
Length of output: 1599
π Script executed:
# Look for any tests or examples related to this specific route
fd "slug" docs/ --type fRepository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if there's documentation about the page structure
find docs -name "*.md" -type f | xargs grep -l "body\|title\|description" 2>/dev/null | head -5Repository: nitrojs/nitro
Length of output: 191
π Script executed:
# Check if there are any TypeScript type definitions in the project
fd "*.d.ts" docs/ | head -10Repository: nitrojs/nitro
Length of output: 342
π Script executed:
# Look for any validation or type checking of the page object
rg "page\." docs/.docs/server/ -B 2 -A 2Repository: nitrojs/nitro
Length of output: 1021
π Script executed:
# Check the Nuxt Content documentation online or look for imports
rg "from.*content" docs/.docs/server/ -B 1 -A 1Repository: nitrojs/nitro
Length of output: 274
π Script executed:
# Search for any error handling or try-catch around queryCollection calls
rg "queryCollection|\.first\(\)" docs/.docs/server/ -B 5 -A 5Repository: nitrojs/nitro
Length of output: 1723
π Script executed:
# Look for any tests or example data to understand page structure
find docs -name "*.json" -o -name "*.yaml" | xargs grep -l "title\|description" 2>/dev/null | head -5Repository: nitrojs/nitro
Length of output: 99
π Script executed:
# Check if there's a nuxt-content configuration file
fd "content.config\|nuxt.config" docs/Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check the docs directory structure to see what examples exist
ls -la docs/.docs/content/examples/ 2>/dev/null || ls -la examples/ | head -20Repository: nitrojs/nitro
Length of output: 1183
π Script executed:
# Look for any README files in examples that might show the expected structure
find examples -name "README.md" | head -5 | xargs cat | head -50Repository: nitrojs/nitro
Length of output: 1123
π Script executed:
# Check if there's any validation code or error handling in similar files
fd "\[.*\]\..*\.ts" docs/.docs/server/routes/ | xargs cat 2>/dev/nullRepository: nitrojs/nitro
Length of output: 1178
π Script executed:
# Check if there are any tests for this route
fd "slug.*test\|test.*slug" docs/Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Look for any other examples of defensive body handling in the codebase
rg "body\.value" -B 3 -A 3 --type tsRepository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if Nuxt Content is listed as a dependency anywhere
rg "@nuxt/content" --type jsonRepository: nitrojs/nitro
Length of output: 39
π Script executed:
# Check if there's any documentation about the page structure from Nuxt Content
find docs -name "*.md" | xargs grep -l "queryCollection\|page\.body\|page\.title" 2>/dev/null | head -5Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Look for any examples or tests in the repository that show how page is used
rg "stringify.*page" -B 5 -A 2Repository: nitrojs/nitro
Length of output: 39
π Script executed:
# Search for how unshift is used in the codebase
rg "unshift" --type ts -B 2 -A 2Repository: nitrojs/nitro
Length of output: 4504
π Script executed:
# Check if there are integration tests or fixtures that might show what a page object looks like
find docs -name "*.json" -o -name "*.yaml" -o -name "*.yml" | xargs grep -l "body" 2>/dev/null | head -5Repository: nitrojs/nitro
Length of output: 76
π Script executed:
# Check the Undocs source to understand if it wraps Nuxt Content
rg "undocs" package.json docs/package.jsonRepository: nitrojs/nitro
Length of output: 205
π Script executed:
# Look for any README or documentation files in the docs directory
find docs -maxdepth 2 -name "README*" -o -name "*.md" | head -10 | xargs cat 2>/dev/null | head -100Repository: nitrojs/nitro
Length of output: 4698
π Script executed:
# Let me check if there are any actual README files that might be parsed to understand the expected structure
find examples -name "README.md" -exec head -15 {} \; | head -50Repository: nitrojs/nitro
Length of output: 2223
π Script executed:
# Check if there's any type definition or interface that defines the page structure from Nuxt Content
rg "interface.*page|type.*page" docs/.docs/ --type tsRepository: nitrojs/nitro
Length of output: 105
π Script executed:
cat -n docs/.docs/content.config.tsRepository: nitrojs/nitro
Length of output: 715
π Script executed:
# Let's also check if there's any actual data in the content directory that shows what a real page looks like
find docs -path "*content*" -name "*.md" -o -name "*.json" 2>/dev/null | head -10Repository: nitrojs/nitro
Length of output: 74
π Script executed:
# Check if there's a lockfile or version info for `@nuxt/content`
grep -r "@nuxt/content" docs/pnpm-lock.yaml 2>/dev/null | head -5Repository: nitrojs/nitro
Length of output: 742
π Script executed:
# Look for any other routes that use page.title or page.description
rg "page\.(title|description|body)" docs/.docs/server/ --type tsRepository: nitrojs/nitro
Length of output: 532
π Script executed:
# Check if there are any Vue components that might show how the page object is used
rg "page\." docs/.docs/pages/ --type vue --type ts | head -20Repository: nitrojs/nitro
Length of output: 84
Add guards for optional page properties before injection.
The code assumes page.body.value, page.title, and page.description always exist, but they may be undefined depending on the README structure. This can cause crashes or render undefined values in the output. Additionally, mutating page.body.value directly modifies the cached object.
Safer implementation
// Add title and description to the top of the page if missing
- if (page.body.value[0]?.[0] !== 'h1') {
- page.body.value.unshift(['blockquote', {}, page.description])
- page.body.value.unshift(['h1', {}, page.title])
+ const body = Array.isArray(page.body?.value) ? [...page.body.value] : []
+ if (body[0]?.[0] !== 'h1') {
+ if (page.description) body.unshift(['blockquote', {}, page.description])
+ if (page.title) body.unshift(['h1', {}, page.title])
}
setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8')
- return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' })
+ return stringify({ ...page.body, value: body, type: 'minimark' }, { format: 'markdown/html' })π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Add title and description to the top of the page if missing | |
| if (page.body.value[0]?.[0] !== 'h1') { | |
| page.body.value.unshift(['blockquote', {}, page.description]) | |
| page.body.value.unshift(['h1', {}, page.title]) | |
| } | |
| setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') | |
| return stringify({ ...page.body, type: 'minimark' }, { format: 'markdown/html' }) | |
| // Add title and description to the top of the page if missing | |
| const body = Array.isArray(page.body?.value) ? [...page.body.value] : [] | |
| if (body[0]?.[0] !== 'h1') { | |
| if (page.description) body.unshift(['blockquote', {}, page.description]) | |
| if (page.title) body.unshift(['h1', {}, page.title]) | |
| } | |
| setHeader(event, 'Content-Type', 'text/markdown; charset=utf-8') | |
| return stringify({ ...page.body, value: body, type: 'minimark' }, { format: 'markdown/html' }) |
π€ Prompt for AI Agents
In `@docs/.docs/server/routes/raw/examples/`[...slug].md.get.ts around lines 20 -
27, The code assumes page.body.value, page.title, and page.description exist and
mutates page.body.value directly; fix by creating a local safe copy of the body
before modification (e.g., clone page.body or page.body.value into a new array),
guard that page.body and page.body.value are arrays (provide empty array
fallback), and guard page.title/page.description with sensible defaults before
unshifting the H1/blockquote; then call setHeader and pass the modified clone
into stringify (preserving the { type: 'minimark' } option) so the original
cached page object is not mutated.
| <!-- automd:file src="server.ts" code --> | ||
|
|
||
| ```ts [server.ts] | ||
| import { defineHandler } from "nitro/h3"; | ||
| import { useDatabase } from "nitro/database"; | ||
|
|
||
| export default defineHandler(async () => { | ||
| const db = useDatabase(); | ||
|
|
||
| // Create users table | ||
| await db.sql`DROP TABLE IF EXISTS users`; | ||
| await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; | ||
|
|
||
| // Add a new user | ||
| const userId = String(Math.round(Math.random() * 10_000)); | ||
| await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', '')`; | ||
|
|
||
| // Query for users | ||
| const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`; | ||
|
|
||
| return { | ||
| rows, | ||
| }; | ||
| }); | ||
| ``` | ||
|
|
||
| <!-- /automd --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Do not drop/recreate tables in request handlers.
The example drops and recreates the users table on every HTTP request. This is extremely dangerous and does not reflect best practices:
- Data loss risk: All existing data is destroyed on each request
- Race conditions: Concurrent requests will cause table-level conflicts
- Performance: Schema operations are expensive and block the database
Schema setup should happen once during application initialization or via migrations, not in request handlers.
As per coding guidelines, examples in examples/ must reflect best practices.
π§ Proposed fix: Separate schema setup from request handling
Option 1: Move schema setup to a separate initialization file or startup hook, then have the handler only query/insert data.
Option 2: Update the example to assume the schema already exists and focus purely on demonstrating the query API:
export default defineHandler(async () => {
const db = useDatabase();
- // Create users table
- await db.sql`DROP TABLE IF EXISTS users`;
- await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`;
-
// Add a new user
const userId = String(Math.round(Math.random() * 10_000));
await db.sql`INSERT INTO users VALUES (${userId}, 'John', 'Doe', 'john.doe@example.com')`;
// Query for users
const { rows } = await db.sql`SELECT * FROM users WHERE id = ${userId}`;
return {
rows,
};
});And add a note in the documentation that the schema should be created via the migration task shown later in the example.
π€ Prompt for AI Agents
In `@examples/database/README.md` around lines 31 - 57, The handler in server.ts
(export default defineHandler) is performing schema changes (db.sql`DROP TABLE
IF EXISTS users` and db.sql`CREATE TABLE IF NOT EXISTS users`) on every request
which is unsafe; remove those DROP/CREATE calls from the handler and either (a)
move the schema setup into a startup/init script or migration task run once at
application bootstrap, or (b) update the example handler to assume the users
table exists and only demonstrate the runtime operations (using useDatabase(),
db.sql`INSERT INTO users ...`, db.sql`SELECT * FROM users WHERE id =
${userId}`), and add a note in README that schema creation should be done via
migrations or an initialization file rather than inside defineHandler.
| <!-- automd:file src="tasks/db/migrate.ts" code --> | ||
|
|
||
| ```ts [migrate.ts] | ||
| import { defineTask } from "nitro/task"; | ||
| import { useDatabase } from "nitro/database"; | ||
|
|
||
| export default defineTask({ | ||
| meta: { | ||
| description: "Run database migrations", | ||
| }, | ||
| async run() { | ||
| const db = useDatabase(); | ||
|
|
||
| console.log("Running database migrations..."); | ||
|
|
||
| // Create users table | ||
| await db.sql`DROP TABLE IF EXISTS users`; | ||
| await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`; | ||
|
|
||
| return { | ||
| result: "Database migrations complete!", | ||
| }; | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| <!-- /automd --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reconsider dropping tables in migrations.
The migration task uses DROP TABLE IF EXISTS, which destroys all existing data. While acceptable for development/demo resets, production migrations typically use CREATE TABLE IF NOT EXISTS without the drop, or implement proper versioned migrations with schema alterations.
Consider either:
- Renaming this to
reset.tsorseed.tsto clarify it's a destructive reset operation - Showing a proper additive migration pattern that doesn't destroy data
As per coding guidelines, examples should reflect best practices.
β»οΈ Suggested approach for non-destructive migrations
export default defineTask({
meta: {
- description: "Run database migrations",
+ description: "Initialize database schema",
},
async run() {
const db = useDatabase();
- console.log("Running database migrations...");
+ console.log("Initializing database schema...");
- // Create users table
- await db.sql`DROP TABLE IF EXISTS users`;
+ // Create users table if it doesn't exist
await db.sql`CREATE TABLE IF NOT EXISTS users ("id" TEXT PRIMARY KEY, "firstName" TEXT, "lastName" TEXT, "email" TEXT)`;
return {
- result: "Database migrations complete!",
+ result: "Database schema initialized!",
};
},
});Or create a separate tasks/db/reset.ts for the destructive DROP operation used during development.
π€ Prompt for AI Agents
In `@examples/database/README.md` around lines 65 - 91, The migration task in
migrate.ts uses a destructive DROP TABLE IF EXISTS which will wipe data; update
the task to avoid destructive operations by removing the DROP TABLE call in the
defineTask exported default (the async run function that uses useDatabase and
db.sql) and instead only run CREATE TABLE IF NOT EXISTS, or rename this file to
reset.ts (or add a separate tasks/db/reset.ts) to make clear it is a destructive
reset; ensure the migration example uses an additive pattern (CREATE TABLE IF
NOT EXISTS and ALTER TABLE statements or a versioned migration approach) within
the run function rather than dropping tables.
| export default () => ( | ||
| <html> | ||
| <h1>Nitro + mongo-jsx works!</h1> | ||
| </html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix mono-jsx typo in example output.
The snippet says βmongo-jsxβ but the example and heading are βmono-jsxβ.
π οΈ Suggested fix
export default () => (
<html>
- <h1>Nitro + mongo-jsx works!</h1>
+ <h1>Nitro + mono-jsx works!</h1>
</html>
);π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export default () => ( | |
| <html> | |
| <h1>Nitro + mongo-jsx works!</h1> | |
| </html> | |
| export default () => ( | |
| <html> | |
| <h1>Nitro + mono-jsx works!</h1> | |
| </html> | |
| ); |
π€ Prompt for AI Agents
In `@examples/mono-jsx/README.md` around lines 27 - 30, The README example text
mismatches: change the "mongo-jsx" typo to "mono-jsx" so the exported JSX
component and heading match the project name β update the snippet returned by
the default export (the JSX fragment containing <html> and <h1>) to read "Nitro
+ mono-jsx works!" instead of "Nitro + mongo-jsx works!".
| export default definePlugin((nitroApp) => { | ||
| const hooks = useNitroHooks(); | ||
| hooks.hook("response", (event) => { | ||
| event.headers.set("content-type", "html; charset=utf-8"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect MIME type for HTML content.
The content-type value "html; charset=utf-8" is invalid. The correct MIME type for HTML is "text/html; charset=utf-8".
π§ Proposed fix
- event.headers.set("content-type", "html; charset=utf-8");
+ event.headers.set("content-type", "text/html; charset=utf-8");π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| event.headers.set("content-type", "html; charset=utf-8"); | |
| event.headers.set("content-type", "text/html; charset=utf-8"); |
π€ Prompt for AI Agents
In `@examples/plugins/README.md` at line 38, The content-type header value set via
event.headers.set is using an invalid MIME type ("html; charset=utf-8"); update
the call to event.headers.set("content-type", "text/html; charset=utf-8") so the
correct MIME type for HTML is used (locate the event.headers.set invocation in
the example and replace the value accordingly).
| ``` | ||
| βββ api/ | ||
| β βββ highlight.ts | ||
| βββ index.html | ||
| βββ nitro.config.ts | ||
| βββ package.json | ||
| βββ README.md | ||
| βββ styles.css | ||
| βββ tsconfig.json | ||
| βββ vite.config.ts | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a language to the dir-tree fence (MD040).
The directory tree block lacks a language specifier; markdownlint will keep flagging this.
β Suggested fix
-```
+```text
βββ api/
β βββ highlight.ts
βββ index.html
βββ nitro.config.ts
βββ package.json
βββ README.md
βββ styles.css
βββ tsconfig.json
βββ vite.config.ts
-```
+```π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ``` | |
| βββ api/ | |
| β βββ highlight.ts | |
| βββ index.html | |
| βββ nitro.config.ts | |
| βββ package.json | |
| βββ README.md | |
| βββ styles.css | |
| βββ tsconfig.json | |
| βββ vite.config.ts | |
| ``` |
π§° Tools
πͺ markdownlint-cli2 (0.20.0)
13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
π€ Prompt for AI Agents
In `@examples/shiki/README.md` around lines 13 - 23, The code fence containing the
directory tree in README.md is missing a language specifier (MD040); update the
opening fence for the tree block (the triple-backtick that precedes the
directory listing) to include a language token such as text (i.e., change ``` to
```text) so markdownlint stops flagging it and the block remains rendered the
same.
| ```tsx [entry-server.tsx] | ||
| import "./styles.css"; | ||
| import { renderToReadableStream } from "preact-render-to-string/stream"; | ||
| import { App } from "./app.jsx"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import file extension to match actual file.
The import uses .jsx extension, but the directory tree (line 15) shows the file is app.tsx. This mismatch could cause module resolution issues.
π§ Proposed fix
-import { App } from "./app.jsx";
+import { App } from "./app.tsx";π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { App } from "./app.jsx"; | |
| import { App } from "./app.tsx"; |
π€ Prompt for AI Agents
In `@examples/vite-ssr-preact/README.md` at line 89, Update the import for the App
component so its extension matches the actual file name: replace the current
import statement importing App from "./app.jsx" with an import that points to
"./app.tsx" (or remove the explicit extension if your bundler resolves .tsx),
ensuring the symbol App imported from app.tsx is used consistently.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
π€ Fix all issues with AI agents
In `@docs/package.json`:
- Around line 7-10: The package.json currently lists automd and zod under
"dependencies" but those are transitively provided by undocs (automd) and
`@nuxt/content` (zod) and are only used at build-time (see content.config.ts
referencing z from `@nuxt/content`); either remove "automd" and "zod" from
dependencies entirely, or move them to "devDependencies" with a brief comment in
package.json explaining why theyβre pinned separately (e.g., version override or
build-time tooling), and ensure any justification references the packages by
name (automd, zod) and the supplying packages (undocs, `@nuxt/content`) so future
maintainers understand the decision.
π§Ή Nitpick comments (1)
docs/package.json (1)
13-13: Using@latestwith nightly builds may impact reproducibility, but is likely intentional for development.The
undocs-nightly@latestdependency will resolve to different versions across installs since nightly builds are designed to track the latest development version. While this reduces reproducibility, it appears intentional for the docs build to always use the latest undocs features during development. Consider adding a comment to thedevDependenciesclarifying whether this is temporary for testing or permanent, and document the decision in migration notes if this represents a shift in the docs tooling approach.
| "dependencies": { | ||
| "automd": "^0.4.3", | ||
| "zod": "^4.3.6" | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Check if automd or zod are used in server routes or runtime code
# Search for automd imports in docs directory
echo "=== Searching for automd usage ==="
rg -n "from ['\"]automd['\"]|require\(['\"]automd['\"]" docs/ 2>/dev/null || echo "No automd imports found"
# Search for zod imports in docs directory
echo "=== Searching for zod usage ==="
rg -n "from ['\"]zod['\"]|require\(['\"]zod['\"]" docs/ 2>/dev/null || echo "No zod imports found"
# Check if there are server routes that might need runtime deps
echo "=== Checking for server routes ==="
find docs -type f \( -name "*.ts" -o -name "*.js" -o -name "*.tsx" -o -name "*.jsx" \) 2>/dev/null | head -20
# Check the docs package.json for scripts to understand build process
echo "=== Package.json scripts ==="
jq '.scripts' docs/package.json 2>/dev/null || cat docs/package.json | grep -A 10 '"scripts"'Repository: nitrojs/nitro
Length of output: 431
π Script executed:
#!/bin/bash
# Check automd config and usage context
echo "=== automd.config.ts content ==="
cat docs/.config/automd.config.ts 2>/dev/null || echo "File not found"
echo -e "\n=== examples server route ==="
cat docs/.docs/server/routes/raw/examples/\[...\].md.get.ts 2>/dev/null || find docs -name "*examples*.get.ts" -exec cat {} \;
echo -e "\n=== Check if zod/automd are in undocs dependencies ==="
npm ls undocs 2>/dev/null | head -5 || echo "Checking undocs package..."
# Also check if these are listed in any imports or require statements indirectly
echo -e "\n=== Check content.config.ts for schema/validation ==="
cat docs/.docs/content.config.ts 2>/dev/null || echo "File not found"
# Check the actual root package.json to see if undocs lists these as dependencies
echo -e "\n=== Root package.json undocs entry ==="
jq '.dependencies.undocs // .devDependencies.undocs' package.json 2>/dev/null || grep -A 2 "undocs" package.json | head -5Repository: nitrojs/nitro
Length of output: 982
π Script executed:
#!/bin/bash
# Check full docs/package.json to understand all dependencies
echo "=== Full docs/package.json ==="
cat docs/package.json
# Check if `@nuxt/content` is listed and includes zod
echo -e "\n=== Search for `@nuxt/content` in root package.json ==="
grep -i "nuxt/content" package.json
# Verify if standalone zod is imported anywhere in docs (including indirect)
echo -e "\n=== Check if zod is used directly (not via `@nuxt/content`) ==="
rg "import.*\{.*z.*\}" docs/ --type ts --type tsx --type js -A 2 | head -30
# Check if undocs is the main driver of automd and zod
echo -e "\n=== Check what undocs package includes ==="
npm info undocs 2>/dev/null | grep -A 5 -B 5 -i "automd\|zod" || echo "Could not fetch undocs info"Repository: nitrojs/nitro
Length of output: 755
π Script executed:
#!/bin/bash
# Final verification: Check if there's any rationale in git history or comments for these explicit deps
echo "=== Check git log for context on automd/zod addition ==="
git log --oneline -20 docs/package.json 2>/dev/null | head -10
# Check if automd is actually needed as explicit dep (maybe for CLI usage?)
echo -e "\n=== Check if automd CLI is used directly ==="
grep -r "automd" docs/ --include="*.sh" --include="*.json" 2>/dev/null | grep -v node_modules
# Verify undocs doesn't re-export automd/zod to dependent projects
echo -e "\n=== Check if docs is a package meant to be used as a dependency ==="
grep -E '"main"|"exports"|"types"' docs/package.jsonRepository: nitrojs/nitro
Length of output: 342
Remove redundant dependencies or move to devDependencies with justification.
automd and zod appear to be already managed by undocs:
undocsincludesautomd@^0.4.2(docs lists ^0.4.3)@nuxt/content(included inundocs) provides zod;content.config.tsuseszfrom@nuxt/content, not standalone zod
These are build-time tools and have no direct usage in the docs source. Either remove them entirely or move to devDependencies with documentation explaining why they're needed separately from undocs. If kept due to version pinning or explicit requirements, add a comment clarifying the rationale.
π€ Prompt for AI Agents
In `@docs/package.json` around lines 7 - 10, The package.json currently lists
automd and zod under "dependencies" but those are transitively provided by
undocs (automd) and `@nuxt/content` (zod) and are only used at build-time (see
content.config.ts referencing z from `@nuxt/content`); either remove "automd" and
"zod" from dependencies entirely, or move them to "devDependencies" with a brief
comment in package.json explaining why theyβre pinned separately (e.g., version
override or build-time tooling), and ensure any justification references the
packages by name (automd, zod) and the supplying packages (undocs,
`@nuxt/content`) so future maintainers understand the decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
π€ Fix all issues with AI agents
In `@examples/api-routes/README.md`:
- Around line 79-89: Update the import source for defineHandler in the shown
POST handler so it matches the recommended pattern used elsewhere: change the
import from "h3" to "nitro/h3" for the defineHandler import and exported handler
(the async default export using defineHandler). Also find the other repeated
code block later in the document that uses defineHandler and apply the same
import change so all examples consistently import defineHandler from "nitro/h3".
In `@examples/hello-world/README.md`:
- Line 79: Remove the orphaned closing automd HTML comment by deleting the
standalone "<!-- /automd -->" token found in the README.md (no matching "<!--
automd:... -->" opener exists); simply remove that closing comment so the file
no longer contains the stray "<!-- /automd -->" marker.
In `@examples/shiki/README.md`:
- Around line 123-142: The handler exported as export default async ({ req }: {
req: Request }) => { ... } currently accepts any method and unbounded body;
change it to accept only POST by checking req.method and returning a 405
response for other methods, and enforce a basic request-body size limit (e.g.,
read up to a configured max bytes and return 413 Payload Too Large if exceeded)
before calling req.text(); keep using highlighter.codeToHtml for rendering and
return the same HTML response on success. Ensure responses include appropriate
Content-Type and a short message for 405/413 cases.
- Around line 197-205: The template is interpolating the result of hl which
returns a Promise from serverFetch, causing `[object Promise]` if not awaited;
change the server script so hl awaits serverFetch (or make hl async and then
call `const highlighted = await hl(code)`), store the resolved HTML string
(e.g., highlighted) in a variable in the server script, and interpolate that
variable with {{{ highlighted }}} so the template receives a string rather than
a Promise.
In `@examples/vite-rsc/README.md`:
- Around line 282-287: The README references incorrect paths β update the two
list items that say "Edit src/client.tsx" and "Edit src/root.tsx" to match the
example layout by changing them to "Edit app/client.tsx" and "Edit app/root.tsx"
respectively so the path references align with the project structure used
elsewhere in the example.
- Around line 276-277: The JSX calls getServerCounter() synchronously which
returns a Promise; make the Root component async, await getServerCounter()
during render (e.g., const serverCount = await getServerCounter()), and replace
{getServerCounter()} with the awaited value (e.g., {serverCount}); keep the
existing updateServerCounter.bind(null, 1) form action unchanged and ensure the
component signature is updated to async so the awaited value is rendered instead
of a Promise.
In `@examples/vite-ssr-solid/README.md`:
- Around line 267-273: The import in entry-client.tsx references the wrong
extension ("./app.jsx") causing module resolution failures; update the import to
match the actual module filename by changing the import source to "./app.tsx" in
the entry-client.tsx file (the hydrate call and document selector remain
unchanged) so the module loader imports the correct App component.
- Around line 74-80: The import in entry-client.tsx references the wrong
extension ("./app.jsx") causing module resolution errors; update the import in
the hydrate call to point to the actual file ("./app.tsx") so hydrate(() => <App
/>, document.querySelector("#app")!) imports the App component from the correct
module.
- Around line 82-120: The import in entry-server.tsx uses the wrong file
extension ("./app.jsx") which does not match the actual module (app.tsx); update
the import statement in the module that defines fetch and calls
renderToStringAsync(() => <App />) to import "./app.tsx" so the App symbol
resolves correctly at runtime and module resolution errors are avoided.
- Around line 217-256: The import path for the App component is using the wrong
file extension; update the import statement in entry-server.tsx from "import {
App } from \"./app.jsx\";" to reference the actual file "app.tsx" so module
resolution succeeds; verify the App symbol and subsequent renderToStringAsync(()
=> <App />) usage still resolve and adjust any other import extensions (e.g.,
entry-client/entry-server asset queries) if they mismatch actual filenames.
In `@examples/vite-ssr-vue-router/README.md`:
- Around line 22-31: The devDependencies entry uses an unstable Vite version
("vite": "beta"); update the package devDependencies by replacing the "vite":
"beta" entry with the stable release (e.g., "vite": "7.3.0" or a caret-pinned
"^7.3.0") so the README example reflects best practices and stable behavior;
look for the "devDependencies" block and the "vite": "beta" key in the shown
diff to make the change.
π§Ή Nitpick comments (3)
examples/vite-trpc/README.md (1)
153-165: Clarify the in-memory counter is demo-only.The global
counterimplies stateful storage; in production this will reset on reloads and can behave unpredictably in serverless or multi-instance deployments. Consider adding a short note that this is a demo and suggest durable storage for real apps.Based on learnings: "Applies to examples/**/* : Add examples in `examples/` for new integrations and ensure they reflect best practices".βοΈ Suggested doc note
Define procedures using `t.procedure.query()` for read operations and `t.procedure.mutation()` for write operations. Export the `AppRouter` type so clients get full type inference. The default export uses tRPC's fetch adapter to handle incoming requests. + +> Note: The in-memory counter is for demonstration only. For production use, persist state in a database or durable store.Also applies to: 258-258
examples/vite-ssr-solid/README.md (1)
14-28: Consider using stable dependency versions.Using
"vite": "beta"and"nitro": "latest"may not reflect best practices for examples. Beta versions can have instability, andlatestcan introduce unexpected breaking changes. Consider pinning to specific stable versions to ensure the example remains reproducible and reliable.As per coding guidelines, examples should reflect best practices.
examples/vite-rsc/README.md (1)
260-268: Addrel="noreferrer"for externaltarget="_blank"links.This avoids reverseβtabnabbing and is a bestβpractice for examples.
As per coding guidelines: βAdd examples inexamples/for new integrations and ensure they reflect best practicesβ.Suggested update
- <a href="https://vite.dev" target="_blank"> + <a href="https://vite.dev" target="_blank" rel="noreferrer"> ... - <a href="https://react.dev/reference/rsc/server-components" target="_blank"> + <a href="https://react.dev/reference/rsc/server-components" target="_blank" rel="noreferrer"> ... - <a href="https://v3.nitro.build" target="_blank"> + <a href="https://v3.nitro.build" target="_blank" rel="noreferrer">
| ```ts [api/test.post.ts] | ||
| import { defineHandler } from "h3"; | ||
|
|
||
| export default defineHandler(async (event) => { | ||
| const body = await event.req.json(); | ||
| return { | ||
| message: "Test post handler", | ||
| body, | ||
| }; | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent import source for defineHandler.
The POST handler imports from "h3" while all other handlers import from "nitro/h3". For consistency and to demonstrate the recommended pattern, consider using "nitro/h3" throughout the documentation.
βοΈ Suggested fix
-import { defineHandler } from "h3";
+import { defineHandler } from "nitro/h3";Apply the same fix to the repeated code block at lines 155-165.
π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ```ts [api/test.post.ts] | |
| import { defineHandler } from "h3"; | |
| export default defineHandler(async (event) => { | |
| const body = await event.req.json(); | |
| return { | |
| message: "Test post handler", | |
| body, | |
| }; | |
| }); | |
| ``` |
π€ Prompt for AI Agents
In `@examples/api-routes/README.md` around lines 79 - 89, Update the import source
for defineHandler in the shown POST handler so it matches the recommended
pattern used elsewhere: change the import from "h3" to "nitro/h3" for the
defineHandler import and exported handler (the async default export using
defineHandler). Also find the other repeated code block later in the document
that uses defineHandler and apply the same import change so all examples
consistently import defineHandler from "nitro/h3".
| The `fetch` method follows the same signature as Service Workers and Cloudflare Workers. This pattern works across all deployment targets because it uses web standards. | ||
|
|
||
|
|
||
| <!-- /automd --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Orphaned closing automd comment.
Line 79 has a closing <!-- /automd --> comment without a corresponding opening <!-- automd:... --> directive. This appears to be a leftover that should be removed.
βοΈ Suggested fix
-<!-- /automd -->
-
Add the Nitro plugin to Vite and it handles the rest: dev server, hot reloading, and production builds.π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <!-- /automd --> | |
| Add the Nitro plugin to Vite and it handles the rest: dev server, hot reloading, and production builds. |
π€ Prompt for AI Agents
In `@examples/hello-world/README.md` at line 79, Remove the orphaned closing
automd HTML comment by deleting the standalone "<!-- /automd -->" token found in
the README.md (no matching "<!-- automd:... -->" opener exists); simply remove
that closing comment so the file no longer contains the stray "<!-- /automd -->"
marker.
| ```ts [api/highlight.ts] | ||
| import { createHighlighterCore } from "shiki/core"; | ||
| import { createOnigurumaEngine } from "shiki/engine/oniguruma"; | ||
|
|
||
| const highlighter = await createHighlighterCore({ | ||
| engine: createOnigurumaEngine(import("shiki/wasm")), | ||
| themes: [await import("shiki/themes/vitesse-dark.mjs")], | ||
| langs: [await import("shiki/langs/ts.mjs")], | ||
| }); | ||
|
|
||
| export default async ({ req }: { req: Request }) => { | ||
| const code = await req.text(); | ||
| const html = await highlighter.codeToHtml(code, { | ||
| lang: "ts", | ||
| theme: "vitesse-dark", | ||
| }); | ||
| return new Response(html, { | ||
| headers: { "Content-Type": "text/html; charset=utf-8" }, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a method guard and basic input limits.
The example accepts any method and unbounded body. For best-practice examples, itβs worth showing a POST-only handler and rejecting oversized payloads. As per coding guidelines, examples should reflect best practices.
β Suggested adjustment
export default async ({ req }: { req: Request }) => {
+ if (req.method !== "POST") {
+ return new Response("Method Not Allowed", { status: 405 });
+ }
const code = await req.text();
+ if (code.length > 100_000) {
+ return new Response("Payload too large", { status: 413 });
+ }
const html = await highlighter.codeToHtml(code, {
lang: "ts",
theme: "vitesse-dark",
});π€ Prompt for AI Agents
In `@examples/shiki/README.md` around lines 123 - 142, The handler exported as
export default async ({ req }: { req: Request }) => { ... } currently accepts
any method and unbounded body; change it to accept only POST by checking
req.method and returning a 405 response for other methods, and enforce a basic
request-body size limit (e.g., read up to a configured max bytes and return 413
Payload Too Large if exceeded) before calling req.text(); keep using
highlighter.codeToHtml for rendering and return the same HTML response on
success. Ensure responses include appropriate Content-Type and a short message
for 405/413 cases.
| <script server> | ||
| const hl = (code) => | ||
| serverFetch("/api/highlight", { | ||
| method: "POST", | ||
| body: code, | ||
| }); | ||
| </script> | ||
| <pre><code>{{{ hl(`console.log("π Simple is beautiful!");`) }}}</code></pre> | ||
| </div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure server-rendered output isnβt a Promise.
serverFetch returns a Promise; if Nitro server templates donβt auto-await, this will render [object Promise] instead of highlighted HTML. Consider awaiting before interpolation.
β Possible fix
- <script server>
- const hl = (code) =>
- serverFetch("/api/highlight", {
- method: "POST",
- body: code,
- });
- </script>
- <pre><code>{{{ hl(`console.log("π Simple is beautiful!");`) }}}</code></pre>
+ <script server>
+ const hl = async (code) =>
+ await serverFetch("/api/highlight", {
+ method: "POST",
+ body: code,
+ });
+ </script>
+ <pre><code>{{{ await hl(`console.log("π Simple is beautiful!");`) }}}</code></pre>Does Nitro server-side template interpolation ({{{ }}}) auto-await Promises returned from server scripts? Are `await` expressions allowed inside `{{{ }}}`?
π€ Prompt for AI Agents
In `@examples/shiki/README.md` around lines 197 - 205, The template is
interpolating the result of hl which returns a Promise from serverFetch, causing
`[object Promise]` if not awaited; change the server script so hl awaits
serverFetch (or make hl async and then call `const highlighted = await
hl(code)`), store the resolved HTML string (e.g., highlighted) in a variable in
the server script, and interpolate that variable with {{{ highlighted }}} so the
template receives a string rather than a Promise.
| <form action={updateServerCounter.bind(null, 1)}> | ||
| <button>Server Counter: {getServerCounter()}</button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
π§© Analysis chain
π Script executed:
# First, locate the vite-rsc example and explore its structure
find . -type f -path "*/vite-rsc/*" -name "*.ts*" -o -path "*/vite-rsc/*" -name "*.jsx" -o -path "*/vite-rsc/*" -name "*.tsx" | head -20Repository: nitrojs/nitro
Length of output: 441
π Script executed:
# Also search for getServerCounter definition
rg "getServerCounter" --type ts --type tsx --type js --type jsx -A 5Repository: nitrojs/nitro
Length of output: 84
π Web query:
React Server Components RSC pattern for calling async server functions in component render
π‘ Result:
Key rule
- You can
awaitasync work during render only in a Server Component. (e.g.,fetch, DB/ORM, filesystem). In frameworks like Next.js App Router, make the Server Componentasyncandawaitinside it. [2] - You cannot call a βServer Function / Server Actionβ during a Client Componentβs initial render. Server Functions (
"use server") are meant to be invoked via an action (form submit) or user interaction (e.g., button click), i.e., after render. [1]
Pattern A (recommended): Server Component does the async work during render
// app/page.tsx (Server Component)
export default async function Page() {
const posts = await getPostsFromDbOrApi(); // db query or fetch
return <PostList posts={posts} />;
}This is the standard RSC data-fetching model. [2]
Pattern B: Server Function (βuse serverβ) is passed to a Client Component and called on interaction
// app/actions.ts
export async function createPost() {
'use server'
await db.posts.create({ /* ... */ })
}
// app/page.tsx (Server Component)
import { createPost } from './actions'
import CreateButton from './CreateButton' // Client Component
export default function Page() {
return <CreateButton onCreate={createPost} />
}
// app/CreateButton.tsx
'use client'
export default function CreateButton({ onCreate }) {
return <button onClick={() => onCreate()}>Create</button>
}React/your framework turns the "use server" function into a server-executed callable reference for the Client Component. [1]
What not to do
- Donβt make Client Components
async(they canβt be async functions). [3] - Donβt call Server Functions/Actions βinlineβ during initial render; trigger them from an action/interaction instead. [1]
If you need client-rendered data, but still want Suspense-style rendering
In Next.js, a common pattern is: Server Component creates the promise (optionally cached) β Client Component consumes it via use() (often via context). Next.js documents combining React.cache + context to share a fetched promise across Server/Client boundaries. [4]
Sources: React Server Functions docs [1], Next.js fetching data in Server Components [2], Next.js βno async client componentβ error guidance [3], Next.js notes on sharing data with React.cache + context [4].
π Script executed:
# Search for getServerCounter definition without file type issues
rg "getServerCounter" -A 5 --max-count 20Repository: nitrojs/nitro
Length of output: 3172
π Script executed:
# Read the vite-rsc README to see the actual code context
head -300 ./examples/vite-rsc/README.md | tail -50Repository: nitrojs/nitro
Length of output: 1553
π Script executed:
# Also check if there's source code files
rg "function getServerCounter|const getServerCounter" -A 5Repository: nitrojs/nitro
Length of output: 739
Make the Root component async and await the server function call.
getServerCounter() is an async function but is being called synchronously in JSX. This renders a Promise instead of the actual value. Per RSC best practices and the coding guidelines requiring examples to reflect best practices, make the component async and await the call during render:
Suggested fix
-export function Root(props: { url: URL }) {
+export async function Root(props: { url: URL }) {
+ const serverCount = await getServerCounter();
return (
<div id="root">
{/* ... */}
<div className="card">
<form action={updateServerCounter.bind(null, 1)}>
- <button>Server Counter: {getServerCounter()}</button>
+ <button>Server Counter: {serverCount}</button>
</form>
</div>π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <form action={updateServerCounter.bind(null, 1)}> | |
| <button>Server Counter: {getServerCounter()}</button> | |
| export async function Root(props: { url: URL }) { | |
| const serverCount = await getServerCounter(); | |
| return ( | |
| <div id="root"> | |
| {/* ... */} | |
| <div className="card"> | |
| <form action={updateServerCounter.bind(null, 1)}> | |
| <button>Server Counter: {serverCount}</button> | |
| </form> | |
| </div> |
π€ Prompt for AI Agents
In `@examples/vite-rsc/README.md` around lines 276 - 277, The JSX calls
getServerCounter() synchronously which returns a Promise; make the Root
component async, await getServerCounter() during render (e.g., const serverCount
= await getServerCounter()), and replace {getServerCounter()} with the awaited
value (e.g., {serverCount}); keep the existing updateServerCounter.bind(null, 1)
form action unchanged and ensure the component signature is updated to async so
the awaited value is rendered instead of a Promise.
| ```tsx [src/entry-client.tsx] | ||
| import { hydrate } from "solid-js/web"; | ||
| import "./styles.css"; | ||
| import { App } from "./app.jsx"; | ||
|
|
||
| hydrate(() => <App />, document.querySelector("#app")!); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix file extension inconsistency in import statement.
Line 77 imports from "./app.jsx" but the actual file shown in the code tree is app.tsx. This mismatch will cause module resolution errors at runtime.
π Proposed fix
import { hydrate } from "solid-js/web";
import "./styles.css";
-import { App } from "./app.jsx";
+import { App } from "./app.tsx";
hydrate(() => <App />, document.querySelector("#app")!);π€ Prompt for AI Agents
In `@examples/vite-ssr-solid/README.md` around lines 74 - 80, The import in
entry-client.tsx references the wrong extension ("./app.jsx") causing module
resolution errors; update the import in the hydrate call to point to the actual
file ("./app.tsx") so hydrate(() => <App />, document.querySelector("#app")!)
imports the App component from the correct module.
| ```tsx [src/entry-server.tsx] | ||
| import { renderToStringAsync, HydrationScript } from "solid-js/web"; | ||
| import { App } from "./app.jsx"; | ||
|
|
||
| import clientAssets from "./entry-client?assets=client"; | ||
| import serverAssets from "./entry-server?assets=ssr"; | ||
|
|
||
| export default { | ||
| async fetch(req: Request): Promise<Response> { | ||
| const appHTML = await renderToStringAsync(() => <App />); | ||
| const rootHTML = await renderToStringAsync(() => <Root appHTML={appHTML} />); | ||
| return new Response(rootHTML, { | ||
| headers: { "Content-Type": "text/html" }, | ||
| }); | ||
| }, | ||
| }; | ||
|
|
||
| function Root(props: { appHTML?: string }) { | ||
| const assets = clientAssets.merge(serverAssets); | ||
| return ( | ||
| <html lang="en"> | ||
| <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| {assets.css.map((attr: any) => ( | ||
| <link key={attr.href} rel="stylesheet" {...attr} /> | ||
| ))} | ||
| {assets.js.map((attr: any) => ( | ||
| <link key={attr.href} type="modulepreload" {...attr} /> | ||
| ))} | ||
| </head> | ||
| <body> | ||
| <div id="app" innerHTML={props.appHTML || ""} /> | ||
| <HydrationScript /> | ||
| <script type="module" src={assets.entry} /> | ||
| </body> | ||
| </html> | ||
| ); | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix file extension inconsistency in import statement.
Line 84 imports from "./app.jsx" but the actual file shown in the code tree is app.tsx. This mismatch will cause module resolution errors at runtime.
π Proposed fix
import { renderToStringAsync, HydrationScript } from "solid-js/web";
-import { App } from "./app.jsx";
+import { App } from "./app.tsx";
import clientAssets from "./entry-client?assets=client";π€ Prompt for AI Agents
In `@examples/vite-ssr-solid/README.md` around lines 82 - 120, The import in
entry-server.tsx uses the wrong file extension ("./app.jsx") which does not
match the actual module (app.tsx); update the import statement in the module
that defines fetch and calls renderToStringAsync(() => <App />) to import
"./app.tsx" so the App symbol resolves correctly at runtime and module
resolution errors are avoided.
| ```tsx [entry-server.tsx] | ||
| import { renderToStringAsync, HydrationScript } from "solid-js/web"; | ||
| import { App } from "./app.jsx"; | ||
|
|
||
| import clientAssets from "./entry-client?assets=client"; | ||
| import serverAssets from "./entry-server?assets=ssr"; | ||
|
|
||
| export default { | ||
| async fetch(req: Request): Promise<Response> { | ||
| const appHTML = await renderToStringAsync(() => <App />); | ||
| const rootHTML = await renderToStringAsync(() => <Root appHTML={appHTML} />); | ||
| return new Response(rootHTML, { | ||
| headers: { "Content-Type": "text/html" }, | ||
| }); | ||
| }, | ||
| }; | ||
|
|
||
| function Root(props: { appHTML?: string }) { | ||
| const assets = clientAssets.merge(serverAssets); | ||
| return ( | ||
| <html lang="en"> | ||
| <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| {assets.css.map((attr: any) => ( | ||
| <link key={attr.href} rel="stylesheet" {...attr} /> | ||
| ))} | ||
| {assets.js.map((attr: any) => ( | ||
| <link key={attr.href} type="modulepreload" {...attr} /> | ||
| ))} | ||
| </head> | ||
| <body> | ||
| <div id="app" innerHTML={props.appHTML || ""} /> | ||
| <HydrationScript /> | ||
| <script type="module" src={assets.entry} /> | ||
| </body> | ||
| </html> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix file extension inconsistency in import statement.
Line 219 imports from "./app.jsx" but the actual file shown in the code tree is app.tsx. This mismatch will cause module resolution errors at runtime.
π Proposed fix
import { renderToStringAsync, HydrationScript } from "solid-js/web";
-import { App } from "./app.jsx";
+import { App } from "./app.tsx";
import clientAssets from "./entry-client?assets=client";π€ Prompt for AI Agents
In `@examples/vite-ssr-solid/README.md` around lines 217 - 256, The import path
for the App component is using the wrong file extension; update the import
statement in entry-server.tsx from "import { App } from \"./app.jsx\";" to
reference the actual file "app.tsx" so module resolution succeeds; verify the
App symbol and subsequent renderToStringAsync(() => <App />) usage still resolve
and adjust any other import extensions (e.g., entry-client/entry-server asset
queries) if they mismatch actual filenames.
| ```tsx [entry-client.tsx] | ||
| import { hydrate } from "solid-js/web"; | ||
| import "./styles.css"; | ||
| import { App } from "./app.jsx"; | ||
|
|
||
| hydrate(() => <App />, document.querySelector("#app")!); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix file extension inconsistency in import statement.
Line 270 imports from "./app.jsx" but the actual file shown in the code tree is app.tsx. This mismatch will cause module resolution errors at runtime.
π Proposed fix
import { hydrate } from "solid-js/web";
import "./styles.css";
-import { App } from "./app.jsx";
+import { App } from "./app.tsx";
hydrate(() => <App />, document.querySelector("#app")!);π€ Prompt for AI Agents
In `@examples/vite-ssr-solid/README.md` around lines 267 - 273, The import in
entry-client.tsx references the wrong extension ("./app.jsx") causing module
resolution failures; update the import to match the actual module filename by
changing the import source to "./app.tsx" in the entry-client.tsx file (the
hydrate call and document selector remain unchanged) so the module loader
imports the correct App component.
| "devDependencies": { | ||
| "@vitejs/plugin-vue": "^6.0.3", | ||
| "nitro": "latest", | ||
| "unhead": "^2.1.2", | ||
| "vite": "beta", | ||
| "vite-plugin-devtools-json": "^1.0.0", | ||
| "vue": "^3.5.27", | ||
| "vue-router": "^4.6.4" | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Web query:
latest stable version of Vite 2025
π‘ Result:
As of 2025, the latest stable Vite release is Vite 7.3.0 (tagged βLatestβ on the official GitHub Releases page, published Dec 15, 2025). [1]
Vite 8 was still in beta in 2025 (e.g., 8.0.0-beta.x), so itβs not the latest stable. [2]
Sources: [1] [2]
π Script executed:
# Check if there's context in the example about why beta Vite is used
cat -n examples/vite-ssr-vue-router/README.md | head -40Repository: nitrojs/nitro
Length of output: 1104
π Script executed:
# Check if there's any explanation in the README about why beta Vite is used
cat -n examples/vite-ssr-vue-router/README.md | grep -A 5 -B 5 -i "beta\|vite.*version" | head -50Repository: nitrojs/nitro
Length of output: 362
π Script executed:
# Also check if there's a comment or note about Vite beta requirements elsewhere in the file
wc -l examples/vite-ssr-vue-router/README.mdRepository: nitrojs/nitro
Length of output: 99
π Script executed:
# Search for mentions of "vite" throughout the README to understand context
rg -i "vite.*beta|beta.*vite|vite.*version" examples/vite-ssr-vue-router/README.md -A 3 -B 3Repository: nitrojs/nitro
Length of output: 251
π Script executed:
# Check the full content around the package.json section and see if there's explanation
sed -n '1,100p' examples/vite-ssr-vue-router/README.mdRepository: nitrojs/nitro
Length of output: 2236
π Script executed:
# Check git history or comments in the file about why beta Vite was chosen
git log --oneline examples/vite-ssr-vue-router/README.md | head -10Repository: nitrojs/nitro
Length of output: 92
π Script executed:
# Search the entire example directory for any comments or docs explaining beta Vite
grep -r "vite.*beta\|beta.*vite" examples/vite-ssr-vue-router/ --include="*.md" --include="*.ts" --include="*.js" -B 3 -A 3Repository: nitrojs/nitro
Length of output: 524
Use a stable Vite version instead of beta.
The example specifies "vite": "beta" (line 26), which contradicts the guideline that examples should reflect best practices. Vite 7.3.0 is the latest stable release and should be used instead. Beta versions are unstable and subject to breaking changes, leading to inconsistent behavior for users copying this template.
π€ Prompt for AI Agents
In `@examples/vite-ssr-vue-router/README.md` around lines 22 - 31, The
devDependencies entry uses an unstable Vite version ("vite": "beta"); update the
package devDependencies by replacing the "vite": "beta" entry with the stable
release (e.g., "vite": "7.3.0" or a caret-pinned "^7.3.0") so the README example
reflects best practices and stable behavior; look for the "devDependencies"
block and the "vite": "beta" key in the shown diff to make the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
π€ Fix all issues with AI agents
In `@examples/vite-ssr-tss-react/README.md`:
- Line 28: The .gitignore entry "/public/build# Sentry Config File" is malformed
because the comment is appended to the path pattern; change it so the path and
comment are separatedβfor example replace "/public/build# Sentry Config File"
with either "/public/build # Sentry Config File" or split into two lines:
"/public/build" and a separate "# Sentry Config File" comment; update the
README.md entry accordingly.
- Line 63: Replace the pre-release specifier for Vite with a stable version:
locate the dependency entry named "vite" (the "vite": "beta" line shown) in the
example's package.json/README snippet and update its version to a stable pinned
range such as "^7.3.0" so the example uses the latest stable Vite release
instead of a beta pre-release.
π§Ή Nitpick comments (3)
examples/vite-ssr-vue-router/README.md (1)
156-173: Consider adding type safety for asset loading.The asset loading logic uses
anytypes for the metadata functions and attributes. While functional, defining interfaces for these would demonstrate better TypeScript practices.π‘ Example type definitions
// Add before the handler function interface AssetMeta { css: Array<{ href: string }>; js: Array<{ href: string }>; } interface RouteAssets { default: AssetMeta; } // Then update the type casts: // Line 161: .map((fn) => (fn as () => Promise<RouteAssets>)()... // Lines 169-170: ...assets.css.map((attrs) => ({...Based on learnings, examples should reflect best practices, and strong typing is a TypeScript best practice.
examples/vite-ssr-tss-react/README.md (2)
252-252: Replace@ts-ignorewith@ts-expect-errorfor better type safety.When intentionally writing code that produces a type error (like linking to a non-existent route for 404 demonstration), use
@ts-expect-errorinstead of@ts-ignore. This provides better safety because@ts-expect-errorwill fail if the error is fixed or if no error exists, while@ts-ignoresuppresses all errors indiscriminately.β»οΈ Proposed fix
<Link - // `@ts-ignore` + // `@ts-expect-error` - Intentionally non-existent route for 404 demo to="/this-route-does-not-exist" activeProps={{ className: "font-bold" }} >As per coding guidelines, examples should reflect best practices.
491-493: Consider adding API route implementation example.The documentation mentions API routes and the generated route tree (line 143) references
src/routes/api/test, but no implementation is provided. Including a complete example would better demonstrate the full capabilities of TanStack Start with Nitro.Would you like me to suggest a sample API route implementation to include in this example?
| /build/ | ||
| /api/ | ||
| /server/build | ||
| /public/build# Sentry Config File |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix malformed .gitignore entry.
The comment is directly appended to the path pattern without proper separation. This will cause the entire line (including # Sentry Config File) to be treated as a single path pattern rather than a path with a comment.
π Proposed fix
-/public/build# Sentry Config File
+/public/build
+# Sentry Config Fileπ Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /public/build# Sentry Config File | |
| /public/build | |
| # Sentry Config File |
π€ Prompt for AI Agents
In `@examples/vite-ssr-tss-react/README.md` at line 28, The .gitignore entry
"/public/build# Sentry Config File" is malformed because the comment is appended
to the path pattern; change it so the path and comment are separatedβfor example
replace "/public/build# Sentry Config File" with either "/public/build # Sentry
Config File" or split into two lines: "/public/build" and a separate "# Sentry
Config File" comment; update the README.md entry accordingly.
| "@vitejs/plugin-react": "^5.1.2", | ||
| "tailwindcss": "^4.1.18", | ||
| "typescript": "^5.9.3", | ||
| "vite": "beta", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
fd "package.json" examples/vite-ssr-tss-react/ --type fRepository: nitrojs/nitro
Length of output: 97
π Script executed:
cat -n examples/vite-ssr-tss-react/package.jsonRepository: nitrojs/nitro
Length of output: 998
π Script executed:
web_search
What does "beta" npm tag mean for Vite and what is the current latest stable version of Vite in 2025?Repository: nitrojs/nitro
Length of output: 150
π Web query:
What does "beta" npm tag mean for Vite and what is the current latest stable version of Vite?
π‘ Result:
What the beta npm tag means (for Vite)
On npm, dist-tags (like latest, beta, next) are just human-readable aliases that point to specific published versions. Theyβre used like npm i vite@beta. npm itself gives no special meaning to beta beyond βa tag nameβ; by convention itβs used for pre-releases/unstable builds, while latest is typically used for stable releases. Also, npm i vite (no tag) installs whatever is under latest. [1]
For Vite specifically, the Vite 8 pre-release line is published under beta versions like 8.0.0-beta.x (and commonly exposed via a beta dist-tag). [2]
Current latest stable Vite version
As of January 31, 2026, the latest stable Vite release is v7.3.0 (released December 15, 2025). [3]
Sources: npm dist-tags docs [1], Vite 8 beta announcement [2], Vite GitHub releases [3].
π Script executed:
cat -n examples/vite-ssr-tss-react/README.md | head -100Repository: nitrojs/nitro
Length of output: 2822
Use stable Vite version in example.
The example uses "vite": "beta" which installs pre-release versions. All other dependencies are stable and pinned. Update to "vite": "^7.3.0" (current latest stable) to match best practices and demonstrate a production-ready setup.
π€ Prompt for AI Agents
In `@examples/vite-ssr-tss-react/README.md` at line 63, Replace the pre-release
specifier for Vite with a stable version: locate the dependency entry named
"vite" (the "vite": "beta" line shown) in the example's package.json/README
snippet and update its version to a stable pinned range such as "^7.3.0" so the
example uses the latest stable Vite release instead of a beta pre-release.
β Type of change
π Description
Adds examples section to the docs
π Checklist