You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
loader/builder.js

126 lines
2.9 KiB
JavaScript

3 years ago
const defaultHtml = `
<!doctype html>
<html>
<head>
<title>macchiato.dev</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
</style>
</head>
<body>
${ '<' + 'script type="module" src="/app.js"><' + '/script>' }
</body>
</html>
`.trim()
const defaultIntro = `
window.Macchiato = {
location: new MLocation(),
}
`.trim()
export class Builder {
3 years ago
constructor(files) {
this.files = files
}
buildModule(file) {
const script = document.createElement('script')
script.setAttribute('type', 'module')
let initAppend = ""
let append = ""
const data = file.data.replaceAll(
/^\s*export\s+(?:class|function|async\s+function|const)\s+(\S+)/gms,
(match, p1) => {
const path = JSON.stringify(file.name)
const mref = `Macchiato.modules[${path}]`
const pref = `[${JSON.stringify(p1)}]`
initAppend = `\n\n${mref} = {}`
const s = `${mref}${pref} = ${p1}`
append += "\n" + s
return `// append: ${s}\n${match}`
}
).replaceAll(
/^\s*import\s+(\{[^}]+\})\s+from\s+("[^"]+"|')/gms,
(match, p1, p2) => {
const vars = p1.replaceAll(' as ', ': ')
const importPath = p2.slice(1, -1)
const path = JSON.stringify(
importPath.slice(
importPath.indexOf('/') + 1
)
)
const ref = `Macchiato.modules[${path}]`
return `const ${vars} = ${ref}`
}
)
script.textContent = (
"\n" + data + initAppend + append + "\n"
)
return script.outerHTML
}
3 years ago
buildReplace(filesMap) {
if ('_replace.js' in filesMap) {
const rSrc = filesMap['_replace.js']
return new Function(
rSrc.match(/\((\w+)\)/)[1],
rSrc.slice(
rSrc.indexOf('{') + 1,
rSrc.lastIndexOf('}')
)
)
} else {
return s => s
}
}
3 years ago
build() {
3 years ago
const filesMap = Object.fromEntries(
this.files.map(
({name, data}) => ([name, data])
)
)
const intro = this.buildModule({
name: '_intro.js',
data: (
'_intro.js' in filesMap ?
filesMap['_intro.js'] :
defaultIntro
),
3 years ago
})
3 years ago
const replace = this.buildReplace(filesMap)
const modules = this.files.filter(({name}) => (
name.endsWith('.js') &&
!name.startsWith('_')
)).map(file => (
this.buildModule({
...file,
data: replace(file.data),
})
))
const html = (
'index.html' in filesMap ?
filesMap['index.html'] :
defaultHtml
)
return html.replace(
'<' + 'script type="module" src="/app.js"><' + '/script>',
intro + "\n" + modules.join("\n")
)
3 years ago
}
}