prasi-bun/pkgs/core/build.ts

155 lines
3.8 KiB
TypeScript

import brotliPromise from "brotli-wasm";
import { spawn } from "bun";
import { dir } from "dir";
import { Plugin, context } from "esbuild";
import { $ } from "execa";
import { fdir } from "fdir";
import { statSync } from "fs";
import {
listAsync,
removeAsync,
writeAsync,
inspectTree,
existsAsync,
copyAsync,
} from "fs-jetpack";
const brotli = await brotliPromise;
await removeAsync(dir.path("app/web/.parcel-cache"));
await removeAsync(dir.path("app/static"));
const args = [
"node",
dir.path("node_modules/.bin/parcel"),
"build",
"./src/index.tsx",
// "--no-optimize",
"--no-scope-hoist",
"--dist-dir",
dir.path(`app/static`),
];
const parcel = spawn({
cmd: args,
cwd: dir.path("app/web"),
stdio: ["ignore", "inherit", "inherit"],
});
await parcel.exited;
const public_br = dir.path("app/web/public-br");
if (!(await existsAsync(public_br))) {
const api = new fdir().withRelativePaths().crawl(dir.path("app/web/public"));
const files = api.sync();
if (files) {
await Promise.all(
files.map(async (file) => {
const br = brotli.compress(
new Uint8Array(
await Bun.file(dir.path(`app/web/public/${file}`)).arrayBuffer()
),
{ quality: 11 }
);
if (br) {
console.log(`Compressing ${file}`);
await writeAsync(
dir.path(`app/web/public-br/${file}`),
Buffer.from(br)
);
}
})
);
}
}
const static_br = dir.path("app/static-br");
await removeAsync(static_br);
const files = await listAsync(dir.path("app/static"));
if (files) {
await Promise.all(
files
.filter((file) => statSync(dir.path(`app/static/${file}`)).isFile())
.map(async (file) => {
if (!(await Bun.file(dir.path(`app/static-br/${file}`)).exists())) {
const br = brotli.compress(
new Uint8Array(
await Bun.file(dir.path(`app/static/${file}`)).arrayBuffer()
),
{ quality: 11 }
);
if (br) {
console.log(`Compressing ${file}`);
await writeAsync(
dir.path(`app/static-br/${file}`),
Buffer.from(br)
);
}
}
})
);
const pub = await listAsync(dir.path("app/web/public-br"));
if (pub) {
await Promise.all(
pub.map((file) =>
copyAsync(
dir.path(`app/web/public-br/${file}`),
dir.path(`app/static-br/${file}`),
{ overwrite: true }
)
)
);
}
}
const buildSite = async () => {
await removeAsync(dir.path("app/srv/site"));
const onEndPlugin: Plugin = {
name: "on-end",
setup(build) {
build.onEnd(async (result) => {
console.log("Compressing deploy");
await removeAsync(dir.path("app/srv/site.zip"));
await $({ cwd: dir.path("app/srv") })`zip -r site.zip site`;
process.exit(0);
});
},
};
console.log("Building deploy");
const ctx = await context({
bundle: true,
absWorkingDir: dir.path(""),
entryPoints: [dir.path("app/web/src/render/site/site.tsx")],
outdir: dir.path("app/srv/site"),
splitting: true,
format: "esm",
jsx: "transform",
minify: true,
sourcemap: true,
logLevel: "error",
plugins: [onEndPlugin],
define: {
"process.env.NODE_ENV": `"production"`,
},
});
await ctx.rebuild();
await writeAsync(
dir.path("app/srv/site/index.html"),
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="https://prasi.app/index.css">
</head>
<body class="flex-col flex-1 w-full min-h-screen flex opacity-0">
<div id="root"></div>
<script src="/site.js" type="module"></script>
</body>
</html>`
);
};
await buildSite();