sprae 9.0.0 → 9.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 CHANGED
@@ -3,7 +3,7 @@
3
3
  > DOM tree microhydration
4
4
 
5
5
  _Sprae_ is a compact & ergonomic progressive enhancement framework.<br/>
6
- It provides `:`-attributes for inline markup logic with _signals_-based reactivity.<br/>
6
+ It provides `:`-attributes for inline markup logic with [_signals_](https://github.com/proposal-signals/proposal-signals) reactivity.<br/>
7
7
  Perfect for small-scale websites, prototypes, or lightweight UI.<br/>
8
8
 
9
9
 
@@ -29,281 +29,220 @@ Sprae evaluates `:`-directives and evaporates them, attaching state to html.
29
29
 
30
30
  ## Directives
31
31
 
32
- <details>
33
- <summary><strong>:if, :else</strong></summary>
34
-
35
- #### `:if="condition"`, `:else`
36
-
37
- Control flow of elements.
38
-
39
- ```html
40
- <span :if="foo">foo</span>
41
- <span :else :if="bar">bar</span>
42
- <span :else>baz</span>
43
-
44
- <!-- fragment -->
45
- <template :if="foo">
46
- foo <span>bar</span> baz
47
- </template>
48
- ```
49
- </details>
50
-
51
- <details>
52
- <summary><strong>:each</strong></summary>
53
-
54
- #### `:each="item, index in items"`
55
-
56
- Multiply element. Item is identified either by `item.id` or `item.key`.
57
-
58
- ```html
59
- <ul><li :each="item in items" :text="item"/></ul>
60
-
61
- <!-- cases -->
62
- <li :each="item, idx in list" />
63
- <li :each="val, key in obj" />
64
- <li :each="idx in number" />
65
-
66
- <!-- by condition -->
67
- <li :if="items" :each="item in items" :text="item" />
68
- <li :else>Empty list</li>
69
-
70
- <!-- fragment -->
71
- <template :each="item in items">
72
- <dt :text="item.term"/>
73
- <dd :text="item.definition"/>
74
- </template>
75
-
76
- <!-- prevent FOUC -->
77
- <style>[:each] {visibility: hidden}</style>
78
- ```
79
- </details>
32
+ #### `:if="condition"`, `:else`
80
33
 
81
- <details>
82
- <summary><strong>:text</strong></summary>
34
+ Control flow of elements.
83
35
 
84
- #### `:text="value"`
36
+ ```html
37
+ <span :if="foo">foo</span>
38
+ <span :else :if="bar">bar</span>
39
+ <span :else>baz</span>
85
40
 
86
- Set text content of an element.
41
+ <!-- fragment -->
42
+ <template :if="foo">foo <span>bar</span> baz</template>
43
+ ```
87
44
 
88
- ```html
89
- Welcome, <span :text="user.name">Guest</span>.
90
45
 
91
- <!-- fragment -->
92
- Welcome, <template :text="user.name" />.
93
- ```
94
- </details>
46
+ #### `:each="item, index in items"`
95
47
 
96
- <details>
97
- <summary><strong>:class</strong></summary>
48
+ Multiply element. Item is identified either by `item.id`, `item.key` or `item` itself.
98
49
 
99
- #### `:class="value"`
50
+ ```html
51
+ <ul><li :each="item in items" :text="item"/></ul>
100
52
 
101
- Set class value, extends existing `class`.
53
+ <!-- cases -->
54
+ <li :each="item, idx in list" />
55
+ <li :each="val, key in obj" />
56
+ <li :each="idx in number" />
102
57
 
103
- ```html
104
- <!-- string with interpolation -->
105
- <div :class="'foo $<bar>'"></div>
58
+ <!-- by condition -->
59
+ <li :if="items" :each="item in items" :text="item" />
60
+ <li :else>Empty list</li>
106
61
 
107
- <!-- array/object a-la clsx -->
108
- <div :class="[foo && 'foo', {bar: bar}]"></div>
109
- ```
110
- </details>
62
+ <!-- fragment -->
63
+ <template :each="item in items">
64
+ <dt :text="item.term"/>
65
+ <dd :text="item.definition"/>
66
+ </template>
111
67
 
112
- <details>
113
- <summary><strong>:style</strong></summary>
114
-
115
- #### `:style="value"`
68
+ <!-- prevent FOUC -->
69
+ <style>[:each] {visibility: hidden}</style>
70
+ ```
116
71
 
117
- Set style value, extends existing `style`.
72
+ #### `:text="value"`
118
73
 
119
- ```html
120
- <!-- string with interpolation -->
121
- <div :style="'foo: $<bar>'"></div>
74
+ Set text content of an element.
122
75
 
123
- <!-- object -->
124
- <div :style="{foo: 'bar'}"></div>
76
+ ```html
77
+ Welcome, <span :text="user.name">Guest</span>.
125
78
 
126
- <!-- CSS variable -->
127
- <div :style="{'--baz': qux}"></div>
128
- ```
129
- </details>
79
+ <!-- fragment -->
80
+ Welcome, <template :text="user.name" />.
81
+ ```
130
82
 
131
- <details>
132
- <summary><strong>:value</strong></summary>
83
+ #### `:class="value"`
133
84
 
134
- #### `:value="value"`
85
+ Set class value, extends existing `class`.
135
86
 
136
- Set value of an input, textarea or select. Takes handle of `checked` and `selected` attributes.
87
+ ```html
88
+ <!-- string with interpolation -->
89
+ <div :class="'foo $<bar>'"></div>
137
90
 
138
- ```html
139
- <input :value="value" />
140
- <textarea :value="value" />
91
+ <!-- array/object a-la clsx -->
92
+ <div :class="[foo && 'foo', {bar: bar}]"></div>
93
+ ```
141
94
 
142
- <!-- selects right option -->
143
- <select :value="selected">
144
- <option :each="i in 5" :value="i" :text="i"></option>
145
- </select>
146
- ```
147
- </details>
95
+ #### `:style="value"`
148
96
 
149
- <details>
150
- <summary><strong>:*</strong></summary>
97
+ Set style value, extends existing `style`.
151
98
 
152
- #### `:*="value"`, `:="values"`
99
+ ```html
100
+ <!-- string with interpolation -->
101
+ <div :style="'foo: $<bar>'"></div>
153
102
 
154
- Set any attribute(s).
103
+ <!-- object -->
104
+ <div :style="{foo: 'bar'}"></div>
155
105
 
156
- ```html
157
- <label :for="name" :text="name" />
106
+ <!-- CSS variable -->
107
+ <div :style="{'--baz': qux}"></div>
108
+ ```
158
109
 
159
- <!-- multiple attributes -->
160
- <input :id:name="name" />
110
+ #### `:value="value"`
161
111
 
162
- <!-- spread attributes -->
163
- <input :="{ id: name, name, type: 'text', value }" />
164
- ```
165
- </details>
112
+ Set value of an input, textarea or select. Takes handle of `checked` and `selected` attributes.
166
113
 
167
- <details>
168
- <summary><strong>:scope</strong></summary>
114
+ ```html
115
+ <input :value="value" />
116
+ <textarea :value="value" />
169
117
 
170
- #### `:scope="data"`
118
+ <!-- selects right option -->
119
+ <select :value="selected">
120
+ <option :each="i in 5" :value="i" :text="i"></option>
121
+ </select>
122
+ ```
171
123
 
172
- Define or extend data scope for a subtree.
124
+ #### `:[prop]="value"`, `:="values"`
173
125
 
174
- ```html
175
- <x :scope="{ foo: signal('bar') }">
176
- <!-- extends parent scope -->
177
- <y :scope="{ baz: 'qux' }" :text="foo + baz"></y>
178
- </x>
179
- ```
180
- </details>
126
+ Set any attribute(s).
181
127
 
182
- <details>
183
- <summary><strong>:ref</strong></summary>
128
+ ```html
129
+ <label :for="name" :text="name" />
184
130
 
185
- #### `:ref="name"`
131
+ <!-- multiple attributes -->
132
+ <input :id:name="name" />
186
133
 
187
- Expose element to current scope with `name`.
134
+ <!-- spread attributes -->
135
+ <input :="{ id: name, name, type: 'text', value }" />
136
+ ```
188
137
 
189
- ```html
190
- <textarea :ref="text" placeholder="Enter text..."></textarea>
138
+ #### `:scope="data"`
191
139
 
192
- <!-- iterable items -->
193
- <li :each="item in items" :ref="item">
194
- <input :onfocus..onblur=="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
195
- </li>
196
- ```
197
- </details>
140
+ Define or extend data scope for a subtree.
198
141
 
199
- <details>
200
- <summary><strong>:fx</strong></summary>
142
+ ```html
143
+ <x :scope="{ foo: signal('bar') }">
144
+ <!-- extends parent scope -->
145
+ <y :scope="{ baz: 'qux' }" :text="foo + baz"></y>
146
+ </x>
147
+ ```
201
148
 
202
- #### `:fx="values"`
149
+ #### `:ref="name"`
203
150
 
204
- Run effect, not changing any attribute.<br/>Optional cleanup is called in-between effect calls or on disposal.
151
+ Expose element to current scope with `name`.
205
152
 
206
- ```html
207
- <div :fx="a.value ? foo() : bar()" />
153
+ ```html
154
+ <textarea :ref="text" placeholder="Enter text..."></textarea>
208
155
 
209
- <!-- cleanup function -->
210
- <div :fx="id = setInterval(tick, interval), () => clearInterval(tick)" />
211
- ```
212
- </details>
156
+ <!-- iterable items -->
157
+ <li :each="item in items" :ref="item">
158
+ <input :onfocus..onblur=="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
159
+ </li>
160
+ ```
213
161
 
214
- <details>
215
- <summary><strong>:on*</strong></summary>
162
+ #### `:fx="code"`
216
163
 
217
- #### `:on<event>.<mod>="handler"`, `:on<in>..on<out>="handler"`
164
+ Run effect, not changing any attribute.<br/>Optional cleanup is called in-between effect calls or on disposal.
218
165
 
219
- Attach event(s) listener with possible modifiers.
166
+ ```html
167
+ <div :fx="a.value ? foo() : bar()" />
220
168
 
221
- ```html
222
- <input type="checkbox" :onchange="e => isChecked = e.target.value">
169
+ <!-- cleanup function -->
170
+ <div :fx="id = setInterval(tick, interval), () => clearInterval(tick)" />
171
+ ```
223
172
 
224
- <!-- multiple events -->
225
- <input :value="text" :oninput:onchange="e => text = e.target.value">
173
+ #### `:on[event]="handler"`
226
174
 
227
- <!-- events sequence -->
228
- <button :onfocus..onblur="e => ( handleFocus(), e => handleBlur())">
175
+ Attach event(s) listener with possible modifiers.
229
176
 
230
- <!-- event modifiers -->
231
- <button :onclick.throttle-500="handler">Not too often</button>
232
- ```
177
+ ```html
178
+ <input type="checkbox" :onchange="e => isChecked = e.target.value">
233
179
 
234
- ##### Modifiers:
180
+ <!-- multiple events -->
181
+ <input :value="text" :oninput:onchange="e => text = e.target.value">
235
182
 
236
- * `.once`, `.passive`, `.capture` – listener [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options).
237
- * `.prevent`, `.stop` prevent default or stop propagation.
238
- * `.window`, `.document`, `.outside`, `.self` – specify event target.
239
- * `.throttle-<ms>`, `.debounce-<ms>` – defer function call with one of the methods.
240
- * `.ctrl`, `.shift`, `.alt`, `.meta`, `.arrow`, `.enter`, `.escape`, `.tab`, `.space`, `.backspace`, `.delete`, `.digit`, `.letter`, `.character` – filter by [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
241
- * `.ctrl-<key>, .alt-<key>, .meta-<key>, .shift-<key>` – key combinations, eg. `.ctrl-alt-delete` or `.meta-x`.
242
- * `.*` – any other modifier has no effect, but allows binding multiple handlers to same event (like jQuery event classes).
183
+ <!-- events sequence -->
184
+ <button :onfocus..onblur="e => ( handleFocus(), e => handleBlur())">
243
185
 
244
- </details>
186
+ <!-- event modifiers -->
187
+ <button :onclick.throttle-500="handler">Not too often</button>
188
+ ```
245
189
 
190
+ ##### Modifiers:
246
191
 
247
- <details>
248
- <summary><strong>:html</strong> 🔌</summary>
192
+ * `.once`, `.passive`, `.capture` – listener [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options).
193
+ * `.prevent`, `.stop` – prevent default or stop propagation.
194
+ * `.window`, `.document`, `.outside`, `.self` – specify event target.
195
+ * `.throttle-<ms>`, `.debounce-<ms>` – defer function call with one of the methods.
196
+ * `.ctrl`, `.shift`, `.alt`, `.meta`, `.arrow`, `.enter`, `.escape`, `.tab`, `.space`, `.backspace`, `.delete`, `.digit`, `.letter`, `.character` – filter by [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
197
+ * `.ctrl-<key>, .alt-<key>, .meta-<key>, .shift-<key>` – key combinations, eg. `.ctrl-alt-delete` or `.meta-x`.
198
+ * `.*` – any other modifier has no effect, but allows binding multiple handlers to same event (like jQuery event classes).
249
199
 
250
- #### `:html="element"`
200
+ #### `:html="element"` 🔌
251
201
 
252
- > Include as `import 'sprae/directive/html'`.
202
+ > Include as `import 'sprae/directive/html'`.
253
203
 
254
- Set html content of an element or instantiate a template.
204
+ Set html content of an element or instantiate a template.
255
205
 
256
- ```html
257
- Hello, <span :html="userElement">Guest</span>.
206
+ ```html
207
+ Hello, <span :html="userElement">Guest</span>.
258
208
 
259
- <!-- fragment -->
260
- Hello, <template :html="user.name">Guest</template>.
209
+ <!-- fragment -->
210
+ Hello, <template :html="user.name">Guest</template>.
261
211
 
262
- <!-- instantiate template -->
263
- <template :ref="tpl"><span :text="foo"></span></template>
264
- <div :html="tpl" :scope="{foo:'bar'}">...inserted here...</div>
265
- ```
266
- </details>
212
+ <!-- instantiate template -->
213
+ <template :ref="tpl"><span :text="foo"></span></template>
214
+ <div :html="tpl" :scope="{foo:'bar'}">...inserted here...</div>
215
+ ```
267
216
 
217
+ #### `:data="values"` 🔌
268
218
 
269
- <details>
270
- <summary><strong>:data</strong> 🔌</summary>
219
+ > Include as `import 'sprae/directive/data'`.
271
220
 
272
- #### `:data="values"`
221
+ Set `data-*` attributes. CamelCase is converted to dash-case.
273
222
 
274
- > Include as `import 'sprae/directive/data'`.
223
+ ```html
224
+ <input :data="{foo: 1, barBaz: true}" />
225
+ <!-- <input data-foo="1" data-bar-baz /> -->
226
+ ```
275
227
 
276
- Set `data-*` attributes. CamelCase is converted to dash-case.
228
+ #### `:aria="values"` 🔌
277
229
 
278
- ```html
279
- <input :data="{foo: 1, barBaz: true}" />
280
- <!-- <input data-foo="1" data-bar-baz /> -->
281
- ```
282
- </details>
230
+ > Include as `import 'sprae/directive/aria'`.
283
231
 
232
+ Set `aria-*` attributes. Boolean values are stringified.
284
233
 
285
- <details>
286
- <summary><strong>:aria</strong> 🔌</summary>
287
-
288
- #### `:aria="values"`
289
-
290
- > Include as `import 'sprae/directive/aria'`.
291
-
292
- Set `aria-*` attributes. Boolean values are stringified.
293
-
294
- ```html
295
- <input role="combobox" :aria="{
296
- controls: 'joketypes',
297
- autocomplete: 'list',
298
- expanded: false,
299
- activeOption: 'item1',
300
- activedescendant: ''
301
- }" />
302
- <!--
303
- <input role="combobox" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant>
304
- -->
305
- ```
306
- </details>
234
+ ```html
235
+ <input role="combobox" :aria="{
236
+ controls: 'joketypes',
237
+ autocomplete: 'list',
238
+ expanded: false,
239
+ activeOption: 'item1',
240
+ activedescendant: ''
241
+ }" />
242
+ <!--
243
+ <input role="combobox" aria-controls="joketypes" aria-autocomplete="list" aria-expanded="false" aria-active-option="item1" aria-activedescendant>
244
+ -->
245
+ ```
307
246
 
308
247
  <!--
309
248
  #### `:onvisible..oninvisible="e => e => {}"`
@@ -326,10 +265,43 @@ Trigger when element is connected / disconnected from DOM.
326
265
  ```
327
266
  -->
328
267
 
268
+ ## Customization
269
+
270
+ _Sprae_ can be reconfigured to use alternative signals provider, expressions evaluator or directives.
271
+
272
+ ### Signals
273
+
274
+ Sprae uses [standard signals](https://github.com/proposal-signals/proposal-signals) for reactivity, but can be switched to any preact-flavored signals library:
275
+
276
+ ```js
277
+ import sprae, { signal, computed, effect, batch, untracked } from 'sprae';
278
+ import * as signals from '@preact/signals-core';
279
+
280
+ sprae.use(signals);
281
+
282
+ sprae(el, { name: signal('Kitty') });
283
+ ```
284
+
285
+ Provider | Size | Feature
286
+ :---|:---|:---
287
+ [`ulive`](https://ghub.io/ulive) | 350b | Minimal implementation, basic performance, good for small states
288
+ [`@webreflection/signal`](https://ghib.io/@webreflection/signal) | 531b | Class-based, better performance, good for small-medium states
289
+ [`usignal`](https://ghib.io/usignal) | 850b | Class-based with optimizations, good for medium states
290
+ [`@preact/signals-core`](https://ghub.io/@preact/signals-core) | 1.47kb | Best performance, good for any states
291
+
292
+
293
+ ### Evaluator
294
+
295
+ Expressions use _new Function_ as default evaluator, which is fast & compact way, but violates "unsafe-eval" CSP. To make eval stricter & safer, an alternative evaluator can be configured, eg. _justin_:
329
296
 
330
- ## Expressions
297
+ ```js
298
+ import sprae from 'sprae'
299
+ import justin from 'subscript/justin'
300
+
301
+ sprae.use({compile: justin}) // set up justin as default compiler
302
+ ```
331
303
 
332
- Expressions use [_justin_](https://github.com/dy/subscript?tab=readme-ov-file#justin), a minimal JS subset. It avoids "unsafe-eval" CSP and provides sandboxing. Also it's _fast_.
304
+ [_Justin_](https://github.com/dy/subscript?tab=readme-ov-file#justin) is minimal JS subset. It avoids "unsafe-eval" CSP and provides sandboxing.
333
305
 
334
306
  ###### Operators:
335
307
 
@@ -344,42 +316,29 @@ Expressions use [_justin_](https://github.com/dy/subscript?tab=readme-ov-file#ju
344
316
  `true false null undefined NaN`
345
317
 
346
318
 
347
- ## Signals
348
-
349
- Sprae uses minimal signals based on [`ulive`](https://ghub.io/ulive). It can be switched to [`@preact/signals-core`](https://ghub.io/@preact/signals-core), [`@webreflection/signal`](https://ghib.io/@webreflection/signal), [`usignal`](https://ghib.io/usignal), which are better for complex states:
350
-
351
- ```js
352
- import sprae, { signal, computed, effect, batch, untracked } from 'sprae';
353
- import * as signals from '@preact/signals-core';
354
-
355
- sprae.use(signals);
356
-
357
- sprae(el, { name: signal('Kitty') });
358
- ```
359
-
360
-
361
- ## Customization
319
+ ## Directives
362
320
 
363
- Sprae build can be tailored to project needs via `sprae/core` and `sprae/directive/*`:
321
+ Sprae build can be tailored to project needs via `sprae/core`:
364
322
 
365
323
  ```js
366
- import sprae, { directive, compile } from 'sprae/core.js'
324
+ import sprae, { directive } from 'sprae/core.js'
367
325
 
368
326
  // include directives
369
- import 'sprae/directive/if.js';
370
- import 'sprae/directive/text.js';
327
+ import 'sprae/directive/if.js'
328
+ import 'sprae/directive/text.js'
371
329
 
372
330
  // define custom directive
373
- directive.id = (el, expr, state) => {
374
- const evaluate = compile(state, 'id') // expression string -> evaluator
331
+ directive.id = (el, evaluate, state) => {
375
332
  return () => el.id = evaluate(state) // return update function
376
333
  }
377
334
  ```
378
335
 
336
+ See [`sprae.js`](./sprae.js) for example.
337
+
379
338
  <!--
380
339
  ### DOM diffing
381
340
 
382
- DOM differ uses [swapdom](https://github.com/dy/swapdom), can be reconfigured to [list-difference](https://github.com/paldepind/list-difference/), [udomdiff](https://github.com/WebReflection/udomdiff), [domdiff](https://github.com/WebReflection/domdiff), [etc](https://github.com/luwes/js-diff-benchmark):
341
+ DOM diffing uses [swapdom](https://github.com/dy/swapdom), but can be reconfigured to [list-difference](https://github.com/paldepind/list-difference/), [udomdiff](https://github.com/WebReflection/udomdiff), [domdiff](https://github.com/WebReflection/domdiff), or any other ([benchmark](https://github.com/luwes/js-diff-benchmark)):
383
342
 
384
343
  ```js
385
344
  import sprae from 'sprae';
@@ -390,26 +349,13 @@ sprae.use({ swap: domdiff });
390
349
  ```
391
350
  -->
392
351
 
393
- <!--
394
- ### Custom Build
395
-
396
- `sprae/core` exports bare-bones engine without directives, which allows tailoring build to project needs:
397
-
398
- ```js
399
- import sprae, { directive, effect } from 'sprae/core'
400
-
401
- // include required directives
402
- import 'sprae/directive/if'
403
- import 'sprae/directive/text'
404
- ```
405
- -->
406
-
407
352
 
408
353
  <!-- ## Dispose
409
354
 
410
355
  To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. -->
411
356
 
412
357
 
358
+ <!--
413
359
  ## v9 changes
414
360
 
415
361
  * No autoinit → use manual init via `import sprae from 'sprae'; sprae(document.body, state)`.
@@ -422,13 +368,13 @@ To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. --
422
368
  * Async props / events are not supported, pass async functions via state.
423
369
  * Directives order matters, eg. `<a :if :each :scope />` !== `<a :scope :each :if />`
424
370
  * Only one directive per `<template>`, eg. `<template :each />`, not `<template :if :each/>`
425
-
371
+ -->
426
372
 
427
373
  ## Justification
428
374
 
429
- [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but 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). [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) (no access to data, own reactivity, own expressions, own domdiffer).
375
+ [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but 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). [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).
430
376
 
431
- _Sprae_ holds to open & minimalistic philosophy, combining _`:`-directives_ with _signals_.
377
+ _Sprae_ holds open & minimalistic philosophy, combining _`:`-directives_ with _signals_.
432
378
 
433
379
  <!--
434
380
  | | [AlpineJS](https://github.com/alpinejs/alpine) | [Petite-Vue](https://github.com/vuejs/petite-vue) | Sprae |
@@ -474,18 +420,15 @@ npm ci
474
420
  npm run build-prod
475
421
 
476
422
  # bench
477
- cd ../../..
478
- cd webdriver-ts
423
+ cd ../../../webdriver-ts
479
424
  npm ci
480
425
  npm run compile
481
426
  npm run bench keyed/sprae
482
427
 
483
428
  # show results
484
- cd ..
485
- cd webdriver-ts-results
429
+ cd ../webdriver-ts-results
486
430
  npm ci
487
- cd ..
488
- cd webdriver-ts
431
+ cd ../webdriver-ts
489
432
  npm run results
490
433
  ```
491
434
  </details>
package/sprae.js CHANGED
@@ -1,6 +1,9 @@
1
- export * from './core.js'
2
- export { default } from './core.js'
1
+ import sprae from './core.js'
3
2
 
3
+ import * as signals from './signal.js'
4
+ import swap from 'swapdom/inflate'
5
+
6
+ // default directives
4
7
  import './directive/if.js'
5
8
  import './directive/each.js'
6
9
  import './directive/ref.js'
@@ -12,3 +15,15 @@ import './directive/style.js'
12
15
  import './directive/value.js'
13
16
  import './directive/fx.js'
14
17
  import './directive/default.js'
18
+
19
+ // default signals
20
+ sprae.use(signals)
21
+
22
+ // default compiler (indirect new Function to avoid detector)
23
+ sprae.use({ compile: expr => sprae.constructor(`__scope`, `with (__scope) { return ${expr} };`) })
24
+
25
+ // defaul dom swapper
26
+ sprae.use({ swap })
27
+
28
+ export default sprae
29
+ export { signal, computed, effect, batch, untracked } from './core.js'