rask-ui 0.1.0
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 +263 -0
- package/dist/component.d.ts +21 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +128 -0
- package/dist/context.d.ts +5 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +29 -0
- package/dist/createAsync.d.ts +16 -0
- package/dist/createAsync.d.ts.map +1 -0
- package/dist/createAsync.js +24 -0
- package/dist/createAsyncState.d.ts +16 -0
- package/dist/createAsyncState.d.ts.map +1 -0
- package/dist/createAsyncState.js +24 -0
- package/dist/createContext.d.ts +5 -0
- package/dist/createContext.d.ts.map +1 -0
- package/dist/createContext.js +29 -0
- package/dist/createMutation.d.ts +20 -0
- package/dist/createMutation.d.ts.map +1 -0
- package/dist/createMutation.js +53 -0
- package/dist/createQuery.d.ts +19 -0
- package/dist/createQuery.d.ts.map +1 -0
- package/dist/createQuery.js +57 -0
- package/dist/createRef.d.ts +5 -0
- package/dist/createRef.d.ts.map +1 -0
- package/dist/createRef.js +7 -0
- package/dist/createState.d.ts +2 -0
- package/dist/createState.d.ts.map +1 -0
- package/dist/createState.js +52 -0
- package/dist/error.d.ts +5 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/jsx-dev-runtime.d.ts +4 -0
- package/dist/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx-dev-runtime.js +5 -0
- package/dist/jsx-runtime.d.ts +15 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +19 -0
- package/dist/jsx.d.ts +257 -0
- package/dist/jsx.d.ts.map +1 -0
- package/dist/jsx.js +42 -0
- package/dist/observation.d.ts +17 -0
- package/dist/observation.d.ts.map +1 -0
- package/dist/observation.js +50 -0
- package/dist/render.d.ts +7 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +65 -0
- package/dist/suspense.d.ts +25 -0
- package/dist/suspense.d.ts.map +1 -0
- package/dist/suspense.js +97 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# RASK
|
|
2
|
+
|
|
3
|
+
A lightweight reactive component library built on [Snabbdom](https://github.com/snabbdom/snabbdom) with MobX-inspired reactivity.
|
|
4
|
+
|
|
5
|
+
## Component and Element Lifecycle
|
|
6
|
+
|
|
7
|
+
### Overview
|
|
8
|
+
|
|
9
|
+
RASK uses a **host-based architecture** where each component creates a host DOM element (`<component>` tag with `display: contents`) and manages its own virtual DOM independently. Components track their direct children for efficient cleanup detection.
|
|
10
|
+
|
|
11
|
+
### Component Lifecycle Phases
|
|
12
|
+
|
|
13
|
+
#### 1. Component Creation
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
function MyComponent(props) {
|
|
17
|
+
// Setup Phase - runs ONCE per component instance
|
|
18
|
+
const state = createState({ count: 0 });
|
|
19
|
+
const ref = createRef();
|
|
20
|
+
|
|
21
|
+
onMount(() => console.log('Mounted!'));
|
|
22
|
+
onCleanup(() => console.log('Cleaning up!'));
|
|
23
|
+
|
|
24
|
+
// Return render function
|
|
25
|
+
return () => <div ref={ref}>{state.count}</div>;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**What happens:**
|
|
30
|
+
- `createComponentInstance()` creates a new component instance
|
|
31
|
+
- Component function executes (setup phase)
|
|
32
|
+
- `createState()` creates reactive state
|
|
33
|
+
- `onMount()` and `onCleanup()` register lifecycle callbacks
|
|
34
|
+
- Render function is stored on the instance
|
|
35
|
+
- Instance is wrapped with an observer for reactivity
|
|
36
|
+
|
|
37
|
+
#### 2. Initial Render
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
render(<MyComponent />, container)
|
|
41
|
+
↓
|
|
42
|
+
jsxToVNode() - Convert JSX to VNode
|
|
43
|
+
↓
|
|
44
|
+
renderComponent() - Create component instance
|
|
45
|
+
↓
|
|
46
|
+
observer(() => { /* render function */ })
|
|
47
|
+
↓
|
|
48
|
+
Initial render (hostElement = null)
|
|
49
|
+
↓
|
|
50
|
+
Store child VNodes for host element creation
|
|
51
|
+
↓
|
|
52
|
+
Create host <component> VNode with children
|
|
53
|
+
↓
|
|
54
|
+
patch() - Superfine creates DOM
|
|
55
|
+
↓
|
|
56
|
+
setupComponentInstances() - Walk VNode tree
|
|
57
|
+
↓
|
|
58
|
+
Set instance.hostElement from vnode.node
|
|
59
|
+
↓
|
|
60
|
+
Store instance on element.__componentInstance
|
|
61
|
+
↓
|
|
62
|
+
Find parent component via DOM traversal
|
|
63
|
+
↓
|
|
64
|
+
Add to parent.children Set
|
|
65
|
+
↓
|
|
66
|
+
Run onMount() callbacks
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Key steps:**
|
|
70
|
+
1. **JSX to VNode**: JSX is converted to Superfine VNodes
|
|
71
|
+
2. **Component Instance Creation**: Each component gets a unique instance
|
|
72
|
+
3. **Observer Setup**: Render function is wrapped to track state dependencies
|
|
73
|
+
4. **Initial Render**: Observer runs but hostElement is null, so child VNodes are stored
|
|
74
|
+
5. **Host Element Creation**: A `<component>` VNode is created with the children
|
|
75
|
+
6. **Superfine Patch**: Superfine creates actual DOM elements
|
|
76
|
+
7. **Instance Setup**: VNode tree is walked to set `hostElement` from `vnode.node`
|
|
77
|
+
8. **Parent-Child Tracking**: Each instance finds its parent and registers as a child
|
|
78
|
+
9. **Mount Callbacks**: `onMount()` callbacks run after DOM is ready
|
|
79
|
+
|
|
80
|
+
#### 3. State Changes and Re-renders
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
state.count++ (or props change)
|
|
84
|
+
↓
|
|
85
|
+
Observer notified (reactive dependency)
|
|
86
|
+
↓
|
|
87
|
+
observer() callback executes
|
|
88
|
+
↓
|
|
89
|
+
Render function executes
|
|
90
|
+
↓
|
|
91
|
+
jsx() - Convert new JSX to VNodes
|
|
92
|
+
↓
|
|
93
|
+
patch(hostNode, newVNode)
|
|
94
|
+
↓
|
|
95
|
+
Snabbdom updates DOM
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Key steps:**
|
|
99
|
+
1. **State Change**: Setting a state property notifies observers
|
|
100
|
+
2. **Observer Execution**: The component's observer callback runs
|
|
101
|
+
3. **Re-render**: Render function executes, accessing state (tracks dependencies)
|
|
102
|
+
4. **JSX to VNode**: New JSX is converted to VNodes
|
|
103
|
+
5. **Patch**: Snabbdom updates the host element's children
|
|
104
|
+
|
|
105
|
+
#### 4. Component Cleanup
|
|
106
|
+
|
|
107
|
+
**Cleanup happens in two scenarios:**
|
|
108
|
+
|
|
109
|
+
##### A. Parent Re-renders and Removes Child
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Parent re-renders
|
|
113
|
+
↓
|
|
114
|
+
patch(parentHost, newVNode)
|
|
115
|
+
↓
|
|
116
|
+
Snabbdom removes child <component> from DOM
|
|
117
|
+
↓
|
|
118
|
+
Destroy hook called
|
|
119
|
+
↓
|
|
120
|
+
Run cleanupCallbacks
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Key steps:**
|
|
124
|
+
1. **Detection**: Snabbdom's destroy hook is called when element is removed
|
|
125
|
+
2. **Callbacks**: `onCleanup()` callbacks execute (clear timers, subscriptions, etc.)
|
|
126
|
+
|
|
127
|
+
### Element Lifecycle (Regular DOM Elements)
|
|
128
|
+
|
|
129
|
+
Regular DOM elements (div, span, etc.) follow Snabbdom's patching lifecycle:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
JSX: <div>Hello</div>
|
|
133
|
+
↓
|
|
134
|
+
jsx() creates VNode
|
|
135
|
+
↓
|
|
136
|
+
patch() creates or updates DOM
|
|
137
|
+
↓
|
|
138
|
+
vnode.elm contains DOM element
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Key points:**
|
|
142
|
+
- Regular elements don't create component instances
|
|
143
|
+
- They're managed entirely by Snabbdom's patch algorithm
|
|
144
|
+
- No special cleanup needed (browser handles DOM removal)
|
|
145
|
+
|
|
146
|
+
### Parent-Child Relationship Tracking
|
|
147
|
+
|
|
148
|
+
Components track their parent via the component stack during initialization:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
componentStack.unshift(instance)
|
|
152
|
+
const render = component(instance.reactiveProps)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Key points:**
|
|
156
|
+
- Parent-child relationships tracked through component stack
|
|
157
|
+
- Used for context propagation
|
|
158
|
+
- Cleanup handled by Snabbdom's destroy hooks
|
|
159
|
+
|
|
160
|
+
### Complete Lifecycle Example
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
function Counter() {
|
|
164
|
+
// SETUP PHASE (once)
|
|
165
|
+
const state = createState({ count: 0 });
|
|
166
|
+
|
|
167
|
+
onMount(() => {
|
|
168
|
+
console.log('Counter mounted');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
onCleanup(() => {
|
|
172
|
+
console.log('Counter cleaning up');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// RENDER PHASE (every update)
|
|
176
|
+
return () => (
|
|
177
|
+
<div>
|
|
178
|
+
<p>Count: {state.count}</p>
|
|
179
|
+
<button onClick={() => state.count++}>+</button>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Mount
|
|
185
|
+
render(<Counter />, document.getElementById('app'));
|
|
186
|
+
// Logs: "Counter mounted"
|
|
187
|
+
|
|
188
|
+
// User clicks button
|
|
189
|
+
// → state.count++ triggers observer
|
|
190
|
+
// → Render function executes
|
|
191
|
+
// → DOM updates via patch()
|
|
192
|
+
|
|
193
|
+
// Component removed (parent re-renders without it)
|
|
194
|
+
// → Snabbdom destroy hook called
|
|
195
|
+
// → Logs: "Counter cleaning up"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Key Architectural Decisions
|
|
199
|
+
|
|
200
|
+
### 1. Host-Based Architecture
|
|
201
|
+
- Each component has a host `<component>` element (display: contents)
|
|
202
|
+
- Components render independently to their own host
|
|
203
|
+
- No parent-child instance relationships for rendering
|
|
204
|
+
- Isolation enables fine-grained updates
|
|
205
|
+
|
|
206
|
+
### 2. Lifecycle Management
|
|
207
|
+
- Snabbdom hooks manage component lifecycle
|
|
208
|
+
- Insert hook runs onMount callbacks
|
|
209
|
+
- Destroy hook runs onCleanup callbacks
|
|
210
|
+
- Parent-child tracking for context propagation
|
|
211
|
+
|
|
212
|
+
### 3. Observer Pattern for Reactivity
|
|
213
|
+
- Render functions wrapped with observer
|
|
214
|
+
- Automatically tracks state/props access
|
|
215
|
+
- Re-renders only when observed properties change
|
|
216
|
+
- No manual subscriptions needed
|
|
217
|
+
|
|
218
|
+
### 4. Context Traversal
|
|
219
|
+
- Context walks component tree via parent references
|
|
220
|
+
- Set during component initialization
|
|
221
|
+
- Natural hierarchical lookup
|
|
222
|
+
- Works with host element architecture
|
|
223
|
+
|
|
224
|
+
### 5. Hook-Based Cleanup
|
|
225
|
+
- Cleanup callbacks run via Snabbdom destroy hooks
|
|
226
|
+
- Synchronous and predictable
|
|
227
|
+
- No manual tracking required
|
|
228
|
+
- Integrated with virtual DOM lifecycle
|
|
229
|
+
|
|
230
|
+
### 6. Thunk-Based Components
|
|
231
|
+
|
|
232
|
+
Components use Snabbdom's thunk feature for optimization:
|
|
233
|
+
|
|
234
|
+
```tsx
|
|
235
|
+
const thunkNode = thunk("component", props.key, component, [props, children]);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**How it works:**
|
|
239
|
+
- Components wrapped as thunks with custom hooks
|
|
240
|
+
- Init hook: Creates component instance and runs setup
|
|
241
|
+
- Prepatch hook: Updates reactive props before render
|
|
242
|
+
- Postpatch hook: Syncs props after patch
|
|
243
|
+
- Insert hook: Runs onMount callbacks
|
|
244
|
+
- Destroy hook: Runs onCleanup callbacks
|
|
245
|
+
|
|
246
|
+
**Benefits:**
|
|
247
|
+
- Leverages Snabbdom's thunk optimization
|
|
248
|
+
- Props changes trigger reactive updates
|
|
249
|
+
- Lifecycle hooks integrated with virtual DOM
|
|
250
|
+
- Efficient component reconciliation
|
|
251
|
+
|
|
252
|
+
## API Reference
|
|
253
|
+
|
|
254
|
+
See main [README](../../README.md) for full API details:
|
|
255
|
+
- `createState(initialState)` - Create reactive state
|
|
256
|
+
- `onMount(callback)` - Register mount callback
|
|
257
|
+
- `onCleanup(callback)` - Register cleanup callback
|
|
258
|
+
- `createContext()` - Create context for data sharing
|
|
259
|
+
- `createAsync(promise)` - Handle async operations
|
|
260
|
+
- `createQuery(fetcher)` - Create query with refetch
|
|
261
|
+
- `createMutation(mutator)` - Create mutation handler
|
|
262
|
+
- `ErrorBoundary` - Error boundary component
|
|
263
|
+
- `render(jsx, container)` - Mount component to DOM
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type VNode } from "snabbdom";
|
|
2
|
+
import { Observer } from "./observation";
|
|
3
|
+
import { ChildNode } from "./render";
|
|
4
|
+
export type Component<P> = ((props: P) => () => VNode) | (() => () => VNode);
|
|
5
|
+
export type ComponentInstance = {
|
|
6
|
+
parent: ComponentInstance | null;
|
|
7
|
+
component: Component<any>;
|
|
8
|
+
contexts: Map<object, object> | null;
|
|
9
|
+
onMounts: Array<() => void>;
|
|
10
|
+
onCleanups: Array<() => void>;
|
|
11
|
+
hostNode?: VNode;
|
|
12
|
+
observer: Observer;
|
|
13
|
+
reactiveProps: object;
|
|
14
|
+
error: unknown;
|
|
15
|
+
notifyError(error: unknown): void;
|
|
16
|
+
};
|
|
17
|
+
export declare function getCurrentComponent(): ComponentInstance;
|
|
18
|
+
export declare function onMount(cb: () => void): void;
|
|
19
|
+
export declare function onCleanup(cb: () => void): void;
|
|
20
|
+
export declare function createComponent(component: Component<any>, props: Record<string, unknown>, children: ChildNode[] | ChildNode): import("snabbdom").Thunk;
|
|
21
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,KAAK,EAAkB,MAAM,UAAU,CAAC;AAE7D,OAAO,EAAsB,QAAQ,EAAU,MAAM,eAAe,CAAC;AACrE,OAAO,EAAc,SAAS,EAAE,MAAM,UAAU,CAAC;AAGjD,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;AAE7E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAIF,wBAAgB,mBAAmB,sBAElC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAQrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAQvC;AAwGD,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,4BAOlC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { thunk } from "snabbdom";
|
|
2
|
+
import { getCurrentObserver, Observer, Signal } from "./observation";
|
|
3
|
+
import { jsx, patch } from "./render";
|
|
4
|
+
import { createState } from "./createState";
|
|
5
|
+
const componentStack = [];
|
|
6
|
+
export function getCurrentComponent() {
|
|
7
|
+
return componentStack[0] || null;
|
|
8
|
+
}
|
|
9
|
+
export function onMount(cb) {
|
|
10
|
+
const current = componentStack[0];
|
|
11
|
+
if (!current) {
|
|
12
|
+
throw new Error("Only use onMount in component setup");
|
|
13
|
+
}
|
|
14
|
+
current.onMounts.push(cb);
|
|
15
|
+
}
|
|
16
|
+
export function onCleanup(cb) {
|
|
17
|
+
const current = componentStack[0];
|
|
18
|
+
if (!current) {
|
|
19
|
+
throw new Error("Only use onCleanup in component setup");
|
|
20
|
+
}
|
|
21
|
+
current.onCleanups.push(cb);
|
|
22
|
+
}
|
|
23
|
+
const hook = {
|
|
24
|
+
insert(vnode) {
|
|
25
|
+
componentStack.shift();
|
|
26
|
+
vnode.data.componentInstance.onMounts.forEach((cb) => cb());
|
|
27
|
+
},
|
|
28
|
+
destroy(vnode) {
|
|
29
|
+
componentStack.shift();
|
|
30
|
+
vnode.data.componentInstance.onCleanups.forEach((cb) => cb());
|
|
31
|
+
},
|
|
32
|
+
prepatch(oldVnode, thunk) {
|
|
33
|
+
copyToThunk(oldVnode, thunk);
|
|
34
|
+
componentStack.unshift(thunk.data.componentInstance);
|
|
35
|
+
},
|
|
36
|
+
postpatch(_, newNode) {
|
|
37
|
+
const componentInstance = newNode.data.componentInstance;
|
|
38
|
+
componentStack.shift();
|
|
39
|
+
const props = newNode.data.args[0];
|
|
40
|
+
const children = newNode.data.args[1];
|
|
41
|
+
for (const key in props) {
|
|
42
|
+
componentInstance.reactiveProps[key] = props[key];
|
|
43
|
+
}
|
|
44
|
+
componentInstance.reactiveProps.children = children;
|
|
45
|
+
},
|
|
46
|
+
init(thunk) {
|
|
47
|
+
const component = thunk.data.fn;
|
|
48
|
+
const args = thunk.data.args;
|
|
49
|
+
let errorSignal;
|
|
50
|
+
let error;
|
|
51
|
+
const executeRender = () => {
|
|
52
|
+
const stopObserving = instance.observer.observe();
|
|
53
|
+
let renderResult = null;
|
|
54
|
+
try {
|
|
55
|
+
renderResult = render();
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
instance.notifyError(error);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
stopObserving();
|
|
62
|
+
}
|
|
63
|
+
return jsx("component", {
|
|
64
|
+
hook: {
|
|
65
|
+
insert: hook.insert,
|
|
66
|
+
destroy: hook.destroy,
|
|
67
|
+
},
|
|
68
|
+
"data-name": component.name,
|
|
69
|
+
}, Array.isArray(renderResult) ? renderResult : [renderResult]);
|
|
70
|
+
};
|
|
71
|
+
const instance = {
|
|
72
|
+
parent: getCurrentComponent(),
|
|
73
|
+
component,
|
|
74
|
+
contexts: null,
|
|
75
|
+
onMounts: [],
|
|
76
|
+
onCleanups: [],
|
|
77
|
+
observer: new Observer(() => {
|
|
78
|
+
const renderResult = executeRender();
|
|
79
|
+
instance.hostNode = patch(instance.hostNode, renderResult);
|
|
80
|
+
}),
|
|
81
|
+
reactiveProps: createState({
|
|
82
|
+
...args[0],
|
|
83
|
+
children: args[1],
|
|
84
|
+
}),
|
|
85
|
+
get error() {
|
|
86
|
+
if (!errorSignal) {
|
|
87
|
+
errorSignal = new Signal();
|
|
88
|
+
}
|
|
89
|
+
const observer = getCurrentObserver();
|
|
90
|
+
if (observer) {
|
|
91
|
+
observer.subscribeSignal(errorSignal);
|
|
92
|
+
}
|
|
93
|
+
return error;
|
|
94
|
+
},
|
|
95
|
+
notifyError(childError) {
|
|
96
|
+
if (errorSignal) {
|
|
97
|
+
error = childError;
|
|
98
|
+
errorSignal.notify();
|
|
99
|
+
}
|
|
100
|
+
else if (instance.parent) {
|
|
101
|
+
instance.parent.notifyError(childError);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw childError;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
componentStack.unshift(instance);
|
|
109
|
+
const render = component(instance.reactiveProps);
|
|
110
|
+
const renderResult = executeRender();
|
|
111
|
+
renderResult.data.componentInstance = instance;
|
|
112
|
+
copyToThunk(renderResult, thunk);
|
|
113
|
+
instance.hostNode = thunk;
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
export function createComponent(component, props, children) {
|
|
117
|
+
const thunkNode = thunk("component", props.key, component, [props, children]);
|
|
118
|
+
Object.assign(thunkNode.data.hook, hook);
|
|
119
|
+
return thunkNode;
|
|
120
|
+
}
|
|
121
|
+
function copyToThunk(vnode, thunk) {
|
|
122
|
+
vnode.data.fn = thunk.data.fn;
|
|
123
|
+
vnode.data.args = thunk.data.args;
|
|
124
|
+
thunk.data = vnode.data;
|
|
125
|
+
thunk.children = vnode.children;
|
|
126
|
+
thunk.text = vnode.text;
|
|
127
|
+
thunk.elm = vnode.elm;
|
|
128
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM;eAE/B,CAAC;WAaL,CAAC;EAmBX"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getCurrentComponent } from "./component";
|
|
2
|
+
export function createContext() {
|
|
3
|
+
const context = {
|
|
4
|
+
set(value) {
|
|
5
|
+
const currentComponent = getCurrentComponent();
|
|
6
|
+
if (!currentComponent) {
|
|
7
|
+
throw new Error("You can not set context out component setup");
|
|
8
|
+
}
|
|
9
|
+
if (!currentComponent.contexts) {
|
|
10
|
+
currentComponent.contexts = new Map();
|
|
11
|
+
}
|
|
12
|
+
currentComponent.contexts.set(context, value);
|
|
13
|
+
},
|
|
14
|
+
get() {
|
|
15
|
+
let currentComponent = getCurrentComponent();
|
|
16
|
+
if (!currentComponent) {
|
|
17
|
+
throw new Error("You can not set context out component setup");
|
|
18
|
+
}
|
|
19
|
+
while (currentComponent) {
|
|
20
|
+
if (currentComponent.contexts?.has(context)) {
|
|
21
|
+
return currentComponent.contexts.get(context);
|
|
22
|
+
}
|
|
23
|
+
currentComponent = currentComponent.parent;
|
|
24
|
+
}
|
|
25
|
+
throw new Error("Could not find context in parent components");
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
return context;
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type AsyncState<T> = {
|
|
2
|
+
isPending: true;
|
|
3
|
+
value: null;
|
|
4
|
+
error: null;
|
|
5
|
+
} | {
|
|
6
|
+
isPending: false;
|
|
7
|
+
value: T;
|
|
8
|
+
error: null;
|
|
9
|
+
} | {
|
|
10
|
+
isPending: false;
|
|
11
|
+
value: null;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function createAsync<T>(promise: Promise<T>): AsyncState<T>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=createAsync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAsync.d.ts","sourceRoot":"","sources":["../src/createAsync.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,CAAC,CAAC,IACb;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iBAwBjD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createState } from "./createState";
|
|
2
|
+
export function createAsync(promise) {
|
|
3
|
+
const state = createState({
|
|
4
|
+
isPending: true,
|
|
5
|
+
error: null,
|
|
6
|
+
value: null,
|
|
7
|
+
});
|
|
8
|
+
promise
|
|
9
|
+
.then((value) => {
|
|
10
|
+
Object.assign(state, {
|
|
11
|
+
value,
|
|
12
|
+
error: null,
|
|
13
|
+
isPending: false,
|
|
14
|
+
});
|
|
15
|
+
})
|
|
16
|
+
.catch((error) => {
|
|
17
|
+
Object.assign(state, {
|
|
18
|
+
value: null,
|
|
19
|
+
error: String(error),
|
|
20
|
+
isPending: false,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type AsyncState<T> = {
|
|
2
|
+
isPending: true;
|
|
3
|
+
value: null;
|
|
4
|
+
error: null;
|
|
5
|
+
} | {
|
|
6
|
+
isPending: false;
|
|
7
|
+
value: T;
|
|
8
|
+
error: null;
|
|
9
|
+
} | {
|
|
10
|
+
isPending: false;
|
|
11
|
+
value: null;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function createAsyncState<T>(promise: Promise<T>): AsyncState<T>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=createAsyncState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAsyncState.d.ts","sourceRoot":"","sources":["../src/createAsyncState.ts"],"names":[],"mappings":"AAEA,KAAK,UAAU,CAAC,CAAC,IACb;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,iBAwBtD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createState } from "./createState";
|
|
2
|
+
export function createAsyncState(promise) {
|
|
3
|
+
const state = createState({
|
|
4
|
+
isPending: true,
|
|
5
|
+
error: null,
|
|
6
|
+
value: null,
|
|
7
|
+
});
|
|
8
|
+
promise
|
|
9
|
+
.then((value) => {
|
|
10
|
+
Object.assign(state, {
|
|
11
|
+
value,
|
|
12
|
+
error: null,
|
|
13
|
+
isPending: false,
|
|
14
|
+
});
|
|
15
|
+
})
|
|
16
|
+
.catch((error) => {
|
|
17
|
+
Object.assign(state, {
|
|
18
|
+
value: null,
|
|
19
|
+
error: String(error),
|
|
20
|
+
isPending: false,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
return state;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createContext.d.ts","sourceRoot":"","sources":["../src/createContext.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM;eAE/B,CAAC;WAaL,CAAC;EAmBX"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getCurrentComponent } from "./component";
|
|
2
|
+
export function createContext() {
|
|
3
|
+
const context = {
|
|
4
|
+
set(value) {
|
|
5
|
+
const currentComponent = getCurrentComponent();
|
|
6
|
+
if (!currentComponent) {
|
|
7
|
+
throw new Error("You can not set context out component setup");
|
|
8
|
+
}
|
|
9
|
+
if (!currentComponent.contexts) {
|
|
10
|
+
currentComponent.contexts = new Map();
|
|
11
|
+
}
|
|
12
|
+
currentComponent.contexts.set(context, value);
|
|
13
|
+
},
|
|
14
|
+
get() {
|
|
15
|
+
let currentComponent = getCurrentComponent();
|
|
16
|
+
if (!currentComponent) {
|
|
17
|
+
throw new Error("You can not set context out component setup");
|
|
18
|
+
}
|
|
19
|
+
while (currentComponent) {
|
|
20
|
+
if (currentComponent.contexts?.has(context)) {
|
|
21
|
+
return currentComponent.contexts.get(context);
|
|
22
|
+
}
|
|
23
|
+
currentComponent = currentComponent.parent;
|
|
24
|
+
}
|
|
25
|
+
throw new Error("Could not find context in parent components");
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
return context;
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type MutationState<T> = {
|
|
2
|
+
isPending: true;
|
|
3
|
+
params: T;
|
|
4
|
+
error: null;
|
|
5
|
+
} | {
|
|
6
|
+
isPending: false;
|
|
7
|
+
params: null;
|
|
8
|
+
error: null;
|
|
9
|
+
} | {
|
|
10
|
+
isPending: false;
|
|
11
|
+
params: null;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export type Mutation<T> = MutationState<T> & {
|
|
15
|
+
mutate(): void;
|
|
16
|
+
mutate(params: T): void;
|
|
17
|
+
};
|
|
18
|
+
export declare function createMutation<T>(mutator: (params: T) => Promise<T>): Mutation<T>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=createMutation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createMutation.d.ts","sourceRoot":"","sources":["../src/createMutation.ts"],"names":[],"mappings":"AAEA,KAAK,aAAa,CAAC,CAAC,IAChB;IACE,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,IAAI,CAAC;CACb,GACD;IACE,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IAC3C,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,wBAAgB,cAAc,CAAC,CAAC,EAC9B,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC,QAAQ,CAAC,CAAC,CAAC,CA0Db"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createState } from "./createState";
|
|
2
|
+
export function createMutation(mutator) {
|
|
3
|
+
const state = createState({
|
|
4
|
+
isPending: false,
|
|
5
|
+
params: null,
|
|
6
|
+
error: null,
|
|
7
|
+
});
|
|
8
|
+
const assign = (newState) => {
|
|
9
|
+
Object.assign(state, newState);
|
|
10
|
+
};
|
|
11
|
+
let currentAbortController;
|
|
12
|
+
return {
|
|
13
|
+
get isPending() {
|
|
14
|
+
return state.isPending;
|
|
15
|
+
},
|
|
16
|
+
get params() {
|
|
17
|
+
return state.params;
|
|
18
|
+
},
|
|
19
|
+
get error() {
|
|
20
|
+
return state.error;
|
|
21
|
+
},
|
|
22
|
+
mutate(params) {
|
|
23
|
+
currentAbortController?.abort();
|
|
24
|
+
const abortController = (currentAbortController = new AbortController());
|
|
25
|
+
assign({
|
|
26
|
+
isPending: true,
|
|
27
|
+
params,
|
|
28
|
+
error: null,
|
|
29
|
+
});
|
|
30
|
+
mutator(params)
|
|
31
|
+
.then(() => {
|
|
32
|
+
if (abortController.signal.aborted) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
assign({
|
|
36
|
+
isPending: false,
|
|
37
|
+
params: null,
|
|
38
|
+
error: null,
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
.catch((error) => {
|
|
42
|
+
if (abortController.signal.aborted) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
assign({
|
|
46
|
+
isPending: false,
|
|
47
|
+
params: null,
|
|
48
|
+
error: String(error),
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type QueryState<T> = {
|
|
2
|
+
isPending: true;
|
|
3
|
+
data: T | null;
|
|
4
|
+
error: null;
|
|
5
|
+
} | {
|
|
6
|
+
isPending: false;
|
|
7
|
+
data: T;
|
|
8
|
+
error: null;
|
|
9
|
+
} | {
|
|
10
|
+
isPending: false;
|
|
11
|
+
data: null;
|
|
12
|
+
error: string;
|
|
13
|
+
};
|
|
14
|
+
export type Query<T> = QueryState<T> & {
|
|
15
|
+
fetch(force?: boolean): void;
|
|
16
|
+
};
|
|
17
|
+
export declare function createQuery<T>(fetcher: () => Promise<T>): Query<T>;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=createQuery.d.ts.map
|