2 changed files with 172 additions and 0 deletions
@ -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() |
|||
@ -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; |
|||
} |
|||
Loading…
Reference in new issue