@moku-labs/web 0.3.1 → 0.4.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.
@@ -0,0 +1,20 @@
1
+ import { render } from "preact";
2
+ //#region src/plugins/spa/render.ts
3
+ /**
4
+ * Render a route's `VNode` into the live swap region, replacing its contents.
5
+ * Reuses the build's component output verbatim (same `route.render`), so the
6
+ * client paint matches the SSG paint.
7
+ *
8
+ * @param vnode - The VNode produced by the matched route's `.render(ctx)`.
9
+ * @param region - The swap-region element to render into.
10
+ * @example
11
+ * ```ts
12
+ * const { renderVNode } = await import("./render");
13
+ * renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
14
+ * ```
15
+ */
16
+ function renderVNode(vnode, region) {
17
+ render(vnode, region);
18
+ }
19
+ //#endregion
20
+ export { renderVNode };
@@ -0,0 +1,20 @@
1
+ let preact = require("preact");
2
+ //#region src/plugins/spa/render.ts
3
+ /**
4
+ * Render a route's `VNode` into the live swap region, replacing its contents.
5
+ * Reuses the build's component output verbatim (same `route.render`), so the
6
+ * client paint matches the SSG paint.
7
+ *
8
+ * @param vnode - The VNode produced by the matched route's `.render(ctx)`.
9
+ * @param region - The swap-region element to render into.
10
+ * @example
11
+ * ```ts
12
+ * const { renderVNode } = await import("./render");
13
+ * renderVNode(route._handlers.render(ctx), document.querySelector("main > section"));
14
+ * ```
15
+ */
16
+ function renderVNode(vnode, region) {
17
+ (0, preact.render)(vnode, region);
18
+ }
19
+ //#endregion
20
+ exports.renderVNode = renderVNode;
@@ -0,0 +1,90 @@
1
+ import { t as dataSuffix } from "./convention-X3zLTlJ8.mjs";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import pLimit from "p-limit";
5
+ //#region src/plugins/data/writer.ts
6
+ /**
7
+ * @file data plugin — Node write side of the agnostic provider (`write()`).
8
+ *
9
+ * This is the ONLY module in the `data` plugin that touches `node:*`. It is
10
+ * reached exclusively through a lazy `await import("./writer")` inside `api.ts`'s
11
+ * `write()`, so a browser bundle that composes `data` for the read side never
12
+ * statically pulls `node:fs` (see `__tests__/unit/isolation.test.ts`).
13
+ *
14
+ * It is domain-agnostic: it persists whatever `data` each {@link DataEntry} carries
15
+ * (the route's own `load`/projection output — `build` produced these), one JSON
16
+ * file per page, at the path {@link dataSuffix} mirrors from the page URL. No
17
+ * content/article knowledge, no expansion (build already expanded the routes).
18
+ */
19
+ /** Default build output root, matching `build`'s `defaultBuildConfig.outDir`. */
20
+ const DEFAULT_OUT_DIR = "./dist";
21
+ /** Concurrency bound for per-page writes (matches the OG-image phase's pool). */
22
+ const WRITE_CONCURRENCY = 8;
23
+ /**
24
+ * Resolve the `outDir`-relative file for a page path using the shared convention,
25
+ * trimming a trailing slash from the config dir so the join stays clean.
26
+ *
27
+ * @param outputDir - The configured data output subdir (e.g. `"_data"`).
28
+ * @param pagePath - The page URL path (e.g. `/en/hello/`).
29
+ * @returns The `outDir`-relative file path (e.g. `_data/en/hello/index.json`).
30
+ * @example
31
+ * ```ts
32
+ * relativeFile("_data", "/en/hello/"); // "_data/en/hello/index.json"
33
+ * ```
34
+ */
35
+ function relativeFile(outputDir, pagePath) {
36
+ return `${outputDir.endsWith("/") ? outputDir.slice(0, -1) : outputDir}/${dataSuffix(pagePath)}`;
37
+ }
38
+ /**
39
+ * Persist one entry's data as JSON under `<outDir>/<relativeFile>` and return the
40
+ * written `{ relative, bytes }`.
41
+ *
42
+ * @param entry - The page entry to persist.
43
+ * @param outDir - The build output root.
44
+ * @param outputDir - The configured data output subdir.
45
+ * @returns The written file's `outDir`-relative path and byte length.
46
+ * @example
47
+ * ```ts
48
+ * await writeEntry({ path: "/en/hello/", data }, "./dist", "_data");
49
+ * ```
50
+ */
51
+ async function writeEntry(entry, outDir, outputDir) {
52
+ const relative = relativeFile(outputDir, entry.path);
53
+ const filePath = path.join(outDir, relative);
54
+ await mkdir(path.dirname(filePath), { recursive: true });
55
+ const body = JSON.stringify(entry.data);
56
+ await writeFile(filePath, body, "utf8");
57
+ return {
58
+ relative,
59
+ bytes: Buffer.byteLength(body, "utf8")
60
+ };
61
+ }
62
+ /**
63
+ * The Node write side of the provider. Persists one JSON file per entry (bounded
64
+ * by `p-limit`) — domain-agnostic, no route expansion (`build` already did that).
65
+ * Records the summary in `ctx.state.lastWrite`.
66
+ *
67
+ * @param ctx - The data plugin context (state, config).
68
+ * @param entries - The per-page data to persist.
69
+ * @param options - Optional overrides.
70
+ * @param options.outDir - Build output directory to write under (default `./dist`).
71
+ * @returns A summary of the written files.
72
+ * @example
73
+ * ```ts
74
+ * const summary = await writeData(ctx, [{ path: "/en/hello/", data }], { outDir: "./dist" });
75
+ * ```
76
+ */
77
+ async function writeData(ctx, entries, options) {
78
+ const outDir = options?.outDir ?? DEFAULT_OUT_DIR;
79
+ const limit = pLimit(WRITE_CONCURRENCY);
80
+ const written = await Promise.all(entries.map((entry) => limit(() => writeEntry(entry, outDir, ctx.config.outputDir))));
81
+ const summary = {
82
+ fileCount: written.length,
83
+ bytes: written.reduce((total, file) => total + file.bytes, 0),
84
+ files: written.map((file) => file.relative)
85
+ };
86
+ ctx.state.lastWrite = summary;
87
+ return summary;
88
+ }
89
+ //#endregion
90
+ export { writeData };
@@ -0,0 +1,92 @@
1
+ const require_convention = require("./convention-Dr8jxG70.cjs");
2
+ let node_fs_promises = require("node:fs/promises");
3
+ let node_path = require("node:path");
4
+ node_path = require_convention.__toESM(node_path, 1);
5
+ let p_limit = require("p-limit");
6
+ p_limit = require_convention.__toESM(p_limit, 1);
7
+ //#region src/plugins/data/writer.ts
8
+ /**
9
+ * @file data plugin — Node write side of the agnostic provider (`write()`).
10
+ *
11
+ * This is the ONLY module in the `data` plugin that touches `node:*`. It is
12
+ * reached exclusively through a lazy `await import("./writer")` inside `api.ts`'s
13
+ * `write()`, so a browser bundle that composes `data` for the read side never
14
+ * statically pulls `node:fs` (see `__tests__/unit/isolation.test.ts`).
15
+ *
16
+ * It is domain-agnostic: it persists whatever `data` each {@link DataEntry} carries
17
+ * (the route's own `load`/projection output — `build` produced these), one JSON
18
+ * file per page, at the path {@link dataSuffix} mirrors from the page URL. No
19
+ * content/article knowledge, no expansion (build already expanded the routes).
20
+ */
21
+ /** Default build output root, matching `build`'s `defaultBuildConfig.outDir`. */
22
+ const DEFAULT_OUT_DIR = "./dist";
23
+ /** Concurrency bound for per-page writes (matches the OG-image phase's pool). */
24
+ const WRITE_CONCURRENCY = 8;
25
+ /**
26
+ * Resolve the `outDir`-relative file for a page path using the shared convention,
27
+ * trimming a trailing slash from the config dir so the join stays clean.
28
+ *
29
+ * @param outputDir - The configured data output subdir (e.g. `"_data"`).
30
+ * @param pagePath - The page URL path (e.g. `/en/hello/`).
31
+ * @returns The `outDir`-relative file path (e.g. `_data/en/hello/index.json`).
32
+ * @example
33
+ * ```ts
34
+ * relativeFile("_data", "/en/hello/"); // "_data/en/hello/index.json"
35
+ * ```
36
+ */
37
+ function relativeFile(outputDir, pagePath) {
38
+ return `${outputDir.endsWith("/") ? outputDir.slice(0, -1) : outputDir}/${require_convention.dataSuffix(pagePath)}`;
39
+ }
40
+ /**
41
+ * Persist one entry's data as JSON under `<outDir>/<relativeFile>` and return the
42
+ * written `{ relative, bytes }`.
43
+ *
44
+ * @param entry - The page entry to persist.
45
+ * @param outDir - The build output root.
46
+ * @param outputDir - The configured data output subdir.
47
+ * @returns The written file's `outDir`-relative path and byte length.
48
+ * @example
49
+ * ```ts
50
+ * await writeEntry({ path: "/en/hello/", data }, "./dist", "_data");
51
+ * ```
52
+ */
53
+ async function writeEntry(entry, outDir, outputDir) {
54
+ const relative = relativeFile(outputDir, entry.path);
55
+ const filePath = node_path.default.join(outDir, relative);
56
+ await (0, node_fs_promises.mkdir)(node_path.default.dirname(filePath), { recursive: true });
57
+ const body = JSON.stringify(entry.data);
58
+ await (0, node_fs_promises.writeFile)(filePath, body, "utf8");
59
+ return {
60
+ relative,
61
+ bytes: Buffer.byteLength(body, "utf8")
62
+ };
63
+ }
64
+ /**
65
+ * The Node write side of the provider. Persists one JSON file per entry (bounded
66
+ * by `p-limit`) — domain-agnostic, no route expansion (`build` already did that).
67
+ * Records the summary in `ctx.state.lastWrite`.
68
+ *
69
+ * @param ctx - The data plugin context (state, config).
70
+ * @param entries - The per-page data to persist.
71
+ * @param options - Optional overrides.
72
+ * @param options.outDir - Build output directory to write under (default `./dist`).
73
+ * @returns A summary of the written files.
74
+ * @example
75
+ * ```ts
76
+ * const summary = await writeData(ctx, [{ path: "/en/hello/", data }], { outDir: "./dist" });
77
+ * ```
78
+ */
79
+ async function writeData(ctx, entries, options) {
80
+ const outDir = options?.outDir ?? DEFAULT_OUT_DIR;
81
+ const limit = (0, p_limit.default)(WRITE_CONCURRENCY);
82
+ const written = await Promise.all(entries.map((entry) => limit(() => writeEntry(entry, outDir, ctx.config.outputDir))));
83
+ const summary = {
84
+ fileCount: written.length,
85
+ bytes: written.reduce((total, file) => total + file.bytes, 0),
86
+ files: written.map((file) => file.relative)
87
+ };
88
+ ctx.state.lastWrite = summary;
89
+ return summary;
90
+ }
91
+ //#endregion
92
+ exports.writeData = writeData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/web",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Content static-site generator + SPA web framework for TypeScript, built on @moku-labs/core.",
5
5
  "author": "Oleksandr Kucherenko",
6
6
  "license": "MIT",
@@ -42,13 +42,14 @@
42
42
  }
43
43
  }
44
44
  },
45
+ "sideEffects": false,
45
46
  "files": [
46
47
  "dist",
47
48
  "LICENSE",
48
49
  "README.md"
49
50
  ],
50
51
  "engines": {
51
- "node": ">=22.0.0",
52
+ "node": ">=24.0.0",
52
53
  "bun": ">=1.3.14"
53
54
  },
54
55
  "dependencies": {