diff --git a/packages/vinxi-references/server.js b/packages/vinxi-references/server.js index 39b568fd..5d9eb0c9 100644 --- a/packages/vinxi-references/server.js +++ b/packages/vinxi-references/server.js @@ -1,5 +1,5 @@ import { readFileSync } from "fs"; -import { join } from "vinxi/lib/path"; +import { handlerModule, join } from "vinxi/lib/path"; import { CLIENT_REFERENCES_MANIFEST, hash } from "./constants.js"; @@ -37,7 +37,7 @@ export function server({ ); input = { - entry: "#vinxi/handler", + entry: handlerModule(router), ...Object.fromEntries( reactClientManifest.server.map((key) => { return [`c_${hash(key)}`, key]; diff --git a/packages/vinxi/lib/build.js b/packages/vinxi/lib/build.js index f7810b5c..cd9319e3 100644 --- a/packages/vinxi/lib/build.js +++ b/packages/vinxi/lib/build.js @@ -10,7 +10,7 @@ import { createIncomingMessage, createServerResponse } from "./http-stream.js"; import invariant from "./invariant.js"; import { consola, withLogger } from "./logger.js"; import { createSPAManifest } from "./manifest/spa-manifest.js"; -import { join, relative } from "./path.js"; +import { handlerModule, join, relative, virtualId } from "./path.js"; import { config } from "./plugins/config.js"; import { manifest } from "./plugins/manifest.js"; import { routes } from "./plugins/routes.js"; @@ -79,6 +79,7 @@ export async function createBuild(app, buildConfig) { ...app.config.routers .map((router) => { if (router.mode === "handler") { + invariant(router.handler, "Missing router.handler"); const bundlerManifest = JSON.parse( readFileSync( join(router.outDir, router.base, ".vite", "manifest.json"), @@ -86,12 +87,14 @@ export async function createBuild(app, buildConfig) { ), ); + const virtualHandlerId = virtualId(handlerModule(router)); + const handler = join( router.outDir, router.base, bundlerManifest[ - "virtual:#vinxi/handler" in bundlerManifest - ? "virtual:#vinxi/handler" + virtualHandlerId in bundlerManifest + ? virtualHandlerId : relative(app.config.root, router.handler) ].file, ); @@ -121,6 +124,7 @@ export async function createBuild(app, buildConfig) { .map((router) => { if (router.mode === "static") { return { + // @ts-expect-error dir: router.dir, baseURL: router.base, passthrough: true, @@ -291,7 +295,7 @@ async function createViteBuild(config) { /** * * @param {import("./app.js").App} app - * @param {Exclude} router + * @param {import("./router-mode.js").Router} router */ async function createRouterBuild(app, router) { let buildRouter = router; @@ -347,9 +351,9 @@ async function createRouterBuild(app, router) { router: buildRouter, app, plugins: [ - routerModePlugin[buildRouter.internals.mode.name]?.() ?? [], - buildTargetPlugin[buildRouter.target]?.() ?? [], - ...((await buildRouter.plugins?.()) ?? []), + routerModePlugin[buildRouter.internals.mode.name]?.(buildRouter) ?? [], + buildTargetPlugin[buildRouter.target]?.(buildRouter) ?? [], + ...((await buildRouter.plugins?.(buildRouter)) ?? []), ], }); @@ -409,10 +413,10 @@ const spaManifest = () => { const routerModePlugin = { static: () => [], - build: () => [ + build: (router) => [ virtual( { - "#vinxi/handler": ({ config }) => { + [handlerModule(router)]: ({ config }) => { invariant( config.router.mode === "build", "#vinxi/handler is only supported in build mode", @@ -444,10 +448,10 @@ const routerModePlugin = { }, }), ], - handler: () => [ + handler: (router) => [ virtual( { - "#vinxi/handler": ({ config }) => { + [handlerModule(router)]: ({ config }) => { invariant( config.router.mode === "handler", "#vinxi/handler is only supported in handler mode", @@ -520,12 +524,12 @@ function toRouteId(route) { /** * - * @param {Exclude} router + * @param {import("./router-mode.js").Router<{ handler: string }>} router * @returns */ export async function getEntries(router) { return [ - router.handler.endsWith(".html") ? router.handler : "#vinxi/handler", + router.handler.endsWith(".html") ? router.handler : handlerModule(router), ...( (await router.internals.routes?.getRoutes())?.map((r) => Object.entries(r) @@ -542,15 +546,15 @@ export async function getEntries(router) { function handerBuild() { return { name: "react-rsc:handler", - async config(inlineConfig, env) { + async config({ router }, env) { if (env.command === "build") { invariant( - inlineConfig.router && inlineConfig.router.mode !== "static", + router && router.mode !== "static" && router.handler, "Invalid router", ); const { builtinModules } = await import("module"); const { join } = await import("./path.js"); - const input = await getEntries(inlineConfig.router); + const input = await getEntries(router); return { build: { rollupOptions: { @@ -565,10 +569,10 @@ function handerBuild() { manifest: true, target: "node18", ssrEmitAssets: true, - outDir: join(inlineConfig.router.outDir, inlineConfig.router.base), + outDir: join(router.outDir, router.base), emptyOutDir: false, }, - base: inlineConfig.router.base, + base: router.base, publicDir: false, }; } @@ -582,25 +586,22 @@ function handerBuild() { function browserBuild() { return { name: "build:browser", - async config(inlineConfig, env) { + async config({ router }, env) { if (env.command === "build") { - invariant( - inlineConfig.router && inlineConfig.router.mode !== "static", - "Invalid router", - ); + invariant(router && router.mode !== "static", "Invalid router"); const { join } = await import("./path.js"); return { build: { rollupOptions: { - input: await getEntries(inlineConfig.router), + input: await getEntries(router), treeshake: true, }, manifest: true, - outDir: join(inlineConfig.router.outDir, inlineConfig.router.base), + outDir: join(router.outDir, router.base), target: "esnext", emptyOutDir: false, }, - base: inlineConfig.router.base, + base: router.base, publicDir: false, }; } diff --git a/packages/vinxi/lib/manifest/client-manifest.js b/packages/vinxi/lib/manifest/client-manifest.js index f753694a..127d231f 100644 --- a/packages/vinxi/lib/manifest/client-manifest.js +++ b/packages/vinxi/lib/manifest/client-manifest.js @@ -1,20 +1,21 @@ /// -/// +/// import { invariant } from "../invariant"; -import { join } from "../path.js"; +import { handlerModule, join, virtualId } from "../path.js"; const manifest = new Proxy( {}, { - get(target, bundlerName) { + get(target, routerName) { invariant( - typeof bundlerName === "string", + typeof routerName === "string", "Bundler name should be a string", ); return { handler: import.meta.env.DEV ? join(import.meta.env.CWD, import.meta.env.ROUTER_HANDLER) - : "virtual:#vinxi/handler", + : // @ts-ignore + virtualId(handlerModule({ name: routerName })), chunks: new Proxy( {}, { @@ -46,7 +47,7 @@ const manifest = new Proxy( const assetsPath = join( import.meta.env.BASE_URL, - `@manifest/${bundlerName}/${Date.now()}/assets`, + `@manifest/${routerName}/${Date.now()}/assets`, ) + `?id=${input}`; return (await import(/* @vite-ignore */ assetsPath)) .default; diff --git a/packages/vinxi/lib/manifest/prod-server-manifest.js b/packages/vinxi/lib/manifest/prod-server-manifest.js index 6432b1ca..d11b3eec 100644 --- a/packages/vinxi/lib/manifest/prod-server-manifest.js +++ b/packages/vinxi/lib/manifest/prod-server-manifest.js @@ -1,5 +1,5 @@ import invariant from "vinxi/lib/invariant"; -import { join } from "vinxi/lib/path"; +import { handlerModule, join, virtualId } from "vinxi/lib/path"; import findAssetsInViteManifest from "./vite-manifest.js"; @@ -84,7 +84,9 @@ export function createProdManifest(app) { const id = input; if (router.target === "server") { const id = - input === router.handler ? "virtual:#vinxi/handler" : input; + input === router.handler + ? virtualId(handlerModule(router)) + : input; return { assets() { return findAssetsInViteManifest(bundlerManifest, id) @@ -110,7 +112,7 @@ export function createProdManifest(app) { } else if (router.target === "browser") { const id = input === router.handler && !input.endsWith(".html") - ? "virtual:#vinxi/handler" + ? virtualId(handlerModule(router)) : input; return { assets() { diff --git a/packages/vinxi/lib/path.js b/packages/vinxi/lib/path.js index f3684d34..9f872534 100644 --- a/packages/vinxi/lib/path.js +++ b/packages/vinxi/lib/path.js @@ -1 +1,11 @@ export * from "pathe"; + +export function virtualId(/** @type {string} */ moduleName) { + return `virtual:${moduleName}`; +} + +export function handlerModule( + /** @type {import("./router-mode").Router} */ router, +) { + return `#vinxi/handler/${router.name}`; +} diff --git a/packages/vinxi/lib/router-dev-plugins.js b/packages/vinxi/lib/router-dev-plugins.js index 5751c731..1f89c3d4 100644 --- a/packages/vinxi/lib/router-dev-plugins.js +++ b/packages/vinxi/lib/router-dev-plugins.js @@ -1,6 +1,6 @@ import { devEntries } from "./dev-server.js"; import invariant from "./invariant.js"; -import { join } from "./path.js"; +import { handlerModule, join } from "./path.js"; import { config } from "./plugins/config.js"; import { css } from "./plugins/css.js"; import { fileSystemWatcher } from "./plugins/fs-watcher.js"; @@ -34,21 +34,24 @@ export const ROUTER_MODE_DEV_PLUGINS = { /** @type {import("./router-modes.js").HandlerRouterSchema} */ router, ) => [ virtual({ - "#vinxi/handler": ({ config }) => { - // invariant( - // config.router.mode === "handler", - // "#vinxi/handler is only supported in handler mode", - // ); - if (config.router.middleware) { + [handlerModule(router)]: ({ config }) => { + /** @type {import("./router-mode.js").Router<{ middleware?: string; }>} */ + const router = config.router; + invariant( + router.handler === "handler", + "#vinxi/handler is only supported in handler mode", + ); + + if (router.middleware) { return ` - import middleware from "${join(config.router.root, config.router.middleware)}"; - import handler from "${join(config.router.root, config.router.handler)}"; + import middleware from "${join(router.root, router.middleware)}"; + import handler from "${join(router.root, router.handler)}"; import { eventHandler } from "vinxi/server"; export default eventHandler({ onRequest: middleware.onRequest, onBeforeResponse: middleware.onBeforeResponse, handler});`; } return `import handler from "${join( - config.router.root, - config.router.handler, + router.root, + router.handler, )}"; export default handler;`; }, }), @@ -76,11 +79,8 @@ export const ROUTER_MODE_DEV_PLUGINS = { css(), virtual( { - "#vinxi/handler": ({ config }) => { - invariant( - config.router.mode === "build", - "#vinxi/handler is only supported in build mode", - ); + [handlerModule(router)]: ({ config }) => { + invariant(config.router.handler, ""); return `import * as mod from "${join( config.router.root, config.router.handler, diff --git a/packages/vinxi/lib/router-mode.d.ts b/packages/vinxi/lib/router-mode.d.ts index 608f8e2e..23acf755 100644 --- a/packages/vinxi/lib/router-mode.d.ts +++ b/packages/vinxi/lib/router-mode.d.ts @@ -18,6 +18,7 @@ type DevHandler = { type Router = T & { base: string; + mode: string; internals: Internals; order: number; outDir: string; diff --git a/packages/vinxi/lib/router-modes.js b/packages/vinxi/lib/router-modes.js index b7a4bd94..fe493a81 100644 --- a/packages/vinxi/lib/router-modes.js +++ b/packages/vinxi/lib/router-modes.js @@ -58,7 +58,7 @@ export const spaRouterSchema = v.object({ routes: v.optional(v.custom((value) => value !== null)), handler: v.string(), outDir: v.string().optional(), - target: v.literal("browser"), + target: v.literal("browser").optional().default("browser"), plugins: v.optional(v.custom((value) => typeof value === "function")), }); const customRouterSchema = v.object({ @@ -148,10 +148,6 @@ const routerModes = { return { route: router.base, handler: eventHandler({ - onRequest: async (event) => { - setHeader(event, "Cross-Origin-Embedder-Policy", "require-corp"); - setHeader(event, "Cross-Origin-Opener-Policy", "same-origin"); - }, handler: fromNodeMiddleware(viteDevServer.middlewares), }), }; @@ -227,10 +223,10 @@ const routerModes = { } const { createViteHandler } = await import("./dev-server.js"); - const viteDevServer = await createViteHandler(router, app, serveConfig); + const viteServer = await createViteHandler(router, app, serveConfig); const handler = eventHandler(async (event) => { - const { default: handler } = await viteDevServer.ssrLoadModule( - "#vinxi/handler", + const { default: handler } = await viteServer.ssrLoadModule( + router.handler, ); return handler(event); }); @@ -303,28 +299,12 @@ const routerModes = { { route: `${router.base}/**`, handler: defineEventHandler({ - onRequest: async (event) => { - setHeader( - event, - "Cross-Origin-Embedder-Policy", - "require-corp", - ); - setHeader(event, "Cross-Origin-Opener-Policy", "same-origin"); - }, handler: fromNodeMiddleware(viteDevServer.middlewares), }), }, { route: router.base, handler: defineEventHandler({ - onRequest: async (event) => { - setHeader( - event, - "Cross-Origin-Embedder-Policy", - "require-corp", - ); - setHeader(event, "Cross-Origin-Opener-Policy", "same-origin"); - }, handler: fromNodeMiddleware(viteDevServer.middlewares), }), },