99 * Make sure to yarn add / npm install (in your project root)
1010 * anything you import here (except for express and compression).
1111 */
12- import express from 'express'
12+ import { Hono } from 'hono'
13+ import { serve } from '@hono/node-server'
14+ import { serveStatic } from '@hono/node-server/serve-static'
15+ import { lstatSync } from 'node:fs'
1316import {
1417 defineSsrCreate ,
18+ defineSsrInjectDevMiddleware ,
1519 defineSsrListen ,
1620 defineSsrClose ,
1721 defineSsrServeStaticContent ,
@@ -23,9 +27,49 @@ import {
2327 * If needed, prepare your webserver to receive
2428 * connect-like middlewares.
2529 *
26- * Should NOT be async!
30+ * Can be async: defineSsrCreate(async ({ ... }) => { ... })
2731 */
28- export const create = defineSsrCreate ( ( /* { ... } */ ) => express ( ) )
32+ export const create = defineSsrCreate ( async ( /* { ... } */ ) => {
33+ const app = new Hono ( )
34+
35+ // place here any middlewares that
36+ // absolutely need to run before anything else
37+ if ( import . meta. env . QUASAR_PROD ) {
38+ const { compress } = await import ( 'hono/compress' )
39+ app . use ( compress ( ) )
40+ }
41+
42+ return app
43+ } )
44+
45+ /**
46+ * Used by Quasar SSR dev server to inject middleware into the webserver.
47+ * It uses it to handle Vite dev server, handle public paths, etc.
48+ * The given middleware is compatible with `node:http`'s Server, Express, Connect, etc.
49+ *
50+ * Can be async: defineSsrInjectDevMiddleware(async ({ app }) => { ... })
51+ */
52+ export const injectDevMiddleware = defineSsrInjectDevMiddleware (
53+ ( { app } ) =>
54+ middleware => {
55+ app . use ( '*' , async ( c , next ) => {
56+ // @hono /node-server exposes the raw Node.js req and res objects
57+ const req = c . env . incoming
58+ const res = c . env . outgoing
59+
60+ // Run the connect-style middleware (Vite Dev Server)
61+ const passed = await new Promise ( resolve => {
62+ middleware ( req , res , ( ) => resolve ( true ) )
63+ } )
64+
65+ // If the Vite middleware calls next(), it didn't handle the request,
66+ // so we let Hono continue down the chain.
67+ if ( passed ) {
68+ await next ( )
69+ }
70+ } )
71+ }
72+ )
2973
3074/**
3175 * You need to make the server listen to the indicated port
@@ -37,15 +81,37 @@ export const create = defineSsrCreate((/* { ... } */) => express())
3781 *
3882 * For production, you can instead export your
3983 * handler for serverless use or whatever else fits your needs.
84+ *
85+ * Can be async: defineSsrListen(async ({ app, devHttpsApp, port }) => { ... })
4086 */
41- export const listen = defineSsrListen ( ( { app , devHttpsApp , port } ) => {
42- const server = devHttpsApp || app
43- return server . listen ( port , ( ) => {
44- if ( import . meta . env . QUASAR_PROD ) {
45- console . log ( 'Server listening at port ' + port )
87+ export const listen = defineSsrListen (
88+ async ( { app , devHttpsOptions , port } ) => {
89+ const opts = {
90+ fetch : app . fetch ,
91+ port
4692 }
47- } )
48- } )
93+
94+ /**
95+ * For production HTTPS you can use the /src-ssr/server-assets folder
96+ * to place your certificates and then read them here to create the server.
97+ */
98+
99+ if ( import . meta. env . QUASAR_DEV && devHttpsOptions ) {
100+ const { createServer } = await import ( 'node:https' )
101+ opts . createServer = createServer
102+ opts . serverOptions = { ...devHttpsOptions }
103+ } else {
104+ const { createServer } = await import ( 'node:http' )
105+ opts . createServer = createServer
106+ }
107+
108+ return serve ( opts , info => {
109+ if ( import . meta. env . QUASAR_PROD ) {
110+ console . log ( `🚀 Server listening at port ${ info . port } ` )
111+ }
112+ } )
113+ }
114+ )
49115
50116/**
51117 * Should close the server and free up any resources.
@@ -55,7 +121,7 @@ export const listen = defineSsrListen(({ app, devHttpsApp, port }) => {
55121 * Should you need the result of the "listen()" call above,
56122 * you can use the "listenResult" param.
57123 *
58- * Can be async.
124+ * Can be async: defineSsrClose(async ({ listenResult }) => { ... })
59125 */
60126export const close = defineSsrClose ( ( { listenResult } ) => listenResult . close ( ) )
61127
@@ -66,15 +132,40 @@ const maxAge = import.meta.env.QUASAR_DEV ? 0 : 1000 * 60 * 60 * 24 * 30
66132 * to serve static content at "urlPath" from "pathToServe" folder/file.
67133 *
68134 * Notice resolve.urlPath(urlPath) and resolve.public(pathToServe) usages.
135+ *
136+ * Can be async: defineSsrServeStaticContent(async ({ app, resolve }) => {
137+ * Can return an async function: return async ({ urlPath = '/', pathToServe = '.', opts = {} }) => {
69138 */
70139export const serveStaticContent = defineSsrServeStaticContent (
71- ( { app, resolve } ) =>
72- ( { urlPath = '/' , pathToServe = '.' , opts = { } } ) => {
73- const serveFn = express . static ( resolve . public ( pathToServe ) , {
74- maxAge,
75- ...opts
76- } )
77- app . use ( resolve . urlPath ( urlPath ) , serveFn )
140+ ( { app, publicPath, resolve } ) =>
141+ ( { urlPath, pathToServe, opts = { } } ) => {
142+ const pubPath = resolve . public ( pathToServe )
143+ const isDir = lstatSync ( publicPath ) . isDirectory ( )
144+
145+ const resolvedUrlPath = resolve . urlPath ( urlPath )
146+ const routePath = isDir
147+ ? resolvedUrlPath . endsWith ( '*' )
148+ ? resolvedUrlPath
149+ : `${ resolvedUrlPath } *`
150+ : resolvedUrlPath
151+
152+ const { maxAge : maxAgeOpt , ...serveOpts } = opts
153+ const cacheAge = maxAgeOpt !== void 0 ? maxAgeOpt : maxAge
154+
155+ if ( cacheAge > 0 ) {
156+ app . get ( routePath , async ( c , next ) => {
157+ c . header ( 'Cache-Control' , `public, max-age=${ cacheAge } ` )
158+ await next ( )
159+ } )
160+ }
161+
162+ app . use (
163+ routePath ,
164+ serveStatic ( {
165+ [ isDir ? 'root' : 'path' ] : pubPath ,
166+ ...serveOpts
167+ } )
168+ )
78169 }
79170)
80171
0 commit comments