UNPKG

@adonisjs/inertia

Version:

Official Inertia.js adapter for AdonisJS

419 lines (418 loc) 12.5 kB
import { t as InertiaHeaders } from "./headers-DafWEpBh.js"; import { t as debug_default } from "./debug-CBMTuPUm.js"; import "node:module"; import { createHash } from "node:crypto"; import { BaseSerializer } from "@adonisjs/core/transformers"; import { pathToFileURL } from "node:url"; var __defProp = Object.defineProperty; var __exportAll = (all, no_symbols) => { let target = {}; for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" }); return target; }; var symbols_exports = /* @__PURE__ */ __exportAll({ ALWAYS_PROP: () => ALWAYS_PROP, DEEP_MERGE: () => DEEP_MERGE, DEFERRED_PROP: () => DEFERRED_PROP, OPTIONAL_PROP: () => OPTIONAL_PROP, TO_BE_MERGED: () => TO_BE_MERGED }); const ALWAYS_PROP = Symbol.for("ALWAYS_PROP"); const OPTIONAL_PROP = Symbol.for("OPTIONAL_PROP"); const TO_BE_MERGED = Symbol.for("TO_BE_MERGED"); const DEFERRED_PROP = Symbol.for("DEFERRED_PROP"); const DEEP_MERGE = Symbol.for("DEEP_MERGE"); var InertiaSerializer = class extends BaseSerializer { wrap = void 0; definePaginationMetaData(metaData) { return metaData; } }; const inertiaSerializer = new InertiaSerializer(); function isObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } function defer(fn, group = "default") { return { group, compute: fn, merge() { return merge(this); }, [DEFERRED_PROP]: true }; } function optional(fn) { return { compute: fn, [OPTIONAL_PROP]: true }; } function always(value) { return { value, [ALWAYS_PROP]: true }; } function merge(value) { return { value, [TO_BE_MERGED]: true, [DEEP_MERGE]: false }; } function deepMerge(value) { return { value, [TO_BE_MERGED]: true, [DEEP_MERGE]: true }; } function isDeferredProp(propValue) { return DEFERRED_PROP in propValue; } function isMergeableProp(propValue) { return TO_BE_MERGED in propValue; } function isAlwaysProp(propValue) { return ALWAYS_PROP in propValue; } function isOptionalProp(propValue) { return OPTIONAL_PROP in propValue; } async function unpackPropValue(value, containerResolver) { return inertiaSerializer.serialize(value, containerResolver); } async function buildStandardVisitProps(pageProps, containerResolver) { const mergeProps = []; const deepMergeProps = []; const newProps = {}; const deferredProps = {}; const unpackedValues = []; for (const [key, value] of Object.entries(pageProps)) if (isObject(value)) { if (isDeferredProp(value)) { deferredProps[value.group] = deferredProps[value.group] ?? []; deferredProps[value.group].push(key); continue; } if (isOptionalProp(value)) continue; if (isAlwaysProp(value)) { unpackedValues.push({ key, value: value.value }); continue; } if (isMergeableProp(value)) { if (value[DEEP_MERGE]) deepMergeProps.push(key); else mergeProps.push(key); if (isObject(value.value) && isDeferredProp(value.value)) { deferredProps[value.value.group] = deferredProps[value.value.group] ?? []; deferredProps[value.value.group].push(key); unpackedValues.push({ key, value: value.value.compute }); } else unpackedValues.push({ key, value: value.value }); continue; } unpackedValues.push({ key, value }); } else { if (typeof value === "function") { unpackedValues.push({ key, value }); continue; } newProps[key] = value; } await Promise.all(unpackedValues.map(async ({ key, value }) => { if (typeof value === "function") return Promise.resolve(value()).then((r) => unpackPropValue(r, containerResolver)).then((jsonValue) => { newProps[key] = jsonValue; }); else return unpackPropValue(value, containerResolver).then((jsonValue) => { newProps[key] = jsonValue; }); })); return { props: newProps, mergeProps, deepMergeProps, deferredProps }; } async function buildPartialRequestProps(pageProps, cherryPickProps, containerResolver) { const mergeProps = []; const deepMergeProps = []; const newProps = {}; const unpackedValues = []; for (const [key, value] of Object.entries(pageProps)) if (isObject(value)) { if (isAlwaysProp(value)) { unpackedValues.push({ key, value: value.value }); continue; } if (!cherryPickProps.includes(key)) continue; if (isDeferredProp(value)) { unpackedValues.push({ key, value: value.compute }); continue; } if (isOptionalProp(value)) { unpackedValues.push({ key, value: value.compute }); continue; } if (isMergeableProp(value)) { if (value[DEEP_MERGE]) deepMergeProps.push(key); else mergeProps.push(key); if (isObject(value.value) && isDeferredProp(value.value)) unpackedValues.push({ key, value: value.value.compute }); else unpackedValues.push({ key, value: value.value }); continue; } unpackedValues.push({ key, value }); } else { if (!cherryPickProps.includes(key)) continue; if (typeof value === "function") { unpackedValues.push({ key, value }); continue; } newProps[key] = value; } await Promise.all(unpackedValues.map(async ({ key, value }) => { if (typeof value === "function") return Promise.resolve(value()).then((r) => unpackPropValue(r, containerResolver)).then((jsonValue) => { newProps[key] = jsonValue; }); else return unpackPropValue(value, containerResolver).then((jsonValue) => { newProps[key] = jsonValue; }); })); return { props: newProps, mergeProps, deepMergeProps, deferredProps: {} }; } var Inertia = class { #sharedStateProviders; #cachedRequestInfo; #serverRenderer; #vite; #shouldClearHistory; #shouldEncryptHistory; #cachedVersion; defer = defer; always = always; merge = merge; optional = optional; deepMerge = deepMerge; constructor(ctx, config, vite, serverRenderer) { this.ctx = ctx; this.config = config; if (debug_default.enabled) debug_default("instantiating inertia instance for request \"%s\" using config %O", ctx.request.url(), this.config); this.#shouldClearHistory = false; this.#vite = vite; this.#serverRenderer = serverRenderer; this.#shouldEncryptHistory = config.encryptHistory ?? false; this.#cachedVersion = this.config.assetsVersion ? String(this.config.assetsVersion) : void 0; } #resolveRootView() { return typeof this.config.rootView === "function" ? this.config.rootView(this.ctx) : this.config.rootView; } async #buildPageProps(component, requestInfo, pageProps) { let finalProps; if (this.#sharedStateProviders) finalProps = { ...await Promise.all(this.#sharedStateProviders.map((provider) => { return typeof provider === "function" ? provider() : provider; })).then((resolvedSharedState) => { return resolvedSharedState.reduce((result, state) => { return { ...result, ...state }; }, {}); }), ...pageProps }; else finalProps = { ...pageProps }; if (requestInfo.partialComponent === component) { const only = requestInfo.onlyProps; const except = requestInfo.exceptProps ?? []; const cherryPickProps = Object.keys(finalProps).filter((propName) => { if (only) return only.includes(propName) && !except.includes(propName); return !except.includes(propName); }); debug_default("building props for a partial reload %O", requestInfo); debug_default("cherry picking props %s", cherryPickProps); return buildPartialRequestProps(finalProps, cherryPickProps, this.ctx.containerResolver); } debug_default("building props for a standard visit %O", requestInfo); return buildStandardVisitProps(finalProps, this.ctx.containerResolver); } #handleInertiaRequest(pageObject) { this.ctx.response.header(InertiaHeaders.Inertia, "true"); return pageObject; } async #renderWithSSR(pageObject, viewProps) { if (!this.#serverRenderer) throw new Error("Cannot server render pages without a server renderer"); debug_default("server-side rendering %O", pageObject); const { head, body } = await this.#serverRenderer.render(pageObject); return this.ctx.view.render(this.#resolveRootView(), { page: { ssrHead: head, ssrBody: body, ...pageObject }, ...viewProps }); } async #renderClientSide(pageObject, viewProps) { debug_default("rendering shell for SPA %O", pageObject); return this.ctx.view.render(this.#resolveRootView(), { page: pageObject, ...viewProps }); } requestInfo(reCompute) { if (reCompute) this.#cachedRequestInfo = void 0; this.#cachedRequestInfo = this.#cachedRequestInfo ?? { version: this.ctx.request.header(InertiaHeaders.Version), isInertiaRequest: !!this.ctx.request.header(InertiaHeaders.Inertia), isPartialRequest: !!this.ctx.request.header(InertiaHeaders.PartialComponent), partialComponent: this.ctx.request.header(InertiaHeaders.PartialComponent), onlyProps: this.ctx.request.header(InertiaHeaders.PartialOnly)?.split(","), exceptProps: this.ctx.request.header(InertiaHeaders.PartialExcept)?.split(","), resetProps: this.ctx.request.header(InertiaHeaders.Reset)?.split(","), errorBag: this.ctx.request.header(InertiaHeaders.ErrorBag) }; return this.#cachedRequestInfo; } getVersion() { if (this.#cachedVersion) return this.#cachedVersion; if (this.#vite?.hasManifestFile) this.#cachedVersion = createHash("md5").update(JSON.stringify(this.#vite.manifest())).digest("hex"); else this.#cachedVersion = "1"; return this.#cachedVersion; } async ssrEnabled(component) { if (!this.config.ssr.enabled) return false; if (typeof this.config.ssr.pages === "function") return this.config.ssr.pages(this.ctx, component); if (this.config.ssr.pages) return this.config.ssr.pages?.includes(component); return true; } share(sharedState) { if (!this.#sharedStateProviders) this.#sharedStateProviders = []; this.#sharedStateProviders.push(sharedState); return this; } async page(page, pageProps) { const requestInfo = this.requestInfo(); const { props, mergeProps, deferredProps, deepMergeProps } = await this.#buildPageProps(page, requestInfo, pageProps); return { component: page, url: this.ctx.request.url(true), version: this.getVersion(), clearHistory: this.#shouldClearHistory, encryptHistory: this.#shouldEncryptHistory, props, deferredProps, mergeProps, deepMergeProps }; } async render(page, pageProps, viewProps) { const requestInfo = this.requestInfo(); const pageObject = await this.page(page, pageProps); if (requestInfo.isInertiaRequest) return this.#handleInertiaRequest(pageObject); if (await this.ssrEnabled(page)) return this.#renderWithSSR(pageObject, viewProps); return this.#renderClientSide(pageObject, viewProps); } clearHistory() { this.#shouldClearHistory = true; } encryptHistory(encrypt = true) { this.#shouldEncryptHistory = encrypt; } location(url) { this.ctx.response.header(InertiaHeaders.Location, url); this.ctx.response.status(409); } }; var ServerRenderer = class { #runtime; #ssrEnvironment; #config; #vite; constructor(config, vite) { this.#config = config; this.#vite = vite; } async render(pageObject) { let render; const devServer = this.#vite.getDevServer(); if (devServer) { debug_default("creating SSR bundle using dev-server"); const currentSsrEnv = devServer.environments.ssr; if (this.#ssrEnvironment !== currentSsrEnv) { if (this.#runtime) await this.#runtime.close(); this.#runtime = void 0; this.#ssrEnvironment = currentSsrEnv; } this.#runtime ??= await this.#vite.createModuleRunner(); this.#runtime.clearCache(); render = await this.#runtime.import(this.#config.ssr.entrypoint); } else { debug_default("creating SSR bundle using production build"); render = await import(pathToFileURL(this.#config.ssr.bundle).href); } const result = await render.default(pageObject); debug_default("SSR bundle %o", result); return { head: result.head, body: result.body }; } }; var InertiaManager = class { #vite; #config; #serverRenderer; constructor(config, vite) { this.#config = config; this.#vite = vite; this.#serverRenderer = this.#vite ? new ServerRenderer(this.#config, this.#vite) : void 0; } createForRequest(ctx) { return new Inertia(ctx, this.#config, this.#vite, this.#serverRenderer); } }; export { symbols_exports as i, ServerRenderer as n, Inertia as r, InertiaManager as t };