ueberdosis / tiptap

The headless rich text editor framework for web artisans.
https://tiptap.dev
MIT License
27.34k stars 2.27k forks source link

Uncaught TypeError: ystate is undefined when using Collaboration and CollaborationCursor and esbuild #3039

Closed a-morphous closed 1 year ago

a-morphous commented 2 years ago

What’s the bug you are facing?

Hi there. I'm using esbuild as the build system in my app, and found that I could not use both Collaboration and CollaborationCursor together without the app error-ing out with an Uncaught TypeError: ystate is undefined error. The code builds properly, and produces this error at runtime.

Investigation suggests that this is because both plugins are creating their own instances of the y-sync plugin, and then the creation of yState fails the second time because the key no longer matches the one in the state, resulting in an undefined yState.

image

Which browser was this experienced in? Are any special extensions installed?

Firefox 102.0.1, Chrome v103. No special extensions.

How can we reproduce the bug on our side?

A minimal repro is to take the code from the CollaborationCursor example from the documentation site, and use esbuild to bundle it (with the bundle: true param). The bundle should be produced successfully, but trying to run the bundle will fail.

I have a demo repository with that minimal repro here: https://github.com/a-morphous/tiptap-esbuild-collab-issue/

You can run npm run build and then npm run serve to see the error locally. Commenting out the use of CollaborationCursor will make the code in the repository run as expected.

Can you provide a CodeSandbox?

Unlikely, given that this is most likely a build issue; I'd be happy to try to create one if it would help, though.

What did you expect to happen?

The app builds and runs without producing any errors.

Anything to add? (optional)

It looks like the schema for adding multiple plugins into tiptap (or maybe prosemirror or yjs?) is causing a name mismatch with the state, as shown when I set a breakpoint when yState gets set:

image

It doesn't error out if I take out the CollaborationCursor plugin, because the first time y-sync is created, the key and state names match; it's only the second one onwards that fails.

Did you update your dependencies?

Are you sponsoring us?

a-morphous commented 2 years ago

Upon further investigation: the built version of the example is bundling y-prosemirror twice: once from the Collaborate plugin and once from CollaborateCursor, even though they request the same version of y-prosemirror. This is causing the y-sync plugin to be registered twice, and thus the key state fetch failure.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 7 days

ArihantBapna commented 1 year ago

I'm having the same issue. I'm using vite + react. @a-morphous were you able to fix the issue?

ArihantBapna commented 1 year ago

Yes but why is it working on the test cases and not for us.

owynreveon commented 1 year ago
'use client'

import './styles.css'
import { useEffect, useState } from 'react'

import * as Y from 'yjs'
import { HocuspocusProvider } from '@hocuspocus/provider'
import { useEditor, EditorContent } from '@tiptap/react'

import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import Placeholder from '@tiptap/extension-placeholder'
import StarterKit from '@tiptap/starter-kit'

const Tiptap = () => {

  const [provider, setProvider] = useState<HocuspocusProvider>()

  useEffect(() => {
    setProvider(
      new HocuspocusProvider({
        document: new Y.Doc(),
        name: 'document',
        url: 'ws://localhost:1234'
      })
    )
  }, [])

  const extensions = provider ? [
    Collaboration.configure({ document: provider.document }),
    CollaborationCursor.configure({
      provider,
      user: { color: '#3daee9', name: 'John Doe' },
    }),
    Placeholder.configure({ placeholder: 'Start typing...' }),
    StarterKit.configure({ history: false })
  ] : [Placeholder.configure({ placeholder: 'Loading...' }), StarterKit]

  const editor = useEditor({ extensions }, [provider])
  return <EditorContent editor={editor} />
}

export default Tiptap

Hey there!

I know the Tiptap collaborative extension documentation can be a bit tricky, but don't worry! If you're looking to implement it on a React-based framework, the code I provided earlier is a great starting point.

It's a basic setup that you can easily customize to meet your specific needs. And since it's built with React, it will be a breeze to integrate with any React-based framework, including Next.js.

Oh, and here's a quick styles.css file to get you going! 💻

I hope this helps you get started! If you have any other questions or need further clarification, don't hesitate to ask. Good luck and happy coding! 😎

.ProseMirror > * + * {
  margin-top: 0.75em;
}

.ProseMirror p.is-editor-empty:first-child::before {
  color: #adb5bd;
  content: attr(data-placeholder);
  float: left;
  height: 0;
  pointer-events: none;
}

.collaboration-cursor__caret {
  border-left: 1px solid #0d0d0d;
  border-right: 1px solid #0d0d0d;
  margin-left: -1px;
  margin-right: -1px;
  pointer-events: none;
  position: relative;
  word-break: normal;
}

.collaboration-cursor__label {
  border-radius: 3px 3px 3px 0;
  color: #0d0d0d;
  font-size: 12px;
  font-weight: 600;
  left: -1px;
  padding: 0.1rem 0.3rem;
  position: absolute;
  top: -1.4em;
  user-select: none;
  white-space: nowrap;
}
a-morphous commented 1 year ago

@ArihantBapna I never did fix it, because my solution was to not include collaboration cursors and keep building the rest of the app...it's been a while since I looked into this, however this line in one of the newest releases caught my eye:

Make y-prosemirror a peer dependency (extension-collaboration) by @svenadlung in https://github.com/ueberdosis/tiptap/pull/3697

...which may, in fact, solve the problem in my case, because the original problem was being caused by both the Collaborate and CollaborateCursor plugins bundling their own versions of y-prosemirror, and then my own build system on top of that trying to deduplicate and winding up with mismatched keys.

I have not verified with my test case, though, and probably won't have any other useful information to provide.

ArihantBapna commented 1 year ago

Yup that works! Thank you for the helpful guide @ owynreveon and info @ a-morphous, much appreciated. I have collaboration cursor working in all my environments (React and Vite) now.

adrice727 commented 1 year ago

We're running into this issue right now with version 2.0.0-beta.218.

owynreveon commented 1 year ago

We're running into this issue right now with version 2.0.0-beta.218.

Well, well, well... Look who's here! 😎 Have you had the chance to give my solution a spin yet, or are you still living on the edge? 👀

adrice727 commented 1 year ago

Well, well, well... Look who's here! 😎 Have you had the chance to give my solution a spin yet, or are you still living on the edge? 👀

No longer living on the edge. I have the same setup as you do above, but state is still undefined here:

Screen Shot 2023-02-22 at 8 02 26 AM
HengCC commented 1 year ago

@adrice727 I encountered the same problem as you before. But this problem should have been fixed in the 214 version, and the 218 version I am currently using locally does not have such a problem. I am not sure if it is an environmental problem or a version problem on your side. Remove node_module directory then trynpm cache clean and try npm i again ?

adrice727 commented 1 year ago

@HengCC I'm still running into this error. I'm wondering if it has to do with the Yjs was already imported warning that I'm seeing when I start the server. When getDecorations is called, it's trying to access ystate using y-sync$1 while I only have y-sync$ on my editor state.

Screen Shot 2023-02-23 at 3 38 29 PM
HengCC commented 1 year ago

@adrice727 I encountered this problem at the time because I used the @tiptap/extension-collaboration-cursor extension, The following are the versions of the related libraries used by my client and server. FYR :)

client

"@hocuspocus/provider": "^1.0.2",
"@tiptap/extension-code-block-lowlight": "^2.0.0-beta.218",
"@tiptap/extension-collaboration": "^2.0.0-beta.218",
"@tiptap/extension-collaboration-cursor": "^2.0.0-beta.218",
"@tiptap/extension-color": "^2.0.0-beta.218",
"@tiptap/extension-dropcursor": "^2.0.0-beta.218",
"@tiptap/extension-focus": "^2.0.0-beta.218",
"@tiptap/extension-font-family": "^2.0.0-beta.218",
"@tiptap/extension-image": "^2.0.0-beta.218",
"@tiptap/extension-link": "^2.0.0-beta.218",
"@tiptap/extension-mention": "^2.0.0-beta.218",
"@tiptap/extension-placeholder": "^2.0.0-beta.218",
"@tiptap/extension-subscript": "^2.0.0-beta.218",
"@tiptap/extension-superscript": "^2.0.0-beta.218",
"@tiptap/extension-table": "^2.0.0-beta.218",
"@tiptap/extension-table-cell": "^2.0.0-beta.218",
"@tiptap/extension-table-header": "^2.0.0-beta.218",
"@tiptap/extension-table-row": "^2.0.0-beta.218",
"@tiptap/extension-task-item": "^2.0.0-beta.218",
"@tiptap/extension-task-list": "^2.0.0-beta.218",
"@tiptap/extension-text-align": "^2.0.0-beta.218",
"@tiptap/extension-text-style": "^2.0.0-beta.218",
"@tiptap/extension-typography": "^2.0.0-beta.218",
"@tiptap/extension-underline": "^2.0.0-beta.218",
"@tiptap/pm": "^2.0.0-beta.218",
"@tiptap/react": "^2.0.0-beta.218",
"@tiptap/starter-kit": "^2.0.0-beta.218",
"y-protocols": "^1.0.5",
"yjs": "^13.5.46"

server

    "@hocuspocus/extension-database": "^1.0.1",
    "@hocuspocus/extension-logger": "^1.0.1",
    "@hocuspocus/extension-monitor": "^1.0.1",
    "@hocuspocus/extension-redis": "^1.0.1",
    "@hocuspocus/server": "^1.0.1",
    "yjs": "^13.5.46"
mmilad commented 1 year ago

Have the same issue here. I copy / pasted the code in this example, which is working fine until I try to use @tiptap/extension-collaboration-cursor extension. Tried in versions 2.0.0-beta.214 - 2.0.0-beta.218.

Getting the same error as @adrice727.

I also tried to install the y-prosemirror dependency (1.2.0) without any success. After taking a look into the peer deps of @tiptap/extension-collaboration-cursor and @tiptap/extension-collaboration-cursor, i saw the the y-prosemirror version requested as peerDeps are 1.0.20. Worked after installed that version.

Thank @a-morphous

nmante commented 1 year ago

This is likely due to dependency mismatches. I had y-prosemirror installed with version 1.2.0, and tiptap 2.0.0-beta.218 packages. I was getting the ystate undefined error. The fix was to pin the y-prosemirror version to 1.0.20 like @mmilad said. Here's my package.js for anyone curious. The relevant packages are tiptap y-* packages as well as yjs.

{
  "dependencies": {
    "@auth0/auth0-spa-js": "^1.16.0",
    "@emotion/cache": "^11.4.0",
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@hocuspocus/provider": "^1.1.0",
    "@material-ui/core": "^5.0.0-beta.2",
    "@material-ui/icons": "^5.0.0-beta.1",
    "@material-ui/lab": "^5.0.0-alpha.41",
    "@mui/icons-material": "^5.11.0",
    "@mui/lab": "^5.0.0-alpha.106",
    "@mui/material": "^5.11.4",
    "@react-pdf/renderer": "^2.0.16",
    "@sentry/integrations": "^7.15.0",
    "@sentry/react": "^7.15.0",
    "@sentry/tracing": "^7.15.0",
    "@tiptap/core": "^2.0.0-beta.218",
    "@tiptap/extension-collaboration": "^2.0.0-beta.218",
    "@tiptap/extension-collaboration-cursor": "^2.0.0-beta.218",
    "@tiptap/extension-image": "^2.0.0-beta.218",
    "@tiptap/extension-link": "^2.0.0-beta.218",
    "@tiptap/extension-mention": "^2.0.0-beta.218",
    "@tiptap/extension-ordered-list": "^2.0.0-beta.218",
    "@tiptap/extension-paragraph": "^2.0.0-beta.218",
    "@tiptap/extension-placeholder": "^2.0.0-beta.218",
    "@tiptap/extension-youtube": "^2.0.0-beta.218",
    "@tiptap/pm": "^2.0.0-beta.218",
    "@tiptap/react": "^2.0.0-beta.218",
    "@tiptap/starter-kit": "^2.0.0-beta.218",
    "@tiptap/suggestion": "^2.0.0-beta.218",
    "@types/react": "^18.0.27",
    "@typescript-eslint/eslint-plugin": "^5.49.0",
    "@typescript-eslint/parser": "^5.49.0",
    "apexcharts": "^3.27.1",
    "aws-amplify": "^4.1.0",
    "compressorjs": "^1.1.1",
    "css-to-react-native": "^3.0.0",
    "date-fns": "^2.22.1",
    "dompurify": "^2.4.0",
    "emoji-essential": "^2.0.0",
    "emoji-picker-react": "^4.4.3",
    "firebase": "^8.7.0",
    "formik": "^2.2.9",
    "gray-matter": "^4.0.3",
    "history": "^5.0.0",
    "html-react-parser": "^3.0.1",
    "i18next": "^20.3.5",
    "lodash": "^4.17.21",
    "lodash.debounce": "^4.0.8",
    "nprogress": "^0.2.0",
    "numeral": "^2.0.6",
    "parchment": "^2.0.1",
    "prop-types": "^15.7.2",
    "prosemirror-state": "^1.4.2",
    "quill": "1.3.6",
    "quill-image-drop-and-paste": "^1.2.14",
    "quill-image-resize": "^3.0.9",
    "quill-image-uploader": "^1.2.3",
    "react": "^17.0.2",
    "react-apexcharts": "^1.3.9",
    "react-beautiful-dnd": "^13.1.1",
    "react-collapsible": "^2.10.0",
    "react-cool-onclickoutside": "^1.7.0",
    "react-dom": "^17.0.2",
    "react-dropzone": "^11.3.2",
    "react-fast-compare": "^3.2.0",
    "react-fast-image": "^0.0.30",
    "react-giphy-searchbox": "^1.5.4",
    "react-google-button": "^0.7.2",
    "react-helmet-async": "^1.0.9",
    "react-hot-toast": "^2.0.0",
    "react-i18next": "^11.11.4",
    "react-markdown": "^5.0.3",
    "react-quill": "^1.3.5",
    "react-router": "6.0.0-beta.6",
    "react-router-dom": "6.0.0-beta.6",
    "react-router-hash-link": "^2.4.3",
    "react-syntax-highlighter": "^15.4.3",
    "sass": "^1.56.1",
    "simplebar": "^5.3.3",
    "simplebar-react": "^2.3.3",
    "stripe": "^9.12.0",
    "style-loader": "^3.3.1",
    "stylis-plugin-rtl": "^2.1.0",
    "tippy.js": "^6.3.7",
    "typescript": "^4.9.4",
    "use-places-autocomplete": "^4.0.0",
    "uuid": "^9.0.0",
    "y-prosemirror": "1.0.20",
    "y-protocols": "^1.0.5",
    "yjs": "^13.5.47",
    "yup": "^0.32.9"
  },
  "devDependencies": {
    "@babel/core": "^7.14.8",
    "@babel/eslint-parser": "^7.14.7",
    "@babel/preset-react": "^7.14.5",
    "@types/google.maps": "^3.50.4",
    "eslint": "^7.31.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-config-airbnb-typescript": "^12.3.1",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "npm-run-all": "^4.1.5",
    "react-scripts": "4.0.3"
  }
}
adrice727 commented 1 year ago

Thank you, @mmilad and @nmante. I finally got it working by pinning y-prosemirror to 1.0.20.