主编辑区改造准备
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
"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;
|
||||
}
|
||||
Reference in New Issue
Block a user