@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.
@@ -0,0 +1,10 @@
1
+ $ rollup -c --bundleConfigAsCjs
2
+ 
3
+ src/index.ts β†’ index.js...
4
+ (!) Circular dependencies
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
+ created index.js in 2.8s
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 &lt;b&gt;</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 &lt;b&gt;" />`) 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 &amp; 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
+ `&lt;b>`) and entities never decoded (`&lt;` showed as `&lt;`), 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).