Just curious if anyone has tried getting codemirror running inside of a virtual dom environment like JSDOM. I’m generating server side html with React and then bootstrapping codemirror in the browser, but this leads to a rather long and unpleasant flash of unstyled text area. I’d like to be able to generate a full (static) DOM tree with codemirror so that things are at least styled before they are functional. The current attempt in JSDOM leads to: TypeError: Object [ BODY ] has no method ‘createTextRange’, which I’m looking into.
Alright, injecting this code is enough to get it to not error out:
document.createRange = function() {
return {
setEnd: function(){},
setStart: function(){},
getBoundingClientRect: function(){
return {right: 0};
}
}
};
It still doesn’t render correctly, but I’ll keep working on it. I see PhantomJS is how you’re running tests, but I think it will be too heavy for my use case. I may try that out as well.
Hey man!, were you able to run it in the backend or not? I’m getting an error:
/Users/danielzamorano/workspace/basepath/node_modules/codemirror/lib/codemirror.js:25
var gecko = /gecko/\d/i.test(navigator.userAgent);
I’m also using JSDOM
Nevermind I found the solution:
var expect = require(‘chai’).expect;
var jsdom = require(‘node-jsdom’);
var sinon = require(‘sinon’);
// virtual DOM environment for codemirror
global.navigator = ‘gecko’;
global.document = jsdom.jsdom();
global.window = global.document.defaultView;
var markdownEditor = require(’…/…/…/app/Authoring/Edit/markdown_editor.js’);
I used boucher’s approach and that’s how initialization looks for me (I believe you shoud define createTextRange, not just createRange):
import jsdom from 'jsdom'
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')
global.document = doc
global.window = doc.defaultView
global.document.body.createTextRange = function() {
return {
setEnd: function(){},
setStart: function(){},
getBoundingClientRect: function(){
return {right: 0};
},
getClientRects: function(){
return {
length: 0,
left: 0,
right: 0
}
}
}
}
Still see this warning document.body.createTextRange is not a function
I was able to implement a similar polyfill by instead putting it on the Document prototype
Something like the following:
const polyfills = {
createRange() {
// @ts-ignore
Document.prototype.createRange = function () {
return {
setEnd: function () {},
setStart: function () {},
getBoundingClientRect: function () {
return { right: 0 }
},
getClientRects: function () {
return {
length: 0,
left: 0,
right: 0,
}
},
}
}
},
}
polyfills.createRange()
This was useful for ProseMirror, but something similar may be used to fix this in CodeMirror as well.
Trying to spawn a basic setup in codemirror 6:
import("./haverbeke_2020_codemirror.js").then(({EditorState,EditorView})=>
new EditorView({state:EditorState.create({doc:""})}));
(the import resolution doesn’t even matter actually) and I get:
Uncaught ReferenceError: btoa is not defined
at underline (VM1218 haverbeke_2020_codemirror.js:15010)
at VM1218 haverbeke_2020_codemirror.js:15034
at ModuleJob.run (VM95 module_job:146)
at async Loader.import (VM93 loader:165)
the throwing function looks like this:
function underline(color) {
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="6" height="3">
<path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>
</svg>`;
return `url('data:image/svg+xml;base64,${btoa(svg)}')`;
}
btoa seems easily replaceable with Buffer.from,
should this be a github issue, or am I missing
something that there can be done?
My intention is basically to maintain a server-side
state of a document during a collaborative session.
Oh right, I just had my package.json point to an earlier version. btoa went away, now with the same basic setup (and using JSDOM to set a window and document object on globalThis), I am getting
Uncaught ReferenceError: MutationObserver is not defined
at new DOMObserver (VM1154 haverbeke_2020_codemirror.js:8874)
at new EditorView (VM1154 haverbeke_2020_codemirror.js:9334)
the source looks like this:
class DOMObserver
{constructor(view, onChange, onScrollChanged) {
this.view = view;
this.onChange = onChange;
this.onScrollChanged = onScrollChanged;
[...]
this.dom = view.contentDOM;
this.observer = new MutationObserver(mutations=>{})
[...]
Has anyone managed to get around MutationObserver?
Due to my mentioned use case,
I’m only going to run transactions against this view/state,
not observed mutations so I could probably do without this part.
…actually, nevermind I realised I don’t need the View for this.
now I just need to figure out how to shuffle transactions on a State.
You can run transactions without having a view—@codemirror/next/state
runs fine in node without JSDOM. But creating an actual view instance there is probably not going to work in any meaningful way.