socket-function 0.12.3 → 0.12.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SocketFunction.ts +7 -2
- package/hot/HotReloadController.ts +60 -9
- package/package.json +1 -1
- package/require/RequireController.ts +7 -3
- package/require/require.js +20 -16
- package/src/webSocketServer.ts +16 -15
package/SocketFunction.ts
CHANGED
|
@@ -211,7 +211,7 @@ export class SocketFunction {
|
|
|
211
211
|
* to add additional imports to ensure the register call runs.
|
|
212
212
|
*/
|
|
213
213
|
public static expose(socketRegistered: SocketRegistered) {
|
|
214
|
-
console.log(
|
|
214
|
+
console.log(`Exposing Controller ${blue(socketRegistered._classGuid)}`);
|
|
215
215
|
exposeClass(socketRegistered);
|
|
216
216
|
this.exposedClasses.add(socketRegistered._classGuid);
|
|
217
217
|
|
|
@@ -257,7 +257,12 @@ export class SocketFunction {
|
|
|
257
257
|
return this.mountedNodeId;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
/** Sets the default call when an http request is made, but no classGuid is set.
|
|
260
|
+
/** Sets the default call when an http request is made, but no classGuid is set.
|
|
261
|
+
* NOTE: All other calls should be endpoint calls, even if those endpoints return a static file with an HTML content type.
|
|
262
|
+
* - However, to load new content, you should probably just use `require("./example.ts")`, which works on any files
|
|
263
|
+
* clientside that have also been required serverside (and whitelisted with module.allowclient = true,
|
|
264
|
+
* or with an `allowclient.flag` file in the directory or parent directory).
|
|
265
|
+
*/
|
|
261
266
|
public static setDefaultHTTPCall<
|
|
262
267
|
Registered extends SocketRegistered,
|
|
263
268
|
FunctionName extends keyof Registered["nodes"][""] & string,
|
|
@@ -7,7 +7,7 @@ import { cache, lazy } from "../src/caching";
|
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import debugbreak from "debugbreak";
|
|
9
9
|
import { isNode } from "../src/misc";
|
|
10
|
-
import { red } from "../src/formatting/logColors";
|
|
10
|
+
import { magenta, red } from "../src/formatting/logColors";
|
|
11
11
|
|
|
12
12
|
/** Enables some hot reload functionality.
|
|
13
13
|
* - Triggers a refresh clientside
|
|
@@ -35,8 +35,13 @@ export function watchFilesAndTriggerHotReloading(noAutomaticBrowserWatch = false
|
|
|
35
35
|
declare global {
|
|
36
36
|
namespace NodeJS {
|
|
37
37
|
interface Module {
|
|
38
|
+
/** Causes us to hotreload the file. Applies both serverside and clientside.
|
|
39
|
+
* - If not set for any files clientside, we will refresh.
|
|
40
|
+
* - If not set for any files serverside, we will do nothing (and just leave old code running).
|
|
41
|
+
*/
|
|
38
42
|
hotreload?: boolean;
|
|
39
|
-
|
|
43
|
+
/** Only hotreloads the file in the browser. */
|
|
44
|
+
hotreloadBrowser?: boolean;
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
}
|
|
@@ -48,10 +53,20 @@ export function isHotReloading() {
|
|
|
48
53
|
export function setExternalHotReloading(value: boolean) {
|
|
49
54
|
isHotReloadingValue = value;
|
|
50
55
|
}
|
|
56
|
+
let hotReloadCallbacks: ((modules: NodeJS.Module[]) => void)[] = [];
|
|
57
|
+
export function onHotReload(callback: (modules: NodeJS.Module[]) => void) {
|
|
58
|
+
hotReloadCallbacks.push(callback);
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
const hotReloadModule = cache((module: NodeJS.Module) => {
|
|
53
62
|
if (!module.updateContents) return;
|
|
54
|
-
|
|
63
|
+
let interval = 1000;
|
|
64
|
+
let fast = false;
|
|
65
|
+
if (module.hotreload || module.hotreloadBrowser) {
|
|
66
|
+
interval = 50;
|
|
67
|
+
fast = true;
|
|
68
|
+
}
|
|
69
|
+
fs.watchFile(module.filename, { persistent: false, interval }, (curr, prev) => {
|
|
55
70
|
if (curr.mtime.getTime() === prev.mtime.getTime()) return;
|
|
56
71
|
console.log(`Hot reloading due to change: ${module.filename}`);
|
|
57
72
|
module.updateContents?.();
|
|
@@ -75,24 +90,32 @@ const hotReloadModule = cache((module: NodeJS.Module) => {
|
|
|
75
90
|
}
|
|
76
91
|
}
|
|
77
92
|
}
|
|
78
|
-
triggerClientSideReload(
|
|
93
|
+
triggerClientSideReload({
|
|
94
|
+
files: [module.filename],
|
|
95
|
+
changeTime: curr.mtimeMs,
|
|
96
|
+
fast,
|
|
97
|
+
});
|
|
79
98
|
});
|
|
80
99
|
});
|
|
81
100
|
let reloadTriggering = false;
|
|
82
101
|
let clientWatcherNodes = new Set<string>();
|
|
83
|
-
function triggerClientSideReload(
|
|
102
|
+
function triggerClientSideReload(config: {
|
|
103
|
+
files: string[];
|
|
104
|
+
changeTime: number;
|
|
105
|
+
fast?: boolean;
|
|
106
|
+
}) {
|
|
84
107
|
if (reloadTriggering) return;
|
|
85
108
|
reloadTriggering = true;
|
|
86
109
|
setTimeout(async () => {
|
|
87
110
|
reloadTriggering = false;
|
|
88
111
|
for (let clientNodeId of clientWatcherNodes) {
|
|
89
112
|
console.log(`Notifying client of hot reload: ${clientNodeId}`);
|
|
90
|
-
HotReloadController.nodes[clientNodeId].fileUpdated().catch(() => {
|
|
113
|
+
HotReloadController.nodes[clientNodeId].fileUpdated(config.files, config.changeTime).catch(() => {
|
|
91
114
|
console.log(`Removing erroring client: ${clientNodeId}`);
|
|
92
115
|
clientWatcherNodes.delete(clientNodeId);
|
|
93
116
|
});
|
|
94
117
|
}
|
|
95
|
-
}, 300);
|
|
118
|
+
}, config.fast ? 10 : 300);
|
|
96
119
|
}
|
|
97
120
|
|
|
98
121
|
class HotReloadControllerBase {
|
|
@@ -102,8 +125,36 @@ class HotReloadControllerBase {
|
|
|
102
125
|
let callerId = SocketFunction.getCaller().nodeId;
|
|
103
126
|
clientWatcherNodes.add(callerId);
|
|
104
127
|
}
|
|
105
|
-
async fileUpdated() {
|
|
106
|
-
|
|
128
|
+
async fileUpdated(files: string[], changeTime: number) {
|
|
129
|
+
console.groupCollapsed(magenta(`Trigger hotreload for files (${Date.now() - changeTime}ms after file change)`));
|
|
130
|
+
for (let file of files) {
|
|
131
|
+
console.log(file);
|
|
132
|
+
}
|
|
133
|
+
console.groupEnd();
|
|
134
|
+
let modules: NodeJS.Module[] = [];
|
|
135
|
+
for (let file of files) {
|
|
136
|
+
let module = require.cache[file];
|
|
137
|
+
if (!module) {
|
|
138
|
+
console.log(`Module not found: ${file}, reloading page to ensure new version is loaded`);
|
|
139
|
+
document.location.reload();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (!module.hotreload && !module.hotreloadBrowser) {
|
|
143
|
+
console.log(`Module not hotreloadable: ${file}, reloading page to ensure new version is loaded`);
|
|
144
|
+
document.location.reload();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
modules.push(module);
|
|
148
|
+
}
|
|
149
|
+
for (let module of modules) {
|
|
150
|
+
module.loaded = false;
|
|
151
|
+
}
|
|
152
|
+
await Promise.all(modules.map(module => module.load(module.filename)));
|
|
153
|
+
|
|
154
|
+
for (let callback of hotReloadCallbacks) {
|
|
155
|
+
callback(modules);
|
|
156
|
+
}
|
|
157
|
+
console.log(magenta(`Hot reload complete (${Date.now() - changeTime}ms after file change)`));
|
|
107
158
|
}
|
|
108
159
|
}
|
|
109
160
|
|
package/package.json
CHANGED
|
@@ -98,13 +98,17 @@ function addMapGetModules(remap: typeof mapGetModules[number]["remap"]) {
|
|
|
98
98
|
class RequireControllerBase {
|
|
99
99
|
public rootResolvePath = "";
|
|
100
100
|
|
|
101
|
-
public async requireHTML(
|
|
101
|
+
public async requireHTML(config?: {
|
|
102
|
+
requireCalls?: string[];
|
|
103
|
+
}) {
|
|
104
|
+
let { requireCalls } = config || {};
|
|
102
105
|
let result = resolvedHTMLFile;
|
|
103
106
|
if (beforeEntryText.length > 0) {
|
|
104
107
|
result = result.replace(BEFORE_ENTRY_TEMPLATE, beforeEntryText.join("\n"));
|
|
105
108
|
}
|
|
106
|
-
if (
|
|
107
|
-
result = result.replace(ENTRY_TEMPLATE, `<script
|
|
109
|
+
if (requireCalls) {
|
|
110
|
+
result = result.replace(ENTRY_TEMPLATE, `<script>\n${requireCalls.map(x => `require(${JSON.stringify(x)});`).join(" \n")
|
|
111
|
+
}</script>`);
|
|
108
112
|
}
|
|
109
113
|
return setHTTPResultHeaders(Buffer.from(result), { "Content-Type": "text/html" });
|
|
110
114
|
}
|
package/require/require.js
CHANGED
|
@@ -245,22 +245,27 @@
|
|
|
245
245
|
return builtInModuleExports[request];
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
)
|
|
248
|
+
let resolvedPath;
|
|
249
|
+
if (request in moduleCache) {
|
|
250
|
+
resolvedPath = request;
|
|
251
|
+
} else {
|
|
252
|
+
if (!(request in serializedModule.requests)) {
|
|
253
|
+
if (!asyncIsFine) {
|
|
254
|
+
console.warn(`Accessed unexpected module %c${request}%c in %c${module.id}%c\n\tTreating it as an async require.\n\tAll modules require synchronously clientside must be required serverside at a module level.`,
|
|
255
|
+
"color: red", "color: unset",
|
|
256
|
+
"color: red", "color: unset",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return rootRequire(request);
|
|
254
260
|
}
|
|
255
|
-
return rootRequire(request);
|
|
256
|
-
}
|
|
257
261
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
// Built in modules that we haven't been implemented
|
|
263
|
+
if (serializedModule.requests[request] === "") {
|
|
264
|
+
return {};
|
|
265
|
+
}
|
|
262
266
|
|
|
263
|
-
|
|
267
|
+
resolvedPath = serializedModule.requests[request];
|
|
268
|
+
}
|
|
264
269
|
if (resolvedPath !== "NOTALLOWEDCLIENTSIDE" && !serializedModules[resolvedPath]) {
|
|
265
270
|
if (!asyncIsFine) {
|
|
266
271
|
console.warn(`Accessed unexpected module %c${request}%c in %c${module.id}%c\n\tTreating it as an async require.\n\tAll modules require synchronously clientside must be required serverside at a module level.`,
|
|
@@ -381,11 +386,10 @@
|
|
|
381
386
|
delete alreadyHave.seqNums[serializedModule.seqNum];
|
|
382
387
|
}
|
|
383
388
|
// NOTE: There is almost never recovery from module downloading errors, so just don't catch them
|
|
384
|
-
|
|
389
|
+
return Promise.resolve().then(() => rootRequire(resolvedId, true)).then(async () => {
|
|
385
390
|
module.loaded = true;
|
|
386
|
-
load();
|
|
391
|
+
await load();
|
|
387
392
|
});
|
|
388
|
-
return;
|
|
389
393
|
}
|
|
390
394
|
|
|
391
395
|
module.requires = serializedModule.requests;
|
package/src/webSocketServer.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { delay, runInfinitePoll } from "./batching";
|
|
|
16
16
|
import { magenta } from "./formatting/logColors";
|
|
17
17
|
import { yellow } from "./formatting/logColors";
|
|
18
18
|
import { green } from "./formatting/logColors";
|
|
19
|
+
import { formatTime } from "./formatting/format";
|
|
19
20
|
|
|
20
21
|
export type SocketServerConfig = (
|
|
21
22
|
https.ServerOptions & {
|
|
@@ -210,21 +211,21 @@ export async function startSocketServer(
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
let port = config.port;
|
|
214
|
+
async function isPortInUse(port: number): Promise<boolean> {
|
|
215
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
216
|
+
let server = net.createServer();
|
|
217
|
+
server.listen(port, host)
|
|
218
|
+
.on("listening", function () {
|
|
219
|
+
server.close();
|
|
220
|
+
resolve(false);
|
|
221
|
+
}).on("close", function () {
|
|
222
|
+
resolve(true);
|
|
223
|
+
}).on("error", function (e) {
|
|
224
|
+
resolve(true);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
213
228
|
if (config.useAvailablePortIfPortInUse && port) {
|
|
214
|
-
async function isPortInUse(port: number): Promise<boolean> {
|
|
215
|
-
return new Promise<boolean>((resolve, reject) => {
|
|
216
|
-
let server = net.createServer();
|
|
217
|
-
server.listen(port, host)
|
|
218
|
-
.on("listening", function () {
|
|
219
|
-
server.close();
|
|
220
|
-
resolve(false);
|
|
221
|
-
}).on("close", function () {
|
|
222
|
-
resolve(true);
|
|
223
|
-
}).on("error", function (e) {
|
|
224
|
-
resolve(true);
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
229
|
if (await isPortInUse(port)) {
|
|
229
230
|
port = 0;
|
|
230
231
|
}
|
|
@@ -239,7 +240,7 @@ export async function startSocketServer(
|
|
|
239
240
|
|
|
240
241
|
port = (realServer.address() as net.AddressInfo).port;
|
|
241
242
|
let nodeId = getNodeId(getCommonName(config.cert), port);
|
|
242
|
-
console.log(green(`Started Listening on ${nodeId}`));
|
|
243
|
+
console.log(green(`Started Listening on ${nodeId} after ${formatTime(process.uptime() * 1000)}`));
|
|
243
244
|
|
|
244
245
|
return nodeId;
|
|
245
246
|
}
|