rasti 4.0.1 → 4.1.0-alpha.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 +134 -13
- package/dist/rasti.js +7 -3
- package/dist/rasti.js.map +1 -1
- package/dist/rasti.min.js +1 -1
- package/dist/rasti.min.js.map +1 -1
- package/es/Component.js +1 -1
- package/es/Component.js.map +1 -1
- package/es/Model.js +3 -1
- package/es/Model.js.map +1 -1
- package/es/View.js +3 -1
- package/es/View.js.map +1 -1
- package/lib/Component.cjs +1 -1
- package/lib/Component.cjs.map +1 -1
- package/lib/Model.cjs +3 -1
- package/lib/Model.cjs.map +1 -1
- package/lib/View.cjs +3 -1
- package/lib/View.cjs.map +1 -1
- package/package.json +36 -26
- package/src/Component.js +1 -1
- package/src/Model.js +3 -1
- package/src/View.js +3 -1
- package/types/Component.d.ts +286 -0
- package/types/Emitter.d.ts +111 -0
- package/types/Model.d.ts +123 -0
- package/types/View.d.ts +181 -0
- package/types/index.d.ts +17 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.1/docs/logo-dark.svg">
|
|
4
|
-
<img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.1/docs/logo.svg" height="120">
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.1.0-alpha.1/docs/logo-dark.svg">
|
|
4
|
+
<img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.1.0-alpha.1/docs/logo.svg" height="120">
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
It provides declarative, composable **components** for building state-driven UIs.
|
|
14
14
|
Its low-level MVC core, inspired by **Backbone.js**’s architecture, provides **models**, **views** and **event emitters** as the fundamental building blocks.
|
|
15
15
|
|
|
16
|
-
[](https://github.com/8tentaculos/rasti/actions/workflows/ci.yml)
|
|
17
17
|
[](https://www.npmjs.com/package/rasti)
|
|
18
18
|
[](https://unpkg.com/rasti/dist/rasti.min.js)
|
|
19
19
|
[](https://www.npmjs.com/package/rasti)
|
|
20
20
|
[](https://www.jsdelivr.com/package/npm/rasti)
|
|
21
|
+
[](https://github.com/8tentaculos/rasti/blob/master/LICENSE)
|
|
21
22
|
|
|
22
23
|
## Key Features
|
|
23
24
|
|
|
@@ -35,6 +36,8 @@ Its low-level MVC core, inspired by **Backbone.js**’s architecture, provides *
|
|
|
35
36
|
Seamlessly integrates into existing **Backbone.js** legacy projects.
|
|
36
37
|
- **Standards-Based** 📐
|
|
37
38
|
Built on modern web standards, no tooling required.
|
|
39
|
+
- **TypeScript Support** 🧩
|
|
40
|
+
Ships with type definitions for strict typing of models, views, components, props, and events.
|
|
38
41
|
|
|
39
42
|
## Getting Started
|
|
40
43
|
|
|
@@ -179,28 +182,147 @@ Counter.mount({ model }, document.body);
|
|
|
179
182
|
- **Lightweight and Efficient**
|
|
180
183
|
Minimal footprint with optimized performance, ensuring smooth updates.
|
|
181
184
|
- **Just the Right Abstraction**
|
|
182
|
-
Keeps you close to the DOM with no over-engineering. Fully hackable
|
|
185
|
+
Keeps you close to the DOM with no over-engineering. Fully hackable — if you're curious about how something works, just check the source code.
|
|
186
|
+
|
|
187
|
+
## Scaffolding a New Project
|
|
188
|
+
|
|
189
|
+
The fastest way to start a real-world **Rasti** project is [`create-rasti`](https://github.com/8tentaculos/create-rasti), the official scaffolding tool. It generates a ready-to-use **Rasti** + **Vite** setup with optional server-side rendering, routing, styling, and icon components.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Interactive setup
|
|
193
|
+
npm create rasti
|
|
194
|
+
|
|
195
|
+
# Non-interactive single-page app
|
|
196
|
+
npm create rasti my-app
|
|
197
|
+
|
|
198
|
+
# Server-side rendering with routing and Tailwind CSS
|
|
199
|
+
npm create rasti my-app --ssr --router --tailwind
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Available options include:
|
|
203
|
+
|
|
204
|
+
- **Rendering** — Single-page app (default), server-side rendering (`--ssr`), or static pre-rendering (`--static`).
|
|
205
|
+
- **Styling** — Plain CSS (default), Tailwind CSS (`--tailwind`), or CSSFUN with light/dark theme support (`--cssfun`).
|
|
206
|
+
- **Routing** (`--router`) — A small universal router built on `path-to-regexp`.
|
|
207
|
+
- **Icons** (`--icons`) — Generate **Rasti** components from popular SVG icon sets (heroicons, akar-icons, feathericon, pixelarticons, and more).
|
|
208
|
+
|
|
209
|
+
See the [`create-rasti` repository](https://github.com/8tentaculos/create-rasti) for the full list of templates and options.
|
|
183
210
|
|
|
184
211
|
## Example
|
|
185
212
|
|
|
186
|
-
|
|
213
|
+
To see how **Rasti**'s API and architecture come together in a small app, explore the sample **TODO application** in the [example folder](https://github.com/8tentaculos/rasti/tree/master/example/todo) of the **Rasti** [GitHub repository](https://github.com/8tentaculos/rasti). It's a concise, self-contained reference for understanding how models, views, and components fit together in a simple application. Try it live [here](https://rasti.js.org/example/todo/index.html).
|
|
214
|
+
|
|
215
|
+
To scaffold a real-world project, use [`create-rasti`](#scaffolding-a-new-project).
|
|
187
216
|
|
|
188
217
|
## API Documentation
|
|
189
218
|
|
|
190
219
|
For detailed information on how to use **Rasti**, refer to the [API documentation](/docs/api.md).
|
|
191
220
|
|
|
221
|
+
## TypeScript
|
|
222
|
+
|
|
223
|
+
**Rasti** ships with TypeScript declarations out of the box. The types are bundled in the package and resolved automatically.
|
|
224
|
+
|
|
225
|
+
### Components
|
|
226
|
+
|
|
227
|
+
Pass generics explicitly to type the resulting class:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const Header = Component.create<{ handleAddTodo: (title: string) => void }>`
|
|
231
|
+
<header>...</header>
|
|
232
|
+
`;
|
|
233
|
+
|
|
234
|
+
new Header({ handleAddTodo: (t) => console.log(t) }); // ✅
|
|
235
|
+
|
|
236
|
+
// With a typed model:
|
|
237
|
+
const App = Component.create<{}, any, AppModel>`<main>...</main>`;
|
|
238
|
+
App.mount({ model: new AppModel() }, document.body);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Without generics, `Component.create` stays permissive (parity with JS):
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const Plain = Component.create`<div></div>`;
|
|
245
|
+
new Plain({ anything: 'goes' }); // ✅
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`Component.extend` adds the object members to the instance type. Inside its methods, `this` is the extended component, and lifecycle overrides get their parameters typed automatically:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
const Counter = Component.create<{ initial: number }>`<div>...</div>`.extend({
|
|
252
|
+
onCreate() {
|
|
253
|
+
this.state = new Model({ count: this.props.initial }); // `this` is typed
|
|
254
|
+
},
|
|
255
|
+
onChange(model, changed) { // parameters typed automatically
|
|
256
|
+
if ('count' in changed) this.render();
|
|
257
|
+
},
|
|
258
|
+
increment() { this.state.count++; },
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
Counter.mount({ initial: 0 }, document.body).increment(); // ✅ increment is typed
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Models
|
|
265
|
+
|
|
266
|
+
Type the attributes with `Model<Attrs>`. Use **declaration merging** to surface the auto-generated getters/setters as instance properties:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { Model } from 'rasti';
|
|
270
|
+
|
|
271
|
+
interface TodoAttrs { title: string; completed: boolean; }
|
|
272
|
+
|
|
273
|
+
class Todo extends Model<TodoAttrs> {
|
|
274
|
+
preinitialize() {
|
|
275
|
+
this.defaults = { title: '', completed: false };
|
|
276
|
+
}
|
|
277
|
+
toggle() { this.completed = !this.completed; }
|
|
278
|
+
}
|
|
279
|
+
interface Todo extends TodoAttrs {} // Exposes this.title, this.completed
|
|
280
|
+
|
|
281
|
+
const t = new Todo({ title: 'x' });
|
|
282
|
+
t.title.toUpperCase(); // ✅
|
|
283
|
+
t.on('change:completed', (m, value) => value && /* boolean */ console.log('done'));
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Helper types
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
import {
|
|
290
|
+
EventHandler,
|
|
291
|
+
RenderExpression,
|
|
292
|
+
Attrs,
|
|
293
|
+
Props,
|
|
294
|
+
State,
|
|
295
|
+
ComponentModel,
|
|
296
|
+
} from 'rasti';
|
|
297
|
+
|
|
298
|
+
// Typed event handler with `this` bound to the component
|
|
299
|
+
const onClick: EventHandler<Counter, MouseEvent> = function(ev) {
|
|
300
|
+
this.props.label;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// Typed render expression (`(component) => any`)
|
|
304
|
+
const renderLabel: RenderExpression<Counter> = ({ props }) => props.label;
|
|
305
|
+
|
|
306
|
+
// Extract types from existing classes
|
|
307
|
+
type T = Attrs<Todo>; // TodoAttrs
|
|
308
|
+
type P = Props<Counter>; // CounterProps
|
|
309
|
+
type S = State<Counter>; // CounterState
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Known limitations
|
|
313
|
+
|
|
314
|
+
- **Template interpolation callbacks are `any`**. Functions inside `Component.create\`...\`` template literals (`${({ model }) => ...}`, `onClick=${function() { this.x }}`) cannot be inferred from the surrounding template. To type them, annotate explicitly: `function(this: MyComponent, ev) { ... }` or `({ model }: MyComponent) => ...`.
|
|
315
|
+
- **`Model<A>` instance keys require declaration merging**. TypeScript can't add `A`'s keys to a `class extends Model<A>` automatically — see the `interface Todo extends TodoAttrs {}` pattern above.
|
|
316
|
+
- **`this.$()` can return `null`**. It mirrors `querySelector`, so handle the empty case (`?.`) and pass a type argument to narrow the element: `this.$<HTMLInputElement>('input.edit')?.focus()`. `this.$$()` returns a `NodeListOf<HTMLElement>` (also narrowable).
|
|
317
|
+
- **`this.model` / `this.state` are optional**. Both are `undefined` unless provided, so guard (`this.model?.foo`) or assert (`this.model!`) when you know one was passed. Both accept a Rasti `Model` or a model from another library (e.g. Backbone); Components subscribe to `change` events automatically when the object exposes `on`/`off`.
|
|
318
|
+
|
|
192
319
|
## Working with LLMs
|
|
193
320
|
|
|
194
321
|
For those working with LLMs, there is an [AI Agents reference guide](/docs/AGENTS.md) that provides API patterns, lifecycle methods, and best practices, optimized for LLM context. You can share this guide with AI assistants to help them understand **Rasti**'s architecture and component APIs.
|
|
195
322
|
|
|
196
|
-
##
|
|
323
|
+
## Changelog
|
|
197
324
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
- **[v4.0.0](https://github.com/8tentaculos/rasti/releases/tag/v4.0.0)**
|
|
201
|
-
- **[v3.0.0](https://github.com/8tentaculos/rasti/releases/tag/v3.0.0)**
|
|
202
|
-
- **[v2.0.0](https://github.com/8tentaculos/rasti/releases/tag/v2.0.0)**
|
|
203
|
-
- **[v1.0.0](https://github.com/8tentaculos/rasti/releases/tag/v1.0.0)**
|
|
325
|
+
Release history and migration notes for major versions are in [CHANGELOG.md](CHANGELOG.md).
|
|
204
326
|
|
|
205
327
|
## License
|
|
206
328
|
|
|
@@ -209,4 +331,3 @@ We strive to minimize breaking changes between major versions. However, if you'r
|
|
|
209
331
|
## Contributing
|
|
210
332
|
|
|
211
333
|
Contributions are welcome! Share feature ideas or report bugs on our [GitHub Issues page](https://github.com/8tentaculos/rasti/issues).
|
|
212
|
-
|
package/dist/rasti.js
CHANGED
|
@@ -419,7 +419,6 @@
|
|
|
419
419
|
* @param {...*} [args] Additional arguments passed to `preinitialize` and `parse` methods
|
|
420
420
|
* @property {object|Function} defaults Default attributes for the model. If a function, it's called bound to the model instance to get defaults.
|
|
421
421
|
* @property {object} previous Object containing previous attributes when a change occurs.
|
|
422
|
-
* @property {string} attributePrefix Static property that defines a prefix for generated getters/setters. Defaults to empty string.
|
|
423
422
|
* @example
|
|
424
423
|
* import { Model } from 'rasti';
|
|
425
424
|
*
|
|
@@ -733,6 +732,9 @@
|
|
|
733
732
|
* Static property that defines a prefix for generated getters/setters.
|
|
734
733
|
* When set, all attribute properties will be prefixed (e.g., 'attr_name' instead of 'name').
|
|
735
734
|
* Useful for avoiding naming conflicts or creating a consistent property naming convention.
|
|
735
|
+
* @static
|
|
736
|
+
* @memberof module:Model
|
|
737
|
+
* @name attributePrefix
|
|
736
738
|
* @type {string}
|
|
737
739
|
* @default ''
|
|
738
740
|
* @example
|
|
@@ -778,7 +780,7 @@
|
|
|
778
780
|
* @property {object|Function} events Object in the format `{'event selector' : 'listener'}`. It will be used to bind delegated event listeners to the root element. If it is a function, it will be called to get the events object, bound to the view instance. See {@link module_view_delegateevents View.delegateEvents}.
|
|
779
781
|
* @property {object} model A model or any object containing data and business logic.
|
|
780
782
|
* @property {Function} template A function that returns a string with the view's inner HTML. See {@link module_view__render View.render}.
|
|
781
|
-
* @property {
|
|
783
|
+
* @property {string} uid Unique identifier for the view instance. This can be used to generate unique IDs for elements within the view. It is automatically generated and should not be set manually.
|
|
782
784
|
* @example
|
|
783
785
|
* import { View, Model } from 'rasti';
|
|
784
786
|
*
|
|
@@ -1163,6 +1165,8 @@
|
|
|
1163
1165
|
* For server-side rendering, this counter should be reset to `0` on every request to ensure that the generated
|
|
1164
1166
|
* unique IDs match those on the client, enabling seamless hydration of components.
|
|
1165
1167
|
* @static
|
|
1168
|
+
* @memberof module:View
|
|
1169
|
+
* @name uid
|
|
1166
1170
|
* @type {number}
|
|
1167
1171
|
* @default 0
|
|
1168
1172
|
*/
|
|
@@ -2803,7 +2807,7 @@
|
|
|
2803
2807
|
// In this case, where the component updates, it handles children recycling.
|
|
2804
2808
|
const addChild = (component) => {
|
|
2805
2809
|
let out = component;
|
|
2806
|
-
let found
|
|
2810
|
+
let found;
|
|
2807
2811
|
// Check if child already exists by key.
|
|
2808
2812
|
if (component.key) {
|
|
2809
2813
|
found = previousChildren.find(prev => prev.key === component.key);
|