From 649ed6db6c55f81304805af9461c2b42268cebed Mon Sep 17 00:00:00 2001 From: bat Date: Wed, 19 Apr 2023 06:38:47 +0000 Subject: [PATCH] Download files --- app.js | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ style.css | 29 +++++++++++ 2 files changed, 172 insertions(+) create mode 100644 app.js create mode 100644 style.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..8a05429 --- /dev/null +++ b/app.js @@ -0,0 +1,143 @@ +function log(message, cls = 'cyan') { + const el = document.createElement('pre') + el.classList.add(cls) + el.innerText = message + document.body.append(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, '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) + } + + 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 + //output.value = `${typeof rollup}` + } +} + +new App() \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..36da046 --- /dev/null +++ b/style.css @@ -0,0 +1,29 @@ +html, body { + min-height: 100%; +} + +body { + display: flex; + flex-direction: column; + align-items: stretch; + padding: 0; + margin: 0; + background: navy; + margin: 10px; + gap: 10px; +} + +pre { + padding: 8px; + border-radius: 5px; + margin: 0; + overflow-x: auto; +} + +pre.cyan { + background: cyan; +} + +pre.green { + background: lightgreen; +} \ No newline at end of file