xpine 0.0.45 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,6 @@ XPine uses page based routing. Render an HTML page using JSX components, for exa
13
13
  ```
14
14
  import { WrapperProps } from 'xpine/dist/types';
15
15
  import Base from '../components/Base';
16
- import Navbar from '../components/Navbar';
17
16
 
18
17
  export const config = {
19
18
  data() {
@@ -156,6 +155,19 @@ You can create catch all routes by naming the file \_all\_.(jsx|tsx|js|ts). You
156
155
 
157
156
  You can get the route param in your function with req.params[0], such as how express handles catch all routes.
158
157
 
158
+ ### Route specific middleware
159
+
160
+ If you need route specific middleware, e.g. for file uploads, you can specify a `routeMiddleware` function in a config variable in the endpoint file:
161
+
162
+ ```
163
+ export const config = {
164
+ routeMiddleware(req, res, next) {
165
+ console.log('route middleware');
166
+ next();
167
+ }
168
+ }
169
+ ```
170
+
159
171
  ### Static Site Generation
160
172
 
161
173
  Generate path specific static pages by specifying in the config of either the page's file, such as `/src/pages/about.tsx` with a config export:
@@ -296,14 +308,61 @@ export default async function startServer() {
296
308
  }
297
309
  ```
298
310
 
311
+ Add a directory in `/src/server` called `run`, and create two files: `/src/server/dev.ts` and `/src/server/prod.ts`.
312
+
313
+ `dev.ts` should look like this:
314
+
315
+ ```
316
+ import { runDevServer } from 'xpine';
317
+
318
+ runDevServer();
319
+ ```
320
+
321
+ and can be run with an npm command like this in your package.json scripts: `"dev": "PORT=8888 LOCALHOST=1 xpine-dev"`.
322
+
323
+ The `prod.ts` should look like this:
324
+
325
+ ```
326
+ import startServer from '../app';
327
+
328
+ await startServer();
329
+ ```
330
+
331
+ and can be run with an npm command in your package.json scripts like this, after the app has been built with `xpine-build`:
332
+
333
+ `"start": "PORT=8888 node ./dist/server/run/prod.js"`
334
+
299
335
  ### xpine.config.mjs file
300
336
 
301
- Add an xpine.config.mjs file to your root directory. This is used primarily for configuring file paths
337
+ Add an xpine.config.mjs file to your root directory. This is used primarily for configuring/changing file paths. Configs can be imported with `import { config } from "xpine"`.
302
338
 
303
339
  ```
304
340
  export default {}
305
341
  ```
306
342
 
343
+ These are the following default paths:
344
+
345
+ ```
346
+ rootDir: process.cwd
347
+ srcDir: rootDir + ./src
348
+ distDir: rootDir + ./dist
349
+ packageJsonPath: rootDir + ./package.json
350
+ distPublicDir: distDir + ./public
351
+ distPublicScriptsDir: distPublicDir + ./scripts
352
+ distTempFolder: distDir + ./temp
353
+ clientJSBundlePath: distPublicScriptsDir + ./app.js
354
+ alpineDataPath: distTempFolder + ./alpine-data.ts
355
+ serverDistDir: distDir + ./server
356
+ serverDistAppPath: serverDistDir + ./app.js
357
+ pagesDir: srcDir + ./pages
358
+ distPagesDir: distDir + ./pages
359
+ publicDir: srcDir + ./public
360
+ serverDir: srcDir + ./server
361
+ runDir: serverDir + ./run
362
+ serverAppPath: serverDir + ./app.ts
363
+ globalCSSFile: publicDir + ./styles/global.css
364
+ ```
365
+
307
366
  ### SPA interactivity
308
367
 
309
368
  - data-spa="true"
package/TODO CHANGED
@@ -0,0 +1,2 @@
1
+ [ ] folder specific client side JS builds
2
+ [ ] TypeError: Cannot read properties of undefined (reading 'find')
package/dist/index.d.ts CHANGED
@@ -6,4 +6,5 @@ export * from './src/scripts/build';
6
6
  export * from './src/auth';
7
7
  export * from './src/util/html';
8
8
  export * from './src/context';
9
+ export * from './src/util/web';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -531,14 +531,12 @@ async function createRouteFunction(route, configFiles) {
531
531
  }
532
532
  let config2 = {};
533
533
  const configFilePaths = getConfigFiles(route.originalRoute, configFiles);
534
- if (!isDev) {
535
- config2 = configFilePaths && await getCompleteConfig(configFilePaths, Date.now());
536
- if (componentImport?.config) {
537
- config2 = {
538
- ...config2,
539
- ...componentImport.config
540
- };
541
- }
534
+ config2 = configFilePaths && await getCompleteConfig(configFilePaths, Date.now());
535
+ if (componentImport?.config) {
536
+ config2 = {
537
+ ...config2,
538
+ ...componentImport.config
539
+ };
542
540
  }
543
541
  async function routeFn(req, res) {
544
542
  const urlPath = req?.route?.path || "/";
@@ -588,6 +586,7 @@ async function createRouteFunction(route, configFiles) {
588
586
  formattedRouteItem,
589
587
  foundMethod,
590
588
  route,
589
+ config: config2,
591
590
  routeFn
592
591
  };
593
592
  }
@@ -599,8 +598,12 @@ async function createRouter() {
599
598
  await triggerXPineOnLoad();
600
599
  for (const route of routeMap) {
601
600
  try {
602
- const { formattedRouteItem, foundMethod, routeFn } = await createRouteFunction(route, configFiles);
603
- router[foundMethod || "get"](formattedRouteItem, routeFn);
601
+ const { formattedRouteItem, foundMethod, config: config2, routeFn } = await createRouteFunction(route, configFiles);
602
+ if (config2?.routeMiddleware) {
603
+ router[foundMethod || "get"](formattedRouteItem, config2.routeMiddleware, routeFn);
604
+ } else {
605
+ router[foundMethod || "get"](formattedRouteItem, routeFn);
606
+ }
604
607
  routeResults.push({
605
608
  formattedRouteItem,
606
609
  foundMethod,
@@ -787,7 +790,7 @@ async function buildAppFiles(files, isDev2) {
787
790
  const fileName = file.split("/").at(-1).split(".").shift();
788
791
  return fileName === "+config";
789
792
  });
790
- const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
793
+ const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
791
794
  fs7.ensureDirSync(config.distDir);
792
795
  await build({
793
796
  entryPoints: backendFiles,
@@ -918,7 +921,7 @@ async function buildPublicFolderSymlinks() {
918
921
  async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
919
922
  const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
920
923
  const fileSizes = files.map((file) => {
921
- if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
924
+ if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
922
925
  return {
923
926
  file,
924
927
  size: fs7.statSync(file).size / (1024 * 1e3)
@@ -1108,7 +1111,9 @@ async function runDevServer() {
1108
1111
  ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path8.join(config.serverDir, "./prisma"))
1109
1112
  });
1110
1113
  watcher.on("all", async (event, path9) => {
1111
- const shouldReloadServer = path9.startsWith(config.serverDir) && !path9.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path9.startsWith(config.pagesDir);
1114
+ const isRegularExpressRoute = path9.startsWith(config.pagesDir) && (path9.endsWith(".ts") || path9.endsWith(".js"));
1115
+ const isServerDir = path9.startsWith(config.serverDir) && !path9.startsWith(config.runDir);
1116
+ const shouldReloadServer = isServerDir || isRegularExpressRoute || ["add", "unlink"].includes(event) && path9.startsWith(config.pagesDir);
1112
1117
  if (shouldReloadServer) {
1113
1118
  await asyncServerClose(appServer.server);
1114
1119
  rebuildEmitter.emit("rebuild-server");
@@ -1201,6 +1206,11 @@ var html = class {
1201
1206
  function JSXRuntime() {
1202
1207
  return true;
1203
1208
  }
1209
+
1210
+ // src/util/web.ts
1211
+ function getCurrentBreakpoint() {
1212
+ return window.getComputedStyle(document.documentElement).getPropertyValue("--active-breakpoint")?.replace(/[\'\"]/g, "")?.trim() || "";
1213
+ }
1204
1214
  export {
1205
1215
  JSXRuntime,
1206
1216
  addToContextArray,
@@ -1217,6 +1227,7 @@ export {
1217
1227
  filePathToURLPath,
1218
1228
  fromRoot,
1219
1229
  getComponentDynamicPaths,
1230
+ getCurrentBreakpoint,
1220
1231
  getTokenFromRequest,
1221
1232
  html,
1222
1233
  logSize,
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;AAsJ5E,wBAAsB,YAAY;;;GA8BjC;AAiBD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,iBAyB1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,KAAK,CAanG;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,UAQzE"}
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAgB,OAAO,EAAqB,MAAM,SAAS,CAAC;AAqJ5E,wBAAsB,YAAY;;;GAkCjC;AAiBD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,iBAyB1F;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,KAAK,CAanG;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,OAAc,UAQzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"runDevServer.d.ts","sourceRoot":"","sources":["../../src/runDevServer.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,kBA6DjC"}
1
+ {"version":3,"file":"runDevServer.d.ts","sourceRoot":"","sources":["../../src/runDevServer.ts"],"names":[],"mappings":"AAYA,wBAAsB,YAAY,kBA+DjC"}
@@ -583,7 +583,7 @@ async function buildAppFiles(files, isDev3) {
583
583
  const fileName = file.split("/").at(-1).split(".").shift();
584
584
  return fileName === "+config";
585
585
  });
586
- const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
586
+ const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
587
587
  fs7.ensureDirSync(config.distDir);
588
588
  await build({
589
589
  entryPoints: backendFiles,
@@ -714,7 +714,7 @@ async function buildPublicFolderSymlinks() {
714
714
  async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
715
715
  const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
716
716
  const fileSizes = files.map((file) => {
717
- if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
717
+ if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
718
718
  return {
719
719
  file,
720
720
  size: fs7.statSync(file).size / (1024 * 1e3)
@@ -590,7 +590,7 @@ async function buildAppFiles(files, isDev2) {
590
590
  const fileName = file.split("/").at(-1).split(".").shift();
591
591
  return fileName === "+config";
592
592
  });
593
- const backendFiles = files.filter((file) => extensions.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
593
+ const backendFiles = files.filter((file) => extensions?.find((ext) => file.endsWith(ext))).filter((file) => !file.startsWith(config.publicDir));
594
594
  fs7.ensureDirSync(config.distDir);
595
595
  await build({
596
596
  entryPoints: backendFiles,
@@ -721,7 +721,7 @@ async function buildPublicFolderSymlinks() {
721
721
  async function logSize(pathName, type, validExtensions = [".js", ".css"]) {
722
722
  const files = globSync3(pathName + "/**/*" + (type === "css" ? ".css" : ""));
723
723
  const fileSizes = files.map((file) => {
724
- if (!validExtensions.find((ext) => file.endsWith(ext))) return false;
724
+ if (!validExtensions?.find((ext) => file.endsWith(ext))) return false;
725
725
  return {
726
726
  file,
727
727
  size: fs7.statSync(file).size / (1024 * 1e3)
@@ -911,7 +911,9 @@ async function runDevServer() {
911
911
  ignored: (pathName) => pathName.endsWith(".map") || pathName.startsWith(path7.join(config.serverDir, "./prisma"))
912
912
  });
913
913
  watcher.on("all", async (event, path8) => {
914
- const shouldReloadServer = path8.startsWith(config.serverDir) && !path8.startsWith(config.runDir) || ["add", "unlink"].includes(event) && path8.startsWith(config.pagesDir);
914
+ const isRegularExpressRoute = path8.startsWith(config.pagesDir) && (path8.endsWith(".ts") || path8.endsWith(".js"));
915
+ const isServerDir = path8.startsWith(config.serverDir) && !path8.startsWith(config.runDir);
916
+ const shouldReloadServer = isServerDir || isRegularExpressRoute || ["add", "unlink"].includes(event) && path8.startsWith(config.pagesDir);
915
917
  if (shouldReloadServer) {
916
918
  await asyncServerClose(appServer.server);
917
919
  rebuildEmitter.emit("rebuild-server");
@@ -0,0 +1,2 @@
1
+ export declare function getCurrentBreakpoint(): string;
2
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../../src/util/web.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,WAEnC"}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Request, Response } from 'express';
1
+ import { NextFunction, Request, Response } from 'express';
2
2
  import ts from 'typescript';
3
3
  export type XPineConfig = {
4
4
  [key: string]: any;
@@ -24,6 +24,7 @@ export type ConfigFile = {
24
24
  }[]>);
25
25
  wrapper?: (props: WrapperProps) => Promise<any>;
26
26
  data?: (req: ServerRequest) => Promise<any>;
27
+ routeMiddleware?: (req: ServerRequest, res: Response, next: NextFunction) => void;
27
28
  };
28
29
  export type PageProps = {
29
30
  req: ServerRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC7C,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,aAAa,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC;CACvB,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG;IACpC,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,aAAa,CAAC;IACnB,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CACnF,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,GAAG,EAAE,aAAa,CAAC;IACnB,GAAG,EAAE,QAAQ,CAAC;IACd,IAAI,EAAE,GAAG,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;CACnB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC;CACvB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpine",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "main": "dist/index.js",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-secrets-manager": "^3.758.0",
package/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Request, Response } from 'express';
1
+ import { NextFunction, Request, Response } from 'express';
2
2
  import ts from 'typescript';
3
3
 
4
4
  export type XPineConfig = {
@@ -27,6 +27,7 @@ export type ConfigFile = {
27
27
  staticPaths?: boolean | (() => Promise<{ [key: string]: string }[]>);
28
28
  wrapper?: (props: WrapperProps) => Promise<any>;
29
29
  data?: (req: ServerRequest) => Promise<any>;
30
+ routeMiddleware?: (req: ServerRequest, res: Response, next: NextFunction) => void;
30
31
  }
31
32
 
32
33
  export type PageProps = {