@adonisjs/inertia
Version:
Official Inertia.js adapter for AdonisJS
419 lines (418 loc) • 12.5 kB
JavaScript
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 };