@wrnrlr/prelude 0.2.17 → 0.2.18

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/deno.jsonc CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrnrlr/prelude",
3
- "version": "0.2.17",
3
+ "version": "0.2.18",
4
4
  "exports": {
5
5
  ".": "./src/mod.ts",
6
6
  "./style.css": "./src/style.css"
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "tasks": {
20
20
  "dev": "deno run -A npm:vite --config www/vite.config.js",
21
- "test": "deno test -A ./test/*.[jt]s",
21
+ "test": "deno test -A ./test/*.[jt]s --no-check",
22
22
  "release": "git push origin main:release"
23
23
  },
24
24
  "lint": {
@@ -0,0 +1,29 @@
1
+ <script type="module">
2
+
3
+ import {h,signal,effect,batch,wrap,render,context,useContext,List} from '../src/mod.ts'
4
+
5
+ const CounterCtx = context()
6
+
7
+ const useCounter = () => {
8
+ const c = useContext(CounterCtx)
9
+ return c
10
+ }
11
+
12
+ function CounterProvider(props) {
13
+ const counter = signal(0)
14
+ const increment = ()=>(console.log('increment', counter()),counter(v=>++v))
15
+ return h(CounterCtx, {value:()=>[counter, increment]}, props.children)
16
+ }
17
+
18
+ function Counter() {
19
+ const [val, increment] = useCounter()
20
+ return [val, h('button', {onClick:e=>increment()}, '+')]
21
+ }
22
+
23
+ function App() {
24
+ return h(CounterProvider, ()=>h(Counter))
25
+ }
26
+
27
+ render(App, document.body)
28
+
29
+ </script>
package/example/h.html ADDED
@@ -0,0 +1,24 @@
1
+ <script type="module">
2
+
3
+ // import { createSignal, createEffect } from 'solid'
4
+ import { render } from 'https://esm.sh/solid-js/web'
5
+ import h from 'https://esm.sh/solid-js/h'
6
+
7
+ function A(props) {
8
+ console.log('A')
9
+ return ['A', h(B)]
10
+ }
11
+
12
+ function B() {
13
+ console.log('B')
14
+ return 'B'
15
+ }
16
+
17
+ function App() {
18
+ console.log('App')
19
+ return h(A, h(B))
20
+ }
21
+
22
+ render(App, document.body)
23
+
24
+ </script>
@@ -11,6 +11,8 @@
11
11
  <li><a href="todo.html">Todo</a></li>
12
12
  <li><a href="widget.html">Widget</a></li>
13
13
  <li><a href="table.html">Table</a></li>
14
+ <li><a href="context.html">Context (Dependency Injection)</a></li>
15
+ <li><a href="h.html">h</a></li>
14
16
  </ul>
15
17
  <!-- <a href="router.html">Router</a> -->
16
18
  <!-- <a href="context.html">Context</a> -->
package/example/style.css CHANGED
@@ -12,6 +12,7 @@
12
12
  :root {
13
13
  font-family: sans-serif;
14
14
  }
15
+
15
16
  .example {
16
17
  display: flex;
17
18
  padding: 2rem;
@@ -22,8 +23,5 @@ details {
22
23
  display: block;
23
24
  }
24
25
  }
25
- script {
26
- display: block;
27
- }
28
26
 
29
27
  .select {}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wrnrlr/prelude",
3
3
  "type": "module",
4
- "version": "0.2.17",
4
+ "version": "0.2.18",
5
5
  "author": "Werner Laurensse",
6
6
  "description": "A signal based frontend library with fine-grained reactivity",
7
7
  "main": "./src/mod.ts",
package/readme.md CHANGED
@@ -227,3 +227,7 @@ deno task test
227
227
  * [Homepage](https://wrnrlr.github.io/prelude)
228
228
  * [NPM](https://www.npmjs.com/package/@wrnrlr/prelude)
229
229
  * [JSR](https://jsr.io/@wrnrlr/prelude)
230
+
231
+ ## More Links
232
+
233
+ * [Fine-grained reactivity - A deep dive into SolidJS](https://github.com/everweij/solidjs-fine-grained-reactivity)
@@ -1,167 +1,211 @@
1
1
  import type {DOMElements} from './constants.ts'
2
- import type {Runtime,Mountable} from './runtime.ts'
2
+ import type {Mountable} from './runtime.ts'
3
3
  import {r} from './runtime.ts'
4
4
 
5
- const ELEMENT: unique symbol = Symbol(), {isArray} = Array
6
-
7
- // export type Mountable = View | HTMLElement | string | number | bigint | symbol | boolean | Date | Mountable[];
8
- export type Component<T> = ((props:T) => Mountable) | ((props:T) => Mountable[])
9
- export type Tag = typeof DOMElements extends Set<infer K> ? K : never;
10
- export type Child = { ():Child } | Element | Child[] | string | number | symbol | bigint | boolean | Date | Record<string,unknown> | {():Child, [ELEMENT]:boolean}
11
- export type View = {():void, [ELEMENT]?:boolean}
5
+ // const document = globalThis.document
12
6
 
13
7
  export const h = hyperscript(r)
14
8
 
15
- // export type PropsKeys = typeof Properties extends Set<infer K> ? K : never;
16
- // export type BooleanProps = typeof BooleanAttributes ext ends Set<infer K> ? K : never;
17
- // export type HandlerProps = typeof DelegatedEvents extends Set<infer K> ? K : never;
18
- // export type ChildProps = {children?:any[]}
19
- // export type ElementProps = BooleanProps & HandlerProps & ChildProps & {class: string}
20
- // export type ComponentProps = { children?:Child }
21
- // export type Props = Record<string, any>
22
- // type AllProps = ElementProps | ComponentProps
23
- // type EmptyProps = Record<string,never>
24
- // const Fragment:Component<Props> = <T extends Props>(props:T):Mountable => (props as any).children
9
+ type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node
10
+
11
+ interface Runtime {
12
+ insert(parent: MountableElement, accessor: any, marker?: Node | null, init?: any): any;
13
+ spread(node: Element, accessor: any, isSVG?: Boolean, skipChildren?: Boolean): void;
14
+ assign(node: Element, props: any, isSVG?: Boolean, skipChildren?: Boolean): void;
15
+ component(Comp: (props: any) => any, props: any): any;
16
+ SVGElements: Set<string>;
17
+ }
18
+
19
+ const $ELEMENT = Symbol("hyper-element");
25
20
 
26
- export type TagParser = <T extends string>(s:T) => {name:string,id?:string,classes:string[],namespace?:string}
21
+ type ExpandableNode = Node & { [key: string]: any };
22
+ type Props = { [key: string]: any };
27
23
 
28
24
  export type HyperScript = {
29
- (first: Tag): View
30
- <P>(first: Tag, second: P): View
31
- <C extends Child>(first: Tag, second: C): View
32
- <C extends Child, P>(first: Tag, second: P, third: C): View
33
-
34
- (first: Component<Record<string,never>>): View
35
- <P extends Record<string, unknown>>(first: Component<P>, second: P): View
36
- <C>(first: Component<{children:C}>, second: C): View
37
- <P extends Record<string, unknown>, C>(first: Component<P & {children:C}>, second:P, third:C): View
38
- }
25
+ (...args: any[]): () => ExpandableNode | ExpandableNode[];
26
+ Fragment: (props: {
27
+ children: (() => ExpandableNode) | (() => ExpandableNode)[];
28
+ }) => ExpandableNode[];
29
+ };
39
30
 
40
- /**
41
- @param r
42
- @param patch
43
- @group Hyperscript
44
- */
45
- export function hyperscript(r: Runtime, parseTag: TagParser = parseHtmlTag): HyperScript {
46
-
47
- function item(e: Element, c: Child, m?: null): void {
48
- if (c===null) return
49
- const t = typeof c
50
- if (isArray(c))
51
- for (const child of c)
52
- item(e, child, m)
53
- else if (t === 'object' && r.isChild(c))
54
- r.insert(e, c, m)
55
- else if (t === 'string')
56
- e.appendChild(r.text((c as string)))
57
- else if (t === 'function') {
58
- // while (c[ELEMENT]?.call) c = (c as any)()
59
- r.insert(e, c, m)
60
- } else e.appendChild(r.text(c.toString()))
61
- }
31
+ // Inspired by https://github.com/hyperhype/hyperscript
32
+ export function hyperscript(r: Runtime): HyperScript {
33
+ function h() {
34
+ let args: any = [].slice.call(arguments),
35
+ e: ExpandableNode | undefined,
36
+ classes:string[] = [],
37
+ multiExpression = false;
62
38
 
63
- return function h<P extends Record<string,unknown>, C = never>(
64
- first: Tag | Component<P>,
65
- second?: P | C,
66
- third?: C
67
- ): View {
68
- let props: P
69
- let children: Child
70
-
71
- if (typeof second === 'object' && !isArray(second)) {
72
- children = (third as Child) || [];
73
- props = ((second ?? {}) as P & {children:C})
74
- } else {
75
- children = (second as Child) || []
76
- props = {} as P & {children:C}
39
+ while (Array.isArray(args[0])) args = args[0];
40
+ if (args[0][$ELEMENT]) args.unshift(h.Fragment);
41
+ typeof args[0] === "string" && detectMultiExpression(args);
42
+ const ret: (() => ExpandableNode) & { [$ELEMENT]?: boolean } = () => {
43
+ while (args.length) item(args.shift());
44
+ if (e instanceof globalThis.Element && classes.length) e.classList.add(...classes)
45
+ return e as ExpandableNode;
46
+ };
47
+ ret[$ELEMENT] = true;
48
+ return ret;
49
+
50
+ function item(l: any) {
51
+ const type = typeof l;
52
+ if (l == null) void 0;
53
+ else if ("string" === type) {
54
+ if (!e) parseHtmlTag(l);
55
+ else e.appendChild(globalThis.document.createTextNode(l));
56
+ } else if (
57
+ "number" === type ||
58
+ "boolean" === type ||
59
+ "bigint" === type ||
60
+ "symbol" === type ||
61
+ l instanceof Date ||
62
+ l instanceof RegExp
63
+ ) {
64
+ (e as Node).appendChild(globalThis.document.createTextNode(l.toString()));
65
+ } else if (Array.isArray(l)) {
66
+ for (let i = 0; i < l.length; i++) item(l[i]);
67
+ } else if (l instanceof globalThis.Element) {
68
+ r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
69
+ } else if ("object" === type) {
70
+ let dynamic = false;
71
+ const d = Object.getOwnPropertyDescriptors(l);
72
+ for (const k in d) {
73
+ if (k === "class" && classes.length !== 0) {
74
+ console.log('classes',classes)
75
+ const fixedClasses = classes.join(" "),
76
+ value = typeof d["class"].value === "function" ?
77
+ ()=>[...classes,...(d["class"].value()??'').split(' ')].filter(c=>c).join(' ') :
78
+ [...classes,...(d["class"].value??'').split(' ')].filter(c=>c).join(' ')
79
+ Object.defineProperty(l,"class",{...d[k],value})
80
+ // classes = []
81
+ }
82
+ if (k !== "ref" && k.slice(0, 2) !== "on" && typeof d[k].value === "function") {
83
+ dynamicProperty(l, k);
84
+ dynamic = true;
85
+ } else if (d[k].get) dynamic = true;
86
+ }
87
+ dynamic
88
+ ? r.spread(e as globalThis.Element, l, !!args.length)
89
+ : r.assign(e as globalThis.Element, l, !!args.length);
90
+ } else if ("function" === type) {
91
+ if (!e) {
92
+ let props: Props | undefined,
93
+ next = args[0];
94
+ if (
95
+ next == null ||
96
+ (typeof next === "object" && !Array.isArray(next) && !(next instanceof globalThis.Element))
97
+ )
98
+ props = args.shift();
99
+ props || (props = {});
100
+ if (args.length) {
101
+ props.children = args.length > 1 ? args : args[0];
102
+ }
103
+ const d = Object.getOwnPropertyDescriptors(props);
104
+ for (const k in d) {
105
+ if (Array.isArray(d[k].value)) {
106
+ const list = d[k].value;
107
+ props[k] = () => {
108
+ for (let i = 0; i < list.length; i++) {
109
+ while (list[i][$ELEMENT]) list[i] = list[i]();
110
+ }
111
+ return list;
112
+ };
113
+ dynamicProperty(props, k);
114
+ } else if (typeof d[k].value === "function" && !d[k].value.length)
115
+ dynamicProperty(props, k);
116
+ }
117
+ e = r.component(l, props);
118
+ args = [];
119
+ } else {
120
+ while ((l as any)[$ELEMENT]) l = ((l as unknown) as () => ExpandableNode)();
121
+ r.insert(e as globalThis.Element, l, multiExpression ? null : undefined);
122
+ }
123
+ }
77
124
  }
125
+ function parseHtmlTag(s: string) {
126
+ let i:number
127
+
128
+ i = s.indexOf('#')
129
+ if (i===-1) i = s.indexOf('.')
130
+ if (i===-1) i = s.length
131
+ const name = s.slice(0, i) || 'div'
132
+ e = r.SVGElements.has(name)
133
+ ? globalThis.document.createElementNS("http://www.w3.org/2000/svg", name)
134
+ : globalThis.document.createElement(name);
135
+ s = s.slice(i)
136
+
137
+ if (s[0]==='#') {
138
+ i = s.indexOf('.')
139
+ if (i===-1) i = s.length
140
+ e!.setAttribute("id", s.slice(1, i))
141
+ s = s.slice(i)
142
+ }
78
143
 
79
- let ret:View
80
-
81
- if (typeof first === 'string') {
82
- const tag = parseTag(first)
83
- const multiExpression = detectMultiExpression(children) ? null : undefined
84
- const e = r.element(tag.name)
85
- const props2 = props as P & {class?: string|(()=>string)}
86
- if (tag.id) e.setAttribute('id',tag.id)
87
- if (tag.classes?.length) {
88
- const cd = Object.getOwnPropertyDescriptor(props2,'class') ?? ({value:'',writable:true,enumerable:true});
89
- props2.class = (cd.value?.call) ?
90
- () => [...tag.classes,...(cd.value()??'').split(' ')].filter(c=>c).join(' ') :
91
- [...tag.classes,...(cd.value??'').split(' ')].filter(c=>c).join(' ')
144
+ while(s[0]==='.') {
145
+ i = s.indexOf('.',1)
146
+ if (i===-1) i = s.length
147
+ classes.push(s.slice(1, i))
148
+ s = s.slice(i)
92
149
  }
93
- // if (patch) patch(props2)
94
- let dynamic = false
95
- const d = Object.getOwnPropertyDescriptors(props2)
96
- for (const k in d) {
97
- if (k !== 'ref' && !k.startsWith('on') && typeof d[k].value === 'function') {
98
- dynamicProperty(props2, k)
99
- dynamic = true
100
- } else if (d[k].get) dynamic = true
150
+ }
151
+ function parseClass(string: string) {
152
+ const m = string.split(/([\.#]?[^\s#.]+)/);
153
+ if (/^\.|#/.test(m[1])) e = globalThis.document.createElement("div");
154
+ for (let i = 0; i < m.length; i++) {
155
+ let v = m[i]
156
+ const s = v.substring(1, v.length);
157
+ if (!v) v = 'div';
158
+ if (!e)
159
+ e = r.SVGElements.has(v)
160
+ ? globalThis.document.createElementNS("http://www.w3.org/2000/svg", v)
161
+ : globalThis.document.createElement(v);
162
+ if (v[0] === ".") classes.push(s);
163
+ else if (v[0] === "#") e.setAttribute("id", s);
101
164
  }
102
- (dynamic ? r.spread : r.assign) (e, props2, !!(children as {length?: number})?.length)
103
- item(e, children, multiExpression)
104
- ret = () => e
105
- } else {
106
- const d = Object.getOwnPropertyDescriptors(props)
107
- if (children) (props as unknown as {children:unknown}).children = children
108
- for (const k in d) {
109
- if (isArray(d[k].value)) {
110
- const list = d[k].value;
111
- (props as Record<string, ()=>unknown>)[k] = () => {
112
- for (let i = 0; i < list.length; i++)
113
- while (list[i][ELEMENT]) list[i] = list[i]()
114
- return list
115
- }
116
- dynamicProperty(props, k)
117
- } else if (typeof d[k].value==='function' && !d[k].value.length) { // A function with zero arguments
118
- dynamicProperty(props, k)
165
+ }
166
+ function detectMultiExpression(list: any[]) {
167
+ for (let i = 1; i < list.length; i++) {
168
+ if (typeof list[i] === "function") {
169
+ multiExpression = true;
170
+ return;
171
+ } else if (Array.isArray(list[i])) {
172
+ detectMultiExpression(list[i]);
119
173
  }
120
174
  }
121
- const e = r.component(() => (first as Component<P>)(props))
122
- ret = () => e
123
175
  }
124
- ret[ELEMENT] = true
125
- return ret
126
176
  }
127
- }
128
177
 
129
- function detectMultiExpression(children: Child): boolean {
130
- if (typeof children === 'function') return true
131
- else if (!isArray(children)) return false
132
- for (const i of children) {
133
- if (typeof i === 'function') return true
134
- else if (isArray(i)) return detectMultiExpression(i)
135
- }
136
- return false
178
+ h.Fragment = (props: any) => props.children;
179
+ return h;
137
180
  }
138
181
 
182
+
139
183
  // ^([a-zA-Z]\w*)?(#[a-zA-Z][-\w]*)?(.[a-zA-Z][-\w]*)*
140
- export function parseHtmlTag(s:Tag) {
141
- const classes:string[] = [];
142
- let id:string|undefined = undefined, i:number
143
-
144
- i = s.indexOf('#')
145
- if (i===-1) i = s.indexOf('.')
146
- if (i===-1) i = s.length
147
- const name = s.slice(0, i) || 'div'
148
- s = s.slice(i)
149
-
150
- if (s[0]==='#') {
151
- i = s.indexOf('.')
152
- if (i===-1) i = s.length
153
- id = s.slice(1, i)
154
- s = s.slice(i)
155
- }
184
+ // export function parseHtmlTag(s:Tag) {
185
+ // const classes:string[] = [];
186
+ // let id:string|undefined = undefined, i:number
156
187
 
157
- while(s[0]==='.') {
158
- i = s.indexOf('.',1)
159
- if (i===-1) i = s.length
160
- classes.push(s.slice(1, i))
161
- s = s.slice(i)
162
- }
163
- return {name:name as string,classes,id:id}
164
- }
188
+ // i = s.indexOf('#')
189
+ // if (i===-1) i = s.indexOf('.')
190
+ // if (i===-1) i = s.length
191
+ // const name = s.slice(0, i) || 'div'
192
+ // s = s.slice(i)
193
+
194
+ // if (s[0]==='#') {
195
+ // i = s.indexOf('.')
196
+ // if (i===-1) i = s.length
197
+ // id = s.slice(1, i)
198
+ // s = s.slice(i)
199
+ // }
200
+
201
+ // while(s[0]==='.') {
202
+ // i = s.indexOf('.',1)
203
+ // if (i===-1) i = s.length
204
+ // classes.push(s.slice(1, i))
205
+ // s = s.slice(i)
206
+ // }
207
+ // return {name:name as string,classes,id:id}
208
+ // }
165
209
 
166
210
  function dynamicProperty<T>(props: Record<string, unknown>, key: string) {
167
211
  const src = props[key] as ()=>unknown
package/src/reactive.ts CHANGED
@@ -59,13 +59,6 @@ export type Setter<T> = {
59
59
  (value: T): T
60
60
  }
61
61
 
62
- export type Context<T> = {
63
- id: symbol,
64
- defaultValue: T,
65
- get(): T,
66
- set(value: T): void
67
- }
68
-
69
62
  export type Options<T> = {
70
63
  equals?: false | EqualsFn<T>
71
64
  }
@@ -297,42 +290,32 @@ export function root<T>(fn: RootFn<T>): T {
297
290
  return new Root().wrap(fn)
298
291
  }
299
292
 
300
- export function context<T>(): Context<T | undefined>;
301
- export function context<T>(defaultValue: T): Context<T>;
293
+ export type Context<T> = {
294
+ id: symbol,
295
+ defaultValue: T,
296
+ get(): T,
297
+ set(value: T): void
298
+ }
299
+
300
+ export function context<T>(): Context<T | undefined>
301
+ export function context<T>(defaultValue: T): Context<T>
302
302
  export function context<T>(defaultValue?: T) {
303
303
  const id = Symbol()
304
- const get = (): T | undefined => OBSERVER?.get(id) ?? defaultValue
305
- const set = ( value: T ): void => OBSERVER?.set(id, value)
306
- // const Provider = createProvider(id)
307
- const s = {id, defaultValue, get, set}
308
- const f = Object.assign((props:any) => {
309
- set(props.value)
304
+ const s = {id, defaultValue}
305
+ return Object.assign((props:any) => {
306
+ OBSERVER?.set(id, props.value)
310
307
  return () => props.children.call ? props.children() : props.children
311
- }, s)
312
- return f as unknown as Context<T>
313
- // return {id, Provider, defaultValue} as unknown as Context<T>
308
+ }, s) as unknown as Context<T>
314
309
  }
315
310
 
316
- function createProvider(id: symbol): any {
317
- return function provider(props) {
318
- let res;
319
- renderEffect(() => (res = untrack(() => {
320
- console.log('observer', OBSERVER, props)
321
- OBSERVER!.context = { ...OBSERVER!.context, [id]: props.value };
322
- return children(() => props.children);
323
- })))
324
- return res;
325
- };
311
+ export function useContext<T>(ctx: Context<T>): T {
312
+ return OBSERVER?.get(ctx.id) ?? ctx.defaultValue
326
313
  }
327
314
 
328
315
  export function renderEffect(fn:()=>void) {
329
316
  return globalThis.requestAnimationFrame(()=>effect(fn))
330
317
  }
331
318
 
332
- export function useContext<T>(context: Context<T>): T {
333
- return context.get()
334
- }
335
-
336
319
  export type S<T> = Getter<T> | Setter<T>
337
320
 
338
321
  /**
package/src/runtime.ts CHANGED
@@ -2,26 +2,12 @@
2
2
  import {effect,untrack,root} from './reactive.ts'
3
3
  import {SVGNamespace,SVGElements,ChildProperties,getPropAlias,Properties,Aliases,DelegatedEvents} from './constants.ts'
4
4
  import {reconcileArrays} from './domdiff.ts'
5
- // import type {Mountable} from './constants.ts'
6
5
 
7
6
  const {isArray} = Array
8
7
 
9
8
  export type Mountable = HTMLElement | Document | ShadowRoot | DocumentFragment | Node | string | number | bigint | symbol;
10
9
 
11
- export const r = { render, insert, spread, assign, element, component, text, isChild, clearDelegatedEvents}
12
-
13
- // declare global {
14
- // interface Document {
15
- // '_$DX_DELEGATE'?: Record<string, Set<unknown>>
16
- // }
17
- // // interface SVGElement {}
18
- // }
19
-
20
- // interface Element {
21
- // style?: string
22
- // }
23
-
24
- // declare const globalThis: Document
10
+ export const r = { render, insert, spread, assign, element, component, text, isChild, clearDelegatedEvents, SVGElements }
25
11
 
26
12
  /**
27
13
 
@@ -67,8 +53,8 @@ function isChild(a:unknown): a is Element {
67
53
  return a instanceof Element
68
54
  }
69
55
 
70
- function component(fn:()=>unknown) {
71
- return untrack(fn)
56
+ function component(fn:()=>unknown, props: unknown) {
57
+ return untrack(()=>fn(props))
72
58
  }
73
59
 
74
60
  function render(code: ()=>void, element:Element|Document, init?: unknown): void {
@@ -1,16 +1,13 @@
1
- // import {runtime} from '../src/runtime.ts'
2
1
  import { Window } from 'happy-dom'
3
2
  import {assertEquals} from '@std/assert'
4
3
 
5
4
  const window = new Window
6
5
 
7
- globalThis.window = window
8
- globalThis.document = window.document
9
- globalThis.navigator = window.navigator
6
+ globalThis = window
10
7
 
11
- import {signal,root} from '../src/reactive.ts'
8
+ import { signal, root } from '../src/reactive.ts'
12
9
  import { r } from '../src/runtime.ts'
13
- import {h} from '../src/hyperscript.ts'
10
+ import { h } from '../src/hyperscript.ts'
14
11
 
15
12
  function testing(name, props, f=props) {
16
13
  const htest = t => (name, f) => {
@@ -44,7 +41,7 @@ testing('h with basic element', async test => {
44
41
  await test("number content", () => assertHTML(h('i',1), '<i>1</i>'))
45
42
  await test("bigint content", () => assertHTML(h('i',2n), '<i>2</i>'))
46
43
  await test("symbol content", () => assertHTML(h('i',Symbol('A')), '<i>Symbol(A)</i>'))
47
- // await test('regex content', () => assertHTML(h('b',/\w/), '<b>/\\w/</b>'))
44
+ await test('regex content', () => assertHTML(h('b',/\w/), '<b>/\\w/</b>'))
48
45
  await test("signal content", () => assertHTML(h('i',()=>1), '<i>1</i>'))
49
46
  await test('array content', () => assertHTML(h('i',['A',1,2n]), '<i>A12</i>'))
50
47
  await test('ref property', () => assertHTML(h('hr',{ref:el=>el.setAttribute('a','1')}), '<hr a="1">'))