Files
WRNexusJS/packages/dev-server/src/plugin-assets.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

165 lines
4.9 KiB
TypeScript

import { readFile } from "node:fs/promises";
import type {
ClientRuntimeDefinition,
PackageAssetDefinition,
PluginContributions,
} from "@wrnexus/plugin";
import { contentTypeForPath, normalizeClientRuntime, normalizePackageAsset } from "@wrnexus/plugin";
import type { ScriptAsset } from "@wrnexus/ssr";
export interface ServedPluginAsset {
id: string;
publicPath: string;
entry?: string;
source?: string | Uint8Array;
contentType: string;
immutable: boolean;
runtime?: boolean;
runtimeType?: "module" | "script";
bundle?: boolean;
}
export interface ClientRuntimeAsset extends ClientRuntimeDefinition {
publicPath: string;
}
function toBody(value: Uint8Array): ArrayBuffer {
return Uint8Array.from(value).buffer;
}
export function pluginAssetsFromContributions(
contributions: PluginContributions,
): ServedPluginAsset[] {
const assets: ServedPluginAsset[] = [];
for (const input of contributions.clientRuntimes) {
const runtime = normalizeClientRuntime(input);
assets.push({
id: `runtime:${runtime.id}`,
publicPath: runtime.publicPath!,
entry: runtime.entry,
source: runtime.source,
contentType: "text/javascript; charset=utf-8",
immutable: false,
runtime: true,
runtimeType: runtime.type,
bundle: runtime.bundle,
});
}
for (const input of contributions.assets) {
const asset = normalizePackageAsset(input);
assets.push({
id: `asset:${asset.id}`,
publicPath: asset.publicPath!,
entry: asset.entry,
source: asset.source,
contentType: asset.contentType ?? contentTypeForPath(asset.entry ?? asset.publicPath!),
immutable: asset.immutable ?? false,
});
}
return assets;
}
export async function readPluginAsset(asset: ServedPluginAsset): Promise<string | ArrayBuffer> {
if (typeof asset.source === "string") return asset.source;
if (asset.source instanceof Uint8Array) return toBody(asset.source);
if (!asset.entry) throw new Error(`Plugin asset '${asset.id}' has no source.`);
const shouldBundle = asset.runtime && (asset.bundle ?? /\.[cm]?tsx?$/.test(asset.entry));
if (!shouldBundle) return toBody(await readFile(asset.entry));
const result = await Bun.build({
entrypoints: [asset.entry],
target: "browser",
format: asset.runtimeType === "script" ? "iife" : "esm",
minify: false,
sourcemap: "inline",
});
if (!result.success || !result.outputs[0]) {
throw new Error(
`Plugin runtime '${asset.id}' failed to build:\n${result.logs.map(String).join("\n")}`,
);
}
return toBody(new Uint8Array(await result.outputs[0].arrayBuffer()));
}
export async function servePluginAsset(
assets: readonly ServedPluginAsset[],
pathname: string,
mode: "development" | "production",
): Promise<Response | null> {
const asset = assets.find((entry) => entry.publicPath === pathname);
if (!asset) return null;
try {
const body = await readPluginAsset(asset);
return new Response(body, {
headers: {
"content-type": asset.contentType,
"cache-control":
mode === "production" && asset.immutable
? "public, max-age=31536000, immutable"
: "no-cache",
"x-content-type-options": "nosniff",
},
});
} catch (error) {
console.error(`[wrnexus] failed to serve plugin asset ${asset.publicPath}`, error);
return new Response("Plugin asset unavailable", { status: 503 });
}
}
export function runtimeScript(input: ClientRuntimeDefinition): ScriptAsset {
const runtime = normalizeClientRuntime(input);
return {
src: runtime.publicPath!,
type: runtime.type === "script" ? "classic" : "module",
async: runtime.load === "eager" ? false : undefined,
defer: runtime.load !== "eager",
integrity: runtime.integrity,
crossOrigin: runtime.crossOrigin,
attributes: {
"data-wrnexus-runtime-src": runtime.id,
...(runtime.attributes ?? {}),
},
};
}
export function runtimeIdsFromMarkup(body: string): Set<string> {
const ids = new Set<string>();
for (const match of body.matchAll(/\bdata-wrnexus-runtime\s*=\s*["']([^"']+)["']/gi)) {
for (const id of match[1]!.split(/[\s,]+/)) {
if (id) ids.add(id);
}
}
return ids;
}
export function runtimeScriptsForMarkup(
body: string,
runtimes: readonly ClientRuntimeDefinition[] = [],
): ScriptAsset[] {
const ids = runtimeIdsFromMarkup(body);
return runtimes.filter((runtime) => ids.has(runtime.id)).map(runtimeScript);
}
export function mergePluginAssets(
runtimes: readonly ClientRuntimeDefinition[],
assets: readonly PackageAssetDefinition[],
): ServedPluginAsset[] {
return pluginAssetsFromContributions({
componentDirs: [],
clientRuntimes: [...runtimes],
assets: [...assets],
styles: [],
routes: [],
middleware: [],
migrations: [],
directives: [],
cliCommands: [],
virtualModules: [],
deploymentAdapters: [],
configSchemas: [],
documentation: [],
typeDefinitions: [],
});
}