deno.land / x / ultra@v2.3.8 / lib / ultra.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220import type { ReactElement } from "react";import { ULTRA_COMPILER_PATH } from "./constants.ts";import { fromFileUrl, Hono, logger, relative, resolve, sprintf,} from "./deps.ts";import { log } from "./logger.ts";import { renderToStream } from "./render.ts";import { Context, Env, ImportMap, Mode } from "./types.ts";import { toUltraUrl } from "./utils/url.ts";
type UltraServerRenderOptions = { entrypoint?: string; generateStaticHTML?: boolean; enableEsModuleShims?: boolean; disableHydration?: boolean; flushEffectsToHead?: boolean;};
type UltraServerOptions = { mode: Mode; importMapPath?: string; assetManifestPath: string; enableEsModuleShims?: boolean; esModuleShimsPath?: string; entrypoint?: string;};
export class UltraServer< E extends Env = Env, // deno-lint-ignore ban-types S = {}, BasePath extends string = "/",> extends Hono<E, S, BasePath> { public importMap: ImportMap | undefined; public assetManifest: Map<string, string> | undefined;
public mode: Mode; public baseUrl: string; public importMapPath?: string; public assetManifestPath: string; public enableEsModuleShims?: boolean; public esModuleShimsPath?: string; public entrypoint?: string; public ultraDir?: string;
constructor( public root: string, options: UltraServerOptions, ) { super();
this.mode = options.mode; this.importMapPath = options.importMapPath; this.assetManifestPath = options.assetManifestPath; this.enableEsModuleShims = options.enableEsModuleShims; this.esModuleShimsPath = options.esModuleShimsPath; this.entrypoint = options.entrypoint; this.baseUrl = this.mode === "development" ? `${ULTRA_COMPILER_PATH}/` : "/";
const logFn = options.mode === "development" ? (message: string) => log.info(message) : (message: string) => console.info(message);
this.use("*", logger(logFn)); }
async init() { log.debug("Initialising server");
/** * Parse the importMap if provided */ this.importMap = this.importMapPath ? await this.#parseJsonFile(this.importMapPath) : undefined;
/** * Parse the provided asset manifest if we have an entrypoint */ const assetManifest: [string, string][] | undefined = this.mode === "production" ? await this.#parseJsonFile(this.assetManifestPath) : undefined;
this.assetManifest = assetManifest ? new Map(assetManifest) : undefined;
/** * Prepare the entrypoint if provided an importMap */ if (this.importMap) { this.#importMapHandler(this.importMap); this.entrypoint = this.#prepareEntrypoint( this.importMap, this.entrypoint, ); }
// Validate this.#valid(); }
render( Component: ReactElement, options?: UltraServerRenderOptions, ) { return this.renderWithContext(Component, undefined, options); }
renderWithContext( Component: ReactElement, context: Context | undefined, options?: UltraServerRenderOptions, ) { log.debug("Rendering component");
let bootstrapModules: string[] = [];
if (options?.entrypoint) { options.entrypoint = options?.entrypoint && this.importMap ? this.#prepareEntrypoint(this.importMap, options.entrypoint) : undefined; }
const entrypoint = options?.entrypoint || this.entrypoint;
if (entrypoint) { bootstrapModules = [ entrypoint.startsWith("file://") ? fromFileUrl(entrypoint) : entrypoint, ]; }
return renderToStream(Component, context, { mode: this.mode, baseUrl: this.baseUrl, assetManifest: this.assetManifest, importMap: this.importMap, enableEsModuleShims: this.enableEsModuleShims, esModuleShimsPath: this.esModuleShimsPath, bootstrapModules, ...options, }); }
async #parseJsonFile<T>(path: string): Promise<T> { try { log.debug(sprintf("Parsing JSON: %s", path));
const bytes = await fetch(path).then((response) => response.arrayBuffer() ); const content = new TextDecoder().decode(bytes);
const json = JSON.parse(content); log.debug(json);
return json; } catch (cause) { throw new Error(sprintf("Failed to parse JSON file at path: %s", path), { cause, }); } }
#importMapHandler(importMap: ImportMap | undefined) { if (importMap?.imports) { const ultraUrl = importMap.imports["ultra/"]; // Set importMap for ultra/ framework if (ultraUrl && !ultraUrl.startsWith("http")) { if (ultraUrl.startsWith("/")) { this.ultraDir = ultraUrl; } else { this.ultraDir = resolve(Deno.cwd(), ultraUrl); } importMap.imports["ultra/"] = "/ultra/"; } } }
#valid() { // Check we have an importMap if we we're provided an entrypoint if (!this.importMap && this.entrypoint) { throw new Error("Import map has not been parsed."); } }
#prepareEntrypoint(importMap: ImportMap, entrypoint?: string) { if (!entrypoint) { log.debug("No entrypoint provided, hydration disabled."); return entrypoint; }
log.debug(sprintf("Resolving entrypoint: %s", entrypoint));
let entrypointSpecifier = `./${ relative(this.root, fromFileUrl(entrypoint)) }`;
if (this.mode === "production") { for (const [specifier, resolved] of Object.entries(importMap.imports)) { if (specifier === entrypointSpecifier) { entrypointSpecifier = resolved.replace("./", "/"); } } } else { entrypointSpecifier = toUltraUrl(this.root, entrypoint, this.mode)!; }
log.debug(sprintf("Resolved entrypoint: %s", entrypointSpecifier));
return entrypointSpecifier; }}
Version Info