@dom-expressions/runtime 0.50.0-next.15
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/CHANGELOG.md +348 -0
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/package.json +36 -0
- package/src/client.d.ts +117 -0
- package/src/client.js +893 -0
- package/src/constants.d.ts +23 -0
- package/src/constants.js +517 -0
- package/src/jsx-customize.mjs +58 -0
- package/src/jsx-h.d.ts +4148 -0
- package/src/jsx-properties.d.ts +93 -0
- package/src/jsx-update.mjs +96 -0
- package/src/jsx.d.ts +4134 -0
- package/src/reconcile.d.ts +1 -0
- package/src/reconcile.js +149 -0
- package/src/serializer.d.ts +3 -0
- package/src/serializer.js +54 -0
- package/src/server.d.ts +193 -0
- package/src/server.js +1302 -0
- package/src/universal.d.ts +38 -0
- package/src/universal.js +352 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# dom-expressions
|
|
2
|
+
|
|
3
|
+
## 0.50.0-next.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 42ca328: Awaited renderToStream (`then()`, which renderToStringAsync wraps) now waits out blocking promises and re-pulls pending root holes before completing, matching `pipe()`. Previously a render whose only async was a blocked root hole (e.g. `lazy()` or an async component source with no registered fragment) completed immediately with an unfinished shell.
|
|
8
|
+
- ed01d41: Source server `mergeProps` from rxcore like the client and universal entries instead of shipping a local copy. The local merger resolved function sources for key enumeration only — the per-key getter read values off the raw, un-invoked function — so SSR dropped spread props whose source is a function (`<div {...fn()}>`, `<Dynamic {...props}>`; solidjs/solid#2815). Prop-merge semantics belong to the framework core; renderers must now export `mergeProps` from their server rxcore module (the universal entry already required this).
|
|
9
|
+
- df03fb8: Move all packages under the `@dom-expressions` npm scope with new names:
|
|
10
|
+
- `dom-expressions` → `@dom-expressions/runtime`
|
|
11
|
+
- `babel-plugin-jsx-dom-expressions` → `@dom-expressions/babel-plugin-jsx`
|
|
12
|
+
- `jsx-dom-expressions-compiler` → `@dom-expressions/jsx-compiler`
|
|
13
|
+
- `hyper-dom-expressions` → `@dom-expressions/hyperscript`
|
|
14
|
+
- `tagged-jsx-dom-expressions` → `@dom-expressions/tagged-jsx`
|
|
15
|
+
|
|
16
|
+
The old unscoped names stop receiving `next` prereleases and remain in use
|
|
17
|
+
only by the Solid 1.x maintenance line published from `main`.
|
|
18
|
+
|
|
19
|
+
`lit-dom-expressions` is dropped from the prerelease line; it has been
|
|
20
|
+
superseded by `@dom-expressions/tagged-jsx`.
|
|
21
|
+
|
|
22
|
+
`@dom-expressions/jsx-compiler` now distributes prebuilt native binaries
|
|
23
|
+
through per-platform packages (`@dom-expressions/jsx-compiler-darwin-x64`,
|
|
24
|
+
`-darwin-arm64`, `-linux-x64-gnu`, `-linux-arm64-gnu`, `-win32-x64-msvc`)
|
|
25
|
+
resolved automatically via `optionalDependencies`, instead of shipping a
|
|
26
|
+
binary inside the main package.
|
|
27
|
+
|
|
28
|
+
## 0.50.0-next.14
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- 910e5fe: Add `host` option to `insert` for portal-style slots. Top-level nodes managed by the slot are tagged with a live `_$host` getter after each update, replacing proxy-based DOM call interception. The mount parent is now a real element so slot-ownership checks (`parentNode` identity) behave correctly — fixes portal content accumulating on swaps (solidjs/solid#2757) — and tagging covers the `replaceChild`, reconcile, and hydration claim paths the proxy missed.
|
|
33
|
+
- 58284f7: Make `ClassValue` recursive so nested arrays type-check. The runtime already
|
|
34
|
+
flattens arbitrarily nested class arrays (e.g. `class={["a", ["b"]]}`), but the
|
|
35
|
+
type only allowed a single level. `ClassValue` is now `string | number |
|
|
36
|
+
boolean | null | undefined | Record<string, boolean> | ClassValue[]`.
|
|
37
|
+
- a9357a2: Universal renderer `render()` disposers now remove the top-level host nodes they mounted, matching DOM `render()` cleanup semantics. Custom renderers can provide `cleanupNodes(parent, nodes)` to override the default per-node `removeNode` teardown.
|
|
38
|
+
|
|
39
|
+
## 0.50.0-next.13
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- a75a56b: Expose the `ClassValue` type from JSX and lit runtime declarations so consumers can type wrapper props against the supported string, object, and array class forms.
|
|
44
|
+
- 78bb855: Harden DOM-runtime insertion against nodes that have migrated out of their
|
|
45
|
+
original slot between renders. Resolves the class of bugs reported as
|
|
46
|
+
solidjs/solid#2030 (a new JSX value that wraps the previous slot's node) and
|
|
47
|
+
solidjs/solid#2357 (a single node referenced from multiple sibling slots).
|
|
48
|
+
Previously, `cleanChildren` and `reconcileArrays` could either throw
|
|
49
|
+
`replaceChild` "new child contains the parent", or silently destroy the
|
|
50
|
+
migrated node by trusting a stale `current.parentNode === parent` check.
|
|
51
|
+
|
|
52
|
+
Every runtime insertion site (`appendNodes`, `insertExpression`'s
|
|
53
|
+
element-node branch, the replacement path in `cleanChildren`, all four
|
|
54
|
+
insertion sites in `reconcileArrays`) now tags the inserted node with a
|
|
55
|
+
per-slot `$$SLOT` Symbol property carrying the slot's marker. Every
|
|
56
|
+
destructive operation (`remove`, `replaceChild`, `insertBefore` against a
|
|
57
|
+
sibling anchor) is now gated on parent-and-tag ownership: an untagged node
|
|
58
|
+
is treated as unclaimed (the slot may manage it), a tagged node is touched
|
|
59
|
+
only when its tag matches the current slot's marker. Foreign nodes — refs
|
|
60
|
+
appended by user code, nodes that have migrated to another slot, content
|
|
61
|
+
inserted by other runtimes — are left alone.
|
|
62
|
+
|
|
63
|
+
The `tail.nextSibling` `after` anchor in `reconcileArrays` is also gated:
|
|
64
|
+
if `a`'s tail has migrated, the `after` falls back to the slot's marker
|
|
65
|
+
rather than reading a sibling pointer that now points into another slot's
|
|
66
|
+
region. The symmetric end-swap fast-path (`a[0]===b[n-1] && b[0]===a[n-1]`)
|
|
67
|
+
gains an anchor-ownership check so it cannot stage moves against a foreign
|
|
68
|
+
front anchor; mismatched anchors fall through to the map branch which
|
|
69
|
+
re-gates each destructive op.
|
|
70
|
+
|
|
71
|
+
Scope: DOM renderer (`client.js`) only. `universal.js` is intentionally
|
|
72
|
+
unchanged — universal hosts target older JS environments (Chrome 38+),
|
|
73
|
+
expando writes on platform nodes can collide with proxy-based node wrappers,
|
|
74
|
+
and the JSX-DOM-ref migration patterns this fix addresses are not idiomatic
|
|
75
|
+
on non-DOM platforms. If a real case surfaces on a universal renderer it
|
|
76
|
+
can be revisited with a host-appropriate storage strategy.
|
|
77
|
+
|
|
78
|
+
- f1bcd5f: Stop giving special compiler handling to `style:foo` and `class:foo` JSX namespace syntax, and rename the static compiler marker from `@once` to `@static`. `style:foo` and `class:foo` now fall through to literal HTML attributes (e.g. `<div style:border="1px solid black">` emits `style:border` verbatim).
|
|
79
|
+
|
|
80
|
+
Internal optimizations still split `style={{...}}` into `setStyleProperty` calls and `class={{...}}` into `classList.toggle` calls.
|
|
81
|
+
|
|
82
|
+
- f17f7a1: Rename the generated event listener helper from `addEventListener` to `addEvent` so compiled browser bundles no longer introduce a binding that can shadow the native `window.addEventListener` method.
|
|
83
|
+
- a45b224: Dispose the partially-created reactive scope when `render()` (and the universal renderer's `render()`) throws during initial mount. Previously a synchronous throw inside the top-level component would orphan the root and, in the DOM client, leave the delegated-root counter bumped — leaking event-delegation state with no recovery path since the caller never receives the disposer. The throw still propagates; the cleanup just happens before it does. `hydrate()` benefits transitively because it delegates to `render()`.
|
|
84
|
+
|
|
85
|
+
## 0.50.0-next.12
|
|
86
|
+
|
|
87
|
+
### Patch Changes
|
|
88
|
+
|
|
89
|
+
- Port relevant maintenance fixes from the stable branch. Add `omitAttributeSpacing` for strict template attribute spacing, and align `server.js`/`server.d.ts` with the current `client.d.ts` export surface so isomorphic imports continue to resolve on the server.
|
|
90
|
+
- 64e9aee: Delegated events are now owned by render roots instead of the document by default. `render()` installs and disposes its delegated listeners with the root, `delegateEvents()` now only declares event demand, and additional listener containers can be registered explicitly for framework features that render outside the root.
|
|
91
|
+
|
|
92
|
+
## 0.50.0-next.11
|
|
93
|
+
|
|
94
|
+
### Patch Changes
|
|
95
|
+
|
|
96
|
+
- d5cd499: Remove `on:` namespace event support from compiler, runtime, JSX types, and renderer packages.
|
|
97
|
+
|
|
98
|
+
## 0.50.0-next.10
|
|
99
|
+
|
|
100
|
+
### Patch Changes
|
|
101
|
+
|
|
102
|
+
- afbe2ff: Optimize synchronous SSR function holes and plain template array resolution.
|
|
103
|
+
|
|
104
|
+
## 0.50.0-next.9
|
|
105
|
+
|
|
106
|
+
### Patch Changes
|
|
107
|
+
|
|
108
|
+
- d883fad: Schedule `insert()` function-child DOM writes when the parent insert effect is updating an existing slot. This lets async reads inside nested render effects hold the active transition before replacing already-mounted content, and mirrors the fix in the universal renderer.
|
|
109
|
+
|
|
110
|
+
## 0.50.0-next.8
|
|
111
|
+
|
|
112
|
+
### Patch Changes
|
|
113
|
+
|
|
114
|
+
- 858cf13: Fix `ssr()` double-invocation on bail paths.
|
|
115
|
+
|
|
116
|
+
A function hole whose return value walked into the bail branch (e.g., an array containing a NotReady-throwing item) was being invoked twice: once by `tryResolveString` for sync probing and again by the fallback `resolveSSRNode(hole, result)`. For closures that read stateful getters such as JSX `props.children` — whose backing component rebuilds an owner subtree on each access — the duplicate invocation produced a second owner tree with a divergent hydration-key prefix that the client could not claim, surfacing as "Hydration completed with N unclaimed server-rendered node(s)" warnings.
|
|
117
|
+
|
|
118
|
+
`tryResolveString` now evaluates each function node exactly once and threads the evaluated value through the bail object so `ssr()` can hand it to `resolveSSRNode` without re-invoking the original closure.
|
|
119
|
+
|
|
120
|
+
## 0.50.0-next.7
|
|
121
|
+
|
|
122
|
+
### Patch Changes
|
|
123
|
+
|
|
124
|
+
- 0bd165e: Preserve shared class tokens when diffing object keys that contain multiple class names.
|
|
125
|
+
Ensure class-method JSX captures `this` before lifted DOM setup statements run.
|
|
126
|
+
- 2fe6310: Speed up DOM and universal reconcile's symmetric end-swap branch on reorder-heavy
|
|
127
|
+
patterns (e.g. `<For>` reverse / large rotations).
|
|
128
|
+
|
|
129
|
+
The trigger condition is unchanged
|
|
130
|
+
(`a[aStart] === b[bEnd-1] && b[bStart] === a[aEnd-1]`), but the body now
|
|
131
|
+
walks inward against a single stable front anchor (`a[aStart]`) instead
|
|
132
|
+
of issuing two cross-anchored `insertBefore` calls per pair. Each move
|
|
133
|
+
targets the same DOM position so the browser's adjacency cache stays
|
|
134
|
+
warm and per-call native `insertBefore` cost drops sharply. The inner
|
|
135
|
+
loop also continues consuming consecutive symmetric swaps without
|
|
136
|
+
re-entering the outer dispatch.
|
|
137
|
+
|
|
138
|
+
Behaviorally equivalent to the previous implementation: same DOM
|
|
139
|
+
mutation count, same correctness surface, no false-positive widening.
|
|
140
|
+
Validated against `dom-expressions` reconcile tests, the full Solid
|
|
141
|
+
test suite, UIBench `tree/[500]/[reverse]`, and `js-framework-benchmark`
|
|
142
|
+
`05_swap1k`.
|
|
143
|
+
|
|
144
|
+
- 10f3250: SSR: group contiguous attribute and `textContent` closures into a single
|
|
145
|
+
`_$ssrGroup(() => […], N)` call per element so the runtime can resolve
|
|
146
|
+
all `N` hole positions with one closure invocation instead of `N`. The
|
|
147
|
+
compiler walks each top-level element's `templateValues`, identifies
|
|
148
|
+
runs of `≥2` groupable entries (inserts/children break a run, preserving
|
|
149
|
+
child isolation), and replaces them with one grouped declarator repeated
|
|
150
|
+
`N` times in the `ssr(...)` argument list. `_$ssrGroup` tags the
|
|
151
|
+
function with `fn.$g = N` so `ssr()` can dispatch through a fast path
|
|
152
|
+
that's gated at the end of the typeof chain — non-function holes pay
|
|
153
|
+
nothing for the new branch.
|
|
154
|
+
|
|
155
|
+
For the async escalation path (group fn throws `NotReadyError`), every
|
|
156
|
+
retry slot for the group shares a module-scoped cache keyed on `fn`:
|
|
157
|
+
slot 0 evaluates and caches `arr` (success) or `err` (still-pending),
|
|
158
|
+
slots `1..N-1` short-circuit on the cached outcome, and the cache
|
|
159
|
+
invalidates when slot 0 re-fires next pass. Net retry cost: 1 evaluation
|
|
160
|
+
per group per pass on either outcome — `N²` → `N` on success, `N²` → `1`
|
|
161
|
+
on failure — with no per-state bookkeeping.
|
|
162
|
+
|
|
163
|
+
Bench: `+15%` on `search-results` (heavy attribute usage), neutral on
|
|
164
|
+
`color-picker` (no qualifying groups). Hydration ids are unaffected:
|
|
165
|
+
attribute/textContent expressions never allocate ids, and inserts (which
|
|
166
|
+
do) stay outside groups by construction.
|
|
167
|
+
|
|
168
|
+
- 3574228: SSR rendering performance pass.
|
|
169
|
+
|
|
170
|
+
**Runtime (`dom-expressions`):**
|
|
171
|
+
- Inline hole resolution in `ssr()`. Switch from a `(t, ...nodes)` rest
|
|
172
|
+
parameter to an `arguments` walk, eliminating the per-call holes-array
|
|
173
|
+
allocation. Inline `string`/`number`/`null`/`boolean` fast paths skip
|
|
174
|
+
`tryResolveString` for the typical "all-static-after-eval" hole shape; only
|
|
175
|
+
the heavy path (async escalation) materializes the `{ t, h, p }` result.
|
|
176
|
+
- Single forward-pass `escape()`. The previous implementation walked the
|
|
177
|
+
string twice in the hot path (`indexOf(delim)` + `indexOf("&")` upfront
|
|
178
|
+
then early-exit on the no-hit case). Replaced with a `charCodeAt` loop
|
|
179
|
+
that bails after one pass for clean strings (the common case), and
|
|
180
|
+
resumes the slow path from the first hit so the clean prefix isn't
|
|
181
|
+
re-scanned.
|
|
182
|
+
- Remove the `ssrRunInScope` public export. The function had been a true
|
|
183
|
+
pass-through identity (`fn => fn`) since owner-capture moved into
|
|
184
|
+
`tryResolveString`'s `NotReadyError` handler, and the compiler stopped
|
|
185
|
+
emitting it. With no internal callers and no behavior, the export was
|
|
186
|
+
dead surface area. User code that called it can drop the wrap (it was a
|
|
187
|
+
no-op) or replicate the original deferred-callback owner-capture intent
|
|
188
|
+
in two lines with `getOwner()` + `runWithOwner()`.
|
|
189
|
+
|
|
190
|
+
**Compiler (`babel-plugin-jsx-dom-expressions`):**
|
|
191
|
+
- IIFE elision in statement-position JSX. When `<jsx/>` is the argument of
|
|
192
|
+
a `return` or the initializer of a `const` (the overwhelmingly common
|
|
193
|
+
shapes), the surrounding IIFE is removed and the body lifts to flat
|
|
194
|
+
statements before the parent. Saves one closure allocation + one
|
|
195
|
+
function-call frame per render. Applies to `dom`, `ssr`, and `universal`
|
|
196
|
+
emissions; expression-position JSX (ternary branches, array elements,
|
|
197
|
+
function args) keeps the IIFE since lifting would change observable
|
|
198
|
+
evaluation semantics.
|
|
199
|
+
- SSR templates emit hoisted `var` declarations for dynamic-expression temp
|
|
200
|
+
vars instead of wrapping the whole thing in an IIFE. In statement
|
|
201
|
+
position the declarations precede the `ssr(...)` call; in expression
|
|
202
|
+
position they hoist to the enclosing function scope and the
|
|
203
|
+
assignment + call become a comma sequence expression.
|
|
204
|
+
- Drop `ssrRunInScope` emission around dynamic SSR expressions. The
|
|
205
|
+
temp-var hoist stays — it's a V8 IC-stability tactic (keeps the `ssr()`
|
|
206
|
+
call site specialized on `Identifier` argument shapes), not an
|
|
207
|
+
evaluation-order requirement. Ordering is preserved by JS left-to-right
|
|
208
|
+
semantics.
|
|
209
|
+
- Drop `createComponent` wrap on SSR component invocations. The SSR
|
|
210
|
+
runtime's `createComponent` is `Comp(props || {})`; the compiler always
|
|
211
|
+
emits a real `props` object, so the `|| {}` fallback never fires. Inline
|
|
212
|
+
to a direct `Comp(props)` call. DOM / dev modes keep the wrapper since
|
|
213
|
+
it does real work (`untrack`, dev metadata).
|
|
214
|
+
|
|
215
|
+
Net effect on representative SSR shapes (color-picker, search-results) is
|
|
216
|
+
fewer allocations per render and a flatter call graph through the hot path.
|
|
217
|
+
|
|
218
|
+
## 0.50.0-next.6
|
|
219
|
+
|
|
220
|
+
### Patch Changes
|
|
221
|
+
|
|
222
|
+
- f0ca033: Add a build-time JSX declaration customization script for renderer packages.
|
|
223
|
+
|
|
224
|
+
## 0.50.0-next.5
|
|
225
|
+
|
|
226
|
+
### Patch Changes
|
|
227
|
+
|
|
228
|
+
- 4f17771: Fix document-root rendering so lazy memo-owned content remains reactive after `render(..., document)` or `hydrate(..., document)`. Full-document render paths now keep the root JSX tree observed without inserting into `document`, preventing nested content from going stale after later signal updates.
|
|
229
|
+
|
|
230
|
+
## 0.50.0-next.4
|
|
231
|
+
|
|
232
|
+
### Patch Changes
|
|
233
|
+
|
|
234
|
+
- a307ac7: Expose `VoidElements` and `RawTextElements` consistently from every
|
|
235
|
+
runtime entry that already re-exports the other HTML constants. The
|
|
236
|
+
runtime added these sets to `client.js` in `0.50.0-next.3`, but
|
|
237
|
+
`server.js` was missed and the hand-maintained `client.d.ts` /
|
|
238
|
+
`server.d.ts` declaration files didn't pick them up either. Now both
|
|
239
|
+
entries (`dom-expressions/client` and `dom-expressions/server`) and
|
|
240
|
+
their type declarations export the same constant surface, so consumers
|
|
241
|
+
like `@solidjs/web` no longer need to layer their own explicit
|
|
242
|
+
re-exports or copy-script workarounds to surface the symbols.
|
|
243
|
+
|
|
244
|
+
## 0.50.0-next.3
|
|
245
|
+
|
|
246
|
+
### Patch Changes
|
|
247
|
+
|
|
248
|
+
- 816870a: Export `VoidElements` and `RawTextElements` from the runtime constants. These are the standard HTML void-element and raw-text-element sets used by HTML parsers, exposed so downstream tagged-template runtimes (e.g. `sld-dom-expressions`) can consume them without redefining the lists.
|
|
249
|
+
- 4dae801: Normalize the `repository` field in every package to the standard npm
|
|
250
|
+
convention: a `git+https://github.com/ryansolid/dom-expressions.git` URL
|
|
251
|
+
with a `directory` pointing at the package within the monorepo. Restores
|
|
252
|
+
"View source" / "Open in repo" links on the npm registry and unblocks
|
|
253
|
+
tooling that resolves source from package metadata.
|
|
254
|
+
|
|
255
|
+
## 0.50.0-next.2
|
|
256
|
+
|
|
257
|
+
### Patch Changes
|
|
258
|
+
|
|
259
|
+
- d9b571c: Replace the `memo(accessor, true)` wrap in `insert()` with a conditionally
|
|
260
|
+
nested render-effect pattern. The memo wrap fixed `<Show>` siblings
|
|
261
|
+
re-rendering but introduced two regressions: stale reads broke at the memo
|
|
262
|
+
boundary during transitions, and the memo could claim transition ownership
|
|
263
|
+
and strand later synchronous writes in stashed queues (the Sierpinski hover
|
|
264
|
+
freeze).
|
|
265
|
+
|
|
266
|
+
The outer effect now reads `accessor()` with `doNotUnwrap` so function
|
|
267
|
+
children are preserved without subscribing to their internals. When
|
|
268
|
+
function children exist, the outer's compute installs a nested
|
|
269
|
+
render-effect that owns DOM writes for this slot (signalled via an
|
|
270
|
+
`INNER_OWNED` sentinel so the outer's write callback no-ops). Every
|
|
271
|
+
reactive hop on the path is a render-effect with correct stale-value and
|
|
272
|
+
transition-ownership semantics. Same node count as before for
|
|
273
|
+
function-children slots, one fewer for primitive slots.
|
|
274
|
+
|
|
275
|
+
Mirrored into `universal.js`.
|
|
276
|
+
|
|
277
|
+
- 39c207c: Fix a SyntaxError when an element has 222+ merged dynamic attributes
|
|
278
|
+
(solidjs/solid#2682). The internal identifier generator produced `in` at
|
|
279
|
+
index 221, and since these identifiers are emitted as object shorthand
|
|
280
|
+
destructuring bindings, the resulting `({ …, in }) => …` could not be parsed.
|
|
281
|
+
`getNumberedId` now shifts past any natural index that would encode to a JS
|
|
282
|
+
reserved word, keeping the mapping injective and the output at 2 characters
|
|
283
|
+
for all practical dynamic counts.
|
|
284
|
+
- 03da8a5: Fix SSR escaping gaps reachable from JSX, and tighten the compiler so
|
|
285
|
+
redundant runtime `escape` calls drop out of the output.
|
|
286
|
+
|
|
287
|
+
Security fixes:
|
|
288
|
+
- `ssrStyle` and `ssrClassName` now attribute-escape object keys, not
|
|
289
|
+
just values. Previously a user-controlled key in `<div style={{…}} />`
|
|
290
|
+
or `<div class={{…}} />` could break out of the surrounding attribute.
|
|
291
|
+
- Dynamic fragment-child expressions (`<>{state.text}</>`) now compile
|
|
292
|
+
to `_$memo(() => _$escape(expr))`. Element-child expressions already
|
|
293
|
+
escaped via `escapeExpression`; fragment children reached SSR through
|
|
294
|
+
a separate path and were concatenated raw.
|
|
295
|
+
- Computed-key object styles (`style={{ [k]: v }}`) escape the key at
|
|
296
|
+
compile time.
|
|
297
|
+
|
|
298
|
+
Compiler alignment:
|
|
299
|
+
- SSR now matches DOM in rejecting fragments placed directly inside an
|
|
300
|
+
element: `<div><>…</></div>` is a compile error in both renderers.
|
|
301
|
+
Fragments reached via conditionals (`<div>{cond && <>…</>}</div>`)
|
|
302
|
+
remain legal.
|
|
303
|
+
|
|
304
|
+
Compiler optimizations:
|
|
305
|
+
- `escapeExpression` drops the outer `_$escape` wrap on a `JSXFragment`
|
|
306
|
+
when its single significant child is either a dynamic expression
|
|
307
|
+
(compiles to a memoized accessor function, `escape(fn)` is a no-op)
|
|
308
|
+
or a native element (compiles to an `_$ssr(…)` SSR node object,
|
|
309
|
+
`escape(object)` is a no-op). This turns
|
|
310
|
+
`cond && _$escape(_$memo(() => _$escape(state.text)))` into
|
|
311
|
+
`cond && _$memo(() => _$escape(state.text))`, and
|
|
312
|
+
`cond && _$escape(_$ssr(_tmpl$N))` into `cond && _$ssr(_tmpl$N)`.
|
|
313
|
+
|
|
314
|
+
SSR fixtures for `components`, `conditionalExpressions`, `fragments`,
|
|
315
|
+
and `attributeExpressions` regenerate. Each security fix has a JSX
|
|
316
|
+
round-trip test in `packages/dom-expressions/test/ssr/jsx.spec.jsx`
|
|
317
|
+
that feeds hostile input through `renderToString`.
|
|
318
|
+
|
|
319
|
+
- 305d9ce: - SSR: Duplicate attributes in JSX without spreads are now deduplicated —
|
|
320
|
+
`<div class="a" class="b" />` correctly renders as `<div class="b" />`
|
|
321
|
+
(last-wins), matching client behavior. Previously the compiler kept both
|
|
322
|
+
attributes in the output.
|
|
323
|
+
- Client: `setAttributeNS` / `removeAttributeNS` now use matching names when
|
|
324
|
+
clearing namespaced attributes (e.g. `xlink:href`). Previously removal could
|
|
325
|
+
leave the attribute in place because it used the local name while the set
|
|
326
|
+
used the qualified name.
|
|
327
|
+
- Expanded test coverage across all four packages; no other behavior changes.
|
|
328
|
+
|
|
329
|
+
## 0.50.0-next.1
|
|
330
|
+
|
|
331
|
+
### Patch Changes
|
|
332
|
+
|
|
333
|
+
- ee365e0: - `insert()` accepts an optional 5th `options` argument that is forwarded to the
|
|
334
|
+
internal `effect()` call, letting callers (e.g. Solid's `render()`) opt into
|
|
335
|
+
transition-aware initial mounts without otherwise changing `insert`'s
|
|
336
|
+
behavior.
|
|
337
|
+
- SSR: `$dflj(ids)` now materializes every id in the list in a single call
|
|
338
|
+
instead of stopping after the first successful `$dfl`. Callers pass only the
|
|
339
|
+
keys they intend to materialize, which simplifies the primitive and composes
|
|
340
|
+
cleanly for bulk-uncollapse cases (e.g. a group activation revealing several
|
|
341
|
+
held fallbacks at once).
|
|
342
|
+
- SSR: Fix cascading async root holes in the streaming shell. When an inner
|
|
343
|
+
Loading boundary resolved its first chunk while the outer shell was still
|
|
344
|
+
pending, `flushEnd` could call `serializer.flush()` before `doShell()` had
|
|
345
|
+
written the root `_assets` module map, causing seroval to silently drop the
|
|
346
|
+
writes and client hydration to fail with "module was not preloaded". Root
|
|
347
|
+
asset serialization is now memoized and gated on both paths.
|
|
348
|
+
- Type formatting cleanup in `jsx-properties.d.ts`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018-2019 Ryan Carniato
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# DOM Expressions
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ryansolid/dom-expressions/actions/workflows/main-ci.yml)
|
|
4
|
+
[](https://coveralls.io/github/ryansolid/dom-expressions?branch=main)
|
|
5
|
+
[](https://www.npmjs.com/package/dom-expressions)
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
[](https://gitter.im/dom-expressions/community)
|
|
9
|
+
|
|
10
|
+
DOM Expressions is a Rendering Runtime for reactive libraries that do fine grained change detection. These libraries rely on concepts like Observables and Signals rather than Lifecycle functions and the Virtual DOM. Standard JSX transformers are not helpful to these libraries as they need to evaluate their expressions in isolation to avoid re-rendering unnecessary parts of the DOM.
|
|
11
|
+
|
|
12
|
+
This package wraps libraries like KnockoutJS or MobX and use them independent of their current render systems using a small library to render pure DOM expressions. This approach has been proven to be incredibly fast, dominating the highest rankings in the [JS Framework Benchmark](https://github.com/krausest/js-framework-benchmark).
|
|
13
|
+
|
|
14
|
+
It is designed to be used with a companion render API. Currently there is a JSX Babel Plugin, and Tagged Template Literals, and HyperScript runtime APIs. Most developers will not use this package directly. It is intended to help author your own Reactive Libraries and not to be used directly in projects.
|
|
15
|
+
|
|
16
|
+
## Example Implementations
|
|
17
|
+
|
|
18
|
+
- [Solid](https://github.com/ryansolid/solid): A declarative JavaScript library for building user interfaces.
|
|
19
|
+
- [mobx-jsx](https://github.com/ryansolid/mobx-jsx): Ever wondered how much more performant MobX is without React? A lot.
|
|
20
|
+
- [vuerx-jsx](https://github.com/ryansolid/vuerx-jsx): Ever wondered how much more performant Vue is without Vue? ...renderer built on @vue/reactivity
|
|
21
|
+
- [ko-jsx](https://github.com/ryansolid/ko-jsx): Knockout JS with JSX rendering.
|
|
22
|
+
- [s-jsx](https://github.com/ryansolid/s-jsx): Testbed for trying new techniques in the fine grained space.
|
|
23
|
+
|
|
24
|
+
## Runtime Generator
|
|
25
|
+
|
|
26
|
+
Dom Expressions is designed to allow you to create a runtime to be tree shakeable. It does that by using "babel-plugin-transform-rename-import" to rename the import to your reactive core file. Setup the babel plugin and then `export * from "dom-expressions/src/runtime"`from your runtime. Be sure to not exclude the dom-expressions node_module.
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
{
|
|
30
|
+
plugins: [
|
|
31
|
+
[
|
|
32
|
+
"babel-plugin-transform-rename-import",
|
|
33
|
+
{
|
|
34
|
+
original: "rxcore",
|
|
35
|
+
replacement: "../src/core"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
What is the reactive core file. It exports an object with the methods required by the runtime.
|
|
43
|
+
Example:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { untrack } from "@solidjs/signals";
|
|
47
|
+
|
|
48
|
+
export const sharedConfig = {};
|
|
49
|
+
|
|
50
|
+
export function createComponent(Comp, props) {
|
|
51
|
+
return untrack(() => Comp(props));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export {
|
|
55
|
+
createRoot as root,
|
|
56
|
+
createRenderEffect as effect,
|
|
57
|
+
createMemo as memo,
|
|
58
|
+
getOwner,
|
|
59
|
+
untrack,
|
|
60
|
+
merge as mergeProps
|
|
61
|
+
} from "@solidjs/signals";
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Runtime Renderers
|
|
65
|
+
|
|
66
|
+
Once you have generated a runtime it can be used with companion render APIs:
|
|
67
|
+
|
|
68
|
+
### JSX
|
|
69
|
+
|
|
70
|
+
[Babel Plugin JSX DOM Expressions](https://github.com/ryansolid/dom-expressions/blob/main/packages/babel-plugin-jsx) is by far the best way to use this library. Pre-compilation lends to the best performance since the whole template can be analyzed and optimal compiled into the most performant JavaScript. This allows for not only the most performant code, but the cleanest and the smallest.
|
|
71
|
+
|
|
72
|
+
### Tagged Template
|
|
73
|
+
|
|
74
|
+
If precompilation is not an option Tagged Template Literals are the next best thing. [Tagged JSX DOM Expressions](https://github.com/ryansolid/dom-expressions/blob/main/packages/tagged-jsx) provides a similar experience to the JSX, parsing JSX-shaped templates at runtime and caching the compiled result on first run. It keeps most of the performance and syntax of the compiled JSX version with no build step.
|
|
75
|
+
|
|
76
|
+
### HyperScript
|
|
77
|
+
|
|
78
|
+
While not as performant as the other options this library provides a mechanism to expose a HyperScript version. [Hyper DOM Expressions](https://github.com/ryansolid/dom-expressions/blob/main/packages/hyperscript) offers the greatest flexibility working with existing tooling for HyperScript and enables pure JS DSLs.
|
|
79
|
+
|
|
80
|
+
## Work in Progress
|
|
81
|
+
|
|
82
|
+
This is still a work in progress. My goal here is to better understand and generalize this approach to provide non Virtual DOM alternatives to developing web applications.
|
|
83
|
+
|
|
84
|
+
## Updating JSX Types
|
|
85
|
+
|
|
86
|
+
JSX types are located in `packages/runtime/src/jsx-h.d.ts`.
|
|
87
|
+
|
|
88
|
+
To update JSX types:
|
|
89
|
+
|
|
90
|
+
1. modify/update `jsx-h.d.ts`
|
|
91
|
+
2. run `npm run jsx-sync-types`
|
|
92
|
+
|
|
93
|
+
NOTE: The file `jsx.d.ts` is autogenerated taking `jsx-h.d.ts` as source. Changes to `jsx.d.ts` will be discarded!
|
|
94
|
+
|
|
95
|
+
### Customizing JSX Element Types
|
|
96
|
+
|
|
97
|
+
Renderer packages that vendor these JSX declarations can customize `JSX.Element` at build time with `dom-expressions-jsx-types`.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
dom-expressions-jsx-types \
|
|
101
|
+
--input ./src/jsx.d.ts \
|
|
102
|
+
--element "SolidElement | Node | ArrayElement" \
|
|
103
|
+
--import 'import type { Element as SolidElement } from "solid-js";'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The command updates the declaration file in place. Use `--output` to write a separate file. Each `--import` statement is inserted after the local `jsx-properties.js` import and de-duplicated on repeated runs.
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dom-expressions/runtime",
|
|
3
|
+
"description": "A Fine-Grained Runtime for Performant DOM Rendering",
|
|
4
|
+
"version": "0.50.0-next.15",
|
|
5
|
+
"author": "Ryan Carniato",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/ryansolid/dom-expressions.git",
|
|
10
|
+
"directory": "packages/runtime"
|
|
11
|
+
},
|
|
12
|
+
"readmeFilename": "README.md",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"bin": {
|
|
15
|
+
"dom-expressions-jsx-types": "./src/jsx-customize.mjs"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"babel-plugin-transform-rename-import": "^2.3.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"csstype": "^3.0",
|
|
22
|
+
"seroval": "~1.5.0",
|
|
23
|
+
"seroval-plugins": "~1.5.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"csstype": "^3.1",
|
|
27
|
+
"seroval": "~1.5.0",
|
|
28
|
+
"seroval-plugins": "~1.5.0",
|
|
29
|
+
"@dom-expressions/babel-plugin-jsx": "0.50.0-next.15"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "jest --no-cache",
|
|
33
|
+
"test:coverage": "jest --coverage --no-cache",
|
|
34
|
+
"report:coverage": "cat ./coverage/lcov.info | ../../node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/client.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { JSX } from "./jsx.js";
|
|
2
|
+
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
|
|
3
|
+
export const ChildProperties: Set<string>;
|
|
4
|
+
export const DelegatedEvents: Set<string>;
|
|
5
|
+
export const DOMElements: Set<string>;
|
|
6
|
+
export const SVGElements: Set<string>;
|
|
7
|
+
export const MathMLElements: Set<string>;
|
|
8
|
+
export const VoidElements: Set<string>;
|
|
9
|
+
export const RawTextElements: Set<string>;
|
|
10
|
+
export const Namespaces: Record<string, string>;
|
|
11
|
+
|
|
12
|
+
type MountableElement = Element | Document | ShadowRoot | DocumentFragment | Node;
|
|
13
|
+
export function render(
|
|
14
|
+
code: () => JSX.Element,
|
|
15
|
+
element: MountableElement,
|
|
16
|
+
init?: JSX.Element,
|
|
17
|
+
options?: { owner?: unknown }
|
|
18
|
+
): () => void;
|
|
19
|
+
/**
|
|
20
|
+
* @param flag
|
|
21
|
+
* - `undefined` — clone the template as-is (uses `cloneNode`).
|
|
22
|
+
* - `1` — use `document.importNode` instead of `cloneNode`.
|
|
23
|
+
* - `2` — the template html is wrapped; the outer tag is stripped at clone time.
|
|
24
|
+
*/
|
|
25
|
+
export function template(html: string, flag?: 1 | 2): () => Element;
|
|
26
|
+
export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
|
|
27
|
+
export function memo<T>(fn: () => T, equal: boolean): () => T;
|
|
28
|
+
export function untrack<T>(fn: () => T): T;
|
|
29
|
+
export function insert<T>(
|
|
30
|
+
parent: MountableElement,
|
|
31
|
+
accessor: (() => T) | T,
|
|
32
|
+
marker?: Node | null,
|
|
33
|
+
init?: JSX.Element,
|
|
34
|
+
options?: {
|
|
35
|
+
/**
|
|
36
|
+
* Live accessor for the slot's logical host in the source tree (portals).
|
|
37
|
+
* Each top-level node the slot manages is tagged with a `_$host` getter
|
|
38
|
+
* backed by this accessor so delegated events retarget correctly.
|
|
39
|
+
*/
|
|
40
|
+
host?: () => Node | null;
|
|
41
|
+
/** Defer the insert effect to the queue instead of running it inline. */
|
|
42
|
+
schedule?: boolean;
|
|
43
|
+
}
|
|
44
|
+
): JSX.Element;
|
|
45
|
+
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
|
|
46
|
+
export function delegateEvents(eventNames: string[]): void;
|
|
47
|
+
export function registerDelegatedRoot(root: MountableElement): void;
|
|
48
|
+
export function unregisterDelegatedRoot(root: MountableElement): void;
|
|
49
|
+
export function registerDelegatedContainer(
|
|
50
|
+
container: MountableElement,
|
|
51
|
+
owner?: MountableElement
|
|
52
|
+
): void;
|
|
53
|
+
export function unregisterDelegatedContainer(
|
|
54
|
+
container: MountableElement,
|
|
55
|
+
owner?: MountableElement
|
|
56
|
+
): void;
|
|
57
|
+
export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
|
|
58
|
+
export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
|
|
59
|
+
export function assign(
|
|
60
|
+
node: Element,
|
|
61
|
+
props: any,
|
|
62
|
+
skipChildren?: Boolean,
|
|
63
|
+
prevProps?: any,
|
|
64
|
+
skipRef?: Boolean
|
|
65
|
+
): void;
|
|
66
|
+
export function setAttribute(node: Element, name: string, value: string): void;
|
|
67
|
+
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
68
|
+
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
|
|
69
|
+
export function setProperty(node: Element, name: string, value: any): void;
|
|
70
|
+
export function setStyleProperty(node: Element, name: string, value: any): void;
|
|
71
|
+
export function addEvent(
|
|
72
|
+
node: Element,
|
|
73
|
+
name: string,
|
|
74
|
+
handler: EventListener | EventListenerObject | (EventListenerObject & AddEventListenerOptions),
|
|
75
|
+
delegate: boolean
|
|
76
|
+
): void;
|
|
77
|
+
export function style(
|
|
78
|
+
node: Element,
|
|
79
|
+
value: { [k: string]: string },
|
|
80
|
+
prev?: { [k: string]: string }
|
|
81
|
+
): void;
|
|
82
|
+
export function getOwner(): unknown;
|
|
83
|
+
export function mergeProps(...sources: unknown[]): unknown;
|
|
84
|
+
export function dynamicProperty(props: unknown, key: string): unknown;
|
|
85
|
+
export function applyRef(
|
|
86
|
+
r: ((element: Element) => void) | ((element: Element) => void)[],
|
|
87
|
+
element: Element
|
|
88
|
+
): void;
|
|
89
|
+
export function ref(
|
|
90
|
+
fn: () => ((element: Element) => void) | ((element: Element) => void)[],
|
|
91
|
+
element: Element
|
|
92
|
+
): void;
|
|
93
|
+
|
|
94
|
+
export function hydrate(
|
|
95
|
+
fn: () => JSX.Element,
|
|
96
|
+
node: MountableElement,
|
|
97
|
+
options?: { renderId?: string; owner?: unknown }
|
|
98
|
+
): () => void;
|
|
99
|
+
export function getHydrationKey(): string | undefined;
|
|
100
|
+
export function getNextElement(template?: () => Element): Element;
|
|
101
|
+
export function getNextMatch(start: Node, elementName: string): Element;
|
|
102
|
+
export function getNextMarker(start: Node): [Node, Array<Node>];
|
|
103
|
+
export function useAssets(fn: () => JSX.Element): void;
|
|
104
|
+
export function getAssets(): string;
|
|
105
|
+
export function HydrationScript(props?: { nonce?: string; eventNames?: string[] }): JSX.Element;
|
|
106
|
+
export function generateHydrationScript(options?: {
|
|
107
|
+
nonce?: string;
|
|
108
|
+
eventNames?: string[];
|
|
109
|
+
}): string;
|
|
110
|
+
export function Assets(props: { children?: JSX.Element }): JSX.Element;
|
|
111
|
+
export interface RequestEvent {
|
|
112
|
+
request: Request;
|
|
113
|
+
locals: Record<string | number | symbol, any>;
|
|
114
|
+
}
|
|
115
|
+
export declare const RequestContext: unique symbol;
|
|
116
|
+
export function getRequestEvent(): RequestEvent | undefined;
|
|
117
|
+
export function runHydrationEvents(): void;
|