@solidjs/h 2.0.0-experimental.9 → 2.0.0-rc.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
@@ -6,31 +6,37 @@ HyperScript function takes a few forms. The 2nd props argument is optional. Chil
6
6
 
7
7
  ```js
8
8
  // create an element with a title attribute
9
- h("button", { title: "My button" }, "Click Me")
9
+ h("button", { title: "My button" }, "Click Me");
10
10
 
11
11
  // create a component with a title prop
12
- h(Button, { title: "My button" }, "Click Me")
12
+ h(Button, { title: "My button" }, "Click Me");
13
13
 
14
14
  // create an element with many children
15
- h("div", { title: "My button" }, h("span", "1"), h("span", "2"), h("span", "3"))
15
+ h("div", { title: "My button" }, h("span", "1"), h("span", "2"), h("span", "3"));
16
16
  ```
17
17
 
18
18
  This is the least efficient way to use Solid as it requires a slightly larger runtime that isn't treeshakeable, and cannot leverage anything in the way of analysis, so it requires manual wrapping of expressions and has a few other caveats (see below).
19
19
 
20
+ > `h(...)` returns a tagged zero-arity thunk rather than a DOM node directly.
21
+ > Pass a function reference (or `() => h(App)`) to `render(...)` so the thunk
22
+ > is invoked inside the root. Nested `h(...)` thunks auto-invoke when consumed,
23
+ > so composition with control-flow components like `<For>` and `<Show>` works
24
+ > without extra wrapping.
25
+
20
26
  ## Example
21
27
 
22
28
  ```js
23
- import { render } from "solid-js/web";
24
- import h from "solid-js/h";
29
+ import { render } from "@solidjs/web";
30
+ import h from "@solidjs/h";
25
31
  import { createSignal } from "solid-js";
26
32
 
27
33
  function Button(props) {
28
- return h("button.btn-primary", props)
34
+ return h("button.btn-primary", props);
29
35
  }
30
36
 
31
37
  function Counter() {
32
38
  const [count, setCount] = createSignal(0);
33
- const increment = (e) => setCount(c => c + 1);
39
+ const increment = e => setCount(c => c + 1);
34
40
 
35
41
  return h(Button, { type: "button", onClick: increment }, count);
36
42
  }
@@ -38,6 +44,21 @@ function Counter() {
38
44
  render(Counter, document.getElementById("app"));
39
45
  ```
40
46
 
47
+ ## TypeScript JSX
48
+
49
+ When using TypeScript's automatic JSX runtime with hyperscript, point `jsxImportSource` at `@solidjs/h`:
50
+
51
+ ```json
52
+ {
53
+ "compilerOptions": {
54
+ "jsx": "react-jsx",
55
+ "jsxImportSource": "@solidjs/h"
56
+ }
57
+ }
58
+ ```
59
+
60
+ `solid-js` does not provide JSX runtime types in 2.0. Web JSX should use `@solidjs/web`; hyperscript JSX should use `@solidjs/h`.
61
+
41
62
  ## Differences from JSX
42
63
 
43
64
  There are a few differences from Solid's JSX that are important to note. And also apply when attempting use any transformation that would compile to HyperScript.
@@ -46,22 +67,22 @@ There are a few differences from Solid's JSX that are important to note. And als
46
67
 
47
68
  ```js
48
69
  // jsx
49
- <div id={props.id}>{firstName() + lastName()}</div>
70
+ <div id={props.id}>{firstName() + lastName()}</div>;
50
71
 
51
72
  // hyperscript
52
- h("div", { id: () => props.id }, () => firstName() + lastName())
73
+ h("div", { id: () => props.id }, () => firstName() + lastName());
53
74
  ```
54
75
 
55
76
  2. Merging spreads requires using the merge props helper to keep reactivity
56
77
 
57
78
  ```js
58
79
  // jsx
59
- <div class={selectedClass()} {...props} />
80
+ <div class={selectedClass()} {...props} />;
60
81
 
61
82
  // hyperscript
62
- import { mergeProps } from "solid-js"
83
+ import { merge } from "solid-js";
63
84
 
64
- h("div", mergeProps({ class: selectedClass }, props))
85
+ h("div", merge({ class: selectedClass }, props));
65
86
  ```
66
87
 
67
88
  3. Events on components require explicit event in the arguments
@@ -70,10 +91,10 @@ Solid's HyperScript automatically wraps functions passed to props of components
70
91
 
71
92
  ```js
72
93
  // good
73
- h(Button, { onClick: (e) => console.log("Hi")});
94
+ h(Button, { onClick: e => console.log("Hi") });
74
95
 
75
96
  // bad
76
- h(Button, { onClick: () => console.log("Hi")})
97
+ h(Button, { onClick: () => console.log("Hi") });
77
98
  ```
78
99
 
79
100
  4. All refs are callback form
@@ -83,17 +104,17 @@ We can't do the compiled assignment trick so only the callback form is supported
83
104
  ```js
84
105
  let myEl;
85
106
 
86
- h(div, { ref: (el) => myEl = el });
107
+ h(div, { ref: el => (myEl = el) });
87
108
  ```
88
109
 
89
110
  5. There is a shorthand for static id and classes
90
111
 
91
112
  ```js
92
- h("div#some-id.my-class")
113
+ h("div#some-id.my-class");
93
114
  ```
94
115
 
95
116
  6. Fragments are just arrays
96
117
 
97
118
  ```js
98
- [h("span", "1"), h("span", "2")]
119
+ [h("span", "1"), h("span", "2")];
99
120
  ```
package/dist/h.cjs CHANGED
@@ -2,15 +2,54 @@
2
2
 
3
3
  var web = require('@solidjs/web');
4
4
 
5
+ const $ELEMENT = Symbol("hyper-element");
6
+ const $WRAPPED = Symbol("hyper-wrapped");
7
+ function resolveThunks(value) {
8
+ if (typeof value === "function" && value[$ELEMENT]) return resolveThunks(value());
9
+ if (Array.isArray(value)) {
10
+ const out = new Array(value.length);
11
+ for (let i = 0; i < value.length; i++) out[i] = resolveThunks(value[i]);
12
+ return out;
13
+ }
14
+ return value;
15
+ }
16
+ function wrapCallback(orig) {
17
+ let w;
18
+ if (orig.length === 1) {
19
+ w = function (a) {
20
+ return resolveThunks(orig.call(this, a));
21
+ };
22
+ } else if (orig.length === 2) {
23
+ w = function (a, b) {
24
+ return resolveThunks(orig.call(this, a, b));
25
+ };
26
+ } else {
27
+ w = function (...args) {
28
+ return resolveThunks(orig.apply(this, args));
29
+ };
30
+ Object.defineProperty(w, "length", {
31
+ value: orig.length
32
+ });
33
+ }
34
+ w[$WRAPPED] = true;
35
+ return w;
36
+ }
5
37
  function createHyperScript(r) {
6
- function h() {
7
- let args = [].slice.call(arguments),
8
- e,
9
- classes = [],
10
- multiExpression = false;
38
+ function h(...rawArgs) {
39
+ if (rawArgs.length === 1 && Array.isArray(rawArgs[0])) return rawArgs[0];
40
+ const thunk = () => r.untrack(() => materialize(rawArgs));
41
+ thunk[$ELEMENT] = true;
42
+ return thunk;
43
+ }
44
+ function materialize(args) {
45
+ let e;
46
+ let classes = [];
47
+ let multiExpression = false;
48
+ args = args.slice();
11
49
  function item(l) {
50
+ if (l == null) return;
12
51
  const type = typeof l;
13
- if (l == null) ;else if ("string" === type) {
52
+ if ("string" === type) {
14
53
  if (!e) parseClass(l);else e.appendChild(document.createTextNode(l));
15
54
  } else if ("number" === type || "boolean" === type || "bigint" === type || "symbol" === type || l instanceof Date || l instanceof RegExp) {
16
55
  e.appendChild(document.createTextNode(l.toString()));
@@ -35,29 +74,27 @@ function createHyperScript(r) {
35
74
  dynamic = true;
36
75
  } else if (d[k].get) dynamic = true;
37
76
  }
38
- dynamic ? r.spread(e, l, e instanceof SVGElement, !!args.length) : r.assign(e, l, e instanceof SVGElement, !!args.length);
77
+ dynamic ? r.spread(e, l, !!args.length) : r.assign(e, l, !!args.length);
39
78
  } else if ("function" === type) {
40
79
  if (!e) {
41
- let props,
42
- next = args[0];
43
- if (next == null || typeof next === "object" && !Array.isArray(next) && !(next instanceof Element)) props = args.shift();
44
- props || (props = {});
45
- if (args.length) {
46
- props.children = args.length > 1 ? args : args[0];
47
- }
48
- const d = Object.getOwnPropertyDescriptors(props);
49
- for (const k in d) {
50
- if (typeof d[k].value === "function" && !d[k].value.length) r.dynamicProperty(props, k);
80
+ const first = args[0];
81
+ const props = first == null || typeof first === "object" && !Array.isArray(first) && !(first instanceof Element) ? args.shift() || {} : {};
82
+ if (args.length) props.children = args.length > 1 ? args : args[0];
83
+ for (const k in props) {
84
+ const v = props[k];
85
+ if (typeof v === "function") {
86
+ if (!v.length) r.dynamicProperty(props, k);else if (!v[$ELEMENT] && !v[$WRAPPED]) {
87
+ props[k] = wrapCallback(v);
88
+ }
89
+ }
51
90
  }
52
91
  e = r.createComponent(l, props);
92
+ while (typeof e === "function" && e[$ELEMENT]) e = e();
53
93
  args = [];
54
- } else {
55
- r.insert(e, l, multiExpression ? null : undefined);
56
- }
94
+ } else if (l[$ELEMENT]) item(l());else r.insert(e, l, multiExpression ? null : undefined);
57
95
  }
58
96
  }
59
- if (args.length === 1 && Array.isArray(args[0])) return args[0];
60
- typeof args[0] === "string" && detectMultiExpression(args);
97
+ if (typeof args[0] === "string") detectMultiExpression(args);
61
98
  while (args.length) item(args.shift());
62
99
  if (e instanceof Element && classes.length) e.classList.add(...classes);
63
100
  return e;
@@ -68,7 +105,7 @@ function createHyperScript(r) {
68
105
  const v = m[i],
69
106
  s = v.substring(1, v.length);
70
107
  if (!v) continue;
71
- if (!e) e = r.SVGElements.has(v) ? document.createElementNS("http://www.w3.org/2000/svg", v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
108
+ if (!e) e = r.SVGElements.has(v) ? document.createElementNS(r.Namespaces.svg, v) : r.MathMLElements.has(v) ? document.createElementNS(r.Namespaces.mathml, v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
72
109
  }
73
110
  }
74
111
  function detectMultiExpression(list) {
@@ -92,7 +129,10 @@ const h = createHyperScript({
92
129
  insert: web.insert,
93
130
  createComponent: web.createComponent,
94
131
  dynamicProperty: web.dynamicProperty,
95
- SVGElements: web.SVGElements
132
+ untrack: web.untrack,
133
+ SVGElements: web.SVGElements,
134
+ MathMLElements: web.MathMLElements,
135
+ Namespaces: web.Namespaces
96
136
  });
97
137
 
98
138
  module.exports = h;
package/dist/h.js CHANGED
@@ -1,14 +1,53 @@
1
- import { spread, assign, insert, createComponent, dynamicProperty, SVGElements } from '@solidjs/web';
1
+ import { Namespaces, MathMLElements, SVGElements, untrack, dynamicProperty, createComponent, insert, assign, spread } from '@solidjs/web';
2
2
 
3
+ const $ELEMENT = Symbol("hyper-element");
4
+ const $WRAPPED = Symbol("hyper-wrapped");
5
+ function resolveThunks(value) {
6
+ if (typeof value === "function" && value[$ELEMENT]) return resolveThunks(value());
7
+ if (Array.isArray(value)) {
8
+ const out = new Array(value.length);
9
+ for (let i = 0; i < value.length; i++) out[i] = resolveThunks(value[i]);
10
+ return out;
11
+ }
12
+ return value;
13
+ }
14
+ function wrapCallback(orig) {
15
+ let w;
16
+ if (orig.length === 1) {
17
+ w = function (a) {
18
+ return resolveThunks(orig.call(this, a));
19
+ };
20
+ } else if (orig.length === 2) {
21
+ w = function (a, b) {
22
+ return resolveThunks(orig.call(this, a, b));
23
+ };
24
+ } else {
25
+ w = function (...args) {
26
+ return resolveThunks(orig.apply(this, args));
27
+ };
28
+ Object.defineProperty(w, "length", {
29
+ value: orig.length
30
+ });
31
+ }
32
+ w[$WRAPPED] = true;
33
+ return w;
34
+ }
3
35
  function createHyperScript(r) {
4
- function h() {
5
- let args = [].slice.call(arguments),
6
- e,
7
- classes = [],
8
- multiExpression = false;
36
+ function h(...rawArgs) {
37
+ if (rawArgs.length === 1 && Array.isArray(rawArgs[0])) return rawArgs[0];
38
+ const thunk = () => r.untrack(() => materialize(rawArgs));
39
+ thunk[$ELEMENT] = true;
40
+ return thunk;
41
+ }
42
+ function materialize(args) {
43
+ let e;
44
+ let classes = [];
45
+ let multiExpression = false;
46
+ args = args.slice();
9
47
  function item(l) {
48
+ if (l == null) return;
10
49
  const type = typeof l;
11
- if (l == null) ;else if ("string" === type) {
50
+ if ("string" === type) {
12
51
  if (!e) parseClass(l);else e.appendChild(document.createTextNode(l));
13
52
  } else if ("number" === type || "boolean" === type || "bigint" === type || "symbol" === type || l instanceof Date || l instanceof RegExp) {
14
53
  e.appendChild(document.createTextNode(l.toString()));
@@ -33,29 +72,27 @@ function createHyperScript(r) {
33
72
  dynamic = true;
34
73
  } else if (d[k].get) dynamic = true;
35
74
  }
36
- dynamic ? r.spread(e, l, e instanceof SVGElement, !!args.length) : r.assign(e, l, e instanceof SVGElement, !!args.length);
75
+ dynamic ? r.spread(e, l, !!args.length) : r.assign(e, l, !!args.length);
37
76
  } else if ("function" === type) {
38
77
  if (!e) {
39
- let props,
40
- next = args[0];
41
- if (next == null || typeof next === "object" && !Array.isArray(next) && !(next instanceof Element)) props = args.shift();
42
- props || (props = {});
43
- if (args.length) {
44
- props.children = args.length > 1 ? args : args[0];
45
- }
46
- const d = Object.getOwnPropertyDescriptors(props);
47
- for (const k in d) {
48
- if (typeof d[k].value === "function" && !d[k].value.length) r.dynamicProperty(props, k);
78
+ const first = args[0];
79
+ const props = first == null || typeof first === "object" && !Array.isArray(first) && !(first instanceof Element) ? args.shift() || {} : {};
80
+ if (args.length) props.children = args.length > 1 ? args : args[0];
81
+ for (const k in props) {
82
+ const v = props[k];
83
+ if (typeof v === "function") {
84
+ if (!v.length) r.dynamicProperty(props, k);else if (!v[$ELEMENT] && !v[$WRAPPED]) {
85
+ props[k] = wrapCallback(v);
86
+ }
87
+ }
49
88
  }
50
89
  e = r.createComponent(l, props);
90
+ while (typeof e === "function" && e[$ELEMENT]) e = e();
51
91
  args = [];
52
- } else {
53
- r.insert(e, l, multiExpression ? null : undefined);
54
- }
92
+ } else if (l[$ELEMENT]) item(l());else r.insert(e, l, multiExpression ? null : undefined);
55
93
  }
56
94
  }
57
- if (args.length === 1 && Array.isArray(args[0])) return args[0];
58
- typeof args[0] === "string" && detectMultiExpression(args);
95
+ if (typeof args[0] === "string") detectMultiExpression(args);
59
96
  while (args.length) item(args.shift());
60
97
  if (e instanceof Element && classes.length) e.classList.add(...classes);
61
98
  return e;
@@ -66,7 +103,7 @@ function createHyperScript(r) {
66
103
  const v = m[i],
67
104
  s = v.substring(1, v.length);
68
105
  if (!v) continue;
69
- if (!e) e = r.SVGElements.has(v) ? document.createElementNS("http://www.w3.org/2000/svg", v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
106
+ if (!e) e = r.SVGElements.has(v) ? document.createElementNS(r.Namespaces.svg, v) : r.MathMLElements.has(v) ? document.createElementNS(r.Namespaces.mathml, v) : document.createElement(v);else if (v[0] === ".") classes.push(s);else if (v[0] === "#") e.setAttribute("id", s);
70
107
  }
71
108
  }
72
109
  function detectMultiExpression(list) {
@@ -90,7 +127,10 @@ const h = createHyperScript({
90
127
  insert,
91
128
  createComponent,
92
129
  dynamicProperty,
93
- SVGElements
130
+ untrack,
131
+ SVGElements,
132
+ MathMLElements,
133
+ Namespaces
94
134
  });
95
135
 
96
136
  export { h as default };
@@ -0,0 +1,7 @@
1
+ export type { JSX } from "./jsx.d.ts";
2
+ import type { JSX } from "./jsx.d.ts";
3
+ declare function Fragment(props: {
4
+ children: JSX.Element;
5
+ }): JSX.Element;
6
+ declare function jsx(type: any, props: any): JSX.Element;
7
+ export { jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Shared type-level helpers used to derive `prop:*` attribute typings from
3
+ * DOM element interfaces (e.g. `HTMLInputElement`, `HTMLButtonElement`).
4
+ *
5
+ * The wrapping of each value (`FunctionMaybe<T>` in `jsx-h.d.ts` vs. the
6
+ * raw value in `jsx.d.ts`) is applied by each consumer when composing its
7
+ * own `Properties<T>` mapped type. That way this file stays identical in
8
+ * both reactive and non-reactive contexts and only needs to exist once.
9
+ *
10
+ * originally from
11
+ * @url https://github.com/potahtml/pota
12
+ */
13
+
14
+ /** Base-class properties shared by all elements — skipped from `prop:*`. */
15
+ export type SkipPropsFrom = HTMLUnknownElement & HTMLElement & Element & Node;
16
+
17
+ /**
18
+ * Value types allowed on a `prop:*`. Primitives plus the writable
19
+ * non-primitive DOM-object props worth exposing:
20
+ *
21
+ * - `HTMLMediaElement.srcObject`
22
+ * - `HTMLButtonElement.popoverTargetElement` / `commandForElement` (and the same via
23
+ * `PopoverTargetAttributes` mixin on `HTMLInputElement`)
24
+ */
25
+ export type PropValue =
26
+ | string
27
+ | number
28
+ | boolean
29
+ | null
30
+ | MediaStream
31
+ | MediaSource
32
+ | Blob
33
+ | File
34
+ | Date
35
+ | Element;
36
+
37
+ /**
38
+ * Ergonomics widening for emitted `prop:*` value types:
39
+ *
40
+ * - general `string` → `string | number` (HTML coerces numbers)
41
+ * - string literal unions (`'on' | 'off'`) stay exact, so users still get autocomplete /
42
+ * narrowing
43
+ * - other types pass through unchanged
44
+ */
45
+ type WidenString<V> = string extends V ? string | number : V;
46
+ export type WidenPropValue<V> = [V] extends [string] ? WidenString<V> : V;
47
+
48
+ /**
49
+ * Structurally identical → `Y`; distinct → `N`. Used by `IsReadonlyKey` to detect
50
+ * readonly keys by comparing `Pick<T, K>` with `Readonly<Pick<T, K>>`.
51
+ */
52
+ export type IfEquals<A, B, Y = unknown, N = never> =
53
+ (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
54
+
55
+ /**
56
+ * True when `K` is readonly on `T`. Singleton-constant properties (e.g.
57
+ * `tagName: "INPUT"`, `nodeType: 1`) are always `readonly` in `lib.dom.d.ts`, so this
58
+ * single check covers both readonly and singleton-literal cases.
59
+ */
60
+ export type IsReadonlyKey<T, K extends keyof T> = IfEquals<
61
+ Pick<T, K>,
62
+ Readonly<Pick<T, K>>,
63
+ true,
64
+ false
65
+ >;
66
+
67
+ /**
68
+ * Resolves to the `prop:K` string literal when `K` is a writable, element-specific
69
+ * property suitable for a `prop:*` attribute; otherwise resolves to `never` so the
70
+ * key is filtered out of the resulting mapped type.
71
+ *
72
+ * Filters out:
73
+ *
74
+ * - base-class keys (via `SkipPropsFrom`)
75
+ * - aria-* keys (already typed via `AriaAttributes`)
76
+ * - readonly keys
77
+ * - keys whose value types fall outside `PropValue`
78
+ * - the generic `string` index signature (e.g. `HTMLFormElement[name: string]: any`),
79
+ * which would otherwise shadow every key with an `any`-typed `prop:*`
80
+ */
81
+ export type PropKey<T, K extends keyof T> = K extends keyof SkipPropsFrom
82
+ ? never
83
+ : K extends string
84
+ ? string extends K
85
+ ? never
86
+ : K extends `aria${string}`
87
+ ? never
88
+ : T[K] extends PropValue
89
+ ? IsReadonlyKey<T, K> extends true
90
+ ? never
91
+ : `prop:${K}`
92
+ : never
93
+ : never;