{"version":3,"file":"blocknote-server-util.cjs","names":[],"sources":["../src/context/ServerBlockNoteEditor.ts"],"sourcesContent":["import {\n  Block,\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n  BlockSchema,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  InlineContentSchema,\n  PartialBlock,\n  StyleSchema,\n  blocksToMarkdown,\n  createExternalHTMLExporter,\n  createInternalHTMLSerializer,\n  docToBlocks,\n} from \"@blocknote/core\";\nimport {\n  _blocksToProsemirrorNode as blocksToProsemirrorNodeUtil,\n  _prosemirrorJSONToBlocks as prosemirrorJSONToBlocksUtil,\n  blocksToYDoc as blocksToYDocUtil,\n  blocksToYXmlFragment as blocksToYXmlFragmentUtil,\n  yDocToBlocks as yDocToBlocksUtil,\n  yXmlFragmentToBlocks as yXmlFragmentToBlocksUtil,\n} from \"@blocknote/core/yjs\";\n\nimport { BlockNoteViewRaw } from \"@blocknote/react\";\nimport { Node } from \"@tiptap/pm/model\";\nimport * as jsdom from \"jsdom\";\nimport * as React from \"react\";\nimport { createElement } from \"react\";\nimport { flushSync } from \"react-dom\";\nimport { createRoot } from \"react-dom/client\";\nimport type * as Y from \"yjs\";\n\n/**\n * Use the ServerBlockNoteEditor to interact with BlockNote documents in a server (nodejs) environment.\n */\nexport class ServerBlockNoteEditor<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> {\n  /**\n   * Internal BlockNoteEditor (not recommended to use directly, use the methods of this class instead)\n   */\n  public readonly editor: BlockNoteEditor<BSchema, ISchema, SSchema>;\n\n  /**\n   * We currently use a JSDOM instance to mock document and window methods\n   *\n   * A possible improvement could be to make this:\n   * a) pluggable so other shims can be used as well\n   * b) obsolete, but for this all blocks should be React based and we need to remove all references to document / window\n   *    from the core / react package. (and even then, it's likely some custom blocks would still use document / window methods)\n   */\n  private jsdom = new jsdom.JSDOM();\n\n  /**\n   * Calls a function with mocking window and document using JSDOM\n   *\n   * We could make this obsolete by passing in a document / window object to the render / serialize methods of Blocks\n   */\n  public async _withJSDOM<T>(fn: () => Promise<T>) {\n    const prevWindow = globalThis.window;\n    const prevDocument = globalThis.document;\n    globalThis.document = this.jsdom.window.document;\n    (globalThis as any).window = this.jsdom.window;\n    (globalThis as any).window.__TEST_OPTIONS = (\n      prevWindow as any\n    )?.__TEST_OPTIONS;\n    try {\n      return await fn();\n    } finally {\n      globalThis.document = prevDocument;\n      globalThis.window = prevWindow;\n    }\n  }\n\n  public static create<\n    BSchema extends BlockSchema = DefaultBlockSchema,\n    ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n    SSchema extends StyleSchema = DefaultStyleSchema,\n  >(options: Partial<BlockNoteEditorOptions<BSchema, ISchema, SSchema>> = {}) {\n    return new ServerBlockNoteEditor(options) as ServerBlockNoteEditor<\n      BSchema,\n      ISchema,\n      SSchema\n    >;\n  }\n\n  protected constructor(\n    options: Partial<BlockNoteEditorOptions<any, any, any>>,\n  ) {\n    this.editor = BlockNoteEditor.create(options) as any;\n  }\n\n  /** PROSEMIRROR / BLOCKNOTE conversions */\n\n  /**\n   * Turn Prosemirror JSON to BlockNote style JSON\n   * @param json Prosemirror JSON\n   * @returns BlockNote style JSON\n   */\n  public _prosemirrorNodeToBlocks(pmNode: Node) {\n    return docToBlocks(pmNode);\n  }\n\n  /**\n   * Turn Prosemirror JSON to BlockNote style JSON\n   * @param json Prosemirror JSON\n   * @returns BlockNote style JSON\n   */\n  public _prosemirrorJSONToBlocks(json: any) {\n    return prosemirrorJSONToBlocksUtil(this.editor, json);\n  }\n\n  /**\n   * Turn BlockNote JSON to Prosemirror node / state\n   * @param blocks BlockNote blocks\n   * @returns Prosemirror root node\n   */\n  public _blocksToProsemirrorNode(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n  ) {\n    return blocksToProsemirrorNodeUtil(this.editor, blocks);\n  }\n\n  /** YJS / BLOCKNOTE conversions */\n\n  /**\n   * Turn a Y.XmlFragment collaborative doc into a BlockNote document (BlockNote style JSON of all blocks)\n   * @returns BlockNote document (BlockNote style JSON of all blocks)\n   */\n  public yXmlFragmentToBlocks(xmlFragment: Y.XmlFragment) {\n    return yXmlFragmentToBlocksUtil(this.editor, xmlFragment);\n  }\n\n  /**\n   * Convert blocks to a Y.XmlFragment\n   *\n   * This can be used when importing existing content to Y.Doc for the first time,\n   * note that this should not be used to rehydrate a Y.Doc from a database once\n   * collaboration has begun as all history will be lost\n   *\n   * @param blocks the blocks to convert\n   * @returns Y.XmlFragment\n   */\n  public blocksToYXmlFragment(\n    blocks: Block<BSchema, ISchema, SSchema>[],\n    xmlFragment?: Y.XmlFragment,\n  ) {\n    return blocksToYXmlFragmentUtil(this.editor, blocks, xmlFragment);\n  }\n\n  /**\n   * Turn a Y.Doc collaborative doc into a BlockNote document (BlockNote style JSON of all blocks)\n   * @returns BlockNote document (BlockNote style JSON of all blocks)\n   */\n  public yDocToBlocks(ydoc: Y.Doc, xmlFragment = \"prosemirror\") {\n    return yDocToBlocksUtil(this.editor, ydoc, xmlFragment);\n  }\n\n  /**\n   * This can be used when importing existing content to Y.Doc for the first time,\n   * note that this should not be used to rehydrate a Y.Doc from a database once\n   * collaboration has begun as all history will be lost\n   *\n   * @param blocks\n   */\n  public blocksToYDoc(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n    xmlFragment = \"prosemirror\",\n  ) {\n    return blocksToYDocUtil(this.editor, blocks, xmlFragment);\n  }\n\n  /** HTML / BLOCKNOTE conversions */\n\n  /**\n   * Exports blocks into a simplified HTML string. To better conform to HTML standards, children of blocks which aren't list\n   * items are un-nested in the output HTML.\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public async blocksToHTMLLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n  ): Promise<string> {\n    return this._withJSDOM(async () => {\n      const exporter = createExternalHTMLExporter(\n        this.editor.pmSchema,\n        this.editor,\n      );\n\n      return exporter.exportBlocks(blocks, {\n        document: this.jsdom.window.document,\n      });\n    });\n  }\n\n  /**\n   * Serializes blocks into an HTML string in the format that would normally be rendered by the editor.\n   *\n   * Use this method if you want to server-side render HTML (for example, a blog post that has been edited in BlockNote)\n   * and serve it to users without loading the editor on the client (i.e.: displaying the blog post)\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public async blocksToFullHTML(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n  ): Promise<string> {\n    return this._withJSDOM(async () => {\n      const exporter = createInternalHTMLSerializer(\n        this.editor.pmSchema,\n        this.editor,\n      );\n\n      return exporter.serializeBlocks(blocks, {\n        document: this.jsdom.window.document,\n      });\n    });\n  }\n\n  /**\n   * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and\n   * `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote\n   * doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.\n   * @param html The HTML string to parse blocks from.\n   * @returns The blocks parsed from the HTML string.\n   */\n  public async tryParseHTMLToBlocks(\n    html: string,\n  ): Promise<Block<BSchema, ISchema, SSchema>[]> {\n    return this._withJSDOM(async () => {\n      return this.editor.tryParseHTMLToBlocks(html);\n    });\n  }\n\n  /** MARKDOWN / BLOCKNOTE conversions */\n\n  /**\n   * Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of\n   * BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.\n   * @param blocks An array of blocks that should be serialized into Markdown.\n   * @returns The blocks, serialized as a Markdown string.\n   */\n  public async blocksToMarkdownLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n  ): Promise<string> {\n    return this._withJSDOM(async () => {\n      return blocksToMarkdown(blocks, this.editor.pmSchema, this.editor, {\n        document: this.jsdom.window.document,\n      });\n    });\n  }\n\n  /**\n   * Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on\n   * Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it\n   * as text.\n   * @param markdown The Markdown string to parse blocks from.\n   * @returns The blocks parsed from the Markdown string.\n   */\n  public async tryParseMarkdownToBlocks(\n    markdown: string,\n  ): Promise<Block<BSchema, ISchema, SSchema>[]> {\n    return this._withJSDOM(async () => {\n      return this.editor.tryParseMarkdownToBlocks(markdown);\n    });\n  }\n\n  /**\n   * If you're using React Context in your blocks, you can use this method to wrap editor calls for importing / exporting / block manipulation\n   * with the React Context Provider.\n   * \n   * Example:\n   * \n   * ```tsx\n      const html = await editor.withReactContext(\n      ({ children }) => (\n        <YourContext.Provider value={true}>{children}</YourContext.Provider>\n      ),\n      async () => editor.blocksToFullHTML(blocks)\n    );\n   */\n  public async withReactContext<T>(comp: React.FC<any>, fn: () => Promise<T>) {\n    return this._withJSDOM(async () => {\n      const tmpRoot = createRoot(\n        this.jsdom.window.document.createElement(\"div\"),\n      );\n\n      flushSync(() => {\n        tmpRoot.render(\n          createElement(\n            comp,\n            {},\n            createElement(BlockNoteViewRaw<any, any, any>, {\n              editor: this.editor,\n              // Skip UI components to avoid rendering them in the server environment\n              formattingToolbar: false,\n              linkToolbar: false,\n              slashMenu: false,\n              emojiPicker: false,\n              sideMenu: false,\n              filePanel: false,\n              tableHandles: false,\n              comments: false,\n            }),\n          ),\n        );\n      });\n      try {\n        return await fn();\n      } finally {\n        tmpRoot.unmount();\n        await new Promise((resolve) => setTimeout(resolve, 3));\n      }\n    });\n  }\n}\n"],"mappings":"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"}