use web component to show build progress; use Promise.all so errors are shown

pages
bat 3 years ago
parent ba18185138
commit d56ede02fe

122
app.js

@ -40,15 +40,7 @@ import {lintKeymap} from '@codemirror/lint'
]) ])
])()` ])()`
function log(message, cls = 'cyan') { class Builder {
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/' jApiBaseUrl = 'https://data.jsdelivr.com/v1/'
jCdnBaseUrl = 'https://cdn.jsdelivr.net/npm/' jCdnBaseUrl = 'https://cdn.jsdelivr.net/npm/'
@ -73,9 +65,6 @@ class App {
constructor() { constructor() {
this.downloads = [] this.downloads = []
this.run().catch(e => {
log(`${e}`)
})
} }
checkOk(resp) { checkOk(resp) {
@ -104,7 +93,7 @@ class App {
`${dep}@${version}/package.json` `${dep}@${version}/package.json`
) )
this.checkOk(pkgResp) this.checkOk(pkgResp)
log(dep) this.log(dep)
const pkg = await pkgResp.json() const pkg = await pkgResp.json()
this.scripts[dep].path = ( this.scripts[dep].path = (
pkg.module ?? pkg.main pkg.module ?? pkg.main
@ -118,18 +107,22 @@ class App {
))) )))
} }
async checkIntegrity(resp, name, script) { async sha(ab) {
const blob = await resp.blob()
const ab = await blob.arrayBuffer()
const hash = await crypto.subtle.digest( const hash = await crypto.subtle.digest(
"SHA-256", ab "SHA-256", ab
) )
const checkValue = 'sha256-' + btoa( return 'sha256-' + btoa(
String.fromCharCode( String.fromCharCode(
...new Uint8Array(hash) ...new Uint8Array(hash)
) )
) )
if (checkValue !== script.sha) { }
async checkIntegrity(resp, name, script) {
const blob = await resp.blob()
const ab = await blob.arrayBuffer()
const sha = await this.sha(ab)
if (sha !== script.sha) {
throw new Error( throw new Error(
'failed integrity check: ' + 'failed integrity check: ' +
`${checkValue} !== ${script.sha}` `${checkValue} !== ${script.sha}`
@ -139,7 +132,7 @@ class App {
} }
async getScript(name) { async getScript(name) {
log('[downloading] ' + name, 'green') this.log('[downloading] ' + name, 'green')
const script = this.scripts[name] const script = this.scripts[name]
if (script.text) { if (script.text) {
return script.text return script.text
@ -159,7 +152,10 @@ class App {
script.text = await resp.text() script.text = await resp.text()
script.sha = resp.integrity script.sha = resp.integrity
} }
log('[downloaded] ' + url + ` [${script.text.length}]`, 'green') this.log(
'[downloaded] ' + url + ` [${script.text.length}]`,
'green'
)
return script.text return script.text
} }
@ -180,29 +176,105 @@ class App {
}, },
load: async id => { load: async id => {
if (id === 'editor.js') { if (id === 'editor.js') {
this.log(`[found] editor.js`)
return editorSrc return editorSrc
} else if (id in this.scripts) { } else if (id in this.scripts) {
log(`[found] ${id}`) this.log(`[found] ${id}`)
return this.scripts[id].text return this.scripts[id].text
} }
}, },
} }
} }
async run() { async build() {
await Promise.allSettled( const result = await Promise.all(
this.topLevelDeps.map(dep => ( this.topLevelDeps.map(dep => (
this.loadDep(dep) this.loadDep(dep)
)) ))
) )
await Promise.allSettled(this.downloads) await Promise.all(this.downloads)
await this.loadScript('@rollup/browser') await this.loadScript('@rollup/browser')
const { rollup } = window.rollup const { rollup } = window.rollup
const input = 'editor.js' const input = 'editor.js'
const plugins = [this.loaderPlugin] const plugins = [this.loaderPlugin]
const bundle = await rollup({input, plugins}) const bundle = await rollup({input, plugins})
const {output} = await bundle.generate({format: 'es'}) const {output} = await bundle.generate({format: 'es'})
log(output[0].code) this.code = output[0].code
this.sha = await this.sha(
new TextEncoder().encode(this.code)
)
this.log(`built ${this.sha}`)
}
}
class BuildView extends HTMLElement {
constructor() {
super()
this.attachShadow({mode: 'open'})
this.shadowRoot.adoptedStyleSheets = [
this.constructor.styleSheet
]
}
log(message, cls = 'cyan') {
const el = document.createElement('pre')
el.classList.add(cls)
el.innerText = message
this.shadowRoot.append(el)
;(el.scrollIntoViewIfNeeded ?? el.scrollIntoView).call(el)
}
static get styleSheet() {
if (this._styleSheet === undefined) {
this._styleSheet = new CSSStyleSheet()
this._styleSheet.replaceSync(this.css)
}
return this._styleSheet
}
static css = `
:host {
display: flex;
flex-direction: column;
align-items: stretch;
margin: 10px;
gap: 10px;
}
pre {
padding: 8px;
border-radius: 5px;
margin: 0;
overflow-x: auto;
}
pre.cyan {
background: cyan;
}
pre.green {
background: lightgreen;
}
`
}
customElements.define('m-build-view', BuildView)
class App {
constructor() {
this.builder = new Builder()
this.buildView = document.createElement('m-build-view')
document.body.append(this.buildView)
this.builder.log = this.buildView.log.bind(this.buildView)
this.build()
}
async build() {
try {
await this.builder.build()
} catch (e) {
this.buildView.log(`${e}`)
}
} }
} }

@ -9,21 +9,4 @@ body {
padding: 0; padding: 0;
margin: 0; margin: 0;
background: navy; 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…
Cancel
Save