socket-function 0.9.3 → 0.9.5
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/.eslintrc.js +50 -50
- package/SocketFunction.ts +280 -280
- package/SocketFunctionTypes.ts +90 -90
- package/hot/HotReloadController.ts +105 -105
- package/mobx/UrlParam.ts +39 -39
- package/mobx/observer.tsx +49 -49
- package/mobx/promiseToObservable.tsx +41 -41
- package/package.json +1 -1
- package/require/CSSShim.ts +19 -19
- package/require/RequireController.ts +252 -252
- package/require/buffer.js +2368 -2368
- package/require/compileFlags.ts +44 -44
- package/require/require.html +13 -13
- package/require/require.js +464 -462
- package/spec.txt +115 -115
- package/src/CallFactory.ts +389 -389
- package/src/JSONLACKS/JSONLACKS.generated.js +17 -17
- package/src/JSONLACKS/JSONLACKS.pegjs +247 -247
- package/src/JSONLACKS/JSONLACKS.ts +441 -429
- package/src/args.ts +21 -21
- package/src/batching.ts +177 -170
- package/src/caching.ts +359 -318
- package/src/callHTTPHandler.ts +203 -203
- package/src/callManager.ts +134 -134
- package/src/certStore.ts +29 -29
- package/src/fixLargeNetworkCalls.ts +8 -8
- package/src/formatting/colors.ts +78 -78
- package/src/formatting/format.ts +160 -160
- package/src/formatting/logColors.ts +17 -17
- package/src/misc.ts +315 -302
- package/src/nodeCache.ts +92 -92
- package/src/nodeProxy.ts +54 -54
- package/src/profiling/getOwnTime.ts +107 -142
- package/src/profiling/measure.ts +289 -273
- package/src/profiling/stats.ts +212 -212
- package/src/profiling/tcpLagProxy.ts +63 -63
- package/src/storagePath.ts +10 -10
- package/src/tlsParsing.ts +96 -96
- package/src/types.ts +8 -8
- package/src/webSocketServer.ts +254 -250
- package/test/client.css +2 -2
- package/test/client.ts +46 -46
- package/test/server.ts +43 -43
- package/test/shared.ts +52 -52
- package/tsconfig.json +26 -26
package/require/CSSShim.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
/// <reference path=".//RequireController.ts" />
|
|
2
|
-
import debugbreak from "debugbreak";
|
|
3
|
-
import { compileTransformBefore } from "typenode";
|
|
4
|
-
|
|
5
|
-
compileTransformBefore((contents: string, path: string, module: NodeJS.Module): string => {
|
|
6
|
-
if (path.endsWith(".css")) {
|
|
7
|
-
module.allowclient = true;
|
|
8
|
-
function injectCSS(contents: string) {
|
|
9
|
-
if (typeof document === "undefined") {
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
let style = document.createElement("style");
|
|
13
|
-
style.innerHTML = contents;
|
|
14
|
-
document.head.appendChild(style);
|
|
15
|
-
}
|
|
16
|
-
return `(${injectCSS.toString()})(${JSON.stringify(contents)})`;
|
|
17
|
-
}
|
|
18
|
-
return contents;
|
|
19
|
-
});
|
|
1
|
+
/// <reference path=".//RequireController.ts" />
|
|
2
|
+
import debugbreak from "debugbreak";
|
|
3
|
+
import { compileTransformBefore } from "typenode";
|
|
4
|
+
|
|
5
|
+
compileTransformBefore((contents: string, path: string, module: NodeJS.Module): string => {
|
|
6
|
+
if (path.endsWith(".css")) {
|
|
7
|
+
module.allowclient = true;
|
|
8
|
+
function injectCSS(contents: string) {
|
|
9
|
+
if (typeof document === "undefined") {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let style = document.createElement("style");
|
|
13
|
+
style.innerHTML = contents;
|
|
14
|
+
document.head.appendChild(style);
|
|
15
|
+
}
|
|
16
|
+
return `(${injectCSS.toString()})(${JSON.stringify(contents)})`;
|
|
17
|
+
}
|
|
18
|
+
return contents;
|
|
19
|
+
});
|
|
@@ -1,253 +1,253 @@
|
|
|
1
|
-
/// <reference path="../../typenode/index.d.ts" />
|
|
2
|
-
import debugbreak from "debugbreak";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import { SocketFunction } from "../SocketFunction";
|
|
5
|
-
import { setHTTPResultHeaders } from "../src/callHTTPHandler";
|
|
6
|
-
import { isNodeTrue } from "../src/misc";
|
|
7
|
-
|
|
8
|
-
module.allowclient = true;
|
|
9
|
-
|
|
10
|
-
declare global {
|
|
11
|
-
namespace NodeJS {
|
|
12
|
-
interface Module {
|
|
13
|
-
/** Indicates the module is allowed clientside. */
|
|
14
|
-
allowclient?: boolean;
|
|
15
|
-
|
|
16
|
-
/** Causes the module to not preload, requiring `await import()` for it to load correctly
|
|
17
|
-
* - Shouldn't be set recursively, otherwise nested packages will break.
|
|
18
|
-
*/
|
|
19
|
-
lazyload?: boolean;
|
|
20
|
-
|
|
21
|
-
/** Indicates the module is definitely not allowed clientside */
|
|
22
|
-
serveronly?: boolean;
|
|
23
|
-
|
|
24
|
-
// TODO: Move seqNum into the actual compilation, and make it increment,
|
|
25
|
-
// so the clientside can properly handle race conditions during hot reloading.
|
|
26
|
-
// And... maybe it is useful in other cases?
|
|
27
|
-
/** Used internally by RequireController */
|
|
28
|
-
requireControllerSeqNum?: number;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
interface Window {
|
|
32
|
-
clientsideBootTime: number;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface SerializedModule {
|
|
37
|
-
originalId: string;
|
|
38
|
-
filename: string;
|
|
39
|
-
// If a module is not allowed clientside it is likely requests will be empty,
|
|
40
|
-
// to save effort parsing requests for modules that only exist to give better
|
|
41
|
-
// error messages.
|
|
42
|
-
requests: {
|
|
43
|
-
// request => resolvedPath
|
|
44
|
-
[request: string]: string;
|
|
45
|
-
};
|
|
46
|
-
// NOTE: IF !allowclient && !serveronly, it might just mean we didn't add allowclient
|
|
47
|
-
// to the module yet. BUT, if serveronly, then we know for sure we don't want it client.
|
|
48
|
-
// So the messages and behavior will be different.
|
|
49
|
-
allowclient?: boolean;
|
|
50
|
-
serveronly?: boolean;
|
|
51
|
-
// Just for errors mostly
|
|
52
|
-
alwayssend?: boolean;
|
|
53
|
-
|
|
54
|
-
/** Only set if allowclient. */
|
|
55
|
-
source?: string;
|
|
56
|
-
|
|
57
|
-
seqNum: number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let nextModuleSeqNum = 1;
|
|
61
|
-
|
|
62
|
-
const requireSeqNumProcessId = "requireSeqNumProcessId_" + Date.now() + "_" + Math.random();
|
|
63
|
-
|
|
64
|
-
const htmlFile = isNodeTrue() && fs.readFileSync(__dirname + "/require.html").toString();
|
|
65
|
-
const jsFile = isNodeTrue() && fs.readFileSync(__dirname + "/require.js").toString();
|
|
66
|
-
const bufferShim = isNodeTrue() && fs.readFileSync(__dirname + "/buffer.js").toString();
|
|
67
|
-
|
|
68
|
-
const resolvedHTMLFile = isNodeTrue() && (
|
|
69
|
-
htmlFile
|
|
70
|
-
.replace(`<script src="./buffer.js"></script>`, `<script>${bufferShim}</script>`)
|
|
71
|
-
.replace(`<script src="./require.js"></script>`, `<script>${jsFile}</script>`)
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
class RequireControllerBase {
|
|
75
|
-
public rootResolvePath = "";
|
|
76
|
-
|
|
77
|
-
public async requireHTML(bootRequirePath?: string) {
|
|
78
|
-
let result = resolvedHTMLFile;
|
|
79
|
-
if (bootRequirePath) {
|
|
80
|
-
result = result.replace(`<!-- ENTRY_TEMPLATE -->`, `<script>require(${JSON.stringify(bootRequirePath)});</script>`);
|
|
81
|
-
}
|
|
82
|
-
return setHTTPResultHeaders(Buffer.from(result), { "Content-Type": "text/html" });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public async bufferJS() {
|
|
86
|
-
return setHTTPResultHeaders(Buffer.from(bufferShim), { "Content-Type": "text/javascript" });
|
|
87
|
-
}
|
|
88
|
-
public async requireJS() {
|
|
89
|
-
return setHTTPResultHeaders(Buffer.from(jsFile), { "Content-Type": "text/javascript" });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
public async getModules(
|
|
93
|
-
pathRequests: string[],
|
|
94
|
-
alreadyHave?: {
|
|
95
|
-
requireSeqNumProcessId: string;
|
|
96
|
-
// NOTE: Highly optimized, as otherwise this can easily be KBs (I was seeing 9KB),
|
|
97
|
-
// which is uploaded, and so can be quite slow on slow connections.
|
|
98
|
-
seqNumRanges: {
|
|
99
|
-
s: number;
|
|
100
|
-
// undefined means s + 1 (so just a single number)
|
|
101
|
-
e?: number;
|
|
102
|
-
}[];
|
|
103
|
-
},
|
|
104
|
-
): Promise<{
|
|
105
|
-
requestsResolvedPaths: string[];
|
|
106
|
-
modules: {
|
|
107
|
-
[resolvedPath: string]: SerializedModule;
|
|
108
|
-
};
|
|
109
|
-
requireSeqNumProcessId: string;
|
|
110
|
-
}> {
|
|
111
|
-
let seqNums: { [seqNum: number]: 1 } = {};
|
|
112
|
-
if (alreadyHave?.requireSeqNumProcessId === requireSeqNumProcessId) {
|
|
113
|
-
for (let { s, e } of alreadyHave.seqNumRanges) {
|
|
114
|
-
if (e === undefined) {
|
|
115
|
-
e = s + 1;
|
|
116
|
-
}
|
|
117
|
-
for (let i = s; i < e; i++) {
|
|
118
|
-
seqNums[i] = 1;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
let modules: {
|
|
124
|
-
[resolvedPath: string]: SerializedModule;
|
|
125
|
-
} = Object.create(null);
|
|
126
|
-
function addModule(module: NodeJS.Module, rootImport = false) {
|
|
127
|
-
if (!rootImport && module.lazyload) return;
|
|
128
|
-
if (!module.requireControllerSeqNum) {
|
|
129
|
-
module.requireControllerSeqNum = nextModuleSeqNum++;
|
|
130
|
-
}
|
|
131
|
-
if (seqNums[module.requireControllerSeqNum]) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (module.filename in modules) return;
|
|
135
|
-
|
|
136
|
-
// TODO: Remove unused exports. We know why the module is being requested, so we can
|
|
137
|
-
// actually very effectively know which exports it has which will never be used.
|
|
138
|
-
// - Of course, we would need to make the module specially, so if any new modules
|
|
139
|
-
// use it we can know... what was removed? It becomes complicated with
|
|
140
|
-
// lazy modules, but... it is still very important.
|
|
141
|
-
|
|
142
|
-
// IMPORTANT! Use module.filename, to strip the ".CLIENT_NAMEPSACE" extension
|
|
143
|
-
modules[module.filename] = {
|
|
144
|
-
originalId: module.id,
|
|
145
|
-
filename: module.filename,
|
|
146
|
-
// NOTE: Due to recursive sets of allowclient, it is very possible for allowclient && serveronly to be set.
|
|
147
|
-
allowclient: module.allowclient && !module.serveronly,
|
|
148
|
-
serveronly: module.serveronly,
|
|
149
|
-
requests: Object.create(null),
|
|
150
|
-
seqNum: module.requireControllerSeqNum,
|
|
151
|
-
};
|
|
152
|
-
let moduleObj = modules[module.filename];
|
|
153
|
-
if (moduleObj.allowclient) {
|
|
154
|
-
moduleObj.source = module.moduleContents;
|
|
155
|
-
if (module.filename.endsWith(".json") && !moduleObj.source) {
|
|
156
|
-
moduleObj.source = module.moduleContents = fs.readFileSync(module.filename).toString();
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// NOTE: Iterate on children even if it isn't allowed client, as the module may have children
|
|
161
|
-
// that are allowed clientside, and that have side-effects! (Mostly for static resources)
|
|
162
|
-
// - Surprisingly, this only increases the returned size by about 8% (probably more like 16%
|
|
163
|
-
// if we turn source maps off), so... it's fine. And with compression most of the extra
|
|
164
|
-
// size will go away, as paths are highly repetitive.
|
|
165
|
-
// - And now it increases the size by much less, as we ignore any subtree which are entirely
|
|
166
|
-
// not allowed on the client.
|
|
167
|
-
for (let request in module.requires) {
|
|
168
|
-
let requireResolvedPath = module.requires[request];
|
|
169
|
-
let requiredModule = require.cache[requireResolvedPath];
|
|
170
|
-
|
|
171
|
-
if (requiredModule) {
|
|
172
|
-
addModule(requiredModule);
|
|
173
|
-
moduleObj.requests[request] = requiredModule.filename;
|
|
174
|
-
} else {
|
|
175
|
-
moduleObj.requests[request] = "";
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
let searchPaths: string[] = [];
|
|
181
|
-
{
|
|
182
|
-
searchPaths.push(this.rootResolvePath);
|
|
183
|
-
let pathParts = this.rootResolvePath.replaceAll("\\", "/").split("/");
|
|
184
|
-
for (let i = 0; i < pathParts.length; i++) {
|
|
185
|
-
// Skip empty path parts, to preventing the case where the path ends
|
|
186
|
-
// with a /, which would result in "D:/test//node_modules"
|
|
187
|
-
if (!pathParts[i]) continue;
|
|
188
|
-
searchPaths.push(pathParts.slice(0, i + 1).join("/") + "/node_modules");
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let requestsResolvedPaths: string[] = [];
|
|
194
|
-
for (let pathRequest of pathRequests) {
|
|
195
|
-
let resolvedPath = "";
|
|
196
|
-
try {
|
|
197
|
-
resolvedPath = require.resolve(pathRequest, { paths: searchPaths });
|
|
198
|
-
} catch { }
|
|
199
|
-
requestsResolvedPaths.push(resolvedPath);
|
|
200
|
-
|
|
201
|
-
function createNotFoundModule(error: string): NodeJS.Module {
|
|
202
|
-
console.warn(error);
|
|
203
|
-
return {
|
|
204
|
-
exports: {},
|
|
205
|
-
children: [],
|
|
206
|
-
filename: resolvedPath,
|
|
207
|
-
id: resolvedPath,
|
|
208
|
-
isPreloading: false,
|
|
209
|
-
require: null as any,
|
|
210
|
-
loaded: true,
|
|
211
|
-
load: null as any,
|
|
212
|
-
parent: undefined,
|
|
213
|
-
path: "",
|
|
214
|
-
paths: [],
|
|
215
|
-
requires: {},
|
|
216
|
-
allowclient: true,
|
|
217
|
-
moduleContents: `console.warn(${JSON.stringify(error)})`,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// TODO: We could use import() here... but that would only make the root call asynchronous,
|
|
222
|
-
// which wouldn't prevent synchronous blocking by that much anyway...
|
|
223
|
-
//require(rootPath);
|
|
224
|
-
let clientModule = require.cache[resolvedPath];
|
|
225
|
-
if (!clientModule) {
|
|
226
|
-
clientModule = createNotFoundModule(`Module ${pathRequest} (resolved to ${JSON.stringify(resolvedPath)}) was not included serverside. Resolve root ${JSON.stringify(this.rootResolvePath)} (set by call to setRequireBootRequire), resolve search paths: ${JSON.stringify(searchPaths)})}`);
|
|
227
|
-
}
|
|
228
|
-
if (!clientModule.allowclient) {
|
|
229
|
-
clientModule = createNotFoundModule(`Module ${pathRequest} (resolved to ${resolvedPath}) is not allowed clientside (set module.allowclient in it, or call setFlag when it is imported).`);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
addModule(clientModule, true);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
return { requestsResolvedPaths, modules, requireSeqNumProcessId };
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
let baseController = new RequireControllerBase();
|
|
240
|
-
export function setRequireBootRequire(path: string) {
|
|
241
|
-
baseController.rootResolvePath = path;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export const RequireController = SocketFunction.register(
|
|
245
|
-
"RequireController-e2f811f3-14b8-4759-b0d6-73f14516cf1d",
|
|
246
|
-
baseController,
|
|
247
|
-
() => ({
|
|
248
|
-
getModules: {},
|
|
249
|
-
requireHTML: {},
|
|
250
|
-
bufferJS: {},
|
|
251
|
-
requireJS: {},
|
|
252
|
-
})
|
|
1
|
+
/// <reference path="../../typenode/index.d.ts" />
|
|
2
|
+
import debugbreak from "debugbreak";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { SocketFunction } from "../SocketFunction";
|
|
5
|
+
import { setHTTPResultHeaders } from "../src/callHTTPHandler";
|
|
6
|
+
import { isNodeTrue } from "../src/misc";
|
|
7
|
+
|
|
8
|
+
module.allowclient = true;
|
|
9
|
+
|
|
10
|
+
declare global {
|
|
11
|
+
namespace NodeJS {
|
|
12
|
+
interface Module {
|
|
13
|
+
/** Indicates the module is allowed clientside. */
|
|
14
|
+
allowclient?: boolean;
|
|
15
|
+
|
|
16
|
+
/** Causes the module to not preload, requiring `await import()` for it to load correctly
|
|
17
|
+
* - Shouldn't be set recursively, otherwise nested packages will break.
|
|
18
|
+
*/
|
|
19
|
+
lazyload?: boolean;
|
|
20
|
+
|
|
21
|
+
/** Indicates the module is definitely not allowed clientside */
|
|
22
|
+
serveronly?: boolean;
|
|
23
|
+
|
|
24
|
+
// TODO: Move seqNum into the actual compilation, and make it increment,
|
|
25
|
+
// so the clientside can properly handle race conditions during hot reloading.
|
|
26
|
+
// And... maybe it is useful in other cases?
|
|
27
|
+
/** Used internally by RequireController */
|
|
28
|
+
requireControllerSeqNum?: number;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
interface Window {
|
|
32
|
+
clientsideBootTime: number;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SerializedModule {
|
|
37
|
+
originalId: string;
|
|
38
|
+
filename: string;
|
|
39
|
+
// If a module is not allowed clientside it is likely requests will be empty,
|
|
40
|
+
// to save effort parsing requests for modules that only exist to give better
|
|
41
|
+
// error messages.
|
|
42
|
+
requests: {
|
|
43
|
+
// request => resolvedPath
|
|
44
|
+
[request: string]: string;
|
|
45
|
+
};
|
|
46
|
+
// NOTE: IF !allowclient && !serveronly, it might just mean we didn't add allowclient
|
|
47
|
+
// to the module yet. BUT, if serveronly, then we know for sure we don't want it client.
|
|
48
|
+
// So the messages and behavior will be different.
|
|
49
|
+
allowclient?: boolean;
|
|
50
|
+
serveronly?: boolean;
|
|
51
|
+
// Just for errors mostly
|
|
52
|
+
alwayssend?: boolean;
|
|
53
|
+
|
|
54
|
+
/** Only set if allowclient. */
|
|
55
|
+
source?: string;
|
|
56
|
+
|
|
57
|
+
seqNum: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let nextModuleSeqNum = 1;
|
|
61
|
+
|
|
62
|
+
const requireSeqNumProcessId = "requireSeqNumProcessId_" + Date.now() + "_" + Math.random();
|
|
63
|
+
|
|
64
|
+
const htmlFile = isNodeTrue() && fs.readFileSync(__dirname + "/require.html").toString();
|
|
65
|
+
const jsFile = isNodeTrue() && fs.readFileSync(__dirname + "/require.js").toString();
|
|
66
|
+
const bufferShim = isNodeTrue() && fs.readFileSync(__dirname + "/buffer.js").toString();
|
|
67
|
+
|
|
68
|
+
const resolvedHTMLFile = isNodeTrue() && (
|
|
69
|
+
htmlFile
|
|
70
|
+
.replace(`<script src="./buffer.js"></script>`, `<script>${bufferShim}</script>`)
|
|
71
|
+
.replace(`<script src="./require.js"></script>`, `<script>${jsFile}</script>`)
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
class RequireControllerBase {
|
|
75
|
+
public rootResolvePath = "";
|
|
76
|
+
|
|
77
|
+
public async requireHTML(bootRequirePath?: string) {
|
|
78
|
+
let result = resolvedHTMLFile;
|
|
79
|
+
if (bootRequirePath) {
|
|
80
|
+
result = result.replace(`<!-- ENTRY_TEMPLATE -->`, `<script>require(${JSON.stringify(bootRequirePath)});</script>`);
|
|
81
|
+
}
|
|
82
|
+
return setHTTPResultHeaders(Buffer.from(result), { "Content-Type": "text/html" });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async bufferJS() {
|
|
86
|
+
return setHTTPResultHeaders(Buffer.from(bufferShim), { "Content-Type": "text/javascript" });
|
|
87
|
+
}
|
|
88
|
+
public async requireJS() {
|
|
89
|
+
return setHTTPResultHeaders(Buffer.from(jsFile), { "Content-Type": "text/javascript" });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public async getModules(
|
|
93
|
+
pathRequests: string[],
|
|
94
|
+
alreadyHave?: {
|
|
95
|
+
requireSeqNumProcessId: string;
|
|
96
|
+
// NOTE: Highly optimized, as otherwise this can easily be KBs (I was seeing 9KB),
|
|
97
|
+
// which is uploaded, and so can be quite slow on slow connections.
|
|
98
|
+
seqNumRanges: {
|
|
99
|
+
s: number;
|
|
100
|
+
// undefined means s + 1 (so just a single number)
|
|
101
|
+
e?: number;
|
|
102
|
+
}[];
|
|
103
|
+
},
|
|
104
|
+
): Promise<{
|
|
105
|
+
requestsResolvedPaths: string[];
|
|
106
|
+
modules: {
|
|
107
|
+
[resolvedPath: string]: SerializedModule;
|
|
108
|
+
};
|
|
109
|
+
requireSeqNumProcessId: string;
|
|
110
|
+
}> {
|
|
111
|
+
let seqNums: { [seqNum: number]: 1 } = {};
|
|
112
|
+
if (alreadyHave?.requireSeqNumProcessId === requireSeqNumProcessId) {
|
|
113
|
+
for (let { s, e } of alreadyHave.seqNumRanges) {
|
|
114
|
+
if (e === undefined) {
|
|
115
|
+
e = s + 1;
|
|
116
|
+
}
|
|
117
|
+
for (let i = s; i < e; i++) {
|
|
118
|
+
seqNums[i] = 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let modules: {
|
|
124
|
+
[resolvedPath: string]: SerializedModule;
|
|
125
|
+
} = Object.create(null);
|
|
126
|
+
function addModule(module: NodeJS.Module, rootImport = false) {
|
|
127
|
+
if (!rootImport && module.lazyload) return;
|
|
128
|
+
if (!module.requireControllerSeqNum) {
|
|
129
|
+
module.requireControllerSeqNum = nextModuleSeqNum++;
|
|
130
|
+
}
|
|
131
|
+
if (seqNums[module.requireControllerSeqNum]) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (module.filename in modules) return;
|
|
135
|
+
|
|
136
|
+
// TODO: Remove unused exports. We know why the module is being requested, so we can
|
|
137
|
+
// actually very effectively know which exports it has which will never be used.
|
|
138
|
+
// - Of course, we would need to make the module specially, so if any new modules
|
|
139
|
+
// use it we can know... what was removed? It becomes complicated with
|
|
140
|
+
// lazy modules, but... it is still very important.
|
|
141
|
+
|
|
142
|
+
// IMPORTANT! Use module.filename, to strip the ".CLIENT_NAMEPSACE" extension
|
|
143
|
+
modules[module.filename] = {
|
|
144
|
+
originalId: module.id,
|
|
145
|
+
filename: module.filename,
|
|
146
|
+
// NOTE: Due to recursive sets of allowclient, it is very possible for allowclient && serveronly to be set.
|
|
147
|
+
allowclient: module.allowclient && !module.serveronly,
|
|
148
|
+
serveronly: module.serveronly,
|
|
149
|
+
requests: Object.create(null),
|
|
150
|
+
seqNum: module.requireControllerSeqNum,
|
|
151
|
+
};
|
|
152
|
+
let moduleObj = modules[module.filename];
|
|
153
|
+
if (moduleObj.allowclient) {
|
|
154
|
+
moduleObj.source = module.moduleContents;
|
|
155
|
+
if (module.filename.endsWith(".json") && !moduleObj.source) {
|
|
156
|
+
moduleObj.source = module.moduleContents = fs.readFileSync(module.filename).toString();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// NOTE: Iterate on children even if it isn't allowed client, as the module may have children
|
|
161
|
+
// that are allowed clientside, and that have side-effects! (Mostly for static resources)
|
|
162
|
+
// - Surprisingly, this only increases the returned size by about 8% (probably more like 16%
|
|
163
|
+
// if we turn source maps off), so... it's fine. And with compression most of the extra
|
|
164
|
+
// size will go away, as paths are highly repetitive.
|
|
165
|
+
// - And now it increases the size by much less, as we ignore any subtree which are entirely
|
|
166
|
+
// not allowed on the client.
|
|
167
|
+
for (let request in module.requires) {
|
|
168
|
+
let requireResolvedPath = module.requires[request];
|
|
169
|
+
let requiredModule = require.cache[requireResolvedPath];
|
|
170
|
+
|
|
171
|
+
if (requiredModule) {
|
|
172
|
+
addModule(requiredModule);
|
|
173
|
+
moduleObj.requests[request] = requiredModule.filename;
|
|
174
|
+
} else {
|
|
175
|
+
moduleObj.requests[request] = "";
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
let searchPaths: string[] = [];
|
|
181
|
+
{
|
|
182
|
+
searchPaths.push(this.rootResolvePath);
|
|
183
|
+
let pathParts = this.rootResolvePath.replaceAll("\\", "/").split("/");
|
|
184
|
+
for (let i = 0; i < pathParts.length; i++) {
|
|
185
|
+
// Skip empty path parts, to preventing the case where the path ends
|
|
186
|
+
// with a /, which would result in "D:/test//node_modules"
|
|
187
|
+
if (!pathParts[i]) continue;
|
|
188
|
+
searchPaths.push(pathParts.slice(0, i + 1).join("/") + "/node_modules");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
let requestsResolvedPaths: string[] = [];
|
|
194
|
+
for (let pathRequest of pathRequests) {
|
|
195
|
+
let resolvedPath = "";
|
|
196
|
+
try {
|
|
197
|
+
resolvedPath = require.resolve(pathRequest, { paths: searchPaths });
|
|
198
|
+
} catch { }
|
|
199
|
+
requestsResolvedPaths.push(resolvedPath);
|
|
200
|
+
|
|
201
|
+
function createNotFoundModule(error: string): NodeJS.Module {
|
|
202
|
+
console.warn(error);
|
|
203
|
+
return {
|
|
204
|
+
exports: {},
|
|
205
|
+
children: [],
|
|
206
|
+
filename: resolvedPath,
|
|
207
|
+
id: resolvedPath,
|
|
208
|
+
isPreloading: false,
|
|
209
|
+
require: null as any,
|
|
210
|
+
loaded: true,
|
|
211
|
+
load: null as any,
|
|
212
|
+
parent: undefined,
|
|
213
|
+
path: "",
|
|
214
|
+
paths: [],
|
|
215
|
+
requires: {},
|
|
216
|
+
allowclient: true,
|
|
217
|
+
moduleContents: `console.warn(${JSON.stringify(error)})`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// TODO: We could use import() here... but that would only make the root call asynchronous,
|
|
222
|
+
// which wouldn't prevent synchronous blocking by that much anyway...
|
|
223
|
+
//require(rootPath);
|
|
224
|
+
let clientModule = require.cache[resolvedPath];
|
|
225
|
+
if (!clientModule) {
|
|
226
|
+
clientModule = createNotFoundModule(`Module ${pathRequest} (resolved to ${JSON.stringify(resolvedPath)}) was not included serverside. Resolve root ${JSON.stringify(this.rootResolvePath)} (set by call to setRequireBootRequire), resolve search paths: ${JSON.stringify(searchPaths)})}`);
|
|
227
|
+
}
|
|
228
|
+
if (!clientModule.allowclient) {
|
|
229
|
+
clientModule = createNotFoundModule(`Module ${pathRequest} (resolved to ${resolvedPath}) is not allowed clientside (set module.allowclient in it, or call setFlag when it is imported).`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
addModule(clientModule, true);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { requestsResolvedPaths, modules, requireSeqNumProcessId };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let baseController = new RequireControllerBase();
|
|
240
|
+
export function setRequireBootRequire(path: string) {
|
|
241
|
+
baseController.rootResolvePath = path;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export const RequireController = SocketFunction.register(
|
|
245
|
+
"RequireController-e2f811f3-14b8-4759-b0d6-73f14516cf1d",
|
|
246
|
+
baseController,
|
|
247
|
+
() => ({
|
|
248
|
+
getModules: {},
|
|
249
|
+
requireHTML: {},
|
|
250
|
+
bufferJS: {},
|
|
251
|
+
requireJS: {},
|
|
252
|
+
})
|
|
253
253
|
);
|