@bananacool467/authtics-host 0.2.6 → 0.2.8
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/.agents/memory/MEMORY.md +1 -0
- package/.agents/memory/node-child-process-tests.md +10 -0
- package/README.md +168 -6
- package/assets/dev_panel.jpg +0 -0
- package/assets/result.jpg +0 -0
- package/bin/hostjs.js +62 -0
- package/bin/logger.js +29 -0
- package/dist/BrowserAPIs.d.ts +10 -0
- package/dist/BrowserAPIs.js +53 -0
- package/dist/cli.d.ts +5 -3
- package/dist/cli.js +408 -58
- package/dist/config.d.ts +5 -0
- package/dist/config.js +15 -1
- package/dist/devPanel.d.ts +11 -0
- package/dist/devPanel.js +358 -56
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/renderEngine.d.ts +4 -3
- package/dist/renderEngine.js +265 -68
- package/dist/rendererTempFiles.d.ts +10 -1
- package/dist/rendererTempFiles.js +101 -17
- package/dist/superBuildOutput.d.ts +9 -0
- package/dist/superBuildOutput.js +175 -0
- package/dist/svg_assets/slope_top_left.d.ts +1 -0
- package/dist/svg_assets/slope_top_left.js +4 -0
- package/package.json +9 -2
- package/scripts/post-merge.sh +5 -0
- package/svg_assets/slope_top_left.tsx +19 -0
- package/tests/source-map-access.test.mjs +167 -0
- package/tests/source-map-location.test.mjs +237 -0
package/dist/renderEngine.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
4
|
import * as esbuild from "esbuild";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { renderToString } from "react-dom/server";
|
|
7
|
-
import { tempFileForTsx, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
|
|
7
|
+
import { tempFileForTsx, clientBundleForTsx, serverBundleForTsx, ssgHtmlForTsx, getBuildStyle, getBuildTrigger, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
const customRequire = createRequire(import.meta.url);
|
|
10
10
|
import { WebSocketServer } from "ws";
|
|
11
|
-
|
|
11
|
+
let srtwsServer = null;
|
|
12
|
+
export function deleteSrtwsServer() {
|
|
13
|
+
if (srtwsServer)
|
|
14
|
+
srtwsServer.close();
|
|
15
|
+
srtwsServer = null;
|
|
16
|
+
}
|
|
17
|
+
export function handleSrtwsConnection(server, tsxPath, config, reqUrl, reqDAT, rmvFileHandler, fileHandler) {
|
|
12
18
|
const wss = new WebSocketServer({ noServer: true });
|
|
19
|
+
srtwsServer = wss;
|
|
13
20
|
server.on("upgrade", (req, socket, head) => {
|
|
14
21
|
if (req.url === "/__authtics_srtws__") {
|
|
15
22
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
@@ -18,8 +25,8 @@ export function handleSrtwsConnection(server, PageComponent) {
|
|
|
18
25
|
}
|
|
19
26
|
});
|
|
20
27
|
wss.on("connection", (ws) => {
|
|
21
|
-
const renderAndSend = () => {
|
|
22
|
-
const pageHtml =
|
|
28
|
+
const renderAndSend = async () => {
|
|
29
|
+
const pageHtml = await renderPage(tsxPath, reqUrl, config.devMode, config.compilerType, config.hotReload, config.autoPage, config.autoPageDir, config.appDir, fileHandler, rmvFileHandler, reqDAT, config.dat, 'csr', config.patchForSsr);
|
|
23
30
|
ws.send(JSON.stringify({ type: "DOM", html: pageHtml }));
|
|
24
31
|
};
|
|
25
32
|
ws.on("message", () => {
|
|
@@ -27,7 +34,7 @@ export function handleSrtwsConnection(server, PageComponent) {
|
|
|
27
34
|
renderAndSend();
|
|
28
35
|
}
|
|
29
36
|
catch (err) {
|
|
30
|
-
console.error("[
|
|
37
|
+
console.error("[HostJS] SRTWS error:", err);
|
|
31
38
|
}
|
|
32
39
|
});
|
|
33
40
|
});
|
|
@@ -55,12 +62,22 @@ const SRTWS_CLIENT_SCRIPT = `<script>
|
|
|
55
62
|
const ws = new WebSocket(protocol + window.location.host + '/__authtics_srtws__');
|
|
56
63
|
|
|
57
64
|
ws.onmessage = function(event) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
const msg = JSON.parse(event.data);
|
|
66
|
+
if (msg.type === 'DOM') {
|
|
67
|
+
document.documentElement.innerHTML = msg.html;
|
|
68
|
+
const scripts = document.documentElement.querySelectorAll('script');
|
|
69
|
+
|
|
70
|
+
scripts.forEach(oldScript => {
|
|
71
|
+
const newScript = document.createElement('script');
|
|
72
|
+
Array.from(oldScript.attributes).forEach(attr => {
|
|
73
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
74
|
+
});
|
|
75
|
+
newScript.textContent = oldScript.textContent;
|
|
76
|
+
oldScript.parentNode.replaceChild(newScript, oldScript);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
64
81
|
|
|
65
82
|
ws.onopen = function() {
|
|
66
83
|
ws.send(JSON.stringify({}));
|
|
@@ -78,7 +95,6 @@ const nodeStubPlugin = {
|
|
|
78
95
|
}));
|
|
79
96
|
build.onLoad({ filter: /.*/, namespace: 'browser-node-stub' }, () => ({
|
|
80
97
|
contents: `
|
|
81
|
-
// Node.js built-in stub for browser builds
|
|
82
98
|
const stub = new Proxy({}, { get: () => () => undefined });
|
|
83
99
|
export default stub;
|
|
84
100
|
`,
|
|
@@ -86,9 +102,11 @@ const nodeStubPlugin = {
|
|
|
86
102
|
}));
|
|
87
103
|
},
|
|
88
104
|
};
|
|
89
|
-
async function buildWithEsbuild(virtualContent, resolveDir, platform,
|
|
105
|
+
async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForServer = false, options = {}) {
|
|
90
106
|
const toFwdSlash = (p) => p.split(path.sep).join('/');
|
|
91
107
|
const isNode = platform === 'node';
|
|
108
|
+
const sourceMaps = options.sourceMaps === true;
|
|
109
|
+
const outfile = path.resolve(options.outfile ?? path.join(resolveDir, `virtual-entry-${platform}.js`));
|
|
92
110
|
const result = await esbuild.build({
|
|
93
111
|
stdin: {
|
|
94
112
|
contents: virtualContent,
|
|
@@ -97,14 +115,16 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSs
|
|
|
97
115
|
},
|
|
98
116
|
bundle: true,
|
|
99
117
|
write: false,
|
|
118
|
+
outfile,
|
|
100
119
|
format: isNode ? 'cjs' : 'iife',
|
|
101
120
|
platform,
|
|
102
121
|
jsx: 'automatic',
|
|
103
|
-
sourcemap: false,
|
|
104
122
|
target: isNode ? ['node18'] : ['es2020'],
|
|
105
|
-
|
|
123
|
+
sourcemap: sourceMaps ? "external" : false,
|
|
124
|
+
sourcesContent: sourceMaps,
|
|
125
|
+
minify: options.minify ?? !sourceMaps,
|
|
106
126
|
treeShaking: true,
|
|
107
|
-
banner: (isNode &&
|
|
127
|
+
banner: (isNode && patchForServer) ? {
|
|
108
128
|
js: `if (typeof globalThis.window === "undefined") {
|
|
109
129
|
const noop = () => {};
|
|
110
130
|
|
|
@@ -195,7 +215,15 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSs
|
|
|
195
215
|
toFwdSlash(path.join(__dirname, 'rendererTempFiles.js'))
|
|
196
216
|
],
|
|
197
217
|
});
|
|
198
|
-
|
|
218
|
+
const jsFile = result.outputFiles.find((file) => path.resolve(file.path) === outfile);
|
|
219
|
+
const mapFile = result.outputFiles.find((file) => path.resolve(file.path) === `${outfile}.map`);
|
|
220
|
+
if (!jsFile) {
|
|
221
|
+
throw new Error(`esbuild did not produce ${outfile}`);
|
|
222
|
+
}
|
|
223
|
+
const code = mapFile && options.sourceMapUrl
|
|
224
|
+
? `${jsFile.text}\n//# sourceMappingURL=${options.sourceMapUrl}\n`
|
|
225
|
+
: jsFile.text;
|
|
226
|
+
return { code, map: mapFile?.text };
|
|
199
227
|
}
|
|
200
228
|
export function findFileUpwards(startDir, relativePath) {
|
|
201
229
|
let dir = path.resolve(startDir);
|
|
@@ -227,6 +255,73 @@ function resolveTemplate(appDir, autoPageDir, autoPaging) {
|
|
|
227
255
|
}
|
|
228
256
|
return nextToCli;
|
|
229
257
|
}
|
|
258
|
+
let htmlPanelBundleCache;
|
|
259
|
+
async function getHtmlPanelClientBundle() {
|
|
260
|
+
if (!devPanelPath) {
|
|
261
|
+
throw new Error("[HostJS] DevPanel is not available.");
|
|
262
|
+
}
|
|
263
|
+
const mtimeMs = fs.statSync(devPanelPath).mtimeMs;
|
|
264
|
+
if (htmlPanelBundleCache?.path === devPanelPath &&
|
|
265
|
+
htmlPanelBundleCache.mtimeMs === mtimeMs) {
|
|
266
|
+
return htmlPanelBundleCache.bundle;
|
|
267
|
+
}
|
|
268
|
+
const clientEntry = `
|
|
269
|
+
import React from "react";
|
|
270
|
+
import { hydrateRoot } from "react-dom/client";
|
|
271
|
+
import DevPanel from ${JSON.stringify(devPanelPath)};
|
|
272
|
+
|
|
273
|
+
const panelRoot = document.getElementById("authtics-dev-panel-root");
|
|
274
|
+
if (panelRoot) {
|
|
275
|
+
hydrateRoot(panelRoot, React.createElement(DevPanel));
|
|
276
|
+
}
|
|
277
|
+
`;
|
|
278
|
+
const bundle = buildWithEsbuild(clientEntry, path.dirname(devPanelPath), "browser", false, { sourceMaps: false, minify: true }).then(({ code }) => code);
|
|
279
|
+
htmlPanelBundleCache = {
|
|
280
|
+
path: devPanelPath,
|
|
281
|
+
mtimeMs,
|
|
282
|
+
bundle,
|
|
283
|
+
};
|
|
284
|
+
return bundle;
|
|
285
|
+
}
|
|
286
|
+
export async function handleHTML(rawHtml, actuallyShowPanel, configDAT, userDAT, hotReload = false) {
|
|
287
|
+
const datMatches = configDAT === undefined ||
|
|
288
|
+
configDAT === userDAT ||
|
|
289
|
+
(Array.isArray(configDAT) && configDAT.includes(userDAT ?? ""));
|
|
290
|
+
const usesDevPanel = actuallyShowPanel &&
|
|
291
|
+
devPanelPath !== null &&
|
|
292
|
+
datMatches;
|
|
293
|
+
const configScript = `<script>window.__authtics_config__=` +
|
|
294
|
+
JSON.stringify({
|
|
295
|
+
datRestricted: configDAT !== undefined,
|
|
296
|
+
devMode: usesDevPanel,
|
|
297
|
+
}) +
|
|
298
|
+
`;</script>`;
|
|
299
|
+
let panelContainer = "";
|
|
300
|
+
let panelClientScript = "";
|
|
301
|
+
if (usesDevPanel && devPanelPath) {
|
|
302
|
+
const panelModule = await import(pathToFileURL(devPanelPath).href);
|
|
303
|
+
const Panel = (panelModule.default ?? panelModule.DevPanel);
|
|
304
|
+
const panelHtml = renderToString(React.createElement(Panel));
|
|
305
|
+
panelContainer =
|
|
306
|
+
`<div id="authtics-dev-panel-root">${panelHtml}</div>`;
|
|
307
|
+
const clientCode = await getHtmlPanelClientBundle();
|
|
308
|
+
const safeClientCode = clientCode.replace(/<\/script/gi, "<\\/script");
|
|
309
|
+
panelClientScript = `<script>${safeClientCode}</script>`;
|
|
310
|
+
}
|
|
311
|
+
const injection = [
|
|
312
|
+
panelContainer,
|
|
313
|
+
configScript,
|
|
314
|
+
panelClientScript,
|
|
315
|
+
hotReload ? HMR_SCRIPT : "",
|
|
316
|
+
]
|
|
317
|
+
.filter(Boolean)
|
|
318
|
+
.join("\n");
|
|
319
|
+
const bodyClose = /<\/body\s*>/i;
|
|
320
|
+
if (bodyClose.test(rawHtml)) {
|
|
321
|
+
return rawHtml.replace(bodyClose, `${injection}\n$&`);
|
|
322
|
+
}
|
|
323
|
+
return `${rawHtml}\n${injection}`;
|
|
324
|
+
}
|
|
230
325
|
function buildHtml(opts) {
|
|
231
326
|
const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName, datRestricted, devMode, renderMode } = opts;
|
|
232
327
|
const configScript = `<script>window.__authtics_config__={datRestricted:${datRestricted ? 'true' : 'false'},devMode:${devMode !== false ? 'true' : 'false'}};</script>`;
|
|
@@ -260,11 +355,18 @@ function buildHtml(opts) {
|
|
|
260
355
|
</body>
|
|
261
356
|
</html>`;
|
|
262
357
|
}
|
|
263
|
-
function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) {
|
|
264
|
-
if (!fs.existsSync(tempPath) || !fs.existsSync(serverCjsPath))
|
|
358
|
+
function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath, clientBundlePath, serverBundleRequired = true, requiredArtifacts = [], checkArtifactFreshness = true) {
|
|
359
|
+
if (!fs.existsSync(tempPath) || (serverBundleRequired && !fs.existsSync(serverCjsPath)))
|
|
360
|
+
return false;
|
|
361
|
+
if (clientBundlePath && !fs.existsSync(clientBundlePath))
|
|
362
|
+
return false;
|
|
363
|
+
if (requiredArtifacts.some((artifactPath) => !fs.existsSync(artifactPath)))
|
|
265
364
|
return false;
|
|
266
365
|
try {
|
|
267
366
|
const cacheMtime = fs.statSync(tempPath).mtimeMs;
|
|
367
|
+
if (checkArtifactFreshness &&
|
|
368
|
+
requiredArtifacts.some((artifactPath) => fs.statSync(artifactPath).mtimeMs < cacheMtime))
|
|
369
|
+
return false;
|
|
268
370
|
if (fs.statSync(tsxPath).mtimeMs > cacheMtime)
|
|
269
371
|
return false;
|
|
270
372
|
if (templatePath && fs.existsSync(templatePath) && fs.statSync(templatePath).mtimeMs > cacheMtime)
|
|
@@ -277,51 +379,89 @@ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPa
|
|
|
277
379
|
return false;
|
|
278
380
|
}
|
|
279
381
|
}
|
|
280
|
-
const HMR_SCRIPT = `<script>
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
382
|
+
const HMR_SCRIPT = `<script>
|
|
383
|
+
(function () {
|
|
384
|
+
var es = null;
|
|
385
|
+
var disposed = false;
|
|
386
|
+
var reloading = false;
|
|
285
387
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
388
|
+
function connect() {
|
|
389
|
+
if (disposed || reloading || es) return;
|
|
390
|
+
|
|
391
|
+
es = new EventSource('/hmr');
|
|
392
|
+
|
|
393
|
+
es.onopen = function () { };
|
|
394
|
+
|
|
395
|
+
es.onmessage = function (event) {
|
|
396
|
+
if (event.data !== 'reload' || disposed || reloading) return;
|
|
397
|
+
|
|
398
|
+
reloading = true;
|
|
399
|
+
|
|
400
|
+
es.onmessage = null;
|
|
401
|
+
es.onerror = null;
|
|
290
402
|
es.close();
|
|
403
|
+
es = null;
|
|
404
|
+
|
|
291
405
|
window.location.reload();
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
es.onerror = function () {
|
|
409
|
+
if (disposed || reloading) return;
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function dispose() {
|
|
414
|
+
if (disposed) return;
|
|
415
|
+
|
|
416
|
+
disposed = true;
|
|
417
|
+
|
|
418
|
+
if (es) {
|
|
419
|
+
es.onmessage = null;
|
|
420
|
+
es.onerror = null;
|
|
421
|
+
es.close();
|
|
422
|
+
es = null;
|
|
292
423
|
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
},1500);
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
})();</script>`;
|
|
305
|
-
export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT, renderMode = 'ssr', patchForSsr = false) {
|
|
424
|
+
}
|
|
425
|
+
window.addEventListener('pagehide', dispose, { once: true });
|
|
426
|
+
window.addEventListener('unload', dispose, { once: true });
|
|
427
|
+
|
|
428
|
+
connect();
|
|
429
|
+
})();
|
|
430
|
+
</script>`;
|
|
431
|
+
export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT, renderMode = 'ssr', patchForServer = false, prebuildOnly = false) {
|
|
306
432
|
ensureTempDirs();
|
|
307
433
|
if (!fs.existsSync(tsxPath)) {
|
|
308
434
|
throw new Error(`renderPage: source file not found: ${tsxPath}`);
|
|
309
435
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (!datMatched)
|
|
318
|
-
usesDevPanel = false;
|
|
319
|
-
}
|
|
436
|
+
const datMatches = configDAT === undefined ||
|
|
437
|
+
configDAT === reqDAT ||
|
|
438
|
+
(Array.isArray(configDAT) && configDAT.includes(reqDAT ?? ""));
|
|
439
|
+
const usesDevPanel = showPanel &&
|
|
440
|
+
devPanelPath !== null &&
|
|
441
|
+
datMatches;
|
|
442
|
+
const enableDiagnostics = usesDevPanel;
|
|
320
443
|
const templatePath = resolveTemplate(appDir, autoPageDir, autoPaging);
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
444
|
+
const cacheVariant = [
|
|
445
|
+
usesDevPanel ? "panel" : "normal",
|
|
446
|
+
enableDiagnostics ? "mapped" : "fast",
|
|
447
|
+
compiler,
|
|
448
|
+
].join("-");
|
|
449
|
+
const tempPath = tempFileForTsx(tsxPath, cacheVariant);
|
|
450
|
+
const separateClientBundle = getBuildStyle() === "super";
|
|
451
|
+
const clientBundlePath = clientBundleForTsx(tsxPath, cacheVariant);
|
|
452
|
+
const serverCjsPath = serverBundleForTsx(tsxPath, cacheVariant, tempPath);
|
|
453
|
+
const ssgHtmlPath = ssgHtmlForTsx(tsxPath, cacheVariant, tempPath);
|
|
454
|
+
const requiredClientBundlePath = separateClientBundle ? clientBundlePath : undefined;
|
|
455
|
+
const serverCodeDisabled = renderMode === 'csr' || renderMode === 'srtws';
|
|
456
|
+
const clientJsUrl = `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`;
|
|
457
|
+
const clientMapUrl = `${clientJsUrl}.map`;
|
|
458
|
+
const clientMapPath = `${clientBundlePath}.map`;
|
|
459
|
+
const serverMapPath = `${serverCjsPath}.map`;
|
|
460
|
+
const requiredMapArtifacts = enableDiagnostics && renderMode !== "srtws"
|
|
461
|
+
? [clientMapPath, ...(!serverCodeDisabled ? [serverMapPath] : [])]
|
|
462
|
+
: [];
|
|
463
|
+
if (renderMode === 'ssg' &&
|
|
464
|
+
isCacheValid(tsxPath, ssgHtmlPath, serverCjsPath, templatePath, devPanelPath, requiredClientBundlePath, !serverCodeDisabled, requiredMapArtifacts, false)) {
|
|
325
465
|
try {
|
|
326
466
|
return fs.readFileSync(ssgHtmlPath, "utf-8");
|
|
327
467
|
}
|
|
@@ -329,19 +469,38 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
329
469
|
}
|
|
330
470
|
}
|
|
331
471
|
let clientCode;
|
|
472
|
+
let clientMap;
|
|
332
473
|
let serverCode;
|
|
474
|
+
let serverMap;
|
|
333
475
|
let freshWrite = false;
|
|
334
|
-
if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath
|
|
476
|
+
if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath, requiredClientBundlePath, !serverCodeDisabled, requiredMapArtifacts) &&
|
|
477
|
+
(autoPaging || getBuildTrigger() === "prebuilt")) {
|
|
335
478
|
try {
|
|
336
479
|
const parsed = JSON.parse(readTempFile(tempPath));
|
|
337
|
-
|
|
338
|
-
|
|
480
|
+
if (separateClientBundle) {
|
|
481
|
+
const expectedBundleRef = path.relative(process.cwd(), clientBundlePath);
|
|
482
|
+
clientCode = parsed.clientBundle === expectedBundleRef
|
|
483
|
+
? fs.readFileSync(clientBundlePath, "utf-8")
|
|
484
|
+
: undefined;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
clientCode = parsed.clientCode;
|
|
488
|
+
}
|
|
489
|
+
if (enableDiagnostics && renderMode !== "srtws") {
|
|
490
|
+
clientMap = fs.readFileSync(clientMapPath, "utf-8");
|
|
491
|
+
if (!serverCodeDisabled)
|
|
492
|
+
serverMap = fs.readFileSync(serverMapPath, "utf-8");
|
|
493
|
+
}
|
|
494
|
+
serverCode = serverCodeDisabled ? undefined : fs.readFileSync(serverCjsPath, "utf-8");
|
|
339
495
|
}
|
|
340
496
|
catch {
|
|
497
|
+
clientCode = undefined;
|
|
498
|
+
clientMap = undefined;
|
|
499
|
+
serverCode = undefined;
|
|
500
|
+
serverMap = undefined;
|
|
341
501
|
}
|
|
342
502
|
}
|
|
343
|
-
|
|
344
|
-
if (!clientCode || (!serverCode && !isCsr)) {
|
|
503
|
+
if (clientCode === undefined || (clientCode === "" && renderMode !== "srtws") || (!serverCode && !serverCodeDisabled)) {
|
|
345
504
|
const virtualServer = `
|
|
346
505
|
${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
|
|
347
506
|
import Page from ${JSON.stringify(tsxPath)};
|
|
@@ -366,26 +525,64 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
366
525
|
}
|
|
367
526
|
`;
|
|
368
527
|
if (compiler === 'esbuild') {
|
|
369
|
-
if (!
|
|
370
|
-
|
|
528
|
+
if (!serverCodeDisabled) {
|
|
529
|
+
const builtServer = await buildWithEsbuild(virtualServer, process.cwd(), 'node', patchForServer, {
|
|
530
|
+
sourceMaps: enableDiagnostics,
|
|
531
|
+
minify: !enableDiagnostics,
|
|
532
|
+
outfile: serverCjsPath,
|
|
533
|
+
sourceMapUrl: path.basename(serverMapPath),
|
|
534
|
+
});
|
|
535
|
+
serverCode = builtServer.code;
|
|
536
|
+
serverMap = builtServer.map;
|
|
537
|
+
}
|
|
371
538
|
if (renderMode !== 'srtws') {
|
|
372
|
-
|
|
539
|
+
const built = await buildWithEsbuild(virtualClient, process.cwd(), 'browser', patchForServer, {
|
|
540
|
+
sourceMaps: enableDiagnostics,
|
|
541
|
+
minify: !enableDiagnostics,
|
|
542
|
+
outfile: clientBundlePath,
|
|
543
|
+
sourceMapUrl: clientMapUrl,
|
|
544
|
+
});
|
|
545
|
+
clientCode = built.code;
|
|
546
|
+
clientMap = built.map;
|
|
373
547
|
}
|
|
374
548
|
else {
|
|
375
549
|
clientCode = "";
|
|
376
550
|
}
|
|
377
551
|
}
|
|
378
552
|
if (serverCode)
|
|
379
|
-
await fs.promises.writeFile(serverCjsPath, serverCode
|
|
380
|
-
|
|
553
|
+
await fs.promises.writeFile(serverCjsPath, serverCode, "utf-8");
|
|
554
|
+
if (separateClientBundle) {
|
|
555
|
+
await fs.promises.writeFile(clientBundlePath, clientCode ?? "", "utf-8");
|
|
556
|
+
writeTempFile(tempPath, JSON.stringify({
|
|
557
|
+
clientBundle: path.relative(process.cwd(), clientBundlePath),
|
|
558
|
+
}));
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
writeTempFile(tempPath, JSON.stringify({ clientCode }));
|
|
562
|
+
}
|
|
563
|
+
if (serverMap)
|
|
564
|
+
await fs.promises.writeFile(serverMapPath, serverMap, "utf-8");
|
|
565
|
+
if (clientMap)
|
|
566
|
+
await fs.promises.writeFile(clientMapPath, clientMap, "utf-8");
|
|
567
|
+
if (requiredMapArtifacts.includes(clientMapPath) && !clientMap) {
|
|
568
|
+
throw new Error(`[HostJS] Source-map build did not produce ${clientMapPath}`);
|
|
569
|
+
}
|
|
570
|
+
if (requiredMapArtifacts.includes(serverMapPath) && !serverMap) {
|
|
571
|
+
throw new Error(`[HostJS] Source-map build did not produce ${serverMapPath}`);
|
|
572
|
+
}
|
|
381
573
|
freshWrite = true;
|
|
382
574
|
}
|
|
383
|
-
|
|
575
|
+
if (clientCode !== undefined)
|
|
576
|
+
fileHandler?.(clientCode, clientJsUrl);
|
|
577
|
+
if (clientMap !== undefined)
|
|
578
|
+
fileHandler?.(clientMap, clientMapUrl);
|
|
579
|
+
if (prebuildOnly && renderMode !== 'ssg')
|
|
580
|
+
return "";
|
|
384
581
|
let pageHtml = "";
|
|
385
582
|
let devPanelContainer = usesDevPanel
|
|
386
583
|
? `<div id="authtics-dev-panel-root"></div>`
|
|
387
584
|
: "";
|
|
388
|
-
if (!
|
|
585
|
+
if (!serverCodeDisabled) {
|
|
389
586
|
const absoluteServerPath = path.resolve(serverCjsPath);
|
|
390
587
|
delete customRequire.cache[absoluteServerPath];
|
|
391
588
|
const serverModule = customRequire(absoluteServerPath);
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
import type { BuildStyle, BuildTrigger } from "./config.js";
|
|
2
|
+
export declare function setBuildStyle(style: BuildStyle, projectRoot?: string): void;
|
|
3
|
+
export declare function getBuildStyle(): BuildStyle;
|
|
4
|
+
export declare function setBuildTrigger(trigger: BuildTrigger): void;
|
|
5
|
+
export declare function getBuildTrigger(): BuildTrigger;
|
|
6
|
+
export declare function getBuildOutputRoot(): string;
|
|
1
7
|
export declare function ensureTempDirs(): void;
|
|
2
|
-
export declare function tempFileForTsx(tsxPath: string): string;
|
|
8
|
+
export declare function tempFileForTsx(tsxPath: string, variant?: string): string;
|
|
9
|
+
export declare function clientBundleForTsx(tsxPath: string, variant?: string): string;
|
|
10
|
+
export declare function serverBundleForTsx(tsxPath: string, variant?: string, tempPath?: string): string;
|
|
11
|
+
export declare function ssgHtmlForTsx(tsxPath: string, variant?: string, tempPath?: string): string;
|
|
3
12
|
export declare function tempFileForDev(): string;
|
|
4
13
|
export declare function isTempUpToDate(tsxPath: string, tempPath: string): boolean;
|
|
5
14
|
export declare function writeTempFile(tempPath: string, content: string): void;
|
|
@@ -1,25 +1,100 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
let activeBuildStyle = "default";
|
|
4
|
+
let activeBuildTrigger = "default";
|
|
5
|
+
let activeProjectRoot = process.cwd();
|
|
6
|
+
function hostjsDir() {
|
|
7
|
+
return path.join(activeProjectRoot, ".hostjs");
|
|
8
|
+
}
|
|
9
|
+
function legacyDir() {
|
|
10
|
+
return path.join(activeProjectRoot, ".authtics_host");
|
|
11
|
+
}
|
|
12
|
+
function artifactName(tsxPath, variant = "normal") {
|
|
13
|
+
const base = path.basename(tsxPath);
|
|
14
|
+
const hash = simpleHash(`${tsxPath}:${variant}`);
|
|
15
|
+
return `${base}_${hash}`;
|
|
16
|
+
}
|
|
17
|
+
export function setBuildStyle(style, projectRoot = process.cwd()) {
|
|
18
|
+
if (style !== "default" && style !== "super") {
|
|
19
|
+
throw new Error('[HostJS] Invalid buildStyle. Expected "default" or "super".');
|
|
20
|
+
}
|
|
21
|
+
activeBuildStyle = style;
|
|
22
|
+
activeProjectRoot = path.resolve(projectRoot);
|
|
23
|
+
ensureTempDirs();
|
|
24
|
+
}
|
|
25
|
+
export function getBuildStyle() {
|
|
26
|
+
return activeBuildStyle;
|
|
27
|
+
}
|
|
28
|
+
export function setBuildTrigger(trigger) {
|
|
29
|
+
if (trigger !== "default" && trigger !== "prebuilt") {
|
|
30
|
+
throw new Error('[HostJS] Invalid buildTrigger. Expected "default" or "prebuilt".');
|
|
31
|
+
}
|
|
32
|
+
activeBuildTrigger = trigger;
|
|
33
|
+
}
|
|
34
|
+
export function getBuildTrigger() {
|
|
35
|
+
return activeBuildTrigger;
|
|
36
|
+
}
|
|
37
|
+
export function getBuildOutputRoot() {
|
|
38
|
+
return activeBuildStyle === "super" ? hostjsDir() : legacyDir();
|
|
39
|
+
}
|
|
6
40
|
export function ensureTempDirs() {
|
|
7
|
-
if (
|
|
8
|
-
fs.mkdirSync(
|
|
9
|
-
|
|
10
|
-
fs.mkdirSync(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
if (activeBuildStyle === "default") {
|
|
42
|
+
fs.mkdirSync(legacyDir(), { recursive: true });
|
|
43
|
+
fs.mkdirSync(path.join(legacyDir(), "cache"), { recursive: true });
|
|
44
|
+
fs.mkdirSync(path.join(legacyDir(), "dev-cache"), { recursive: true });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const dirs = [
|
|
48
|
+
path.join(hostjsDir(), "cache", "bundler", "client-production"),
|
|
49
|
+
path.join(hostjsDir(), "cache", "bundler", "server-production"),
|
|
50
|
+
path.join(hostjsDir(), "cache", "bundler", "edge-server-production"),
|
|
51
|
+
path.join(hostjsDir(), "diagnostics"),
|
|
52
|
+
path.join(hostjsDir(), "server", "app"),
|
|
53
|
+
path.join(hostjsDir(), "server", "pages"),
|
|
54
|
+
path.join(hostjsDir(), "server", "chunks"),
|
|
55
|
+
path.join(hostjsDir(), "server", "route-cache"),
|
|
56
|
+
path.join(hostjsDir(), "types", "app"),
|
|
57
|
+
path.join(hostjsDir(), "dev"),
|
|
58
|
+
path.join(hostjsDir(), "trace"),
|
|
59
|
+
path.join(hostjsDir(), "trace-build"),
|
|
60
|
+
];
|
|
61
|
+
for (const dir of dirs)
|
|
62
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
export function tempFileForTsx(tsxPath, variant = "normal") {
|
|
15
65
|
ensureTempDirs();
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
66
|
+
const name = artifactName(tsxPath, variant);
|
|
67
|
+
const dir = activeBuildStyle === "super"
|
|
68
|
+
? path.join(hostjsDir(), "server", "route-cache")
|
|
69
|
+
: path.join(legacyDir(), "cache");
|
|
70
|
+
return path.join(dir, `${name}.json`);
|
|
71
|
+
}
|
|
72
|
+
export function clientBundleForTsx(tsxPath, variant = "normal") {
|
|
73
|
+
ensureTempDirs();
|
|
74
|
+
if (activeBuildStyle === "default") {
|
|
75
|
+
return tempFileForTsx(tsxPath, variant).replace(/\.json$/, ".client.js");
|
|
76
|
+
}
|
|
77
|
+
return path.join(hostjsDir(), "cache", "bundler", "client-production", `${artifactName(tsxPath, variant)}.js`);
|
|
78
|
+
}
|
|
79
|
+
export function serverBundleForTsx(tsxPath, variant = "normal", tempPath) {
|
|
80
|
+
ensureTempDirs();
|
|
81
|
+
if (activeBuildStyle === "default") {
|
|
82
|
+
return (tempPath ?? tempFileForTsx(tsxPath, variant)).replace(/\.json$/, ".server.cjs");
|
|
83
|
+
}
|
|
84
|
+
return path.join(hostjsDir(), "cache", "bundler", "server-production", `${artifactName(tsxPath, variant)}.server.cjs`);
|
|
85
|
+
}
|
|
86
|
+
export function ssgHtmlForTsx(tsxPath, variant = "normal", tempPath) {
|
|
87
|
+
ensureTempDirs();
|
|
88
|
+
if (activeBuildStyle === "default") {
|
|
89
|
+
return (tempPath ?? tempFileForTsx(tsxPath, variant)).replace(/\.json$/, ".ssg.html");
|
|
90
|
+
}
|
|
91
|
+
return path.join(hostjsDir(), "server", "route-cache", `${artifactName(tsxPath, variant)}.ssg.html`);
|
|
19
92
|
}
|
|
20
93
|
export function tempFileForDev() {
|
|
21
94
|
ensureTempDirs();
|
|
22
|
-
return
|
|
95
|
+
return activeBuildStyle === "super"
|
|
96
|
+
? path.join(hostjsDir(), "dev", "page.cache.json")
|
|
97
|
+
: path.join(legacyDir(), "dev-cache", "page.cache.json");
|
|
23
98
|
}
|
|
24
99
|
export function isTempUpToDate(tsxPath, tempPath) {
|
|
25
100
|
try {
|
|
@@ -32,14 +107,23 @@ export function isTempUpToDate(tsxPath, tempPath) {
|
|
|
32
107
|
}
|
|
33
108
|
}
|
|
34
109
|
export function writeTempFile(tempPath, content) {
|
|
110
|
+
fs.mkdirSync(path.dirname(tempPath), { recursive: true });
|
|
35
111
|
fs.writeFileSync(tempPath, content, "utf-8");
|
|
36
112
|
}
|
|
37
113
|
export function readTempFile(tempPath) {
|
|
38
114
|
return fs.readFileSync(tempPath, "utf-8");
|
|
39
115
|
}
|
|
40
116
|
export function clearCache() {
|
|
41
|
-
if (
|
|
42
|
-
fs.rmSync(
|
|
117
|
+
if (activeBuildStyle === "default") {
|
|
118
|
+
fs.rmSync(path.join(legacyDir(), "cache"), { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
for (const dir of [
|
|
122
|
+
path.join(hostjsDir(), "cache", "bundler"),
|
|
123
|
+
path.join(hostjsDir(), "server", "route-cache"),
|
|
124
|
+
]) {
|
|
125
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
126
|
+
}
|
|
43
127
|
}
|
|
44
128
|
ensureTempDirs();
|
|
45
129
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AuthticsConfig } from "./config.js";
|
|
2
|
+
export type HostJSRouteKind = "page" | "api" | "static";
|
|
3
|
+
export interface HostJSRouteRecord {
|
|
4
|
+
pathname: string;
|
|
5
|
+
kind: HostJSRouteKind;
|
|
6
|
+
source: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function refreshSuperRouteManifest(projectRoot: string, config: AuthticsConfig, pagesDir?: string, publicDir?: string): void;
|
|
9
|
+
export declare function initializeSuperBuildOutput(projectRoot: string, config: AuthticsConfig, pagesDir?: string, publicDir?: string): void;
|