sprae 2.14.1 → 3.0.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/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "2.14.1",
4
+ "version": "3.0.0",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
8
8
  "dependencies": {
9
- "@preact/signals-core": "^1.2.3",
10
- "primitive-pool": "^2.0.0",
11
- "signal-struct": "^1.10.0",
12
9
  "swapdom": "^1.1.1"
13
10
  },
14
11
  "devDependencies": {
@@ -17,6 +14,7 @@
17
14
  "esbuild": "^0.15.14",
18
15
  "hyperf": "^1.5.0",
19
16
  "jsdom": "^21.1.0",
17
+ "signal-struct": "^1.10.0",
20
18
  "terser": "^5.15.1",
21
19
  "tst": "^7.1.1",
22
20
  "usignal": "^0.8.9",
@@ -25,7 +23,7 @@
25
23
  },
26
24
  "scripts": {
27
25
  "test": "node -r ./test/register.cjs test/test.js",
28
- "build": "esbuild --bundle ./src/index.js --outfile=sprae.js --format=esm",
26
+ "build": "esbuild --bundle ./src/index.js --outfile=sprae.js --format=esm && npm run min",
29
27
  "watch": "esbuild --bundle ./src/index.js --outfile=sprae.js --format=esm --sourcemap --watch",
30
28
  "min": "terser sprae.js -o sprae.min.js --module -c passes=3 -m"
31
29
  },
package/r&d.md CHANGED
@@ -320,7 +320,7 @@
320
320
  + it has better control over serialization
321
321
  + `:onchange:oninput="e=>xyz"` is very good
322
322
 
323
- ## [ ] Sandbox? -> too complex for now. Waiting for use-cases
323
+ ## [x] Sandbox? -> we need it anyways via Proxy, so yes
324
324
 
325
325
  1. Use subscript?
326
326
  + solves access to any internal signals on syntactic level
@@ -359,12 +359,13 @@
359
359
  ? maybe define setters such that when they're set
360
360
 
361
361
  2. Use sandboxed proxy
362
- - tough evaluation
363
- - implementation hiccups
364
- - screwed up data
365
- - no full protection
362
+ - tougher evaluation
363
+ - no full protection (Function.constructor)
366
364
  - relatively slow
367
365
  + does minimal catch
366
+ + allows scope control
367
+ + allows dynamic subscription to requested fields ( no need for preact/signals neither for signal-struct )
368
+ + we anyways need sandbox even in case of subscript
368
369
 
369
370
  ## [x] :onclick="direct code" ? -> no: immediately invoked.
370
371
 
@@ -447,30 +448,32 @@
447
448
  * onkey.after, onkey.microtask, onkey.defer, onkey.immediate
448
449
  * onkey.tick-1?
449
450
 
450
- ## [x] Mods design -> use event modifiers, not props modifiers
451
-
452
- * [ ] Prop modifiers
453
- - overall seems code complication without much benefit
454
- * [ ] value.bind? value.watch?
455
- - let's wait for use-case: value can be too big to set it every time
456
- * [ ] prop.reflect, prop.observe
457
- - let's wait for use-case
458
- * [ ] prop.boolean, .number, .string, .array, .object
459
- - let's wait for use-case
460
- * [ ] prop.once, prop.fx ? prop.init?
461
- - doesn't seem required, let's wait for use case
462
- * [ ] prop.change - run only if value changes
463
- - seems like unnecessary manual optimization that must be done always automatically
464
- ? are there cases where force-update is necessary?
465
- * [ ] prop.throttle-xxx, prop.debounce-xxx
466
- - let's wait until that's really a problem
467
- * [ ] prop.class
468
- ? what's the use-case
469
- * [ ] prop.next="" - run update after other DOM updates happen
470
- * [ ] prop.fx="" - run effect without changing property
471
- * [ ] x.prop="xyz" - set element property, rather than attribute (following topic)
472
- * [ ] x.raf="abc" - run regularly?
473
- * [ ] x.watch-xyz - update by change of any of the deps
451
+ ### [ ] onkey.unfocusable
452
+
453
+ ## [ ] Prop modifiers
454
+
455
+ - overall seems code complication without much benefit
456
+ * [ ] value.bind? value.watch?
457
+ - let's wait for use-case: value can be too big to set it every time
458
+ * [ ] prop.reflect, prop.observe
459
+ - let's wait for use-case
460
+ * [ ] prop.boolean, .number, .string, .array, .object
461
+ - let's wait for use-case
462
+ * [ ] prop.once, prop.fx ? prop.init?
463
+ - doesn't seem required, let's wait for use case
464
+ * [ ] prop.change - run only if value changes
465
+ - seems like unnecessary manual optimization that must be done always automatically
466
+ ? are there cases where force-update is necessary?
467
+ * [ ] prop.throttle-xxx, prop.debounce-xxx
468
+ - let's wait until that's really a problem
469
+ * [ ] prop.class
470
+ ? what's the use-case
471
+ * [ ] prop.next="" - run update after other DOM updates happen
472
+ * [ ] prop.fx="" - run effect without changing property
473
+ * [ ] x.prop="xyz" - set element property, rather than attribute (following topic)
474
+ * [ ] x.raf="abc" - run regularly?
475
+ * [ ] x.watch-a-b-c - update by change of any of the deps
476
+ * [ ] :x.always - update by _any_ dep change
474
477
 
475
478
  ## [x] Writing props on elements (like ones in :each) -> nah, just use `:x="this.x=abc"`
476
479
 
@@ -522,4 +525,4 @@
522
525
  ? does it replace element or puts a content?
523
526
 
524
527
  1. :use="ref-id"
525
- 2. :
528
+ 2. :
package/readme.md CHANGED
@@ -22,6 +22,7 @@ Sprae defines attributes starting with `:` as directives:
22
22
 
23
23
  * `sprae` initializes subtree with data and immediately evaporates `:` attrs.
24
24
  * `state` is object reflecting current values, changing any of its props rerenders subtree.
25
+ * To batch-update multiple properties `sprae` can be run repeatedly as: `sprae(container, newValues)`
25
26
 
26
27
  <!--
27
28
  <details>
@@ -139,7 +140,7 @@ Set any attribute value or run effect.
139
140
  <!-- Bulk properties -->
140
141
  <input :="{ id: name, name, type:'text', value }" />
141
142
 
142
- <!-- Effect (triggers any time foo or bar changes) -->
143
+ <!-- Effects (trigger any time foo or bar changes) -->
143
144
  <div :="if (foo) bar()" :fx="void bar()" ></div>
144
145
  ```
145
146
 
@@ -251,96 +252,13 @@ Expose element to data scope with the `id`:
251
252
  </script>
252
253
  ```
253
254
 
255
+ ## Sandbox
254
256
 
255
- <!--
256
-
257
- ### Reactivity
258
-
259
- _sprae_ is built on top of [_@preact/signals_](https://ghub.io/@preact/signals). That gives:
260
-
261
- * Expressions don't require explicit access to `.value` (see [signal-struct](https://github.com/dy/signal-struct))
262
- * Expressions support any reactive values in data (see [sube](https://github.com/dy/sube))
263
- * Updates happen minimally only when used values update
264
- * Subscription is weak and get disposed when element is disposed.
265
-
266
- Directive expressions are natively reactive, ie. data may contain any async/reactive values, such as:
267
-
268
- * _Promise_ / _Thenable_
269
- * _Observable_ / _Subject_ / _Subscribable_
270
- * _AsyncIterable_
271
- * _observ-*_
272
- * etc., see [sube](https://github.com/dy/sube/blob/main/README.md) for the full list.
273
-
274
- This way, for example, _@preact/signals_ or _rxjs_ can be connected directly bypassing subscription or reading value.
275
- -->
276
-
277
- ## Hints
278
-
279
- **1.** To batch-update state (avoid multiple DOM changes), rerun sprae with new state:
280
-
281
- ```html
282
- <li :each="item, id in items" :key="id" :text="item"></li>
283
-
284
- <script type="module">
285
- sprae(el, {items: ['foo', 'bar', 'baz']})
286
- // <li>foo</li><li>bar</li><li>baz</li>
287
-
288
- sprae(el, {items: ['foo', 'qux']})
289
- // <li>foo</li><li>qux</li>
290
- </script>
291
- ```
292
-
293
- **2.** Data supports signal values, which can be an alternative way to control template state:
294
-
295
- ```html
296
- <div id="done" :text="loading ? 'loading' : result">...</div>
297
-
298
- <script type="module">
299
- import sprae from 'sprae';
300
- import { signal } from '@preact/signals';
257
+ Expressions are sandboxed, ie. have no access to global or window.
301
258
 
302
- // <div id="done">...</div>
259
+ Default sandbox provides: _Array_, _Object_, _Number_, _String_, _Boolean_, _Date_, _console_.
303
260
 
304
- const loading = signal(true), result = signal(false);
305
- sprae(done, { loading, result })
306
- setTimeout(() => (loading.value = true, result.value = 'done'), 1000)
307
-
308
- // <div id="done">loading</div>
309
-
310
- // ... 1s after
311
- // <div id="done">done</div>
312
- </script>
313
- ```
314
-
315
- **3.** Data recognizes reactive values as inputs as well: _Promise_ / _Thenable_, _Observable_ / _Subscribable_, _AsyncIterable_ (etc., see [sube](https://github.com/dy/sube/blob/main/README.md)). This way, for example, _rxjs_ can be connected to template directly.
316
-
317
- ```html
318
- <div :text="clicks">#</div> clicks
319
-
320
- <script type="module">
321
- import sprae from 'sprae';
322
- import { fromEvent, scan } from 'rxjs';
323
- sprae(document, {
324
- clicks: fromEvent(document, 'click').pipe(scan((count) => count + 1, 0))
325
- });
326
- </script>
327
- ```
328
-
329
- **4.** Getters turn into computed values automatically (setters remain as is):
330
-
331
- ```html
332
- <div id="x-plus-y">
333
- <span :text="x">x</span> + <span :text="y">y</span> = <span :text="z">z</span>
334
- </div>
335
-
336
- <script type="module">
337
- import sprae from 'sprae';
338
- let state = sprae(document, { x:1, y:1, get z() { return this.x + this.y } })
339
-
340
- state.x = 2, state.y = 2
341
- state.z // 4
342
- </script>
343
- ```
261
+ Sandbox can be extended via `Object.assign(sprae.sandbox, { BigInt, window, document })` etc.
344
262
 
345
263
  ## Examples
346
264