102 lines
3.9 KiB
TypeScript
102 lines
3.9 KiB
TypeScript
|
|
import { defineConfig } from 'vite'
|
||
|
|
import { readFileSync } from 'fs'
|
||
|
|
import { basename, extname, join } from 'path'
|
||
|
|
import { minify } from 'html-minifier-next'
|
||
|
|
import { buildSync } from 'esbuild'
|
||
|
|
|
||
|
|
const ROOT = join(process.cwd(), 'application')
|
||
|
|
const DIST = join(ROOT, 'dist')
|
||
|
|
|
||
|
|
export default defineConfig({
|
||
|
|
root: ROOT,
|
||
|
|
build: {
|
||
|
|
outDir: DIST,
|
||
|
|
modulePreload: {
|
||
|
|
polyfill: false,
|
||
|
|
},
|
||
|
|
rollupOptions: {
|
||
|
|
input: [
|
||
|
|
join(ROOT, 'index.html'),
|
||
|
|
join(ROOT, 'not-found.html'),
|
||
|
|
join(ROOT, 'qr-code.html'),
|
||
|
|
join(ROOT, 'secret-sprites.html'),
|
||
|
|
join(ROOT, 'secret-picross.html'),
|
||
|
|
join(ROOT, 'about.html'),
|
||
|
|
],
|
||
|
|
output: {
|
||
|
|
hashCharacters: 'hex',
|
||
|
|
entryFileNames: 'assets/[name].[hash].js',
|
||
|
|
chunkFileNames: 'assets/[name].[hash].js',
|
||
|
|
assetFileNames: 'assets/[name].[hash][extname]',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
server: {
|
||
|
|
strictPort: true,
|
||
|
|
port: 5173,
|
||
|
|
},
|
||
|
|
plugins: [
|
||
|
|
{
|
||
|
|
name: 'server-uri',
|
||
|
|
configureServer(server) {
|
||
|
|
const includeDir = join(ROOT, 'include')
|
||
|
|
server.watcher.add(includeDir)
|
||
|
|
server.watcher.on('change', (file) => {
|
||
|
|
if (file.startsWith(includeDir)) {
|
||
|
|
server.ws.send({ type: 'full-reload' })
|
||
|
|
}
|
||
|
|
})
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
name: 'html-generator',
|
||
|
|
transformIndexHtml(html, _) {
|
||
|
|
html = html.replaceAll(/{{ include-b64 '(.*?)' '(.*?)' }}/g, (_, mime: string, filepath: string) => {
|
||
|
|
const content = readFileSync(join(ROOT, filepath), 'base64')
|
||
|
|
return `data:${mime};base64,${content}`
|
||
|
|
})
|
||
|
|
|
||
|
|
html = html.replaceAll(/{{ include-tag '(.*?)' '(.*?)' }}/g, (_, tag: string, filepath: string) => {
|
||
|
|
if (tag === 'script' && filepath.endsWith('.ts')) {
|
||
|
|
const result = buildSync({
|
||
|
|
entryPoints: [join(ROOT, filepath)],
|
||
|
|
bundle: true,
|
||
|
|
write: false,
|
||
|
|
format: 'esm',
|
||
|
|
target: 'es2023',
|
||
|
|
})
|
||
|
|
return `<script>requestIdleCallback(()=>{${result.outputFiles[0].text}})</script>`
|
||
|
|
} else {
|
||
|
|
const content = readFileSync(join(ROOT, filepath), 'utf8')
|
||
|
|
return `<${tag}>\n${content}</${tag}>`
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
html = html.replaceAll(/{{ include-article '(.*?)' }}/g, (_, filepath: string) => {
|
||
|
|
const id = basename(filepath).slice(0, -extname(filepath).length)
|
||
|
|
const content = readFileSync(join(ROOT, filepath), 'utf8').replaceAll(
|
||
|
|
/<pre([^>]*)>\s*\n([\s\S]*?)<\/pre>/g,
|
||
|
|
(_, attributes, inner) => {
|
||
|
|
const lines = inner.split('\n')
|
||
|
|
const indent = lines[0].match(/^(\s*)/)?.[1].length ?? 0
|
||
|
|
const trimmed = lines
|
||
|
|
.map((l: string) => l.slice(indent))
|
||
|
|
.join('\n')
|
||
|
|
.trim()
|
||
|
|
return `<pre${attributes}>${trimmed}</pre>`
|
||
|
|
},
|
||
|
|
)
|
||
|
|
return `<template class="article" id="${id}">\n${content}</template>`
|
||
|
|
})
|
||
|
|
|
||
|
|
return minify(html, {
|
||
|
|
collapseWhitespace: true,
|
||
|
|
removeComments: true,
|
||
|
|
minifyCSS: true,
|
||
|
|
minifyJS: true,
|
||
|
|
})
|
||
|
|
},
|
||
|
|
},
|
||
|
|
],
|
||
|
|
})
|