@rasenjs/web 0.1.0-alpha

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 ADDED
@@ -0,0 +1,263 @@
1
+ # @rasenjs/web
2
+
3
+ Isomorphic web components with routing that automatically switch between DOM and HTML rendering based on the execution environment.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rasenjs/web @rasenjs/router @rasenjs/core zod
9
+ ```
10
+
11
+ ## Overview
12
+
13
+ `@rasenjs/web` is a unified facade package that provides both basic elements and routing components for web rendering. It automatically exports the appropriate renderer based on the environment:
14
+
15
+ - **Server-side (SSR)**: Uses `@rasenjs/html` + `@rasenjs/router-html` to render HTML strings
16
+ - **Client-side (Browser)**: Uses `@rasenjs/dom` + `@rasenjs/router-dom` for reactive DOM manipulation
17
+
18
+ This allows you to write **isomorphic components with routing** that work seamlessly in both environments without code changes.
19
+
20
+ ## How It Works
21
+
22
+ The package uses [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) in `package.json`:
23
+
24
+ ```json
25
+ {
26
+ "exports": {
27
+ ".": {
28
+ "ssr": "./dist/html.js", // Server-side rendering
29
+ "browser": "./dist/dom.js", // Client-side rendering
30
+ "default": "./dist/dom.js" // Default to browser
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ Build tools like Vite automatically select the correct export condition based on the environment.
37
+
38
+ ## Quick Start
39
+
40
+ ### Isomorphic Component
41
+
42
+ Write your component once, and it works in both SSR and client environments:
43
+
44
+ ```typescript
45
+ import { setReactiveRuntime } from '@rasenjs/core'
46
+ import { createReactiveRuntime } from '@rasenjs/reactive-vue'
47
+ import { div, p, button } from '@rasenjs/web'
48
+ import { ref } from 'vue'
49
+
50
+ // Setup reactive runtime
51
+ setReactiveRuntime(createReactiveRuntime())
52
+
53
+ // Isomorphic component - works in both SSR and browser
54
+ export const Counter = () => {
55
+ const count = ref(0)
56
+ const increment = () => count.value++
57
+
58
+ return div({
59
+ children: [
60
+ p(() => `Count: ${count.value}`),
61
+ button({ onClick: increment }, 'Increment')
62
+ ]
63
+ })
64
+ }
65
+ ```
66
+
67
+ ### Server-Side Rendering (SSR)
68
+
69
+ On the server, `@rasenjs/web` renders to HTML strings:
70
+
71
+ ```typescript
72
+ import { renderToString } from '@rasenjs/web'
73
+ import { Counter } from './Counter'
74
+
75
+ // Renders to HTML string
76
+ const html = renderToString(Counter())
77
+ // Result: '<div><p>Count: 0</p><button>Increment</button></div>'
78
+ ```
79
+
80
+ ### Client-Side Hydration
81
+
82
+ On the client, `@rasenjs/web` creates interactive DOM elements:
83
+
84
+ ```typescript
85
+ import { mount } from '@rasenjs/web'
86
+ import { Counter } from './Counter'
87
+
88
+ // Mounts and hydrates the component
89
+ mount(Counter(), document.getElementById('app'))
90
+ // Now the button is interactive and updates the counter
91
+ ```
92
+
93
+ ## API
94
+
95
+ `@rasenjs/web` re-exports all APIs from the appropriate renderer and router:
96
+
97
+ ### SSR Mode (Server)
98
+
99
+ Exports everything from `@rasenjs/html` + `@rasenjs/router-html`:
100
+
101
+ - `renderToString()` - Render component to HTML string
102
+ - `div()`, `p()`, `button()`, etc. - HTML element creators
103
+ - `RouterView()` - Render matched route to HTML
104
+ - `RouterLink()` - Create `<a>` tag with href
105
+ - All standard HTML elements
106
+
107
+ ### Browser Mode (Client)
108
+
109
+ Exports everything from `@rasenjs/dom` + `@rasenjs/router-dom`:
110
+
111
+ - `mount()` - Mount component to DOM
112
+ - `div()`, `p()`, `button()`, etc. - DOM element creators
113
+ - `RouterView()` - Render matched route to DOM
114
+ - `RouterLink()` - Create interactive `<a>` with click handling
115
+ - All standard HTML elements with event handling
116
+
117
+ ## Important Considerations
118
+
119
+ ### 1. Event Handlers
120
+
121
+ Event handlers only work in the browser. On the server, they are ignored:
122
+
123
+ ```typescript
124
+ // ✅ Safe - works in browser, ignored in SSR
125
+ button({ onClick: handler }, 'Click me')
126
+
127
+ // ❌ Avoid - 'on' object would render as HTML attribute in SSR
128
+ button({ on: { click: handler } }, 'Click me')
129
+ ```
130
+
131
+ **Best Practice**: Use `onClick`, `onInput`, etc. (not `on: { click }` object).
132
+
133
+ ### 2. Reactive Text Functions
134
+
135
+ Reactive text functions work in both environments:
136
+
137
+ ```typescript
138
+ // ✅ Works in both SSR and browser
139
+ p(() => `Count: ${count.value}`)
140
+ ```
141
+
142
+ - **SSR**: Evaluates once and renders the initial value
143
+ - **Browser**: Sets up reactivity and updates on changes
144
+
145
+ ### 3. Import Path
146
+
147
+ Always import from `@rasenjs/web`, not from `@rasenjs/dom` or `@rasenjs/html` directly:
148
+
149
+ ```typescript
150
+ // ✅ Correct - automatic environment detection
151
+ import { div, mount } from '@rasenjs/web'
152
+
153
+ // Routing
154
+
155
+ ### Define Routes
156
+
157
+ ```typescript
158
+ import { createRouter, createMemoryHistory, template as tpl } from '@rasenjs/router'
159
+ import { z } from 'zod'
160
+
161
+ export const router = createRouter({
162
+ home: '/',
163
+ about: '/about',
164
+ user: tpl`/user/${{ id: z.string() }}`,
165
+ }, {
166
+ history: createMemoryHistory(),
167
+ })
168
+
169
+ export const { routes } = router
170
+ ```
171
+
172
+ ### Use Router Components
173
+
174
+ ```typescript
175
+ import { RouterView, RouterLink, div, nav } from '@rasenjs/web'
176
+ import { router, routes } from './router'
177
+
178
+ export const App = () => div({},
179
+ nav({},
180
+ RouterLink({ to: routes.home }, 'Home'),
181
+ RouterLink({ to: routes.about }, 'About'),
182
+ RouterLink({ to: 'user', params: { id: 'alice' } }, 'User')
183
+ ),
184
+ RouterView(router, {
185
+ [routes.home]: () => Home(),
186
+ [routes.about]: () => About(),
187
+ [routes.user]: (params) => User(params)
188
+ })
189
+ )
190
+ ```
191
+
192
+ ## Use Cases
193
+
194
+ ### Universal Components with Routing
195
+
196
+ Build full-featured apps that work in any environment:
197
+
198
+ ```typescriptRouterView` and `RouterLink`
199
+ - Dynamic route parameters
200
+ - Hot module replacement (HMR)
201
+
202
+ ## Related Packages
203
+
204
+ - [`@rasenjs/router`](../router) - Core headless router
205
+ - [`@rasenjs/dom`](../dom) - DOM renderer (browser)
206
+ - [`@rasenjs/html`](../html) - HTML renderer (SSR)
207
+ - [`@rasenjs/router-dom`](../router-dom) - DOM router components (internal)
208
+ - [`@rasenjs/router-html`](../router-html) - HTML router components (internal)
209
+
210
+ ### SSR with Client Hydration and Routing
211
+
212
+ Perfect for server-rendered apps with client interactivity and navigation:
213
+
214
+ ```typescript
215
+ // server.ts
216
+ import { renderToString } from '@rasenjs/web'
217
+ import { router } from './router'
218
+
219
+ const html = (url: string) => {
220
+ router.push(url) // Set route from request URL
221
+ return `<!DOCTYPE html>
222
+ <html>
223
+ <body>
224
+ <div id="app">${renderToString(App())}</div>
225
+ <script type="module" src="/client.js"></script>
226
+ </body>
227
+ </html>`
228
+ }
229
+
230
+ // client.ts
231
+ import { mount } from '@rasenjs/web'
232
+ import { createBrowserHistory } from '@rasenjs/router'
233
+ import { router } from './router'
234
+
235
+ router.history = createBrowserHistory()
236
+ </html>`
237
+
238
+ // client.ts
239
+ import { mount } from '@rasenjs/web'
240
+ mount(App(), document.getElementById('app'))
241
+ ```
242
+
243
+ ## Complete Example
244
+
245
+ See the [SSR example](../../examples/ssr) for a full implementation with:
246
+
247
+ - Server-side rendering
248
+ - Client-side hydration
249
+ - Routing with `RouterView` and `RouterLink`
250
+ - Dynamic route parameters
251
+ - Hot module replacement (HMR)
252
+
253
+ ## Related Packages
254
+
255
+ - [`@rasenjs/router`](../router) - Core headless router
256
+ - [`@rasenjs/dom`](../dom) - DOM renderer (browser)
257
+ - [`@rasenjs/html`](../html) - HTML renderer (SSR)
258
+ - [`@rasenjs/router-dom`](../router-dom) - DOM router components (internal)
259
+ - [`@rasenjs/router-html`](../router-html) - HTML router components (internal)
260
+
261
+ ## License
262
+
263
+ MIT
package/dist/dom.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from '@rasenjs/dom';
2
+ export * from '@rasenjs/router-dom';
package/dist/dom.js ADDED
@@ -0,0 +1,3 @@
1
+ // src/dom.ts
2
+ export * from "@rasenjs/dom";
3
+ export * from "@rasenjs/router-dom";
package/dist/html.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from '@rasenjs/html';
2
+ export * from '@rasenjs/router-html';
package/dist/html.js ADDED
@@ -0,0 +1,3 @@
1
+ // src/html.ts
2
+ export * from "@rasenjs/html";
3
+ export * from "@rasenjs/router-html";
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@rasenjs/web",
3
+ "version": "0.1.0-alpha",
4
+ "description": "Isomorphic web components with routing (exports DOM or HTML based on environment)",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "ssr": "./dist/html.js",
9
+ "browser": "./dist/dom.js",
10
+ "default": "./dist/dom.js"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsup"
15
+ },
16
+ "dependencies": {
17
+ "@rasenjs/dom": "0.1.5-alpha",
18
+ "@rasenjs/html": "0.1.1-alpha",
19
+ "@rasenjs/router-dom": "0.1.6-alpha",
20
+ "@rasenjs/router-html": "0.1.0-alpha"
21
+ },
22
+ "devDependencies": {
23
+ "tsup": "^8.0.1",
24
+ "typescript": "^5.3.3"
25
+ }
26
+ }
package/src/dom.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * DOM exports for @rasenjs/web
3
+ * Used in client-side/browser environments
4
+ */
5
+ export * from '@rasenjs/dom'
6
+ export * from '@rasenjs/router-dom'
package/src/html.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * HTML exports for @rasenjs/web
3
+ * Used in SSR/server-side environments
4
+ */
5
+ export * from '@rasenjs/html'
6
+ export * from '@rasenjs/router-html'
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src"]
8
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig([
4
+ {
5
+ entry: { dom: 'src/dom.ts' },
6
+ format: ['esm'],
7
+ dts: true,
8
+ clean: true,
9
+ external: ['@rasenjs/dom']
10
+ },
11
+ {
12
+ entry: { html: 'src/html.ts' },
13
+ format: ['esm'],
14
+ dts: true,
15
+ clean: false,
16
+ external: ['@rasenjs/html']
17
+ }
18
+ ])