sprae 10.0.1 → 10.1.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/core.js +49 -66
- package/directive/each.js +7 -4
- package/directive/with.js +16 -8
- package/dist/sprae.js +67 -106
- package/dist/sprae.min.js +1 -1
- package/package.json +1 -1
- package/readme.md +9 -8
- package/sprae.js +0 -4
- package/store.js +7 -9
package/core.js
CHANGED
|
@@ -1,90 +1,73 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { use } from "./signal.js";
|
|
2
2
|
import store, { _signals } from './store.js';
|
|
3
3
|
|
|
4
4
|
// polyfill
|
|
5
5
|
const _dispose = (Symbol.dispose ||= Symbol("dispose"));
|
|
6
6
|
|
|
7
|
-
// mark
|
|
8
|
-
const SPRAE = `∴`
|
|
9
|
-
|
|
10
7
|
// reserved directives - order matters!
|
|
11
8
|
export const directive = {};
|
|
12
9
|
|
|
13
10
|
// sprae element: apply directives
|
|
14
11
|
const memo = new WeakMap();
|
|
15
12
|
|
|
16
|
-
export default function sprae(
|
|
17
|
-
|
|
13
|
+
export default function sprae(el, values) {
|
|
14
|
+
// text nodes, comments etc - but collections are fine
|
|
15
|
+
if (!el?.children) return
|
|
18
16
|
|
|
19
|
-
//
|
|
20
|
-
if (
|
|
17
|
+
// repeated call can be caused by :each with new objects with old keys needs an update
|
|
18
|
+
if (memo.has(el)) {
|
|
19
|
+
// we rewrite signals instead of update, because user should have what he provided
|
|
20
|
+
return Object.assign(memo.get(el), values)
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (el?.children) {
|
|
25
|
-
// repeated call can be caused by :each with new objects with old keys needs an update
|
|
26
|
-
if (memo.has(el)) {
|
|
27
|
-
// we rewrite signals instead of update, because user should have what he provided
|
|
28
|
-
Object.assign(memo.get(el), values)
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
// take over existing state instead of creating clone
|
|
32
|
-
state ||= store(values || {});
|
|
23
|
+
// take over existing state instead of creating clone
|
|
24
|
+
const state = store(values || {}), disposes = []
|
|
33
25
|
|
|
34
|
-
|
|
26
|
+
init(el);
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
// if element was spraed by :with or :each instruction - skip, otherwise save
|
|
29
|
+
if (!memo.has(el)) memo.set(el, state);
|
|
30
|
+
|
|
31
|
+
el[_dispose] = () => {
|
|
32
|
+
while (disposes.length) disposes.pop()();
|
|
33
|
+
memo.delete(el);
|
|
34
|
+
}
|
|
41
35
|
|
|
42
36
|
return state;
|
|
43
|
-
}
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
38
|
+
function init(el, parent = el.parentNode) {
|
|
39
|
+
if (el.attributes) {
|
|
40
|
+
// init generic-name attributes second
|
|
41
|
+
for (let i = 0; i < el.attributes.length;) {
|
|
42
|
+
let attr = el.attributes[i];
|
|
43
|
+
|
|
44
|
+
if (attr.name[0] === ':') {
|
|
45
|
+
el.removeAttribute(attr.name);
|
|
46
|
+
|
|
47
|
+
// multiple attributes like :id:for=""
|
|
48
|
+
let names = attr.name.slice(1).split(':')
|
|
49
|
+
|
|
50
|
+
// NOTE: secondary directives don't stop flow nor extend state, so no need to check
|
|
51
|
+
for (let name of names) {
|
|
52
|
+
let dir = directive[name] || directive.default
|
|
53
|
+
let evaluate = (dir.parse || parse)(attr.value, parse)
|
|
54
|
+
let dispose = dir(el, evaluate, state, name);
|
|
55
|
+
if (dispose) disposes.push(dispose);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// stop if element was spraed by internal directive
|
|
59
|
+
if (memo.has(el)) return disposes.push(el[_dispose]);
|
|
60
|
+
|
|
61
|
+
// stop if element is skipped (detached) like in case of :if or :each
|
|
62
|
+
if (el.parentNode !== parent) return;
|
|
63
|
+
} else i++;
|
|
64
|
+
}
|
|
72
65
|
}
|
|
73
|
-
}
|
|
74
66
|
|
|
75
|
-
|
|
67
|
+
for (let child of [...el.children]) init(child, el)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
76
70
|
|
|
77
|
-
// mark spraed element
|
|
78
|
-
el.classList?.add(SPRAE);
|
|
79
|
-
el[_dispose] = () => {
|
|
80
|
-
while (effects.length) effects.pop()();
|
|
81
|
-
el.classList.remove(SPRAE);
|
|
82
|
-
memo.delete(el);
|
|
83
|
-
// NOTE: each child disposes own children etc.
|
|
84
|
-
let els = el.getElementsByClassName(SPRAE);
|
|
85
|
-
while (els.length) els[0][_dispose]?.()
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
71
|
|
|
89
72
|
// compiler
|
|
90
73
|
const evalMemo = {};
|
package/directive/each.js
CHANGED
|
@@ -58,15 +58,18 @@ directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
|
58
58
|
[itemVar]: { get() { return cur[idx] } },
|
|
59
59
|
[idxVar]: { value: keys ? keys[idx] : idx },
|
|
60
60
|
}),
|
|
61
|
-
el = (tpl.content || tpl).cloneNode(true),
|
|
62
|
-
|
|
61
|
+
el = (tpl.content || tpl).cloneNode(true),
|
|
62
|
+
frag = tpl.content ?
|
|
63
|
+
// fake fragment to init sprae
|
|
64
|
+
{ children: [...el.children], remove() { this.children.map(el => el.remove()) } } :
|
|
65
|
+
el;
|
|
63
66
|
|
|
64
67
|
holder.before(el);
|
|
65
|
-
sprae(
|
|
68
|
+
sprae(frag, scope);
|
|
66
69
|
|
|
67
70
|
// signal/holder disposal removes element
|
|
68
71
|
((cur[_signals] ||= [])[i] ||= {})[Symbol.dispose] = () => {
|
|
69
|
-
|
|
72
|
+
frag[Symbol.dispose](), frag.remove()
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
}
|
package/directive/with.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import sprae, { directive } from "../core.js";
|
|
2
2
|
import store, { _signals } from '../store.js';
|
|
3
3
|
import { effect } from "../signal.js";
|
|
4
|
+
import { signal } from "ulive";
|
|
4
5
|
|
|
5
6
|
directive.with = (el, evaluate, rootState) => {
|
|
6
|
-
let state
|
|
7
|
+
let state
|
|
7
8
|
return effect(() => {
|
|
8
|
-
values = evaluate(rootState);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
)
|
|
14
|
-
|
|
9
|
+
let values = evaluate(rootState);
|
|
10
|
+
|
|
11
|
+
if (!state) {
|
|
12
|
+
state = store({});
|
|
13
|
+
// inherit root signals
|
|
14
|
+
Object.assign(state[_signals], rootState[_signals]);
|
|
15
|
+
// create local scope signals
|
|
16
|
+
for (let key in values) state[_signals][key] = null, state[key] = values[key]
|
|
17
|
+
|
|
18
|
+
sprae(el, state)
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
Object.assign(state, values)
|
|
22
|
+
}
|
|
15
23
|
})
|
|
16
24
|
};
|
package/dist/sprae.js
CHANGED
|
@@ -21,17 +21,16 @@ function use(s) {
|
|
|
21
21
|
// store.js
|
|
22
22
|
var _signals = Symbol("signals");
|
|
23
23
|
var _change = Symbol("length");
|
|
24
|
-
function store(values
|
|
24
|
+
function store(values) {
|
|
25
25
|
if (!values)
|
|
26
26
|
return values;
|
|
27
|
-
if (values[_signals]
|
|
27
|
+
if (values[_signals])
|
|
28
28
|
return values;
|
|
29
29
|
if (Array.isArray(values))
|
|
30
30
|
return list(values);
|
|
31
31
|
if (values.constructor !== Object)
|
|
32
32
|
return values;
|
|
33
|
-
let _len = signal(Object.values(values).length);
|
|
34
|
-
signals ||= {};
|
|
33
|
+
let signals = {}, _len = signal(Object.values(values).length);
|
|
35
34
|
const state = new Proxy(signals, {
|
|
36
35
|
get: (_, key) => key === _change ? _len : key === _signals ? signals : signals[key]?.valueOf(),
|
|
37
36
|
set: (_, key, v, s) => (s = signals[key], set(signals, key, v), s || ++_len.value),
|
|
@@ -41,19 +40,15 @@ function store(values, signals) {
|
|
|
41
40
|
return Reflect.ownKeys(signals);
|
|
42
41
|
}
|
|
43
42
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(signals[key] = computed(desc.get.bind(state)))._set = desc.set?.bind(state);
|
|
52
|
-
} else {
|
|
53
|
-
signals[key] = null;
|
|
54
|
-
set(signals, key, values[key]);
|
|
55
|
-
}
|
|
43
|
+
for (let key in values) {
|
|
44
|
+
const desc = Object.getOwnPropertyDescriptor(values, key);
|
|
45
|
+
if (desc?.get) {
|
|
46
|
+
(signals[key] = computed(desc.get.bind(state)))._set = desc.set?.bind(state);
|
|
47
|
+
} else {
|
|
48
|
+
signals[key] = null;
|
|
49
|
+
set(signals, key, values[key]);
|
|
56
50
|
}
|
|
51
|
+
}
|
|
57
52
|
return state;
|
|
58
53
|
}
|
|
59
54
|
function list(values) {
|
|
@@ -63,10 +58,8 @@ function list(values) {
|
|
|
63
58
|
let _len = signal(values.length), signals = Array(values.length).fill(null);
|
|
64
59
|
const state = new Proxy(signals, {
|
|
65
60
|
get(_, key) {
|
|
66
|
-
if (key ===
|
|
67
|
-
return _len;
|
|
68
|
-
if (key === _signals)
|
|
69
|
-
return signals;
|
|
61
|
+
if (typeof key === "symbol")
|
|
62
|
+
return key === _change ? _len : key === _signals ? signals : signals[key];
|
|
70
63
|
if (key === "length")
|
|
71
64
|
return Array.prototype[lastProp] ? _len.peek() : _len.value;
|
|
72
65
|
lastProp = key;
|
|
@@ -131,62 +124,50 @@ function del(signals, key) {
|
|
|
131
124
|
|
|
132
125
|
// core.js
|
|
133
126
|
var _dispose = Symbol.dispose ||= Symbol("dispose");
|
|
134
|
-
var SPRAE = `\u2234`;
|
|
135
127
|
var directive = {};
|
|
136
128
|
var memo = /* @__PURE__ */ new WeakMap();
|
|
137
|
-
function sprae(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (el?.children) {
|
|
143
|
-
if (memo.has(el)) {
|
|
144
|
-
Object.assign(memo.get(el), values);
|
|
145
|
-
} else {
|
|
146
|
-
state ||= store(values || {});
|
|
147
|
-
init(el, state);
|
|
148
|
-
if (!memo.has(el))
|
|
149
|
-
memo.set(el, state);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
;
|
|
154
|
-
return state;
|
|
155
|
-
}
|
|
156
|
-
function init(el, state, parent = el.parentNode, effects = []) {
|
|
157
|
-
if (el.attributes) {
|
|
158
|
-
for (let i = 0; i < el.attributes.length; ) {
|
|
159
|
-
let attr2 = el.attributes[i];
|
|
160
|
-
if (attr2.name[0] === ":") {
|
|
161
|
-
el.removeAttribute(attr2.name);
|
|
162
|
-
let names = attr2.name.slice(1).split(":");
|
|
163
|
-
for (let name of names) {
|
|
164
|
-
let dir = directive[name] || directive.default;
|
|
165
|
-
let evaluate = (dir.parse || parse)(attr2.value, parse);
|
|
166
|
-
let dispose = dir(el, evaluate, state, name);
|
|
167
|
-
if (dispose)
|
|
168
|
-
effects.push(dispose);
|
|
169
|
-
}
|
|
170
|
-
if (memo.has(el))
|
|
171
|
-
return;
|
|
172
|
-
if (el.parentNode !== parent)
|
|
173
|
-
return;
|
|
174
|
-
} else
|
|
175
|
-
i++;
|
|
176
|
-
}
|
|
129
|
+
function sprae(el, values) {
|
|
130
|
+
if (!el?.children)
|
|
131
|
+
return;
|
|
132
|
+
if (memo.has(el)) {
|
|
133
|
+
return Object.assign(memo.get(el), values);
|
|
177
134
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
135
|
+
const state = store(values || {}), disposes = [];
|
|
136
|
+
init(el);
|
|
137
|
+
if (!memo.has(el))
|
|
138
|
+
memo.set(el, state);
|
|
181
139
|
el[_dispose] = () => {
|
|
182
|
-
while (
|
|
183
|
-
|
|
184
|
-
el.classList.remove(SPRAE);
|
|
140
|
+
while (disposes.length)
|
|
141
|
+
disposes.pop()();
|
|
185
142
|
memo.delete(el);
|
|
186
|
-
let els = el.getElementsByClassName(SPRAE);
|
|
187
|
-
while (els.length)
|
|
188
|
-
els[0][_dispose]?.();
|
|
189
143
|
};
|
|
144
|
+
return state;
|
|
145
|
+
function init(el2, parent = el2.parentNode) {
|
|
146
|
+
if (el2.attributes) {
|
|
147
|
+
for (let i = 0; i < el2.attributes.length; ) {
|
|
148
|
+
let attr2 = el2.attributes[i];
|
|
149
|
+
if (attr2.name[0] === ":") {
|
|
150
|
+
el2.removeAttribute(attr2.name);
|
|
151
|
+
let names = attr2.name.slice(1).split(":");
|
|
152
|
+
for (let name of names) {
|
|
153
|
+
let dir = directive[name] || directive.default;
|
|
154
|
+
let evaluate = (dir.parse || parse)(attr2.value, parse);
|
|
155
|
+
let dispose = dir(el2, evaluate, state, name);
|
|
156
|
+
if (dispose)
|
|
157
|
+
disposes.push(dispose);
|
|
158
|
+
}
|
|
159
|
+
if (memo.has(el2))
|
|
160
|
+
return disposes.push(el2[_dispose]);
|
|
161
|
+
if (el2.parentNode !== parent)
|
|
162
|
+
return;
|
|
163
|
+
} else
|
|
164
|
+
i++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (let child of [...el2.children])
|
|
168
|
+
init(child, el2);
|
|
169
|
+
}
|
|
170
|
+
;
|
|
190
171
|
}
|
|
191
172
|
var evalMemo = {};
|
|
192
173
|
var parse = (expr, dir, fn) => {
|
|
@@ -273,28 +254,6 @@ var batch2 = (fn) => {
|
|
|
273
254
|
};
|
|
274
255
|
var untracked2 = (fn, prev, v) => (prev = current, current = null, v = fn(), current = prev, v);
|
|
275
256
|
|
|
276
|
-
// node_modules/swapdom/deflate.js
|
|
277
|
-
var swap = (parent, a, b, end = null, { remove, insert } = swap) => {
|
|
278
|
-
let i = 0, cur, next, bi, bidx = new Set(b);
|
|
279
|
-
while (bi = a[i++])
|
|
280
|
-
!bidx.has(bi) ? remove(bi, parent) : cur = cur || bi;
|
|
281
|
-
cur = cur || end, i = 0;
|
|
282
|
-
while (bi = b[i++]) {
|
|
283
|
-
next = cur ? cur.nextSibling : end;
|
|
284
|
-
if (cur === bi)
|
|
285
|
-
cur = next;
|
|
286
|
-
else {
|
|
287
|
-
if (b[i] === next)
|
|
288
|
-
cur = next;
|
|
289
|
-
insert(bi, cur, parent);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return b;
|
|
293
|
-
};
|
|
294
|
-
swap.insert = (a, b, parent) => parent.insertBefore(a, b);
|
|
295
|
-
swap.remove = (a, parent) => parent.removeChild(a);
|
|
296
|
-
var deflate_default = swap;
|
|
297
|
-
|
|
298
257
|
// directive/each.js
|
|
299
258
|
var _each = Symbol(":each");
|
|
300
259
|
directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
@@ -336,12 +295,13 @@ directive.each = (tpl, [itemVar, idxVar, evaluate], state) => {
|
|
|
336
295
|
return cur[idx];
|
|
337
296
|
} },
|
|
338
297
|
[idxVar]: { value: keys2 ? keys2[idx] : idx }
|
|
339
|
-
}), el = (tpl.content || tpl).cloneNode(true),
|
|
298
|
+
}), el = (tpl.content || tpl).cloneNode(true), frag = tpl.content ? { children: [...el.children], remove() {
|
|
299
|
+
this.children.map((el2) => el2.remove());
|
|
300
|
+
} } : el;
|
|
340
301
|
holder.before(el);
|
|
341
|
-
sprae(
|
|
302
|
+
sprae(frag, scope);
|
|
342
303
|
((cur[_signals] ||= [])[i] ||= {})[Symbol.dispose] = () => {
|
|
343
|
-
|
|
344
|
-
el2[Symbol.dispose](), el2.remove();
|
|
304
|
+
frag[Symbol.dispose](), frag.remove();
|
|
345
305
|
};
|
|
346
306
|
}
|
|
347
307
|
}
|
|
@@ -543,16 +503,18 @@ directive.ref.parse = (expr) => expr;
|
|
|
543
503
|
|
|
544
504
|
// directive/with.js
|
|
545
505
|
directive.with = (el, evaluate, rootState) => {
|
|
546
|
-
let state
|
|
506
|
+
let state;
|
|
547
507
|
return effect(() => {
|
|
548
|
-
values = evaluate(rootState);
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
)
|
|
555
|
-
|
|
508
|
+
let values = evaluate(rootState);
|
|
509
|
+
if (!state) {
|
|
510
|
+
state = store({});
|
|
511
|
+
Object.assign(state[_signals], rootState[_signals]);
|
|
512
|
+
for (let key in values)
|
|
513
|
+
state[_signals][key] = null, state[key] = values[key];
|
|
514
|
+
sprae(el, state);
|
|
515
|
+
} else {
|
|
516
|
+
Object.assign(state, values);
|
|
517
|
+
}
|
|
556
518
|
});
|
|
557
519
|
};
|
|
558
520
|
|
|
@@ -637,7 +599,6 @@ directive.fx = (el, evaluate, state) => {
|
|
|
637
599
|
// sprae.js
|
|
638
600
|
sprae.use(ulive_es_exports);
|
|
639
601
|
sprae.use({ compile: (expr) => sprae.constructor(`__scope`, `with (__scope) { return ${expr} };`) });
|
|
640
|
-
sprae.use({ swap: deflate_default });
|
|
641
602
|
var sprae_default = sprae;
|
|
642
603
|
export {
|
|
643
604
|
sprae_default as default
|
package/dist/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r,l,n,s=Object.defineProperty,a=Symbol("signals"),o=Symbol("length");function i(t
|
|
1
|
+
var e,t,r,l,n,s=Object.defineProperty,a=Symbol("signals"),o=Symbol("length");function i(t){if(!t)return t;if(t[a])return t;if(Array.isArray(t))return function(t){let r;if(t[a])return t;let l=e(t.length),n=Array(t.length).fill(null);const s=new Proxy(n,{get:(s,c)=>"symbol"==typeof c?c===o?l:c===a?n:n[c]:"length"===c?Array.prototype[r]?l.peek():l.value:(r=c,n[c]?n[c].valueOf():c<n.length?(n[c]=e(i(t[c]))).value:void 0),set(e,t,r){if("length"===t){for(let e=r,t=n.length;e<t;e++)delete s[e];return l.value=n.length=r,!0}return c(n,t,r),t>=l.peek()&&(l.value=n.length=Number(t)+1),!0},deleteProperty:(e,t)=>(u(n,t),!0)});return s}(t);if(t.constructor!==Object)return t;let r={},l=e(Object.values(t).length);const s=new Proxy(r,{get:(e,t)=>t===o?l:t===a?r:r[t]?.valueOf(),set:(e,t,n,s)=>(s=r[t],c(r,t,n),s||++l.value),deleteProperty:(e,t)=>u(r,t)&&l.value--,ownKeys:()=>(l.value,Reflect.ownKeys(r))});for(let e in t){const l=Object.getOwnPropertyDescriptor(t,e);l?.get?(r[e]=n(l.get.bind(s)))._set=l.set?.bind(s):(r[e]=null,c(r,e,t[e]))}return s}function c(t,n,s){let a=t[n];if(a)if(s===a.peek());else if(a._set)a._set(s);else if(Array.isArray(s)&&Array.isArray(a.peek())){const e=a.peek();e[o]?r((()=>{l((()=>{let t=0,r=s.length;for(;t<r;t++)e[t]=s[t];e.length=r}))})):a.value=s}else a.value=i(s);else t[n]=a=s?.peek?s:e(i(s))}function u(e,t){const r=e[t];if(r){const l=r[Symbol.dispose];return l&&delete r[Symbol.dispose],delete e[t],l?.(),!0}}var f=Symbol.dispose||=Symbol("dispose"),p={},d=new WeakMap;function y(e,t){if(!e?.children)return;if(d.has(e))return Object.assign(d.get(e),t);const r=i(t||{}),l=[];return function e(t,n=t.parentNode){if(t.attributes)for(let e=0;e<t.attributes.length;){let s=t.attributes[e];if(":"===s.name[0]){t.removeAttribute(s.name);let e=s.name.slice(1).split(":");for(let n of e){let e=p[n]||p.default,a=e(t,(e.parse||b)(s.value,b),r,n);a&&l.push(a)}if(d.has(t))return l.push(t[f]);if(t.parentNode!==n)return}else e++}for(let r of[...t.children])e(r,t)}(e),d.has(e)||d.set(e,r),e[f]=()=>{for(;l.length;)l.pop()();d.delete(e)},r}var v,h={},b=(e,t,r)=>{if(r=h[e=e.trim()])return r;try{r=v(e)}catch(r){throw Object.assign(r,{message:`∴ ${r.message}\n\n${t}${e?`="${e}"\n\n`:""}`,expr:e})}return h[e]=r};y.use=s=>{s.signal&&function(s){e=s.signal,t=s.effect,n=s.computed,l=s.batch||(e=>e()),r=s.untracked||l}(s),s.compile&&(v=s.compile)};var g,m,k={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(k,{batch:()=>O,computed:()=>w,effect:()=>S,signal:()=>A,untracked:()=>N});var A=(e,t,r=new Set)=>((t={get value(){return g?.deps.push(r.add(g)),e},set value(t){if(t!==e){e=t;for(let e of r)m?m.add(e):e()}},peek:()=>e}).toJSON=t.then=t.toString=t.valueOf=()=>t.value,t),S=(e,t,r,l)=>(l=(r=l=>{t?.call?.(),l=g,g=r;try{t=e()}finally{g=l}}).deps=[],r(),e=>{for(t?.call?.();e=l.pop();)e.delete(r)}),w=(e,t=A(),r,l)=>((r={get value(){return l||=S((()=>t.value=e())),t.value},peek:t.peek}).toJSON=r.then=r.toString=r.valueOf=()=>r.value,r),O=e=>{let t=m;t||(m=new Set);try{e()}finally{if(!t){t=m,m=null;for(const e of t)e()}}},N=(e,t,r)=>(t=g,g=null,r=e(),g=t,r),j=Symbol(":each");p.each=(e,[l,s,i],c)=>{const u=e[j]=document.createTextNode("");e.replaceWith(u);let f,p,d=0;const v=n((()=>{p=null;let e=i(c);return"number"==typeof e&&(e=Array.from({length:e},((e,t)=>t+1))),e?.constructor===Object&&(p=Object.keys(e),e=Object.values(e)),e||[]})),h=()=>{r((()=>{let t=0,r=v.value,n=r.length;if(f&&!f[o]){for(let e of f[a]||[])e[Symbol.dispose]();f=null,d=0}if(n<d)f.length=n;else{if(f)for(;t<d;t++)f[t]=r[t];else f=r;for(;t<n;t++){f[t]=r[t];let n=t,o=Object.create(c,{[l]:{get:()=>f[n]},[s]:{value:p?p[n]:n}}),i=(e.content||e).cloneNode(!0),d=e.content?{children:[...i.children],remove(){this.children.map((e=>e.remove()))}}:i;u.before(i),y(d,o),((f[a]||=[])[t]||={})[Symbol.dispose]=()=>{d[Symbol.dispose](),d.remove()}}}d=n}))};let b=0;return t((()=>{f||v.value[o]?.value,b?b++:(h(),queueMicrotask((()=>(b&&h(),b=0))))}))},p.each.parse=(e,t)=>{let[r,l]=e.split(/\s+in\s+/),[n,s="$"]=r.split(/\s*,\s*/);return[n,s,t(l)]};var x=Symbol("if");p.if=(e,r,l)=>{let n,s,a,o=e.parentNode,i=e.nextElementSibling,c=document.createTextNode(""),u=[];return e.after(c),e.content?(n=u,e.remove(),s=[...e.content.childNodes]):s=n=[e],i?.hasAttribute(":else")?(i.removeAttribute(":else"),i.hasAttribute(":if")?a=u:(i.remove(),a=i.content?[...i.content.childNodes]:[i])):a=u,t((()=>{const t=r(l)?s:e[x]?u:a;if(i&&(i[x]=t===s),n!=t){n[0]?.[j]&&(n=[n[0][j]]);for(let e of n)e.remove();n=t;for(let e of n)o.insertBefore(e,c),y(e,l)}}))},p.default=(e,r,l,n)=>{let s,a=n.startsWith("on")&&n.slice(2);return t(a?()=>(s?.(),s=$(e,a,r(l))):()=>{let t=r(l);if(n)T(e,n,D(t,l));else for(let r in t)T(e,_(r),D(t[r],l))})};var $=(e,t,r=(()=>{}))=>{const l={evt:"",target:e,test:()=>!0};l.evt=t.replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(l.test=E[t]?.(l,...r.split("-"))||l.test,"")));const{evt:n,target:s,test:a,defer:o,stop:i,prevent:c,...u}=l;o&&(r=o(r));const f=e=>a(e)&&(i&&e.stopPropagation(),c&&e.preventDefault(),r.call(s,e));return s.addEventListener(n,f,u),()=>s.removeEventListener(n,f,u)},E={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},throttle(e,t){e.defer=e=>C(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>W(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>P.ctrl(e)&&t.every((t=>P[t]?P[t](e):e.key===t)),shift:(e,...t)=>e=>P.shift(e)&&t.every((t=>P[t]?P[t](e):e.key===t)),alt:(e,...t)=>e=>P.alt(e)&&t.every((t=>P[t]?P[t](e):e.key===t)),meta:(e,...t)=>e=>P.meta(e)&&t.every((t=>P[t]?P[t](e):e.key===t)),arrow:()=>P.arrow,enter:()=>P.enter,escape:()=>P.escape,tab:()=>P.tab,space:()=>P.space,backspace:()=>P.backspace,delete:()=>P.delete,digit:()=>P.digit,letter:()=>P.letter,character:()=>P.character},P={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},T=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},C=(e,t)=>{let r,l,n=s=>{r=!0,setTimeout((()=>{if(r=!1,l)return l=!1,n(s),e(s)}),t)};return t=>r?l=!0:(n(t),e(t))},W=(e,t)=>{let r;return l=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(l)}),t)}},_=e=>e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),D=(e,t)=>e?.replace?e.replace(/\$<([^>]+)>/g,((e,r)=>t[r]??"")):e;p.ref=(e,t,r)=>{Object.defineProperty(r,D(t,r),{value:e})},p.ref.parse=e=>e,p.with=(e,r,l)=>{let n;return t((()=>{let t=r(l);if(n)Object.assign(n,t);else{n=i({}),Object.assign(n[a],l[a]);for(let e in t)n[a][e]=null,n[e]=t[e];y(e,n)}}))},p.html=(e,t,r)=>{let l=t(r);if(!l)return;let n=(l.content||l).cloneNode(!0);e.replaceChildren(n),y(e,r)},p.text=(e,r,l)=>(e.content&&e.replaceWith(e=document.createTextNode("")),t((()=>{let t=r(l);e.textContent=null==t?"":t}))),p.class=(e,r,l)=>{let n=new Set;return t((()=>{let t=r(l),s=new Set;t&&("string"==typeof t?D(t,l).split(" ").map((e=>s.add(e))):Array.isArray(t)?t.map((e=>(e=D(e,l))&&s.add(e))):Object.entries(t).map((([e,t])=>t&&s.add(e))));for(let t of n)s.has(t)?s.delete(t):e.classList.remove(t);for(let t of n=s)e.classList.add(t)}))},p.style=(e,r,l)=>{let n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t((()=>{let t=r(l);if("string"==typeof t)e.setAttribute("style",n+D(t,l));else{e.setAttribute("style",n);for(let r in t)e.style.setProperty(r,D(t[r],l))}}))},p.value=(e,r,l)=>{let n,s,a="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(n=e.selectionStart,s=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),n&&e.setSelectionRange(n,s)):"checkbox"===e.type?t=>(e.checked=t,T(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return t((()=>a(r(l))))},p.fx=(e,r,l)=>t((()=>r(l))),y.use(k),y.use({compile:e=>y.constructor("__scope",`with (__scope) { return ${e} };`)});var K=y;export{K as default};
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -144,7 +144,7 @@ Set any attribute(s).
|
|
|
144
144
|
Define values for a subtree.
|
|
145
145
|
|
|
146
146
|
```html
|
|
147
|
-
<x :with="{ foo:
|
|
147
|
+
<x :with="{ foo: 'bar' }">
|
|
148
148
|
<y :with="{ baz: 'qux' }" :text="foo + baz"></y>
|
|
149
149
|
</x>
|
|
150
150
|
```
|
|
@@ -335,7 +335,7 @@ import { effect } from 'sprae/signal'
|
|
|
335
335
|
import * as signals from '@preact/signals'
|
|
336
336
|
import compile from 'subscript'
|
|
337
337
|
|
|
338
|
-
//
|
|
338
|
+
// standard directives
|
|
339
339
|
import 'sprae/directive/if.js'
|
|
340
340
|
import 'sprae/directive/text.js'
|
|
341
341
|
|
|
@@ -358,9 +358,10 @@ To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. --
|
|
|
358
358
|
|
|
359
359
|
## Justification
|
|
360
360
|
|
|
361
|
-
[Template-parts](https://github.com/dy/template-parts) is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc)
|
|
361
|
+
[Template-parts](https://github.com/dy/template-parts) is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc).<br/>
|
|
362
|
+
[Alpine](https://github.com/alpinejs/alpine) / [petite-vue](https://github.com/vuejs/petite-vue) / [lucia](https://github.com/aidenyabi/lucia) escape native HTML quirks, but have excessive API (`:`, `x-`, `{}`, `@`, `$`) and tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223).
|
|
362
363
|
|
|
363
|
-
_Sprae_ holds open & minimalistic philosophy, combining _`:`-directives_ with
|
|
364
|
+
_Sprae_ holds open & minimalistic philosophy, combining _`:`-directives_ with _signals_.
|
|
364
365
|
|
|
365
366
|
<!--
|
|
366
367
|
| | [AlpineJS](https://github.com/alpinejs/alpine) | [Petite-Vue](https://github.com/vuejs/petite-vue) | Sprae |
|
|
@@ -406,14 +407,14 @@ npm ci
|
|
|
406
407
|
npm run build-prod
|
|
407
408
|
|
|
408
409
|
# bench
|
|
409
|
-
cd ../../../webdriver-ts
|
|
410
|
+
[cd ../../../webdriver-ts
|
|
410
411
|
npm ci
|
|
411
|
-
npm run compile
|
|
412
|
+
npm run compile]
|
|
412
413
|
npm run bench keyed/sprae
|
|
413
414
|
|
|
414
415
|
# show results
|
|
415
|
-
cd ../webdriver-ts-results
|
|
416
|
-
npm ci
|
|
416
|
+
[cd ../webdriver-ts-results
|
|
417
|
+
npm ci]
|
|
417
418
|
cd ../webdriver-ts
|
|
418
419
|
npm run results
|
|
419
420
|
```
|
package/sprae.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import sprae from './core.js'
|
|
2
2
|
|
|
3
3
|
import * as signals from 'ulive'
|
|
4
|
-
import swap from 'swapdom/deflate'
|
|
5
4
|
|
|
6
5
|
// default directives
|
|
7
6
|
import './directive/if.js'
|
|
@@ -22,7 +21,4 @@ sprae.use(signals)
|
|
|
22
21
|
// default compiler (indirect new Function to avoid detector)
|
|
23
22
|
sprae.use({ compile: expr => sprae.constructor(`__scope`, `with (__scope) { return ${expr} };`) })
|
|
24
23
|
|
|
25
|
-
// defaul dom swapper
|
|
26
|
-
sprae.use({ swap })
|
|
27
|
-
|
|
28
24
|
export default sprae
|
package/store.js
CHANGED
|
@@ -4,11 +4,11 @@ import { signal, computed, effect, batch, untracked } from './signal.js'
|
|
|
4
4
|
export const _signals = Symbol('signals'), _change = Symbol('length');
|
|
5
5
|
|
|
6
6
|
// object store is not lazy
|
|
7
|
-
export default function store(values
|
|
7
|
+
export default function store(values) {
|
|
8
8
|
if (!values) return values
|
|
9
9
|
|
|
10
10
|
// ignore existing state as argument
|
|
11
|
-
if (values[_signals]
|
|
11
|
+
if (values[_signals]) return values;
|
|
12
12
|
|
|
13
13
|
// redirect for optimized array store
|
|
14
14
|
if (Array.isArray(values)) return list(values)
|
|
@@ -17,9 +17,8 @@ export default function store(values, signals) {
|
|
|
17
17
|
if (values.constructor !== Object) return values;
|
|
18
18
|
|
|
19
19
|
// NOTE: if you decide to unlazy values, think about large arrays - init upfront can be costly
|
|
20
|
-
let _len = signal(Object.values(values).length)
|
|
20
|
+
let signals = {}, _len = signal(Object.values(values).length)
|
|
21
21
|
|
|
22
|
-
signals ||= {}
|
|
23
22
|
// proxy conducts prop access to signals
|
|
24
23
|
const state = new Proxy(signals, {
|
|
25
24
|
get: (_, key) => key === _change ? _len : key === _signals ? signals : signals[key]?.valueOf(),
|
|
@@ -32,9 +31,8 @@ export default function store(values, signals) {
|
|
|
32
31
|
},
|
|
33
32
|
})
|
|
34
33
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
else for (let key in values) {
|
|
34
|
+
// init signals for values
|
|
35
|
+
for (let key in values) {
|
|
38
36
|
const desc = Object.getOwnPropertyDescriptor(values, key)
|
|
39
37
|
|
|
40
38
|
// getter turns into computed
|
|
@@ -69,8 +67,8 @@ export function list(values) {
|
|
|
69
67
|
// proxy conducts prop access to signals
|
|
70
68
|
const state = new Proxy(signals, {
|
|
71
69
|
get(_, key) {
|
|
72
|
-
|
|
73
|
-
if (key ===
|
|
70
|
+
// covers Symbol.isConcatSpreadable etc.
|
|
71
|
+
if (typeof key === 'symbol') return key === _change ? _len : key === _signals ? signals : signals[key]
|
|
74
72
|
|
|
75
73
|
// console.log('get', key)
|
|
76
74
|
// if .length is read within .push/etc - peek signal to avoid recursive subscription
|