Skip to content

Commit 41d151d

Browse files
committed
feat: electron next dev serve
1 parent c2e2d85 commit 41d151d

File tree

4 files changed

+44
-17
lines changed

4 files changed

+44
-17
lines changed

electron/main/app.ts

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { Event } from 'electron';
22
import { BrowserWindow, app, dialog, shell } from 'electron';
33
import path from 'node:path';
4-
import serve from 'electron-serve';
4+
import prodServe from 'electron-serve';
5+
import trimEnd from 'lodash-es/trimEnd.js';
56
import { runInBackground } from '../common/async/run-in-background.js';
67
import type { IpcController } from './ipc/ipc.controller.js';
78
import { newIpcController } from './ipc/ipc.controller.js';
@@ -25,7 +26,14 @@ const appEnvIsDev = appEnv === 'development';
2526
// Only load dev tools when running in development.
2627
const appEnableDevTools = appEnvIsDev && !app.isPackaged;
2728

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

4351
const appUrl = appEnvIsProd ? prodAppUrl : devAppUrl;
4452

53+
logger.debug('app paths', {
54+
appPath,
55+
appElectronPath,
56+
appBuildPath,
57+
appPreloadPath,
58+
prodRendererPath,
59+
devRendererPath,
60+
});
61+
4562
// Register custom protocol 'app://' to serve our app.
4663
// Registering the protocol must be done before the app is ready.
4764
// This is necessary for both security and for single-page apps.
4865
// https://bishopfox.com/blog/reasonably-secure-electron
4966
// https://github.com/sindresorhus/electron-serve
5067
if (appEnvIsProd) {
51-
serve({
68+
prodServe({
5269
scheme: prodAppScheme,
5370
directory: prodRendererPath,
5471
});
@@ -63,10 +80,10 @@ const createMainWindow = async (): Promise<void> => {
6380
// If running in development, serve the renderer from localhost.
6481
// This must be done once the app is ready.
6582
// This enables hot reloading of the renderer.
66-
const { serve } = await import('./electron-next/dev-server.js');
67-
await serve({
68-
rendererPath: devRendererPath,
83+
const { devServe } = await import('./electron-next/dev-server.js');
84+
await devServe({
6985
port: devPort,
86+
directory: devRendererPath,
7087
});
7188
}
7289

electron/main/electron-next/dev-server.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,24 @@ import { app } from 'electron';
1111
import * as http from 'node:http';
1212
import type { NextServer, NextServerOptions } from 'next/dist/server/next.js';
1313
import { runInBackground } from '../../common/async/run-in-background.js';
14-
import { sleep } from '../../common/async/sleep.js';
14+
import { logger } from './logger.js';
1515

16-
export const serve = async (options: {
16+
export const devServe = async (options: {
1717
/**
18-
* Path to the renderer directory, the root of your nextjs web app.
18+
* The directory to serve, relative to the app root directory.
1919
*/
20-
rendererPath: string;
20+
directory: string;
2121
/**
2222
* The port to serve the renderer on.
2323
*/
2424
port: number;
2525
}): Promise<void> => {
26-
const { rendererPath, port = 3000 } = options;
26+
const { directory, port = 3000 } = options;
27+
28+
logger.info('starting nextjs dev server', {
29+
directory,
30+
port,
31+
});
2732

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

36-
const nextServer = createNextServer({ dev: true, dir: rendererPath });
41+
const nextServer = createNextServer({ dev: true, dir: directory });
3742

3843
const requestHandler = nextServer.getRequestHandler();
3944

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

43-
console.log('nextjs server is ready');
44-
await sleep(30_000);
45-
4648
// Create a new native HTTP server to support hot code reloading.
4749
const httpServer = http.createServer(
4850
(req: http.IncomingMessage, res: http.ServerResponse) => {
@@ -55,6 +57,9 @@ export const serve = async (options: {
5557
httpServer.listen(port, () => {
5658
// Make sure to stop the server when the app closes.
5759
// Otherwise it keeps running on its own.
58-
app.on('before-quit', () => httpServer.close());
60+
app.once('before-quit', () => {
61+
logger.info('stopping nextjs dev server');
62+
httpServer.close();
63+
});
5964
});
6065
};

electron/main/electron-next/logger.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createLogger } from '../logger/create-logger.js';
2+
3+
const logger = await createLogger('main:electron-next');
4+
5+
export { logger };

next.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ const nextConfig = {
214214
if (!config.resolve) {
215215
config.resolve = {};
216216
}
217-
config.resolve.mainFields = ['module', 'main'];
217+
config.resolve.mainFields = ['module', 'main', 'exports'];
218218

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

0 commit comments

Comments
 (0)