nx_build_site.nx source
↩ module page · 546 lines · 22971 B
1// nx_build_site.nx -- bits-up site builder. Replaces
2// nishi-pages/build/build.py (530 LOC Python) with NishiLang
3// substrate. Per user *"no python nothing but nishi from bits up"*.
4//
5// Pipeline:
6// 1. Read content/site.yaml -> NxYamlDoc site vars (lifted to site.*)
7// 2. Read theme templates: layout.html, index.html, post.html, page.html
8// 3. For each .md in content/posts/:
9// nx_build_doc round-trip
10// emit a post-summary HTML block (for the homepage posts list)
11// write dist/posts/<slug>.html
12// 4. For each .md in content/pages/:
13// nx_build_doc round-trip with PAGE template
14// write dist/pages/<slug>.html
15// 5. Build the homepage:
16// substitute site.* + posts_html (concatenated summaries)
17// wrap with layout.html
18// write dist/index.html
19//
20// What this v1 does NOT do (queued):
21// - Dynamic directory walking (uses a hardcoded list of slugs)
22// - dist/404.html generation
23// - style.css copy to dist/assets/
24// - server/pages.nx route emit
25// These are easy follow-ups that compose with what's here.
26//
27// Memory budget (one rendering pass):
28// ~64K per .md scratch
29// ~32K per template
30// ~256K total site arena
31//
32// nx_safety_envelope:
33// intended_use: sovereign substrate builder for nishi-pages
34// sil_target: SIL2
35// evidence: bench/nx_build_site_smoke.nx renders a fixture
36// tree end-to-end and asserts output bytes
37// verdict: NOT_YET_EVALUATED -- pending smoke
38
39import "nx_syscalls_x86_64.nx"
40import "nx_yaml_subset.nx"
41import "nx_frontmatter.nx"
42import "nx_http_template.nx"
43import "nx_md_render.nx"
44import "nx_build_doc.nx"
45
46// ---- Sealed verdicts ----
47
48const NX_BS_OK: i64 = 0
49const NX_BS_BAD_INPUT: i64 = 1
50const NX_BS_READ_FAIL: i64 = 2
51const NX_BS_WRITE_FAIL: i64 = 3
52const NX_BS_RENDER_FAIL: i64 = 4
53const NX_BS_PATH_TOO_LONG: i64 = 5
54const NX_BS_VERDICT_N: i64 = 6
55
56// ---- Path scratch sizing ----
57
58const NX_BS_PATH_CAP: i64 = 512
59const NX_BS_HTML_CAP: i64 = 65536 // 64K per rendered page
60
61// ---- Helpers ----
62
63func _bs_bcopy(dst: *u8, dst_off: i64, dst_cap: i64,
64 src: *u8, src_n: i64) -> i64 {
65 if dst_off + src_n > dst_cap { return -1 }
66 var i: i64 = 0
67 while i < src_n {
68 dst[dst_off + i] = src[i]
69 i = i + 1
70 }
71 return dst_off + src_n
72}
73
74// Join path components: out = base + "/" + name + suffix. Returns
75// resulting length, or -1 on overflow. Caller pre-allocates out
76// with at least NX_BS_PATH_CAP capacity.
77func nx_bs_join(out: *u8, out_cap: i64,
78 base: *u8, base_n: i64,
79 name: *u8, name_n: i64,
80 suffix: *u8, suffix_n: i64) -> i64 {
81 let want: i64 = base_n + 1 + name_n + suffix_n
82 if want >= out_cap { return -1 }
83 var p: i64 = 0
84 var i: i64 = 0
85 while i < base_n { out[p] = base[i]; p = p + 1; i = i + 1 }
86 out[p] = 47; p = p + 1 // '/'
87 var j: i64 = 0
88 while j < name_n { out[p] = name[j]; p = p + 1; j = j + 1 }
89 var k: i64 = 0
90 while k < suffix_n { out[p] = suffix[k]; p = p + 1; k = k + 1 }
91 out[p] = 0 // NUL-terminate for syscalls
92 return p
93}
94
95// Read whole file into a freshly-mmap'd buffer. Returns buf + sets
96// *out_n; returns NULL on read failure. Composes with sys_read_file_x86_64
97// but adds path-as-bytes helper.
98func nx_bs_read_file(path: *u8, out_n: *i64) -> *u8 {
99 let buf: *u8 = sys_read_file_x86_64(path, out_n)
100 return buf
101}
102
103// Write buf+n to path. Returns 0/-errno.
104func nx_bs_write_file(path: *u8, buf: *u8, n: i64) -> i64 {
105 let fd: i64 = sys_openat_wr(path, 0x1A4) // 0644
106 if fd < 0 { return fd }
107 let wn: i64 = sys_write(fd, buf, n)
108 sys_close(fd)
109 if wn != n { return -1 }
110 return 0
111}
112
113// ---- Per-doc render ----
114//
115// Renders ONE .md file by reading source, splitting frontmatter,
116// parsing YAML, populating vars, applying inner_tmpl + layout_tmpl.
117// Caller passes site_yaml + slug + which inner template (post or page).
118// Writes the rendered HTML to `out_html_buf` (caller-provided);
119// returns the byte count or -err.
120
121func nx_bs_render_doc(md_path: *u8,
122 site_yaml: *NxYamlDoc,
123 slug: *u8, slug_n: i64,
124 inner_tmpl: *u8, inner_n: i64,
125 layout_tmpl: *u8, layout_n: i64,
126 out_html_buf: *u8, out_html_cap: i64) -> i64 {
127 // 1. Read source.
128 let src_n: *i64 = sys_mmap(16) as *i64
129 let src: *u8 = nx_bs_read_file(md_path, src_n)
130 if src as i64 == 0 { return NX_BS_READ_FAIL }
131
132 // 2. Split frontmatter.
133 let yoff: *i64 = sys_mmap(16) as *i64
134 let ylen: *i64 = sys_mmap(16) as *i64
135 let boff: *i64 = sys_mmap(16) as *i64
136 let blen: *i64 = sys_mmap(16) as *i64
137 let fmv: i64 = nx_frontmatter_split(src, src_n[0], yoff, ylen, boff, blen)
138 if fmv != NX_FM_OK { return NX_BS_RENDER_FAIL }
139
140 // 3. Parse YAML frontmatter.
141 let entries: *NxYamlEntry = sys_mmap(NX_YAML_ENTRY_BYTES * 16) as *NxYamlEntry
142 let ykeys: *u8 = sys_mmap(512)
143 let yvals: *u8 = sys_mmap(2048)
144 let yd: *NxYamlDoc = sys_mmap(NX_YAML_DOC_BYTES) as *NxYamlDoc
145 nx_yaml_doc_init(yd, entries, 16, ykeys, 512, yvals, 2048)
146 let ybytes: *u8 = (src as i64 + yoff[0]) as *u8
147 let yv: i64 = nx_yaml_parse(yd, ybytes, ylen[0])
148 if yv != NX_YAML_OK { return NX_BS_RENDER_FAIL }
149
150 // 4. Build inner var table.
151 let bv: *NxBuildVars = sys_mmap(NX_BUILD_VARS_BYTES) as *NxBuildVars
152 nx_build_vars_init(bv)
153 if nx_build_vars_add_yaml(bv, "page." as *u8, 5, yd) != 0 { return NX_BS_RENDER_FAIL }
154 if nx_build_vars_add_yaml(bv, "site." as *u8, 5, site_yaml) != 0 { return NX_BS_RENDER_FAIL }
155 if nx_build_vars_add(bv, "slug" as *u8, 4, slug, slug_n) != 0 { return NX_BS_RENDER_FAIL }
156
157 // Render markdown body -> HTML via nx_md_render (sovereign substrate).
158 // The inner template substitutes {{ body | raw }} so the HTML is
159 // emitted verbatim (no double-escape).
160 let bptr: *u8 = (src as i64 + boff[0]) as *u8
161 let body_html: *u8 = sys_mmap(NX_BS_HTML_CAP)
162 let body_html_n: i64 = nx_md_render(bptr, blen[0], body_html)
163 if nx_build_vars_add(bv, "body" as *u8, 4, body_html, body_html_n) != 0 { return NX_BS_RENDER_FAIL }
164
165 // 5. Apply inner template.
166 let inner_buf: *u8 = sys_mmap(NX_BS_HTML_CAP)
167 let inner_out_n: *i64 = sys_mmap(16) as *i64
168 if nx_build_apply_template(inner_tmpl, inner_n, bv,
169 inner_buf, NX_BS_HTML_CAP, inner_out_n) != NXT_OK {
170 return NX_BS_RENDER_FAIL
171 }
172
173 // 6. Build OUTER var table (vars get sealed at seal-time so we need
174 // a fresh table with `content` added).
175 let bv2: *NxBuildVars = sys_mmap(NX_BUILD_VARS_BYTES) as *NxBuildVars
176 nx_build_vars_init(bv2)
177 if nx_build_vars_add_yaml(bv2, "page." as *u8, 5, yd) != 0 { return NX_BS_RENDER_FAIL }
178 if nx_build_vars_add_yaml(bv2, "site." as *u8, 5, site_yaml) != 0 { return NX_BS_RENDER_FAIL }
179 if nx_build_vars_add(bv2, "content" as *u8, 7, inner_buf, inner_out_n[0]) != 0 { return NX_BS_RENDER_FAIL }
180
181 // 7. Apply layout.
182 let final_n: *i64 = sys_mmap(16) as *i64
183 if nx_build_apply_template(layout_tmpl, layout_n, bv2,
184 out_html_buf, out_html_cap, final_n) != NXT_OK {
185 return NX_BS_RENDER_FAIL
186 }
187
188 return final_n[0]
189}
190
191// ---- Build site driver ----
192//
193// Walks a HARDCODED list of slugs (v1; dynamic walk via nx_dir is
194// queued). Caller passes:
195// base_dir = path to nishi-pages root
196// post_slugs[] = list of post slug strings (NUL-terminated)
197// n_post_slugs
198// page_slugs[] = list of page slug strings
199// n_page_slugs
200//
201// For each slug, reads content/posts/<slug>.md (or pages/), renders,
202// writes to dist/posts/<slug>.html (or dist/pages/). Then builds
203// the homepage by reading themes/default/index.html, substituting
204// site.* vars + a pre-built posts_html block, wrapping with layout.
205
206// Read the four theme templates into mmap'd buffers + return them via
207// out-pointers. Returns 0 on OK or NX_BS_READ_FAIL.
208func nx_bs_load_theme(base_dir: *u8, base_n: i64,
209 layout_out: **u8, layout_n_out: *i64,
210 index_out: **u8, index_n_out: *i64,
211 post_out: **u8, post_n_out: *i64,
212 page_out: **u8, page_n_out: *i64) -> i64 {
213 let path: *u8 = sys_mmap(NX_BS_PATH_CAP)
214
215 nx_bs_join(path, NX_BS_PATH_CAP, base_dir, base_n,
216 "themes/default" as *u8, 14, "/layout.html" as *u8, 12)
217 let l: *u8 = nx_bs_read_file(path, layout_n_out)
218 if l as i64 == 0 { return NX_BS_READ_FAIL }
219 layout_out[0] = l
220
221 nx_bs_join(path, NX_BS_PATH_CAP, base_dir, base_n,
222 "themes/default" as *u8, 14, "/index.html" as *u8, 11)
223 let i_tmpl: *u8 = nx_bs_read_file(path, index_n_out)
224 if i_tmpl as i64 == 0 { return NX_BS_READ_FAIL }
225 index_out[0] = i_tmpl
226
227 nx_bs_join(path, NX_BS_PATH_CAP, base_dir, base_n,
228 "themes/default" as *u8, 14, "/post.html" as *u8, 10)
229 let p_tmpl: *u8 = nx_bs_read_file(path, post_n_out)
230 if p_tmpl as i64 == 0 { return NX_BS_READ_FAIL }
231 post_out[0] = p_tmpl
232
233 nx_bs_join(path, NX_BS_PATH_CAP, base_dir, base_n,
234 "themes/default" as *u8, 14, "/page.html" as *u8, 10)
235 let pg_tmpl: *u8 = nx_bs_read_file(path, page_n_out)
236 if pg_tmpl as i64 == 0 { return NX_BS_READ_FAIL }
237 page_out[0] = pg_tmpl
238
239 return NX_BS_OK
240}
241
242// Read site.yaml from base_dir/content/site.yaml. Returns parsed
243// NxYamlDoc or NULL on fail.
244func nx_bs_load_site_yaml(base_dir: *u8, base_n: i64) -> *NxYamlDoc {
245 let path: *u8 = sys_mmap(NX_BS_PATH_CAP)
246 nx_bs_join(path, NX_BS_PATH_CAP, base_dir, base_n,
247 "content" as *u8, 7, "/site.yaml" as *u8, 10)
248 let y_n: *i64 = sys_mmap(16) as *i64
249 let y_bytes: *u8 = nx_bs_read_file(path, y_n)
250 if y_bytes as i64 == 0 { return 0 as *NxYamlDoc }
251
252 let entries: *NxYamlEntry = sys_mmap(NX_YAML_ENTRY_BYTES * 16) as *NxYamlEntry
253 let keys: *u8 = sys_mmap(1024)
254 let vals: *u8 = sys_mmap(2048)
255 let doc: *NxYamlDoc = sys_mmap(NX_YAML_DOC_BYTES) as *NxYamlDoc
256 nx_yaml_doc_init(doc, entries, 16, keys, 1024, vals, 2048)
257 if nx_yaml_parse(doc, y_bytes, y_n[0]) != NX_YAML_OK { return 0 as *NxYamlDoc }
258 return doc
259}
260
261// Render one post: read content/posts/<slug>.md, apply post + layout,
262// write to dist/posts/<slug>.html.
263func nx_bs_render_one_post(base_dir: *u8, base_n: i64,
264 slug: *u8, slug_n: i64,
265 site_yaml: *NxYamlDoc,
266 post_tmpl: *u8, post_n: i64,
267 layout_tmpl: *u8, layout_n: i64) -> i64 {
268 let src_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
269 // base_dir/content/posts/<slug>.md
270 var p: i64 = 0
271 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, base_dir, base_n)
272 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, "/content/posts/" as *u8, 15)
273 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, slug, slug_n)
274 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, ".md" as *u8, 3)
275 src_path[p] = 0
276
277 let final_html: *u8 = sys_mmap(NX_BS_HTML_CAP)
278 let n: i64 = nx_bs_render_doc(src_path, site_yaml,
279 slug, slug_n,
280 post_tmpl, post_n,
281 layout_tmpl, layout_n,
282 final_html, NX_BS_HTML_CAP)
283 if n < 0 { return n }
284
285 let out_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
286 var q: i64 = 0
287 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, base_dir, base_n)
288 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, "/dist/posts/" as *u8, 12)
289 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, slug, slug_n)
290 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, ".html" as *u8, 5)
291 out_path[q] = 0
292
293 return nx_bs_write_file(out_path, final_html, n)
294}
295
296// ---- Post summary builder ----
297//
298// Emits ONE <article class="post-summary">...</article> chunk for
299// the homepage's posts-list section. Caller passes the post's
300// slug + parsed-frontmatter NxYamlDoc; this helper looks up title,
301// date, description and appends the HTML chunk to `out` at offset
302// `out_off`. Returns new offset, or -1 on overflow.
303
304func nx_bs_append_post_summary(out: *u8, out_off: i64, out_cap: i64,
305 slug: *u8, slug_n: i64,
306 yaml: *NxYamlDoc) -> i64 {
307 let off1: *i64 = sys_mmap(16) as *i64
308 let len1: *i64 = sys_mmap(16) as *i64
309 let off2: *i64 = sys_mmap(16) as *i64
310 let len2: *i64 = sys_mmap(16) as *i64
311 let off3: *i64 = sys_mmap(16) as *i64
312 let len3: *i64 = sys_mmap(16) as *i64
313
314 let has_title: i64 = nx_yaml_lookup(yaml, "title" as *u8, 5, off1, len1)
315 let has_date: i64 = nx_yaml_lookup(yaml, "date" as *u8, 4, off2, len2)
316 let has_desc: i64 = nx_yaml_lookup(yaml, "description" as *u8, 11, off3, len3)
317
318 var p: i64 = out_off
319 p = _bs_bcopy(out, p, out_cap, "<article class=\"post-summary\">\n <h3><a href=\"/posts/" as *u8, 55)
320 if p < 0 { return -1 }
321 p = _bs_bcopy(out, p, out_cap, slug, slug_n)
322 if p < 0 { return -1 }
323 p = _bs_bcopy(out, p, out_cap, "\">" as *u8, 2)
324 if p < 0 { return -1 }
325 if has_title == 1 {
326 let title_ptr: *u8 = (yaml.values as i64 + off1[0]) as *u8
327 p = _bs_bcopy(out, p, out_cap, title_ptr, len1[0])
328 if p < 0 { return -1 }
329 }
330 p = _bs_bcopy(out, p, out_cap, "</a></h3>\n <time datetime=\"" as *u8, 30)
331 if p < 0 { return -1 }
332 if has_date == 1 {
333 let date_ptr: *u8 = (yaml.values as i64 + off2[0]) as *u8
334 p = _bs_bcopy(out, p, out_cap, date_ptr, len2[0])
335 if p < 0 { return -1 }
336 p = _bs_bcopy(out, p, out_cap, "\">" as *u8, 2)
337 if p < 0 { return -1 }
338 p = _bs_bcopy(out, p, out_cap, date_ptr, len2[0])
339 if p < 0 { return -1 }
340 }
341 p = _bs_bcopy(out, p, out_cap, "</time>\n <p>" as *u8, 15)
342 if p < 0 { return -1 }
343 if has_desc == 1 {
344 let desc_ptr: *u8 = (yaml.values as i64 + off3[0]) as *u8
345 p = _bs_bcopy(out, p, out_cap, desc_ptr, len3[0])
346 if p < 0 { return -1 }
347 }
348 p = _bs_bcopy(out, p, out_cap, "</p>\n </article>\n " as *u8, 20)
349 if p < 0 { return -1 }
350 return p
351}
352
353// Read a post's frontmatter into out_yaml (caller-allocated NxYamlDoc).
354// Returns NX_BS_OK or -err. Body is ignored (caller doesn't need it).
355func nx_bs_load_post_yaml(base_dir: *u8, base_n: i64,
356 slug: *u8, slug_n: i64,
357 out_yaml: *NxYamlDoc) -> i64 {
358 let path: *u8 = sys_mmap(NX_BS_PATH_CAP)
359 var p: i64 = 0
360 p = _bs_bcopy(path, p, NX_BS_PATH_CAP, base_dir, base_n)
361 p = _bs_bcopy(path, p, NX_BS_PATH_CAP, "/content/posts/" as *u8, 15)
362 p = _bs_bcopy(path, p, NX_BS_PATH_CAP, slug, slug_n)
363 p = _bs_bcopy(path, p, NX_BS_PATH_CAP, ".md" as *u8, 3)
364 path[p] = 0
365
366 let src_n: *i64 = sys_mmap(16) as *i64
367 let src: *u8 = nx_bs_read_file(path, src_n)
368 if src as i64 == 0 { return NX_BS_READ_FAIL }
369
370 let yoff: *i64 = sys_mmap(16) as *i64
371 let ylen: *i64 = sys_mmap(16) as *i64
372 let boff: *i64 = sys_mmap(16) as *i64
373 let blen: *i64 = sys_mmap(16) as *i64
374 if nx_frontmatter_split(src, src_n[0], yoff, ylen, boff, blen) != NX_FM_OK {
375 return NX_BS_RENDER_FAIL
376 }
377
378 let ybytes: *u8 = (src as i64 + yoff[0]) as *u8
379 if nx_yaml_parse(out_yaml, ybytes, ylen[0]) != NX_YAML_OK { return NX_BS_RENDER_FAIL }
380 return NX_BS_OK
381}
382
383// ---- Homepage rendering ----
384//
385// Builds dist/index.html. Iterates the post-slugs list (caller-
386// provided concatenated names + lengths array), reads each post's
387// frontmatter, appends one summary chunk to a posts_html buffer,
388// then renders index.html with site.* + posts_html, wraps with
389// layout.html.
390
391func nx_bs_render_homepage(base_dir: *u8, base_n: i64,
392 site_yaml: *NxYamlDoc,
393 index_tmpl: *u8, index_n: i64,
394 layout_tmpl: *u8, layout_n: i64,
395 post_slug_ptrs: **u8,
396 post_slug_lens: *i64,
397 n_posts: i64) -> i64 {
398 // 1. Build posts_html by iterating posts.
399 let posts_html: *u8 = sys_mmap(16384)
400 var posts_off: i64 = 0
401 var i: i64 = 0
402 while i < n_posts {
403 // Per-post yaml + entries.
404 let entries: *NxYamlEntry = sys_mmap(NX_YAML_ENTRY_BYTES * 16) as *NxYamlEntry
405 let keys: *u8 = sys_mmap(512)
406 let vals: *u8 = sys_mmap(2048)
407 let yd: *NxYamlDoc = sys_mmap(NX_YAML_DOC_BYTES) as *NxYamlDoc
408 nx_yaml_doc_init(yd, entries, 16, keys, 512, vals, 2048)
409
410 let slug_ptr: *u8 = post_slug_ptrs[i]
411 let slug_n: i64 = post_slug_lens[i]
412 if nx_bs_load_post_yaml(base_dir, base_n, slug_ptr, slug_n, yd) != NX_BS_OK {
413 return NX_BS_RENDER_FAIL
414 }
415 let new_off: i64 = nx_bs_append_post_summary(posts_html, posts_off, 16384,
416 slug_ptr, slug_n, yd)
417 if new_off < 0 { return NX_BS_RENDER_FAIL }
418 posts_off = new_off
419 i = i + 1
420 }
421
422 // 2. Build inner var table for index.html.
423 let bv: *NxBuildVars = sys_mmap(NX_BUILD_VARS_BYTES) as *NxBuildVars
424 nx_build_vars_init(bv)
425 if nx_build_vars_add_yaml(bv, "site." as *u8, 5, site_yaml) != 0 { return NX_BS_RENDER_FAIL }
426 if nx_build_vars_add(bv, "page.title" as *u8, 10, "Nishi Family" as *u8, 12) != 0 { return NX_BS_RENDER_FAIL }
427 if nx_build_vars_add(bv, "page.description" as *u8, 16, "Nishi Family home" as *u8, 17) != 0 { return NX_BS_RENDER_FAIL }
428 if nx_build_vars_add(bv, "posts_html" as *u8, 10, posts_html, posts_off) != 0 { return NX_BS_RENDER_FAIL }
429
430 // 3. Render index.html inner.
431 let inner_buf: *u8 = sys_mmap(NX_BS_HTML_CAP)
432 let inner_n: *i64 = sys_mmap(16) as *i64
433 if nx_build_apply_template(index_tmpl, index_n, bv,
434 inner_buf, NX_BS_HTML_CAP, inner_n) != NXT_OK {
435 return NX_BS_RENDER_FAIL
436 }
437
438 // 4. Wrap with layout.
439 let bv2: *NxBuildVars = sys_mmap(NX_BUILD_VARS_BYTES) as *NxBuildVars
440 nx_build_vars_init(bv2)
441 if nx_build_vars_add_yaml(bv2, "site." as *u8, 5, site_yaml) != 0 { return NX_BS_RENDER_FAIL }
442 if nx_build_vars_add(bv2, "page.title" as *u8, 10, "Nishi Family" as *u8, 12) != 0 { return NX_BS_RENDER_FAIL }
443 if nx_build_vars_add(bv2, "page.description" as *u8, 16, "Nishi Family home" as *u8, 17) != 0 { return NX_BS_RENDER_FAIL }
444 if nx_build_vars_add(bv2, "content" as *u8, 7, inner_buf, inner_n[0]) != 0 { return NX_BS_RENDER_FAIL }
445
446 let final_buf: *u8 = sys_mmap(NX_BS_HTML_CAP)
447 let final_n: *i64 = sys_mmap(16) as *i64
448 if nx_build_apply_template(layout_tmpl, layout_n, bv2,
449 final_buf, NX_BS_HTML_CAP, final_n) != NXT_OK {
450 return NX_BS_RENDER_FAIL
451 }
452
453 // 5. Write dist/index.html.
454 let out_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
455 var q: i64 = 0
456 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, base_dir, base_n)
457 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, "/dist/index.html" as *u8, 16)
458 out_path[q] = 0
459
460 return nx_bs_write_file(out_path, final_buf, final_n[0])
461}
462
463// ---- dist/404.html ----
464
465func nx_bs_render_404(base_dir: *u8, base_n: i64,
466 site_yaml: *NxYamlDoc,
467 layout_tmpl: *u8, layout_n: i64) -> i64 {
468 let body: *u8 = "<section><h1>404</h1><p>This page has not been written yet.</p></section>" as *u8
469 let body_n: i64 = 72
470
471 let bv: *NxBuildVars = sys_mmap(NX_BUILD_VARS_BYTES) as *NxBuildVars
472 nx_build_vars_init(bv)
473 nx_build_vars_add_yaml(bv, "site." as *u8, 5, site_yaml)
474 nx_build_vars_add(bv, "page.title" as *u8, 10, "Not Found" as *u8, 9)
475 nx_build_vars_add(bv, "page.description" as *u8, 16, "Page not found." as *u8, 15)
476 nx_build_vars_add(bv, "content" as *u8, 7, body, body_n)
477
478 let final_buf: *u8 = sys_mmap(NX_BS_HTML_CAP)
479 let final_n: *i64 = sys_mmap(16) as *i64
480 if nx_build_apply_template(layout_tmpl, layout_n, bv,
481 final_buf, NX_BS_HTML_CAP, final_n) != NXT_OK {
482 return NX_BS_RENDER_FAIL
483 }
484
485 let out_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
486 var q: i64 = 0
487 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, base_dir, base_n)
488 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, "/dist/404.html" as *u8, 14)
489 out_path[q] = 0
490
491 return nx_bs_write_file(out_path, final_buf, final_n[0])
492}
493
494// ---- Asset copy: themes/default/style.css -> dist/assets/style.css ----
495
496func nx_bs_copy_style_css(base_dir: *u8, base_n: i64) -> i64 {
497 let src_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
498 var p: i64 = 0
499 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, base_dir, base_n)
500 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, "/themes/default/style.css" as *u8, 25)
501 src_path[p] = 0
502
503 let src_n: *i64 = sys_mmap(16) as *i64
504 let src: *u8 = nx_bs_read_file(src_path, src_n)
505 if src as i64 == 0 { return NX_BS_READ_FAIL }
506
507 let out_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
508 var q: i64 = 0
509 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, base_dir, base_n)
510 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, "/dist/assets/style.css" as *u8, 22)
511 out_path[q] = 0
512
513 return nx_bs_write_file(out_path, src, src_n[0])
514}
515
516func nx_bs_render_one_page(base_dir: *u8, base_n: i64,
517 slug: *u8, slug_n: i64,
518 site_yaml: *NxYamlDoc,
519 page_tmpl: *u8, page_n: i64,
520 layout_tmpl: *u8, layout_n: i64) -> i64 {
521 let src_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
522 var p: i64 = 0
523 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, base_dir, base_n)
524 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, "/content/pages/" as *u8, 15)
525 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, slug, slug_n)
526 p = _bs_bcopy(src_path, p, NX_BS_PATH_CAP, ".md" as *u8, 3)
527 src_path[p] = 0
528
529 let final_html: *u8 = sys_mmap(NX_BS_HTML_CAP)
530 let n: i64 = nx_bs_render_doc(src_path, site_yaml,
531 slug, slug_n,
532 page_tmpl, page_n,
533 layout_tmpl, layout_n,
534 final_html, NX_BS_HTML_CAP)
535 if n < 0 { return n }
536
537 let out_path: *u8 = sys_mmap(NX_BS_PATH_CAP)
538 var q: i64 = 0
539 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, base_dir, base_n)
540 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, "/dist/pages/" as *u8, 12)
541 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, slug, slug_n)
542 q = _bs_bcopy(out_path, q, NX_BS_PATH_CAP, ".html" as *u8, 5)
543 out_path[q] = 0
544
545 return nx_bs_write_file(out_path, final_html, n)
546}