round-core 0.0.1
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 +354 -0
- package/Round.png +0 -0
- package/bun.lock +414 -0
- package/cli.js +2 -0
- package/index.html +19 -0
- package/index.js +2 -0
- package/package.json +40 -0
- package/src/cli.js +599 -0
- package/src/compiler/index.js +2 -0
- package/src/compiler/transformer.js +395 -0
- package/src/compiler/vite-plugin.js +461 -0
- package/src/index.js +45 -0
- package/src/runtime/context.js +62 -0
- package/src/runtime/dom.js +345 -0
- package/src/runtime/error-boundary.js +48 -0
- package/src/runtime/error-reporter.js +13 -0
- package/src/runtime/error-store.js +85 -0
- package/src/runtime/errors.js +152 -0
- package/src/runtime/lifecycle.js +142 -0
- package/src/runtime/markdown.js +72 -0
- package/src/runtime/router.js +371 -0
- package/src/runtime/signals.js +510 -0
- package/src/runtime/store.js +208 -0
- package/src/runtime/suspense.js +106 -0
- package/vite.config.js +10 -0
- package/vitest.config.js +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
<h1 align="center">Round Framework</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/ZtaMDev/RoundJS/main/Round.png" alt="Dars Framework Logo" width="200" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://img.shields.io/npm/v/round-core?color=brightgreen" alt="PyPI Version" />
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<em>Round is a lightweight frontend framework focused on building <b>single-page applications (SPAs)</b> with <b>fine‑grained reactivity.</b></em>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install round-core
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Instead of a Virtual DOM diff, Round updates the UI by subscribing DOM updates directly to reactive primitives (**signals**). This keeps rendering predictable, small, and fast for interactive apps.
|
|
20
|
+
|
|
21
|
+
## What Round is focused on
|
|
22
|
+
|
|
23
|
+
- **SPA-first**: client-side navigation and UI updates.
|
|
24
|
+
- **Fine-grained reactivity**: update only what depends on the changed signal.
|
|
25
|
+
- **Ergonomic bindings**: built-in two-way bindings with `bind:*` directives.
|
|
26
|
+
- **A JSX superset**: `.round` files support extra control-flow syntax that compiles to JavaScript.
|
|
27
|
+
- **Minimal runtime**: DOM-first runtime (no VDOM diffing).
|
|
28
|
+
|
|
29
|
+
## Concepts
|
|
30
|
+
|
|
31
|
+
### SPA
|
|
32
|
+
|
|
33
|
+
A **Single Page Application (SPA)** loads one HTML page and then updates the UI dynamically as the user navigates and interacts—without full page reloads.
|
|
34
|
+
|
|
35
|
+
### Fine-grained reactivity (signals)
|
|
36
|
+
|
|
37
|
+
A **signal** is a small reactive container.
|
|
38
|
+
|
|
39
|
+
- Reading a signal inside an `effect()` tracks a dependency.
|
|
40
|
+
- Writing to a signal triggers only the subscribed computations.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Normal Installation
|
|
44
|
+
|
|
45
|
+
Simply install the `round-core` package
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bun add round-core
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Or:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install round-core
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Repo Installation
|
|
58
|
+
|
|
59
|
+
> Round is currently in active development. If you are using the repository directly, install dependencies and run the CLI locally.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bun install
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick start (create a new app)
|
|
72
|
+
|
|
73
|
+
Round includes a CLI with a project initializer.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
round init myapp
|
|
77
|
+
cd myapp
|
|
78
|
+
npm install
|
|
79
|
+
npm run dev
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This scaffolds a minimal Round app with `src/app.round` and an example `src/counter.round`.
|
|
83
|
+
|
|
84
|
+
## `.round` files
|
|
85
|
+
|
|
86
|
+
A `.round` file is a JSX-based component module (ESM) compiled by the Round toolchain.
|
|
87
|
+
|
|
88
|
+
Example `src/app.round`:
|
|
89
|
+
|
|
90
|
+
```jsx
|
|
91
|
+
import { Route } from 'round-core';
|
|
92
|
+
import { Counter } from './counter';
|
|
93
|
+
|
|
94
|
+
export default function App() {
|
|
95
|
+
return (
|
|
96
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
97
|
+
<Route route="/" title="Home">
|
|
98
|
+
<Counter />
|
|
99
|
+
</Route>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Core API
|
|
106
|
+
|
|
107
|
+
### `signal(initialValue)`
|
|
108
|
+
|
|
109
|
+
Create a reactive signal.
|
|
110
|
+
|
|
111
|
+
- Call with no arguments to **read**.
|
|
112
|
+
- Call with one argument to **write**.
|
|
113
|
+
- Use `.value` to read/write the current value in a non-subscribing way (static access).
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
import { signal } from 'round-core';
|
|
117
|
+
|
|
118
|
+
export function Counter() {
|
|
119
|
+
const count = signal(0);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div>
|
|
123
|
+
<h1>Count: {count()}</h1>
|
|
124
|
+
<button onClick={() => count(count() + 1)}>Increment</button>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `effect(fn)`
|
|
131
|
+
|
|
132
|
+
Run `fn` whenever the signals it reads change.
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
import { signal, effect } from 'round-core';
|
|
136
|
+
|
|
137
|
+
const name = signal('Ada');
|
|
138
|
+
|
|
139
|
+
effect(() => {
|
|
140
|
+
console.log('Name changed:', name());
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
name('Grace');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `bindable(initialValue)`
|
|
147
|
+
|
|
148
|
+
`bindable()` creates a signal intended for **two-way DOM bindings**.
|
|
149
|
+
|
|
150
|
+
```jsx
|
|
151
|
+
import { bindable } from 'round-core';
|
|
152
|
+
|
|
153
|
+
export function Example() {
|
|
154
|
+
const email = bindable('');
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div>
|
|
158
|
+
<input bind:value={email} placeholder="Email" />
|
|
159
|
+
<div>Typed: {email()}</div>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `bindable.object(initialObject)` and deep binding
|
|
166
|
+
|
|
167
|
+
Round supports object-shaped state with ergonomic deep bindings via proxies.
|
|
168
|
+
|
|
169
|
+
```jsx
|
|
170
|
+
import { bindable } from 'round-core';
|
|
171
|
+
|
|
172
|
+
export function Profile() {
|
|
173
|
+
const user = bindable.object({
|
|
174
|
+
profile: { bio: '' },
|
|
175
|
+
flags: { newsletter: false }
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div>
|
|
180
|
+
<textarea bind:value={user.profile.bio} />
|
|
181
|
+
<label>
|
|
182
|
+
<input type="checkbox" bind:checked={user.flags.newsletter} />
|
|
183
|
+
Subscribe
|
|
184
|
+
</label>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### `$pick(path)`
|
|
191
|
+
|
|
192
|
+
Create a view signal from a signal/bindable that holds an object.
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
import { bindable } from 'round-core';
|
|
196
|
+
|
|
197
|
+
const user = bindable({ profile: { bio: 'Hello' } });
|
|
198
|
+
const bio = user.$pick('profile.bio');
|
|
199
|
+
|
|
200
|
+
console.log(bio());
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### `.transform(fromInput, toOutput)`
|
|
204
|
+
|
|
205
|
+
Transform a signal/bindable to adapt between DOM values (often strings) and your internal representation.
|
|
206
|
+
|
|
207
|
+
```jsx
|
|
208
|
+
import { bindable } from 'round-core';
|
|
209
|
+
|
|
210
|
+
export function AgeField() {
|
|
211
|
+
const age = bindable('18')
|
|
212
|
+
.transform(
|
|
213
|
+
(str) => Math.max(0, parseInt(str, 10) || 0),
|
|
214
|
+
(num) => String(num)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
return <input type="number" bind:value={age} />;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### `.validate(validator, options)`
|
|
222
|
+
|
|
223
|
+
Attach validation to a signal/bindable.
|
|
224
|
+
|
|
225
|
+
- Invalid writes do not update the underlying value.
|
|
226
|
+
- `signal.error` is itself a signal (reactive) containing the current error message or `null`.
|
|
227
|
+
- `options.validateOn` can be `'input'` (default) or `'blur'`.
|
|
228
|
+
- `options.validateInitial` can trigger validation on startup.
|
|
229
|
+
|
|
230
|
+
```jsx
|
|
231
|
+
import { bindable } from 'round-core';
|
|
232
|
+
|
|
233
|
+
export function EmailField() {
|
|
234
|
+
const email = bindable('')
|
|
235
|
+
.validate(
|
|
236
|
+
(v) => v.includes('@') || 'Invalid email',
|
|
237
|
+
{ validateOn: 'blur' }
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<div>
|
|
242
|
+
<input bind:value={email} placeholder="name@example.com" />
|
|
243
|
+
<div style={() => ({ color: email.error() ? 'crimson' : '#666' })}>
|
|
244
|
+
{email.error}
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## DOM binding directives
|
|
252
|
+
|
|
253
|
+
Round supports two-way bindings via props:
|
|
254
|
+
|
|
255
|
+
- `bind:value={someBindable}` for text-like inputs, `<textarea>`, and `<select>`.
|
|
256
|
+
- `bind:checked={someBindable}` for `<input type="checkbox">` and `<input type="radio">`.
|
|
257
|
+
|
|
258
|
+
Round will warn if the value is not signal-like, and will warn if you bind a plain `signal()` instead of a `bindable()`.
|
|
259
|
+
|
|
260
|
+
## JSX superset control flow
|
|
261
|
+
|
|
262
|
+
Round extends JSX inside `.round` files with a control-flow syntax that compiles to JavaScript.
|
|
263
|
+
|
|
264
|
+
### `if / else if / else`
|
|
265
|
+
|
|
266
|
+
```jsx
|
|
267
|
+
{if(user.loggedIn){
|
|
268
|
+
<Dashboard />
|
|
269
|
+
} else if(user.loading){
|
|
270
|
+
<div>Loading...</div>
|
|
271
|
+
} else {
|
|
272
|
+
<Login />
|
|
273
|
+
}}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Notes:
|
|
277
|
+
|
|
278
|
+
- Conditions may be normal JS expressions.
|
|
279
|
+
- For *simple paths* like `flags.showCounter` (identifier/member paths), Round will auto-unwrap signal-like values (call them) so the condition behaves as expected.
|
|
280
|
+
|
|
281
|
+
### `for (... in ...)`
|
|
282
|
+
|
|
283
|
+
```jsx
|
|
284
|
+
{for(item in items){
|
|
285
|
+
<div className="row">{item}</div>
|
|
286
|
+
}}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
This compiles roughly to a `.map(...)` under the hood.
|
|
290
|
+
|
|
291
|
+
## Rendering model (no VDOM)
|
|
292
|
+
|
|
293
|
+
Round renders to the DOM directly using a small runtime:
|
|
294
|
+
|
|
295
|
+
- Elements are created with `document.createElement(...)`.
|
|
296
|
+
- Dynamic children and reactive props are updated via `effect()` subscriptions.
|
|
297
|
+
- Components are functions returning DOM nodes (or arrays of nodes).
|
|
298
|
+
|
|
299
|
+
This is a **DOM-first, fine-grained reactive model**, rather than a Virtual DOM diffing renderer.
|
|
300
|
+
|
|
301
|
+
## Routing
|
|
302
|
+
|
|
303
|
+
Round includes router primitives intended for SPA navigation.
|
|
304
|
+
|
|
305
|
+
Typical usage:
|
|
306
|
+
|
|
307
|
+
```jsx
|
|
308
|
+
import { Route } from 'round-core';
|
|
309
|
+
|
|
310
|
+
export default function App() {
|
|
311
|
+
return (
|
|
312
|
+
<div>
|
|
313
|
+
<Route route="/" title="Home">
|
|
314
|
+
<div>Home</div>
|
|
315
|
+
</Route>
|
|
316
|
+
<Route route="/about" title="About">
|
|
317
|
+
<div>About</div>
|
|
318
|
+
</Route>
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Suspense and lazy loading
|
|
325
|
+
|
|
326
|
+
Round supports `Suspense` for promise-based rendering and `lazy()` for code splitting.
|
|
327
|
+
|
|
328
|
+
(These APIs are evolving; expect improvements as Round’s compiler and runtime expand.)
|
|
329
|
+
|
|
330
|
+
## Error handling
|
|
331
|
+
|
|
332
|
+
Round aims to provide strong developer feedback:
|
|
333
|
+
|
|
334
|
+
- Runtime error reporting with safe boundaries.
|
|
335
|
+
- `ErrorBoundary` to catch render-time errors and show a fallback.
|
|
336
|
+
|
|
337
|
+
## CLI
|
|
338
|
+
|
|
339
|
+
The CLI is intended for day-to-day development:
|
|
340
|
+
|
|
341
|
+
- `round dev`
|
|
342
|
+
- `round build`
|
|
343
|
+
- `round preview`
|
|
344
|
+
- `round init <name>`
|
|
345
|
+
|
|
346
|
+
Run `round -h` to see available commands.
|
|
347
|
+
|
|
348
|
+
## Status
|
|
349
|
+
|
|
350
|
+
Round is under active development and the API is still stabilizing. The README is currently the primary documentation; a dedicated documentation site will be built later using Round itself.
|
|
351
|
+
|
|
352
|
+
## License
|
|
353
|
+
|
|
354
|
+
MIT
|
package/Round.png
ADDED
|
Binary file
|