import { createComponent, generateHydrationScript, NoHydration, renderToString, renderToStringAsync, ssr, Suspense, } from 'solid-js/web'; import { getContext, incrementId } from './context.js'; import type { RendererContext } from './types.js'; const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase()); type RenderStrategy = 'sync' | 'async'; async function check( this: RendererContext, Component: any, props: Record, children: any ) { if (typeof Component !== 'function') return false; if (Component.name === 'QwikComponent') return false; // There is nothing particularly special about Solid components. Basically they are just functions. // In general, components from other frameworks (eg, MDX, React, etc.) tend to render as "undefined", // so we take advantage of this trick to decide if this is a Solid component or not. const { html } = await renderToStaticMarkup.call(this, Component, props, children, { // The purpose of check() is just to validate that this is a Solid component and not // React, etc. We should render in sync mode which should skip Suspense boundaries // or loading resources like external API calls. renderStrategy: 'sync' as RenderStrategy, }); return typeof html === 'string'; } // AsyncRendererComponentFn async function renderToStaticMarkup( this: RendererContext, Component: any, props: Record, { default: children, ...slotted }: any, metadata?: undefined | Record ) { const ctx = getContext(this.result); const renderId = metadata?.hydrate ? incrementId(ctx) : ''; const needsHydrate = metadata?.astroStaticSlot ? !!metadata.hydrate : true; const tagName = needsHydrate ? 'astro-slot' : 'astro-static-slot'; const renderStrategy = (metadata?.renderStrategy ?? 'async') as RenderStrategy; const renderFn = () => { const slots: Record = {}; for (const [key, value] of Object.entries(slotted)) { const name = slotName(key); slots[name] = ssr(`<${tagName} name="${name}">${value}`); } // Note: create newProps to avoid mutating `props` before they are serialized const newProps = { ...props, ...slots, // In Solid SSR mode, `ssr` creates the expected structure for `children`. children: children != null ? ssr(`<${tagName}>${children}`) : children, }; if (renderStrategy === 'sync') { // Sync Render: // // This render mode is not exposed directly to the end user. It is only // used in the check() function. return createComponent(Component, newProps); } else { if (needsHydrate) { // Hydrate + Async Render: // // // return createComponent(Suspense, { get children() { return createComponent(Component, newProps); }, }); } else { // Static + Async Render // // // // // return createComponent(NoHydration, { get children() { return createComponent(Suspense, { get children() { return createComponent(Component, newProps); }, }); }, }); } } }; const componentHtml = renderStrategy === 'async' ? await renderToStringAsync(renderFn, { renderId, // New setting since Solid 1.8.4 that fixes an errant hydration event appearing in // server only components. Not available in TypeScript types yet. // https://github.com/solidjs/solid/issues/1931 // https://github.com/ryansolid/dom-expressions/commit/e09e255ac725fd59195aa0f3918065d4bd974e6b ...({ noScripts: !needsHydrate } as any), }) : renderToString(renderFn, { renderId }); return { attrs: { 'data-solid-render-id': renderId, }, html: componentHtml, }; } export default { check, renderToStaticMarkup, supportsAstroStaticSlot: true, renderHydrationScript: () => generateHydrationScript(), };