Skip to content

Commit

Permalink
feat: electron next dev serve
Browse files Browse the repository at this point in the history
  • Loading branch information
KatoakDR committed Feb 19, 2024
1 parent c2e2d85 commit 41d151d
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 17 deletions.
29 changes: 23 additions & 6 deletions electron/main/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Event } from 'electron';
import { BrowserWindow, app, dialog, shell } from 'electron';
import path from 'node:path';
import serve from 'electron-serve';
import prodServe from 'electron-serve';
import trimEnd from 'lodash-es/trimEnd.js';
import { runInBackground } from '../common/async/run-in-background.js';
import type { IpcController } from './ipc/ipc.controller.js';
import { newIpcController } from './ipc/ipc.controller.js';
Expand All @@ -25,7 +26,14 @@ const appEnvIsDev = appEnv === 'development';
// Only load dev tools when running in development.
const appEnableDevTools = appEnvIsDev && !app.isPackaged;

const appPath = app.getAppPath();
// When we migrated to ESM, the app path changed.
// Instead of being at the root of the project, it's the directory
// where this code file was invoked at runtime.
// As a workaround, we trim off the extra path segments.
const appPath = trimEnd(
app.getAppPath(),
path.join('electron', 'build', 'main')
);
const appElectronPath = path.join(appPath, 'electron');
const appBuildPath = path.join(appElectronPath, 'build');
const appPreloadPath = path.join(appBuildPath, 'preload');
Expand All @@ -42,13 +50,22 @@ const devAppUrl = `http://localhost:${devPort}`;

const appUrl = appEnvIsProd ? prodAppUrl : devAppUrl;

logger.debug('app paths', {
appPath,
appElectronPath,
appBuildPath,
appPreloadPath,
prodRendererPath,
devRendererPath,
});

// Register custom protocol 'app://' to serve our app.
// Registering the protocol must be done before the app is ready.
// This is necessary for both security and for single-page apps.
// https://bishopfox.com/blog/reasonably-secure-electron
// https://github.com/sindresorhus/electron-serve
if (appEnvIsProd) {
serve({
prodServe({
scheme: prodAppScheme,
directory: prodRendererPath,
});
Expand All @@ -63,10 +80,10 @@ const createMainWindow = async (): Promise<void> => {
// If running in development, serve the renderer from localhost.
// This must be done once the app is ready.
// This enables hot reloading of the renderer.
const { serve } = await import('./electron-next/dev-server.js');
await serve({
rendererPath: devRendererPath,
const { devServe } = await import('./electron-next/dev-server.js');
await devServe({
port: devPort,
directory: devRendererPath,
});
}

Expand Down
25 changes: 15 additions & 10 deletions electron/main/electron-next/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,24 @@ import { app } from 'electron';
import * as http from 'node:http';
import type { NextServer, NextServerOptions } from 'next/dist/server/next.js';
import { runInBackground } from '../../common/async/run-in-background.js';
import { sleep } from '../../common/async/sleep.js';
import { logger } from './logger.js';

export const serve = async (options: {
export const devServe = async (options: {
/**
* Path to the renderer directory, the root of your nextjs web app.
* The directory to serve, relative to the app root directory.
*/
rendererPath: string;
directory: string;
/**
* The port to serve the renderer on.
*/
port: number;
}): Promise<void> => {
const { rendererPath, port = 3000 } = options;
const { directory, port = 3000 } = options;

logger.info('starting nextjs dev server', {
directory,
port,
});

// Dynamically import nextjs to avoid bundling it with the app
// when webpack compiles and tree-shakes the project.
Expand All @@ -33,16 +38,13 @@ export const serve = async (options: {
options: NextServerOptions
) => NextServer;

const nextServer = createNextServer({ dev: true, dir: rendererPath });
const nextServer = createNextServer({ dev: true, dir: directory });

const requestHandler = nextServer.getRequestHandler();

// Build the renderer code and watch the files.
await nextServer.prepare();

console.log('nextjs server is ready');
await sleep(30_000);

// Create a new native HTTP server to support hot code reloading.
const httpServer = http.createServer(
(req: http.IncomingMessage, res: http.ServerResponse) => {
Expand All @@ -55,6 +57,9 @@ export const serve = async (options: {
httpServer.listen(port, () => {
// Make sure to stop the server when the app closes.
// Otherwise it keeps running on its own.
app.on('before-quit', () => httpServer.close());
app.once('before-quit', () => {
logger.info('stopping nextjs dev server');
httpServer.close();
});
});
};
5 changes: 5 additions & 0 deletions electron/main/electron-next/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createLogger } from '../logger/create-logger.js';

const logger = await createLogger('main:electron-next');

export { logger };
2 changes: 1 addition & 1 deletion next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const nextConfig = {
if (!config.resolve) {
config.resolve = {};
}
config.resolve.mainFields = ['module', 'main'];
config.resolve.mainFields = ['module', 'main', 'exports'];

// Add extension aliases to support ESM-style imports.
// https://github.com/vercel/next.js/issues/41961
Expand Down

0 comments on commit 41d151d

Please sign in to comment.