diff --git a/.changeset/early-pumas-pump.md b/.changeset/early-pumas-pump.md new file mode 100644 index 00000000..7f90064b --- /dev/null +++ b/.changeset/early-pumas-pump.md @@ -0,0 +1,7 @@ +--- +"@vinxi/server-functions": patch +"vinxi": patch +"solid-ssr-basic": patch +--- + +(feat): deep support for `server.baseURL` API from nitro in dev & prod, diff --git a/examples/react/rsc/spa/app/client.tsx b/examples/react/rsc/spa/app/client.tsx index ba2063da..9d5990a2 100644 --- a/examples/react/rsc/spa/app/client.tsx +++ b/examples/react/rsc/spa/app/client.tsx @@ -11,7 +11,11 @@ import { ServerComponent } from "./server-component"; document.addEventListener("click", async (e) => { console.log(sayHello, "hello"); - const result = await fetchServerAction("/_server", sayHello["$$id"], []); + const result = await fetchServerAction( + import.meta.env.SERVER_BASE_URL + "/_server", + sayHello["$$id"], + [], + ); console.log(result); // sayHello(); }); diff --git a/examples/solid/ssr/basic/app/app.tsx b/examples/solid/ssr/basic/app/app.tsx index 9739c0b6..3067a79c 100644 --- a/examples/solid/ssr/basic/app/app.tsx +++ b/examples/solid/ssr/basic/app/app.tsx @@ -6,7 +6,10 @@ export default function App({ assets, scripts }) { return ( - + {assets} diff --git a/packages/vinxi-server-functions/client-runtime.js b/packages/vinxi-server-functions/client-runtime.js index 82ab62eb..97d3c555 100644 --- a/packages/vinxi-server-functions/client-runtime.js +++ b/packages/vinxi-server-functions/client-runtime.js @@ -15,13 +15,19 @@ export function createServerReference(fn, id, name) { return new Proxy(fn, { get(target, prop, receiver) { if (prop === "url") { - return "/_server" + `id=${id}&name=${name}`; + return ( + import.meta.env.SERVER_BASE_URL + "/_server" + `id=${id}&name=${name}` + ); } return Reflect.get(target, prop, receiver); }, apply(target, thisArg, args) { - return fetchServerAction("/_server", `${id}#${name}`, args); + return fetchServerAction( + `${import.meta.env.SERVER_BASE_URL}/_server`, + `${id}#${name}`, + args, + ); }, }); } diff --git a/packages/vinxi-server-functions/server-runtime.js b/packages/vinxi-server-functions/server-runtime.js index bc0953cd..60538b7f 100644 --- a/packages/vinxi-server-functions/server-runtime.js +++ b/packages/vinxi-server-functions/server-runtime.js @@ -2,7 +2,9 @@ export function createServerReference(fn, id, name) { return new Proxy(fn, { get(target, prop, receiver) { if (prop === "url") { - return "/_server" + `id=${id}&name=${name}`; + return ( + import.meta.env.SERVER_BASE_URL + "/_server" + `id=${id}&name=${name}` + ); } return Reflect.get(target, prop, receiver); diff --git a/packages/vinxi/lib/app.js b/packages/vinxi/lib/app.js index 4293cfa9..92d8cecc 100644 --- a/packages/vinxi/lib/app.js +++ b/packages/vinxi/lib/app.js @@ -12,7 +12,7 @@ import { resolveRouterConfig, routerSchema } from "./router-modes.js"; routers?: import("./router-modes.js").RouterSchemaInput[]; name?: string; - server?: import('nitropack').NitroConfig; + server?: Omit; root?: string }} AppOptions */ @@ -20,7 +20,7 @@ import { resolveRouterConfig, routerSchema } from "./router-modes.js"; config: { name: string; devtools: boolean; - server: import("nitropack").NitroConfig; + server: Omit; routers: import("./router-mode.js").Router[]; root: string; }; diff --git a/packages/vinxi/lib/build.js b/packages/vinxi/lib/build.js index 1a7cdb78..c2722c4a 100644 --- a/packages/vinxi/lib/build.js +++ b/packages/vinxi/lib/build.js @@ -83,6 +83,7 @@ export async function createBuild(app, buildConfig) { "node-fetch-native/polyfill": require.resolve( "node-fetch-native/polyfill", ), + ...(app.config.server.alias ?? {}), // "unstorage/drivers/fs-lite": require.resolve("unstorage/drivers/fs-lite"), // "unstorage/drivers/fs": require.resolve("unstorage/drivers/fs"), // defu: require.resolve("defu"), diff --git a/packages/vinxi/lib/dev-server.js b/packages/vinxi/lib/dev-server.js index 9fa99fe1..b66d3abe 100644 --- a/packages/vinxi/lib/dev-server.js +++ b/packages/vinxi/lib/dev-server.js @@ -3,7 +3,7 @@ import { inspect } from "@vinxi/devtools"; import { fileURLToPath } from "node:url"; import { consola, withLogger } from "./logger.js"; -import { normalize } from "./path.js"; +import { join, normalize } from "./path.js"; export * from "./router-dev-plugins.js"; @@ -57,9 +57,12 @@ export async function createViteHandler(router, app, serveConfig) { ...(((await router.plugins?.(router)) ?? []).filter(Boolean) || []), ].filter(Boolean); + let base = join(app.config.server.baseURL ?? "/", router.base); + + console.log(base); const viteDevServer = await createViteDevServer({ configFile: false, - base: router.base, + base, plugins, optimizeDeps: { force: serveConfig.force, @@ -122,7 +125,7 @@ export async function createDevServer( publicAssets: [ ...app.config.routers .map((router) => { - return router.internals.mode.dev.publicAssets?.(router, app.config); + return router.internals.mode.dev.publicAssets?.(router, app); }) .filter( /** diff --git a/packages/vinxi/lib/manifest/dev-server-manifest.js b/packages/vinxi/lib/manifest/dev-server-manifest.js index 145a9c4a..28d5e844 100644 --- a/packages/vinxi/lib/manifest/dev-server-manifest.js +++ b/packages/vinxi/lib/manifest/dev-server-manifest.js @@ -16,6 +16,8 @@ export function createDevManifest(app) { let router = app.getRouter(bundlerName); + let base = join(app.config.server.baseURL ?? "", router.base); + if (router.mode === "static") { return { json() { @@ -27,7 +29,7 @@ export function createDevManifest(app) { routes() { return []; }, - base: router.base, + base, target: "static", mode: router.mode, handler: undefined, @@ -64,7 +66,7 @@ export function createDevManifest(app) { return {}; }, handler: router.handler, - base: router.base, + base, target: router.target, mode: router.mode, chunks: new Proxy( @@ -83,7 +85,7 @@ export function createDevManifest(app) { if (router.target === "browser") { return { output: { - path: join(router.base, "@fs", absolutePath), + path: join(base, "@fs", absolutePath), }, }; } else { @@ -234,7 +236,7 @@ export function createDevManifest(app) { attrs: { key: "vite-client", type: "module", - src: join(router.base, "@vite", "client"), + src: join(base, "@vite", "client"), }, }, ] @@ -242,7 +244,7 @@ export function createDevManifest(app) { ].filter(Boolean); }, output: { - path: join(router.base, "@fs", absolutePath), + path: join(base, "@fs", absolutePath), }, }; } else { diff --git a/packages/vinxi/lib/manifest/prod-server-manifest.js b/packages/vinxi/lib/manifest/prod-server-manifest.js index 8719450b..e24a5c1b 100644 --- a/packages/vinxi/lib/manifest/prod-server-manifest.js +++ b/packages/vinxi/lib/manifest/prod-server-manifest.js @@ -1,3 +1,4 @@ +import { joinURL } from "ufo"; import invariant from "vinxi/lib/invariant"; import { handlerModule, join, virtualId } from "vinxi/lib/path"; @@ -7,21 +8,19 @@ import findAssetsInViteManifest from "./vite-manifest.js"; /** @typedef {import("../app.js").App & { config: { buildManifest: { [key:string]: any } }}} ProdApp */ -function createHtmlTagsForAssets(router, assets) { - return assets.filter( - (asset) => - asset.endsWith(".css") || asset.endsWith(".js"), - ) - .map((asset) => ({ - tag: "link", - attrs: { - href: join(router.base, asset), - key: join(router.base, asset), - ...(asset.endsWith(".css") - ? { rel: "stylesheet", precendence: "high" } - : { rel: "modulepreload" }), - }, - })); +function createHtmlTagsForAssets(router, app, assets) { + return assets + .filter((asset) => asset.endsWith(".css") || asset.endsWith(".js")) + .map((asset) => ({ + tag: "link", + attrs: { + href: joinURL(app.config.server.baseURL ?? "", router.base, asset), + key: join(app.config.server.baseURL ?? "", router.base, asset), + ...(asset.endsWith(".css") + ? { rel: "stylesheet", precendence: "high" } + : { rel: "modulepreload" }), + }, + })); } /** @@ -121,7 +120,11 @@ export function createProdManifest(app) { : input; return { assets() { - return createHtmlTagsForAssets(router, findAssetsInViteManifest(bundlerManifest, id)); + return createHtmlTagsForAssets( + router, + app, + findAssetsInViteManifest(bundlerManifest, id), + ); }, output: { path: join( @@ -139,17 +142,26 @@ export function createProdManifest(app) { return { import() { return import( - /* @vite-ignore */ join( + /* @vite-ignore */ joinURL( + app.config.server.baseURL ?? "", router.base, bundlerManifest[id].file, ) ); }, assets() { - return createHtmlTagsForAssets(router, findAssetsInViteManifest(bundlerManifest, id)); + return createHtmlTagsForAssets( + router, + app, + findAssetsInViteManifest(bundlerManifest, id), + ); }, output: { - path: join(router.base, bundlerManifest[id].file), + path: joinURL( + app.config.server.baseURL ?? "", + router.base, + bundlerManifest[id].file, + ), }, }; } diff --git a/packages/vinxi/lib/nitro-dev.js b/packages/vinxi/lib/nitro-dev.js index a817dbe5..5980d657 100644 --- a/packages/vinxi/lib/nitro-dev.js +++ b/packages/vinxi/lib/nitro-dev.js @@ -168,7 +168,10 @@ export function createDevServer(nitro) { // Dev-only handlers for (const handler of nitro.options.devHandlers) { - app.use(handler.route || "/", handler.handler); + app.use( + joinURL(nitro.options.runtimeConfig.app.baseURL, handler.route ?? "/"), + handler.handler, + ); } // User defined dev proxy diff --git a/packages/vinxi/lib/plugins/config.js b/packages/vinxi/lib/plugins/config.js index b9cc8935..93e6af2c 100644 --- a/packages/vinxi/lib/plugins/config.js +++ b/packages/vinxi/lib/plugins/config.js @@ -1,7 +1,7 @@ /** * * @param {string} tag - * @param {Omit} conf + * @param {import('../vite-dev.d.ts').CustomizableConfig} conf * @returns {import('../vite-dev.d.ts').Plugin} */ export function config(tag, conf) { diff --git a/packages/vinxi/lib/plugins/manifest.js b/packages/vinxi/lib/plugins/manifest.js index 271fcda1..68aba71c 100644 --- a/packages/vinxi/lib/plugins/manifest.js +++ b/packages/vinxi/lib/plugins/manifest.js @@ -29,6 +29,9 @@ export function manifest() { "import.meta.env.ROUTER_NAME": JSON.stringify(router.name), "import.meta.env.ROUTER_HANDLER": JSON.stringify(router.handler), "import.meta.env.CWD": JSON.stringify(router.root), + "import.meta.env.SERVER_BASE_URL": JSON.stringify( + app.config.server.baseURL ?? "", + ), "import.meta.env.ROUTERS": JSON.stringify( app.config.routers.map((router) => router.name), ), @@ -67,10 +70,13 @@ export function manifest() { export function injectVinxiClient() { /** @type {import('../router-mode.js').Router} */ let router; + /** @type {import('../app.js').App} */ + let app; return { name: "vinxi:inject-client-runtime", configResolved(config) { router = config.router; + app = config.app; }, apply: "serve", transformIndexHtml(html) { @@ -80,6 +86,7 @@ export function injectVinxiClient() { attrs: { type: "module", src: join( + app.config.server.baseURL ?? "", router.base, "@fs", `${fileURLToPath( diff --git a/packages/vinxi/lib/vite-dev.d.ts b/packages/vinxi/lib/vite-dev.d.ts index 97097684..a27f1bca 100644 --- a/packages/vinxi/lib/vite-dev.d.ts +++ b/packages/vinxi/lib/vite-dev.d.ts @@ -1,8 +1,8 @@ import { Plugin as VitePlugin, ResolvedConfig as _ResolvedConfig } from "vite"; import { App } from "./app.js"; -import { Router } from "./router-mode.js"; import { DevConfig } from "./dev-server.js"; +import { Router } from "./router-mode.js"; declare module "vite" { interface UserConfig { @@ -28,4 +28,27 @@ export type ViteConfig = _ResolvedConfig & { router: Router; app: App }; export type Plugin = VitePlugin; +export type CustomizableConfig = Omit< + import("vite").InlineConfig, + | "appType" + | "app" + | "router" + | "base" + | "root" + | "publicDir" + | "mode" + | "server" + | "preview" + | "clearScreen" + | "configFile" + | "envFile" +> & { + build?: Omit< + import("vite").InlineConfig["build"], + "outDir" | "ssr" | "ssrManifest" | "rollupOptions" + > & { + rollupOptions?: Omit; + }; +}; + export type { ConfigEnv as ConfigEnv } from "vite";