@jay-framework/production-server 0.17.4 → 0.18.0
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/dist/index.d.ts +10 -117
- package/dist/index.js +453 -1130
- package/dist/init-services-BKVwxzYb.js +796 -0
- package/dist/serve-index-9aAW1pbg.d.ts +179 -0
- package/dist/serve-index.d.ts +3 -0
- package/dist/serve-index.js +23 -0
- package/package.json +24 -13
package/dist/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
-
var __publicField = (obj, key, value) => {
|
|
4
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
-
return value;
|
|
6
|
-
};
|
|
7
1
|
import { build } from "vite";
|
|
8
2
|
import { jayStackCompiler, extractActionsFromSource } from "@jay-framework/compiler-jay-stack";
|
|
9
3
|
import { scanRoutes, JayRouteParamType, parseRouteSegments } from "@jay-framework/stack-route-scanner";
|
|
@@ -11,17 +5,20 @@ import { getLogger } from "@jay-framework/logger";
|
|
|
11
5
|
import path from "node:path";
|
|
12
6
|
import fs from "node:fs/promises";
|
|
13
7
|
import { createRequire } from "node:module";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { jayRuntime } from "@jay-framework/vite-plugin";
|
|
8
|
+
import { DevSlowlyChangingPhase, slowRenderInstances, scanPlugins, runLoadParams, parseCookies } from "@jay-framework/stack-server-runtime";
|
|
9
|
+
import { l as loadProductionPageParts, g as buildPagePartsConfig, d as initializeServices, r as registerActionsFromManifest, i as isActionRequest, a as fetchActionRequest, c as fetchStaticFile, m as matchRequest, f as fetchPageRequest, F as FilesystemArtifactStore } from "./init-services-BKVwxzYb.js";
|
|
10
|
+
import { e, b } from "./init-services-BKVwxzYb.js";
|
|
18
11
|
import crypto from "node:crypto";
|
|
19
12
|
import fs$1 from "node:fs";
|
|
13
|
+
import { jayRuntime } from "@jay-framework/vite-plugin";
|
|
14
|
+
import { injectHeadfullFSTemplates, JAY_IMPORT_RESOLVER, parseJayFile, generateElementHydrateFile, generateServerElementFile } from "@jay-framework/compiler-jay-html";
|
|
15
|
+
import { checkValidationErrors, RuntimeMode } from "@jay-framework/compiler-shared";
|
|
16
|
+
import { parse } from "node-html-parser";
|
|
20
17
|
import http from "node:http";
|
|
21
18
|
import { Readable } from "node:stream";
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import
|
|
19
|
+
import { isJayWebhook } from "@jay-framework/fullstack-component";
|
|
20
|
+
import "@jay-framework/view-state-merge";
|
|
21
|
+
import "@jay-framework/ssr-runtime";
|
|
25
22
|
async function discoverServerEntries(projectRoot, pagesRoot) {
|
|
26
23
|
const logger = getLogger();
|
|
27
24
|
const routes = await scanRoutes(pagesRoot, {
|
|
@@ -209,384 +206,6 @@ async function parseViteManifest(outputDir, packages) {
|
|
|
209
206
|
await fs.rm(viteManifestPath, { force: true });
|
|
210
207
|
return manifest;
|
|
211
208
|
}
|
|
212
|
-
const require2 = createRequire(import.meta.url);
|
|
213
|
-
async function loadProductionPageParts(route, pageModule, jayHtmlContent, projectRoot, tsConfigFilePath, serverBuildDir) {
|
|
214
|
-
const exportName = route.componentExport || "page";
|
|
215
|
-
const compDefinition = pageModule[exportName] ?? pageModule.default;
|
|
216
|
-
const parts = compDefinition ? [{ compDefinition, clientImport: "", clientPart: "" }] : [];
|
|
217
|
-
const dirName = path.dirname(route.jayHtmlPath);
|
|
218
|
-
const fileName = path.basename(route.jayHtmlPath);
|
|
219
|
-
const jayHtmlWithValidations = await parseJayFile(
|
|
220
|
-
jayHtmlContent,
|
|
221
|
-
fileName,
|
|
222
|
-
dirName,
|
|
223
|
-
{ relativePath: tsConfigFilePath },
|
|
224
|
-
JAY_IMPORT_RESOLVER,
|
|
225
|
-
projectRoot
|
|
226
|
-
);
|
|
227
|
-
const jayHtml = checkValidationErrors(jayHtmlWithValidations);
|
|
228
|
-
const headlessInstanceComponents = [];
|
|
229
|
-
const keyedPartModules = [];
|
|
230
|
-
const headlessModuleInfos = [];
|
|
231
|
-
const headlessImports = jayHtml.headlessImports ?? [];
|
|
232
|
-
getLogger().info(
|
|
233
|
-
`[Build] headlessImports for ${fileName}: ${headlessImports.length}, keys: ${Object.keys(jayHtml).join(",")}`
|
|
234
|
-
);
|
|
235
|
-
for (const headlessImport of headlessImports) {
|
|
236
|
-
const module = headlessImport.codeLink.module;
|
|
237
|
-
const name = headlessImport.codeLink.names[0].name;
|
|
238
|
-
const isLocalModule = module[0] === "." || module[0] === "/";
|
|
239
|
-
let modulePath;
|
|
240
|
-
if (isLocalModule) {
|
|
241
|
-
const sourcePath = path.resolve(dirName, module);
|
|
242
|
-
if (serverBuildDir) {
|
|
243
|
-
const relativeToSrc = path.relative(path.join(projectRoot, "src"), sourcePath);
|
|
244
|
-
let compiledPath = path.join(serverBuildDir, relativeToSrc);
|
|
245
|
-
compiledPath = compiledPath.replace(/\.ts$/, ".js");
|
|
246
|
-
if (!compiledPath.endsWith(".js")) {
|
|
247
|
-
const indexPath = path.join(compiledPath, "index.js");
|
|
248
|
-
try {
|
|
249
|
-
await fs.access(indexPath);
|
|
250
|
-
compiledPath = indexPath;
|
|
251
|
-
} catch {
|
|
252
|
-
compiledPath += ".js";
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
modulePath = compiledPath;
|
|
256
|
-
} else {
|
|
257
|
-
modulePath = sourcePath;
|
|
258
|
-
}
|
|
259
|
-
} else {
|
|
260
|
-
modulePath = require2.resolve(module, { paths: [dirName] });
|
|
261
|
-
}
|
|
262
|
-
const headlessModule = await import(modulePath);
|
|
263
|
-
const headlessCompDef = headlessModule[name];
|
|
264
|
-
if (headlessImport.key) {
|
|
265
|
-
const clientModulePath = isLocalModule ? path.resolve(dirName, module) : `${module}/client`;
|
|
266
|
-
const ci = headlessImport.contract ? { contractName: headlessImport.contract.name, metadata: headlessImport.metadata } : void 0;
|
|
267
|
-
parts.push({
|
|
268
|
-
key: headlessImport.key,
|
|
269
|
-
compDefinition: headlessCompDef,
|
|
270
|
-
clientImport: "",
|
|
271
|
-
clientPart: "",
|
|
272
|
-
contractInfo: ci
|
|
273
|
-
});
|
|
274
|
-
keyedPartModules.push({
|
|
275
|
-
key: headlessImport.key,
|
|
276
|
-
modulePath: clientModulePath,
|
|
277
|
-
exportName: name
|
|
278
|
-
});
|
|
279
|
-
headlessModuleInfos.push({
|
|
280
|
-
modulePath,
|
|
281
|
-
exportName: name,
|
|
282
|
-
isLocal: isLocalModule,
|
|
283
|
-
key: headlessImport.key,
|
|
284
|
-
contractInfo: ci
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
if (!headlessImport.key && headlessImport.contract) {
|
|
288
|
-
headlessInstanceComponents.push({
|
|
289
|
-
contractName: headlessImport.contractName,
|
|
290
|
-
compDefinition: headlessCompDef,
|
|
291
|
-
contract: headlessImport.contract
|
|
292
|
-
});
|
|
293
|
-
headlessModuleInfos.push({
|
|
294
|
-
modulePath,
|
|
295
|
-
exportName: name,
|
|
296
|
-
isLocal: isLocalModule,
|
|
297
|
-
contractName: headlessImport.contractName,
|
|
298
|
-
propNames: headlessImport.contract.props?.map((p) => p.name) ?? []
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
const headlessContracts = (jayHtml.headlessImports ?? []).filter((hi) => hi.contract && hi.key).map((hi) => ({
|
|
303
|
-
key: hi.key,
|
|
304
|
-
contract: hi.contract,
|
|
305
|
-
contractPath: hi.contractPath
|
|
306
|
-
}));
|
|
307
|
-
const jayHtmlForDiscovery = injectHeadfullFSTemplates(
|
|
308
|
-
jayHtmlContent,
|
|
309
|
-
dirName,
|
|
310
|
-
JAY_IMPORT_RESOLVER
|
|
311
|
-
);
|
|
312
|
-
let discoveredInstances = [];
|
|
313
|
-
let forEachInstances = [];
|
|
314
|
-
if (headlessInstanceComponents.length > 0) {
|
|
315
|
-
const firstDiscovery = discoverHeadlessInstances(jayHtmlForDiscovery);
|
|
316
|
-
const contractNames = new Set(headlessInstanceComponents.map((c) => c.contractName));
|
|
317
|
-
const withCoords = assignCoordinatesToJayHtml(
|
|
318
|
-
firstDiscovery.preRenderedJayHtml,
|
|
319
|
-
contractNames
|
|
320
|
-
);
|
|
321
|
-
const finalDiscovery = discoverHeadlessInstances(withCoords);
|
|
322
|
-
discoveredInstances = finalDiscovery.instances;
|
|
323
|
-
forEachInstances = finalDiscovery.forEachInstances;
|
|
324
|
-
}
|
|
325
|
-
return {
|
|
326
|
-
parts,
|
|
327
|
-
headlessContracts,
|
|
328
|
-
headlessInstanceComponents,
|
|
329
|
-
discoveredInstances,
|
|
330
|
-
forEachInstances,
|
|
331
|
-
keyedPartModules,
|
|
332
|
-
headlessModuleInfos,
|
|
333
|
-
serverTrackByMap: jayHtml.serverTrackByMap,
|
|
334
|
-
clientTrackByMap: jayHtml.clientTrackByMap
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
function buildPagePartsConfig(pageParts, pageServerModule, pageExportName, buildDir, pageIsPlugin = false) {
|
|
338
|
-
const parts = [];
|
|
339
|
-
if (pageServerModule) {
|
|
340
|
-
parts.push({
|
|
341
|
-
modulePath: pageServerModule,
|
|
342
|
-
exportName: pageExportName,
|
|
343
|
-
source: pageIsPlugin ? "npm" : "local"
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
for (const info of pageParts.headlessModuleInfos) {
|
|
347
|
-
if (info.key) {
|
|
348
|
-
parts.push({
|
|
349
|
-
modulePath: info.isLocal ? path.relative(buildDir, info.modulePath) : info.modulePath,
|
|
350
|
-
exportName: info.exportName,
|
|
351
|
-
source: info.isLocal ? "local" : "npm",
|
|
352
|
-
key: info.key,
|
|
353
|
-
contractInfo: info.contractInfo
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const instanceComponents = [];
|
|
358
|
-
for (const info of pageParts.headlessModuleInfos) {
|
|
359
|
-
if (info.contractName) {
|
|
360
|
-
instanceComponents.push({
|
|
361
|
-
modulePath: info.isLocal ? path.relative(buildDir, info.modulePath) : info.modulePath,
|
|
362
|
-
exportName: info.exportName,
|
|
363
|
-
source: info.isLocal ? "local" : "npm",
|
|
364
|
-
contractName: info.contractName,
|
|
365
|
-
propNames: info.propNames ?? []
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
parts,
|
|
371
|
-
instanceComponents,
|
|
372
|
-
forEachInstances: pageParts.forEachInstances.map((fi) => ({
|
|
373
|
-
contractName: fi.contractName,
|
|
374
|
-
forEachPath: fi.forEachPath,
|
|
375
|
-
trackBy: fi.trackBy,
|
|
376
|
-
propBindings: fi.propBindings,
|
|
377
|
-
coordinateSuffix: fi.coordinateSuffix
|
|
378
|
-
}))
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
async function loadPagePartsFromConfig(configPath, buildDir) {
|
|
382
|
-
const config = JSON.parse(await fs.readFile(configPath, "utf-8"));
|
|
383
|
-
async function importModule(entry) {
|
|
384
|
-
if (!entry.modulePath) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
`Empty modulePath in page-parts.json for "${entry.exportName}" (source: ${entry.source}). Rebuild required.`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
if (entry.source === "local") {
|
|
390
|
-
return import(path.join(buildDir, entry.modulePath));
|
|
391
|
-
}
|
|
392
|
-
return import(entry.modulePath);
|
|
393
|
-
}
|
|
394
|
-
const parts = [];
|
|
395
|
-
for (const entry of config.parts) {
|
|
396
|
-
const mod = await importModule(entry);
|
|
397
|
-
parts.push({
|
|
398
|
-
compDefinition: mod[entry.exportName] ?? mod.default,
|
|
399
|
-
key: entry.key,
|
|
400
|
-
clientImport: "",
|
|
401
|
-
clientPart: "",
|
|
402
|
-
contractInfo: entry.contractInfo
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
const headlessInstanceComponents = [];
|
|
406
|
-
for (const entry of config.instanceComponents) {
|
|
407
|
-
const mod = await importModule(entry);
|
|
408
|
-
const serveTimeContract = {
|
|
409
|
-
props: entry.propNames.map((name) => ({ name }))
|
|
410
|
-
};
|
|
411
|
-
headlessInstanceComponents.push({
|
|
412
|
-
contractName: entry.contractName,
|
|
413
|
-
compDefinition: mod[entry.exportName] ?? mod.default,
|
|
414
|
-
contract: serveTimeContract
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
return {
|
|
418
|
-
parts,
|
|
419
|
-
headlessContracts: [],
|
|
420
|
-
headlessInstanceComponents,
|
|
421
|
-
discoveredInstances: [],
|
|
422
|
-
forEachInstances: config.forEachInstances,
|
|
423
|
-
keyedPartModules: [],
|
|
424
|
-
headlessModuleInfos: []
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
async function compileServerElement(jayHtmlContent, jayHtmlFilename, jayHtmlDir, outputPath, projectRoot, tsConfigFilePath, sourceDir) {
|
|
428
|
-
const jayFile = await parseJayFile(
|
|
429
|
-
jayHtmlContent,
|
|
430
|
-
jayHtmlFilename,
|
|
431
|
-
jayHtmlDir,
|
|
432
|
-
{ relativePath: tsConfigFilePath },
|
|
433
|
-
JAY_IMPORT_RESOLVER,
|
|
434
|
-
projectRoot,
|
|
435
|
-
sourceDir
|
|
436
|
-
);
|
|
437
|
-
const parsedJayFile = checkValidationErrors(jayFile);
|
|
438
|
-
const serverElementCode = checkValidationErrors(generateServerElementFile(parsedJayFile));
|
|
439
|
-
const outputDir = path.dirname(outputPath);
|
|
440
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
441
|
-
const tsPath = outputPath.replace(/\.js$/, ".ts");
|
|
442
|
-
await fs.writeFile(tsPath, serverElementCode, "utf-8");
|
|
443
|
-
const jayOptions = { tsConfigFilePath };
|
|
444
|
-
await build({
|
|
445
|
-
root: projectRoot,
|
|
446
|
-
plugins: [jayRuntime(jayOptions)],
|
|
447
|
-
build: {
|
|
448
|
-
outDir: outputDir,
|
|
449
|
-
emptyOutDir: false,
|
|
450
|
-
minify: false,
|
|
451
|
-
ssr: true,
|
|
452
|
-
rollupOptions: {
|
|
453
|
-
input: { [path.basename(outputPath, ".js")]: tsPath },
|
|
454
|
-
external: [/^node:/, /^@jay-framework\//],
|
|
455
|
-
output: {
|
|
456
|
-
entryFileNames: "[name].js",
|
|
457
|
-
format: "es"
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
logLevel: "warn"
|
|
462
|
-
});
|
|
463
|
-
await fs.rm(tsPath, { force: true });
|
|
464
|
-
let cssFile;
|
|
465
|
-
const css = parsedJayFile.css;
|
|
466
|
-
if (css) {
|
|
467
|
-
const cssFilename = path.basename(outputPath, ".server-element.js") + ".css";
|
|
468
|
-
const cssPath = path.join(outputDir, cssFilename);
|
|
469
|
-
await fs.writeFile(cssPath, css, "utf-8");
|
|
470
|
-
cssFile = cssFilename;
|
|
471
|
-
}
|
|
472
|
-
getLogger().info(`[Build] Compiled server element: ${path.basename(outputPath)}`);
|
|
473
|
-
return { cssFile };
|
|
474
|
-
}
|
|
475
|
-
async function generateHydrationEntry(options) {
|
|
476
|
-
const {
|
|
477
|
-
jayHtmlPath,
|
|
478
|
-
pageModulePath,
|
|
479
|
-
pageExportName = "page",
|
|
480
|
-
slowViewState,
|
|
481
|
-
trackByMap,
|
|
482
|
-
outputPath,
|
|
483
|
-
keyedParts = [],
|
|
484
|
-
clientInits = []
|
|
485
|
-
} = options;
|
|
486
|
-
const hydrateImport = `${jayHtmlPath}?jay-hydrate`;
|
|
487
|
-
const partImports = keyedParts.map((p, i) => `import { ${p.exportName} as keyedPart${i} } from '${p.modulePath}';`).join("\n");
|
|
488
|
-
const hasPageModule = pageModulePath && pageExportName;
|
|
489
|
-
const pagePartExpr = hasPageModule ? `pagePart && pagePart.comp ? { comp: pagePart.comp, contextMarkers: pagePart.contexts || [] } : null` : `null`;
|
|
490
|
-
const partsArray = [
|
|
491
|
-
pagePartExpr,
|
|
492
|
-
...keyedParts.map(
|
|
493
|
-
(p, i) => `keyedPart${i} && keyedPart${i}.comp ? { comp: keyedPart${i}.comp, contextMarkers: keyedPart${i}.contexts || [], key: '${p.key}' } : null`
|
|
494
|
-
)
|
|
495
|
-
];
|
|
496
|
-
const pageImport = hasPageModule ? `import { ${pageExportName} as pagePart } from '${pageModulePath}';` : "";
|
|
497
|
-
const initImports = clientInits.map((ci, i) => `import { ${ci.exportName} as clientInit${i} } from '${ci.modulePath}';`).join("\n");
|
|
498
|
-
const initCalls = clientInits.map(
|
|
499
|
-
(ci, i) => ` if (clientInit${i}?._clientInit) await clientInit${i}._clientInit(clientInitData['${ci.key}'] || {});`
|
|
500
|
-
).join("\n");
|
|
501
|
-
const hasClientInit = clientInits.length > 0;
|
|
502
|
-
const code = `import { hydrateCompositeJayComponent } from '@jay-framework/stack-client-runtime';
|
|
503
|
-
import { deepMergeViewStates } from '@jay-framework/view-state-merge';
|
|
504
|
-
import { hydrate } from '${hydrateImport}';
|
|
505
|
-
${pageImport}
|
|
506
|
-
${partImports}
|
|
507
|
-
${initImports}
|
|
508
|
-
|
|
509
|
-
const slowViewState = ${JSON.stringify(slowViewState)};
|
|
510
|
-
const trackByMap = ${JSON.stringify(trackByMap)};
|
|
511
|
-
|
|
512
|
-
export async function init(fastViewState, fastCarryForward${hasClientInit ? ", clientInitData" : ""}) {
|
|
513
|
-
${initCalls}
|
|
514
|
-
const viewState = deepMergeViewStates(slowViewState, fastViewState, trackByMap);
|
|
515
|
-
const target = document.getElementById('target');
|
|
516
|
-
const rootElement = target.firstElementChild;
|
|
517
|
-
const parts = [
|
|
518
|
-
${partsArray.join(",\n ")}
|
|
519
|
-
].filter(p => p !== null);
|
|
520
|
-
const pageComp = hydrateCompositeJayComponent(
|
|
521
|
-
hydrate, viewState, fastCarryForward,
|
|
522
|
-
parts, trackByMap, rootElement
|
|
523
|
-
);
|
|
524
|
-
return pageComp({});
|
|
525
|
-
}
|
|
526
|
-
`;
|
|
527
|
-
const outputDir = path.dirname(outputPath);
|
|
528
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
529
|
-
await fs.writeFile(outputPath, code, "utf-8");
|
|
530
|
-
getLogger().info(`[Build] Generated hydration entry: ${path.basename(outputPath)}`);
|
|
531
|
-
}
|
|
532
|
-
async function buildInstanceClient(hydrateEntryPath, instanceId, outputDir, projectRoot, jayOptions, minify = true, pagesRoot, buildDir) {
|
|
533
|
-
const logger = getLogger();
|
|
534
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
535
|
-
const fullJayOptions = {
|
|
536
|
-
...jayOptions,
|
|
537
|
-
...pagesRoot && buildDir ? { pagesRoot, buildFolder: buildDir } : {}
|
|
538
|
-
};
|
|
539
|
-
await build({
|
|
540
|
-
root: projectRoot,
|
|
541
|
-
plugins: [...jayStackCompiler(fullJayOptions)],
|
|
542
|
-
build: {
|
|
543
|
-
outDir: outputDir,
|
|
544
|
-
emptyOutDir: false,
|
|
545
|
-
minify,
|
|
546
|
-
manifest: `${instanceId}-manifest.json`,
|
|
547
|
-
rollupOptions: {
|
|
548
|
-
input: { [instanceId]: hydrateEntryPath },
|
|
549
|
-
external: (id) => id.startsWith("@jay-framework/"),
|
|
550
|
-
output: {
|
|
551
|
-
entryFileNames: "[name]-[hash].js",
|
|
552
|
-
chunkFileNames: "chunks/[name]-[hash].js",
|
|
553
|
-
assetFileNames: "[name]-[hash].[ext]",
|
|
554
|
-
format: "es"
|
|
555
|
-
},
|
|
556
|
-
preserveEntrySignatures: "exports-only"
|
|
557
|
-
}
|
|
558
|
-
},
|
|
559
|
-
logLevel: "warn"
|
|
560
|
-
});
|
|
561
|
-
const manifestPath = path.join(outputDir, `${instanceId}-manifest.json`);
|
|
562
|
-
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
563
|
-
await fs.rm(manifestPath, { force: true });
|
|
564
|
-
const entryKey = Object.keys(manifest).find((k) => manifest[k].isEntry);
|
|
565
|
-
if (!entryKey) {
|
|
566
|
-
throw new Error(`No entry found in instance build manifest for ${instanceId}`);
|
|
567
|
-
}
|
|
568
|
-
const entry = manifest[entryKey];
|
|
569
|
-
const result = {
|
|
570
|
-
jsFile: entry.file,
|
|
571
|
-
cssFile: entry.css?.[0]
|
|
572
|
-
};
|
|
573
|
-
logger.info(`[Build] Client bundle: ${result.jsFile}`);
|
|
574
|
-
return result;
|
|
575
|
-
}
|
|
576
|
-
function resolvePackageNameForRoute(compPath) {
|
|
577
|
-
const dir = path.dirname(compPath);
|
|
578
|
-
for (const candidate of [dir, path.join(dir, "..")]) {
|
|
579
|
-
try {
|
|
580
|
-
const pkgJson = JSON.parse(
|
|
581
|
-
fs$1.readFileSync(path.join(candidate, "package.json"), "utf-8")
|
|
582
|
-
);
|
|
583
|
-
if (pkgJson.name)
|
|
584
|
-
return pkgJson.name;
|
|
585
|
-
} catch {
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return void 0;
|
|
589
|
-
}
|
|
590
209
|
function hashParams(params, suffix) {
|
|
591
210
|
const sorted = Object.keys(params).sort().reduce(
|
|
592
211
|
(acc, key) => {
|
|
@@ -601,7 +220,7 @@ function hashParams(params, suffix) {
|
|
|
601
220
|
const input = suffix ? json + ":" + suffix : json;
|
|
602
221
|
return "_" + crypto.createHash("md5").update(input).digest("hex").substring(0, 8);
|
|
603
222
|
}
|
|
604
|
-
async function buildInstance(route, params, pageModule, ctx) {
|
|
223
|
+
async function buildInstance(route, params, pageModule, ctx, routeServerElementPath, routeCssPath, routeHydratePath, routeClientBundlePath) {
|
|
605
224
|
const logger = getLogger();
|
|
606
225
|
const routeDir = route.rawRoute.replace(/^\//, "") || "index";
|
|
607
226
|
const paramHash = hashParams(params, ctx.rebuildSuffix);
|
|
@@ -611,7 +230,7 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
611
230
|
await fs.mkdir(backendInstanceDir, { recursive: true });
|
|
612
231
|
await fs.mkdir(frontendInstanceDir, { recursive: true });
|
|
613
232
|
const jayHtmlContent = await fs.readFile(route.jayHtmlPath, "utf-8");
|
|
614
|
-
|
|
233
|
+
path.dirname(route.jayHtmlPath);
|
|
615
234
|
const serverBuildDir = path.join(ctx.backendDir, "server");
|
|
616
235
|
const pageParts = await loadProductionPageParts(
|
|
617
236
|
route,
|
|
@@ -636,7 +255,7 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
636
255
|
let pageIsPlugin = false;
|
|
637
256
|
if (route.compPath) {
|
|
638
257
|
if (route.componentExport) {
|
|
639
|
-
pageServerModule = route.compPath;
|
|
258
|
+
pageServerModule = route.packageName || route.compPath;
|
|
640
259
|
pageIsPlugin = true;
|
|
641
260
|
} else {
|
|
642
261
|
const relativePath = path.relative(ctx.projectRoot, route.compPath);
|
|
@@ -675,197 +294,57 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
675
294
|
}
|
|
676
295
|
const slowViewState = slowResult.rendered;
|
|
677
296
|
const carryForward = slowResult.carryForward;
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
const parseResult = parseContract(contractContent, path.basename(contractPath));
|
|
683
|
-
if (parseResult.val)
|
|
684
|
-
contract = parseResult.val;
|
|
685
|
-
} catch {
|
|
686
|
-
}
|
|
687
|
-
const jayHtmlWithTemplates = injectHeadfullFSTemplates(
|
|
688
|
-
jayHtmlContent,
|
|
689
|
-
sourceDir,
|
|
690
|
-
JAY_IMPORT_RESOLVER
|
|
691
|
-
);
|
|
692
|
-
const transformResult = slowRenderTransform({
|
|
693
|
-
jayHtmlContent: jayHtmlWithTemplates,
|
|
694
|
-
slowViewState,
|
|
695
|
-
contract,
|
|
696
|
-
headlessContracts: pageParts.headlessContracts,
|
|
697
|
-
sourceDir,
|
|
698
|
-
importResolver: JAY_IMPORT_RESOLVER
|
|
699
|
-
});
|
|
700
|
-
if (!transformResult.val) {
|
|
701
|
-
throw new Error(
|
|
702
|
-
`Slow render transform failed for ${route.rawRoute}: ${transformResult.validations.join(", ")}`
|
|
297
|
+
if (pageParts.discoveredInstances.length > 0 && pageParts.headlessInstanceComponents.length > 0) {
|
|
298
|
+
const slowResult2 = await slowRenderInstances(
|
|
299
|
+
pageParts.discoveredInstances,
|
|
300
|
+
pageParts.headlessInstanceComponents
|
|
703
301
|
);
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
discovered: [],
|
|
722
|
-
carryForwards: {}
|
|
723
|
-
};
|
|
724
|
-
carryForward.__instances = {
|
|
725
|
-
discovered: [
|
|
726
|
-
...existingInstances.discovered,
|
|
727
|
-
...slowResult2.instancePhaseData.discovered
|
|
728
|
-
],
|
|
729
|
-
carryForwards: {
|
|
730
|
-
...existingInstances.carryForwards,
|
|
731
|
-
...slowResult2.instancePhaseData.carryForwards
|
|
732
|
-
},
|
|
733
|
-
slowViewStates: {
|
|
734
|
-
...existingInstances.slowViewStates || {},
|
|
735
|
-
...slowResult2.instancePhaseData.slowViewStates
|
|
736
|
-
}
|
|
737
|
-
};
|
|
738
|
-
carryForward.__instanceSlowViewStates = {
|
|
739
|
-
...carryForward.__instanceSlowViewStates || {},
|
|
740
|
-
...Object.fromEntries(
|
|
741
|
-
slowResult2.resolvedData.map((d) => [
|
|
742
|
-
d.coordinate.join("/"),
|
|
743
|
-
d.slowViewState
|
|
744
|
-
])
|
|
745
|
-
)
|
|
746
|
-
};
|
|
747
|
-
carryForward.__instanceResolvedData = [
|
|
748
|
-
...carryForward.__instanceResolvedData || [],
|
|
749
|
-
...slowResult2.resolvedData
|
|
750
|
-
];
|
|
751
|
-
const pass2Result = resolveHeadlessInstances(
|
|
752
|
-
preRenderedJayHtml,
|
|
753
|
-
slowResult2.resolvedData,
|
|
754
|
-
JAY_IMPORT_RESOLVER
|
|
755
|
-
);
|
|
756
|
-
if (pass2Result.val) {
|
|
757
|
-
preRenderedJayHtml = pass2Result.val;
|
|
302
|
+
if (slowResult2) {
|
|
303
|
+
const existingInstances = carryForward.__instances || {
|
|
304
|
+
discovered: [],
|
|
305
|
+
carryForwards: {}
|
|
306
|
+
};
|
|
307
|
+
carryForward.__instances = {
|
|
308
|
+
discovered: [
|
|
309
|
+
...existingInstances.discovered,
|
|
310
|
+
...slowResult2.instancePhaseData.discovered
|
|
311
|
+
],
|
|
312
|
+
carryForwards: {
|
|
313
|
+
...existingInstances.carryForwards,
|
|
314
|
+
...slowResult2.instancePhaseData.carryForwards
|
|
315
|
+
},
|
|
316
|
+
slowViewStates: {
|
|
317
|
+
...existingInstances.slowViewStates || {},
|
|
318
|
+
...slowResult2.instancePhaseData.slowViewStates
|
|
758
319
|
}
|
|
759
|
-
}
|
|
320
|
+
};
|
|
760
321
|
}
|
|
761
322
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
return prefix + `src="${rel}"`;
|
|
772
|
-
}
|
|
773
|
-
);
|
|
774
|
-
preRenderedJayHtml = preRenderedJayHtml.replace(
|
|
775
|
-
/(<script\s+type="application\/jay-headfull"[^>]*\s)(contract="([^"]*)")/g,
|
|
776
|
-
(_match, prefix, _contractAttr, contractVal) => {
|
|
777
|
-
if (path.isAbsolute(contractVal))
|
|
778
|
-
return prefix + `contract="${contractVal}"`;
|
|
779
|
-
const abs = path.resolve(sourceDir, contractVal);
|
|
780
|
-
let rel = path.relative(backendInstanceDir, abs);
|
|
781
|
-
if (!rel.startsWith("."))
|
|
782
|
-
rel = "./" + rel;
|
|
783
|
-
return prefix + `contract="${rel}"`;
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
const preRenderedPath = path.join(backendInstanceDir, `${instanceId}.jay-html`);
|
|
787
|
-
await fs.writeFile(preRenderedPath, preRenderedJayHtml, "utf-8");
|
|
788
|
-
const cacheMetadataPath = path.join(backendInstanceDir, `${instanceId}.cache.json`);
|
|
323
|
+
if (pageParts.forEachInstances.length > 0) {
|
|
324
|
+
const existingInstances = carryForward.__instances || {
|
|
325
|
+
discovered: [],
|
|
326
|
+
carryForwards: {}
|
|
327
|
+
};
|
|
328
|
+
existingInstances.forEachInstances = pageParts.forEachInstances;
|
|
329
|
+
carryForward.__instances = existingInstances;
|
|
330
|
+
}
|
|
331
|
+
const cachePath = path.join(backendInstanceDir, `${instanceId}.cache.json`);
|
|
789
332
|
await fs.writeFile(
|
|
790
|
-
|
|
333
|
+
cachePath,
|
|
791
334
|
JSON.stringify({
|
|
792
335
|
slowViewState,
|
|
793
336
|
carryForward
|
|
794
337
|
}),
|
|
795
338
|
"utf-8"
|
|
796
339
|
);
|
|
797
|
-
logger.info(`[Build]
|
|
798
|
-
const serverElementPath = path.join(backendInstanceDir, `${instanceId}.server-element.js`);
|
|
799
|
-
const serverElementResult = await compileServerElement(
|
|
800
|
-
preRenderedJayHtml,
|
|
801
|
-
`${instanceId}.jay-html`,
|
|
802
|
-
backendInstanceDir,
|
|
803
|
-
serverElementPath,
|
|
804
|
-
ctx.projectRoot,
|
|
805
|
-
ctx.tsConfigFilePath,
|
|
806
|
-
sourceDir
|
|
807
|
-
);
|
|
808
|
-
const hydrateEntryPath = path.join(backendInstanceDir, `${instanceId}.hydrate-entry.ts`);
|
|
809
|
-
const relativeJayHtmlPath = path.relative(backendInstanceDir, preRenderedPath);
|
|
810
|
-
let pageModulePath;
|
|
811
|
-
let pageExportName;
|
|
812
|
-
if (route.componentExport) {
|
|
813
|
-
const pkgName = resolvePackageNameForRoute(route.compPath);
|
|
814
|
-
pageModulePath = pkgName ? `${pkgName}/client` : "./" + path.relative(backendInstanceDir, route.compPath);
|
|
815
|
-
pageExportName = route.componentExport;
|
|
816
|
-
} else if (route.compPath) {
|
|
817
|
-
pageModulePath = "./" + path.relative(backendInstanceDir, route.compPath);
|
|
818
|
-
pageExportName = "page";
|
|
819
|
-
} else {
|
|
820
|
-
pageModulePath = "";
|
|
821
|
-
pageExportName = "";
|
|
822
|
-
}
|
|
823
|
-
if (pageParts.keyedPartModules.length > 0) {
|
|
824
|
-
logger.info(
|
|
825
|
-
`[Build] Keyed parts for ${routeDir}: ${pageParts.keyedPartModules.map((p) => p.key).join(", ")}`
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
await generateHydrationEntry({
|
|
829
|
-
jayHtmlPath: "./" + relativeJayHtmlPath,
|
|
830
|
-
pageModulePath,
|
|
831
|
-
pageExportName,
|
|
832
|
-
slowViewState,
|
|
833
|
-
trackByMap: pageParts.clientTrackByMap || {},
|
|
834
|
-
outputPath: hydrateEntryPath,
|
|
835
|
-
keyedParts: pageParts.keyedPartModules,
|
|
836
|
-
clientInits: ctx.clientInits
|
|
837
|
-
});
|
|
838
|
-
const clientResult = await buildInstanceClient(
|
|
839
|
-
hydrateEntryPath,
|
|
840
|
-
instanceId,
|
|
841
|
-
frontendInstanceDir,
|
|
842
|
-
ctx.projectRoot,
|
|
843
|
-
ctx.jayOptions,
|
|
844
|
-
ctx.minify ?? true,
|
|
845
|
-
ctx.pagesRoot,
|
|
846
|
-
ctx.buildDir
|
|
847
|
-
);
|
|
848
|
-
await fs.rm(hydrateEntryPath, { force: true });
|
|
849
|
-
const cssFile = clientResult.cssFile || serverElementResult.cssFile;
|
|
850
|
-
if (serverElementResult.cssFile && !clientResult.cssFile) {
|
|
851
|
-
const srcCss = path.join(backendInstanceDir, serverElementResult.cssFile);
|
|
852
|
-
const dstCss = path.join(frontendInstanceDir, serverElementResult.cssFile);
|
|
853
|
-
try {
|
|
854
|
-
await fs.rename(srcCss, dstCss);
|
|
855
|
-
} catch {
|
|
856
|
-
await fs.copyFile(srcCss, dstCss);
|
|
857
|
-
await fs.rm(srcCss, { force: true });
|
|
858
|
-
}
|
|
859
|
-
}
|
|
340
|
+
logger.info(`[Build] Instance data: ${routeDir}/${instanceId}`);
|
|
341
|
+
const serverElementPath = routeServerElementPath ? path.join(ctx.backendDir, routeServerElementPath) : path.join(backendInstanceDir, `${instanceId}.server-element.js`);
|
|
860
342
|
const instanceEntry = {
|
|
861
343
|
params,
|
|
862
|
-
|
|
344
|
+
cachePath: path.relative(ctx.backendDir, cachePath),
|
|
863
345
|
serverElementPath: path.relative(ctx.backendDir, serverElementPath),
|
|
864
|
-
clientBundlePath:
|
|
865
|
-
|
|
866
|
-
path.join(frontendInstanceDir, clientResult.jsFile)
|
|
867
|
-
),
|
|
868
|
-
clientCssPath: cssFile ? path.relative(ctx.frontendDir, path.join(frontendInstanceDir, cssFile)) : void 0
|
|
346
|
+
clientBundlePath: routeClientBundlePath || "",
|
|
347
|
+
clientCssPath: routeCssPath
|
|
869
348
|
};
|
|
870
349
|
return { status: "success", instanceEntry, slowViewState, carryForward, contracts };
|
|
871
350
|
}
|
|
@@ -979,53 +458,299 @@ async function scanPluginRoutes(projectRoot, projectRoutes) {
|
|
|
979
458
|
rawRoute: route.path,
|
|
980
459
|
jayHtmlPath,
|
|
981
460
|
compPath,
|
|
982
|
-
componentExport
|
|
461
|
+
componentExport,
|
|
462
|
+
packageName: plugin.packageName
|
|
983
463
|
});
|
|
984
464
|
logger.info(`[Routes] Plugin "${plugin.manifest.name}" provides route ${route.path}`);
|
|
985
465
|
}
|
|
986
466
|
}
|
|
987
467
|
return pluginRoutes;
|
|
988
468
|
}
|
|
989
|
-
function resolvePluginExport(pluginPath, exportSubpath) {
|
|
990
|
-
const normalized = exportSubpath.replace(/^\.\//, "");
|
|
991
|
-
const packageJsonPath = path.join(pluginPath, "package.json");
|
|
992
|
-
try {
|
|
993
|
-
const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
|
|
994
|
-
if (packageJson.exports) {
|
|
995
|
-
const exportKey = "./" + normalized;
|
|
996
|
-
const exportValue = packageJson.exports[exportKey];
|
|
997
|
-
if (exportValue) {
|
|
998
|
-
const resolved = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
|
|
999
|
-
if (resolved)
|
|
1000
|
-
return path.join(pluginPath, resolved);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
} catch {
|
|
1004
|
-
}
|
|
1005
|
-
for (const dir of ["dist", "lib", ""]) {
|
|
1006
|
-
const candidate = path.join(pluginPath, dir, normalized);
|
|
1007
|
-
try {
|
|
1008
|
-
fs$1.accessSync(candidate);
|
|
1009
|
-
return candidate;
|
|
1010
|
-
} catch {
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
return void 0;
|
|
469
|
+
function resolvePluginExport(pluginPath, exportSubpath) {
|
|
470
|
+
const normalized = exportSubpath.replace(/^\.\//, "");
|
|
471
|
+
const packageJsonPath = path.join(pluginPath, "package.json");
|
|
472
|
+
try {
|
|
473
|
+
const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
|
|
474
|
+
if (packageJson.exports) {
|
|
475
|
+
const exportKey = "./" + normalized;
|
|
476
|
+
const exportValue = packageJson.exports[exportKey];
|
|
477
|
+
if (exportValue) {
|
|
478
|
+
const resolved = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
|
|
479
|
+
if (resolved)
|
|
480
|
+
return path.join(pluginPath, resolved);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
for (const dir of ["dist", "lib", ""]) {
|
|
486
|
+
const candidate = path.join(pluginPath, dir, normalized);
|
|
487
|
+
try {
|
|
488
|
+
fs$1.accessSync(candidate);
|
|
489
|
+
return candidate;
|
|
490
|
+
} catch {
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return void 0;
|
|
494
|
+
}
|
|
495
|
+
function resolvePluginModule(pluginPath) {
|
|
496
|
+
const pkgJsonPath = path.join(pluginPath, "package.json");
|
|
497
|
+
try {
|
|
498
|
+
const pkg = JSON.parse(fs$1.readFileSync(pkgJsonPath, "utf-8"));
|
|
499
|
+
const mainExport = pkg.exports?.["."];
|
|
500
|
+
const mainPath = typeof mainExport === "string" ? mainExport : mainExport?.default || mainExport?.import || pkg.main;
|
|
501
|
+
if (mainPath) {
|
|
502
|
+
const resolved = path.join(pluginPath, mainPath);
|
|
503
|
+
if (fs$1.existsSync(resolved))
|
|
504
|
+
return resolved;
|
|
505
|
+
}
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
return path.join(pluginPath, "dist", "index.js");
|
|
509
|
+
}
|
|
510
|
+
async function compileServerElement(jayHtmlContent, jayHtmlFilename, jayHtmlDir, outputPath, projectRoot, tsConfigFilePath, sourceDir) {
|
|
511
|
+
const jayFile = await parseJayFile(
|
|
512
|
+
jayHtmlContent,
|
|
513
|
+
jayHtmlFilename,
|
|
514
|
+
jayHtmlDir,
|
|
515
|
+
{ relativePath: tsConfigFilePath },
|
|
516
|
+
JAY_IMPORT_RESOLVER,
|
|
517
|
+
projectRoot,
|
|
518
|
+
sourceDir
|
|
519
|
+
);
|
|
520
|
+
const parsedJayFile = checkValidationErrors(jayFile);
|
|
521
|
+
const serverElementCode = checkValidationErrors(generateServerElementFile(parsedJayFile));
|
|
522
|
+
const outputDir = path.dirname(outputPath);
|
|
523
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
524
|
+
const tsPath = outputPath.replace(/\.js$/, ".ts");
|
|
525
|
+
await fs.writeFile(tsPath, serverElementCode, "utf-8");
|
|
526
|
+
const jayOptions = { tsConfigFilePath };
|
|
527
|
+
await build({
|
|
528
|
+
root: projectRoot,
|
|
529
|
+
plugins: [jayRuntime(jayOptions)],
|
|
530
|
+
build: {
|
|
531
|
+
outDir: outputDir,
|
|
532
|
+
emptyOutDir: false,
|
|
533
|
+
minify: false,
|
|
534
|
+
ssr: true,
|
|
535
|
+
rollupOptions: {
|
|
536
|
+
input: { [path.basename(outputPath, ".js")]: tsPath },
|
|
537
|
+
external: [/^node:/, /^@jay-framework\//],
|
|
538
|
+
output: {
|
|
539
|
+
entryFileNames: "[name].js",
|
|
540
|
+
format: "es"
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
logLevel: "warn"
|
|
545
|
+
});
|
|
546
|
+
await fs.rm(tsPath, { force: true });
|
|
547
|
+
let cssFile;
|
|
548
|
+
const css = parsedJayFile.css;
|
|
549
|
+
if (css) {
|
|
550
|
+
const cssFilename = path.basename(outputPath, ".server-element.js") + ".css";
|
|
551
|
+
const cssPath = path.join(outputDir, cssFilename);
|
|
552
|
+
await fs.writeFile(cssPath, css, "utf-8");
|
|
553
|
+
cssFile = cssFilename;
|
|
554
|
+
}
|
|
555
|
+
getLogger().info(`[Build] Compiled server element: ${path.basename(outputPath)}`);
|
|
556
|
+
return { cssFile };
|
|
557
|
+
}
|
|
558
|
+
async function compileRouteServerElement(jayHtmlPath, outputPath, projectRoot, tsConfigFilePath) {
|
|
559
|
+
const jayHtmlContent = await fs.readFile(jayHtmlPath, "utf-8");
|
|
560
|
+
const sourceDir = path.dirname(jayHtmlPath);
|
|
561
|
+
const outputDir = path.dirname(outputPath);
|
|
562
|
+
let jayHtml = injectHeadfullFSTemplates(jayHtmlContent, sourceDir, JAY_IMPORT_RESOLVER);
|
|
563
|
+
jayHtml = resolveJayHtmlPaths(jayHtml, sourceDir, outputDir);
|
|
564
|
+
return compileServerElement(
|
|
565
|
+
jayHtml,
|
|
566
|
+
path.basename(jayHtmlPath),
|
|
567
|
+
outputDir,
|
|
568
|
+
outputPath,
|
|
569
|
+
projectRoot,
|
|
570
|
+
tsConfigFilePath,
|
|
571
|
+
sourceDir
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
async function compileRouteHydrateScript(jayHtmlPath, outputDir, projectRoot, tsConfigFilePath, minify = true) {
|
|
575
|
+
const jayHtmlContent = await fs.readFile(jayHtmlPath, "utf-8");
|
|
576
|
+
const sourceDir = path.dirname(jayHtmlPath);
|
|
577
|
+
let jayHtml = injectHeadfullFSTemplates(jayHtmlContent, sourceDir, JAY_IMPORT_RESOLVER);
|
|
578
|
+
jayHtml = resolveJayHtmlPaths(jayHtml, sourceDir, outputDir);
|
|
579
|
+
const jayFile = await parseJayFile(
|
|
580
|
+
jayHtml,
|
|
581
|
+
path.basename(jayHtmlPath),
|
|
582
|
+
outputDir,
|
|
583
|
+
{ relativePath: tsConfigFilePath },
|
|
584
|
+
JAY_IMPORT_RESOLVER,
|
|
585
|
+
projectRoot,
|
|
586
|
+
sourceDir
|
|
587
|
+
);
|
|
588
|
+
const parsedJayFile = checkValidationErrors(jayFile);
|
|
589
|
+
const hydrateCode = checkValidationErrors(
|
|
590
|
+
generateElementHydrateFile(parsedJayFile, RuntimeMode.MainTrusted)
|
|
591
|
+
);
|
|
592
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
593
|
+
const tsPath = path.join(outputDir, "route.hydrate.ts");
|
|
594
|
+
await fs.writeFile(tsPath, hydrateCode, "utf-8");
|
|
595
|
+
const jayOptions = { tsConfigFilePath };
|
|
596
|
+
await build({
|
|
597
|
+
root: projectRoot,
|
|
598
|
+
plugins: [...jayStackCompiler(jayOptions)],
|
|
599
|
+
build: {
|
|
600
|
+
outDir: outputDir,
|
|
601
|
+
emptyOutDir: false,
|
|
602
|
+
minify,
|
|
603
|
+
manifest: "route-hydrate-manifest.json",
|
|
604
|
+
rollupOptions: {
|
|
605
|
+
input: { "route.hydrate": tsPath },
|
|
606
|
+
external: (id) => id.startsWith("@jay-framework/"),
|
|
607
|
+
output: {
|
|
608
|
+
entryFileNames: "[name]-[hash].js",
|
|
609
|
+
format: "es"
|
|
610
|
+
},
|
|
611
|
+
preserveEntrySignatures: "exports-only"
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
logLevel: "warn"
|
|
615
|
+
});
|
|
616
|
+
await fs.rm(tsPath, { force: true });
|
|
617
|
+
const manifestPath = path.join(outputDir, "route-hydrate-manifest.json");
|
|
618
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
619
|
+
await fs.rm(manifestPath, { force: true });
|
|
620
|
+
const entryKey = Object.keys(manifest).find((k) => manifest[k].isEntry);
|
|
621
|
+
if (!entryKey)
|
|
622
|
+
throw new Error("No entry in route hydrate manifest");
|
|
623
|
+
const jsFile = manifest[entryKey].file;
|
|
624
|
+
getLogger().info(`[Build] Compiled route hydrate script: ${jsFile}`);
|
|
625
|
+
return { jsFile };
|
|
626
|
+
}
|
|
627
|
+
function resolveJayHtmlPaths(html, sourceDir, targetDir) {
|
|
628
|
+
const root = parse(html, {
|
|
629
|
+
comment: true,
|
|
630
|
+
blockTextElements: { script: true, style: true }
|
|
631
|
+
});
|
|
632
|
+
const rewrite = (el, attr) => {
|
|
633
|
+
const val = el.getAttribute(attr);
|
|
634
|
+
if (val && (val.startsWith("./") || val.startsWith("../"))) {
|
|
635
|
+
const abs = path.resolve(sourceDir, val);
|
|
636
|
+
let rel = path.relative(targetDir, abs);
|
|
637
|
+
if (!rel.startsWith("."))
|
|
638
|
+
rel = "./" + rel;
|
|
639
|
+
el.setAttribute(attr, rel);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
for (const el of root.querySelectorAll('script[type="application/jay-data"]')) {
|
|
643
|
+
rewrite(el, "contract");
|
|
644
|
+
}
|
|
645
|
+
for (const el of root.querySelectorAll('script[type="application/jay-headless"]')) {
|
|
646
|
+
rewrite(el, "src");
|
|
647
|
+
rewrite(el, "contract");
|
|
648
|
+
}
|
|
649
|
+
for (const el of root.querySelectorAll('script[type="application/jay-headfull"]')) {
|
|
650
|
+
rewrite(el, "src");
|
|
651
|
+
rewrite(el, "contract");
|
|
652
|
+
}
|
|
653
|
+
for (const el of root.querySelectorAll('link[rel="stylesheet"]')) {
|
|
654
|
+
rewrite(el, "href");
|
|
655
|
+
}
|
|
656
|
+
return root.toString();
|
|
657
|
+
}
|
|
658
|
+
async function generateRouteHydrationEntry(options) {
|
|
659
|
+
const {
|
|
660
|
+
hydrateImport,
|
|
661
|
+
pageModulePath,
|
|
662
|
+
pageExportName = "page",
|
|
663
|
+
trackByMap,
|
|
664
|
+
outputPath,
|
|
665
|
+
keyedParts = [],
|
|
666
|
+
clientInits = []
|
|
667
|
+
} = options;
|
|
668
|
+
const partImports = keyedParts.map((p, i) => `import { ${p.exportName} as keyedPart${i} } from '${p.modulePath}';`).join("\n");
|
|
669
|
+
const hasPageModule = pageModulePath && pageExportName;
|
|
670
|
+
const pagePartExpr = hasPageModule ? `pagePart && pagePart.comp ? { comp: pagePart.comp, contextMarkers: pagePart.contexts || [] } : null` : `null`;
|
|
671
|
+
const partsArray = [
|
|
672
|
+
pagePartExpr,
|
|
673
|
+
...keyedParts.map(
|
|
674
|
+
(p, i) => `keyedPart${i} && keyedPart${i}.comp ? { comp: keyedPart${i}.comp, contextMarkers: keyedPart${i}.contexts || [], key: '${p.key}' } : null`
|
|
675
|
+
)
|
|
676
|
+
];
|
|
677
|
+
const pageImport = hasPageModule ? `import { ${pageExportName} as pagePart } from '${pageModulePath}';` : "";
|
|
678
|
+
const initImports = clientInits.map((ci, i) => `import { ${ci.exportName} as clientInit${i} } from '${ci.modulePath}';`).join("\n");
|
|
679
|
+
const initCalls = clientInits.map(
|
|
680
|
+
(ci, i) => ` if (clientInit${i}?._clientInit) await clientInit${i}._clientInit(clientInitData['${ci.key}'] || {});`
|
|
681
|
+
).join("\n");
|
|
682
|
+
const code = `import { hydrateCompositeJayComponent } from '@jay-framework/stack-client-runtime';
|
|
683
|
+
import { deepMergeViewStates } from '@jay-framework/view-state-merge';
|
|
684
|
+
import { hydrate } from '${hydrateImport}';
|
|
685
|
+
${pageImport}
|
|
686
|
+
${partImports}
|
|
687
|
+
${initImports}
|
|
688
|
+
|
|
689
|
+
const trackByMap = ${JSON.stringify(trackByMap)};
|
|
690
|
+
|
|
691
|
+
export async function init(slowViewState, fastViewState, fastCarryForward, clientInitData) {
|
|
692
|
+
${initCalls}
|
|
693
|
+
const viewState = deepMergeViewStates(slowViewState, fastViewState, trackByMap);
|
|
694
|
+
const target = document.getElementById('target');
|
|
695
|
+
const rootElement = target.firstElementChild;
|
|
696
|
+
const parts = [
|
|
697
|
+
${partsArray.join(",\n ")}
|
|
698
|
+
].filter(p => p !== null);
|
|
699
|
+
const pageComp = hydrateCompositeJayComponent(
|
|
700
|
+
hydrate, viewState, fastCarryForward,
|
|
701
|
+
parts, trackByMap, rootElement
|
|
702
|
+
);
|
|
703
|
+
return pageComp({});
|
|
704
|
+
}
|
|
705
|
+
`;
|
|
706
|
+
const outputDir = path.dirname(outputPath);
|
|
707
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
708
|
+
await fs.writeFile(outputPath, code, "utf-8");
|
|
709
|
+
getLogger().info(`[Build] Generated route hydration entry: ${path.basename(outputPath)}`);
|
|
1014
710
|
}
|
|
1015
|
-
function
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
711
|
+
async function buildInstanceClient(hydrateEntryPath, instanceId, outputDir, projectRoot, jayOptions, minify = true, pagesRoot, buildDir) {
|
|
712
|
+
const logger = getLogger();
|
|
713
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
714
|
+
const fullJayOptions = {
|
|
715
|
+
...jayOptions,
|
|
716
|
+
...pagesRoot && buildDir ? { pagesRoot, buildFolder: buildDir } : {}
|
|
717
|
+
};
|
|
718
|
+
await build({
|
|
719
|
+
root: projectRoot,
|
|
720
|
+
plugins: [...jayStackCompiler(fullJayOptions)],
|
|
721
|
+
build: {
|
|
722
|
+
outDir: outputDir,
|
|
723
|
+
emptyOutDir: false,
|
|
724
|
+
minify,
|
|
725
|
+
manifest: `${instanceId}-manifest.json`,
|
|
726
|
+
rollupOptions: {
|
|
727
|
+
input: { [instanceId]: hydrateEntryPath },
|
|
728
|
+
external: (id) => id.startsWith("@jay-framework/") || id === "jay-route-hydrate",
|
|
729
|
+
output: {
|
|
730
|
+
entryFileNames: "[name]-[hash].js",
|
|
731
|
+
chunkFileNames: "chunks/[name]-[hash].js",
|
|
732
|
+
assetFileNames: "[name]-[hash].[ext]",
|
|
733
|
+
format: "es"
|
|
734
|
+
},
|
|
735
|
+
preserveEntrySignatures: "exports-only"
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
logLevel: "warn"
|
|
739
|
+
});
|
|
740
|
+
const manifestPath = path.join(outputDir, `${instanceId}-manifest.json`);
|
|
741
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
742
|
+
await fs.rm(manifestPath, { force: true });
|
|
743
|
+
const entryKey = Object.keys(manifest).find((k) => manifest[k].isEntry);
|
|
744
|
+
if (!entryKey) {
|
|
745
|
+
throw new Error(`No entry found in instance build manifest for ${instanceId}`);
|
|
1027
746
|
}
|
|
1028
|
-
|
|
747
|
+
const entry = manifest[entryKey];
|
|
748
|
+
const result = {
|
|
749
|
+
jsFile: entry.file,
|
|
750
|
+
cssFile: entry.css?.[0]
|
|
751
|
+
};
|
|
752
|
+
logger.info(`[Build] Client bundle: ${result.jsFile}`);
|
|
753
|
+
return result;
|
|
1029
754
|
}
|
|
1030
755
|
function crossProductParams(parts) {
|
|
1031
756
|
if (parts.length === 0)
|
|
@@ -1049,8 +774,8 @@ function crossProductParams(parts) {
|
|
|
1049
774
|
const next = parts[i].values;
|
|
1050
775
|
const combined = [];
|
|
1051
776
|
for (const a of result) {
|
|
1052
|
-
for (const
|
|
1053
|
-
combined.push({ ...a, ...
|
|
777
|
+
for (const b2 of next) {
|
|
778
|
+
combined.push({ ...a, ...b2 });
|
|
1054
779
|
}
|
|
1055
780
|
}
|
|
1056
781
|
result = combined;
|
|
@@ -1389,6 +1114,108 @@ async function buildVersion(options) {
|
|
|
1389
1114
|
byRoute.set(info, []);
|
|
1390
1115
|
byRoute.get(info).push(materialized2.params);
|
|
1391
1116
|
}
|
|
1117
|
+
for (const [info] of byRoute) {
|
|
1118
|
+
const { route, entry } = info.routeEntry;
|
|
1119
|
+
if (!route.jayHtmlPath)
|
|
1120
|
+
continue;
|
|
1121
|
+
const routeDir = route.rawRoute.replace(/^\//, "") || "index";
|
|
1122
|
+
const frontendSafeRouteDir = routeDir.replace(/\[/g, "%5B").replace(/\]/g, "%5D");
|
|
1123
|
+
const backendRouteDir = path.join(backendDir, "pre-rendered", routeDir);
|
|
1124
|
+
const frontendRouteDir = path.join(frontendDir, "pages", frontendSafeRouteDir);
|
|
1125
|
+
await fs.mkdir(backendRouteDir, { recursive: true });
|
|
1126
|
+
await fs.mkdir(frontendRouteDir, { recursive: true });
|
|
1127
|
+
const serverElementPath = path.join(backendRouteDir, "route.server-element.js");
|
|
1128
|
+
try {
|
|
1129
|
+
const seResult = await compileRouteServerElement(
|
|
1130
|
+
route.jayHtmlPath,
|
|
1131
|
+
serverElementPath,
|
|
1132
|
+
options.projectRoot,
|
|
1133
|
+
options.tsConfigFilePath
|
|
1134
|
+
);
|
|
1135
|
+
entry.serverElementPath = path.relative(backendDir, serverElementPath);
|
|
1136
|
+
if (seResult.cssFile) {
|
|
1137
|
+
const srcCss = path.join(backendRouteDir, seResult.cssFile);
|
|
1138
|
+
const dstCss = path.join(frontendRouteDir, seResult.cssFile);
|
|
1139
|
+
try {
|
|
1140
|
+
await fs.rename(srcCss, dstCss);
|
|
1141
|
+
} catch {
|
|
1142
|
+
await fs.copyFile(srcCss, dstCss);
|
|
1143
|
+
await fs.rm(srcCss, { force: true });
|
|
1144
|
+
}
|
|
1145
|
+
entry.routeCssPath = path.relative(frontendDir, dstCss);
|
|
1146
|
+
}
|
|
1147
|
+
logger.important(`[Build] Route server element: ${routeDir}`);
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
logger.error(`[Build] Route server element FAILED ${route.rawRoute}: ${err.message}`);
|
|
1150
|
+
}
|
|
1151
|
+
try {
|
|
1152
|
+
const hydrateResult = await compileRouteHydrateScript(
|
|
1153
|
+
route.jayHtmlPath,
|
|
1154
|
+
frontendRouteDir,
|
|
1155
|
+
options.projectRoot,
|
|
1156
|
+
options.tsConfigFilePath,
|
|
1157
|
+
options.minify ?? true
|
|
1158
|
+
);
|
|
1159
|
+
entry.routeHydratePath = path.relative(
|
|
1160
|
+
frontendDir,
|
|
1161
|
+
path.join(frontendRouteDir, hydrateResult.jsFile)
|
|
1162
|
+
);
|
|
1163
|
+
logger.important(`[Build] Route hydrate script: ${routeDir}`);
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
logger.error(`[Build] Route hydrate script FAILED ${route.rawRoute}: ${err.message}`);
|
|
1166
|
+
continue;
|
|
1167
|
+
}
|
|
1168
|
+
try {
|
|
1169
|
+
const ROUTE_HYDRATE_KEY = "jay-route-hydrate";
|
|
1170
|
+
const exportName = route.componentExport || "page";
|
|
1171
|
+
let pageModulePath = "";
|
|
1172
|
+
if (route.compPath) {
|
|
1173
|
+
if (route.componentExport) {
|
|
1174
|
+
const pkgName = route.packageName || route.compPath;
|
|
1175
|
+
pageModulePath = `${pkgName}/client`;
|
|
1176
|
+
} else {
|
|
1177
|
+
pageModulePath = "./" + path.relative(frontendRouteDir, route.compPath);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const pageParts = await loadProductionPageParts(
|
|
1181
|
+
route,
|
|
1182
|
+
{},
|
|
1183
|
+
await fs.readFile(route.jayHtmlPath, "utf-8"),
|
|
1184
|
+
options.projectRoot,
|
|
1185
|
+
options.tsConfigFilePath,
|
|
1186
|
+
path.join(backendDir, "server")
|
|
1187
|
+
);
|
|
1188
|
+
entry.trackByMap = pageParts.serverTrackByMap || pageParts.clientTrackByMap;
|
|
1189
|
+
const entryPath = path.join(frontendRouteDir, "route.entry.ts");
|
|
1190
|
+
await generateRouteHydrationEntry({
|
|
1191
|
+
hydrateImport: ROUTE_HYDRATE_KEY,
|
|
1192
|
+
pageModulePath,
|
|
1193
|
+
pageExportName: exportName,
|
|
1194
|
+
trackByMap: pageParts.clientTrackByMap || {},
|
|
1195
|
+
outputPath: entryPath,
|
|
1196
|
+
keyedParts: pageParts.keyedPartModules,
|
|
1197
|
+
clientInits
|
|
1198
|
+
});
|
|
1199
|
+
const clientResult = await buildInstanceClient(
|
|
1200
|
+
entryPath,
|
|
1201
|
+
"route.client",
|
|
1202
|
+
frontendRouteDir,
|
|
1203
|
+
options.projectRoot,
|
|
1204
|
+
{ tsConfigFilePath: options.tsConfigFilePath },
|
|
1205
|
+
options.minify ?? true,
|
|
1206
|
+
options.pagesRoot,
|
|
1207
|
+
buildDir
|
|
1208
|
+
);
|
|
1209
|
+
await fs.rm(entryPath, { force: true });
|
|
1210
|
+
entry.routeClientBundlePath = path.relative(
|
|
1211
|
+
frontendDir,
|
|
1212
|
+
path.join(frontendRouteDir, clientResult.jsFile)
|
|
1213
|
+
);
|
|
1214
|
+
logger.important(`[Build] Route client bundle: ${routeDir}`);
|
|
1215
|
+
} catch (err) {
|
|
1216
|
+
logger.error(`[Build] Route client bundle FAILED ${route.rawRoute}: ${err.message}`);
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1392
1219
|
for (const [info, paramsList] of byRoute) {
|
|
1393
1220
|
const { route, entry } = info.routeEntry;
|
|
1394
1221
|
let pageModule;
|
|
@@ -1405,7 +1232,16 @@ async function buildVersion(options) {
|
|
|
1405
1232
|
}
|
|
1406
1233
|
for (const params of paramsList) {
|
|
1407
1234
|
try {
|
|
1408
|
-
const result = await buildInstance(
|
|
1235
|
+
const result = await buildInstance(
|
|
1236
|
+
route,
|
|
1237
|
+
params,
|
|
1238
|
+
pageModule,
|
|
1239
|
+
instanceCtx,
|
|
1240
|
+
entry.serverElementPath,
|
|
1241
|
+
entry.routeCssPath,
|
|
1242
|
+
entry.routeHydratePath,
|
|
1243
|
+
entry.routeClientBundlePath
|
|
1244
|
+
);
|
|
1409
1245
|
if (result.status === "success") {
|
|
1410
1246
|
entry.instances.push(result.instanceEntry);
|
|
1411
1247
|
if (result.contracts.length > 0 && !entry.contracts) {
|
|
@@ -1458,516 +1294,6 @@ async function buildVersion(options) {
|
|
|
1458
1294
|
logger.important(`[Build] Done! ${instanceCount} instances built in ${buildDir}`);
|
|
1459
1295
|
return manifest;
|
|
1460
1296
|
}
|
|
1461
|
-
const CACHE_TAG_START = '<script type="application/jay-cache">';
|
|
1462
|
-
const CACHE_TAG_END = "<\/script>";
|
|
1463
|
-
class FilesystemArtifactStore {
|
|
1464
|
-
constructor(basePath) {
|
|
1465
|
-
__publicField(this, "manifestCache");
|
|
1466
|
-
__publicField(this, "metadataMtime");
|
|
1467
|
-
__publicField(this, "moduleCache", /* @__PURE__ */ new Map());
|
|
1468
|
-
this.basePath = basePath;
|
|
1469
|
-
}
|
|
1470
|
-
async readManifest() {
|
|
1471
|
-
const metadataPath = path.join(this.basePath, "build-metadata.json");
|
|
1472
|
-
const manifestPath = path.join(this.basePath, "route-manifest.json");
|
|
1473
|
-
try {
|
|
1474
|
-
const metaStat = await fs.stat(metadataPath);
|
|
1475
|
-
if (this.manifestCache && this.metadataMtime === metaStat.mtimeMs) {
|
|
1476
|
-
return this.manifestCache.manifest;
|
|
1477
|
-
}
|
|
1478
|
-
this.metadataMtime = metaStat.mtimeMs;
|
|
1479
|
-
} catch {
|
|
1480
|
-
}
|
|
1481
|
-
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
1482
|
-
const manifestStat = await fs.stat(manifestPath);
|
|
1483
|
-
this.manifestCache = { manifest, mtime: manifestStat.mtimeMs };
|
|
1484
|
-
return manifest;
|
|
1485
|
-
}
|
|
1486
|
-
async readPreRenderedHtml(relativePath) {
|
|
1487
|
-
const fullPath = path.join(this.basePath, relativePath);
|
|
1488
|
-
const content = await fs.readFile(fullPath, "utf-8");
|
|
1489
|
-
const cachePath = fullPath.replace(/\.jay-html$/, ".cache.json");
|
|
1490
|
-
try {
|
|
1491
|
-
const cacheData = JSON.parse(await fs.readFile(cachePath, "utf-8"));
|
|
1492
|
-
return {
|
|
1493
|
-
content,
|
|
1494
|
-
slowViewState: cacheData.slowViewState || {},
|
|
1495
|
-
carryForward: cacheData.carryForward || {}
|
|
1496
|
-
};
|
|
1497
|
-
} catch {
|
|
1498
|
-
return extractCacheMetadata(content);
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
async loadServerElement(relativePath) {
|
|
1502
|
-
return this.loadModule(relativePath);
|
|
1503
|
-
}
|
|
1504
|
-
async loadPageModule(relativePath) {
|
|
1505
|
-
return this.loadModule(relativePath);
|
|
1506
|
-
}
|
|
1507
|
-
async readRawFile(relativePath) {
|
|
1508
|
-
const fullPath = path.join(this.basePath, relativePath);
|
|
1509
|
-
return await fs.readFile(fullPath, "utf-8");
|
|
1510
|
-
}
|
|
1511
|
-
getAssetPath(relativePath) {
|
|
1512
|
-
return path.join(this.basePath, relativePath);
|
|
1513
|
-
}
|
|
1514
|
-
getBuildDir() {
|
|
1515
|
-
return this.basePath;
|
|
1516
|
-
}
|
|
1517
|
-
async loadModule(relativePath) {
|
|
1518
|
-
const fullPath = path.join(this.basePath, relativePath);
|
|
1519
|
-
const stat = await fs.stat(fullPath);
|
|
1520
|
-
const cached = this.moduleCache.get(relativePath);
|
|
1521
|
-
if (cached && stat.mtimeMs === cached.mtime) {
|
|
1522
|
-
return cached.module;
|
|
1523
|
-
}
|
|
1524
|
-
const mod = await import(fullPath + "?t=" + stat.mtimeMs);
|
|
1525
|
-
this.moduleCache.set(relativePath, { module: mod, mtime: stat.mtimeMs });
|
|
1526
|
-
return mod;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
function extractCacheMetadata(fileContent) {
|
|
1530
|
-
const startIdx = fileContent.indexOf(CACHE_TAG_START);
|
|
1531
|
-
if (startIdx === -1) {
|
|
1532
|
-
return { content: fileContent, slowViewState: {}, carryForward: {} };
|
|
1533
|
-
}
|
|
1534
|
-
const jsonStart = startIdx + CACHE_TAG_START.length;
|
|
1535
|
-
const endIdx = fileContent.indexOf(CACHE_TAG_END, jsonStart);
|
|
1536
|
-
if (endIdx === -1) {
|
|
1537
|
-
return { content: fileContent, slowViewState: {}, carryForward: {} };
|
|
1538
|
-
}
|
|
1539
|
-
const jsonStr = fileContent.substring(jsonStart, endIdx);
|
|
1540
|
-
const metadata = JSON.parse(jsonStr);
|
|
1541
|
-
const tagEnd = endIdx + CACHE_TAG_END.length;
|
|
1542
|
-
const afterTag = fileContent[tagEnd] === "\n" ? tagEnd + 1 : tagEnd;
|
|
1543
|
-
const content = fileContent.substring(0, startIdx) + fileContent.substring(afterTag);
|
|
1544
|
-
return {
|
|
1545
|
-
content,
|
|
1546
|
-
slowViewState: metadata.slowViewState || {},
|
|
1547
|
-
carryForward: metadata.carryForward || {}
|
|
1548
|
-
};
|
|
1549
|
-
}
|
|
1550
|
-
function matchRequest(manifest, pathname) {
|
|
1551
|
-
const urlSegments = pathname.split("/").filter((s) => s.length > 0);
|
|
1552
|
-
for (const route of manifest.routes) {
|
|
1553
|
-
const params = matchSegments(route.segments, urlSegments);
|
|
1554
|
-
if (params) {
|
|
1555
|
-
const instance = findInstance(route, params);
|
|
1556
|
-
if (instance) {
|
|
1557
|
-
return { route, instance, params, pathname };
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
return void 0;
|
|
1562
|
-
}
|
|
1563
|
-
function matchSegments(routeSegments, urlSegments) {
|
|
1564
|
-
const params = {};
|
|
1565
|
-
let urlIdx = 0;
|
|
1566
|
-
for (let i = 0; i < routeSegments.length; i++) {
|
|
1567
|
-
const seg = routeSegments[i];
|
|
1568
|
-
if (seg.type === "static") {
|
|
1569
|
-
if (urlIdx >= urlSegments.length || urlSegments[urlIdx] !== seg.value) {
|
|
1570
|
-
return void 0;
|
|
1571
|
-
}
|
|
1572
|
-
urlIdx++;
|
|
1573
|
-
} else if (seg.type === "param") {
|
|
1574
|
-
if (urlIdx >= urlSegments.length)
|
|
1575
|
-
return void 0;
|
|
1576
|
-
params[seg.value] = urlSegments[urlIdx];
|
|
1577
|
-
urlIdx++;
|
|
1578
|
-
} else if (seg.type === "optional") {
|
|
1579
|
-
if (urlIdx < urlSegments.length) {
|
|
1580
|
-
params[seg.value] = urlSegments[urlIdx];
|
|
1581
|
-
urlIdx++;
|
|
1582
|
-
}
|
|
1583
|
-
} else if (seg.type === "catchAll") {
|
|
1584
|
-
if (urlIdx >= urlSegments.length)
|
|
1585
|
-
return void 0;
|
|
1586
|
-
params[seg.value] = urlSegments.slice(urlIdx).join("/");
|
|
1587
|
-
urlIdx = urlSegments.length;
|
|
1588
|
-
} else if (seg.type === "optionalCatchAll") {
|
|
1589
|
-
if (urlIdx < urlSegments.length) {
|
|
1590
|
-
params[seg.value] = urlSegments.slice(urlIdx).join("/");
|
|
1591
|
-
urlIdx = urlSegments.length;
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
if (urlIdx !== urlSegments.length)
|
|
1596
|
-
return void 0;
|
|
1597
|
-
return params;
|
|
1598
|
-
}
|
|
1599
|
-
function findInstance(route, params) {
|
|
1600
|
-
const paramNames = new Set(
|
|
1601
|
-
route.segments.filter((s) => s.type !== "static").map((s) => s.value)
|
|
1602
|
-
);
|
|
1603
|
-
return route.instances.find((instance) => {
|
|
1604
|
-
for (const name of paramNames) {
|
|
1605
|
-
const urlVal = params[name];
|
|
1606
|
-
const instVal = instance.params[name];
|
|
1607
|
-
if (urlVal === void 0 && instVal === void 0)
|
|
1608
|
-
continue;
|
|
1609
|
-
if (urlVal !== instVal)
|
|
1610
|
-
return false;
|
|
1611
|
-
}
|
|
1612
|
-
return true;
|
|
1613
|
-
});
|
|
1614
|
-
}
|
|
1615
|
-
function buildImportMap(sharedManifest, publicBasePath, sharedDir = "shared") {
|
|
1616
|
-
const imports = {};
|
|
1617
|
-
for (const [pkgName, hashedFile] of Object.entries(sharedManifest)) {
|
|
1618
|
-
imports[pkgName] = `${publicBasePath}${sharedDir}/${hashedFile}`;
|
|
1619
|
-
}
|
|
1620
|
-
return imports;
|
|
1621
|
-
}
|
|
1622
|
-
const pagePartsCache = /* @__PURE__ */ new Map();
|
|
1623
|
-
async function getPageParts(route, artifacts, preRenderedPath) {
|
|
1624
|
-
const cacheKey = route.pattern;
|
|
1625
|
-
const cached = pagePartsCache.get(cacheKey);
|
|
1626
|
-
if (cached)
|
|
1627
|
-
return cached;
|
|
1628
|
-
const routeDir = path.dirname(preRenderedPath);
|
|
1629
|
-
const configPath = artifacts.getAssetPath(path.join(routeDir, "page-parts.json"));
|
|
1630
|
-
const buildDir = artifacts.getBuildDir();
|
|
1631
|
-
const parts = await loadPagePartsFromConfig(configPath, buildDir);
|
|
1632
|
-
pagePartsCache.set(cacheKey, parts);
|
|
1633
|
-
return parts;
|
|
1634
|
-
}
|
|
1635
|
-
async function fetchPageRequest(match, manifest, requestUrl, artifacts, staticBaseUrl, cookies = {}) {
|
|
1636
|
-
const { route, instance } = match;
|
|
1637
|
-
const preRendered = await artifacts.readPreRenderedHtml(instance.preRenderedPath);
|
|
1638
|
-
const pageParts = await getPageParts(route, artifacts, instance.preRenderedPath);
|
|
1639
|
-
const query = Object.fromEntries(requestUrl.searchParams.entries());
|
|
1640
|
-
const fastResult = await renderFastChangingData(
|
|
1641
|
-
match.params,
|
|
1642
|
-
{ params: match.params, query },
|
|
1643
|
-
preRendered.carryForward,
|
|
1644
|
-
pageParts.parts,
|
|
1645
|
-
preRendered.carryForward.__instances,
|
|
1646
|
-
pageParts.forEachInstances,
|
|
1647
|
-
pageParts.headlessInstanceComponents,
|
|
1648
|
-
preRendered.slowViewState,
|
|
1649
|
-
query,
|
|
1650
|
-
cookies
|
|
1651
|
-
);
|
|
1652
|
-
if (fastResult.kind === "Redirect3xx") {
|
|
1653
|
-
return new Response(null, {
|
|
1654
|
-
status: fastResult.status,
|
|
1655
|
-
headers: { Location: fastResult.location }
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
if (fastResult.kind === "ServerError5xx" || fastResult.kind === "ClientError4xx") {
|
|
1659
|
-
return new Response(fastResult.message || "Error", {
|
|
1660
|
-
status: fastResult.status
|
|
1661
|
-
});
|
|
1662
|
-
}
|
|
1663
|
-
const fastViewState = fastResult.rendered || {};
|
|
1664
|
-
const fastCarryForward = fastResult.carryForward || {};
|
|
1665
|
-
const headTagSources = [];
|
|
1666
|
-
const slowHeadTags = preRendered.carryForward.__slowHeadTags;
|
|
1667
|
-
if (slowHeadTags)
|
|
1668
|
-
headTagSources.push(...slowHeadTags);
|
|
1669
|
-
const fastHeadTags = fastResult.headTags;
|
|
1670
|
-
if (fastHeadTags)
|
|
1671
|
-
headTagSources.push(fastHeadTags);
|
|
1672
|
-
const headTags = headTagSources.length > 0 ? mergeHeadTags(headTagSources) : [];
|
|
1673
|
-
const headTagsHtml = headTags.length > 0 ? serializeHeadTags(headTags) + "\n" : "";
|
|
1674
|
-
const fullViewState = deepMergeViewStates(
|
|
1675
|
-
preRendered.slowViewState,
|
|
1676
|
-
fastViewState,
|
|
1677
|
-
route.trackByMap || {}
|
|
1678
|
-
);
|
|
1679
|
-
const serverElement = await artifacts.loadServerElement(instance.serverElementPath);
|
|
1680
|
-
const asyncPromises = [];
|
|
1681
|
-
const importMap = buildImportMap(manifest.sharedManifest, staticBaseUrl);
|
|
1682
|
-
const modulePreloads = Object.values(importMap).map((url) => ` <link rel="modulepreload" href="${url}" />`).join("\n");
|
|
1683
|
-
const cssLink = instance.clientCssPath ? ` <link rel="stylesheet" href="${staticBaseUrl}${instance.clientCssPath}" />` : "";
|
|
1684
|
-
const encoder = new TextEncoder();
|
|
1685
|
-
const stream = new ReadableStream({
|
|
1686
|
-
async start(controller) {
|
|
1687
|
-
const write = (s) => controller.enqueue(encoder.encode(s));
|
|
1688
|
-
const headParts = [headTagsHtml, modulePreloads, cssLink].filter(Boolean).join("\n");
|
|
1689
|
-
write(`<!doctype html>
|
|
1690
|
-
<html lang="en">
|
|
1691
|
-
<head>
|
|
1692
|
-
<meta charset="UTF-8" />
|
|
1693
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1694
|
-
${headParts}
|
|
1695
|
-
<script type="importmap">${JSON.stringify({ imports: importMap })}<\/script>
|
|
1696
|
-
</head>
|
|
1697
|
-
<body>
|
|
1698
|
-
<div id="target">`);
|
|
1699
|
-
serverElement.renderToStream(fullViewState, {
|
|
1700
|
-
write: (chunk) => write(chunk),
|
|
1701
|
-
onAsync: (promise, id, templates) => {
|
|
1702
|
-
asyncPromises.push(
|
|
1703
|
-
promise.then(
|
|
1704
|
-
(val) => asyncSwapScript(id, templates.resolved(val)),
|
|
1705
|
-
(err) => asyncSwapScript(id, templates.rejected(err))
|
|
1706
|
-
)
|
|
1707
|
-
);
|
|
1708
|
-
}
|
|
1709
|
-
});
|
|
1710
|
-
write("</div>");
|
|
1711
|
-
const asyncScripts = (await Promise.all(asyncPromises)).filter((s) => s).join("");
|
|
1712
|
-
if (asyncScripts)
|
|
1713
|
-
write(asyncScripts);
|
|
1714
|
-
const clientInitData = getClientInitData();
|
|
1715
|
-
const clientBundleUrl = `${staticBaseUrl}${instance.clientBundlePath}`;
|
|
1716
|
-
write(`
|
|
1717
|
-
<script type="module">
|
|
1718
|
-
import { init } from '${clientBundleUrl}';
|
|
1719
|
-
await init(${JSON.stringify(fastViewState)}, ${JSON.stringify(fastCarryForward)}, ${JSON.stringify(clientInitData)});
|
|
1720
|
-
<\/script>
|
|
1721
|
-
</body>
|
|
1722
|
-
</html>`);
|
|
1723
|
-
controller.close();
|
|
1724
|
-
}
|
|
1725
|
-
});
|
|
1726
|
-
const responseHeaders = fastResult.responseHeaders || {};
|
|
1727
|
-
return new Response(stream, {
|
|
1728
|
-
headers: { "Content-Type": "text/html; charset=utf-8", ...responseHeaders }
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
const ACTION_PREFIX = "/_jay/actions/";
|
|
1732
|
-
function isActionRequest(pathname) {
|
|
1733
|
-
return pathname.startsWith(ACTION_PREFIX);
|
|
1734
|
-
}
|
|
1735
|
-
async function fetchActionRequest(request, registry = actionRegistry) {
|
|
1736
|
-
const url = new URL(request.url);
|
|
1737
|
-
const actionName = url.pathname.slice(ACTION_PREFIX.length);
|
|
1738
|
-
if (!actionName) {
|
|
1739
|
-
return jsonResponse(400, {
|
|
1740
|
-
success: false,
|
|
1741
|
-
error: {
|
|
1742
|
-
code: "MISSING_ACTION_NAME",
|
|
1743
|
-
message: "Action name is required",
|
|
1744
|
-
isActionError: false
|
|
1745
|
-
}
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1748
|
-
const action = registry.get(actionName);
|
|
1749
|
-
if (!action) {
|
|
1750
|
-
return jsonResponse(404, {
|
|
1751
|
-
success: false,
|
|
1752
|
-
error: {
|
|
1753
|
-
code: "ACTION_NOT_FOUND",
|
|
1754
|
-
message: `Action '${actionName}' is not registered`,
|
|
1755
|
-
isActionError: false
|
|
1756
|
-
}
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
const requestMethod = request.method.toUpperCase();
|
|
1760
|
-
if (requestMethod !== action.method) {
|
|
1761
|
-
return jsonResponse(405, {
|
|
1762
|
-
success: false,
|
|
1763
|
-
error: {
|
|
1764
|
-
code: "METHOD_NOT_ALLOWED",
|
|
1765
|
-
message: `Action '${actionName}' expects ${action.method}, got ${requestMethod}`,
|
|
1766
|
-
isActionError: false
|
|
1767
|
-
}
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
let input;
|
|
1771
|
-
try {
|
|
1772
|
-
if (requestMethod === "GET") {
|
|
1773
|
-
const inputParam = url.searchParams.get("_input");
|
|
1774
|
-
if (inputParam) {
|
|
1775
|
-
input = JSON.parse(inputParam);
|
|
1776
|
-
} else {
|
|
1777
|
-
input = Object.fromEntries(url.searchParams.entries());
|
|
1778
|
-
delete input._input;
|
|
1779
|
-
}
|
|
1780
|
-
} else {
|
|
1781
|
-
const text = await request.text();
|
|
1782
|
-
input = text ? JSON.parse(text) : {};
|
|
1783
|
-
}
|
|
1784
|
-
} catch {
|
|
1785
|
-
return jsonResponse(400, {
|
|
1786
|
-
success: false,
|
|
1787
|
-
error: {
|
|
1788
|
-
code: "INVALID_INPUT",
|
|
1789
|
-
message: "Failed to parse request input",
|
|
1790
|
-
isActionError: false
|
|
1791
|
-
}
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
if (registry.isStreaming(actionName)) {
|
|
1795
|
-
const encoder = new TextEncoder();
|
|
1796
|
-
const stream = new ReadableStream({
|
|
1797
|
-
async start(controller) {
|
|
1798
|
-
try {
|
|
1799
|
-
const generator = registry.executeStream(actionName, input);
|
|
1800
|
-
for await (const chunk of generator) {
|
|
1801
|
-
controller.enqueue(encoder.encode(JSON.stringify({ chunk }) + "\n"));
|
|
1802
|
-
}
|
|
1803
|
-
controller.enqueue(encoder.encode(JSON.stringify({ done: true }) + "\n"));
|
|
1804
|
-
} catch (err) {
|
|
1805
|
-
controller.enqueue(
|
|
1806
|
-
encoder.encode(JSON.stringify({ error: err.message }) + "\n")
|
|
1807
|
-
);
|
|
1808
|
-
}
|
|
1809
|
-
controller.close();
|
|
1810
|
-
}
|
|
1811
|
-
});
|
|
1812
|
-
return new Response(stream, {
|
|
1813
|
-
headers: { "Content-Type": "application/x-ndjson" }
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
const result = await registry.execute(actionName, input);
|
|
1817
|
-
if (result.success) {
|
|
1818
|
-
const headers = {};
|
|
1819
|
-
if (requestMethod === "GET") {
|
|
1820
|
-
const cacheHeaders = registry.getCacheHeaders(actionName);
|
|
1821
|
-
if (cacheHeaders) {
|
|
1822
|
-
headers["Cache-Control"] = cacheHeaders;
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
return jsonResponse(200, { success: true, data: result.data }, headers);
|
|
1826
|
-
} else {
|
|
1827
|
-
const statusCode = getStatusCode(result.error.code, result.error.isActionError);
|
|
1828
|
-
return jsonResponse(statusCode, { success: false, error: result.error });
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
function jsonResponse(status, body, extraHeaders = {}) {
|
|
1832
|
-
return new Response(JSON.stringify(body), {
|
|
1833
|
-
status,
|
|
1834
|
-
headers: { "Content-Type": "application/json", ...extraHeaders }
|
|
1835
|
-
});
|
|
1836
|
-
}
|
|
1837
|
-
async function registerActionsFromManifest(actions, buildDir, registry = actionRegistry) {
|
|
1838
|
-
const logger = getLogger();
|
|
1839
|
-
let count = 0;
|
|
1840
|
-
for (const entry of actions) {
|
|
1841
|
-
try {
|
|
1842
|
-
const modulePath = entry.isPlugin ? entry.packageName : `${buildDir}/${entry.serverModule}`;
|
|
1843
|
-
const mod = await import(modulePath);
|
|
1844
|
-
for (const [, exported] of Object.entries(mod)) {
|
|
1845
|
-
if (isJayAction(exported)) {
|
|
1846
|
-
registry.register(exported);
|
|
1847
|
-
count++;
|
|
1848
|
-
} else if (isJayStreamAction(exported)) {
|
|
1849
|
-
registry.registerStream(exported);
|
|
1850
|
-
count++;
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
} catch (err) {
|
|
1854
|
-
logger.error(
|
|
1855
|
-
`[Server] Failed to load action module ${entry.serverModule}: ${err.message}`
|
|
1856
|
-
);
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
logger.info(`[Server] Registered ${count} actions`);
|
|
1860
|
-
}
|
|
1861
|
-
function getStatusCode(code, isActionError) {
|
|
1862
|
-
if (isActionError)
|
|
1863
|
-
return 422;
|
|
1864
|
-
switch (code) {
|
|
1865
|
-
case "ACTION_NOT_FOUND":
|
|
1866
|
-
return 404;
|
|
1867
|
-
case "INVALID_INPUT":
|
|
1868
|
-
case "VALIDATION_ERROR":
|
|
1869
|
-
return 400;
|
|
1870
|
-
case "UNAUTHORIZED":
|
|
1871
|
-
return 401;
|
|
1872
|
-
case "FORBIDDEN":
|
|
1873
|
-
return 403;
|
|
1874
|
-
default:
|
|
1875
|
-
return 500;
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
const MIME_TYPES = {
|
|
1879
|
-
".js": "application/javascript",
|
|
1880
|
-
".css": "text/css",
|
|
1881
|
-
".json": "application/json",
|
|
1882
|
-
".html": "text/html",
|
|
1883
|
-
".svg": "image/svg+xml",
|
|
1884
|
-
".png": "image/png",
|
|
1885
|
-
".jpg": "image/jpeg",
|
|
1886
|
-
".jpeg": "image/jpeg",
|
|
1887
|
-
".gif": "image/gif",
|
|
1888
|
-
".ico": "image/x-icon",
|
|
1889
|
-
".woff2": "font/woff2",
|
|
1890
|
-
".woff": "font/woff",
|
|
1891
|
-
".ttf": "font/ttf",
|
|
1892
|
-
".webp": "image/webp"
|
|
1893
|
-
};
|
|
1894
|
-
async function fetchStaticFile(pathname, frontendDir) {
|
|
1895
|
-
const normalizedBase = path.resolve(frontendDir);
|
|
1896
|
-
for (const candidate of [
|
|
1897
|
-
path.join(frontendDir, pathname),
|
|
1898
|
-
path.join(frontendDir, "public", pathname)
|
|
1899
|
-
]) {
|
|
1900
|
-
const normalizedFile = path.resolve(candidate);
|
|
1901
|
-
if (!normalizedFile.startsWith(normalizedBase))
|
|
1902
|
-
continue;
|
|
1903
|
-
try {
|
|
1904
|
-
const content = await fs.readFile(candidate);
|
|
1905
|
-
const ext = path.extname(candidate);
|
|
1906
|
-
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
1907
|
-
const isHashed = /[-][a-zA-Z0-9_-]{6,}\./.test(path.basename(candidate));
|
|
1908
|
-
const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "public, max-age=3600";
|
|
1909
|
-
return new Response(content, {
|
|
1910
|
-
headers: {
|
|
1911
|
-
"Content-Type": contentType,
|
|
1912
|
-
"Content-Length": String(content.length),
|
|
1913
|
-
"Cache-Control": cacheControl
|
|
1914
|
-
}
|
|
1915
|
-
});
|
|
1916
|
-
} catch {
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
return null;
|
|
1920
|
-
}
|
|
1921
|
-
async function initializeServices(buildDir, projectRoot, label) {
|
|
1922
|
-
const logger = getLogger();
|
|
1923
|
-
const { discoverPluginsWithInit, sortPluginsByDependencies } = await import("@jay-framework/stack-server-runtime");
|
|
1924
|
-
try {
|
|
1925
|
-
const pluginsWithInit = sortPluginsByDependencies(
|
|
1926
|
-
await discoverPluginsWithInit({ projectRoot })
|
|
1927
|
-
);
|
|
1928
|
-
for (const pluginInit of pluginsWithInit) {
|
|
1929
|
-
try {
|
|
1930
|
-
let modulePath;
|
|
1931
|
-
if (pluginInit.isLocal) {
|
|
1932
|
-
const pluginDirName = path.basename(pluginInit.pluginPath);
|
|
1933
|
-
const initModule = pluginInit.initModule || "index";
|
|
1934
|
-
modulePath = path.join(
|
|
1935
|
-
buildDir,
|
|
1936
|
-
"server",
|
|
1937
|
-
"plugins",
|
|
1938
|
-
pluginDirName,
|
|
1939
|
-
initModule + ".js"
|
|
1940
|
-
);
|
|
1941
|
-
} else {
|
|
1942
|
-
modulePath = pluginInit.packageName;
|
|
1943
|
-
}
|
|
1944
|
-
const pluginModule = await import(modulePath);
|
|
1945
|
-
const init = pluginModule.init || pluginModule[pluginInit.initExport || "init"];
|
|
1946
|
-
if (init?._serverInit) {
|
|
1947
|
-
logger.info(`[${label}] Running plugin init: ${pluginInit.name}`);
|
|
1948
|
-
const data = await init._serverInit();
|
|
1949
|
-
if (data)
|
|
1950
|
-
setClientInitData(pluginInit.name, data);
|
|
1951
|
-
}
|
|
1952
|
-
} catch (err) {
|
|
1953
|
-
logger.warn(`[${label}] Plugin init failed: ${pluginInit.name}: ${err.message}`);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
} catch {
|
|
1957
|
-
}
|
|
1958
|
-
const initModulePath = path.join(buildDir, "server", "init.js");
|
|
1959
|
-
try {
|
|
1960
|
-
const initModule = await import(initModulePath);
|
|
1961
|
-
const init = initModule.init || initModule.default;
|
|
1962
|
-
if (init?._serverInit) {
|
|
1963
|
-
logger.info(`[${label}] Running server init...`);
|
|
1964
|
-
const data = await init._serverInit();
|
|
1965
|
-
if (data)
|
|
1966
|
-
setClientInitData("project", data);
|
|
1967
|
-
}
|
|
1968
|
-
} catch {
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
1297
|
function toFetchRequest(req) {
|
|
1972
1298
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1973
1299
|
const headers = new Headers();
|
|
@@ -2397,14 +1723,9 @@ function paramsMatch(instanceParams, targetParams) {
|
|
|
2397
1723
|
return Object.entries(targetParams).every(([key, value]) => instanceParams[key] === value);
|
|
2398
1724
|
}
|
|
2399
1725
|
function collectInstanceFiles(instance) {
|
|
2400
|
-
if (!instance.
|
|
1726
|
+
if (!instance.cachePath)
|
|
2401
1727
|
return [];
|
|
2402
|
-
const files = [
|
|
2403
|
-
instance.preRenderedPath,
|
|
2404
|
-
instance.preRenderedPath.replace(".jay-html", ".cache.json"),
|
|
2405
|
-
instance.serverElementPath,
|
|
2406
|
-
instance.clientBundlePath
|
|
2407
|
-
];
|
|
1728
|
+
const files = [instance.cachePath, instance.serverElementPath, instance.clientBundlePath];
|
|
2408
1729
|
if (instance.clientCssPath)
|
|
2409
1730
|
files.push(instance.clientCssPath);
|
|
2410
1731
|
return files.filter(Boolean);
|
|
@@ -2579,11 +1900,13 @@ export {
|
|
|
2579
1900
|
fetchPageRequest,
|
|
2580
1901
|
fetchStaticFile,
|
|
2581
1902
|
initializeServices,
|
|
1903
|
+
e as initializeServicesFromModules,
|
|
2582
1904
|
isActionRequest,
|
|
2583
1905
|
matchRequest,
|
|
2584
1906
|
rebuild,
|
|
2585
1907
|
rebuildContract,
|
|
2586
1908
|
registerActionsFromManifest,
|
|
1909
|
+
b as registerActionsFromModules,
|
|
2587
1910
|
resolveContractToRoutes,
|
|
2588
1911
|
startMainServer,
|
|
2589
1912
|
startRendererServer
|