From a2c8ef8eab197534ef498560ad9a34e294d7fb77 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Fri, 28 Apr 2023 16:40:38 +0100 Subject: [PATCH 1/5] add exports so build can be comsumed as a module --- deno.jsonc | 4 +- src/build.ts | 178 +++++++++++++++++++++++++++++++++++--------------- src/server.ts | 6 +- 3 files changed, 129 insertions(+), 59 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index d7f2d89..cd262f1 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,7 +1,7 @@ { "tasks": { - "build": "deno run -A src/build.ts --site src/_site", - "dev": "deno run -A src/build.ts --site src/_site --dev --base=mononykus" + "build": "deno run -A src/build.ts --site_dir src/_site", + "dev": "deno run -A src/build.ts --site_dir src/_site --watch --base=mononykus" }, "compilerOptions": { "strict": true, diff --git a/src/build.ts b/src/build.ts index 59f31aa..a7091ff 100644 --- a/src/build.ts +++ b/src/build.ts @@ -9,27 +9,52 @@ import { walk } from "https://deno.land/std@0.177.0/fs/walk.ts"; import { create_handler } from "./server.ts"; import { globToRegExp } from "https://deno.land/std@0.182.0/path/glob.ts"; import { copy } from "https://deno.land/std@0.179.0/fs/copy.ts"; +import { normalize } from "https://deno.land/std@0.177.0/path/mod.ts"; + +const slashify = (path: string) => normalize(path.replace(/\/?$/, "/")); + +type Options = { + base: string; + out_dir: string; + site_dir: string; + minify: boolean; +}; const flags = parse(Deno.args, { - string: ["site", "build", "base"], - boolean: ["dev"], - default: { site: "_site/", dev: false, base: "/" }, + string: ["site_dir", "out_dir", "base"], + boolean: ["minify", "watch"], + default: { + site_dir: "_site", + out_dir: "build", + base: "/", + minify: false, + watch: false, + }, }); -const site_dir = flags.site.replace(/\/?$/, "/"); -const build_dir = (flags.build ?? `${site_dir}build/`).replace(/\/?$/, "/"); -const base_path = flags.base.replace(/\/?$/, "/"); +const options: Options = { + site_dir: slashify(flags.site_dir), + out_dir: slashify(flags.out_dir), + base: slashify(flags.base), + minify: !flags.watch || flags.minify, +}; // clean out old builds, if they exist -try { - await Deno.remove(build_dir, { recursive: true }); -} catch (_error) { - // do nothing -} +const clean = async (out_dir: Options["out_dir"]) => { + try { + await Deno.remove(out_dir, { recursive: true }); + } catch (_error) { + // do nothing + } + + await ensureDir(out_dir); +}; export const get_svelte_files = async ({ + site_dir, dir, }: { + site_dir: Options["site_dir"]; dir: "routes/" | "components/"; }) => { const glob = (glob: string) => globToRegExp(glob, { globstar: true }); @@ -48,63 +73,108 @@ export const get_svelte_files = async ({ return files; }; -await ensureDir(build_dir); - -const baseESBuildConfig = { - logLevel: "info", - format: "esm", - minify: !flags.dev, - bundle: true, -} as const satisfies Partial; - -const routesESBuildConfig: esbuild.BuildOptions = { - entryPoints: await get_svelte_files({ dir: "routes/" }), - write: false, - plugins: [ - svelte_components(site_dir, base_path), - svelte_internal, - build_routes, - ], - outdir: build_dir, - ...baseESBuildConfig, -}; +const copy_assets = ( + { site_dir, out_dir }: Partial, +) => copy(site_dir + "assets", out_dir + "assets", { overwrite: true }); -const islandsESBuildConfig: esbuild.BuildOptions = { - entryPoints: await get_svelte_files({ dir: "components/" }), - write: true, - plugins: [ - svelte_components(site_dir, base_path), - svelte_internal, - ], - outdir: build_dir + "components/", - splitting: true, - ...baseESBuildConfig, -}; +const rebuild = async ({ + base, + out_dir, + site_dir, + minify, +}: Options) => { + const baseESBuildConfig = { + logLevel: "info", + format: "esm", + minify: minify, + bundle: true, + } as const satisfies Partial; + + const routesESBuildConfig: esbuild.BuildOptions = { + entryPoints: await get_svelte_files({ site_dir, dir: "routes/" }), + write: false, + plugins: [ + svelte_components(site_dir, base), + svelte_internal, + build_routes, + ], + outdir: out_dir, + ...baseESBuildConfig, + }; -const copy_assets = async () => - await copy(site_dir + "assets", build_dir + "assets", { overwrite: true }); + const islandsESBuildConfig: esbuild.BuildOptions = { + entryPoints: await get_svelte_files({ site_dir, dir: "components/" }), + write: true, + plugins: [ + svelte_components(site_dir, base), + svelte_internal, + ], + outdir: out_dir + "components/", + splitting: true, + ...baseESBuildConfig, + }; -const rebuild = async () => { - await Promise.all([ + return Promise.all([ esbuild.build(routesESBuildConfig), esbuild.build(islandsESBuildConfig), - copy_assets(), + copy_assets({ site_dir, out_dir }), ]); }; -await rebuild(); +export const build = async ( + { + base: _base = options.base, + out_dir: _out_dir = options.out_dir, + site_dir: _site_dir = options.site_dir, + minify = options.minify, + }: Partial = {}, +) => { + const base = slashify(_base); + const out_dir = slashify(_out_dir); + const site_dir = slashify(_site_dir); + + clean(out_dir); + + await rebuild({ base, out_dir, site_dir, minify }); + + esbuild.stop(); +}; + +export const watch = async ( + { + base: _base = options.base, + out_dir: _out_dir = options.out_dir, + site_dir: _site_dir = options.site_dir, + minify = true, + }: Partial = {}, +) => { + const base = slashify(_base); + const out_dir = slashify(_out_dir); + const site_dir = slashify(_site_dir); + + clean(out_dir); + + const _rebuild = () => rebuild({ base, out_dir, site_dir, minify }); + + await _rebuild(); + + serve(create_handler({ base: base, out_dir }), { port: 4507 }); -if (flags.dev) { const watcher = Deno.watchFs(site_dir); - serve(create_handler({ base: flags.base, build_dir }), { port: 4507 }); let timeout; for await (const { kind, paths: [path] } of watcher) { if (path && (kind === "modify" || kind === "create")) { - if (path.includes(build_dir)) continue; + if (path.includes(out_dir)) continue; clearTimeout(timeout); - timeout = setTimeout(rebuild, 6); + timeout = setTimeout(_rebuild, 6); } } -} else { - Deno.exit(0); +}; + +if (import.meta.main) { + if (flags.watch) { + await watch(options); + } else { + await build(options); + } } diff --git a/src/server.ts b/src/server.ts index 8bed0bc..86d6ae4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,16 +3,16 @@ import { serveDir } from "https://deno.land/std@0.177.0/http/file_server.ts"; interface ServerOptions { base?: string; - build_dir: string; + out_dir: string; } export const create_handler = ( - { base = "", build_dir }: ServerOptions, + { base = "", out_dir }: ServerOptions, ): Handler => ((req) => { const url = new URL(req.url); if (url.pathname.startsWith("/" + base)) { - return serveDir(req, { fsRoot: build_dir, urlRoot: base }); + return serveDir(req, { fsRoot: out_dir, urlRoot: base }); } else { return Response.redirect(new URL(base + url.pathname, url.origin)); } From 6674b8f732f4031e5158640139535300eeeb0bca Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Fri, 28 Apr 2023 16:52:50 +0100 Subject: [PATCH 2/5] deploy from root level `build` --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 94f93ce..af98c0d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,7 +3,7 @@ on: push: branches: - main - pull_request: + pull_request: workflow_dispatch: # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. @@ -47,7 +47,7 @@ jobs: with: project: mononykus entrypoint: https://deno.land/std@0.183.0/http/file_server.ts - root: src/_site/build + root: build gh: if: github.ref_name == 'main' From 0001043fdf88d5d96d3152aa9a840d27a70bbfe7 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Fri, 28 Apr 2023 17:04:54 +0100 Subject: [PATCH 3/5] `watch` should use `options.watch` --- src/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.ts b/src/build.ts index a7091ff..589d281 100644 --- a/src/build.ts +++ b/src/build.ts @@ -145,7 +145,7 @@ export const watch = async ( base: _base = options.base, out_dir: _out_dir = options.out_dir, site_dir: _site_dir = options.site_dir, - minify = true, + minify = options.minify, }: Partial = {}, ) => { const base = slashify(_base); From 10733bae708f1a5c83d4664cc6c79f2fb0509fc4 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Fri, 28 Apr 2023 17:20:29 +0100 Subject: [PATCH 4/5] `normalize` is all you need --- src/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.ts b/src/build.ts index 589d281..13e831b 100644 --- a/src/build.ts +++ b/src/build.ts @@ -11,7 +11,7 @@ import { globToRegExp } from "https://deno.land/std@0.182.0/path/glob.ts"; import { copy } from "https://deno.land/std@0.179.0/fs/copy.ts"; import { normalize } from "https://deno.land/std@0.177.0/path/mod.ts"; -const slashify = (path: string) => normalize(path.replace(/\/?$/, "/")); +const slashify = (path: string) => normalize(path + "/"); type Options = { base: string; From 45c2a055e09672278e8a80e332051eabf1df77d9 Mon Sep 17 00:00:00 2001 From: Alex Sanders Date: Fri, 28 Apr 2023 17:21:47 +0100 Subject: [PATCH 5/5] dedupe --- src/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build.ts b/src/build.ts index 13e831b..6438de6 100644 --- a/src/build.ts +++ b/src/build.ts @@ -158,7 +158,7 @@ export const watch = async ( await _rebuild(); - serve(create_handler({ base: base, out_dir }), { port: 4507 }); + serve(create_handler({ base, out_dir }), { port: 4507 }); const watcher = Deno.watchFs(site_dir); let timeout;