diff options
-rw-r--r-- | bs5/client/build.mjs | 113 | ||||
-rw-r--r-- | bs5/server/server.ml | 3 |
2 files changed, 114 insertions, 2 deletions
diff --git a/bs5/client/build.mjs b/bs5/client/build.mjs new file mode 100644 index 0000000..f98de63 --- /dev/null +++ b/bs5/client/build.mjs @@ -0,0 +1,113 @@ +import Esbuild from "esbuild"; +import Path from "path"; +import { plugin as extractClientComponents } from "../packages/extract-client-components/esbuild-plugin.mjs"; + +async function build(entryPoints, { env, output, extract, mockWebpackRequire }) { + const outfile = output; + const outdir = Path.dirname(outfile); + const splitting = true; + + const bootstrapOutput = Path.join(Path.dirname(outfile), "bootstrap.js"); + + let plugins = []; + if (extract) { + plugins.push( + extractClientComponents({ + target: "app", + mockWebpackRequire, + bootstrapOutput, + }), + ); + } + + const isDev = env === "development"; + + try { + const result = await Esbuild.build({ + entryPoints, + entryNames: "[name]", + bundle: true, + logLevel: "debug", + platform: "browser", + format: "esm", + splitting, + outdir, + plugins, + write: true, + treeShaking: isDev ? false : true, + minify: isDev ? false : true, + define: { + "process.env.NODE_ENV": `"${env}"`, + "__DEV__": `"${isDev}"`, /* __DEV__ is used by react-client code */ + }, + }); + + entryPoints.forEach((entryPoint) => { + console.log('Build completed successfully for "' + entryPoint + '"'); + }); + return result; + } catch (error) { + console.error("\nBuild failed:", error); + process.exit(1); + } +} + +function parseArgv(argv) { + const args = argv.slice(2); + const result = { _: [] }; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg.startsWith("--")) { + const longArg = arg.slice(2); + if (longArg.includes("=")) { + const [key, value] = longArg.split("="); + result[key] = parseValue(value); + } else if (i + 1 < args.length && !args[i + 1].startsWith("-")) { + result[longArg] = parseValue(args[++i]); + } else { + result[longArg] = true; + } + } else if (arg.startsWith("-")) { + const shortArg = arg.slice(1); + if (shortArg.includes("=")) { + const [key, value] = shortArg.split("="); + result[key] = parseValue(value); + } else if (i + 1 < args.length && !args[i + 1].startsWith("-")) { + result[shortArg] = parseValue(args[++i]); + } else { + for (const char of shortArg) { + result[char] = true; + } + } + } else { + result._.push(parseValue(arg)); + } + } + + return result; +} + +function parseValue(value) { + if (value === "true") return true; + if (value === "false") return false; + if (value === "null") return null; + if (!isNaN(value)) return Number(value); + return value; +} + +function camelCaseKeys(obj) { + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [ + key.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()), + value, + ]), + ); +} + +const flags = parseArgv(process.argv); +const options = camelCaseKeys(flags); +const entryPoints = options._; + +build(entryPoints, options); diff --git a/bs5/server/server.ml b/bs5/server/server.ml index a58abc5..68b4367 100644 --- a/bs5/server/server.ml +++ b/bs5/server/server.ml @@ -14,8 +14,7 @@ let router = (* rendering tricks *) Dream.get "/output.css" (Dream.from_filesystem "./_build/default" "output.css"); - Dream.get "/static/**" - (Dream.static "./_build/default/client/app/client"); + Dream.get "/static/**" (Dream.static "./_build/default/client/app"); getAndPost Router.demoRenderToString (fun _ -> Dream.html Pages.Hydrate.toString); getAndPost Router.demoRenderToStaticMarkup (fun _ -> |