init
This commit is contained in:
155
plugins/ssrPlugin.ts
Normal file
155
plugins/ssrPlugin.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { Plugin } from "vite";
|
||||
export function createVirtualPlugin(name: string, load: Plugin["load"]) {
|
||||
name = "virtual:" + name;
|
||||
return {
|
||||
name,
|
||||
resolveId(source, _importer, _options) {
|
||||
if (source === name || source.startsWith(`${name}?`)) {
|
||||
return `\0${source}`;
|
||||
}
|
||||
return;
|
||||
},
|
||||
load(id, options) {
|
||||
if (id === `\0${name}` || id.startsWith(`\0${name}?`)) {
|
||||
return (load as any).apply(this, [id, options]);
|
||||
}
|
||||
},
|
||||
} satisfies Plugin;
|
||||
}
|
||||
export function clientFirstBuild(): Plugin {
|
||||
return {
|
||||
name: "client-first-build",
|
||||
config(config) {
|
||||
config.builder ??= {};
|
||||
config.builder.buildApp = async (builder) => {
|
||||
const clientEnvironment = builder.environments.client;
|
||||
const workerEnvironments = Object.keys(builder.environments)
|
||||
.filter((name) => name !== "client")
|
||||
.map((name) => builder.environments[name]);
|
||||
// console.log('Client First Build Plugin: Starting builds...', workerEnvironments)
|
||||
// Client build first
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (clientEnvironment) {
|
||||
// clientEnvironment.config.build.outDir = "dist/client";
|
||||
// console.log("Client First Build Plugin: Building client...", Object.keys());
|
||||
await builder.build(clientEnvironment);
|
||||
}
|
||||
// console.log("Client First Build Plugin: Client build complete.", workerEnvironments);
|
||||
// Then worker builds
|
||||
for (const workerEnv of workerEnvironments) {
|
||||
await builder.build(workerEnv);
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
export function injectManifest(): Plugin {
|
||||
let clientOutDir = "dist/client";
|
||||
|
||||
return {
|
||||
name: "inject-manifest",
|
||||
config(config) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const viteConfig = config as any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
clientOutDir =
|
||||
viteConfig.environments?.client?.build?.outDir ?? "dist/client";
|
||||
},
|
||||
async transform(code, id, options) {
|
||||
// Only transform in SSR environment (non-client)
|
||||
if (!options?.ssr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only transform files that contain the placeholder
|
||||
if (!code.includes("__VITE_MANIFEST_CONTENT__")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read manifest from client build output
|
||||
const manifestPath = path.resolve(
|
||||
process.cwd(),
|
||||
clientOutDir,
|
||||
".vite/manifest.json"
|
||||
);
|
||||
let manifestContent: string | undefined;
|
||||
try {
|
||||
manifestContent = await this.fs
|
||||
.readFile(manifestPath)
|
||||
.then((data) => data.toString());
|
||||
} catch {
|
||||
// Manifest not found
|
||||
}
|
||||
|
||||
if (!manifestContent) return;
|
||||
|
||||
// Replace placeholder string with actual manifest data
|
||||
// Format: { "__manifest__": { default: <manifest> } } to match the Object.entries loop
|
||||
const newCode = code.replace(
|
||||
/"__VITE_MANIFEST_CONTENT__"/g,
|
||||
`{ "__manifest__": { default: ${manifestContent} } }`
|
||||
);
|
||||
|
||||
if (newCode !== code) {
|
||||
return { code: newCode, map: null };
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
export default function ssrPlugin(): Plugin[] {
|
||||
// const { hotReload: hotReloadOption = true, entry: entryOption = {} } = options
|
||||
|
||||
const plugins: Plugin[] = [];
|
||||
|
||||
plugins.push(clientFirstBuild());
|
||||
plugins.push({
|
||||
name: "ssr-auto-entry",
|
||||
config(config) {
|
||||
config.define = config.define || {};
|
||||
},
|
||||
resolveId(id, importer, options) {
|
||||
if (!id.startsWith('@httpClientAdapter')) return
|
||||
const pwd = process.cwd()
|
||||
console.log('Resolving httpClientAdapter in', pwd, 'for', {id, importer, options})
|
||||
return path.resolve(
|
||||
__dirname,
|
||||
options?.ssr
|
||||
? pwd+"/src/api/httpClientAdapter.server.ts"
|
||||
: pwd+"/src/api/httpClientAdapter.client.ts"
|
||||
);
|
||||
},
|
||||
async configResolved(config) {
|
||||
const viteConfig = config as any;
|
||||
|
||||
if (!viteConfig.environments) {
|
||||
viteConfig.environments = {};
|
||||
}
|
||||
if (!viteConfig.environments.client) {
|
||||
viteConfig.environments.client = {};
|
||||
}
|
||||
if (!viteConfig.environments.client.build) {
|
||||
viteConfig.environments.client.build = {};
|
||||
}
|
||||
|
||||
const clientBuild = viteConfig.environments.client.build;
|
||||
clientBuild.manifest = true;
|
||||
clientBuild.rollupOptions = clientBuild.rollupOptions || {};
|
||||
clientBuild.rollupOptions.input = "src/client.ts";
|
||||
if (!viteConfig.environments.ssr) {
|
||||
const manifestPath = path.join(clientBuild.outDir as string, '.vite/manifest.json')
|
||||
try {
|
||||
const resolvedPath = path.resolve(process.cwd(), manifestPath)
|
||||
const manifestContent = readFileSync(resolvedPath, 'utf-8')
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
config.define['import.meta.env.VITE_MANIFEST_CONTENT'] = JSON.stringify(manifestContent)
|
||||
} catch {}
|
||||
}
|
||||
},
|
||||
});
|
||||
plugins.push(injectManifest());
|
||||
|
||||
return plugins;
|
||||
}
|
||||
Reference in New Issue
Block a user