From ecd03d50bb3ad741de2962f037bc182a8cb5be53 Mon Sep 17 00:00:00 2001 From: Justin White Date: Fri, 5 Jun 2026 10:32:52 -0500 Subject: [PATCH 1/2] fix(route-meta): add order:pre to route-meta plugin hooks --- src/build/plugins/route-meta.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/build/plugins/route-meta.ts b/src/build/plugins/route-meta.ts index bf31ff43ff..4a099a6d9b 100644 --- a/src/build/plugins/route-meta.ts +++ b/src/build/plugins/route-meta.ts @@ -12,6 +12,7 @@ export async function routeMeta(nitro: Nitro) { return { name: "nitro:route-meta", resolveId: { + order: "pre", // eslint-disable-next-line no-control-regex filter: { id: /^(?!\u0000)(.+)\?meta$/ }, async handler(id, importer, resolveOpts) { @@ -25,6 +26,7 @@ export async function routeMeta(nitro: Nitro) { }, }, load: { + order: "pre", filter: { id: new RegExp(`^${escapeRegExp(PREFIX)}`), }, @@ -40,6 +42,7 @@ export async function routeMeta(nitro: Nitro) { }, }, transform: { + order: "pre", filter: { id: new RegExp(`^${escapeRegExp(PREFIX)}`), }, From f9b00f33a3c90eecff30f3b69d7f2b4fe7451309 Mon Sep 17 00:00:00 2001 From: Justin White Date: Fri, 5 Jun 2026 13:27:20 -0500 Subject: [PATCH 2/2] test(vite): add openapi regression test for defineRouteMeta --- test/vite/openapi-fixture/api/meta/test.ts | 14 +++++ test/vite/openapi-fixture/vite.config.ts | 17 +++++++ test/vite/openapi.test.ts | 59 ++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 test/vite/openapi-fixture/api/meta/test.ts create mode 100644 test/vite/openapi-fixture/vite.config.ts create mode 100644 test/vite/openapi.test.ts diff --git a/test/vite/openapi-fixture/api/meta/test.ts b/test/vite/openapi-fixture/api/meta/test.ts new file mode 100644 index 0000000000..f61afc4644 --- /dev/null +++ b/test/vite/openapi-fixture/api/meta/test.ts @@ -0,0 +1,14 @@ +import { defineRouteMeta } from "nitro"; + +defineRouteMeta({ + openAPI: { + tags: ["test"], + description: "Vite builder route description", + parameters: [{ in: "query", name: "vite-test", required: true }], + responses: { + 200: { description: "result" }, + }, + }, +}); + +export default () => ({ status: "OK" }); diff --git a/test/vite/openapi-fixture/vite.config.ts b/test/vite/openapi-fixture/vite.config.ts new file mode 100644 index 0000000000..337d51d4ac --- /dev/null +++ b/test/vite/openapi-fixture/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vite"; +import { nitro } from "nitro/vite"; + +export default defineConfig({ + plugins: [ + nitro({ + serverDir: "./", + experimental: { openAPI: true }, + openAPI: { + meta: { + title: "OpenAPI Test API", + description: "OpenAPI Test Description", + }, + }, + }), + ], +}); diff --git a/test/vite/openapi.test.ts b/test/vite/openapi.test.ts new file mode 100644 index 0000000000..a21eab4b6f --- /dev/null +++ b/test/vite/openapi.test.ts @@ -0,0 +1,59 @@ +import { fileURLToPath } from "node:url"; +import type { ViteDevServer } from "vite"; +import { describe, test, expect, beforeAll, afterAll } from "vitest"; + +const { createServer } = (await import( + process.env.NITRO_VITE_PKG || "vite" +)) as typeof import("vite"); + +describe("openapi", () => { + let server: ViteDevServer; + let serverURL: string; + + const rootDir = fileURLToPath(new URL("./openapi-fixture", import.meta.url)); + + beforeAll(async () => { + server = await createServer({ root: rootDir }); + await server.listen("0" as unknown as number); + const addr = server.httpServer?.address() as { + port: number; + address: string; + family: string; + }; + serverURL = `http://${addr.family === "IPv6" ? `[${addr.address}]` : addr.address}:${addr.port}`; + }, 30_000); + + afterAll(async () => { + await server?.close(); + }); + + test("extracts defineRouteMeta", async () => { + const res = await fetch(`${serverURL}/_openapi.json`); + const spec: Record = await res.json(); + + expect(spec.openapi).toMatch(/^3\.\d+\.\d+$/); + expect(spec.paths?.["/api/meta/test"]).toBeDefined(); + expect(spec.paths["/api/meta/test"].get.description).toBe("Vite builder route description"); + expect(spec.paths["/api/meta/test"].get.tags).toEqual(["test"]); + + const routeRes = await fetch(`${serverURL}/api/meta/test`); + expect(routeRes.status).toBe(200); + expect(await routeRes.json()).toEqual({ status: "OK" }); + }); + + test("serves swagger UI with meta", async () => { + const res = await fetch(`${serverURL}/_swagger`); + expect(res.status).toBe(200); + const html = await res.text(); + expect(html).toContain("OpenAPI Test API"); + expect(html).toContain(' { + const res = await fetch(`${serverURL}/_scalar`); + expect(res.status).toBe(200); + const html = await res.text(); + expect(html).toContain("OpenAPI Test API"); + expect(html).toContain('