Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML Serialization becomes slower over time #2148

Closed
1 of 2 tasks
jcarvalho opened this issue Nov 8, 2021 · 7 comments · Fixed by #2151
Closed
1 of 2 tasks

HTML Serialization becomes slower over time #2148

jcarvalho opened this issue Nov 8, 2021 · 7 comments · Fixed by #2151
Labels
Type: Bug The issue or pullrequest is related to a bug

Comments

@jcarvalho
Copy link

What’s the bug you are facing?

In Node, repeated invocations of the generateHTML function with specific constructs cause the HTML Serialization process to become slower over time.

How can we reproduce the bug on our side?

Create a new NodeJS project with the following package.json:

{
  "dependencies": {
    "@tiptap/extension-document": "^2.0.0-beta.13",
    "@tiptap/extension-paragraph": "^2.0.0-beta.19",
    "@tiptap/extension-text": "^2.0.0-beta.13",
    "@tiptap/html": "^2.0.0-beta.131",
    "@types/node": "^16.11.6",
    "typescript": "^4.4.4"
  }
}

Create and run the following Typescript script:

import { hrtime } from "process";
import { generateHTML } from "@tiptap/html";
import Document from "@tiptap/extension-document";
import Paragraph from "@tiptap/extension-paragraph";
import Text from "@tiptap/extension-text";

const doc = {
  type: "doc",
  content: new Array(1000).fill({
    type: "paragraph",
    content: [{ type: "text", text: "Hello world" }],
  }),
};

for (let i = 0; i < 100000; i++) {
  const start = hrtime.bigint();
  generateHTML(doc, [Document, Paragraph, Text]);
  const end = hrtime.bigint();
  console.log(`Took ${(end - start) / BigInt(1e6)}ms`);
}

The script will output the time it takes to serialize a document to HTML, and you can notice it keeps increasing over time.

Can you provide a CodeSandbox?

No response

What did you expect to happen?

Serialization times remain roughly constant regardless of how many documents were serialized.

Anything to add? (optional)

We did a little digging on this, and found the culprit to be hostic-dom keeping a list of fragments in a global variable, which is never released. Running the snippet above, we can confirm the USED_JSX variable keeps getting pushed to, containing repeated copies of <p>Hello world</p>.

Replacing hostic-dom with JSDOM or happy-dom seems to fix the issue, but it requires using lower-level TipTap APIs (and thus rendering the @tiptap/html module useless):

export function generateHTML(doc: JSONContent, extensions: Extensions): string {
  const schema = getSchema(extensions);
  const contentNode = Node.fromJSON(schema, doc);

  const window = new Window();

  const fragment = DOMSerializer.fromSchema(schema).serializeFragment(contentNode.content, {
    document: window.document,
  });

  const serializer = new window.XMLSerializer();
  return serializer.serializeToString((fragment as unknown) as INode);
}

Did you update your dependencies?

  • Yes, I’ve updated my dependencies to use the latest version of all packages.

Are you sponsoring us?

  • Yes, I’m a sponsor. 💖
@jcarvalho jcarvalho added the Type: Bug The issue or pullrequest is related to a bug label Nov 8, 2021
@jcarvalho
Copy link
Author

@philippkuehn Saw your linked issue on zeed-dom, and realized I forgot to mention I also tried replacing hostic-dom with zeed-dom, and the issue is still there.

If moving forward, you plan on moving TipTap to zeed-dom, I can open an issue there.

@philippkuehn
Copy link
Contributor

@jcarvalho Oh, good to know! Yeah, would be great if you could create an issue there! 👍

BTW: the reason we moved from JSDOM to hostic-dom is that JSDOM can’t run in the browser and is super bloated. Since we use Cypress for our testing, that was not compatible with it.

@floriankrueger
Copy link
Contributor

Hi, Flo here, a colleague of @jcarvalho 👋 we also looked into replacing hostic-dom with JSDOM but - apart from being much more cumbersome - it also didn't live up to the performance of hostic-dom (or zeed-dom now). happy-dom worked fine and had stable performance rates as well, but we didn't just want to throw another dependency in the mix before talking to y'all.

I opened a new issue with zeed-dom now, referencing this issue here: holtwick/zeed-dom#2

@philippkuehn
Copy link
Contributor

Hi! Let’s see if the issue with zeed-dom can be fixed. Otherwise happy-dom looks like a great alternative.

@floriankrueger
Copy link
Contributor

The performance issue was quick-fixed already. Kudos to @holtwick for acting so fast!

@philippkuehn
Copy link
Contributor

I released a new version!

@floriankrueger
Copy link
Contributor

Already confirmed that this fixes the issue. And @holtwick fixed the underlying issue in parallel. Gonna look into the fix and report back later. It's a busy day 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Bug The issue or pullrequest is related to a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants