/
index.js
154 lines (133 loc) · 4.07 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const { app, protocol, session } = require("electron");
const path = require("path");
const fs = require("fs").promises;
const http = require("http");
module.exports = async function serveNextAt(uri, options = {}) {
// Parse scheme
const [, scheme, host] = uri.match(/^([\w-]*):\/\/(.*)$/i);
// Configure defaults
const {
privileges = {},
port = 3000,
dev = !app.isPackaged,
outputDir = "./out",
partition,
} = options;
// Expose the server port and dev flag as en environment variable
// This allows us to dynamically expose the port to the preload script
process.env.NEXT_ELECTON_SERVER_PORT = port;
process.env.NEXT_ELECTON_SERVER_DEV = dev;
// Validate
if (!scheme) {
const error = new Error(
`next-electron-server - Invalid scheme: ${scheme} (${uri})`
);
throw error;
}
if (!host) {
const error = new Error(
`next-electron-server - Invalid host: ${host} (${uri})`
);
throw error;
}
// Register scheme
protocol.registerSchemesAsPrivileged([
{
scheme,
privileges: {
standard: true,
secure: true,
allowServiceWorkers: true,
supportFetchAPI: true,
corsEnabled: true,
...privileges,
},
},
]);
// Wait for app to be ready
app.whenReady().then(() => {
const { protocol } = partition
? session.fromPartition(partition)
: session.defaultSession;
if (dev) {
if (isNaN(port)) {
const error = new Error(
`next-electron-server - "port" must be a number`
);
throw error;
}
// Development: Serve Next.js using a proxy pointing the localhost:3000
console.log(
"\x1b[33m%s\x1b[0m",
"next-electron-server",
`- Serving files via ${scheme}://${host} from http://localhost:${port}`
);
protocol.registerStreamProtocol(scheme, (request, next) => {
// Parse proxy options
const { url, ...options } = request;
// Rewrite url to localhost localhost
const devServerUrl = url
.replace(`${scheme}://${host}`, `http://localhost:${port}`)
.replace(/\/$/, "");
// Proxy request
lazyProxyRequest(devServerUrl, options, next);
});
} else {
// PRODUCTION: Serve Next.js files using a static handler
const appPath = app.getAppPath();
protocol.registerFileProtocol(scheme, async (request, respond) => {
// Get the requested filePath
const filePath = path.join(
appPath,
request.url.replace(`${scheme}://${host}`, outputDir)
);
// Try to resolve it
let resolvedPath = await resolvePath(filePath);
// If not found lets try to find it as .html file
if (!resolvedPath && !path.extname(filePath)) {
resolvedPath = await resolvePath(filePath + ".html");
}
// Snap the file doesn't exist. Lets render the Next.js 404
if (!resolvedPath) {
resolvedPath = path.join(appPath, outputDir, "./404.html");
}
respond({
path: resolvedPath,
});
});
}
});
};
function lazyProxyRequest(webpackDevServerUrl, options, next) {
return http
.request(webpackDevServerUrl, options, next)
.on("error", async (error) => {
// Lets wait for the Next.js devserver to start
if (error.code === "ECONNREFUSED") {
console.log(
"\x1b[33m%s\x1b[0m",
"next-electron-server",
"- Waiting for Next.js DevServer"
);
// Next devserver is not ready yet, lets wait for it
await new Promise((resolve) => setTimeout(resolve, 500));
// Retry
lazyProxyRequest(webpackDevServerUrl, options, next);
} else {
throw error;
}
})
.end();
}
async function resolvePath(pth) {
try {
const cleanedPath = pth.replace(/\?.*/, "");
const result = await fs.stat(cleanedPath);
if (result.isFile()) {
return cleanedPath;
}
if (result.isDirectory()) {
return resolvePath(path.join(cleanedPath, "index.html"));
}
} catch (_) {}
}