@moriajs/renderer 0.4.32 → 0.4.34
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/.turbo/turbo-build.log +1 -1
- package/README.md +11 -2
- package/SSR_GUIDELINES.md +91 -0
- package/package.json +1 -1
package/.turbo/turbo-build.log
CHANGED
package/README.md
CHANGED
|
@@ -11,6 +11,15 @@ Isomorphic renderer for Mithril.js.
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import {
|
|
15
|
-
const html = await
|
|
14
|
+
import { renderToString } from '@moriajs/renderer';
|
|
15
|
+
const html = await renderToString(MyComponent, initialData);
|
|
16
16
|
```
|
|
17
|
+
|
|
18
|
+
## Isomorphic Components & SSR
|
|
19
|
+
|
|
20
|
+
When building components that run on both server and client, you must follow specific guidelines to avoid ReferenceErrors and state leaks:
|
|
21
|
+
|
|
22
|
+
> [!CAUTION]
|
|
23
|
+
> **Do not use top-level module variables for component state.** This can lead to server crashes (ReferenceError) during HMR and state bleeding across requests. Always use `vnode.state`.
|
|
24
|
+
|
|
25
|
+
See the full [SSR Guidelines](./SSR_GUIDELINES.md) for best practices and common pitfalls.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# MoriaJS SSR Guidelines: Isomorphic Mithril Components
|
|
2
|
+
|
|
3
|
+
MoriaJS uses Server-Side Rendering (SSR) with client-side hydration to provide fast initial page loads and SEO benefits. However, building isomorphic applications (code that runs on both server and client) requires careful consideration of state management and environment differences.
|
|
4
|
+
|
|
5
|
+
## The Pitfall: Top-level Module Variables
|
|
6
|
+
|
|
7
|
+
One of the most common issues in Vite-based SSR is the use of top-level module variables for component state.
|
|
8
|
+
|
|
9
|
+
### Why it fails
|
|
10
|
+
|
|
11
|
+
1. **Vite SSR Closure Scope**: During development, Vite transforms SSR dependencies and handles Hot Module Replacement (HMR). Top-level variables in your component files can lose their declaration context or become undefined during HMR cycles, leading to `ReferenceError: [var] is not defined`.
|
|
12
|
+
2. **State Leaks**: Top-level variables persist for the lifetime of the server process. If multiple requests share the same server process, state stored in module-scope variables will "bleed" across requests, leading to data leaks and inconsistent behavior.
|
|
13
|
+
|
|
14
|
+
### ❌ Incorrect (Anti-pattern)
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
// src/routes/pages/Profile.ts
|
|
18
|
+
let userId; // ERROR: This is shared across ALL server requests and may fail during HMR
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
oninit(vnode) {
|
|
22
|
+
userId = vnode.attrs.id;
|
|
23
|
+
},
|
|
24
|
+
view() {
|
|
25
|
+
return m('div', `User ID: ${userId}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### ✅ Correct (Recommended)
|
|
31
|
+
|
|
32
|
+
Always use `vnode.state` for local component state. This ensures the state is encapsulated within the component instance and cleared when the component is destroyed.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// src/routes/pages/Profile.ts
|
|
36
|
+
export default {
|
|
37
|
+
oninit(vnode) {
|
|
38
|
+
vnode.state.userId = vnode.attrs.id;
|
|
39
|
+
},
|
|
40
|
+
view(vnode) {
|
|
41
|
+
return m('div', `User ID: ${vnode.state.userId}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Browser-Only Globals
|
|
47
|
+
|
|
48
|
+
The server-side environment (Node.js) does not have access to browser globals like `window`, `document`, `localStorage`, or `navigator`.
|
|
49
|
+
|
|
50
|
+
### Safe Access
|
|
51
|
+
|
|
52
|
+
Wrap access to browser globals in checks or use them only in lifecycle hooks that run ONLY on the client (like `oncreate` or `onupdate`).
|
|
53
|
+
|
|
54
|
+
> [!NOTE]
|
|
55
|
+
> MoriaJS's `renderToString` does **not** execute `oncreate` or `onupdate` hooks. These hooks are safe for browser-specific logic.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
export default {
|
|
59
|
+
oninit() {
|
|
60
|
+
// SSR SAFE: Check if window exists
|
|
61
|
+
if (typeof window !== 'undefined') {
|
|
62
|
+
console.log(window.location.href);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
oncreate() {
|
|
66
|
+
// BROWSER ONLY: Safe to use window/document here
|
|
67
|
+
document.title = 'New Title';
|
|
68
|
+
},
|
|
69
|
+
view() {
|
|
70
|
+
return m('div', 'Hello World');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Handling Side Effects
|
|
76
|
+
|
|
77
|
+
### `m.request` and `m.redraw`
|
|
78
|
+
|
|
79
|
+
MoriaJS automatically patches `m.request` and `m.redraw` during SSR to prevent server crashes caused by lack of browser APIs (like `XMLHttpRequest`).
|
|
80
|
+
|
|
81
|
+
- `m.request` returns an immediately resolving promise on the server.
|
|
82
|
+
- `m.redraw` becomes a no-op on the server.
|
|
83
|
+
|
|
84
|
+
If you need data for SSR, use the `getServerData` pattern (if available in your route) or hydrate data via `initialData` in `renderToString`.
|
|
85
|
+
|
|
86
|
+
## Summary Checklist
|
|
87
|
+
|
|
88
|
+
- [ ] Use `vnode.state` instead of top-level variables.
|
|
89
|
+
- [ ] Check `typeof window !== 'undefined'` before using browser globals.
|
|
90
|
+
- [ ] Keep DOM-heavy logic in `oncreate`.
|
|
91
|
+
- [ ] Use `initialData` to pass state from server to client.
|