168 lines
5.6 KiB
JavaScript
168 lines
5.6 KiB
JavaScript
const esbuild = require("esbuild")
|
|
const fs = require("fs")
|
|
const http = require("http")
|
|
const path = require("path")
|
|
|
|
const config = require(process.cwd() + (process.argv?.length > 3 ? "/" + process.argv[3] : "/esbuild.config.js"))
|
|
const { watch } = require("chokidar")
|
|
|
|
function log(str, clear) {
|
|
if (clear && process.stdout.cursorTo && process.stdout.clearScreenDown) {
|
|
process.stdout.cursorTo(0, 0)
|
|
process.stdout.clearScreenDown()
|
|
}
|
|
console.log("\x1b[36m%s\x1b[0m", str)
|
|
}
|
|
|
|
let buildResults
|
|
let ctx
|
|
|
|
function cleanChunks() {
|
|
const outdir = config.options.outdir
|
|
if (!outdir) return
|
|
const chunksDir = path.join(outdir, "chunks")
|
|
if (fs.existsSync(chunksDir)) {
|
|
fs.rmSync(chunksDir, { recursive: true })
|
|
}
|
|
}
|
|
|
|
async function build(catchError) {
|
|
if (config.writeBuildInfo) {
|
|
config.writeBuildInfo()
|
|
}
|
|
cleanChunks()
|
|
if (!ctx) ctx = await esbuild.context(config.options)
|
|
log((buildResults ? "re" : "") + "building...")
|
|
const timerStart = Date.now()
|
|
try {
|
|
buildResults = await ctx.rebuild()
|
|
if (config.options.metafile) {
|
|
fs.writeFileSync(
|
|
(config.options.outfile ? path.dirname(config.options.outfile) : config.options.outdir) + "/meta.json",
|
|
JSON.stringify(buildResults.metafile, null, 4)
|
|
)
|
|
}
|
|
} catch (e) {
|
|
console.log(e)
|
|
if (!catchError) throw e
|
|
}
|
|
|
|
const timerEnd = Date.now()
|
|
log(`built in ${timerEnd - timerStart}ms.`)
|
|
}
|
|
|
|
let bs
|
|
switch (process.argv?.length > 2 ? process.argv[2] : "build") {
|
|
case "serve":
|
|
console.log("\x1b[36m%s\x1b[0mserving...")
|
|
esbuild.context(config.options).then(function (_ctx) {
|
|
_ctx.serve(config.serve).catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|
|
})
|
|
break
|
|
case "start":
|
|
bs = require("browser-sync")
|
|
bs.init(config.browserSync)
|
|
case "watch":
|
|
// config.options.incremental = true
|
|
build(true).then(() => {
|
|
if (bs) {
|
|
bs.reload()
|
|
}
|
|
})
|
|
// Trigger tibi-server project reload for api/ changes
|
|
const apiDir = path.resolve("api") + "/"
|
|
let reloadInFlight = false
|
|
let reloadQueued = false
|
|
function reloadTibi() {
|
|
if (!config.tibiReloadUrl || !config.adminToken) {
|
|
log("tibi-server reload: missing URL or token (running in mock mode?)")
|
|
return
|
|
}
|
|
if (reloadInFlight) {
|
|
reloadQueued = true
|
|
log("tibi-server reload: already in flight, queuing another...")
|
|
return
|
|
}
|
|
reloadInFlight = true
|
|
const parts = new URL(config.tibiReloadUrl)
|
|
const opts = {
|
|
hostname: parts.hostname,
|
|
port: parts.port,
|
|
path: parts.pathname,
|
|
method: "POST",
|
|
headers: { Authorization: "Bearer " + config.adminToken },
|
|
timeout: 5000,
|
|
}
|
|
let body = ""
|
|
const finishReload = () => {
|
|
reloadInFlight = false
|
|
if (reloadQueued) {
|
|
reloadQueued = false
|
|
log("tibi-server reload: executing queued reload...")
|
|
reloadTibi()
|
|
}
|
|
}
|
|
const req = http.request(opts, (res) => {
|
|
res.on("data", (chunk) => (body += chunk))
|
|
res.on("end", () => {
|
|
if (res.statusCode === 200) {
|
|
log("tibi-server reloaded ✓")
|
|
} else {
|
|
console.error(
|
|
"\x1b[33mtibi-server reload: HTTP " + res.statusCode + " — " + body.trim() + "\x1b[0m"
|
|
)
|
|
}
|
|
finishReload()
|
|
})
|
|
})
|
|
req.on("error", (e) => {
|
|
console.error("\x1b[33mtibi-server reload failed: " + e.message + "\x1b[0m")
|
|
finishReload()
|
|
})
|
|
req.end()
|
|
}
|
|
|
|
const { path: watchPaths, ignored: watchIgnored, ...watchRest } = config.watch
|
|
const watcher = watch(watchPaths, { ignored: watchIgnored, ...watchRest })
|
|
log("watching files...")
|
|
const DEBOUNCE_MS = 200
|
|
let frontendTimer, apiTimer
|
|
watcher.on("change", function (changedPath) {
|
|
log(`${changedPath} changed`, true)
|
|
if (changedPath.startsWith(apiDir)) {
|
|
clearTimeout(apiTimer)
|
|
apiTimer = setTimeout(() => {
|
|
log("api/ change detected → reloading tibi-server...")
|
|
reloadTibi()
|
|
}, DEBOUNCE_MS)
|
|
} else {
|
|
clearTimeout(frontendTimer)
|
|
frontendTimer = setTimeout(() => {
|
|
build(true).then(() => {
|
|
if (bs) {
|
|
bs.reload()
|
|
}
|
|
})
|
|
}, DEBOUNCE_MS)
|
|
}
|
|
})
|
|
break
|
|
default:
|
|
if (config.writeBuildInfo) {
|
|
config.writeBuildInfo()
|
|
}
|
|
cleanChunks()
|
|
esbuild.build(config.options).then(function (buildResults) {
|
|
if (config.options.metafile) {
|
|
fs.writeFileSync(
|
|
(config.options.outfile ? path.dirname(config.options.outfile) : config.options.outdir) +
|
|
"/meta.json",
|
|
JSON.stringify(buildResults.metafile, null, 4)
|
|
)
|
|
}
|
|
})
|
|
}
|