rasti 4.1.0-alpha.0 → 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 +47 -10
- package/package.json +1 -1
- package/types/Component.d.ts +55 -7
- package/types/View.d.ts +11 -3
- package/types/index.d.ts +3 -1
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.1.0-alpha.
|
|
4
|
-
<img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.1.0-alpha.
|
|
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
|
|
|
@@ -184,9 +184,35 @@ Counter.mount({ model }, document.body);
|
|
|
184
184
|
- **Just the Right Abstraction**
|
|
185
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
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.
|
|
210
|
+
|
|
187
211
|
## Example
|
|
188
212
|
|
|
189
|
-
|
|
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).
|
|
190
216
|
|
|
191
217
|
## API Documentation
|
|
192
218
|
|
|
@@ -219,6 +245,22 @@ const Plain = Component.create`<div></div>`;
|
|
|
219
245
|
new Plain({ anything: 'goes' }); // ✅
|
|
220
246
|
```
|
|
221
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
|
+
|
|
222
264
|
### Models
|
|
223
265
|
|
|
224
266
|
Type the attributes with `Model<Attrs>`. Use **declaration merging** to surface the auto-generated getters/setters as instance properties:
|
|
@@ -278,14 +320,9 @@ type S = State<Counter>; // CounterState
|
|
|
278
320
|
|
|
279
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.
|
|
280
322
|
|
|
281
|
-
##
|
|
282
|
-
|
|
283
|
-
We strive to minimize breaking changes between major versions. However, if you're migrating between major versions, please refer to the release notes below for details on any breaking changes and migration tips.
|
|
323
|
+
## Changelog
|
|
284
324
|
|
|
285
|
-
|
|
286
|
-
- **[v3.0.0](https://github.com/8tentaculos/rasti/releases/tag/v3.0.0)**
|
|
287
|
-
- **[v2.0.0](https://github.com/8tentaculos/rasti/releases/tag/v2.0.0)**
|
|
288
|
-
- **[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).
|
|
289
326
|
|
|
290
327
|
## License
|
|
291
328
|
|
package/package.json
CHANGED
package/types/Component.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ export interface SafeHTML {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** A partial template produced by `this.partial` — preserves structure for position-based recycling. */
|
|
41
|
-
export interface
|
|
41
|
+
export interface ComponentPartial {
|
|
42
42
|
strings: TemplateStringsArray;
|
|
43
43
|
expressions: any[];
|
|
44
44
|
}
|
|
@@ -75,7 +75,40 @@ export type Props<C> = C extends Component<infer P, any, any> ? P : never;
|
|
|
75
75
|
/** Extracts the state type `S` from a Component subclass. */
|
|
76
76
|
export type State<C> = C extends Component<any, infer S, any> ? S : never;
|
|
77
77
|
/** Extracts the model type `M` from a Component / View subclass. */
|
|
78
|
-
export type ComponentModel<C> =
|
|
78
|
+
export type ComponentModel<C> =
|
|
79
|
+
C extends Component<any, any, infer M> ? M :
|
|
80
|
+
C extends View<infer M> ? M :
|
|
81
|
+
never;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Lifecycle methods, with their real signatures, made available for contextual typing
|
|
85
|
+
* inside `Component.extend({ ... })` so overrides don't need parameter annotations.
|
|
86
|
+
*/
|
|
87
|
+
export interface ComponentLifecycle {
|
|
88
|
+
/** Lifecycle. Called at the end of the constructor. Runs on both client and server. */
|
|
89
|
+
onCreate?(...args: any[]): void;
|
|
90
|
+
/** Lifecycle. Called when model/state/props emit `change`. Default: triggers `render`. */
|
|
91
|
+
onChange?(model: object, changed: Record<string, any>, ...args: any[]): void;
|
|
92
|
+
/** Lifecycle. Called after the first render hydrates the DOM. Client only. */
|
|
93
|
+
onHydrate?(): void;
|
|
94
|
+
/** Lifecycle. Called at the start of `recycle`, before any recycling happens. */
|
|
95
|
+
onBeforeRecycle?(): void;
|
|
96
|
+
/** Lifecycle. Called when the component is recycled and its props are updated. */
|
|
97
|
+
onRecycle?(): void;
|
|
98
|
+
/** Lifecycle. Called at the start of `render` on update. */
|
|
99
|
+
onBeforeUpdate?(): void;
|
|
100
|
+
/** Lifecycle. Called at the end of `render` on update. */
|
|
101
|
+
onUpdate?(): void;
|
|
102
|
+
/** Lifecycle. Called when the component is destroyed. */
|
|
103
|
+
onDestroy?(...args: any[]): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Class returned by `Component.extend`: keeps the statics and constructor signature of the
|
|
108
|
+
* parent class `T`, and adds the members of `O` to the instance type.
|
|
109
|
+
*/
|
|
110
|
+
export type ExtendedComponent<T extends new (...args: any[]) => any, O> =
|
|
111
|
+
Omit<T, never> & (new (...args: ConstructorParameters<T>) => InstanceType<T> & O);
|
|
79
112
|
|
|
80
113
|
/**
|
|
81
114
|
* Components are a special kind of `View` designed to be easily composable. Unlike views,
|
|
@@ -104,14 +137,23 @@ declare class Component<P = {}, S = any, M = any> extends View<M> {
|
|
|
104
137
|
|
|
105
138
|
/**
|
|
106
139
|
* Helper method used to extend a `Component`, creating a subclass.
|
|
140
|
+
*
|
|
141
|
+
* The members of `object` are added to the resulting instance type, and `this` inside
|
|
142
|
+
* its methods is typed as the extended component. Lifecycle overrides (`onCreate`,
|
|
143
|
+
* `onChange`, ...) get their parameters typed automatically.
|
|
144
|
+
*
|
|
107
145
|
* @param object Object with methods to add to the subclass, or a function that receives
|
|
108
146
|
* the parent prototype and returns such an object.
|
|
109
147
|
* @return The newly created Component subclass.
|
|
110
148
|
*/
|
|
111
|
-
static extend<T extends new (...args: any[]) => Component<any, any, any
|
|
149
|
+
static extend<T extends new (...args: any[]) => Component<any, any, any>, O extends object>(
|
|
150
|
+
this: T,
|
|
151
|
+
object: (proto: InstanceType<T>) => O & ThisType<InstanceType<T> & O>,
|
|
152
|
+
): ExtendedComponent<T, O>;
|
|
153
|
+
static extend<T extends new (...args: any[]) => Component<any, any, any>, O extends object>(
|
|
112
154
|
this: T,
|
|
113
|
-
object:
|
|
114
|
-
): T
|
|
155
|
+
object: O & ComponentLifecycle & ThisType<InstanceType<T> & O>,
|
|
156
|
+
): ExtendedComponent<T, O>;
|
|
115
157
|
|
|
116
158
|
/**
|
|
117
159
|
* Mount the component into the DOM.
|
|
@@ -157,6 +199,12 @@ declare class Component<P = {}, S = any, M = any> extends View<M> {
|
|
|
157
199
|
...expressions: any[]
|
|
158
200
|
): typeof Component<P, S, M>;
|
|
159
201
|
|
|
202
|
+
/**
|
|
203
|
+
* A unique key to identify the component, merged from options.
|
|
204
|
+
* Components with keys are recycled when the same key is found in the previous render.
|
|
205
|
+
*/
|
|
206
|
+
key?: string;
|
|
207
|
+
|
|
160
208
|
/**
|
|
161
209
|
* Props passed from the parent component, stored as a `Model` for reactive updates.
|
|
162
210
|
* Accessible directly (`this.props.foo`) or via `Model` API (`this.props.get('foo')`).
|
|
@@ -184,7 +232,7 @@ declare class Component<P = {}, S = any, M = any> extends View<M> {
|
|
|
184
232
|
|
|
185
233
|
/**
|
|
186
234
|
* Tagged template helper bound to the component instance.
|
|
187
|
-
* Returns a `
|
|
235
|
+
* Returns a `ComponentPartial` that preserves structure for position-based recycling.
|
|
188
236
|
* String literals are marked as safe HTML automatically.
|
|
189
237
|
*
|
|
190
238
|
* @example
|
|
@@ -192,7 +240,7 @@ declare class Component<P = {}, S = any, M = any> extends View<M> {
|
|
|
192
240
|
* return this.partial`<header><${Title}>${this.model.title}</${Title}></header>`;
|
|
193
241
|
* }
|
|
194
242
|
*/
|
|
195
|
-
partial(strings: TemplateStringsArray, ...expressions: any[]):
|
|
243
|
+
partial(strings: TemplateStringsArray, ...expressions: any[]): ComponentPartial;
|
|
196
244
|
|
|
197
245
|
/**
|
|
198
246
|
* Subscribes to a `change` event on a model or emitter and invokes `onChange`.
|
package/types/View.d.ts
CHANGED
|
@@ -37,6 +37,14 @@ export interface ViewOptions<M = any> {
|
|
|
37
37
|
* }
|
|
38
38
|
*/
|
|
39
39
|
export default class View<M = any> extends Emitter {
|
|
40
|
+
/**
|
|
41
|
+
* Counter for generating unique IDs for view instances.
|
|
42
|
+
* For server-side rendering, reset it to `0` on every request (see `resetUid`) so the
|
|
43
|
+
* generated IDs match those on the client, enabling seamless hydration of components.
|
|
44
|
+
* @default 0
|
|
45
|
+
*/
|
|
46
|
+
static uid: number;
|
|
47
|
+
|
|
40
48
|
/**
|
|
41
49
|
* Escape HTML entities in a string.
|
|
42
50
|
* Use this method to sanitize user-generated content before inserting it into the DOM.
|
|
@@ -74,8 +82,8 @@ export default class View<M = any> extends Emitter {
|
|
|
74
82
|
/** Child views. Destroyed automatically when the parent is destroyed. */
|
|
75
83
|
children: View[];
|
|
76
84
|
|
|
77
|
-
/** Whether `destroy()` has been called on this view. */
|
|
78
|
-
destroyed
|
|
85
|
+
/** Whether `destroy()` has been called on this view. `undefined` until then. */
|
|
86
|
+
destroyed?: boolean;
|
|
79
87
|
|
|
80
88
|
/**
|
|
81
89
|
* Functions run on `destroy()`. Push cleanup callbacks here for external subscriptions
|
|
@@ -124,7 +132,7 @@ export default class View<M = any> extends Emitter {
|
|
|
124
132
|
* Add a view as a child. Children are stored in `this.children` and destroyed when the parent is destroyed.
|
|
125
133
|
* @return The child view for chaining.
|
|
126
134
|
*/
|
|
127
|
-
addChild(child:
|
|
135
|
+
addChild<C extends View>(child: C): C;
|
|
128
136
|
|
|
129
137
|
/** Call `destroy()` on children views. */
|
|
130
138
|
destroyChildren(): void;
|
package/types/index.d.ts
CHANGED