Los entry points: cliente y servidor
Qué hacen entry-client y entry-server y cuándo tocarlos. entry-client arranca la aplicación en el navegador con mount y StartClient, hidratando el HTML que llegó del servidor. entry-server define el documento HTML completo con createHandler y StartServer, incluyendo el head, el div contenedor y los scripts de hidratación. El apretón de manos entre ambos es el id del contenedor, que debe coincidir. Por qué en el flujo normal apenas se tocan, y cuáles son las razones legítimas para editarlos: etiquetas de head, idioma, arranque solo de cliente.
Dos entradas para un mismo árbol
Recuerda el modelo del nivel: hay un solo árbol de aplicación, app.tsx, y dos entornos que lo ejecutan. Un entry point es el código de arranque específico de un entorno: lo que corre antes de que el árbol exista, para traerlo a la vida en ese lugar. En el servidor, arrancar significa renderizar el árbol a una cadena de HTML y envolverla en una página completa; en el cliente, significa tomar esa página ya pintada y volverla interactiva. Misma aplicación, dos maneras de encenderla, dos ficheros.
La palabra clave es antes. Todo lo que escribes en un entry point sucede en el umbral, en el instante previo a que tu aplicación exista como árbol reactivo. Por eso su contenido es tan escaso y tan peculiar: no es lógica de negocio ni interfaz, es la ceremonia de encendido de un entorno. Confundir ese umbral con el interior de la aplicación es el origen de casi todos los tropiezos que veremos al final.
entry-client: reanimar en el navegador
El entry de cliente es minúsculo, y su brevedad es una pista de lo poco que tienes que tocarlo. Importa mount y StartClient y monta la aplicación sobre un nodo del DOM.
// src/entry-client.tsximport { mount, StartClient } from "@solidjs/start/client";mount(() => <StartClient />, document.getElementById("app")!);StartClient es el componente que conecta el enrutador del lado del cliente con tu app.tsx; no lo escribes tú, lo aporta SolidStart. mount es la operación de arranque, y su comportamiento es adaptativo: cuando hay SSR, el nodo app ya contiene el HTML que envió el servidor, así que mount hidrata —adopta ese DOM existente y le engancha la reactividad sin volver a pintarlo—; cuando el proyecto es SPA, el nodo está vacío y mount renderiza desde cero. Una misma línea sirve a los dos modos porque delega en el runtime la decisión de hidratar o crear.
Lo esencial es el segundo argumento: document.getElementById("app"). Ese identificador tiene que coincidir con el del contenedor que define el servidor. Es la mitad cliente del apretón de manos.
Que mount decida entre hidratar y renderizar según lo que encuentre en el nodo tiene una consecuencia práctica valiosa: el mismo entry-client sirve sin cambios tanto a un proyecto SSR como a uno SPA. No hay una versión del arranque para cada modo; hay un arranque que se adapta. Es otra cara del isomorfismo de Solid, y la razón de que cambiar el modo del proyecto no te obligue a reescribir las puertas de entrada.
entry-server: fabricar el documento
El entry de servidor tiene más cuerpo porque su trabajo es mayor: en el servidor no existe ninguna página todavía, así que hay que construir el documento HTML entero alrededor de tu aplicación.
// src/entry-server.tsximport { createHandler, StartServer } from "@solidjs/start/server";export default createHandler(() => ( <StartServer document={({ assets, children, scripts }) => ( <html lang="es"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> {assets} </head> <body> <div id="app">{children}</div> {scripts} </body> </html> )} />));createHandler produce el manejador de peticiones que Nitro invoca por cada solicitud entrante; es el punto donde el mundo de Solid se conecta con el servidor de la capa de abajo. StartServer renderiza tu app.tsx a HTML para esa petición. Y la prop document es la joya: es el único lugar donde tú posees las etiquetas <html>, <head> y <body>. Recibe tres piezas que debes colocar en su sitio:
assets: las etiquetas de estilo, precargas y cabezera que el build inyecta; van en el<head>.children: el HTML de tu aplicación ya renderizada; va dentro del contenedor.scripts: los scripts de hidratación que despertarán la página en el cliente; van al final del<body>.
Aquí está la mitad servidor del apretón de manos: <div id="app"> tiene que llevar el mismo id que busca entry-client. Ese contenedor es a la vez el destino donde el servidor deposita el HTML de la app y el ancla donde el cliente se engancha para hidratar. Si los dos identificadores no coinciden, el cliente no encuentra el DOM que debía adoptar y la hidratación se rompe.
Conviene ver createHandler como lo que es: el punto donde el render de Solid se conecta con Nitro y, por tanto, donde reside por defecto el streaming del SSR que estudiarás en la última lección. No hay que configurarlo para que transmita en trozos —es el comportamiento de fábrica—, pero saber que el modo de render se decide a la altura de este handler te ahorra buscarlo en app.tsx o en app.config.ts, donde no vive. Es un ejemplo más de la geografía del framework: cada decisión tiene un domicilio, y acertar con él es la mitad del oficio.
Cuándo tocarlos, y cuándo no
La regla de oro es que en el flujo normal no se tocan: son arranque de entorno, no el árbol de tu aplicación. Casi todo lo que un principiante intenta configurar aquí pertenece a app.tsx —los proveedores de contexto, el layout, el enrutador, las fronteras de carga y error— porque eso es la aplicación, y la aplicación es compartida. Meterlo en un entry point lo ataría a un solo entorno y rompería la simetría de la que vive el framework.
Las razones legítimas para editarlos son acotadas y casi todas viven en entry-server, por ser el dueño del documento:
Una edición legítima del entry de cliente se ve así: preparar el entorno del navegador justo antes de encender el árbol.
// entry-client.tsx: una razon legitima, preparar el navegador antes de montarimport { mount, StartClient } from "@solidjs/start/client";registrarServiceWorker(); // solo tiene sentido en el navegadormount(() => <StartClient />, document.getElementById("app")!);Fíjate en el patrón: se toca un entry point cuando lo que quieres es intrínseco a ese entorno concreto —el documento en el servidor, el arranque del navegador en el cliente—. Todo lo demás, por definición, es de la aplicación, y la aplicación vive en app.tsx. Ese criterio es más útil que cualquier lista, porque decide por ti incluso los casos que ninguna lista previó.
Un contraste afila la regla. Un proveedor de tema parece candidato a ir en un entry point porque suena a algo global y de arranque; pero un tema es estado de la aplicación, se comparte entre ambos entornos y debe existir en cada ruta, así que su sitio es la prop root del Router en app.tsx. En cambio, registrar un service worker no es estado de nada: es una gestión del navegador que no tiene análogo en el servidor y que debe ocurrir una sola vez al arrancar el cliente. El primero es aplicación; el segundo, arranque de entorno. Aplicar esa pregunta —si es de la app o del encendido de un entorno— resuelve el noventa por ciento de las dudas sobre dónde colocar algo.