@solidjs/babel-plugin 2.0.0-rc.10
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/.turbo/turbo-build.log +10 -0
- package/CHANGELOG.md +127 -0
- package/LICENSE +21 -0
- package/README.md +386 -0
- package/index.js +16741 -0
- package/package.json +46 -0
- package/tsconfig.json +12 -0
- package/vite.config.mjs +13 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
$ rollup -c --bundleConfigAsCjs
|
|
2
|
+
[36m
|
|
3
|
+
[1msrc/index.ts[22m β [1mindex.js[22m...[39m
|
|
4
|
+
[1m[33m(!) Circular dependencies[39m[22m
|
|
5
|
+
src/shared/transform.ts -> src/dom/element.ts -> src/shared/transform.ts
|
|
6
|
+
src/shared/transform.ts -> src/dom/element.ts -> src/universal/element.ts -> src/shared/transform.ts
|
|
7
|
+
src/shared/transform.ts -> src/ssr/element.ts -> src/shared/transform.ts
|
|
8
|
+
src/shared/transform.ts -> src/shared/component.ts -> src/shared/transform.ts
|
|
9
|
+
src/shared/transform.ts -> src/shared/fragment.ts -> src/shared/transform.ts
|
|
10
|
+
[32mcreated [1mindex.js[22m in [1m2.8s[22m[39m
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @solidjs/babel-plugin
|
|
2
|
+
|
|
3
|
+
## 2.0.0-rc.10
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fd36d37: `sourceNames.bindings`: compiled binding effects are named by what they write. An attribute effect gets `<tag>.<attribute>` as written (`span.textContent`, `div.class:active`, `div.style:color`; a template's merged effect lists all of its bindings), a hole's insert is named for the parent it fills (`div.children`), and a spread passes its tag so the runtime labels its attribute effect `div.spread` and its children insert `div.children`. The names travel as a trailing options argument on `effect`/`insert` (`{ name }`) and a trailing string on `spread`; `@solidjs/web`'s `effect`, `insert`, and `spread` accept them and put them on the render effect nodes, where the dev and observe tiers show them in owner paths, attribution chains, and the Propagation track β a binding effect reads `span.textContent β count` instead of `effect β count`. Production ignores the names; output with the option off is unchanged. DOM output only; `sourceNames: true` turns it on with `components`.
|
|
8
|
+
- fd36d37: Rename the compilers' `componentNames` option to `sourceNames`, now `boolean | { components?: boolean }`. `sourceNames: true` (or `{ components: true }`) is what `componentNames: true` was β the tag as written in source as `createComponent`'s third argument, on DOM and SSR output. The option is now the home for every kind of source name the compilers can carry into output for the dev and observe runtimes to label the reactive graph with (binding effects and primitives follow); the object form picks kinds. `@solidjs/compiler` rejects `componentNames` as an unknown option, so a stale `@solidjs/vite-plugin` fails loudly rather than compiling without labels.
|
|
9
|
+
- 45407d1: JSX passed through a prop other than `children` (a layout's `header`, an object of slots, a context value) now hydrates when another id-allocating hole follows it (#3567). Both compilers' hole-scope predicate treated only `props.children` as able to build hydratable content, so a `{props.header}` hole compiled to a transparent insert: the server reserved the scoped sibling's slot at registration and gave the header's elements the next id, while the client allocated in source order, and every key after the slot shifted. The predicate is now a denylist: a dynamic hole takes a scope unless its value is provably a primitive (literals, template literals, unary, binary and update expressions), so property reads, optional calls, the left operand of `||`/`??`, sequence and assignment expressions, and array literals with hydratable entries are all scoped. Ids of elements after such a hole shift by one slot on both sides.
|
|
10
|
+
- 6717d35: `sourceNames` follows `dev` in both JSX compilers. Unset, every kind (`components`, `bindings`) is on when `dev: true` and off otherwise; `true`/`false` sets every kind (`sourceNames: false` opts a dev build out); the object form picks, and each kind it leaves unspecified follows `dev` (previously `false`). Production output (`dev: false`) is byte-identical with the option set or unset. `@solidjs/babel-plugin`'s `PluginConfig.sourceNames` is now optional. Dev builds label components (`createComponent(Home, props, "Home")`) and binding effects (`span.children`) out of the box, so diagnostics and the Performance panel read as source without build-tool configuration.
|
|
11
|
+
|
|
12
|
+
Primitive naming (`createSignal(0, { name: "count" })`) stays with `@solidjs/compiler`'s standalone `transformSourceNames` pass, which the build tool runs on every module independently of the JSX compiler; it is not a JSX-transform kind, and the READMEs and RFC now say so.
|
|
13
|
+
|
|
14
|
+
- 1735074: SSR: a component's props literal with getters compiles to a module-level constructor with shared getters (`hoistProps`, default on) instead of an object literal, which V8 builds in dictionary mode with a closure per getter per instance. Same own keys, order, descriptors and prototype; a props getter is now defined only for a read through its own object β copying its descriptor elsewhere throws (dev names the rule). Sites closing over a reassigned or later-declared binding, `this`, or `arguments` keep the literal. Babel and the native compiler emit the same output.
|
|
15
|
+
- e10a4ba: SSR: attributes written after an element's last spread are markup, not a source. `<li {...rest} class="row" data-id={id}>` compiles to `ssrElement("li", rest, β¦, _sk$, () => ' class="row"' + ssrElementAttribute("data-id", id))` β statics written as the template path writes them, dynamics through `ssrElementAttribute` (the spread walk's rules for one key), the whole string a literal when every part is static β with a hoisted skip predicate (`var _sk$ = k => k === "class" || k === "data-id"`, one per key set) so the spread's own copies of those keys are never read or emitted. No later source can override a trailing attribute, so the output is unchanged; the thunk runs where the trailing source's getters used to be read, so hydration ids keep their order. What goes away per element is the trailing object literal (with getters, a dictionary-mode object and a closure per getter), its key list, and the precedence check on every key of the spread β an element whose only other source is the spread now serializes from one plain object. Attributes before a spread stay a source, and a source literal with getters (`<li data-id={id} class="row" {...rest}>`) now compiles to the same hoisted constructor as a component's props (`hoistProps`). Spread-static-tail server bench, 500 rows: statics after the spread 1.31Γ; a dynamic and a static after the spread 1.42Γ (1.22Γ from the hoisted source form, 1.17Γ from the thunk on top); the same attributes before the spread 1.21Γ. Child properties (`innerHTML`, `textContent`, `children`), a textarea's `value`, reserved namespaces and JSX-valued attributes stay sources.
|
|
16
|
+
- 9adf007: Escape static text on SSR spread elements (#3557)
|
|
17
|
+
- Static text children of a spread element (`<div {...props}>a <b></div>`) are now HTML-escaped at compile time in both compilers, matching the template path; `script` and `style` children stay raw.
|
|
18
|
+
- A static textarea `value` folded into the template (`<textarea value="a <b>" />`) is escaped in both compilers.
|
|
19
|
+
- The Babel plugin no longer folds a textarea `value` into children on an element that carries a spread, so the value stays a prop and precedence follows JSX source order, matching the native compiler.
|
|
20
|
+
- Static string props on the Babel spread paths are rebuilt from their decoded value, so `<div {...props} title="a & b" />` no longer double-encodes entities.
|
|
21
|
+
|
|
22
|
+
## 2.0.0-rc.9
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 8d6de07: Native elements with several spread sources compile to the runtimes' array form instead of a `mergeProps()` call, in DOM and SSR output: `<div id="x" {...a} {...b}>` becomes `spread(el, [{ id: "x" }, a, b], β¦)` on the client and `ssrElement("div", [{ id: "x" }, a, b], β¦)` on the server. The runtimes read the sources directly β later sources win per key, only the winning source is read β with no merge proxy to build and walk, and a reactive spread is a plain thunk called inside the tracking scope, so it mints no memo and consumes no hydration id on either side (the hydratable SSR `() => mergeProps(β¦)` wrapper is gone for the same reason). A lone spread still passes straight through (#3105). Requires `@solidjs/web` with the `spread`/`ssrElement` array forms (#3418, #3419).
|
|
27
|
+
- 6d2bdeb: Move delegated event handlers off the `$$<type>` element key Solid 1 uses.
|
|
28
|
+
|
|
29
|
+
Solid 1 delegates from `document` and fires any `$$click`/`$$input`/β¦ it finds while walking up from the target, so a 1.x runtime on the same page β an older embedded widget, a devtools panel built on 1.x β ran every delegated handler in a 2.x app a second time. Compiled output and the runtime now stamp `_$$<type>` / `_$$<type>Data` instead; neither version can see the other's handlers, in either nesting direction.
|
|
30
|
+
|
|
31
|
+
The key, the `_$SOLID_EVENT_OWNER` mark, and the walk rules are documented in `client.ts` as the delegated-event wire contract shared by every Solid copy on a page. Anything reading `el.$$click` directly must switch to `el._$$click`.
|
|
32
|
+
|
|
33
|
+
- 246eeeb: Emit non-identifier getter keys in compiled props literals as string literals (`get "aria-label"() {}`) instead of computed keys (`get ["aria-label"]() {}`). Same property, but a computed key drops the whole object literal off V8's boilerplate path into per-property runtime definition; on a seven-getter props literal the computed form costs ~45% more to build. Applies to every getter site in both compilers: component props, dynamic element attributes (DOM, SSR, universal).
|
|
34
|
+
- 63560a1: `componentNames` now applies to SSR output. Under the option both compilers keep the `createComponent` call they otherwise inline to `Comp(props)` and pass the source tag name β `createComponent(Comp, props, "Comp")` β so the server runtime's observe/dev `createComponent` labels the owner and a server finding's `ownerPath` reads `<App> βΊ <Page>` like the client's. Without the option (prod builds) SSR output is unchanged. `@solidjs/vite-plugin` already passes the option for its dev and observe postures, so app server builds pick this up with no config change.
|
|
35
|
+
|
|
36
|
+
Fixes `ssrScope` under transparent owners: the virtual hole scope swapped the current owner's id counter, but content inside a hole resolves ids by walking past transparent owners, so with one in between (the server-component scope owner; now the labelled component owner) the hole's content took ids from the enclosing counter and disagreed with the client. The scope now swaps the nearest id-bearing owner.
|
|
37
|
+
|
|
38
|
+
- 350f65f: TSRX: `@for β¦ index/key` items and `@catch` errors pass through to `For` / `Errored` as the accessors Solid hands out; the compilers no longer rewrite reads of those bindings into calls (#3474). Author `item()`, `i()` (under a custom key), and `err()` exactly as in JSX. A destructuring pattern in one of those positions is rejected with a diagnostic, since there is nothing to destructure β the default keyed `@for` item is still a raw value and still destructures. `projectTsrxForTypecheck` emits the same `(item, i) =>` / `(err, reset) =>` arrows `@tsrx/solid` does, so the two typecheck projections now agree.
|
|
39
|
+
- 1643d2a: The universal renderer's `spread()` follows the `@solidjs/web` contract (#3388): `ref` folds into the props effect and is re-applied only when its identity changes (refs run with no owner, so nothing they create is disposed by the fold); children keep their own owned `insert` β that effect owns the child subtree β but a plain object whose `children` is a data property inserts the value with no effect at all. Three reactive nodes become two when children flow through the spread, one when they don't. `spread` also resolves a lone function source inside its own tracking scopes and accepts an array of sources β `spread(node, [a, b], skipChildren)` β the union of their keys with later sources winning, only the winning source read, function sources called inline with no merge and no memo. Both compilers' universal output uses it: a lone spread passes straight through (reactive included, no more `mergeProps(() => β¦)`), and several sources compile to the array instead of a `mergeProps()` call.
|
|
40
|
+
|
|
41
|
+
## 2.0.0-rc.8
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- 01ac18c: Compiler `componentNames` option: component owner labels that survive minification. With the flag on, DOM output carries the tag as written in source as a third `createComponent` argument β `<Home />` compiles to `createComponent(Home, props, "Home")`, `<Ui.Button />` to `"Ui.Button"`, `<this.Row />` to `"this.Row"` β and the dev and observe runtimes label the component's owner with it (`<Home>` in diagnostic `ownerPath`s, attribution chains, and the devtools `_component.name`), falling back to `Comp.name` as before. Until now an observe-tier production bundle reported hot scopes and holds under whatever the minifier left of the function name (`<Xt> βΊ <Kn>`), and a `lazy()` or HMR wrapper hid the tag name even in dev. Off by default and byte-identical output when off; SSR (which inlines the call) and universal output never emit it; the production `createComponent` ignores the argument. Both compilers implement it in parity (shared fixtures, cross-mode ratchet). `@solidjs/vite-plugin` enables it for the dev and `observe` postures.
|
|
46
|
+
- 7d985b6: Fix SSR XSS: strings yielded by flow-control memos rendered unescaped
|
|
47
|
+
|
|
48
|
+
`<Show when={s}>{s}</Show>`, `<For>{v => v}</For>`, `<Dynamic component={() => s} />`,
|
|
49
|
+
`<Switch>/<Match>`, boundary fallbacks and any component that returns a string through a
|
|
50
|
+
memo rendered that string raw on the server. The server flow controls return memos for
|
|
51
|
+
hydration-id alignment; `escape()` passed functions through by identity, and the resolver
|
|
52
|
+
appended whatever they later produced without escaping.
|
|
53
|
+
|
|
54
|
+
One rule now: `escape(x)` at a hole covers everything reachable from `x` β strings, array
|
|
55
|
+
items, and what a function yields when the resolver calls it (a deferred-escape wrapper).
|
|
56
|
+
Finished `{ t }` nodes pass through. `Loading` escapes its content the way it already
|
|
57
|
+
escaped its fallback. The compilers stop wrapping fragment / mixed component children in
|
|
58
|
+
`_$escape` (they are values; escaping them too double-escaped through
|
|
59
|
+
`<Comp>{props.children}</Comp>`), and a single-expression fragment at a hole keeps the
|
|
60
|
+
hole's wrap. Live-hole tags ride the wrapper and `$slot` survives the array copy, so
|
|
61
|
+
frames behave as before.
|
|
62
|
+
|
|
63
|
+
- ab4c40c: Object-valued `style` / `class` bindings are read in the TRACKED half of their effect. `style()` and `className()` enumerate their object in the effect's untracked commit phase, so a proxy value β a store sub-object (`style={state.style}`, `class={row.classes}`), merged props, anything arriving through a spread β was identity-reactive only: in-place key mutations never re-applied, and every leaf read tripped `STRICT_READ_UNTRACKED` in dev. Both compilers now wrap the compute value of a non-inline `style={expr}` / `class={expr}` in a new compiler primitive, `readShallow()`, and `spread()` applies it to those two keys as it copies. `readShallow` is an identity passthrough for strings, plain objects and proxy-free arrays (a fresh literal is already the compute's own β the common case pays a `typeof`); a proxy is copied with one `ownKeys` trap (its own trap keeps the key set tracked) plus one tracked read per key; arrays are re-mapped only when an element is a proxy. Inline literals are untouched β they already compile per property. Provably-string expressions (string/template literals, concatenation) and literal objects/arrays skip the wrap at compile time. New Tier-1 bench `style-class-object`: plain-object rows at parity; store-backed rows go from identity-only (and, in dev, ~97 ms per 500 elements of diagnostics) to per-key reactive at ~3.5 ms. Octane svg-dashboard (prod build, store-backed style/attrs through spread): mount at parity, style_spread_pulse β6%, select_toggle β7%.
|
|
64
|
+
|
|
65
|
+
`spread()` shares the same enumeration: its compute half copied the source with `forβ¦in` + `hasOwn`, which on a proxy source (`merge()`/`omit()`, `{...props}` in a component, store records β nearly every spread) is an `ownKeys` trap plus two `getOwnPropertyDescriptor` traps per key, each allocating a descriptor and a getter closure. It now takes the key set from one `Reflect.ownKeys` trap (the trap keeps the key set tracked) and reads each string key once; plain sources use `Object.keys`, the exact own-enumerable set the old loop yielded. New Tier-1 bench `spread-enumerate` (500 elements, 8 keys): `merge(static, reactive)` 376 β 537 ops/s (+43%), store record 253 β 415 ops/s (+64%), plain object at parity.
|
|
66
|
+
|
|
67
|
+
## 2.0.0-rc.7
|
|
68
|
+
|
|
69
|
+
### Patch Changes
|
|
70
|
+
|
|
71
|
+
- ead7b1a: Keep hydration IDs aligned when an intrinsic element has a ref and one reactive spread.
|
|
72
|
+
- b3586e8: Validate document-shell templates in the document context (#3259). The `validate` pass round-trips templates through a body-context fragment parse, which strips `<html>`/`<head>`/`<body>` wrappers no matter how well-formed the markup β so once #3099 made validate failures compile errors, a root component owning the document shell failed to compile in plain client mode, and merely importing it (the jsdom component-test configuration) was fatal. Shell-rooted templates now parse as a document and the shell element is compared back β the analogue of the synthetic `<table>` wrap for table partials, in both the Babel plugin and the native compiler. Genuine restructuring (an implied `<head>`, flow content in `<head>`, a `<p>` split in `<body>`) still errors. Since `<template>` parsing flattens shells, actually client-creating one now throws a descriptive dev-mode error from `template()` pointing at `hydrate()` β the failure moved from every import to the one broken act.
|
|
73
|
+
- d601119: Remove the experimental patch channel and patch-mode list driver (always opt-in, never default). Graph-native regions own value delivery and the unified-For design owns list structure, so the channel's parallel delivery machinery is retired: `patch.ts`/`patch-driver.ts` deleted, the compiler-contract exports (`registerPatch`/`registerRowOps`/`registerSlotPatch`/`patchableRaw`, `patchDriver`/`rowProof`/`driveList`) removed, the `patchDriver` compiler option dropped from both compilers, the insert `$ll` seam stripped, and the write-side channel struct dieted to the single written-keys bound (`t.wk`) the core fold/notify paths actually use. Store-family app bundles reclaim up to ~900 B brotli; every measured tier shrinks.
|
|
74
|
+
|
|
75
|
+
## 2.0.0-rc.6
|
|
76
|
+
|
|
77
|
+
### Patch Changes
|
|
78
|
+
|
|
79
|
+
- 1e7fa73: Reject authored TSRX lazy destructuring in Solid while retaining deferred patterns generated for keyed loops and catch clauses.
|
|
80
|
+
- bbff5e0: Direct `value`/`checked` (and other stateful DOM property) bindings no longer overwrite pre-hydration user input during the hydration claim pass (#3182). Hydratable compiled output now routes locked DOM properties through `setProperty`, which skips writes on hydrating nodes and carries the `<select value>` microtask and input/textarea nullish special cases.
|
|
81
|
+
- cdbd584: Assign static `<select value>` values through the live DOM property so the matching option is selected consistently with reactive values.
|
|
82
|
+
- 5a1abb3: Compile TSRX loops with an index but no explicit key using Solid's non-keyed callback shape.
|
|
83
|
+
- 82868c6: Support recursive lazy destructuring, lazy arrow parameters, per-read defaults, computed keys, rest views, standalone assignments, accessor-backed keyed-loop and catch patterns, and JavaScript-correct writes and updates across the Babel and native TSRX frontends.
|
|
84
|
+
- 774aad5: Add compile-time scoped styles, CSS sidecar output, style class maps, and style refs to both TSRX frontends.
|
|
85
|
+
- c9c16cb: Add an experimental TSRX syntax frontend to both compilers. `.tsrx` sources (routed by filename with the new `syntax: "auto" | "jsx" | "tsrx"` option) desugar `@if`/`@else`, `@for β¦ @empty`, `@switch`/`@case`, `@try`/`@catch`/`@pending`, `@{}` statement containers, and lazy destructuring (`&{}`/`&[]`) into the shared Solid JSX lowering, producing byte-identical output from both compilers. The Babel plugin loads the optional `@tsrx/core` peer dependency lazily; the native compiler ships the frontend behind the default-on `tsrx` cargo feature (statement containers in expression position are rejected with a structured diagnostic pending upstream oxc-tsrx support).
|
|
86
|
+
|
|
87
|
+
## 2.0.0-rc.5
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- 320f1f5: Universal text is text (#3127). The DOM and SSR generators splice static
|
|
92
|
+
text into an HTML template that a parser later unescapes, so they escape
|
|
93
|
+
static values and keep JSX entities as written. The universal generator
|
|
94
|
+
hands strings straight to the host β `createTextNode`, `setProp` β with no
|
|
95
|
+
parser downstream, so the escaping rendered literally (`{"<b>"}` showed as
|
|
96
|
+
`<b>`) and entities never decoded (`<` showed as `<`), leaving no
|
|
97
|
+
spelling that produced a literal `<` in static text under a custom
|
|
98
|
+
renderer. Universal-rendered element children now pass static values
|
|
99
|
+
through unescaped and decode JSX entities in text and string attributes,
|
|
100
|
+
matching what component children and fragment text always did. The flag
|
|
101
|
+
rides on the element, not the config, so `generate: "dynamic"` decides per
|
|
102
|
+
renderer. Applied to both compilers; the attribute half closed ten pinned
|
|
103
|
+
cross-mode parity divergences between them. Reported with the fix mapped
|
|
104
|
+
out by @antoinevanwel.
|
|
105
|
+
- 5230666: Fix hydration ids drifting after a reactive lone spread (#3105). A lone spread now passes its accessor straight to `spread()` on the client β no `mergeProps`, no memo, no hydration id β matching the server's existing pass-through fast path. The runtime resolves a function props source inside its own tracking scopes.
|
|
106
|
+
- e27dc29: `validate` now fails the compile instead of warning when a template's markup would be restructured by the browser's HTML parser (#3099). Once the validator fires the emitted positional walk is guaranteed not to match the browser-built DOM (crashed or silently misplaced bindings; desynced hydration under SSR), so warn-and-emit shipped certain breakage with the diagnostic buried in server logs. Errors now point at the offending JSX (code frame in Babel, line:col in the native compiler). `validate: false` remains the opt-out.
|
|
107
|
+
|
|
108
|
+
## 2.0.0-rc.4
|
|
109
|
+
|
|
110
|
+
### Patch Changes
|
|
111
|
+
|
|
112
|
+
- 8d249c7: Patch-channel contract hardening from the stage-2 re-audit: ordinary `patchDriver` registrations unbind with their owner (entries no longer leak past unmount); merged transitions move their held-patch stash so no patch strands; the optimistic drain shares the normal drain's per-entry error isolation and boundary routing; accessor-bearing records are excluded at admission (scan-before-trust) and records that acquire accessors demote their patches to tracked effect fallbacks; writable projection arrays emit setter row ops at their fold-commit visibility moment; row-ops/slot registrations resolve chained backings to the ultimate owner; duplicate keys match occurrence-aware instead of first-wins; the production dev-token typo (`_DX_DEV_`) is fixed; `patchDriver: true` normalizes identically in Babel and the native loader, the option is typed in `TransformOptions`, and a `dom-patch` parity tier ratchets patch-mode output across both compilers (currently byte-identical on all fixtures).
|
|
113
|
+
- b534733: Scope-wrap bare function children in hydratable mode. A function child (`<main>{() => <App/>}</main>`, including via the `children` attribute) is a deferred hole at runtime, but it never classified as `dynamic`, so neither generate reserved an id scope for it β its owner ids drifted across async retry passes on the server and desynced from the client (the #2900 hydration-id-parity class). Both compilers now treat syntactic function expressions as scope-eligible alongside dynamic values, emitting `_$scope(...)` in the ssr generate and around the matching insert accessor in the dom generate. The native compiler also unwraps TS casts in the allocate-ids predicate, matching Babel (fixes a scope-emission desync for `{call() as any}` children).
|
|
114
|
+
|
|
115
|
+
## 2.0.0-rc.3
|
|
116
|
+
|
|
117
|
+
### Minor Changes
|
|
118
|
+
|
|
119
|
+
- 89a0531: Absorb the 0.50 expressions snapshot into this repo: lift compilers as `@solidjs/babel-plugin` and `@solidjs/compiler`, dump runtimes into `@solidjs/web` / `h` / `html` / `universal`. Origin: ryansolid/dom-expressions@e97e4290 (0.50.0-next.44).
|
|
120
|
+
- 89a0531: Collapse the expressions dump: drop the rxcore seam, flatten runtimes into package `src/`, delete `babel-preset-solid`, and publish compiler natives as `@solidjs/compiler-*`.
|
|
121
|
+
|
|
122
|
+
### Patch Changes
|
|
123
|
+
|
|
124
|
+
- 47a797e: Drop leftover DOM Expressions loader fallbacks and reframe the compiler and Babel plugin as Solid 2.0 packages. Node `transform()` now defaults `moduleName` to `@solidjs/web` and `builtIns` to the Solid control-flow set, matching `@solidjs/babel-plugin`. SSR leaves a sole component child unescaped (it is a value; the callee's insert sites escape) and only HTML-escapes mixed/fragment children and element holes. Local native builds remain usable when an in-repo platform-package stub has not been populated yet.
|
|
125
|
+
- 0d2810a: Fix Babel and native compiler lowering divergences around nested content, custom-element ownership, static attributes, namespaces, and conditional evaluation order.
|
|
126
|
+
|
|
127
|
+
The Babel compiler implementation joined the SolidJS 2.0 monorepo at `2.0.0-rc.2` under the temporary `@solidjs/babel-plugin-jsx` name. It adopted this syntax-neutral package name before the integration merged. Earlier releases lived in [DOM Expressions](https://github.com/ryansolid/dom-expressions).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016-2025 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,386 @@
|
|
|
1
|
+
# @solidjs/babel-plugin
|
|
2
|
+
|
|
3
|
+
Solid 2.0's Babel compiler integration. Configure this plugin once in a Babel pipeline; its current syntax frontend lowers JSX to template clones, inserts, and reactive wrappers against `@solidjs/web` (or a custom renderer).
|
|
4
|
+
|
|
5
|
+
The native Oxc compiler β [`@solidjs/compiler`](../compiler) β is the default in `vite-plugin-solid`. Use this plugin when you need a Babel pipeline (or as the JavaScript fallback).
|
|
6
|
+
|
|
7
|
+
> **Solid 2.0 (Release Candidate).** Pin exact versions.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
Lowercase tags are HTML elements; mixed-case tags are components. The plugin supports Custom Elements, camelCase event handlers, DOM-safe attributes (`class`, `for`), `ref`, and object `style` / `class` parsing.
|
|
12
|
+
|
|
13
|
+
Attribute expressions are attributes by default, except on custom elements (properties) and special fields like `class` and `style`. A heuristic wraps expressions that contain function calls or property access; simple literals and variables stay unwrapped.
|
|
14
|
+
|
|
15
|
+
## Example
|
|
16
|
+
|
|
17
|
+
```jsx
|
|
18
|
+
const view = ({ item }) => {
|
|
19
|
+
const itemId = item.id;
|
|
20
|
+
return (
|
|
21
|
+
<tr class={itemId === selected() ? "danger" : ""}>
|
|
22
|
+
<td class="col-md-1">{itemId}</td>
|
|
23
|
+
<td class="col-md-4">
|
|
24
|
+
<a onClick={e => select(item, e)}>{item.label}</a>
|
|
25
|
+
</td>
|
|
26
|
+
<td class="col-md-1">
|
|
27
|
+
<a onClick={e => del(item, e)}>
|
|
28
|
+
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
|
|
29
|
+
</a>
|
|
30
|
+
</td>
|
|
31
|
+
<td class="col-md-6"></td>
|
|
32
|
+
</tr>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Compiles to:
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
import { template as _$template } from "@solidjs/web";
|
|
41
|
+
import { delegateEvents as _$delegateEvents } from "@solidjs/web";
|
|
42
|
+
import { className as _$className } from "@solidjs/web";
|
|
43
|
+
import { effect as _$effect } from "@solidjs/web";
|
|
44
|
+
import { insert as _$insert } from "@solidjs/web";
|
|
45
|
+
|
|
46
|
+
const _tmpl$ = /*#__PURE__*/ _$template(
|
|
47
|
+
`<tr><td class="col-md-1"></td><td class="col-md-4"><a></a></td><td class="col-md-1"><a><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></a></td><td class="col-md-6"></td></tr>`
|
|
48
|
+
);
|
|
49
|
+
const view = ({ item }) => {
|
|
50
|
+
const itemId = item.id;
|
|
51
|
+
return (() => {
|
|
52
|
+
const _el$ = _tmpl$(),
|
|
53
|
+
_el$2 = _el$.firstChild,
|
|
54
|
+
_el$3 = _el$2.nextSibling,
|
|
55
|
+
_el$4 = _el$3.firstChild,
|
|
56
|
+
_el$5 = _el$3.nextSibling,
|
|
57
|
+
_el$6 = _el$5.firstChild;
|
|
58
|
+
_$insert(_el$2, itemId);
|
|
59
|
+
_el$4._$$click = e => select(item, e);
|
|
60
|
+
_$insert(_el$4, () => item.label);
|
|
61
|
+
_el$6._$$click = e => del(item, e);
|
|
62
|
+
_$effect(
|
|
63
|
+
() => selected(),
|
|
64
|
+
_v$ => _$className(_el$, itemId === _v$ ? "danger" : "")
|
|
65
|
+
);
|
|
66
|
+
return _el$;
|
|
67
|
+
})();
|
|
68
|
+
};
|
|
69
|
+
_$delegateEvents(["click"]);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`cloneNode` (via `template()`) improves repeat insert performance and precompilation reduces references to the minimal traversal path.
|
|
73
|
+
|
|
74
|
+
## Configuration
|
|
75
|
+
|
|
76
|
+
Omitted options are the Solid 2.0 defaults that used to live in `babel-preset-solid`: `moduleName: "@solidjs/web"`, `generate: "dom"`, `wrapConditionals`, `contextToCustomElements`, and auto-import of `For` / `Show` / `Switch` / `Match` / `Loading` / `Reveal` / `Portal` / `Repeat` / `Dynamic` / `Errored`.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
{
|
|
80
|
+
plugins: [
|
|
81
|
+
[
|
|
82
|
+
"@solidjs/babel-plugin",
|
|
83
|
+
{
|
|
84
|
+
moduleName: "@solidjs/web",
|
|
85
|
+
generate: "dom",
|
|
86
|
+
hydratable: true
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Plugin Options
|
|
94
|
+
|
|
95
|
+
### moduleName
|
|
96
|
+
|
|
97
|
+
- Type: `string`
|
|
98
|
+
- Default: `"@solidjs/web"`
|
|
99
|
+
|
|
100
|
+
Runtime module the compiled output imports helpers from. Use the same module for SSR; switch `generate` instead.
|
|
101
|
+
|
|
102
|
+
### syntax
|
|
103
|
+
|
|
104
|
+
- Type: `'auto' | 'jsx' | 'tsrx'`
|
|
105
|
+
- Default: `'auto'`
|
|
106
|
+
|
|
107
|
+
Source syntax frontend. `"auto"` routes `.tsrx` files through the TSRX frontend and everything else through standard JSX; `"tsrx"` forces TSRX for every file; `"jsx"` disables TSRX routing entirely. See [TSRX](#tsrx-experimental).
|
|
108
|
+
|
|
109
|
+
### generate
|
|
110
|
+
|
|
111
|
+
- Type: `'dom' | 'ssr' | 'universal' | 'dynamic'`
|
|
112
|
+
- Default: `'dom'`
|
|
113
|
+
|
|
114
|
+
`"dom"` is client DOM output. `"ssr"` emits server strings. `"universal"` targets a custom renderer. `"dynamic"` uses the universal renderer as fallback and can route configured native tags to the DOM renderer.
|
|
115
|
+
|
|
116
|
+
### hydratable
|
|
117
|
+
|
|
118
|
+
- Type: `boolean`
|
|
119
|
+
- Default: `false`
|
|
120
|
+
|
|
121
|
+
Emit hydratable markers.
|
|
122
|
+
|
|
123
|
+
### dev
|
|
124
|
+
|
|
125
|
+
- Type: `boolean`
|
|
126
|
+
- Default: `false`
|
|
127
|
+
|
|
128
|
+
Development output. With `hydratable`, emits the hydration walk validation helpers.
|
|
129
|
+
|
|
130
|
+
### sourceNames
|
|
131
|
+
|
|
132
|
+
- Type: `boolean | { components?: boolean; bindings?: boolean }`
|
|
133
|
+
- Default: follows `dev` β every kind on when `dev: true`, off otherwise
|
|
134
|
+
|
|
135
|
+
Names as written in source, carried into the output so the dev and observe runtimes can label the reactive graph β in diagnostic `ownerPath`s, attribution chains, and the Performance panel tracks β even after a minifier renames everything. Unset, the option follows `dev`; `true`/`false` sets every kind (`sourceNames: false` opts a dev build out); the object form picks, and each kind it leaves unspecified follows `dev`. The production runtimes ignore the names, and production output (`dev: false`) is byte-identical whether the option is set or not. `@solidjs/compiler`'s `transform()` takes the same option with the same shape and defaults.
|
|
136
|
+
|
|
137
|
+
- `components`: emit the tag as written as a third `createComponent` argument β `<Home />` compiles to `createComponent(Home, props, "Home")`, `<Ui.Button />` to `"Ui.Button"` β so each component's owner reads `<Home>` even when a `lazy()`/HMR wrapper hides the function. Applies to DOM and SSR output; for SSR the compiler keeps the `createComponent` call it otherwise inlines to `Comp(props)`, so the server runtime labels the owner the same way (prod SSR output, without the option, is unchanged). Universal and dynamic output are unaffected.
|
|
138
|
+
- `bindings`: every compiled binding effect is named by what it writes, as a trailing options argument the dev and observe runtimes read and production ignores. An attribute effect gets `<tag>.<attribute>` as written β `<span textContent={label()} />` compiles to `effect(() => label(), v => β¦, { name: "span.textContent" })`, `class:active` to `div.class:active`, a `style={{ color: c() }}` property to `div.style:color` β and a template's merged effect lists all of its bindings (`"button.class, span.textContent"`). A hole's insert is named for the parent it fills: `<div>{count()}</div>` compiles to `insert(el, count, undefined, undefined, { name: "div.children" })`; a static child (a component call, a literal) creates no effect and gets no name. A spread passes the tag as its trailing argument (`spread(el, props, false, undefined, "div")`), and the runtime labels its attribute effect `div.spread` and its children insert `div.children`. DOM output only.
|
|
139
|
+
|
|
140
|
+
These are the JSX-level kinds β what a JSX compiler can see. Naming the primitives themselves (`createSignal(0, { name: "count" })`, `createCounter.value` inside a composed primitive) is not a JSX-transform feature and this plugin does not do it: it is `@solidjs/compiler`'s standalone `transformSourceNames` pass, which the build tool runs on every module β `.ts`, `.js`, and JSX files alike β independently of which JSX compiler handles the file. `@solidjs/vite-plugin` (its `solid: { sourceNames }` option, solid-vite-plugin #371) wires both: the JSX-level kinds through this option and primitive names through the standalone pass, for its dev and `observe` postures.
|
|
141
|
+
|
|
142
|
+
### delegateEvents
|
|
143
|
+
|
|
144
|
+
- Type: `boolean`
|
|
145
|
+
- Default: `true`
|
|
146
|
+
|
|
147
|
+
Automatic event delegation on camelCase.
|
|
148
|
+
|
|
149
|
+
### wrapConditionals
|
|
150
|
+
|
|
151
|
+
- Type: `boolean`
|
|
152
|
+
- Default: `true`
|
|
153
|
+
|
|
154
|
+
Smart conditional detection for simple boolean expressions and ternaries in JSX.
|
|
155
|
+
|
|
156
|
+
### contextToCustomElements
|
|
157
|
+
|
|
158
|
+
- Type: `boolean`
|
|
159
|
+
- Default: `true`
|
|
160
|
+
|
|
161
|
+
Set current render context on Custom Elements and slots, so Solid's Context API works with Web Components.
|
|
162
|
+
|
|
163
|
+
### builtIns
|
|
164
|
+
|
|
165
|
+
- Type: `string[]`
|
|
166
|
+
- Default: `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`
|
|
167
|
+
|
|
168
|
+
Component exports from `moduleName` that the plugin should auto-import when it sees them in JSX.
|
|
169
|
+
|
|
170
|
+
### effectWrapper
|
|
171
|
+
|
|
172
|
+
- Type: `string | false`
|
|
173
|
+
- Default: `effect`
|
|
174
|
+
|
|
175
|
+
Reactive wrapper for lazy JSX expressions.
|
|
176
|
+
|
|
177
|
+
### staticMarker
|
|
178
|
+
|
|
179
|
+
- Type: `string`
|
|
180
|
+
- Default: `@static`
|
|
181
|
+
|
|
182
|
+
When an expression starts with this comment, the compiler treats it as static and does not wrap it in an `effect`. Not a reactivity primitive β only use it when the expression is non-reactive for the lifetime of the element and the heuristic cannot infer that.
|
|
183
|
+
|
|
184
|
+
### memoWrapper
|
|
185
|
+
|
|
186
|
+
- Type: `string | false`
|
|
187
|
+
- Default: `memo`
|
|
188
|
+
|
|
189
|
+
Memo function name for derived values used in many reactive computations.
|
|
190
|
+
|
|
191
|
+
### validate
|
|
192
|
+
|
|
193
|
+
- Type: `boolean`
|
|
194
|
+
- Default: `true`
|
|
195
|
+
|
|
196
|
+
Checks for HTML that browsers would "correct" in ways that break DOM walks. Incomplete, but covers the dangerous cases.
|
|
197
|
+
|
|
198
|
+
### omitNestedClosingTags
|
|
199
|
+
|
|
200
|
+
- Type: `boolean`
|
|
201
|
+
- Default: `false`
|
|
202
|
+
|
|
203
|
+
Removes unnecessary closing tags from template output. Tested against Chrome/Edge/Firefox/Safari; other HTML parsers may differ.
|
|
204
|
+
|
|
205
|
+
### omitLastClosingTag
|
|
206
|
+
|
|
207
|
+
- Type: `boolean`
|
|
208
|
+
- Default: `true`
|
|
209
|
+
|
|
210
|
+
Omits the last closing tag when it has no closing parent. Same parser caveat as above.
|
|
211
|
+
|
|
212
|
+
### omitQuotes
|
|
213
|
+
|
|
214
|
+
- Type: `boolean`
|
|
215
|
+
- Default: `true`
|
|
216
|
+
|
|
217
|
+
Drops quotes around HTML attributes when possible.
|
|
218
|
+
|
|
219
|
+
### omitAttributeSpacing
|
|
220
|
+
|
|
221
|
+
- Type: `boolean`
|
|
222
|
+
- Default: `true`
|
|
223
|
+
|
|
224
|
+
When `true`, quoted attributes may omit the space before the next attribute. Set `false` for strictly spaced attributes.
|
|
225
|
+
|
|
226
|
+
### requireImportSource
|
|
227
|
+
|
|
228
|
+
- Type: `string | false`
|
|
229
|
+
- Default: `false`
|
|
230
|
+
|
|
231
|
+
Restrict JSX transformation to files whose `@jsxImportSource` pragma matches.
|
|
232
|
+
|
|
233
|
+
```js
|
|
234
|
+
{
|
|
235
|
+
plugins: [["@solidjs/babel-plugin", { requireImportSource: "@solidjs/web" }]];
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```jsx
|
|
240
|
+
/** @jsxImportSource @solidjs/web */
|
|
241
|
+
const template = <div>Hello</div>;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### inlineStyles
|
|
245
|
+
|
|
246
|
+
- Type: `boolean`
|
|
247
|
+
- Default: `true`
|
|
248
|
+
|
|
249
|
+
Inline style attributes in templates when the value is a string or `Record<string, string>`. Disable for strong CSP configurations; styles are then set at runtime.
|
|
250
|
+
|
|
251
|
+
### serverComponents
|
|
252
|
+
|
|
253
|
+
- Type: `boolean`
|
|
254
|
+
- Default: `false`
|
|
255
|
+
|
|
256
|
+
SSR-only: emit behavior-claim (`_bnd`) markers for `ref` / `on*` on intrinsic elements.
|
|
257
|
+
|
|
258
|
+
### hoistProps
|
|
259
|
+
|
|
260
|
+
- Type: `boolean`
|
|
261
|
+
- Default: `true`
|
|
262
|
+
|
|
263
|
+
SSR-only: a component's props literal with getters compiles to a module-level constructor whose getters are shared across instances, instead of an object literal (which V8 builds in dictionary mode, allocating a closure per getter per instance):
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
var _m$ = Symbol();
|
|
267
|
+
var _d$ = {
|
|
268
|
+
get() {
|
|
269
|
+
const props = this[_m$];
|
|
270
|
+
return props.label;
|
|
271
|
+
},
|
|
272
|
+
enumerable: true,
|
|
273
|
+
configurable: true
|
|
274
|
+
};
|
|
275
|
+
function _P$(_p, _p2) {
|
|
276
|
+
this[_m$] = _p;
|
|
277
|
+
this.as = _p2;
|
|
278
|
+
Object.defineProperty(this, "label", _d$);
|
|
279
|
+
}
|
|
280
|
+
_P$.prototype = Object.prototype;
|
|
281
|
+
Comp(new _P$(props, "a"));
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The instance has the same own keys, order and descriptors as the literal and `Object.prototype` as its prototype; `Object.keys`, spread, `hasOwn` and `isStatic()` answer as before. One contract follows: a props getter is defined only for a read through its own object (`props.x`, spread, `merge()`/`omit()`). Copying its property descriptor onto another object and reading it there throws β define a getter that reads through the source instead. Sites whose getters close over a binding that is reassigned, declared after the site, or `this`/`arguments` keep the literal. `false` keeps the literal everywhere.
|
|
285
|
+
|
|
286
|
+
## TSRX (experimental)
|
|
287
|
+
|
|
288
|
+
TSRX (TypeScript Render Extensions) is a syntax for declarative UI. `.tsrx` sources desugar to the same Solid JSX this plugin already compiles: `@if`/`@else`, `@for β¦ @empty`, `@switch`/`@case`, and `@try`/`@catch`/`@pending` lower to the corresponding control-flow components (`Show`, `For`, `Switch`/`Match`, `Errored`, `Loading`), and `@{}` statement containers mix setup statements with rendered elements.
|
|
289
|
+
|
|
290
|
+
```tsrx
|
|
291
|
+
export function TodoList({ items }) @{
|
|
292
|
+
<ul>
|
|
293
|
+
<style>
|
|
294
|
+
li { padding-block: 0.25rem; }
|
|
295
|
+
</style>
|
|
296
|
+
@for (const item of items; index i; key item.id) {
|
|
297
|
+
<li>{i + 1}. {item.text}</li>
|
|
298
|
+
} @empty {
|
|
299
|
+
<li>No todos</li>
|
|
300
|
+
}
|
|
301
|
+
</ul>
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Requirements and behavior:
|
|
306
|
+
|
|
307
|
+
- Compiling `.tsrx` sources requires the optional peer dependency `@tsrx/core` and Node.js >= 22.12. It loads lazily on first TSRX routing, so plain JSX users never pay for it.
|
|
308
|
+
- Routing is filename-based by default (`syntax: "auto"`), so Babel must receive a `filename`.
|
|
309
|
+
- Desugared constructs rely on the `builtIns` auto-imports, so those components must exist in `moduleName`.
|
|
310
|
+
- Scoped `<style>` blocks are removed at compile time, matching native and dynamic elements receive a `tsrx-<hash>` class, and the scoped/pruned stylesheet is returned as `result.metadata.css` with `result.metadata.cssHash`. Style expressions produce class-map objects, `<style ref={styles}>` initializes a class map, and `:global(...)` opts selectors out of scoping. The plugin emits no runtime style helper; a bundler integration must emit the CSS metadata.
|
|
311
|
+
- Solid rejects authored TSRX lazy destructuring (`&{ β¦ }` / `&[ β¦ ]`). Keep accessor calls and reactive property reads explicit in Solid source.
|
|
312
|
+
- Control-flow bindings pass through exactly as authored. `@for β¦ index` / `@for β¦ key` hand the callback the item as an accessor (and the index as an accessor under a custom key), and `@catch (err, reset)` receives Solid's `ErrorAccessor` β write `item()`, `i()`, `err()` as in JSX. Destructuring a binding in one of those positions is rejected with a diagnostic; the default keyed `@for` item is a raw value and destructures as usual.
|
|
313
|
+
- The native compiler ([`@solidjs/compiler`](../compiler)) compiles the same sources to byte-identical output.
|
|
314
|
+
|
|
315
|
+
## Special Binding
|
|
316
|
+
|
|
317
|
+
### ref
|
|
318
|
+
|
|
319
|
+
Assigns the DOM element to a variable, or calls a function with the element.
|
|
320
|
+
|
|
321
|
+
```jsx
|
|
322
|
+
const Child = props => <div ref={props.ref} />;
|
|
323
|
+
|
|
324
|
+
const Parent = () => {
|
|
325
|
+
let ref;
|
|
326
|
+
return <Child ref={ref} />;
|
|
327
|
+
};
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### on(eventName)
|
|
331
|
+
|
|
332
|
+
CamelCase attributes such as `onClick` are event handlers. The compiler delegates events that bubble or compose, otherwise it uses Level 1 `on_____` properties.
|
|
333
|
+
|
|
334
|
+
Pass an array to bind a value: the second item is the first argument to the handler, the event is second.
|
|
335
|
+
|
|
336
|
+
```jsx
|
|
337
|
+
function handler(itemId, e) {
|
|
338
|
+
/*...*/
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
<ul>
|
|
342
|
+
{list().map(item => (
|
|
343
|
+
<li onClick={[handler, item.id]} />
|
|
344
|
+
))}
|
|
345
|
+
</ul>;
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Delegation works with Web Components and Shadow DOM when events are composed (custom events and most UA UI events). Custom delegated events should follow native all-lowercase naming. Use a ref that calls `addEventListener` when you need listener options or custom casing.
|
|
349
|
+
|
|
350
|
+
Event delegates are owned by render roots and are removed when those roots dispose.
|
|
351
|
+
|
|
352
|
+
### ... (spreads)
|
|
353
|
+
|
|
354
|
+
```jsx
|
|
355
|
+
<div {...props} />
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Given independent binding updates, spread order is not currently guaranteed.
|
|
359
|
+
|
|
360
|
+
## Components
|
|
361
|
+
|
|
362
|
+
Capitalized tags are components. Dynamic props become getters rather than wrapped computations. Property access is tracking, so do not destructure outside computations unless the value should be static.
|
|
363
|
+
|
|
364
|
+
```jsx
|
|
365
|
+
const MyComp = props => {
|
|
366
|
+
const staticProp = props.other;
|
|
367
|
+
return (
|
|
368
|
+
<>
|
|
369
|
+
<div>{props.param}</div>
|
|
370
|
+
<div>{staticProp}</div>
|
|
371
|
+
</>
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
<MyComp param={dynamic()} other={static} />;
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
`props.children` may be a node, a function, a string, or an array of those. Non-expression children evaluate lazily on access.
|
|
379
|
+
|
|
380
|
+
## Fragments
|
|
381
|
+
|
|
382
|
+
`<></>` compiles to arrays.
|
|
383
|
+
|
|
384
|
+
## Acknowledgements
|
|
385
|
+
|
|
386
|
+
The concept of compiling JSX to DOM operations (rather than HTML strings) was inspired by [Surplus](https://github.com/adamhaile/surplus).
|