@vidact/runtime 0.2.0-beta.6 → 0.2.0-beta.8
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/CHANGELOG.md +13 -0
- package/dist/actions.js +1 -1
- package/dist/compiled/core.d.ts +17 -5
- package/dist/compiled/core.d.ts.map +1 -1
- package/dist/compiled/core.js +545 -155
- package/dist/compiled/core.js.map +1 -1
- package/dist/direct-dom.d.ts.map +1 -1
- package/dist/direct-dom.js +102 -11
- package/dist/direct-dom.js.map +1 -1
- package/dist/dom/events.d.ts +4 -1
- package/dist/dom/events.d.ts.map +1 -1
- package/dist/dom/events.js +66 -1
- package/dist/dom/events.js.map +1 -1
- package/dist/dom/namespace.js +1 -1
- package/dist/hydration-bridge.d.ts +4 -4
- package/dist/hydration-bridge.d.ts.map +1 -1
- package/dist/hydration-bridge.js.map +1 -1
- package/dist/hydration.d.ts +2 -2
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/keyed-list.d.ts +6 -2
- package/dist/keyed-list.d.ts.map +1 -1
- package/dist/keyed-list.js +122 -11
- package/dist/keyed-list.js.map +1 -1
- package/dist/metadata.js +1 -1
- package/dist/renderable.d.ts +2 -1
- package/dist/renderable.d.ts.map +1 -1
- package/dist/renderable.js +12 -1
- package/dist/renderable.js.map +1 -1
- package/dist/source-mask.d.ts +2 -1
- package/dist/source-mask.d.ts.map +1 -1
- package/dist/source-mask.js +16 -1
- package/dist/source-mask.js.map +1 -1
- package/dist/spread.js +2 -2
- package/dist/state-slot.d.ts +12 -1
- package/dist/state-slot.d.ts.map +1 -1
- package/dist/state-slot.js +54 -24
- package/dist/state-slot.js.map +1 -1
- package/package.json +3 -3
- package/src/compiled/core.ts +785 -179
- package/src/direct-dom.ts +136 -10
- package/src/dom/events.ts +86 -0
- package/src/hydration-bridge.ts +10 -4
- package/src/hydration.ts +5 -3
- package/src/index.ts +2 -0
- package/src/keyed-list.ts +168 -22
- package/src/renderable.ts +30 -0
- package/src/source-mask.ts +20 -0
- package/src/state-slot.ts +83 -27
package/src/renderable.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { binding, isCompiledBinding } from './compiled/core.ts'
|
|
2
2
|
import type { CompiledBinding, CompiledRenderValue, CompiledScope } from './compiled/types.ts'
|
|
3
|
+
import { compiledComponentSpread } from './component-spread.ts'
|
|
3
4
|
import { Fragment, h, type DirectComponent } from './direct-dom.ts'
|
|
4
5
|
import {
|
|
5
6
|
installRenderableClone,
|
|
@@ -48,6 +49,35 @@ export function createRenderable(
|
|
|
48
49
|
return capability
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
export function createComponentRenderable(
|
|
53
|
+
type: DirectComponent,
|
|
54
|
+
input: RenderablePropsInput,
|
|
55
|
+
identity: unknown = type,
|
|
56
|
+
): CompiledRenderable {
|
|
57
|
+
return createRenderable(
|
|
58
|
+
input,
|
|
59
|
+
(currentInput) =>
|
|
60
|
+
h(
|
|
61
|
+
type,
|
|
62
|
+
{ ...renderableComponentProps(currentInput), ref: renderableRef(currentInput) },
|
|
63
|
+
renderableChildren(currentInput),
|
|
64
|
+
),
|
|
65
|
+
identity,
|
|
66
|
+
true,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function renderableComponentProps(input: RenderablePropsInput): Record<string, unknown> {
|
|
71
|
+
const ordinary = projectInput<Record<string, unknown>>(input, (props) =>
|
|
72
|
+
Object.fromEntries(
|
|
73
|
+
Reflect.ownKeys(props)
|
|
74
|
+
.filter((name) => typeof name !== 'string' || !SPECIAL_PROPS.has(name))
|
|
75
|
+
.map((name) => [name, Reflect.get(props, name)]),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
return isCompiledBinding(ordinary) ? compiledComponentSpread(ordinary, []) : ordinary
|
|
79
|
+
}
|
|
80
|
+
|
|
51
81
|
export function isRenderable(value: unknown): value is CompiledRenderable {
|
|
52
82
|
return isRenderableProtocol(value)
|
|
53
83
|
}
|
package/src/source-mask.ts
CHANGED
|
@@ -50,6 +50,17 @@ export function intersectsSources(left: SourceMask, right: SourceMask): boolean
|
|
|
50
50
|
return false
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
export function forEachSource(mask: SourceMask, visit: (index: number) => void): void {
|
|
54
|
+
if (typeof mask === 'number') {
|
|
55
|
+
visitWordSources(mask, 0, visit)
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (let word = 0; word < mask.length; word += 1) {
|
|
60
|
+
visitWordSources(mask[word] ?? 0, word * BITS_PER_WORD, visit)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
53
64
|
export function isEmptySources(mask: SourceMask): boolean {
|
|
54
65
|
if (typeof mask === 'number') return mask === 0
|
|
55
66
|
for (const word of mask) {
|
|
@@ -78,6 +89,15 @@ function wordAt(mask: SourceMask, index: number): number {
|
|
|
78
89
|
return mask[index] ?? 0
|
|
79
90
|
}
|
|
80
91
|
|
|
92
|
+
function visitWordSources(word: number, offset: number, visit: (index: number) => void): void {
|
|
93
|
+
let remaining = word >>> 0
|
|
94
|
+
while (remaining !== 0) {
|
|
95
|
+
const bit = 31 - Math.clz32(remaining)
|
|
96
|
+
visit(offset + bit)
|
|
97
|
+
remaining = (remaining & ~(1 << bit)) >>> 0
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
function wordLength(mask: SourceMask): number {
|
|
82
102
|
return typeof mask === 'number' ? 1 : mask.length
|
|
83
103
|
}
|
package/src/state-slot.ts
CHANGED
|
@@ -24,6 +24,59 @@ export interface StateSlot<T> {
|
|
|
24
24
|
readonly replace: (value: T) => void
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export interface ReadOnlyStateInvalidationTarget {
|
|
28
|
+
readonly [1]: (sources: SourceMask) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ReadOnlyStateCell<T> extends Pick<StateSlot<T>, 'get'> {
|
|
32
|
+
value: T
|
|
33
|
+
revision?: number
|
|
34
|
+
readonly invalidationTarget: ReadOnlyStateInvalidationTarget
|
|
35
|
+
readonly source: SourceMask
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createReadOnlyStateSlot<T>(
|
|
39
|
+
invalidationTarget: ReadOnlyStateInvalidationTarget,
|
|
40
|
+
source: SourceMask,
|
|
41
|
+
initialValue: T,
|
|
42
|
+
): ReadOnlyStateCell<T> {
|
|
43
|
+
return {
|
|
44
|
+
value: initialValue,
|
|
45
|
+
invalidationTarget,
|
|
46
|
+
source,
|
|
47
|
+
get: readReadOnlyState,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function readReadOnlyState<T>(this: ReadOnlyStateCell<T>): T {
|
|
52
|
+
return this.value
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function replaceReadOnlyStateSlot<T>(cell: ReadOnlyStateCell<T>, replacement: T): void {
|
|
56
|
+
if (Object.is(cell.value, replacement)) return
|
|
57
|
+
const revision = cell.revision ?? 0
|
|
58
|
+
if (
|
|
59
|
+
stateWriteInterceptor?.({
|
|
60
|
+
slot: cell,
|
|
61
|
+
revision,
|
|
62
|
+
update: replacement,
|
|
63
|
+
commit: (pending) => applyReadOnlyState(cell, pending as T),
|
|
64
|
+
currentRevision: () => cell.revision ?? 0,
|
|
65
|
+
})
|
|
66
|
+
) {
|
|
67
|
+
cell.revision ??= revision
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
applyReadOnlyState(cell, replacement)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function applyReadOnlyState<T>(cell: ReadOnlyStateCell<T>, next: T): void {
|
|
74
|
+
if (Object.is(cell.value, next)) return
|
|
75
|
+
cell.value = next
|
|
76
|
+
if (cell.revision !== undefined) cell.revision += 1
|
|
77
|
+
cell.invalidationTarget[1](cell.source)
|
|
78
|
+
}
|
|
79
|
+
|
|
27
80
|
export function createStateSlot<T>(
|
|
28
81
|
invalidate: (sources: SourceMask) => void,
|
|
29
82
|
source: SourceMask,
|
|
@@ -32,45 +85,48 @@ export function createStateSlot<T>(
|
|
|
32
85
|
): StateSlot<T> {
|
|
33
86
|
let value = initialValue
|
|
34
87
|
let revision = 0
|
|
35
|
-
|
|
88
|
+
let token: object | undefined
|
|
36
89
|
|
|
37
|
-
const
|
|
90
|
+
const apply = (next: T): void => {
|
|
38
91
|
if (Object.is(value, next)) return
|
|
39
92
|
|
|
40
93
|
value = next
|
|
41
94
|
revision += 1
|
|
42
95
|
invalidate(source)
|
|
43
96
|
}
|
|
44
|
-
const commitUpdate = (update: StateUpdate<T>): void => {
|
|
45
|
-
const next = typeof update === 'function' ? (update as (previous: T) => T)(value) : update
|
|
46
|
-
commit(next)
|
|
47
|
-
}
|
|
48
|
-
const commitReplacement = (replacement: StateUpdate<T>): void => commit(replacement as T)
|
|
49
|
-
const write = (update: StateUpdate<T>, commitWrite: (update: StateUpdate<T>) => void): void => {
|
|
50
|
-
if (
|
|
51
|
-
stateWriteInterceptor?.({
|
|
52
|
-
slot: token,
|
|
53
|
-
revision,
|
|
54
|
-
update,
|
|
55
|
-
commit: commitWrite,
|
|
56
|
-
currentRevision: () => revision,
|
|
57
|
-
})
|
|
58
|
-
) {
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
commitWrite(update)
|
|
62
|
-
}
|
|
63
|
-
const replace = (next: T): void => {
|
|
64
|
-
assertWritable?.()
|
|
65
|
-
write(next, commitReplacement)
|
|
66
|
-
}
|
|
67
97
|
|
|
68
98
|
return {
|
|
69
99
|
get: () => value,
|
|
70
100
|
set: (update) => {
|
|
71
101
|
assertWritable?.()
|
|
72
|
-
|
|
102
|
+
if (
|
|
103
|
+
stateWriteInterceptor?.({
|
|
104
|
+
slot: (token ??= {}),
|
|
105
|
+
revision,
|
|
106
|
+
update,
|
|
107
|
+
commit: (pending) =>
|
|
108
|
+
apply(typeof pending === 'function' ? (pending as (previous: T) => T)(value) : pending),
|
|
109
|
+
currentRevision: () => revision,
|
|
110
|
+
})
|
|
111
|
+
) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
apply(typeof update === 'function' ? (update as (previous: T) => T)(value) : update)
|
|
115
|
+
},
|
|
116
|
+
replace: (replacement) => {
|
|
117
|
+
assertWritable?.()
|
|
118
|
+
if (
|
|
119
|
+
stateWriteInterceptor?.({
|
|
120
|
+
slot: (token ??= {}),
|
|
121
|
+
revision,
|
|
122
|
+
update: replacement,
|
|
123
|
+
commit: (pending) => apply(pending as T),
|
|
124
|
+
currentRevision: () => revision,
|
|
125
|
+
})
|
|
126
|
+
) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
apply(replacement)
|
|
73
130
|
},
|
|
74
|
-
replace,
|
|
75
131
|
}
|
|
76
132
|
}
|