@quasar/app-webpack
Version:
Quasar Framework App CLI with Webpack
167 lines (146 loc) • 4.95 kB
text/typescript
/**
* More info about this file:
* https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/ssr-webserver
*
* Runs in Node context.
*/
/**
* Make sure to yarn add / npm install (in your project root)
* anything you import here (except for express and compression).
*/
import type { Server } from 'node:http';
import compression from 'compression';
import type { Application, Request, Response } from 'express';
import express from 'express';
import {
defineSsrCreate,
defineSsrInjectDevMiddleware,
defineSsrListen,
defineSsrClose,
defineSsrServeStaticContent,
defineSsrRenderPreloadTag
} from '#q-app/wrappers';
declare module '#q-app' {
interface SsrDriver {
app: Application;
listenResult: Server;
request: Request;
response: Response;
}
}
/**
* Create your webserver and return its instance.
* If needed, prepare your webserver to receive
* connect-like middlewares.
*
* Can be async: defineSsrCreate(async ({ ... }) => { ... })
*/
export const create = defineSsrCreate((/* { ... } */) => {
const app = express();
// attackers can use this header to detect apps running Express
// and then launch specifically-targeted attacks
app.disable('x-powered-by');
// place here any middlewares that
// absolutely need to run before anything else
if (process.env.PROD) {
app.use(compression());
}
return app;
});
/**
* Used by Quasar SSR dev server to inject middleware into the webserver.
* It uses it to handle Webpack dev server, handle public paths, etc.
* The given middleware is compatible with `node:http`'s Server, Express, Connect, etc.
*
* Can be async: defineSsrInjectDevMiddleware(async ({ app }) => { ... })
*/
export const injectDevMiddleware = defineSsrInjectDevMiddleware(({ app }) => {
return (middleware) => {
app.use(middleware);
};
});
/**
* You need to make the server listen to the indicated port
* and return the listening instance or whatever you need to
* close the server with.
*
* The "listenResult" param for the "close()" definition below
* is what you return here.
*
* For production, you can instead export your
* handler for serverless use or whatever else fits your needs.
*
* Can be async: defineSsrListen(async ({ app, devHttpsApp, port }) => { ... })
*/
export const listen = defineSsrListen(({ app, devHttpsApp, port }) => {
const server = devHttpsApp || app;
return server.listen(port, () => {
if (process.env.PROD) {
console.log('Server listening at port ' + port);
}
});
});
/**
* Should close the server and free up any resources.
* Will be used on development only when the server needs
* to be rebooted.
*
* Should you need the result of the "listen()" call above,
* you can use the "listenResult" param.
*
* Can be async: defineSsrClose(async ({ listenResult }) => { ... }))
*/
export const close = defineSsrClose(({ listenResult }) => {
return listenResult.close();
});
const maxAge = process.env.DEV ? 0 : 1000 * 60 * 60 * 24 * 30;
/**
* Should return a function that will be used to configure the webserver
* to serve static content at "urlPath" from "pathToServe" folder/file.
*
* Notice resolve.urlPath(urlPath) and resolve.public(pathToServe) usages.
*
* Can be async: defineSsrServeStaticContent(async ({ app, resolve }) => {
* Can return an async function: return async ({ urlPath = '/', pathToServe = '.', opts = {} }) => {
*/
export const serveStaticContent = defineSsrServeStaticContent(({ app, resolve }) => {
return ({ urlPath = '/', pathToServe = '.', opts = {} }) => {
const serveFn = express.static(resolve.public(pathToServe), { maxAge, ...opts });
app.use(resolve.urlPath(urlPath), serveFn);
};
});
const jsRE = /\.js$/;
const cssRE = /\.css$/;
const woffRE = /\.woff$/;
const woff2RE = /\.woff2$/;
const gifRE = /\.gif$/;
const jpgRE = /\.jpe?g$/;
const pngRE = /\.png$/;
/**
* Should return a String with HTML output
* (if any) for preloading indicated file
*/
export const renderPreloadTag = defineSsrRenderPreloadTag((file/* , { ssrContext } */) => {
if (jsRE.test(file) === true) {
return `<script src="${file}" defer crossorigin></script>`;
}
if (cssRE.test(file) === true) {
return `<link rel="stylesheet" href="${file}" crossorigin>`;
}
if (woffRE.test(file) === true) {
return `<link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`;
}
if (woff2RE.test(file) === true) {
return `<link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`;
}
if (gifRE.test(file) === true) {
return `<link rel="preload" href="${file}" as="image" type="image/gif" crossorigin>`;
}
if (jpgRE.test(file) === true) {
return `<link rel="preload" href="${file}" as="image" type="image/jpeg" crossorigin>`;
}
if (pngRE.test(file) === true) {
return `<link rel="preload" href="${file}" as="image" type="image/png" crossorigin>`;
}
return '';
});