|
|
|
|
@ -40,15 +40,7 @@ import {lintKeymap} from '@codemirror/lint'
|
|
|
|
|
])
|
|
|
|
|
])()`
|
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
class Builder {
|
|
|
|
|
jApiBaseUrl = 'https://data.jsdelivr.com/v1/'
|
|
|
|
|
jCdnBaseUrl = 'https://cdn.jsdelivr.net/npm/'
|
|
|
|
|
|
|
|
|
|
@ -73,9 +65,6 @@ class App {
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
this.downloads = []
|
|
|
|
|
this.run().catch(e => {
|
|
|
|
|
log(`${e}`)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checkOk(resp) {
|
|
|
|
|
@ -104,7 +93,7 @@ class App {
|
|
|
|
|
`${dep}@${version}/package.json`
|
|
|
|
|
)
|
|
|
|
|
this.checkOk(pkgResp)
|
|
|
|
|
log(dep)
|
|
|
|
|
this.log(dep)
|
|
|
|
|
const pkg = await pkgResp.json()
|
|
|
|
|
this.scripts[dep].path = (
|
|
|
|
|
pkg.module ?? pkg.main
|
|
|
|
|
@ -118,18 +107,22 @@ class App {
|
|
|
|
|
)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async checkIntegrity(resp, name, script) {
|
|
|
|
|
const blob = await resp.blob()
|
|
|
|
|
const ab = await blob.arrayBuffer()
|
|
|
|
|
async sha(ab) {
|
|
|
|
|
const hash = await crypto.subtle.digest(
|
|
|
|
|
"SHA-256", ab
|
|
|
|
|
)
|
|
|
|
|
const checkValue = 'sha256-' + btoa(
|
|
|
|
|
return 'sha256-' + btoa(
|
|
|
|
|
String.fromCharCode(
|
|
|
|
|
...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(
|
|
|
|
|
'failed integrity check: ' +
|
|
|
|
|
`${checkValue} !== ${script.sha}`
|
|
|
|
|
@ -139,7 +132,7 @@ class App {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getScript(name) {
|
|
|
|
|
log('[downloading] ' + name, 'green')
|
|
|
|
|
this.log('[downloading] ' + name, 'green')
|
|
|
|
|
const script = this.scripts[name]
|
|
|
|
|
if (script.text) {
|
|
|
|
|
return script.text
|
|
|
|
|
@ -159,7 +152,10 @@ class App {
|
|
|
|
|
script.text = await resp.text()
|
|
|
|
|
script.sha = resp.integrity
|
|
|
|
|
}
|
|
|
|
|
log('[downloaded] ' + url + ` [${script.text.length}]`, 'green')
|
|
|
|
|
this.log(
|
|
|
|
|
'[downloaded] ' + url + ` [${script.text.length}]`,
|
|
|
|
|
'green'
|
|
|
|
|
)
|
|
|
|
|
return script.text
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -180,29 +176,105 @@ class App {
|
|
|
|
|
},
|
|
|
|
|
load: async id => {
|
|
|
|
|
if (id === 'editor.js') {
|
|
|
|
|
this.log(`[found] editor.js`)
|
|
|
|
|
return editorSrc
|
|
|
|
|
} else if (id in this.scripts) {
|
|
|
|
|
log(`[found] ${id}`)
|
|
|
|
|
this.log(`[found] ${id}`)
|
|
|
|
|
return this.scripts[id].text
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async run() {
|
|
|
|
|
await Promise.allSettled(
|
|
|
|
|
async build() {
|
|
|
|
|
const result = await Promise.all(
|
|
|
|
|
this.topLevelDeps.map(dep => (
|
|
|
|
|
this.loadDep(dep)
|
|
|
|
|
))
|
|
|
|
|
)
|
|
|
|
|
await Promise.allSettled(this.downloads)
|
|
|
|
|
await Promise.all(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)
|
|
|
|
|
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}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|