Skip to content

@solidjs/compiler@2.0.0-rc.10

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Sep 06:30
· 32 commits to main since this release
c3adc28

Patch Changes

  • 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.

  • 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.

  • fd36d37: transformSourceNames: the pass behind the Vite plugin's sourceNames.primitives option (a @solidjs/vite-plugin option, not a compiler transform() option — the compiler exposes it as this standalone function). Reactive primitives (createSignal, createMemo, createOptimistic, createStore, createOptimisticStore, createProjection) resolving to a solid-js / @solidjs/signals import are named after the identifier they are declared as — array pattern, binding, property key, or class field — prefixed with the enclosing non-component function (createCounter.value), never overriding an explicit name. Plain JavaScript in and out, so it runs on .ts/.js modules too.

    Stores honour name: createStore(value, { name: "todos" }) labels its property nodes todos.title in attribution output instead of store.title; derived and optimistic stores name their property nodes alongside the projection node.

  • 235173e: Prune the observability surface: OBSERVE.attribution.install, the AttributionHooks type, DEV.setConsoleFooter, the TraceSlot and OriginRef aliases, PerformanceTracksOptions.group, and the untested solid-js/refresh runtime modes (esm, webpack5, rspack-esm) are gone — @solidjs/compiler's transformRefresh({ bundler }) accepts only "vite" | "standard" to match. ownerPath() is now OBSERVE.ownerPath(subject) and diagnosticGuideUrl() is DEV.guideUrl(code); why() also accepts a scope name. Engine record types (RerunEvent, HoldEvent, …) live on solid-js/attribution only, and InteractionRef/NavigationRef on the main entries only. @solidjs/diagnostics types its record tables off the runtimes' own catalogue (RecordEvent<K>), adds the recovery table, and bumps the artifact formatVersion to 8.

  • a5bd44a: Fix directive transform source maps to point to the original input by running dead-code elimination on the existing AST instead of printing and reparsing between passes.

  • 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.

  • 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.

    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.

  • 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.

  • 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.

  • 9adf007: Escape static text on SSR spread elements (#3557)

    • 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.
    • A static textarea value folded into the template (<textarea value="a &lt;b&gt;" />) is escaped in both compilers.
    • 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.
    • 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.