// based on basicSetup https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts const editorSrc = `import {keymap, highlightSpecialChars, drawSelection, highlightActiveLine, dropCursor, rectangularSelection, crosshairCursor, lineNumbers, highlightActiveLineGutter} from '@codemirror/view' import {EditorState} from '@codemirror/state' import {defaultHighlightStyle, syntaxHighlighting, indentOnInput, bracketMatching, foldGutter, foldKeymap} from '@codemirror/language' import {defaultKeymap, history, historyKeymap} from '@codemirror/commands' import {searchKeymap, highlightSelectionMatches} from '@codemirror/search' import {autocompletion, completionKeymap, closeBrackets, closeBracketsKeymap} from '@codemirror/autocomplete' import {lintKeymap} from '@codemirror/lint' /* */export const basicSetup = (() => [ lineNumbers(), highlightActiveLineGutter(), highlightSpecialChars(), history(), foldGutter(), drawSelection(), dropCursor(), EditorState.allowMultipleSelections.of(true), indentOnInput(), syntaxHighlighting(defaultHighlightStyle, {fallback: true}), bracketMatching(), closeBrackets(), autocompletion(), rectangularSelection(), crosshairCursor(), highlightActiveLine(), highlightSelectionMatches(), keymap.of([ ...closeBracketsKeymap, ...defaultKeymap, ...searchKeymap, ...historyKeymap, ...foldKeymap, ...completionKeymap, ...lintKeymap ]) ])()` function log(message, cls = 'cyan') { const el = document.createElement('pre') el.classList.add(cls) el.innerText = message document.body.append(el) ;(el.scrollIntoViewIfNeeded ?? el.scrollIntoView).call(el) } class App { jApiBaseUrl = 'https://data.jsdelivr.com/v1/' jCdnBaseUrl = 'https://cdn.jsdelivr.net/npm/' scripts = { '@rollup/browser': { version: '3.20.4', path: 'dist/rollup.browser.js', sha: 'sha256-GgOznxZmgghx1a7CH09B+VmDKtziPO5tAnC5gC+/5Kw=', }, } topLevelDeps = [ "@codemirror/autocomplete", "@codemirror/commands", "@codemirror/language", "@codemirror/lint", "@codemirror/search", "@codemirror/state", "@codemirror/view", "@codemirror/lang-javascript", ] constructor() { this.downloads = [] this.run().catch(e => { log(`${e}`) }) } checkOk(resp) { if (!resp.ok) { throw new Error(`HTTP request failed: ${resp.status}`) } } async loadDep(dep) { this.scripts[dep] = {} const dataResp = await fetch( this.jApiBaseUrl + `packages/npm/${dep}/resolved`, { headers: { 'User-Agent': 'https://codeberg.org/macchiato', }, }, ) this.checkOk(dataResp) const {version} = await dataResp.json() this.scripts[dep].version = version const pkgResp = await fetch( this.jCdnBaseUrl + `${dep}@${version}/package.json` ) this.checkOk(pkgResp) log(dep) const pkg = await pkgResp.json() this.scripts[dep].path = ( pkg.module ?? pkg.main ) this.downloads.push(this.getScript(dep)) const deps = Object.keys( pkg.dependencies || {} ).filter(dep => !(dep in this.scripts)) await Promise.allSettled(deps.map(dep => ( this.loadDep(dep) ))) } async checkIntegrity(resp, name, script) { const blob = await resp.blob() const ab = await blob.arrayBuffer() const hash = await crypto.subtle.digest( "SHA-256", ab ) const checkValue = 'sha256-' + btoa( String.fromCharCode( ...new Uint8Array(hash) ) ) if (checkValue !== script.sha) { throw new Error( 'failed integrity check: ' + `${checkValue} !== ${script.sha}` ) } return ab } async getScript(name) { log('[downloading] ' + name, 'green') const script = this.scripts[name] if (script.text) { return script.text } const url = ( this.jCdnBaseUrl + `${name}@${script.version}/${script.path}` ) const resp = await fetch(url) this.checkOk(resp) if (script.sha) { const ab = await this.checkIntegrity( resp, name, script ) script.text = new TextDecoder().decode(ab) } else { script.text = await resp.text() script.sha = resp.integrity } log('[downloaded] ' + url + ` [${script.text.length}]`, 'green') return script.text } async loadScript(name) { const text = await this.getScript(name) const s = document.createElement('script') s.text = text document.head.append(s) } get loaderPlugin() { return { name: 'loader', resolveId: async source => { if (source === 'editor.js' || source in this.scripts) { return source } }, load: async id => { if (id === 'editor.js') { return editorSrc } else if (id in this.scripts) { log(`[found] ${id}`) return this.scripts[id].text } }, } } async run() { await Promise.allSettled( this.topLevelDeps.map(dep => ( this.loadDep(dep) )) ) await Promise.allSettled(this.downloads) await this.loadScript('@rollup/browser') const { rollup } = window.rollup const input = 'editor.js' const plugins = [this.loaderPlugin] const bundle = await rollup({input, plugins}) const {output} = await bundle.generate({format: 'es'}) log(output[0].code) } } new App()