@jay-framework/dev-server 0.25.0 → 0.25.2
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 +7 -0
- package/dist/index.js +118 -31
- package/package.json +15 -15
package/dist/index.d.ts
CHANGED
|
@@ -186,6 +186,13 @@ interface DevServer {
|
|
|
186
186
|
freezeStore?: FreezeStore;
|
|
187
187
|
/** Public API for design board applications and CLI (DL#128) */
|
|
188
188
|
service: DevServerService;
|
|
189
|
+
/**
|
|
190
|
+
* Express handler that renders the designed error page for a status (DL#218). Mount
|
|
191
|
+
* `errorPageHandler(404)` as a catch-all **after** all route registrations so unmatched paths
|
|
192
|
+
* get the branded 404 instead of Express's default. Falls back to plain text when no error page
|
|
193
|
+
* matches the status.
|
|
194
|
+
*/
|
|
195
|
+
errorPageHandler: (status: number) => RequestHandler;
|
|
189
196
|
}
|
|
190
197
|
declare function mkDevServer(rawOptions: DevServerOptions): Promise<DevServer>;
|
|
191
198
|
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createRequire as createRequire$1 } from "node:module";
|
|
|
13
13
|
import * as fs from "node:fs";
|
|
14
14
|
import fs__default$1 from "node:fs";
|
|
15
15
|
import { scanRoutes, createRoute, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
|
|
16
|
-
import { makePassthroughInstanceComponent, actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, parseCookies, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeInstancePropNames } from "@jay-framework/stack-server-runtime";
|
|
16
|
+
import { makePassthroughInstanceComponent, actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, serializeScripts, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, parseCookies, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeInstancePropNames, resolveErrorPageToken, buildErrorViewState } from "@jay-framework/stack-server-runtime";
|
|
17
17
|
import * as fs$1 from "node:fs/promises";
|
|
18
18
|
import fs__default from "node:fs/promises";
|
|
19
19
|
import jsBeautify from "js-beautify";
|
|
@@ -2307,8 +2307,7 @@ async function generateSSRPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtm
|
|
|
2307
2307
|
const tagSources = [];
|
|
2308
2308
|
if (headTags && headTags.length > 0) tagSources.push(headTags);
|
|
2309
2309
|
const headMetaTags = headMetaToHeadTags(cached.headMeta, viewState);
|
|
2310
|
-
|
|
2311
|
-
const mergedTags = mergeHeadTags(tagSources);
|
|
2310
|
+
const mergedTags = mergeHeadTags(tagSources, headMetaTags);
|
|
2312
2311
|
const hasCustomTitle = mergedTags.some((t) => t.tag.toLowerCase() === "title");
|
|
2313
2312
|
const titleTag = hasCustomTitle ? "" : " <title>Vite + TS</title>\n";
|
|
2314
2313
|
const headTagsHtml = mergedTags.length > 0 ? serializeHeadTags(mergedTags) : "";
|
|
@@ -2403,19 +2402,6 @@ ${headExtras ? headExtras + "\n" : ""} <style>
|
|
|
2403
2402
|
</body>
|
|
2404
2403
|
</html>`;
|
|
2405
2404
|
}
|
|
2406
|
-
function serializeScripts(scripts, position) {
|
|
2407
|
-
if (!scripts) return "";
|
|
2408
|
-
const filtered = scripts.filter((s2) => s2.position === position);
|
|
2409
|
-
if (filtered.length === 0) return "";
|
|
2410
|
-
return filtered.map((s2) => {
|
|
2411
|
-
const attrs = Object.entries(s2.attributes).map(([k, v]) => v === "" ? k : `${k}="${v}"`).join(" ");
|
|
2412
|
-
const attrStr = attrs ? " " + attrs : "";
|
|
2413
|
-
if (s2.src) {
|
|
2414
|
-
return ` <script src="${s2.src}"${attrStr}><\/script>`;
|
|
2415
|
-
}
|
|
2416
|
-
return ` <script${attrStr}>${s2.inline}<\/script>`;
|
|
2417
|
-
}).join("\n");
|
|
2418
|
-
}
|
|
2419
2405
|
function rebaseRelativeImports(code, fromDir, toDir) {
|
|
2420
2406
|
return code.replace(/from "(\.\.\/[^"]+)"/g, (_match, relPath) => {
|
|
2421
2407
|
const absolutePath = path__default.resolve(fromDir, relPath);
|
|
@@ -4419,10 +4405,22 @@ class DevServerService {
|
|
|
4419
4405
|
let _watchLinkedFiles = () => {
|
|
4420
4406
|
};
|
|
4421
4407
|
async function initRoutes(pagesBaseFolder) {
|
|
4422
|
-
|
|
4408
|
+
const { routes } = await scanRoutes(pagesBaseFolder, {
|
|
4409
|
+
jayHtmlFilename: "page.jay-html",
|
|
4410
|
+
compFilename: "page.ts"
|
|
4411
|
+
});
|
|
4412
|
+
return routes;
|
|
4413
|
+
}
|
|
4414
|
+
async function initErrorPages(pagesBaseFolder) {
|
|
4415
|
+
const { errorPages } = await scanRoutes(pagesBaseFolder, {
|
|
4423
4416
|
jayHtmlFilename: "page.jay-html",
|
|
4424
4417
|
compFilename: "page.ts"
|
|
4425
4418
|
});
|
|
4419
|
+
const map = /* @__PURE__ */ new Map();
|
|
4420
|
+
for (const ep of errorPages) {
|
|
4421
|
+
map.set(ep.token, createRoute(`/_errors/${ep.token}`, ep.jayHtmlPath, ep.compPath));
|
|
4422
|
+
}
|
|
4423
|
+
return map;
|
|
4426
4424
|
}
|
|
4427
4425
|
async function scanPluginRoutes(projectRoot, projectRoutes) {
|
|
4428
4426
|
const plugins = await scanPlugins({ projectRoot, includeDevDeps: true });
|
|
@@ -4552,12 +4550,81 @@ function defaults(options) {
|
|
|
4552
4550
|
httpServer: options.httpServer
|
|
4553
4551
|
};
|
|
4554
4552
|
}
|
|
4555
|
-
|
|
4556
|
-
if (
|
|
4557
|
-
res.status(
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4553
|
+
let _renderDevErrorPage = async (res, info) => {
|
|
4554
|
+
if (!res.headersSent)
|
|
4555
|
+
res.status(info.status).type("text/plain").send(info.message || info.title || "Error");
|
|
4556
|
+
};
|
|
4557
|
+
async function renderDevErrorPage(vite, options, errorRoutes, projectInit, allPluginsWithInit, allPluginClientInits, res, info) {
|
|
4558
|
+
const plainText = () => {
|
|
4559
|
+
if (!res.headersSent)
|
|
4560
|
+
res.status(info.status).type("text/plain").send(info.message || info.title || "Error");
|
|
4561
|
+
};
|
|
4562
|
+
try {
|
|
4563
|
+
const token = resolveErrorPageToken(errorRoutes.keys(), info.status);
|
|
4564
|
+
if (!token) return plainText();
|
|
4565
|
+
const route = errorRoutes.get(token);
|
|
4566
|
+
const partsResult = await loadPageParts(
|
|
4567
|
+
vite,
|
|
4568
|
+
route,
|
|
4569
|
+
options.pagesRootFolder,
|
|
4570
|
+
options.projectRootFolder,
|
|
4571
|
+
options.jayRollupConfig
|
|
4572
|
+
);
|
|
4573
|
+
if (!partsResult.val) {
|
|
4574
|
+
getLogger().warn(
|
|
4575
|
+
`[DevServer] Error page "${token}" failed to load: ${partsResult.validations.join("; ")}`
|
|
4576
|
+
);
|
|
4577
|
+
return plainText();
|
|
4578
|
+
}
|
|
4579
|
+
const { parts, clientTrackByMap, usedPackages, scripts } = partsResult.val;
|
|
4580
|
+
const pluginsForPage = filterPluginsForPage(
|
|
4581
|
+
allPluginClientInits,
|
|
4582
|
+
allPluginsWithInit,
|
|
4583
|
+
usedPackages
|
|
4584
|
+
);
|
|
4585
|
+
const fullViewState = buildErrorViewState(info, {});
|
|
4586
|
+
await sendResponse(
|
|
4587
|
+
vite,
|
|
4588
|
+
res,
|
|
4589
|
+
info.url,
|
|
4590
|
+
route.jayHtmlPath,
|
|
4591
|
+
route.jayHtmlPath,
|
|
4592
|
+
parts,
|
|
4593
|
+
fullViewState,
|
|
4594
|
+
{},
|
|
4595
|
+
clientTrackByMap,
|
|
4596
|
+
projectInit,
|
|
4597
|
+
pluginsForPage,
|
|
4598
|
+
options,
|
|
4599
|
+
routeToExpressRoute(route),
|
|
4600
|
+
getRouteDir(route),
|
|
4601
|
+
{},
|
|
4602
|
+
void 0,
|
|
4603
|
+
void 0,
|
|
4604
|
+
void 0,
|
|
4605
|
+
scripts,
|
|
4606
|
+
info.status
|
|
4607
|
+
);
|
|
4608
|
+
} catch (err) {
|
|
4609
|
+
getLogger().error(
|
|
4610
|
+
`[DevServer] Error page render failed (${info.status} ${info.url}): ${err?.message}`
|
|
4611
|
+
);
|
|
4612
|
+
plainText();
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
async function handleOtherResponseCodes(res, renderedResult, url) {
|
|
4616
|
+
if (renderedResult.kind === `Redirect`) {
|
|
4617
|
+
res.status(renderedResult.status).end("redirect to " + renderedResult.location);
|
|
4618
|
+
return;
|
|
4619
|
+
}
|
|
4620
|
+
await _renderDevErrorPage(res, {
|
|
4621
|
+
status: renderedResult.status,
|
|
4622
|
+
url,
|
|
4623
|
+
message: renderedResult.message,
|
|
4624
|
+
title: renderedResult.title,
|
|
4625
|
+
code: renderedResult.code,
|
|
4626
|
+
details: renderedResult.details
|
|
4627
|
+
});
|
|
4561
4628
|
}
|
|
4562
4629
|
function filterPluginsForPage(allPluginClientInits, allPluginsWithInit, usedPackages) {
|
|
4563
4630
|
const pluginsByPackage = /* @__PURE__ */ new Map();
|
|
@@ -4685,7 +4752,12 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
|
|
|
4685
4752
|
} catch (e2) {
|
|
4686
4753
|
vite?.ssrFixStacktrace(e2);
|
|
4687
4754
|
getLogger().error(e2.stack);
|
|
4688
|
-
|
|
4755
|
+
const errUrl = (req.originalUrl || req.url || "/").split("?")[0];
|
|
4756
|
+
await _renderDevErrorPage(res, {
|
|
4757
|
+
status: 500,
|
|
4758
|
+
url: errUrl,
|
|
4759
|
+
message: e2.message
|
|
4760
|
+
});
|
|
4689
4761
|
timing?.end();
|
|
4690
4762
|
}
|
|
4691
4763
|
};
|
|
@@ -4748,7 +4820,7 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
4748
4820
|
);
|
|
4749
4821
|
timing?.recordFastRender(Date.now() - fastStart);
|
|
4750
4822
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
4751
|
-
handleOtherResponseCodes(res, renderedFast);
|
|
4823
|
+
await handleOtherResponseCodes(res, renderedFast, url);
|
|
4752
4824
|
timing?.end();
|
|
4753
4825
|
return;
|
|
4754
4826
|
}
|
|
@@ -4824,7 +4896,7 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
4824
4896
|
);
|
|
4825
4897
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
4826
4898
|
timing?.recordSlowRender(Date.now() - slowStart);
|
|
4827
|
-
handleOtherResponseCodes(res, renderedSlowly);
|
|
4899
|
+
await handleOtherResponseCodes(res, renderedSlowly, url);
|
|
4828
4900
|
timing?.end();
|
|
4829
4901
|
return;
|
|
4830
4902
|
}
|
|
@@ -4934,7 +5006,7 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
|
|
|
4934
5006
|
);
|
|
4935
5007
|
if (renderedSlowly.kind !== "PhaseOutput") {
|
|
4936
5008
|
timing?.recordSlowRender(Date.now() - slowStart);
|
|
4937
|
-
handleOtherResponseCodes(res, renderedSlowly);
|
|
5009
|
+
await handleOtherResponseCodes(res, renderedSlowly, url);
|
|
4938
5010
|
timing?.end();
|
|
4939
5011
|
return;
|
|
4940
5012
|
}
|
|
@@ -4955,7 +5027,7 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
|
|
|
4955
5027
|
);
|
|
4956
5028
|
timing?.recordFastRender(Date.now() - fastStart);
|
|
4957
5029
|
if (renderedFast.kind !== "PhaseOutput") {
|
|
4958
|
-
handleOtherResponseCodes(res, renderedFast);
|
|
5030
|
+
await handleOtherResponseCodes(res, renderedFast, url);
|
|
4959
5031
|
timing?.end();
|
|
4960
5032
|
return;
|
|
4961
5033
|
}
|
|
@@ -4999,7 +5071,7 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
|
|
|
4999
5071
|
res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
|
|
5000
5072
|
timing?.end();
|
|
5001
5073
|
}
|
|
5002
|
-
async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags, scripts) {
|
|
5074
|
+
async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags, scripts, statusCode = 200) {
|
|
5003
5075
|
let pageHtml;
|
|
5004
5076
|
try {
|
|
5005
5077
|
const jayHtmlDir = path__default.dirname(jayHtmlPath);
|
|
@@ -5062,7 +5134,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
|
|
|
5062
5134
|
const viteStart = Date.now();
|
|
5063
5135
|
const compiledPageHtml = await vite.transformIndexHtml(!!url ? url : "/", pageHtml);
|
|
5064
5136
|
timing?.recordViteClient(Date.now() - viteStart);
|
|
5065
|
-
res.status(
|
|
5137
|
+
res.status(statusCode).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
|
|
5066
5138
|
timing?.end();
|
|
5067
5139
|
}
|
|
5068
5140
|
async function handleFrozenRequest(vite, route, options, freezeStore, slowRenderCache, freezeId, pageParams, format, res, url, timing) {
|
|
@@ -5244,6 +5316,7 @@ async function mkDevServer(rawOptions) {
|
|
|
5244
5316
|
setupActionRouter(vite, buildFolder);
|
|
5245
5317
|
const projectRoutes = await initRoutes(pagesRootFolder);
|
|
5246
5318
|
const filteredProjectRoutes = buildFolder ? projectRoutes.filter((route) => !route.jayHtmlPath.startsWith(buildFolder)) : projectRoutes;
|
|
5319
|
+
const errorRoutes = await initErrorPages(pagesRootFolder);
|
|
5247
5320
|
const pluginRoutes = await scanPluginRoutes(projectRootFolder, filteredProjectRoutes);
|
|
5248
5321
|
const routes = [...filteredProjectRoutes, ...pluginRoutes];
|
|
5249
5322
|
const slowlyPhase = new DevSlowlyChangingPhase();
|
|
@@ -5257,6 +5330,16 @@ async function mkDevServer(rawOptions) {
|
|
|
5257
5330
|
const projectInit = lifecycleManager.getProjectInit() ?? void 0;
|
|
5258
5331
|
const pluginsWithInit = lifecycleManager.getPluginsWithInit();
|
|
5259
5332
|
const pluginClientInits = preparePluginClientInits(pluginsWithInit);
|
|
5333
|
+
_renderDevErrorPage = (res, info) => renderDevErrorPage(
|
|
5334
|
+
vite,
|
|
5335
|
+
options,
|
|
5336
|
+
errorRoutes,
|
|
5337
|
+
projectInit,
|
|
5338
|
+
pluginsWithInit,
|
|
5339
|
+
pluginClientInits,
|
|
5340
|
+
res,
|
|
5341
|
+
info
|
|
5342
|
+
);
|
|
5260
5343
|
const freezeStore = buildFolder ? new FreezeStore(buildFolder) : void 0;
|
|
5261
5344
|
if (freezeStore) {
|
|
5262
5345
|
setupFreezeEndpoint(vite, freezeStore);
|
|
@@ -5328,7 +5411,11 @@ async function mkDevServer(rawOptions) {
|
|
|
5328
5411
|
routes: devServerRoutes,
|
|
5329
5412
|
lifecycleManager,
|
|
5330
5413
|
freezeStore,
|
|
5331
|
-
service
|
|
5414
|
+
service,
|
|
5415
|
+
errorPageHandler: (status) => async (req, res) => {
|
|
5416
|
+
const url = (req.originalUrl || req.url || "/").split("?")[0];
|
|
5417
|
+
await _renderDevErrorPage(res, { status, url });
|
|
5418
|
+
}
|
|
5332
5419
|
};
|
|
5333
5420
|
}
|
|
5334
5421
|
function setupGracefulShutdown(lifecycleManager) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/dev-server",
|
|
3
|
-
"version": "0.25.
|
|
3
|
+
"version": "0.25.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,24 +23,24 @@
|
|
|
23
23
|
"test:watch": "vitest"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@jay-framework/compiler-jay-stack": "^0.25.
|
|
27
|
-
"@jay-framework/compiler-shared": "^0.25.
|
|
28
|
-
"@jay-framework/component": "^0.25.
|
|
29
|
-
"@jay-framework/fullstack-component": "^0.25.
|
|
30
|
-
"@jay-framework/logger": "^0.25.
|
|
31
|
-
"@jay-framework/runtime": "^0.25.
|
|
32
|
-
"@jay-framework/stack-client-runtime": "^0.25.
|
|
33
|
-
"@jay-framework/stack-route-scanner": "^0.25.
|
|
34
|
-
"@jay-framework/stack-server-build": "^0.25.
|
|
35
|
-
"@jay-framework/stack-server-runtime": "^0.25.
|
|
36
|
-
"@jay-framework/view-state-merge": "^0.25.
|
|
26
|
+
"@jay-framework/compiler-jay-stack": "^0.25.2",
|
|
27
|
+
"@jay-framework/compiler-shared": "^0.25.2",
|
|
28
|
+
"@jay-framework/component": "^0.25.2",
|
|
29
|
+
"@jay-framework/fullstack-component": "^0.25.2",
|
|
30
|
+
"@jay-framework/logger": "^0.25.2",
|
|
31
|
+
"@jay-framework/runtime": "^0.25.2",
|
|
32
|
+
"@jay-framework/stack-client-runtime": "^0.25.2",
|
|
33
|
+
"@jay-framework/stack-route-scanner": "^0.25.2",
|
|
34
|
+
"@jay-framework/stack-server-build": "^0.25.2",
|
|
35
|
+
"@jay-framework/stack-server-runtime": "^0.25.2",
|
|
36
|
+
"@jay-framework/view-state-merge": "^0.25.2",
|
|
37
37
|
"busboy": "^1.6.0",
|
|
38
38
|
"vite": "^5.0.11"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@jay-framework/dev-environment": "^0.25.
|
|
42
|
-
"@jay-framework/jay-cli": "^0.25.
|
|
43
|
-
"@jay-framework/stack-client-runtime": "^0.25.
|
|
41
|
+
"@jay-framework/dev-environment": "^0.25.2",
|
|
42
|
+
"@jay-framework/jay-cli": "^0.25.2",
|
|
43
|
+
"@jay-framework/stack-client-runtime": "^0.25.2",
|
|
44
44
|
"@playwright/test": "^1.58.2",
|
|
45
45
|
"@types/busboy": "^1.5.4",
|
|
46
46
|
"@types/express": "^5.0.2",
|