Implement CM6 editor via Django widget

Anyone successfully implemented a CM6 editor as a custom Django widget? I’d like to use as a widget, similar to the many CM5 implementations I’ve seen around (django-mirror etc.). Can’t get a working solution as of yet. So any help would be appreciated.

widgets.py

class CodeMirrorEditor(forms.widgets.Widget):
    template_name = "core/codemirror/widget.html"

    def __init__(self, attrs=None, addons=[], **kwargs):
        """
        Set the config options for the CodeMirror editor.
        """
        self.config = CodeMirrorConfig()
        self.config.update(**kwargs)

        if addons:
            self.config.set_addons(addons)

        if not attrs:
            attrs = {}
        attrs['data-mirror'] = json.dumps(self.config.options)

        super().__init__(attrs)

    @property
    def media(self):
        """
        Dynamically define the css and js assets needed by the widget.
        """
        media = forms.Media(
            js=['codemirror/lang/all/cm.js']
        )

        media += forms.Media(js=['codemirror/init.js'])

        return media

init.js

const djangoMirror = (function() {
	'use strict';

	// init a CodeMirror editor for the given <textarea>
	const initMirror = function (textarea) {
		let options = JSON.parse(textarea.dataset.mirror);
		let view = new EditorView({doc: textarea.value, options})
		textarea.parentNode.insertBefore(view.dom, textarea)
		textarea.style.display = "none"
		if (textarea.form) textarea.form.addEventListener("submit", () => {
			textarea.value = view.state.doc.toString()
		})
		return view
	};

	document.addEventListener('DOMContentLoaded', () => {
		document.querySelectorAll('textarea[data-mirror]').forEach(initMirror);
	});

	return {
		initMirror: initMirror
	};
})();

Generated cm.js by using rollup, with source js:

import * as CodeMirror from "codemirror"
import * as CodeMirrorAutocomplete from "@codemirror/autocomplete"
import * as CodeMirrorCommands from "@codemirror/commands"
import * as CodeMirrorIndentationMarkers from "@replit/codemirror-indentation-markers"
import * as CodeMirrorLanguage from "@codemirror/language"
import * as CodeMirrorLint from "@codemirror/lint"
import * as CodeMirrorSearch from "@codemirror/search"
import * as CodeMirrorState from "@codemirror/state"
import * as CodeMirrorView from "@codemirror/view"

e.g. node_modules/.bin/rollup input.js -f iife -o cm.js -p @rollup/plugin-node-resolve -p @rollup/plugin-commonjs

init.js

const djangoMirror = (function() {
	'use strict';

	const initMirror = function (textarea) {
		let options = JSON.parse(textarea.dataset.mirror);
		let view = createEditorView(textarea.value)
		textarea.parentNode.insertBefore(view.dom, textarea)
		textarea.style.display = "none"
		if (textarea.form) textarea.form.addEventListener("submit", () => {
			textarea.value = view.state.doc.toString()
		})
		return view
	};

	document.addEventListener('DOMContentLoaded', () => {
		document.querySelectorAll('textarea[data-mirror]').forEach(initMirror);
	});

	return {
		initMirror: initMirror
	};
})();

Generated cm.js by using rollup, with source js:

import { EditorState } from '@codemirror/state';
import { highlightSelectionMatches } from '@codemirror/search';
import { indentWithTab, history, defaultKeymap, historyKeymap } from '@codemirror/commands';
import { foldGutter, indentOnInput, indentUnit, bracketMatching, foldKeymap, syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';
import { lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap, EditorView } from '@codemirror/view';

// Theme
import { oneDark } from "@codemirror/theme-one-dark";

// Language
import { javascript } from "@codemirror/lang-javascript";

function createEditorState(initialContents, options = {}) {
    let extensions = [
        lineNumbers(),
        highlightActiveLineGutter(),
        highlightSpecialChars(),
        history(),
        foldGutter(),
        drawSelection(),
        indentUnit.of("    "),
        EditorState.allowMultipleSelections.of(true),
        indentOnInput(),
        bracketMatching(),
        closeBrackets(),
        autocompletion(),
        rectangularSelection(),
        crosshairCursor(),
        highlightActiveLine(),
        highlightSelectionMatches(),
        keymap.of([
            indentWithTab,
            ...closeBracketsKeymap,
            ...defaultKeymap,
            ...historyKeymap,
            ...foldKeymap,
            ...completionKeymap,
        ]),
        javascript(),
        syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
    ];

    if (options.oneDark)
        extensions.push(oneDark);

    return EditorState.create({
        doc: initialContents,
        extensions
    });
}

function createEditorView(state, parent) {
    return new EditorView({ state, parent });
}

export { createEditorState, createEditorView };

window.createEditorView = function(doc) {
    const state = createEditorState(doc)
    return createEditorView(state);
};

e.g. node_modules/.bin/rollup input.js -f iife -o cm.js -p @rollup/plugin-node-resolve -p @rollup/plugin-commonjs

I suggest using Custom Elements instead of a jQuery plugin. You can even use the shadow DOM. I have used Custom Elements with CodeMirror: ristretto/code-edit.md at 9708d29839f7ccd92e4b734c17c5191b65fe80a4 - macchiato/ristretto - Codeberg.org

For the rollup bundle I attach what’s imported from CodeMirror to window.CodeMirrorModules.