@moku-labs/web 0.4.2 → 0.5.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/README.md CHANGED
@@ -73,10 +73,32 @@ from production builds.
73
73
  `createApp`'s **defaults are the isomorphic plugins** — the ones that run unchanged on
74
74
  both Node and the browser: `site`, `i18n`, `router`, `head`, `spa` (plus the `log`/`env`
75
75
  core). The **node-only** plugins (`content`, `build`, `deploy`) are exported but not
76
- defaults — add them with `createApp({ plugins: [...] })` for a Node build, and omit them
77
- in a browser app (with `"sideEffects": false`, your bundler tree-shakes them out). You
78
- also choose the `env` provider per target: `[dotenv(), processEnv()]` on Node,
79
- `[browserEnv()]` in the browser. The framework never hard-blocks either runtime.
76
+ defaults — add them with `createApp({ plugins: [...] })` for a Node build. You also choose
77
+ the `env` provider per target: `[dotenv(), processEnv()]` on Node, `browserEnv()` in the
78
+ browser. The framework never hard-blocks either runtime.
79
+
80
+ Two entry points pick the right surface per target:
81
+
82
+ - **`@moku-labs/web`** (the `.` entry, dual ESM+CJS) — the full surface, for **Node SSG
83
+ builds**: add `contentPlugin`/`buildPlugin`/`deployPlugin` and wire `dotenv()`/`processEnv()`.
84
+ - **`@moku-labs/web/browser`** (ESM-only) — the recommended **client/browser** entry. It
85
+ re-exports the SAME `createApp`/`createPlugin` over the SAME isomorphic default set
86
+ (`site`, `i18n`, `router`, `head`, `spa` + `log`/`env`), plus `dataPlugin`, `defineRoutes`,
87
+ `route`, `createComponent`, `browserEnv`, the SEO head primitives, and the browser-relevant type namespaces
88
+ (`Data`, `Env`, `Head`, `Log`, `Router`, `Spa`). It **excludes** everything node-only
89
+ (`contentPlugin`, `buildPlugin`, `deployPlugin`, the `dotenv`/`processEnv`/`cloudflareBindings`
90
+ env providers, and the `Build`/`Content`/`Deploy` type namespaces), and **pre-wires
91
+ `browserEnv()`** as the default env provider — so `env` works with **zero** consumer
92
+ config (no `pluginConfigs.env.providers` needed), resolving from `import.meta.env` and
93
+ `globalThis.__ENV__`.
94
+
95
+ Importing `@moku-labs/web/browser` can **never** drag node/native code into a client bundle,
96
+ regardless of your bundler or tree-shaking — its static import graph references zero
97
+ node-only modules. This is stronger and more reliable than importing `@moku-labs/web` and
98
+ relying on `"sideEffects": false` tree-shaking, which is fragile (building entries together
99
+ can merge node code into a shared chunk). A CI gate (`bun run check:bundle`) asserts the
100
+ built browser bundle has zero static node/native imports and stays under a gzip size budget
101
+ (the browser bundle is currently ~35 kB gzip).
80
102
 
81
103
  `data` is a special case — an **optional, domain-agnostic data provider**: composed on
82
104
  Node, `build` calls `data.write(...)` to persist each page's real `load()` output as JSON
@@ -91,9 +113,20 @@ The single switch is **`router.mode`** (`"ssg" | "spa" | "hybrid"`): `build` wri
91
113
  declare `.parse(raw => data)` (validated at the client trust boundary) — `build` errors
92
114
  otherwise.
93
115
 
94
- A browser entry is just your own `createApp(...).start()` over the defaults (plus
95
- `dataPlugin` for DATA nav) — `spa`'s `onStart` mounts islands onto the SSR'd DOM and
96
- intercepts navigation.
116
+ A browser entry is `createApp(...).start()` imported from `@moku-labs/web/browser` over the
117
+ defaults (plus `dataPlugin` for DATA nav) — `spa`'s `onStart` mounts islands onto the SSR'd
118
+ DOM and intercepts navigation. `dataPlugin` stays consumer-composed (it is not a default):
119
+ compose it for client DATA navigation (`router.mode` `"spa"` | `"hybrid"`); its node
120
+ write-half is loaded only via dynamic import.
121
+
122
+ ```ts
123
+ // A browser bundle — guaranteed node-free, env pre-wired:
124
+ import { createApp, spaPlugin, dataPlugin, browserEnv, defineRoutes, route } from "@moku-labs/web/browser";
125
+
126
+ // env works with no wiring — browserEnv is the default provider
127
+ const app = createApp({ plugins: [dataPlugin], pluginConfigs: { router: { mode: "spa", routes } } });
128
+ await app.start();
129
+ ```
97
130
 
98
131
  ## Plugins
99
132