@jay-framework/production-server 0.17.4 → 0.18.1
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 +478 -1130
- package/dist/init-services-F6Svgfm9.js +799 -0
- package/dist/serve-index-GLq2Yw4z.d.ts +181 -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,21 @@ 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-F6Svgfm9.js";
|
|
10
|
+
import { e, b } from "./init-services-F6Svgfm9.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";
|
|
17
|
+
import { transform } from "esbuild";
|
|
20
18
|
import http from "node:http";
|
|
21
19
|
import { Readable } from "node:stream";
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import
|
|
20
|
+
import { isJayWebhook } from "@jay-framework/fullstack-component";
|
|
21
|
+
import "@jay-framework/view-state-merge";
|
|
22
|
+
import "@jay-framework/ssr-runtime";
|
|
25
23
|
async function discoverServerEntries(projectRoot, pagesRoot) {
|
|
26
24
|
const logger = getLogger();
|
|
27
25
|
const routes = await scanRoutes(pagesRoot, {
|
|
@@ -209,384 +207,6 @@ async function parseViteManifest(outputDir, packages) {
|
|
|
209
207
|
await fs.rm(viteManifestPath, { force: true });
|
|
210
208
|
return manifest;
|
|
211
209
|
}
|
|
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
210
|
function hashParams(params, suffix) {
|
|
591
211
|
const sorted = Object.keys(params).sort().reduce(
|
|
592
212
|
(acc, key) => {
|
|
@@ -601,7 +221,7 @@ function hashParams(params, suffix) {
|
|
|
601
221
|
const input = suffix ? json + ":" + suffix : json;
|
|
602
222
|
return "_" + crypto.createHash("md5").update(input).digest("hex").substring(0, 8);
|
|
603
223
|
}
|
|
604
|
-
async function buildInstance(route, params, pageModule, ctx) {
|
|
224
|
+
async function buildInstance(route, params, pageModule, ctx, routeServerElementPath, routeCssPath, routeHydratePath, routeClientBundlePath) {
|
|
605
225
|
const logger = getLogger();
|
|
606
226
|
const routeDir = route.rawRoute.replace(/^\//, "") || "index";
|
|
607
227
|
const paramHash = hashParams(params, ctx.rebuildSuffix);
|
|
@@ -611,7 +231,7 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
611
231
|
await fs.mkdir(backendInstanceDir, { recursive: true });
|
|
612
232
|
await fs.mkdir(frontendInstanceDir, { recursive: true });
|
|
613
233
|
const jayHtmlContent = await fs.readFile(route.jayHtmlPath, "utf-8");
|
|
614
|
-
|
|
234
|
+
path.dirname(route.jayHtmlPath);
|
|
615
235
|
const serverBuildDir = path.join(ctx.backendDir, "server");
|
|
616
236
|
const pageParts = await loadProductionPageParts(
|
|
617
237
|
route,
|
|
@@ -636,7 +256,7 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
636
256
|
let pageIsPlugin = false;
|
|
637
257
|
if (route.compPath) {
|
|
638
258
|
if (route.componentExport) {
|
|
639
|
-
pageServerModule = route.compPath;
|
|
259
|
+
pageServerModule = route.packageName || route.compPath;
|
|
640
260
|
pageIsPlugin = true;
|
|
641
261
|
} else {
|
|
642
262
|
const relativePath = path.relative(ctx.projectRoot, route.compPath);
|
|
@@ -675,197 +295,57 @@ async function buildInstance(route, params, pageModule, ctx) {
|
|
|
675
295
|
}
|
|
676
296
|
const slowViewState = slowResult.rendered;
|
|
677
297
|
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(", ")}`
|
|
298
|
+
if (pageParts.discoveredInstances.length > 0 && pageParts.headlessInstanceComponents.length > 0) {
|
|
299
|
+
const slowResult2 = await slowRenderInstances(
|
|
300
|
+
pageParts.discoveredInstances,
|
|
301
|
+
pageParts.headlessInstanceComponents
|
|
703
302
|
);
|
|
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;
|
|
303
|
+
if (slowResult2) {
|
|
304
|
+
const existingInstances = carryForward.__instances || {
|
|
305
|
+
discovered: [],
|
|
306
|
+
carryForwards: {}
|
|
307
|
+
};
|
|
308
|
+
carryForward.__instances = {
|
|
309
|
+
discovered: [
|
|
310
|
+
...existingInstances.discovered,
|
|
311
|
+
...slowResult2.instancePhaseData.discovered
|
|
312
|
+
],
|
|
313
|
+
carryForwards: {
|
|
314
|
+
...existingInstances.carryForwards,
|
|
315
|
+
...slowResult2.instancePhaseData.carryForwards
|
|
316
|
+
},
|
|
317
|
+
slowViewStates: {
|
|
318
|
+
...existingInstances.slowViewStates || {},
|
|
319
|
+
...slowResult2.instancePhaseData.slowViewStates
|
|
758
320
|
}
|
|
759
|
-
}
|
|
321
|
+
};
|
|
760
322
|
}
|
|
761
323
|
}
|
|
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`);
|
|
324
|
+
if (pageParts.forEachInstances.length > 0) {
|
|
325
|
+
const existingInstances = carryForward.__instances || {
|
|
326
|
+
discovered: [],
|
|
327
|
+
carryForwards: {}
|
|
328
|
+
};
|
|
329
|
+
existingInstances.forEachInstances = pageParts.forEachInstances;
|
|
330
|
+
carryForward.__instances = existingInstances;
|
|
331
|
+
}
|
|
332
|
+
const cachePath = path.join(backendInstanceDir, `${instanceId}.cache.json`);
|
|
789
333
|
await fs.writeFile(
|
|
790
|
-
|
|
334
|
+
cachePath,
|
|
791
335
|
JSON.stringify({
|
|
792
336
|
slowViewState,
|
|
793
337
|
carryForward
|
|
794
338
|
}),
|
|
795
339
|
"utf-8"
|
|
796
340
|
);
|
|
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
|
-
}
|
|
341
|
+
logger.info(`[Build] Instance data: ${routeDir}/${instanceId}`);
|
|
342
|
+
const serverElementPath = routeServerElementPath ? path.join(ctx.backendDir, routeServerElementPath) : path.join(backendInstanceDir, `${instanceId}.server-element.js`);
|
|
860
343
|
const instanceEntry = {
|
|
861
344
|
params,
|
|
862
|
-
|
|
345
|
+
cachePath: path.relative(ctx.backendDir, cachePath),
|
|
863
346
|
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
|
|
347
|
+
clientBundlePath: routeClientBundlePath || "",
|
|
348
|
+
clientCssPath: routeCssPath
|
|
869
349
|
};
|
|
870
350
|
return { status: "success", instanceEntry, slowViewState, carryForward, contracts };
|
|
871
351
|
}
|
|
@@ -979,53 +459,319 @@ async function scanPluginRoutes(projectRoot, projectRoutes) {
|
|
|
979
459
|
rawRoute: route.path,
|
|
980
460
|
jayHtmlPath,
|
|
981
461
|
compPath,
|
|
982
|
-
componentExport
|
|
462
|
+
componentExport,
|
|
463
|
+
packageName: plugin.packageName
|
|
983
464
|
});
|
|
984
465
|
logger.info(`[Routes] Plugin "${plugin.manifest.name}" provides route ${route.path}`);
|
|
985
466
|
}
|
|
986
467
|
}
|
|
987
468
|
return pluginRoutes;
|
|
988
469
|
}
|
|
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;
|
|
470
|
+
function resolvePluginExport(pluginPath, exportSubpath) {
|
|
471
|
+
const normalized = exportSubpath.replace(/^\.\//, "");
|
|
472
|
+
const packageJsonPath = path.join(pluginPath, "package.json");
|
|
473
|
+
try {
|
|
474
|
+
const packageJson = JSON.parse(fs$1.readFileSync(packageJsonPath, "utf-8"));
|
|
475
|
+
if (packageJson.exports) {
|
|
476
|
+
const exportKey = "./" + normalized;
|
|
477
|
+
const exportValue = packageJson.exports[exportKey];
|
|
478
|
+
if (exportValue) {
|
|
479
|
+
const resolved = typeof exportValue === "string" ? exportValue : exportValue.default || exportValue.import || exportValue.require;
|
|
480
|
+
if (resolved)
|
|
481
|
+
return path.join(pluginPath, resolved);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
for (const dir of ["dist", "lib", ""]) {
|
|
487
|
+
const candidate = path.join(pluginPath, dir, normalized);
|
|
488
|
+
try {
|
|
489
|
+
fs$1.accessSync(candidate);
|
|
490
|
+
return candidate;
|
|
491
|
+
} catch {
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
function resolvePluginModule(pluginPath) {
|
|
497
|
+
const pkgJsonPath = path.join(pluginPath, "package.json");
|
|
498
|
+
try {
|
|
499
|
+
const pkg = JSON.parse(fs$1.readFileSync(pkgJsonPath, "utf-8"));
|
|
500
|
+
const mainExport = pkg.exports?.["."];
|
|
501
|
+
const mainPath = typeof mainExport === "string" ? mainExport : mainExport?.default || mainExport?.import || pkg.main;
|
|
502
|
+
if (mainPath) {
|
|
503
|
+
const resolved = path.join(pluginPath, mainPath);
|
|
504
|
+
if (fs$1.existsSync(resolved))
|
|
505
|
+
return resolved;
|
|
506
|
+
}
|
|
507
|
+
} catch {
|
|
508
|
+
}
|
|
509
|
+
return path.join(pluginPath, "dist", "index.js");
|
|
510
|
+
}
|
|
511
|
+
async function compileServerElement(jayHtmlContent, jayHtmlFilename, jayHtmlDir, outputPath, projectRoot, tsConfigFilePath, sourceDir, minifyCss = true) {
|
|
512
|
+
const jayFile = await parseJayFile(
|
|
513
|
+
jayHtmlContent,
|
|
514
|
+
jayHtmlFilename,
|
|
515
|
+
jayHtmlDir,
|
|
516
|
+
{ relativePath: tsConfigFilePath },
|
|
517
|
+
JAY_IMPORT_RESOLVER,
|
|
518
|
+
projectRoot,
|
|
519
|
+
sourceDir
|
|
520
|
+
);
|
|
521
|
+
const parsedJayFile = checkValidationErrors(jayFile);
|
|
522
|
+
const serverElementCode = checkValidationErrors(generateServerElementFile(parsedJayFile));
|
|
523
|
+
const outputDir = path.dirname(outputPath);
|
|
524
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
525
|
+
const tsPath = outputPath.replace(/\.js$/, ".ts");
|
|
526
|
+
await fs.writeFile(tsPath, serverElementCode, "utf-8");
|
|
527
|
+
const jayOptions = { tsConfigFilePath };
|
|
528
|
+
await build({
|
|
529
|
+
root: projectRoot,
|
|
530
|
+
plugins: [jayRuntime(jayOptions)],
|
|
531
|
+
build: {
|
|
532
|
+
outDir: outputDir,
|
|
533
|
+
emptyOutDir: false,
|
|
534
|
+
minify: false,
|
|
535
|
+
ssr: true,
|
|
536
|
+
rollupOptions: {
|
|
537
|
+
input: { [path.basename(outputPath, ".js")]: tsPath },
|
|
538
|
+
external: [/^node:/, /^@jay-framework\//],
|
|
539
|
+
output: {
|
|
540
|
+
entryFileNames: "[name].js",
|
|
541
|
+
format: "es"
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
logLevel: "warn"
|
|
546
|
+
});
|
|
547
|
+
await fs.rm(tsPath, { force: true });
|
|
548
|
+
let cssFile;
|
|
549
|
+
let cssImports;
|
|
550
|
+
const css = parsedJayFile.css;
|
|
551
|
+
if (css) {
|
|
552
|
+
cssImports = extractCssImportUrls(css);
|
|
553
|
+
const cssFilename = path.basename(outputPath, ".server-element.js") + ".css";
|
|
554
|
+
const cssPath = path.join(outputDir, cssFilename);
|
|
555
|
+
if (minifyCss) {
|
|
556
|
+
const minified = await transform(css, { loader: "css", minify: true });
|
|
557
|
+
await fs.writeFile(cssPath, minified.code, "utf-8");
|
|
558
|
+
} else {
|
|
559
|
+
await fs.writeFile(cssPath, css, "utf-8");
|
|
560
|
+
}
|
|
561
|
+
cssFile = cssFilename;
|
|
562
|
+
}
|
|
563
|
+
getLogger().info(`[Build] Compiled server element: ${path.basename(outputPath)}`);
|
|
564
|
+
return { cssFile, cssImports };
|
|
565
|
+
}
|
|
566
|
+
async function compileRouteServerElement(jayHtmlPath, outputPath, projectRoot, tsConfigFilePath, minifyCss = true) {
|
|
567
|
+
const jayHtmlContent = await fs.readFile(jayHtmlPath, "utf-8");
|
|
568
|
+
const sourceDir = path.dirname(jayHtmlPath);
|
|
569
|
+
const outputDir = path.dirname(outputPath);
|
|
570
|
+
let jayHtml = injectHeadfullFSTemplates(jayHtmlContent, sourceDir, JAY_IMPORT_RESOLVER);
|
|
571
|
+
jayHtml = resolveJayHtmlPaths(jayHtml, sourceDir, outputDir);
|
|
572
|
+
return compileServerElement(
|
|
573
|
+
jayHtml,
|
|
574
|
+
path.basename(jayHtmlPath),
|
|
575
|
+
outputDir,
|
|
576
|
+
outputPath,
|
|
577
|
+
projectRoot,
|
|
578
|
+
tsConfigFilePath,
|
|
579
|
+
sourceDir,
|
|
580
|
+
minifyCss
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
async function compileRouteHydrateScript(jayHtmlPath, outputDir, projectRoot, tsConfigFilePath, minify = true) {
|
|
584
|
+
const jayHtmlContent = await fs.readFile(jayHtmlPath, "utf-8");
|
|
585
|
+
const sourceDir = path.dirname(jayHtmlPath);
|
|
586
|
+
let jayHtml = injectHeadfullFSTemplates(jayHtmlContent, sourceDir, JAY_IMPORT_RESOLVER);
|
|
587
|
+
jayHtml = resolveJayHtmlPaths(jayHtml, sourceDir, outputDir);
|
|
588
|
+
const jayFile = await parseJayFile(
|
|
589
|
+
jayHtml,
|
|
590
|
+
path.basename(jayHtmlPath),
|
|
591
|
+
outputDir,
|
|
592
|
+
{ relativePath: tsConfigFilePath },
|
|
593
|
+
JAY_IMPORT_RESOLVER,
|
|
594
|
+
projectRoot,
|
|
595
|
+
sourceDir
|
|
596
|
+
);
|
|
597
|
+
const parsedJayFile = checkValidationErrors(jayFile);
|
|
598
|
+
const hydrateCode = checkValidationErrors(
|
|
599
|
+
generateElementHydrateFile(parsedJayFile, RuntimeMode.MainTrusted)
|
|
600
|
+
);
|
|
601
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
602
|
+
const tsPath = path.join(outputDir, "route.hydrate.ts");
|
|
603
|
+
await fs.writeFile(tsPath, hydrateCode, "utf-8");
|
|
604
|
+
const jayOptions = { tsConfigFilePath };
|
|
605
|
+
await build({
|
|
606
|
+
root: projectRoot,
|
|
607
|
+
plugins: [...jayStackCompiler(jayOptions)],
|
|
608
|
+
build: {
|
|
609
|
+
outDir: outputDir,
|
|
610
|
+
emptyOutDir: false,
|
|
611
|
+
minify,
|
|
612
|
+
manifest: "route-hydrate-manifest.json",
|
|
613
|
+
rollupOptions: {
|
|
614
|
+
input: { "route.hydrate": tsPath },
|
|
615
|
+
external: (id) => id.startsWith("@jay-framework/"),
|
|
616
|
+
output: {
|
|
617
|
+
entryFileNames: "[name]-[hash].js",
|
|
618
|
+
format: "es"
|
|
619
|
+
},
|
|
620
|
+
preserveEntrySignatures: "exports-only"
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
logLevel: "warn"
|
|
624
|
+
});
|
|
625
|
+
await fs.rm(tsPath, { force: true });
|
|
626
|
+
const manifestPath = path.join(outputDir, "route-hydrate-manifest.json");
|
|
627
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
628
|
+
await fs.rm(manifestPath, { force: true });
|
|
629
|
+
const entryKey = Object.keys(manifest).find((k) => manifest[k].isEntry);
|
|
630
|
+
if (!entryKey)
|
|
631
|
+
throw new Error("No entry in route hydrate manifest");
|
|
632
|
+
const jsFile = manifest[entryKey].file;
|
|
633
|
+
getLogger().info(`[Build] Compiled route hydrate script: ${jsFile}`);
|
|
634
|
+
return { jsFile };
|
|
635
|
+
}
|
|
636
|
+
function resolveJayHtmlPaths(html, sourceDir, targetDir) {
|
|
637
|
+
const root = parse(html, {
|
|
638
|
+
comment: true,
|
|
639
|
+
blockTextElements: { script: true, style: true }
|
|
640
|
+
});
|
|
641
|
+
const rewrite = (el, attr) => {
|
|
642
|
+
const val = el.getAttribute(attr);
|
|
643
|
+
if (val && (val.startsWith("./") || val.startsWith("../"))) {
|
|
644
|
+
const abs = path.resolve(sourceDir, val);
|
|
645
|
+
let rel = path.relative(targetDir, abs);
|
|
646
|
+
if (!rel.startsWith("."))
|
|
647
|
+
rel = "./" + rel;
|
|
648
|
+
el.setAttribute(attr, rel);
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
for (const el of root.querySelectorAll('script[type="application/jay-data"]')) {
|
|
652
|
+
rewrite(el, "contract");
|
|
653
|
+
}
|
|
654
|
+
for (const el of root.querySelectorAll('script[type="application/jay-headless"]')) {
|
|
655
|
+
rewrite(el, "src");
|
|
656
|
+
rewrite(el, "contract");
|
|
657
|
+
}
|
|
658
|
+
for (const el of root.querySelectorAll('script[type="application/jay-headfull"]')) {
|
|
659
|
+
rewrite(el, "src");
|
|
660
|
+
rewrite(el, "contract");
|
|
661
|
+
}
|
|
662
|
+
for (const el of root.querySelectorAll('link[rel="stylesheet"]')) {
|
|
663
|
+
rewrite(el, "href");
|
|
664
|
+
}
|
|
665
|
+
return root.toString();
|
|
666
|
+
}
|
|
667
|
+
function extractCssImportUrls(css) {
|
|
668
|
+
const imports = [];
|
|
669
|
+
const re = /@import\s*(?:url\(\s*['"]?([^'")]+)['"]?\s*\)|['"]([^'"]+)['"])/g;
|
|
670
|
+
let match;
|
|
671
|
+
while ((match = re.exec(css)) !== null) {
|
|
672
|
+
const url = match[1] || match[2];
|
|
673
|
+
if (url.startsWith("https://")) {
|
|
674
|
+
imports.push(url);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return imports;
|
|
678
|
+
}
|
|
679
|
+
async function generateRouteHydrationEntry(options) {
|
|
680
|
+
const {
|
|
681
|
+
hydrateImport,
|
|
682
|
+
pageModulePath,
|
|
683
|
+
pageExportName = "page",
|
|
684
|
+
trackByMap,
|
|
685
|
+
outputPath,
|
|
686
|
+
keyedParts = [],
|
|
687
|
+
clientInits = []
|
|
688
|
+
} = options;
|
|
689
|
+
const partImports = keyedParts.map((p, i) => `import { ${p.exportName} as keyedPart${i} } from '${p.modulePath}';`).join("\n");
|
|
690
|
+
const hasPageModule = pageModulePath && pageExportName;
|
|
691
|
+
const pagePartExpr = hasPageModule ? `pagePart && pagePart.comp ? { comp: pagePart.comp, contextMarkers: pagePart.contexts || [] } : null` : `null`;
|
|
692
|
+
const partsArray = [
|
|
693
|
+
pagePartExpr,
|
|
694
|
+
...keyedParts.map(
|
|
695
|
+
(p, i) => `keyedPart${i} && keyedPart${i}.comp ? { comp: keyedPart${i}.comp, contextMarkers: keyedPart${i}.contexts || [], key: '${p.key}' } : null`
|
|
696
|
+
)
|
|
697
|
+
];
|
|
698
|
+
const pageImport = hasPageModule ? `import { ${pageExportName} as pagePart } from '${pageModulePath}';` : "";
|
|
699
|
+
const initImports = clientInits.map((ci, i) => `import { ${ci.exportName} as clientInit${i} } from '${ci.modulePath}';`).join("\n");
|
|
700
|
+
const initCalls = clientInits.map(
|
|
701
|
+
(ci, i) => ` if (clientInit${i}?._clientInit) await clientInit${i}._clientInit(clientInitData['${ci.key}'] || {});`
|
|
702
|
+
).join("\n");
|
|
703
|
+
const code = `import { hydrateCompositeJayComponent } from '@jay-framework/stack-client-runtime';
|
|
704
|
+
import { deepMergeViewStates } from '@jay-framework/view-state-merge';
|
|
705
|
+
import { hydrate } from '${hydrateImport}';
|
|
706
|
+
${pageImport}
|
|
707
|
+
${partImports}
|
|
708
|
+
${initImports}
|
|
709
|
+
|
|
710
|
+
const trackByMap = ${JSON.stringify(trackByMap)};
|
|
711
|
+
|
|
712
|
+
export async function init(slowViewState, fastViewState, fastCarryForward, clientInitData) {
|
|
713
|
+
${initCalls}
|
|
714
|
+
const viewState = deepMergeViewStates(slowViewState, fastViewState, trackByMap);
|
|
715
|
+
const target = document.getElementById('target');
|
|
716
|
+
const rootElement = target.firstElementChild;
|
|
717
|
+
const parts = [
|
|
718
|
+
${partsArray.join(",\n ")}
|
|
719
|
+
].filter(p => p !== null);
|
|
720
|
+
const pageComp = hydrateCompositeJayComponent(
|
|
721
|
+
hydrate, viewState, fastCarryForward,
|
|
722
|
+
parts, trackByMap, rootElement
|
|
723
|
+
);
|
|
724
|
+
return pageComp({});
|
|
725
|
+
}
|
|
726
|
+
`;
|
|
727
|
+
const outputDir = path.dirname(outputPath);
|
|
728
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
729
|
+
await fs.writeFile(outputPath, code, "utf-8");
|
|
730
|
+
getLogger().info(`[Build] Generated route hydration entry: ${path.basename(outputPath)}`);
|
|
1014
731
|
}
|
|
1015
|
-
function
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
732
|
+
async function buildInstanceClient(hydrateEntryPath, instanceId, outputDir, projectRoot, jayOptions, minify = true, pagesRoot, buildDir) {
|
|
733
|
+
const logger = getLogger();
|
|
734
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
735
|
+
const fullJayOptions = {
|
|
736
|
+
...jayOptions,
|
|
737
|
+
...pagesRoot && buildDir ? { pagesRoot, buildFolder: buildDir } : {}
|
|
738
|
+
};
|
|
739
|
+
await build({
|
|
740
|
+
root: projectRoot,
|
|
741
|
+
plugins: [...jayStackCompiler(fullJayOptions)],
|
|
742
|
+
build: {
|
|
743
|
+
outDir: outputDir,
|
|
744
|
+
emptyOutDir: false,
|
|
745
|
+
minify,
|
|
746
|
+
manifest: `${instanceId}-manifest.json`,
|
|
747
|
+
rollupOptions: {
|
|
748
|
+
input: { [instanceId]: hydrateEntryPath },
|
|
749
|
+
external: (id) => id.startsWith("@jay-framework/") || id === "jay-route-hydrate",
|
|
750
|
+
output: {
|
|
751
|
+
entryFileNames: "[name]-[hash].js",
|
|
752
|
+
chunkFileNames: "chunks/[name]-[hash].js",
|
|
753
|
+
assetFileNames: "[name]-[hash].[ext]",
|
|
754
|
+
format: "es"
|
|
755
|
+
},
|
|
756
|
+
preserveEntrySignatures: "exports-only"
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
logLevel: "warn"
|
|
760
|
+
});
|
|
761
|
+
const manifestPath = path.join(outputDir, `${instanceId}-manifest.json`);
|
|
762
|
+
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
|
|
763
|
+
await fs.rm(manifestPath, { force: true });
|
|
764
|
+
const entryKey = Object.keys(manifest).find((k) => manifest[k].isEntry);
|
|
765
|
+
if (!entryKey) {
|
|
766
|
+
throw new Error(`No entry found in instance build manifest for ${instanceId}`);
|
|
1027
767
|
}
|
|
1028
|
-
|
|
768
|
+
const entry = manifest[entryKey];
|
|
769
|
+
const result = {
|
|
770
|
+
jsFile: entry.file,
|
|
771
|
+
cssFile: entry.css?.[0]
|
|
772
|
+
};
|
|
773
|
+
logger.info(`[Build] Client bundle: ${result.jsFile}`);
|
|
774
|
+
return result;
|
|
1029
775
|
}
|
|
1030
776
|
function crossProductParams(parts) {
|
|
1031
777
|
if (parts.length === 0)
|
|
@@ -1049,8 +795,8 @@ function crossProductParams(parts) {
|
|
|
1049
795
|
const next = parts[i].values;
|
|
1050
796
|
const combined = [];
|
|
1051
797
|
for (const a of result) {
|
|
1052
|
-
for (const
|
|
1053
|
-
combined.push({ ...a, ...
|
|
798
|
+
for (const b2 of next) {
|
|
799
|
+
combined.push({ ...a, ...b2 });
|
|
1054
800
|
}
|
|
1055
801
|
}
|
|
1056
802
|
result = combined;
|
|
@@ -1389,6 +1135,112 @@ async function buildVersion(options) {
|
|
|
1389
1135
|
byRoute.set(info, []);
|
|
1390
1136
|
byRoute.get(info).push(materialized2.params);
|
|
1391
1137
|
}
|
|
1138
|
+
for (const [info] of byRoute) {
|
|
1139
|
+
const { route, entry } = info.routeEntry;
|
|
1140
|
+
if (!route.jayHtmlPath)
|
|
1141
|
+
continue;
|
|
1142
|
+
const routeDir = route.rawRoute.replace(/^\//, "") || "index";
|
|
1143
|
+
const frontendSafeRouteDir = routeDir.replace(/\[/g, "%5B").replace(/\]/g, "%5D");
|
|
1144
|
+
const backendRouteDir = path.join(backendDir, "pre-rendered", routeDir);
|
|
1145
|
+
const frontendRouteDir = path.join(frontendDir, "pages", frontendSafeRouteDir);
|
|
1146
|
+
await fs.mkdir(backendRouteDir, { recursive: true });
|
|
1147
|
+
await fs.mkdir(frontendRouteDir, { recursive: true });
|
|
1148
|
+
const serverElementPath = path.join(backendRouteDir, "route.server-element.js");
|
|
1149
|
+
try {
|
|
1150
|
+
const seResult = await compileRouteServerElement(
|
|
1151
|
+
route.jayHtmlPath,
|
|
1152
|
+
serverElementPath,
|
|
1153
|
+
options.projectRoot,
|
|
1154
|
+
options.tsConfigFilePath,
|
|
1155
|
+
options.minify ?? true
|
|
1156
|
+
);
|
|
1157
|
+
entry.serverElementPath = path.relative(backendDir, serverElementPath);
|
|
1158
|
+
if (seResult.cssFile) {
|
|
1159
|
+
const srcCss = path.join(backendRouteDir, seResult.cssFile);
|
|
1160
|
+
const dstCss = path.join(frontendRouteDir, seResult.cssFile);
|
|
1161
|
+
try {
|
|
1162
|
+
await fs.rename(srcCss, dstCss);
|
|
1163
|
+
} catch {
|
|
1164
|
+
await fs.copyFile(srcCss, dstCss);
|
|
1165
|
+
await fs.rm(srcCss, { force: true });
|
|
1166
|
+
}
|
|
1167
|
+
entry.routeCssPath = path.relative(frontendDir, dstCss);
|
|
1168
|
+
}
|
|
1169
|
+
if (seResult.cssImports?.length) {
|
|
1170
|
+
entry.cssImports = seResult.cssImports;
|
|
1171
|
+
}
|
|
1172
|
+
logger.important(`[Build] Route server element: ${routeDir}`);
|
|
1173
|
+
} catch (err) {
|
|
1174
|
+
logger.error(`[Build] Route server element FAILED ${route.rawRoute}: ${err.message}`);
|
|
1175
|
+
}
|
|
1176
|
+
try {
|
|
1177
|
+
const hydrateResult = await compileRouteHydrateScript(
|
|
1178
|
+
route.jayHtmlPath,
|
|
1179
|
+
frontendRouteDir,
|
|
1180
|
+
options.projectRoot,
|
|
1181
|
+
options.tsConfigFilePath,
|
|
1182
|
+
options.minify ?? true
|
|
1183
|
+
);
|
|
1184
|
+
entry.routeHydratePath = path.relative(
|
|
1185
|
+
frontendDir,
|
|
1186
|
+
path.join(frontendRouteDir, hydrateResult.jsFile)
|
|
1187
|
+
);
|
|
1188
|
+
logger.important(`[Build] Route hydrate script: ${routeDir}`);
|
|
1189
|
+
} catch (err) {
|
|
1190
|
+
logger.error(`[Build] Route hydrate script FAILED ${route.rawRoute}: ${err.message}`);
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
try {
|
|
1194
|
+
const ROUTE_HYDRATE_KEY = "jay-route-hydrate";
|
|
1195
|
+
const exportName = route.componentExport || "page";
|
|
1196
|
+
let pageModulePath = "";
|
|
1197
|
+
if (route.compPath) {
|
|
1198
|
+
if (route.componentExport) {
|
|
1199
|
+
const pkgName = route.packageName || route.compPath;
|
|
1200
|
+
pageModulePath = `${pkgName}/client`;
|
|
1201
|
+
} else {
|
|
1202
|
+
pageModulePath = "./" + path.relative(frontendRouteDir, route.compPath);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
const pageParts = await loadProductionPageParts(
|
|
1206
|
+
route,
|
|
1207
|
+
{},
|
|
1208
|
+
await fs.readFile(route.jayHtmlPath, "utf-8"),
|
|
1209
|
+
options.projectRoot,
|
|
1210
|
+
options.tsConfigFilePath,
|
|
1211
|
+
path.join(backendDir, "server")
|
|
1212
|
+
);
|
|
1213
|
+
entry.trackByMap = pageParts.serverTrackByMap || pageParts.clientTrackByMap;
|
|
1214
|
+
const entryPath = path.join(frontendRouteDir, "route.entry.ts");
|
|
1215
|
+
await generateRouteHydrationEntry({
|
|
1216
|
+
hydrateImport: ROUTE_HYDRATE_KEY,
|
|
1217
|
+
pageModulePath,
|
|
1218
|
+
pageExportName: exportName,
|
|
1219
|
+
trackByMap: pageParts.clientTrackByMap || {},
|
|
1220
|
+
outputPath: entryPath,
|
|
1221
|
+
keyedParts: pageParts.keyedPartModules,
|
|
1222
|
+
clientInits
|
|
1223
|
+
});
|
|
1224
|
+
const clientResult = await buildInstanceClient(
|
|
1225
|
+
entryPath,
|
|
1226
|
+
"route.client",
|
|
1227
|
+
frontendRouteDir,
|
|
1228
|
+
options.projectRoot,
|
|
1229
|
+
{ tsConfigFilePath: options.tsConfigFilePath },
|
|
1230
|
+
options.minify ?? true,
|
|
1231
|
+
options.pagesRoot,
|
|
1232
|
+
buildDir
|
|
1233
|
+
);
|
|
1234
|
+
await fs.rm(entryPath, { force: true });
|
|
1235
|
+
entry.routeClientBundlePath = path.relative(
|
|
1236
|
+
frontendDir,
|
|
1237
|
+
path.join(frontendRouteDir, clientResult.jsFile)
|
|
1238
|
+
);
|
|
1239
|
+
logger.important(`[Build] Route client bundle: ${routeDir}`);
|
|
1240
|
+
} catch (err) {
|
|
1241
|
+
logger.error(`[Build] Route client bundle FAILED ${route.rawRoute}: ${err.message}`);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1392
1244
|
for (const [info, paramsList] of byRoute) {
|
|
1393
1245
|
const { route, entry } = info.routeEntry;
|
|
1394
1246
|
let pageModule;
|
|
@@ -1405,7 +1257,16 @@ async function buildVersion(options) {
|
|
|
1405
1257
|
}
|
|
1406
1258
|
for (const params of paramsList) {
|
|
1407
1259
|
try {
|
|
1408
|
-
const result = await buildInstance(
|
|
1260
|
+
const result = await buildInstance(
|
|
1261
|
+
route,
|
|
1262
|
+
params,
|
|
1263
|
+
pageModule,
|
|
1264
|
+
instanceCtx,
|
|
1265
|
+
entry.serverElementPath,
|
|
1266
|
+
entry.routeCssPath,
|
|
1267
|
+
entry.routeHydratePath,
|
|
1268
|
+
entry.routeClientBundlePath
|
|
1269
|
+
);
|
|
1409
1270
|
if (result.status === "success") {
|
|
1410
1271
|
entry.instances.push(result.instanceEntry);
|
|
1411
1272
|
if (result.contracts.length > 0 && !entry.contracts) {
|
|
@@ -1458,516 +1319,6 @@ async function buildVersion(options) {
|
|
|
1458
1319
|
logger.important(`[Build] Done! ${instanceCount} instances built in ${buildDir}`);
|
|
1459
1320
|
return manifest;
|
|
1460
1321
|
}
|
|
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
1322
|
function toFetchRequest(req) {
|
|
1972
1323
|
const url = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
1973
1324
|
const headers = new Headers();
|
|
@@ -2397,14 +1748,9 @@ function paramsMatch(instanceParams, targetParams) {
|
|
|
2397
1748
|
return Object.entries(targetParams).every(([key, value]) => instanceParams[key] === value);
|
|
2398
1749
|
}
|
|
2399
1750
|
function collectInstanceFiles(instance) {
|
|
2400
|
-
if (!instance.
|
|
1751
|
+
if (!instance.cachePath)
|
|
2401
1752
|
return [];
|
|
2402
|
-
const files = [
|
|
2403
|
-
instance.preRenderedPath,
|
|
2404
|
-
instance.preRenderedPath.replace(".jay-html", ".cache.json"),
|
|
2405
|
-
instance.serverElementPath,
|
|
2406
|
-
instance.clientBundlePath
|
|
2407
|
-
];
|
|
1753
|
+
const files = [instance.cachePath, instance.serverElementPath, instance.clientBundlePath];
|
|
2408
1754
|
if (instance.clientCssPath)
|
|
2409
1755
|
files.push(instance.clientCssPath);
|
|
2410
1756
|
return files.filter(Boolean);
|
|
@@ -2579,11 +1925,13 @@ export {
|
|
|
2579
1925
|
fetchPageRequest,
|
|
2580
1926
|
fetchStaticFile,
|
|
2581
1927
|
initializeServices,
|
|
1928
|
+
e as initializeServicesFromModules,
|
|
2582
1929
|
isActionRequest,
|
|
2583
1930
|
matchRequest,
|
|
2584
1931
|
rebuild,
|
|
2585
1932
|
rebuildContract,
|
|
2586
1933
|
registerActionsFromManifest,
|
|
1934
|
+
b as registerActionsFromModules,
|
|
2587
1935
|
resolveContractToRoutes,
|
|
2588
1936
|
startMainServer,
|
|
2589
1937
|
startRendererServer
|