diff --git a/src/functions/server/web-pages/generate-web-html.tsx b/src/functions/server/web-pages/generate-web-html.tsx
index 8ed0a7c..3a581f5 100644
--- a/src/functions/server/web-pages/generate-web-html.tsx
+++ b/src/functions/server/web-pages/generate-web-html.tsx
@@ -1,10 +1,10 @@
import path from "path";
import grabContants from "../../../utils/grab-constants";
-import grabDirNames from "../../../utils/grab-dir-names";
import EJSON from "../../../utils/ejson";
import type { LivePageDistGenParams } from "../../../types";
import isDevelopment from "../../../utils/is-development";
import grabWebPageHydrationScript from "./grab-web-page-hydration-script";
+import grabWebMetaHTML from "./grab-web-meta-html";
export default async function genWebHTML({
component,
@@ -12,6 +12,7 @@ export default async function genWebHTML({
bundledMap,
head,
module,
+ meta,
}: LivePageDistGenParams) {
const { ClientRootElementIDName, ClientWindowPagePropsName } =
await grabContants();
@@ -23,24 +24,27 @@ export default async function genWebHTML({
const componentHTML = renderToString(component);
const headHTML = head ? renderToString(head) : "";
- // const SCRIPT_SRC = path.join("/public/pages", bundledMap.path);
- // const CSS_SRC = bundledMap.css_path
- // ? path.join("/public/pages", bundledMap.css_path)
- // : undefined;
- // const { HYDRATION_DST_DIR } = grabDirNames();
-
let html = `\n`;
html += `\n`;
html += `
\n`;
html += ` \n`;
html += ` \n`;
- if (bundledMap.css_path) {
+
+ if (meta) {
+ html += ` ${grabWebMetaHTML({ meta })}\n`;
+ }
+
+ if (bundledMap?.css_path) {
html += ` \n`;
}
+
html += ` \n`;
- html += ` \n`;
+
+ if (bundledMap?.path) {
+ html += ` \n`;
+ }
if (isDevelopment()) {
html += `\n`;
diff --git a/src/functions/server/web-pages/grab-page-component.tsx b/src/functions/server/web-pages/grab-page-component.tsx
index 3a7b028..6bdc0f6 100644
--- a/src/functions/server/web-pages/grab-page-component.tsx
+++ b/src/functions/server/web-pages/grab-page-component.tsx
@@ -2,7 +2,11 @@ import type { FC } from "react";
import grabDirNames from "../../../utils/grab-dir-names";
import grabRouteParams from "../../../utils/grab-route-params";
import grabRouter from "../../../utils/grab-router";
-import type { BunextPageModule, GrabPageComponentRes } from "../../../types";
+import type {
+ BundlerCTXMap,
+ BunextPageModule,
+ GrabPageComponentRes,
+} from "../../../types";
import path from "path";
import AppNames from "../../../utils/grab-app-names";
import { existsSync } from "fs";
@@ -92,6 +96,17 @@ export default async function grabPageComponent({
}
})();
+ const meta = module.meta
+ ? typeof module.meta == "function" && routeParams
+ ? await module.meta({
+ ctx: routeParams,
+ serverRes,
+ })
+ : typeof module.meta == "object"
+ ? module.meta
+ : undefined
+ : undefined;
+
const Component = module.default as FC;
const component = RootComponent ? (
@@ -108,24 +123,51 @@ export default async function grabPageComponent({
routeParams,
module,
bundledMap,
+ meta,
};
} catch (error: any) {
- // console.log(`Grab page component ERROR =>`, error.message);
+ try {
+ const match = router.match("/500");
+ const filePath = match?.filePath || BUNX_ROOT_500_PRESET_COMPONENT;
- const match = router.match("/500");
+ const bundledMap = match?.filePath
+ ? (global.BUNDLER_CTX_MAP?.find(
+ (m) => m.local_path === match.filePath,
+ ) ?? ({} as BundlerCTXMap))
+ : ({} as BundlerCTXMap);
- const filePath = match?.filePath || BUNX_ROOT_500_PRESET_COMPONENT;
+ const module: BunextPageModule = await import(filePath);
+ const Component = module.default as FC;
+ const component = ;
- const module: BunextPageModule = await import(filePath);
+ return {
+ component,
+ routeParams,
+ module,
+ bundledMap,
+ };
+ } catch {
+ const DefaultServerError: FC = () => (
+
+ 500 Internal Server Error
+
+ );
- const Component = module.default as FC;
- const component = ;
-
- return {
- component,
- routeParams,
- module,
- bundledMap: {},
- };
+ return {
+ component: ,
+ routeParams,
+ module: {
+ default: DefaultServerError,
+ },
+ };
+ }
}
}
diff --git a/src/functions/server/web-pages/grab-web-meta-html.ts b/src/functions/server/web-pages/grab-web-meta-html.ts
new file mode 100644
index 0000000..a307388
--- /dev/null
+++ b/src/functions/server/web-pages/grab-web-meta-html.ts
@@ -0,0 +1,63 @@
+import type { BunextPageModuleMeta } from "../../../types";
+
+type Params = {
+ meta: BunextPageModuleMeta;
+};
+
+export default async function grabWebMetaHTML({ meta }: Params) {
+ let html = ``;
+
+ if (meta.title) {
+ html += ` ${meta.title}\n`;
+ }
+
+ if (meta.description) {
+ html += ` \n`;
+ }
+
+ if (meta.keywords) {
+ const keywords = Array.isArray(meta.keywords)
+ ? meta.keywords.join(", ")
+ : meta.keywords;
+ html += ` \n`;
+ }
+
+ if (meta.author) {
+ html += ` \n`;
+ }
+
+ if (meta.robots) {
+ html += ` \n`;
+ }
+
+ if (meta.canonical) {
+ html += ` \n`;
+ }
+
+ if (meta.themeColor) {
+ html += ` \n`;
+ }
+
+ if (meta.og) {
+ const { og } = meta;
+ if (og.title) html += ` \n`;
+ if (og.description) html += ` \n`;
+ if (og.image) html += ` \n`;
+ if (og.url) html += ` \n`;
+ if (og.type) html += ` \n`;
+ if (og.siteName) html += ` \n`;
+ if (og.locale) html += ` \n`;
+ }
+
+ if (meta.twitter) {
+ const { twitter } = meta;
+ if (twitter.card) html += ` \n`;
+ if (twitter.title) html += ` \n`;
+ if (twitter.description) html += ` \n`;
+ if (twitter.image) html += ` \n`;
+ if (twitter.site) html += ` \n`;
+ if (twitter.creator) html += ` \n`;
+ }
+
+ return html;
+}
diff --git a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx
index e332cb2..d14fd93 100644
--- a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx
+++ b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx
@@ -5,7 +5,7 @@ import grabConstants from "../../../utils/grab-constants";
const { BUNX_HYDRATION_SRC_DIR } = grabDirNames();
type Params = {
- bundledMap: BundlerCTXMap;
+ bundledMap?: BundlerCTXMap;
};
export default async function ({ bundledMap }: Params) {
diff --git a/src/functions/server/web-pages/handle-web-pages.tsx b/src/functions/server/web-pages/handle-web-pages.tsx
index 706a3d4..d9d657e 100644
--- a/src/functions/server/web-pages/handle-web-pages.tsx
+++ b/src/functions/server/web-pages/handle-web-pages.tsx
@@ -8,14 +8,23 @@ type Params = {
export default async function ({ req }: Params): Promise {
try {
- const { component, bundledMap, module, serverRes } =
- await grabPageComponent({ req });
+ const {
+ component,
+ bundledMap,
+ module,
+ serverRes,
+ meta,
+ head,
+ routeParams,
+ } = await grabPageComponent({ req });
const html = await genWebHTML({
component,
pageProps: serverRes,
bundledMap,
module,
+ meta,
+ head,
});
const res_opts: ResponseInit = {
diff --git a/src/types/index.ts b/src/types/index.ts
index 3d3b85e..dfc46d6 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -128,12 +128,46 @@ export type LivePageDistGenParams = {
head?: ReactNode;
pageProps?: any;
module?: BunextPageModule;
- bundledMap: BundlerCTXMap;
+ bundledMap?: BundlerCTXMap;
+ meta?: BunextPageModuleMeta;
};
export type BunextPageModule = {
default: FC;
server?: BunextPageServerFn;
+ meta?: BunextPageModuleMeta | BunextPageModuleMetaFn;
+};
+
+export type BunextPageModuleMetaFn = (params: {
+ ctx: BunxRouteParams;
+ serverRes?: BunextPageModuleServerReturn;
+}) => Promise;
+
+export type BunextPageModuleMeta = {
+ title?: string;
+ description?: string;
+ keywords?: string | string[];
+ author?: string;
+ robots?: string;
+ canonical?: string;
+ themeColor?: string;
+ og?: {
+ title?: string;
+ description?: string;
+ image?: string;
+ url?: string;
+ type?: string;
+ siteName?: string;
+ locale?: string;
+ };
+ twitter?: {
+ card?: "summary" | "summary_large_image" | "app" | "player";
+ title?: string;
+ description?: string;
+ image?: string;
+ site?: string;
+ creator?: string;
+ };
};
export type BunextPageServerFn<
@@ -155,8 +189,10 @@ export type GrabPageComponentRes = {
component: JSX.Element;
serverRes?: BunextPageModuleServerReturn;
routeParams?: BunxRouteParams;
- bundledMap: BundlerCTXMap;
+ bundledMap?: BundlerCTXMap;
module: BunextPageModule;
+ meta?: BunextPageModuleMeta;
+ head?: ReactNode;
};
export type PageFiles = {