@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 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
+ [![Build Status](https://github.com/ryansolid/dom-expressions/workflows/DOMExpressions%20CI/badge.svg)](https://github.com/ryansolid/dom-expressions/actions/workflows/main-ci.yml)
4
+ [![Coverage Status](https://img.shields.io/coveralls/github/ryansolid/dom-expressions.svg?style=flat)](https://coveralls.io/github/ryansolid/dom-expressions?branch=main)
5
+ [![NPM Version](https://img.shields.io/npm/v/dom-expressions.svg?style=flat)](https://www.npmjs.com/package/dom-expressions)
6
+ ![](https://img.shields.io/bundlephobia/minzip/dom-expressions.svg?style=flat)
7
+ ![](https://img.shields.io/npm/dt/dom-expressions.svg?style=flat)
8
+ [![Gitter](https://img.shields.io/gitter/room/dom-expressions/community)](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
+ }
@@ -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;