Files
mnote/wolai-frontend/src/components/editor/leptos-tiptap-runtime-loader.ts
T
2026-04-21 06:26:35 +08:00

102 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
const MANIFEST_URL = "/api/leptos-tiptap-runtime/manifest.json";
type RuntimeManifest = {
bridgeRuntimePath: string | null;
extensionModulePaths: string[];
generatedRootPath: string | null;
entryScriptPath: string | null;
wasmPath: string | null;
};
type BridgeSuccess<T> = {
ok: true;
value: T;
};
type BridgeFailure = {
ok: false;
error: {
kind: string;
message: string;
operation?: string;
};
};
type BridgeResult<T> = BridgeSuccess<T> | BridgeFailure;
type BridgeRuntimeModule = {
init_bridge_runtime: () => void;
create: (
request: unknown,
onReady: (payload: { generation: number }) => void,
onChange: () => void,
onSelectionChange: (selection: Record<string, unknown>) => void,
onError: (error: { kind: string; message: string; operation?: string }) => void,
) => void;
destroy: (id: string) => void;
command: (request: unknown) => BridgeResult<{ kind: "empty" }>;
document: (request: unknown) => BridgeResult<{ kind: "content"; content: { format: "json" | "html"; value: unknown } }>;
};
export type LoadedLeptosTiptapRuntime = {
manifest: RuntimeManifest;
bridge: BridgeRuntimeModule;
};
let runtimePromise: Promise<LoadedLeptosTiptapRuntime> | null = null;
function toRuntimeAssetUrl(relativePath: string): string {
return `/api/leptos-tiptap-runtime/${relativePath}`;
}
async function importRuntimeModule<T>(relativePath: string): Promise<T> {
return import(/* webpackIgnore: true */ toRuntimeAssetUrl(relativePath)) as Promise<T>;
}
function extractRegisterFunction(module: Record<string, unknown>): (() => void) | null {
for (const value of Object.values(module)) {
if (typeof value === "function") {
return value as () => void;
}
}
return null;
}
async function fetchRuntimeManifest(): Promise<RuntimeManifest> {
const response = await fetch(MANIFEST_URL, { cache: "no-store" });
if (!response.ok) {
throw new Error(`读取 leptos-tiptap runtime manifest 失败(${response.status}`);
}
const manifest = (await response.json()) as RuntimeManifest;
if (!manifest.bridgeRuntimePath) {
throw new Error("runtime manifest 缺少 bridgeRuntimePath");
}
return manifest;
}
async function loadRuntimeModules(): Promise<LoadedLeptosTiptapRuntime> {
const manifest = await fetchRuntimeManifest();
const bridge = await importRuntimeModule<BridgeRuntimeModule>(manifest.bridgeRuntimePath!);
bridge.init_bridge_runtime();
for (const modulePath of manifest.extensionModulePaths) {
const extensionModule = await importRuntimeModule<Record<string, unknown>>(modulePath);
const register = extractRegisterFunction(extensionModule);
register?.();
}
return { manifest, bridge };
}
export async function loadLeptosTiptapRuntime(): Promise<LoadedLeptosTiptapRuntime> {
if (!runtimePromise) {
runtimePromise = loadRuntimeModules().catch((error) => {
runtimePromise = null;
throw error;
});
}
return runtimePromise;
}