@kaathewise/ssg 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaathewise/ssg",
3
3
  "description": "Bun & JSX static site generator",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "license": "MPL-2.0",
6
6
  "author": "Andrej Kolčin",
7
7
  "type": "module",
package/src/build.ts CHANGED
@@ -1,27 +1,33 @@
1
1
  import * as fs from "node:fs/promises"
2
- import { join } from "node:path"
2
+ import * as path from "node:path"
3
3
  import { Glob, write } from "bun"
4
4
  import { type Page, renderAll } from "./render"
5
5
 
6
6
  const glob = new Glob("**/*.{js,jsx,ts,tsx}")
7
7
 
8
8
  export async function build(pagesDir: string, outDir: string = "dist/") {
9
- pagesDir = join(process.cwd(), pagesDir)
10
- outDir = join(process.cwd(), outDir)
9
+ pagesDir = path.join(process.cwd(), pagesDir)
10
+ outDir = path.join(process.cwd(), outDir)
11
11
 
12
12
  const pages: Page[] = []
13
- for await (const path of glob.scan(pagesDir)) {
14
- const modulePath = join(pagesDir, path)
13
+ for await (const relPath of glob.scan(pagesDir)) {
14
+ const modulePath = path.join(pagesDir, relPath)
15
15
  const p = await renderAll(modulePath, pagesDir)
16
16
  pages.push(...p)
17
17
  }
18
18
 
19
19
  await fs.rm(outDir, { recursive: true, force: true })
20
- // recursive disables errors when the directory already exists
21
- await fs.mkdir(outDir, { recursive: true })
20
+ await fs.mkdir(outDir)
22
21
 
23
22
  for (const page of pages) {
24
- const file = Bun.file(join(outDir, page.path))
23
+ const destPath = path.join(outDir, page.path)
24
+ const dir = path.dirname(destPath)
25
+ if (!(await fs.exists(dir))) {
26
+ await fs.mkdir(path.dirname(destPath), {
27
+ recursive: true,
28
+ })
29
+ }
30
+ const file = Bun.file(destPath)
25
31
  await write(file, page.html)
26
32
  }
27
33
  }
package/src/render.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as path from "node:path"
2
2
 
3
3
  interface Params {
4
- [name: string]: string | string[]
4
+ [name: string]: string
5
5
  }
6
6
 
7
7
  export type Page = {
@@ -58,11 +58,11 @@ export async function renderAll(
58
58
  function substituteParams(inputPath: string, params: Params): string {
59
59
  let path = inputPath
60
60
  for (const [key, value] of Object.entries(params)) {
61
- if (typeof value === "string") {
62
- path = path.replace(`[${key}]`, value)
63
- } else if (Array.isArray(value)) {
64
- path = path.replace(`[[...${key}]]`, value.join("/"))
65
- }
61
+ const single = `[${key}]`
62
+ const multiple = `[[...${key}]]`
63
+
64
+ path = path.replace(single, value)
65
+ path = path.replace(multiple, value)
66
66
  }
67
67
 
68
68
  path = path.replace(/\.tsx?$/, "")
package/src/server.ts CHANGED
@@ -8,23 +8,28 @@ const EVENT_PATH = "/__ssg_dev_sse"
8
8
 
9
9
  export class Server {
10
10
  pagesDir: string
11
- sourcePath: string
12
- router: Bun.FileSystemRouter
11
+ sourceDir: string
12
+ assetDir?: string
13
13
  port?: number = 3001
14
14
 
15
+ router: Bun.FileSystemRouter
16
+
15
17
  constructor(options: {
16
18
  pagesDir: string
17
- sourcePath: string
19
+ sourceDir: string
20
+ assetDir?: string
18
21
  port?: number
19
22
  }) {
20
23
  this.pagesDir = path.join(process.cwd(), options.pagesDir)
21
- this.sourcePath = path.join(process.cwd(), options.sourcePath)
24
+ this.sourceDir = path.join(process.cwd(), options.sourceDir)
22
25
 
23
26
  this.router = new Bun.FileSystemRouter({
24
27
  style: "nextjs",
25
28
  dir: options.pagesDir,
26
29
  })
27
30
 
31
+ this.assetDir ??= options.assetDir
32
+
28
33
  this.port ??= options?.port
29
34
  }
30
35
 
@@ -43,14 +48,32 @@ export class Server {
43
48
  }
44
49
 
45
50
  const route = this.router.match(request.url)
46
- return createHtml(this.pagesDir, route)
51
+ if (route) {
52
+ return createHtml(this.pagesDir, route)
53
+ }
54
+
55
+ if (this.assetDir) {
56
+ // TODO: .. escapes
57
+ const assetPath = path.join(
58
+ this.assetDir,
59
+ url.pathname.slice(1),
60
+ )
61
+
62
+ return new Response(Bun.file(assetPath))
63
+ }
64
+
65
+ return new Response("Not found", {
66
+ headers: {
67
+ "Content-Type": "text/html",
68
+ },
69
+ })
47
70
  },
48
71
  })
49
72
 
50
- const watcher = watch(this.sourcePath, { recursive: true })
73
+ const watcher = watch(this.sourceDir, { recursive: true })
51
74
  for await (const _ of watcher) {
52
75
  this.router.reload()
53
- clearCache(this.sourcePath)
76
+ clearCache(this.sourceDir)
54
77
  for (const client of clients) {
55
78
  client.enqueue("data: RELOAD\n\n")
56
79
  }
@@ -94,11 +117,10 @@ const RELOAD_SCRIPT = `
94
117
  </script>
95
118
  `
96
119
 
97
- async function createHtml(pagesDir: string, route: MatchedRoute | null) {
98
- if (!route) {
99
- throw new Error("TODO not found")
100
- }
101
-
120
+ async function createHtml(
121
+ pagesDir: string,
122
+ route: MatchedRoute,
123
+ ): Promise<Response> {
102
124
  const page = await render(route.filePath, pagesDir, route.params)
103
125
 
104
126
  const response = new Response(page.html, {