Extension instanceof checking

I would like to share my experience developing an extension for CM, and make a suggestion, in case it improves future developer experience.

I’m building a language support, which is a mostly trivial retool of the lang-example template. I was testing against a React app, in a different project, which uses webpack through create-react-app for bundling. This app, of course, also depends on @code-mirror/state.

I added my package under development as a package.json file dependency to the React app, like so:

    "codemirror-lang-bool": "file:~/codemirror-lang-bool",

When I attempt to add the LanguageSupport my package generates as an extension, I got the following runtime error:

Error: Unrecognized extension value in extension set ([object Object]). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.

It’s an accurate error, but still took me quite a while to figure out was going on; I had to dive into state code with a debugger to see that my FacetProvider was failing an instanceof check.

The reason this happened, as you may have guessed, is that my bundler was using state from node_modules of the file dependency, which was causing reference equality problems with my React app’s loaded state.

This felt harder than it needed to be, and I see at least one other developer struggling with it. A couple of conversation points:

  • Guidance on how to perform integration testing, or even an example app, in lang-example may be helpful. I’d be happy to work on a PR for this.
  • Are the tradeoffs too large in using duck typing over instanceof checks in state.flatten?

We’re working from the assumption that in a client side system you really don’t want a bunch of duplicated (big) packages. Given that, instanceof provides very convenient type narrowing in TypeScript, and is much preferable to the kludges that would replace it.

Agreed & the approach makes sense for end-users. I wanted to point out that, as it stands, developing new extensions is tricky, and it’s not clear how best to set up a dev environment for integration testing. I am relatively new to CM, so perhaps it’s just an inherent learning curve. Thanks!

I agree the way dependency resolution works in node is pretty annoying for cases like this. The way I do it is to use the npm workspaces feature to check out all the packages I’m working on in a single place, sharing a node_modules folder.