From 5c690ec13006b7513bc61a474457113e36317c50 Mon Sep 17 00:00:00 2001 From: Odin Thomas Rochmann Date: Thu, 12 Sep 2024 11:30:23 +0200 Subject: [PATCH] feat(cli): create plugin for serving index.html from external public directory (#2449) Create a plugin `externalPublicPlugin` to fix the issue with serving the `index.html` file from the specified external public directory. Vite mode `spa` will not serve the `index.html` file from the specified external public directory. - Enhanced the middleware to intercept requests and serve the `index.html` file from the specified external public directory. - Transformed the HTML using Vite's `transformIndexHtml` method. - Applied appropriate content headers and additional configured headers before sending the response. --- .changeset/hot-ears-fix.md | 30 ++++++++ .../cli/src/bin/plugins/external-public.ts | 71 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 .changeset/hot-ears-fix.md create mode 100644 packages/cli/src/bin/plugins/external-public.ts diff --git a/.changeset/hot-ears-fix.md b/.changeset/hot-ears-fix.md new file mode 100644 index 000000000..dda16d920 --- /dev/null +++ b/.changeset/hot-ears-fix.md @@ -0,0 +1,30 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +**@equinor/fusion-framework-cli:** + +Create a plugin `externalPublicPlugin` to fix the issue with serving the `index.html` file from the specified external public directory. Vite mode `spa` will not serve the `index.html` file from the specified external public directory. + +- Enhanced the middleware to intercept requests and serve the `index.html` file from the specified external public directory. +- Transformed the HTML using Vite's `transformIndexHtml` method. +- Applied appropriate content headers and additional configured headers before sending the response. + +```typescript +const viteConfig = { + roo + plugins: [ + // path wich contains the index.html file + externalPublicPlugin('./my-portal'), + ], +}; + +const viteConfig = defineConfig({ + // vite configuration + root: './src', // this where vite will look for the index.html file + plugins: [ + // path which contains the index.html file + externalPublicPlugin('./my-portal'), + ], +}); +``` diff --git a/packages/cli/src/bin/plugins/external-public.ts b/packages/cli/src/bin/plugins/external-public.ts new file mode 100644 index 000000000..5dbb3d9c1 --- /dev/null +++ b/packages/cli/src/bin/plugins/external-public.ts @@ -0,0 +1,71 @@ +import { join } from 'node:path'; +import { readFileSync } from 'node:fs'; + +import { type Plugin } from 'vite'; + +/** + * Creates a plugin that serves an external public directory. + * + * This plugin is useful when you want to serve a static site from a different directory than the one where the Vite server is running. + * Vite`s built in `mode: 'spa'` will only look for the `index.html` file in the configured `root` directory, + * so this plugin is necessary to serve the `index.html` file from a different directory. + * + * @param path - The path to the external public directory. + * @returns A Plugin object configured to serve the specified public directory. + * + * The plugin: + * - Sets the `publicDir` configuration to the provided path. + * - Adds a middleware to the server that serves the `index.html` file from the specified path. + * + * The middleware: + * - Reads the `index.html` file from the specified path. + * - Transforms the HTML using the server's `transformIndexHtml` method. + * - Responds with the transformed HTML, setting appropriate headers. + */ +export const externalPublicPlugin = (path: string): Plugin => { + return { + name: 'fusion:external-public', + apply: 'serve', + config(config) { + config.publicDir = path; + }, + configureServer(server) { + // intercept requests to serve the index.html file + server.middlewares.use(async (req, res, next) => { + if ( + // Only accept GET or HEAD + (req.method !== 'GET' && req.method !== 'HEAD') || + // Only accept text/html + !req.headers.accept?.includes('text/html') + ) { + return next(); + } + try { + // load the raw html from provided path + const htmlRaw = readFileSync(join(path, 'index.html'), 'utf-8'); + // transform the html, this is where vite plugin hooks are applied + const html = await server.transformIndexHtml( + req.url!, + htmlRaw, + req.originalUrl, + ); + + // apply content headers and configured additional headers + res.writeHead(200, { + 'content-type': 'text/html', + 'content-length': Buffer.byteLength(html), + 'cache-control': 'no-cache', + ...server.config.server.headers, + }); + + // send the transformed html and end the response + res.end(html); + } catch (e) { + next(e); + } + }); + }, + }; +}; + +export default externalPublicPlugin;