fully-react
Version:
React Server for Vite
182 lines (158 loc) • 4.3 kB
text/typescript
import type { Plugin, UserConfig } from "vite";
import path from "node:path";
import url from "node:url";
const dirname =
typeof __dirname === "undefined"
? url.fileURLToPath(new URL(".", import.meta.url))
: __dirname;
export interface VaviteConnectOptions {
/** Entry module that default exports a middleware function.
* @default "/handler" (which resolves to handler.js, handler.ts etc.
* in your project root)
*/
handlerEntry?: string;
/** Cusotm server entry the production build. */
customServerEntry?: string;
/** Whether to serve client-side assets in development.
* @default false
*/
serveClientAssetsInDev?: boolean;
/** Whether to build a standalone server application or a middleware function.
* @default true
*/
standalone?: boolean;
/** Directory where the client-side assets are located. Set to null to disable
* static file serving in production.
* @default null
*/
clientAssetsDir?: string | null;
/** Whether to bundle the sirv package or to import it. You have to install it as
* a production dependency if this is set to false.
* @default true
*/
bundleSirv?: boolean;
}
export default function vaviteConnect(
options: VaviteConnectOptions = {},
): Plugin[] {
const {
handlerEntry = "/handler",
customServerEntry,
serveClientAssetsInDev = false,
standalone = true,
clientAssetsDir = null,
bundleSirv = true,
} = options;
return [
{
name: "@vavite/connect:resolve",
enforce: "pre",
async resolveId(id) {
if (id === "/virtual:vavite-connect-handler") {
return this.resolve(handlerEntry);
} else if (id === "/virtual:vavite-connect-server") {
return path
.resolve(
dirname,
customServerEntry || "entry-standalone-bundled-sirv.js",
)
.replace(/\\/g, "/");
}
},
},
{
name: "@vavite/connect:server",
enforce: "post",
config(config, env) {
const common: UserConfig = {
optimizeDeps: {
// This silences the "could not auto-determine entry point" warning
include: [],
},
};
if (env.command === "build" && config.build?.ssr) {
if (process.env.RSC_WORKER) {
return {
...common,
// build: {
// rollupOptions: {
// input: {
// index: rscEntry,
// },
// },
// },
};
}
return {
...common,
// build: {
// rollupOptions: {
// input: {
// index:
// customServerEntry ||
// (standalone
// ? "/virtual:vavite-connect-server"
// : "/virtual:vavite-connect-handler"),
// },
// },
// },
define: clientAssetsDir
? {
__VAVITE_CLIENT_BUILD_OUTPUT_DIR:
JSON.stringify(clientAssetsDir),
}
: {},
};
}
return common;
},
configureServer(server) {
function addMiddleware() {
server.middlewares.use(async (req, res) => {
function renderError(status: number, message: string) {
res.statusCode = status;
res.end(message);
}
// Restore the original URL (SPA middleware may have changed it)
req.url = req.originalUrl || req.url;
try {
const module = await server.ssrLoadModule(handlerEntry);
await module.default(req, res, () => {
if (!res.writableEnded) renderError(404, "Not found");
});
} catch (err) {
if (err instanceof Error) {
server.ssrFixStacktrace(err);
renderError(500, err.stack || err.message);
} else {
renderError(500, "Unknown error");
}
}
});
}
if (serveClientAssetsInDev) {
return addMiddleware;
} else {
addMiddleware();
}
},
},
];
}
import type { Stats } from "node:fs";
import type { IncomingMessage, ServerResponse } from "node:http";
type Arrayable<T> = T | T[];
export interface SirvOptions {
dev?: boolean;
etag?: boolean;
maxAge?: number;
immutable?: boolean;
single?: string | boolean;
ignores?: false | Arrayable<string | RegExp>;
extensions?: string[];
dotfiles?: boolean;
brotli?: boolean;
gzip?: boolean;
onNoMatch?: (req: IncomingMessage, res: ServerResponse) => void;
setHeaders?: (res: ServerResponse, pathname: string, stats: Stats) => void;
}