[2.0 rc.14 / next] Feature request: public onHydrationEnd, so E2E tests can tell when hydration has finished
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 62/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- javascript, playwright, typescript
Línea de trabajo
Start with the onHydrationEnd exports and server stub in packages/solid/src/index.ts and packages/solid/src/server/index.ts, then read packages/solid/test/internal-surface.spec.ts and published-declarations.spec.ts. Check how the declarations currently exclude this helper and update the public API coverage and documentation described in the issue. Done means the callback is documented as a public export, including when it fires and its single-root limitation, and the declaration checks reflect that.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Our Playwright tests need a documented end-of-hydration signal before they check state the client writes: history state after a navigation, attributes on <html>, no CSP violations once the client code has run. A few tests do the reverse: they hold the client script request and assert that the page has not hydrated yet. Solid 2 has this signal in onHydrationEnd, but it is @internal, so what a test can read from outside the bundle today is a side effect with no contract.
The changelog entry that took onHydrationEnd out of the public API (CHANGELOG.md#L2034) says snapshots make hydration timing transparent. That holds for code running inside the app. A page.evaluate call runs outside it and only sees the DOM and globals, so snapshots don't help there.
Today
// Playwright, against 2.0.0-rc.14
const hydrated = () =>
page.evaluate(() => (globalThis as any)._$HY?.events === null);
// script request held: the page must not be hydrated
expect(await hydrated()).toBe(false);
// script released
await expect.poll(hydrated).toBe(true);
await expect(page.locator("html")).toHaveAttribute("lang", "pt-BR");
We use this check in six E2E test files. It reads internals that each mean "done" at a different moment:
drainHydrationCallbacksruns theonHydrationEndcallbacks synchronously and sets_$HY.done = trueonly in a latersetTimeout(hydration.ts#L387-L401)._$HY.eventsgoes tonullin the microtask thatrunHydrationEvents()queues, only ifsharedConfig.doneis already true when it runs and every queued event could be replayed (client.ts#L2636-L2672). That is the only place it gets nulled.runHydrationEvents()is emitted after a hydratable template root that has a delegated handler or a spread (babel-plugin element.ts#L337-L344, compiler events.rs#L21-L28), plusdynamic().
So on a page where nothing in the hydrated markup has a delegated handler, a spread or a dynamic() element, the check never turns true. A minimal SSR page shows it on rc.14 in headless Chromium: with <main><p>static {n()}</p></main> as the whole app, _$HY.done is true and _$HY.events is still [] five seconds after load. Swap the paragraph for a <button onClick={...}> and _$HY.events goes to null.
_$HY.done would be a better proxy, but server-components-principles.md lists it among the things that dissolve (L656), so we'd rather not move to it. sharedConfig.onHydrationEnd is reachable through solid-js/internal, but that entry says it is not public API and may change or vanish in any release (internal.ts#L1-L4), and the sharedConfig docs call it cross-package wiring (hydration.ts#L187-L198). #3718 moved Solid Router and TanStack off that object for the same reason, and a test hook in an app looks like the same case to us.
Proposal
Drop @internal from onHydrationEnd, maybe as @experimental like setLinkClaim in #3921. #3718 made isHydrating public again after the changelog entry quoted above had removed it. Ruling 3 already names onHydrationEnd and isHydrationInProgress() as the single meaning of done (frames-rulings.md#L127-L135), so exposing it keeps a single channel.
On next it is already a runtime export of solid-js on both entries (index.ts#L138-L155, and a stub on the server at server/index.ts#L203-L206), so the bare export adds no runtime bytes. It sits in an @internal block with other helpers, so a PR would move it out of that block, drop the tag at the declaration and on the server stub, and update internal-surface.spec.ts (which lists onHydrationEnd among the seams that must stay out of the declarations, #L60-L76) and published-declarations.spec.ts, plus a changeset and a line in the docs.
The app would set its own marker and the tests wait for it:
import { onHydrationEnd } from "solid-js";
import { hydrate } from "@solidjs/web";
hydrate(() => <App />, document);
// register after hydrate(), see below
onHydrationEnd(() => {
document.documentElement.dataset.hydrated = "";
});
await expect(page.locator("html[data-hydrated]")).toBeAttached();
Before the entry runs the attribute isn't there, which covers the "client hasn't run yet" assertions too. Registered before hydration starts, the callback is queued for the next microtask and doesn't wait for hydration work started afterward (hydration.ts#L379-L381). In the same repro, with a streamed <Loading> boundary, a callback registered before hydrate() runs before the boundary content arrives, and one registered after hydrate() waits for it. A public version should document this.
The marker would mean the hydration phase ended. Whether it succeeded is a separate question: when a document root's module preload fails, hydrate() sets hydrating to false and keeps the server markup (client.ts#L2455-L2478), and that drains the callbacks. In the same repro, pointing _$HY.r._assets at a missing chunk sets the marker and _$HY.done, and a click on the button does nothing. Our tests check errors and the expected behavior anyway, but the docs would need to say this, or a later version could report how hydration ended, at some cost in bytes. Either would work for us.
We saw that isHydrationInProgress is kept off the exports so app code isn't invited to branch on hydration state (hydration.ts#L327-L333). We'd only use the callback to set a test marker, and our components would never read hydration state. If you'd rather keep it away from app code, a documented marker meant for test tooling would work for us as well. What we need is a documented way to observe this phase without reading _$HY.events.
Our pages have a single hydration root. With several roots GH5 still applies: done fires before a root deferred behind a module preload has claimed anything (preload-hold.spec.tsx#L89, a test.fails). Until that's fixed, the docs shouldn't call this page-wide completion.
Checked against 2.0.0-rc.14 from npm and next at 84dada7. Related: #3718, #3813, #3921.
- Lenguaje dominante
- TypeScript
- Estrellas
- 36.1k
- Forks
- 1.1k
- Merge medio
- 11 h 49 min
- PR fusionados (30 d)
- 346
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de solidjs/solid
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 18/100
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 Medio día Aptitud para principiantes 55/100
Los mantenedores suelen responder en 1 día
-
[2.0 / next] Error formatting and async iterator protocol failures escape memo error handlingAbierto
Dificultad 4/5 3-5 días Aptitud para principiantes 22/100
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 14/100
Los mantenedores suelen responder en 1 día
Todos los issues de solidjs/solid
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 73/100
anomalyco/opencode#56380 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
-
BUG | `MCP 服务器` | 全局层行的停用/启用与删除报 404 server row not found,编辑保存会在 profile 层复制出重复行Posiblemente ocupada @w6242652 la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Chocobozzz/PeerTube#7832 ·
Los mantenedores suelen responder en 3 días
-
Pay to use?Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 66/100
darkreader/darkreader#16108 · 1 comentario ·
Los mantenedores suelen responder en 2 días