|
| 1 | +/** |
| 2 | + * This module enables serving static files generated by Nextjs in production. |
| 3 | + * It's inspired by the project `electron-serve` developed by Sindre Sorhus. |
| 4 | + * https://github.com/sindresorhus/electron-serve |
| 5 | + * |
| 6 | + * It registers a custom protocol to serve files from a directory. |
| 7 | + * https://www.electronjs.org/docs/latest/api/protocol |
| 8 | + * |
| 9 | + * After porting my project to ESM, the `electron-serve` module was no longer |
| 10 | + * resolving the file paths correctly. |
| 11 | + * https://github.com/sindresorhus/electron-serve/issues/29 |
| 12 | + * |
| 13 | + * As a workaround, I re-implemented the logic here. |
| 14 | + */ |
| 15 | + |
| 16 | +import { app, net, protocol } from 'electron'; |
| 17 | +import path from 'node:path'; |
| 18 | +import { isSafePath } from './is-safe-path.js'; |
| 19 | +import { logger } from './logger.js'; |
| 20 | +import { pathToFileURL } from './path-to-file-url.js'; |
| 21 | + |
| 22 | +export const prodServe = (options: { |
| 23 | + /** |
| 24 | + * The protocol to serve the directory on. |
| 25 | + * All URL requests that use this protocol will be served from the directory. |
| 26 | + */ |
| 27 | + scheme: string; |
| 28 | + /** |
| 29 | + * The directory to serve, relative to the app root directory. |
| 30 | + */ |
| 31 | + dirPath: string; |
| 32 | +}): void => { |
| 33 | + const { scheme, dirPath } = options; |
| 34 | + |
| 35 | + logger.info('registering protocol scheme', { |
| 36 | + scheme, |
| 37 | + dirPath, |
| 38 | + }); |
| 39 | + |
| 40 | + const error404Page = pathToFileURL({ dirPath, filePath: '404.html' }); |
| 41 | + |
| 42 | + const requestHandler = async (httpReq: Request): Promise<Response> => { |
| 43 | + const requestURL = new URL(httpReq.url); |
| 44 | + |
| 45 | + let pageToServe = error404Page; |
| 46 | + |
| 47 | + let pathname = requestURL.pathname; |
| 48 | + if (pathname === '/') { |
| 49 | + pathname = 'index.html'; |
| 50 | + } |
| 51 | + |
| 52 | + // Prevent loading files outside of the renderer directory. |
| 53 | + const pathToServe = path.join(dirPath, pathname); |
| 54 | + const isSafe = isSafePath({ dirPath, filePath: pathToServe }); |
| 55 | + |
| 56 | + if (isSafe) { |
| 57 | + pageToServe = pathToFileURL({ dirPath, filePath: pathToServe }); |
| 58 | + } else { |
| 59 | + pageToServe = error404Page; |
| 60 | + } |
| 61 | + |
| 62 | + return net.fetch(pageToServe); |
| 63 | + }; |
| 64 | + |
| 65 | + protocol.registerSchemesAsPrivileged([ |
| 66 | + { |
| 67 | + scheme, |
| 68 | + privileges: { |
| 69 | + standard: true, |
| 70 | + secure: true, |
| 71 | + supportFetchAPI: true, |
| 72 | + allowServiceWorkers: true, |
| 73 | + }, |
| 74 | + }, |
| 75 | + ]); |
| 76 | + |
| 77 | + app.on('ready', () => { |
| 78 | + protocol.handle(scheme, requestHandler); |
| 79 | + }); |
| 80 | +}; |
0 commit comments