import esbuild from "esbuild"; import path from "path"; import fs from "fs"; import sass from "sass"; import { PluginBuild } from "esbuild"; // this should give us the current directory where // the project is being built const BASE = process.cwd(); const distProd = path.join(BASE, "dist", "prod"); const distDev = path.join(BASE, "dist", "dev"); const sourceDir = path.join(BASE, "src"); const preact = path.join( BASE, "node_modules", "preact", "compat", "dist", "compat.module.js", ); // https://preactjs.com/guide/v8/switching-to-preact/#how-to-alias-preact-compat const preactCompatPlugin: esbuild.Plugin = { name: "preact-compat", setup(build) { build.onResolve({ filter: /^(react-dom|react)$/ }, (args) => { return { path: preact, }; }); }, }; export function getFilesInSource(regex: RegExp): string[] { return getFilesInDirectory(sourceDir, regex); } function getFilesInDirectory(startPath: string, regex: RegExp): string[] { if (!fs.existsSync(startPath)) { return []; } const files = fs.readdirSync(startPath); const result = files .flatMap((file) => { const filename = path.join(startPath, file); const stat = fs.lstatSync(filename); if (stat.isDirectory()) { return getFilesInDirectory(filename, regex); } if (regex.test(filename)) { return [filename]; } return []; }) .filter((x) => !!x); return result; } let GIT_ROOT = BASE; while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") { GIT_ROOT = path.join(GIT_ROOT, "../"); } if (GIT_ROOT === "/") { // eslint-disable-next-line no-undef console.log("not found"); // eslint-disable-next-line no-undef process.exit(1); } const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash(); const buf = fs.readFileSync(path.join(BASE, "package.json")); let _package = JSON.parse(buf.toString("utf-8")); function git_hash() { const rev = fs .readFileSync(path.join(GIT_ROOT, ".git", "HEAD")) .toString() .trim() .split(/.*[: ]/) .slice(-1)[0]; if (rev.indexOf("/") === -1) { return rev; } else { return fs.readFileSync(path.join(GIT_ROOT, ".git", rev)).toString().trim(); } } // FIXME: Put this into some helper library. function copyFilesPlugin(files: Array) { return { name: "copy-files", setup(build: PluginBuild) { const outDir = build.initialOptions.outdir; if (outDir === undefined) throw Error("esbuild build options does not specify outdir"); build.onEnd(() => { for (const file of files) { const name = path.parse(file).base; fs.copyFileSync(file, path.join(outDir, name)); } }); }, }; } const DEFAULT_SASS_FILTER = /\.(s[ac]ss|css)$/; const buildSassPlugin: esbuild.Plugin = { name: "custom-build-sass", setup(build) { build.onLoad({ filter: DEFAULT_SASS_FILTER }, ({ path: file }) => { const resolveDir = path.dirname(file); const { css: contents } = sass.compile(file, { loadPaths: ["./"] }); return { resolveDir, loader: "css", contents, }; }); }, }; const buildConfig: esbuild.BuildOptions = { bundle: true, minify: false, loader: { ".svg": "file", ".png": "dataurl", ".jpeg": "dataurl", ".ttf": "file", ".woff": "file", ".woff2": "file", ".eot": "file", }, target: ["es6"], format: "esm", platform: "browser", sourcemap: true, jsxFactory: "h", jsxFragment: "Fragment", define: { __VERSION__: `"${_package.version}"`, __GIT_HASH__: `"${GIT_HASH}"`, }, plugins: [ preactCompatPlugin, copyFilesPlugin(["./src/index.html"]), buildSassPlugin, ], }; /** * Build sources for prod environment */ export function buildProd(entryPoints: string[]) { return esbuild.build({ ...buildConfig, entryPoints, outdir: distProd, }); } /** * Build sources for dev environment */ function buildDev(entryPoints: string[]): Promise { return esbuild.build({ ...buildConfig, entryPoints, outdir: distDev, }); } /** * Do startup for development environment */ export function initializeDev( entryPoints: string[], ): () => Promise { buildConfig.inject = [ "./node_modules/@gnu-taler/web-util/lib/live-reload.mjs", ]; return () => buildDev(entryPoints); }