Skip to content

solid-js@2.0.0-rc.19

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Oct 17:29
· 39 commits to main since this release
d2323b0

Patch Changes

  • 2a0da2f: fix(web): refuse an async memo that resolves to an element (#4242)

    On the server, an async memo (or async iterable source) whose value is rendered output — JSX, a component's result, a <Loading>/<Errored> boundary — can no longer reach the hydration serializer. A boundary's output failed in seroval, so its record never resolved and the hydrating client waited on the memo forever (streamed, the boundary reverted to its fallback); a template object crossed as data, duplicating the markup, and streamed the client could not insert it (UNRECOGNIZED_INSERT_VALUE). The memo now rejects where its value lands, with the new diagnostic ASYNC_MEMO_ELEMENT (recorded on the observe channel; the dev message says what to do instead, other tiers carry the code), and its boundary contains the error like any render error: the nearest <Errored> renders its fallback and the client hydrates it; with none, the <Loading> fragment rejects and the client renders the subtree. Return data from the async memo and render the JSX from it. Ordinary data is never refused (only plain objects in the SSR template shape, boundary outputs and fragment arrays holding them count as rendered output), and content that never hydrates (<NoHydration>) is untouched.

    Also, a fragment failing while a boundary registered under an undefined key (one created inside an async memo's continuation) is pending no longer fails the whole request in renderToStream.

  • 0090fe3: Fix: a streamed <Loading> claimed after a client write changed a shell memo it reads now shows the client's value. A hydrated memo adopted while its server answer was still streaming had no snapshot, so once a client write took its compute over, the boundary's claim read the client's value while adopting the server's markup — and kept the server's text (or attribute) for good, since nothing re-ran the binding. The memo's server answer is now its snapshot when it lands, even when the write already superseded it: the claim reads what the markup shows and replays the client's value at its release, as for a written signal. If the client's recompute is still in flight when the boundary resumes, the claim is served the snapshot instead of going pending, so the boundary keeps the server's content (no fallback over it, no detached nodes, no lost queued click) until the client's value lands.

  • ee94440: Hydration keeps a client-only boundary's served fallback (#4286). A <Loading> whose content reads a bare ssrSource: "client" source is served as its fallback plus the "$$f" marker; the client hydrated that fallback, then resumed by re-running the boundary, and when the content suspended on the question it now asks, the new boundary built a second fallback and dropped the served one (losing focus, selection or a running animation in it). The "$$f" route now creates the loading boundary once, at hydration: its content waits on the resume and its fallback hydrates the served one (built at the boundary's id, as the server built it), so content that suspends after the resume keeps the hydrated fallback until it can show.

  • f5cf675: An <Errored> around a <Loading> whose async read fails during a streamed render now shows its fallback in the server HTML outside server components too, and the client adopts it. Before, the boundary's HTML was empty until hydration (neither the loading nor the error fallback), and the hydrating client rendered the failed boundary from scratch: it asked again and showed the loading fallback. Now, when the <Loading> is all of the <Errored>'s content, the server draws the error fallback in its place (in the shell, or in the streamed fragment after the shell) and serializes the error at the <Errored>, so a client that hydrates after the failure arrived claims that markup and adopts its data without asking again, and recovers when the content's inputs change, as for any hydrated failure (#4273). A client that hydrated before the failure arrived renders it once, with nothing of the server's swap left beside it. With markup between the two boundaries the server still leaves the position blank and the client renders the failure (#4287).

  • 8f1d9d2: Server: when an <Errored> renders its fallback for a <Loading> that failed after the shell, and a hole in that fallback rejects while the server waits for it, the rejection now goes to the next boundary up, as it does when a fallback throws synchronously. Inside a pending outer <Loading>, that outer fragment fails; under an outer <Errored> whose whole content is that position, the outer fallback is drawn there and the client adopts it. Before, the region streamed blank and the hydrating client halted (#4316). A synchronous throw in that fallback no longer writes an error record for an outer <Errored> without sending its fallback.

    The server's wait for that fallback now stops after the same pass budget as the <Loading> resume loop and then streams what it has. It also stops as soon as the render is torn down, for example when the request is aborted (#4317).

  • 8f1d9d2: An <Errored> whose fallback fails while rendering (a hole that rejects, or a synchronous throw) now passes that failure to the enclosing boundary on the server, in the shell as well as in a streamed fragment. An outer <Errored> draws its fallback in place of the region, and with none the request fails. Before, the region was sent blank, with the errors recorded and no markup for them.

  • 0856fee: An <Errored> boundary hydrated in the error state the server rendered now recovers. Before, hydration threw the server's error without running the content, so the boundary tracked nothing and stayed on the fallback until reset(), even after the failed content's inputs changed. Now the content runs during hydration, reading its server data without refetching but claiming no DOM, while the fallback claims the server's markup. If the content fails on the client, the boundary holds that client error and recovers when the content's inputs change. If the error only happened on the server, the server's fallback and error stay up until reset() is called or the content re-runs (#4273).

  • 542ff6f: An <Errored> whose fallback reads async data, around a <Loading> that fails after the shell, now streams its fallback once that data is ready, and a client hydrating after it arrived claims it. Before, the failure landed while the fallback's read was still pending, so the server had no markup for it. On its own, the boundary swapped to a blank and the client rendered the failure itself, asking for the failed read again. Inside an outer <Loading> still pending, the outer fragment arrived with the Errored region blank, and the client hydrated against the blank with warnings and showed nothing there. Now the failure waits for the fallback, as a swap to a fallback waits for the fallback to be ready: the loading placeholder stays until the finished fallback replaces it (carried by the outer fragment when that is still pending), and the error is serialized with it (#4306).

  • 2c9fdd9: A boundary whose server render failed is rendered fresh on the client while adopting the server's records. Inside that render, a <Dynamic> tag now creates its element instead of throwing a hydration mismatch, and a <Portal> mounts. Hydration also completes when the adopted error has no client boundary to catch it.

  • e0e4224: A client hydrating mid-stream no longer asks again for an async read whose server answer was an error, nor flashes back to the <Loading> fallback after the failure showed (#4287, #4306). The content of a hydrated failure (a hydrated <Errored>'s content run, or a rejected fragment's fresh render over the <Errored> fallback the server drew) reads a shell memo a cycle after its error landed, and a later tracked read retries an errored async node; the serialized-value latch took that rerun for a client write and computed. Inside such a content run the rerun now re-serves the server's answer and arms nothing. A rejected fragment with no outcome streams an empty template (it was a lone space), so the nodes its swap lists are the server's outcome alone.

  • e0e4224: A memo that adopted its server answer no longer asks again when the server's answer for one of its sources arrives mid-stream. Hydrated mid-stream, an async shell memo b reading another, a, re-ran when a's server value landed, and the serialized-value latch took that rerun for a client write and computed b. The arrival of a served answer, with the flush that carries it, is now hydration's own pass (as a hydrated failure's content run is): a latched node re-running there re-serves its own answer and arms nothing. Only a client write makes it compute.

  • 1d6c40f: Add onFlush(fn): runs fn once right after the pending flush, before paint, without waiting on async work. A write made there is its own update, so it is not held by async work another write in the same frame started (a menu closing while the consumer's onSelect navigates). Meant for event handlers and effect callbacks; dev warns (ON_FLUSH_IN_OWNED_SCOPE) in a component body or computation. Tree-shakes when unused.

  • 8f1d9d2: Server: a <Loading> that fails after the shell while its fragment waits inside an outer <Loading> that is still pending no longer arrives as a blank region reported as a success. The failure now goes to the nearest enclosing boundary, as it does when the same read fails inside the outer <Loading>'s own render. An <Errored> between them draws its fallback in the outer fragment, the client adopts it, and its error is serialized with it. With no <Errored> between them, the outer fragment fails, and an <Errored> whose whole content is that outer <Loading> draws its fallback there. This also covers an <Errored> fallback that fails in that position. Before, the client hydrated the blank region and either halted or rendered nothing there (#4316).

  • 6d6f03f: <Repeat> with element (non-function) children no longer builds each row twice. It read the children getter once to check for a function and again for the value, so every row's JSX ran twice and its refs fired twice (#4296).

  • e0e4224: A served answer's arrival during hydration no longer swallows a client write made in the same flush. Writes made before the arrival flush first, outside hydration's own pass, and a write made during it (an effect's, a later round of the flush) makes a node it reaches compute, so the node shows the written value instead of re-serving the server's answer. @solidjs/signals exports its internal flush-round counter (clock) for this.

  • 87934c1: Smaller bundles: builds without the optimistic-store, reveal-order or lane hook modules drop their dead branches, and the #4273/#4275 fixes share existing paths.

  • 2c9fdd9: Hydration never asks again for what the server sent, even when a streamed boundary's fragment rejects with no outcome (no <Errored> on the server could draw the failure). The client still renders that fragment fresh, so its graph is created, but every read in it now claims its serialized record: reads the server resolved take their values, and a read whose record is an error throws that error to the nearest client boundary, or is an uncaught error when there is none. Only reads with no record, such as those cut off by a truncated stream, compute. Recovery comes from a real input change or a boundary's reset(), as for any hydrated failure.

  • Updated dependencies [2a0da2f]

  • Updated dependencies [0847122]

  • Updated dependencies [0090fe3]

  • Updated dependencies [0dd40b2]

  • Updated dependencies [f811097]

  • Updated dependencies [8b34f22]

  • Updated dependencies [2d9c9aa]

  • Updated dependencies [c1009e5]

  • Updated dependencies [6ca7e8d]

  • Updated dependencies [3b6f839]

  • Updated dependencies [4ea2f49]

  • Updated dependencies [1d6c40f]

  • Updated dependencies [1212b77]

  • Updated dependencies [e509ad1]

  • Updated dependencies [253561e]

  • Updated dependencies [e0e4224]

  • Updated dependencies [7f03a4f]

  • Updated dependencies [c225400]

  • Updated dependencies [87934c1]

    • @solidjs/signals@2.0.0-rc.19