Published
Edited
Mar 4, 2022
Importers
15 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
editor
Insert cell
viewof CM = loadCodeMirror({view: true, modes: ['javascript', 'sparql', 'clike'], themes: [themeObservableURL, ...themes]})
Insert cell
async function loadCodeMirror(options = {}) {
const {
version = 'latest',
themes = [],
modes = [],
minify = true,
view = false,
viewLabel = (version, modes, themes) => `CodeMirror ${version}${!modes.length ? '' : ` (${modes.join(', ')})`}`,
} = options;
// d3-require uses jsdelivr.com by default, which in turn allows
// us to minify files by attaching ".min" to filenames.
const suffix = minify ? '.min' : '';
// Adding "." to the end keeps d3-require from appending ".js" to the path.
const PATH = (await require.resolve(`codemirror@${version}/.`)).slice(0, -2);
const cm = require(`${PATH}/lib/codemirror${suffix}.js`).then(cm => {
if(!modes.length) return cm;
// Mode scripts reference CodeMirror via "../../lib/codemirror".
// To maintain encapsulation we alias that path to our already resolved CM package.
return require.alias({'../../lib/codemirror': cm})(
// Expand mode URLs and pass to our aliased require().
...modes.map(mode => `${PATH}/mode/${mode}/${mode}${suffix}.js`)
)
// The mode scripts modify our CM directly, so only the instance needs to be returned.
.then(() => cm);
});
// While the scripts are loading, let's load the CSS as well.
const isUrl = str => {
try { new URL(str); return true }
catch(e) { return false }
};
const fetchText = async url => {
const r = await fetch(url);
if(!r.ok) throw Error(`${r.status} (${url})`);
return r.text();
};
const css = Promise.all([
// The editor stylesheet.
fetchText(`${PATH}/lib/codemirror${suffix}.css`),
// Expand theme stylesheet URLs.
...themes.map(name => fetchText(isUrl(name) ? name : `${PATH}/theme/${name}${suffix}.css`))
]);
// Wait until all scripts and stylesheets are loaded.
return Promise.all([cm, css]).then(([cm, css]) => {
// Conveniently concat CSS contents on string conversion.
Object.assign(css, { toString() { return this.join('\n') } });
if(view) return Object.defineProperty(
html`<pre>${viewLabel(cm.version, modes, themes)}</pre><style>${css}`,
'value',
{ value: cm }
);
// Attach the CSS, but keep the file contents separated.
cm.CSS = css;
return cm;
});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more