Closed11

【今のところ】CloudflareWorkers で Lexical を同期している YDoc を HTML に変換する【できない】

naporitannaporitan

YDoc を CloudflareWorkers DurableObjects 内部に持っているという前提。
その条件下でどうにかして YDoc から Lexical の同期状態を取り出して worker 上で HTML として返せるようにしたいというのがゴール。

naporitannaporitan

YDoc の serialize

https://discuss.yjs.dev/t/issue-with-serializing-and-deserializing-y-doc-to-database/883

変更状態しか取れなさそう。ここから HTML にするのは多分無理なので Lexical が必要ぽい。

function serializeYDoc(yDoc: Y.Doc) {
  const documentState = Y.encodeStateAsUpdate(yDoc)
  const base64Encoded = fromUint8Array(documentState)
  return base64Encoded
}

function deserializeYDoc(base64YDoc: string) {
  const binaryEncoded = toUint8Array(base64YDoc)
  const deserializedYDoc = new Y.Doc()
  Y.applyUpdate(deserializedYDoc, binaryEncoded)
  return deserializedYDoc
}
naporitannaporitan

worker 上では editor がないので @lexical/headless から editor をつくるっぽい。

https://lexical.dev/docs/packages/lexical-headless

おおよそやりたいこと発見。

const { createHeadlessEditor } = require('@lexical/headless');
const { $convertToMarkdownString, TRANSFORMERS } = require('@lexical/markdown');

app.get('article/:id/markdown', await (req, res) => {
  const editor = createHeadlessEditor({
    nodes: [],
    onError: () => {},
  });

  const articleEditorStateJSON = await loadArticleBody(req.query.id);
  editor.setEditorState(editor.parseEditorState(articleEditorStateJSON));  

  editor.update(() => {
    const markdown = $convertToMarkdownString(TRANSFORMERS);
    res.send(markdown);
  });
});
naporitannaporitan

YDoc を headless editor で読み込んで html で吐き出す discussions があった。

https://github.com/facebook/lexical/discussions/4442

気になるコードを発見.... worker だと絶対動かなそう

  // $generateHTmlFromNodes can only be used on the server-side when JSDOM is globally set up right now
  // see https://github.com/facebook/lexical/issues/3097
  const restoreDOM = globallyInstallJSDOM();

必要っぽい.....
https://github.com/facebook/lexical/issues/3097#issuecomment-1265509122

naporitannaporitan

HTML にするのは不可能なので markdown で進めてみる。

とりあえず真似して作る

const editor = createHeadlessEditor({
    namespace: "headless",
    nodes,
    onError: (error) => {
      console.error(error);
    },
  });
  const id = "1";
  const provider = {
    awareness: {
      getLocalState: () => null,
      getStates: () => [],
    },
  } as unknown as Provider;

  const targetDoc = new Doc();
  const binding = createBinding(
    editor,
    provider,
    id,
    targetDoc,
    new Map([[id, targetDoc]]),
  );

  binding.root
    .getSharedType()
    .observeDeep((events: YEvent<YText>[], transaction: unknown) => {
      syncYjsChangesToLexical(binding, provider, events, false);
    });

  const state = encodeStateAsUpdate(originDoc);
  applyUpdate(targetDoc, state);

  editor.update(
    () => {
      /** */
    },
    { discrete: true },
  );
  await new Promise<void>((resolve) => {
    const state = editor.getEditorState();
    state.read(() => resolve());
  });
naporitannaporitan

createBinding を入れるとこれが出るようになる。

@lexical/yjs が cjs なため Yjs was already imported. This breaks constructor checks and will lead to issues! - [https://github.com/yjs/yjs/issues/438](https://github.com/yjs/yjs/issues/438)

@lexical/yjs は cjs で yjs は esm で読んでるからっぽい....react の multiple instance 再来の感覚.....

yjs
https://github.com/yjs/yjs/blob/main/package.json#L27-L38

@lexical/yjs
https://github.com/facebook/lexical/blob/main/packages/lexical-yjs/package.json#L15

進展が全くない....

https://github.com/facebook/lexical/issues/1707

naporitannaporitan

error 出るものの動きはするのでいったん無視する(どうせ直せないので)

$convertToMarkdownString(TRANSFORMERS); を実行して markdown を得るだけなのだが、これを improt した瞬間に Prism is not defined で worker が落ちるようになった。

原因はこいつ。Syntax highlight のために prismjs を入れているっぽい。(えー依存に入れるの?って思った外入れしてほしい気持ち)

https://github.com/facebook/lexical/tree/main/packages/lexical-code

naporitannaporitan

prismjs 入れればいいのかと思い入れたらめちゃくちゃ node 依存が入ってきた。

ここが原因。worker の self は WorkerGlobalScope を拡張した ServiceWorkerGlobalScope だが、instanceof で評価したときに別物になる。当然 window もないので node.js モードになり、すべてが入ってくる。終わり

https://github.com/PrismJS/prism/blob/master/prism.js#L8-L14

このスクラップは2024/02/09にクローズされました