From fb31ecb5ae62834b2f0d156b61ac6d55414af09a Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 24 Jun 2026 10:27:07 +0800 Subject: [PATCH] fix(tiptap): expose EditorOptions as runtime class for wasm-bindgen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EditorOptions was a TS interface (erased at runtime), but the Rust wasm-bindgen extern uses #[wasm_bindgen(constructor)] which generates 'new EditorOptions()' — requiring a global constructor. Vite 5/Rollup accidentally leaked the name into IIFE global scope, masking this. Vite 8/Rolldown strictly erases interfaces, exposing it as 'EditorOptions is not defined'. Fix: make EditorOptions a class (retains runtime constructor) and mount it on window, since the IIFE bundle name is taken by TiptapEditor. --- libs/tiptap-editor/src/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/libs/tiptap-editor/src/index.ts b/libs/tiptap-editor/src/index.ts index 8e54e40..21bfb57 100644 --- a/libs/tiptap-editor/src/index.ts +++ b/libs/tiptap-editor/src/index.ts @@ -9,7 +9,16 @@ import { UploadCoordinator, UPLOAD_COORDINATOR_STORAGE_KEY, type UploadEvent } f import { UploadImage } from './upload-image' import './style.css' -export interface EditorOptions { +/** + * 编辑器选项。用 class 而非 interface,使其编译后保留运行时构造函数—— + * Rust 侧 wasm-bindgen 的 `#[wasm_bindgen(constructor)]` 会生成 `new EditorOptions()`, + * 需要一个全局可访问的构造函数(interface 会被 TS 擦除)。 + * 字段全部可选,Rust 通过 setter(set_placeholder/set_on_update/...)逐个赋值。 + * + * 不用 export(避免 IIFE 的 named export 与 default 冲突),而是挂到 globalThis, + * 让 wasm-bindgen glue 里的裸标识符 `EditorOptions` 能解析到。 + */ +class EditorOptions { content?: string placeholder?: string onUpdate?: (markdown: string) => void @@ -24,17 +33,21 @@ export interface EditorOptions { onUploadEvent?: (event: UploadEvent) => void } +// wasm-bindgen 生成的 glue 用裸标识符 `new EditorOptions()` 从全局解析, +// IIFE 的 name 只能挂一个全局(TiptapEditor),这里手动把 EditorOptions 也挂到 window 上。 +;(window as unknown as Record).EditorOptions = EditorOptions + class TiptapEditorInstance { private editor: Editor | null = null private container: HTMLElement private options: EditorOptions - // 源码模式相关状态 + private isSourceMode = false private sourceTextarea: HTMLTextAreaElement | null = null private toggleButton: HTMLButtonElement | null = null private coordinator: UploadCoordinator | null = null - constructor(container: HTMLElement, options: EditorOptions = {}) { + constructor(container: HTMLElement, options: EditorOptions = new EditorOptions()) { this.container = container this.options = options this.init() @@ -257,7 +270,7 @@ class TiptapEditorInstance { const TiptapEditor = { _instances: new Map(), - create(containerId: string, options: EditorOptions = {}): TiptapEditorInstance | null { + create(containerId: string, options: EditorOptions = new EditorOptions()): TiptapEditorInstance | null { const container = document.getElementById(containerId) if (!container) { console.error(`[TiptapEditor] Container not found: #${containerId}`)