@jay-framework/dev-server 0.16.0 → 0.16.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 +13 -3
- package/dist/index.js +203 -19
- package/package.json +16 -14
package/dist/index.d.ts
CHANGED
|
@@ -220,10 +220,20 @@ interface ActionRouterOptions {
|
|
|
220
220
|
*/
|
|
221
221
|
declare function createActionRouter(options?: ActionRouterOptions): RequestHandler$1;
|
|
222
222
|
/**
|
|
223
|
-
*
|
|
223
|
+
* Options for the action body parser middleware.
|
|
224
|
+
*/
|
|
225
|
+
interface ActionBodyParserOptions {
|
|
226
|
+
/** Build folder for temp file storage (DL#131) */
|
|
227
|
+
buildFolder: string;
|
|
228
|
+
/** Action registry to check for acceptsFiles (default: global actionRegistry) */
|
|
229
|
+
registry?: ActionRegistry;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Express middleware to parse request body for action requests.
|
|
233
|
+
* Supports JSON (default) and multipart/form-data (for actions with .withFiles()).
|
|
224
234
|
* Should be applied before the action router.
|
|
225
235
|
*/
|
|
226
|
-
declare function actionBodyParser(): RequestHandler$1;
|
|
236
|
+
declare function actionBodyParser(options: ActionBodyParserOptions): RequestHandler$1;
|
|
227
237
|
|
|
228
238
|
/**
|
|
229
239
|
* Vite Factory
|
|
@@ -267,4 +277,4 @@ declare function createViteForCli(options: {
|
|
|
267
277
|
tsConfigFilePath?: string;
|
|
268
278
|
}): Promise<ViteDevServer>;
|
|
269
279
|
|
|
270
|
-
export { ACTION_ENDPOINT_BASE, type ActionRouterOptions, type CreateViteServerOptions, DEV_SERVER_SERVICE, type DevServer, type DevServerOptions, type DevServerRoute, DevServerService, type FreezeEntry, FreezeStore, type RouteInfo, actionBodyParser, createActionRouter, createViteForCli, createViteServer, mkDevServer };
|
|
280
|
+
export { ACTION_ENDPOINT_BASE, type ActionBodyParserOptions, type ActionRouterOptions, type CreateViteServerOptions, DEV_SERVER_SERVICE, type DevServer, type DevServerOptions, type DevServerRoute, DevServerService, type FreezeEntry, FreezeStore, type RouteInfo, actionBodyParser, createActionRouter, createViteForCli, createViteServer, mkDevServer };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,10 @@ import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServer
|
|
|
20
20
|
import * as fs$1 from "node:fs/promises";
|
|
21
21
|
import fs__default from "node:fs/promises";
|
|
22
22
|
import { pathToFileURL } from "node:url";
|
|
23
|
+
import Busboy from "busboy";
|
|
24
|
+
import fs$2 from "fs";
|
|
25
|
+
import path$1 from "path";
|
|
26
|
+
import crypto from "crypto";
|
|
23
27
|
import { randomUUID } from "node:crypto";
|
|
24
28
|
import { createJayService } from "@jay-framework/fullstack-component";
|
|
25
29
|
const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c$1 = new Proxy(e$1, {
|
|
@@ -915,6 +919,7 @@ function extractActionFromExpression(node) {
|
|
|
915
919
|
let current = node;
|
|
916
920
|
let method = "POST";
|
|
917
921
|
let explicitMethod = null;
|
|
922
|
+
let acceptsFiles = false;
|
|
918
923
|
while (c$1.isCallExpression(current)) {
|
|
919
924
|
const expr = current.expression;
|
|
920
925
|
if (c$1.isPropertyAccessExpression(expr) && expr.name.text === "withMethod") {
|
|
@@ -925,6 +930,11 @@ function extractActionFromExpression(node) {
|
|
|
925
930
|
current = expr.expression;
|
|
926
931
|
continue;
|
|
927
932
|
}
|
|
933
|
+
if (c$1.isPropertyAccessExpression(expr) && expr.name.text === "withFiles") {
|
|
934
|
+
acceptsFiles = true;
|
|
935
|
+
current = expr.expression;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
928
938
|
if (c$1.isPropertyAccessExpression(expr) && ["withServices", "withCaching", "withHandler", "withTimeout"].includes(expr.name.text)) {
|
|
929
939
|
current = expr.expression;
|
|
930
940
|
continue;
|
|
@@ -938,7 +948,8 @@ function extractActionFromExpression(node) {
|
|
|
938
948
|
return {
|
|
939
949
|
actionName: nameArg.text,
|
|
940
950
|
method: "POST",
|
|
941
|
-
isStreaming: true
|
|
951
|
+
isStreaming: true,
|
|
952
|
+
...acceptsFiles && { acceptsFiles: true }
|
|
942
953
|
};
|
|
943
954
|
}
|
|
944
955
|
method = funcName === "makeJayQuery" ? "GET" : "POST";
|
|
@@ -947,7 +958,8 @@ function extractActionFromExpression(node) {
|
|
|
947
958
|
}
|
|
948
959
|
return {
|
|
949
960
|
actionName: nameArg.text,
|
|
950
|
-
method
|
|
961
|
+
method,
|
|
962
|
+
...acceptsFiles && { acceptsFiles: true }
|
|
951
963
|
};
|
|
952
964
|
}
|
|
953
965
|
}
|
|
@@ -1354,13 +1366,14 @@ function jayStackCompiler(options = {}) {
|
|
|
1354
1366
|
""
|
|
1355
1367
|
];
|
|
1356
1368
|
for (const action of actions) {
|
|
1369
|
+
const filesOpt = action.acceptsFiles ? ", { acceptsFiles: true }" : "";
|
|
1357
1370
|
if (action.isStreaming) {
|
|
1358
1371
|
lines.push(
|
|
1359
|
-
`export const ${action.exportName} = createStreamCaller('${action.actionName}');`
|
|
1372
|
+
`export const ${action.exportName} = createStreamCaller('${action.actionName}'${filesOpt});`
|
|
1360
1373
|
);
|
|
1361
1374
|
} else {
|
|
1362
1375
|
lines.push(
|
|
1363
|
-
`export const ${action.exportName} = createActionCaller('${action.actionName}', '${action.method}');`
|
|
1376
|
+
`export const ${action.exportName} = createActionCaller('${action.actionName}', '${action.method}'${filesOpt});`
|
|
1364
1377
|
);
|
|
1365
1378
|
}
|
|
1366
1379
|
}
|
|
@@ -1782,6 +1795,11 @@ function createActionRouter(options) {
|
|
|
1782
1795
|
requestMethod,
|
|
1783
1796
|
ACTION_ENDPOINT_BASE + "/" + actionName
|
|
1784
1797
|
);
|
|
1798
|
+
const tempDir = req._jayTempDir;
|
|
1799
|
+
const cleanup = () => {
|
|
1800
|
+
if (tempDir)
|
|
1801
|
+
cleanupTempDir(tempDir);
|
|
1802
|
+
};
|
|
1785
1803
|
if (registry.isStreaming(actionName)) {
|
|
1786
1804
|
res.setHeader("Content-Type", "application/x-ndjson");
|
|
1787
1805
|
res.setHeader("Transfer-Encoding", "chunked");
|
|
@@ -1794,11 +1812,13 @@ function createActionRouter(options) {
|
|
|
1794
1812
|
} catch (err) {
|
|
1795
1813
|
res.write(JSON.stringify({ error: err.message }) + "\n");
|
|
1796
1814
|
}
|
|
1815
|
+
cleanup();
|
|
1797
1816
|
res.end();
|
|
1798
1817
|
timing?.end();
|
|
1799
1818
|
return;
|
|
1800
1819
|
}
|
|
1801
1820
|
const result = await registry.execute(actionName, input);
|
|
1821
|
+
cleanup();
|
|
1802
1822
|
if (requestMethod === "GET" && result.success) {
|
|
1803
1823
|
const cacheHeaders = registry.getCacheHeaders(actionName);
|
|
1804
1824
|
if (cacheHeaders) {
|
|
@@ -1839,7 +1859,106 @@ function getStatusCodeForError(code, isActionError) {
|
|
|
1839
1859
|
return 500;
|
|
1840
1860
|
}
|
|
1841
1861
|
}
|
|
1842
|
-
|
|
1862
|
+
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
1863
|
+
const DEFAULT_MAX_FILES = 10;
|
|
1864
|
+
function parseMultipart(req, tempDir, maxFileSize, maxFiles) {
|
|
1865
|
+
return new Promise((resolve, reject) => {
|
|
1866
|
+
fs$2.mkdirSync(tempDir, { recursive: true });
|
|
1867
|
+
const files = {};
|
|
1868
|
+
let jsonData = {};
|
|
1869
|
+
let fileCount = 0;
|
|
1870
|
+
let errored = false;
|
|
1871
|
+
const pendingWrites = [];
|
|
1872
|
+
const bb = Busboy({
|
|
1873
|
+
headers: req.headers,
|
|
1874
|
+
limits: {
|
|
1875
|
+
fileSize: maxFileSize,
|
|
1876
|
+
files: maxFiles
|
|
1877
|
+
}
|
|
1878
|
+
});
|
|
1879
|
+
bb.on("file", (fieldname, stream, info) => {
|
|
1880
|
+
if (errored)
|
|
1881
|
+
return;
|
|
1882
|
+
fileCount++;
|
|
1883
|
+
const filename = info.filename || `upload-${fileCount}`;
|
|
1884
|
+
const tempPath = path$1.join(tempDir, `${fileCount}-${filename}`);
|
|
1885
|
+
let size = 0;
|
|
1886
|
+
let truncated = false;
|
|
1887
|
+
const writeStream = fs$2.createWriteStream(tempPath);
|
|
1888
|
+
stream.pipe(writeStream);
|
|
1889
|
+
stream.on("data", (data) => {
|
|
1890
|
+
size += data.length;
|
|
1891
|
+
});
|
|
1892
|
+
stream.on("limit", () => {
|
|
1893
|
+
truncated = true;
|
|
1894
|
+
});
|
|
1895
|
+
pendingWrites.push(
|
|
1896
|
+
new Promise((resolveWrite, rejectWrite) => {
|
|
1897
|
+
writeStream.on("close", () => {
|
|
1898
|
+
if (truncated) {
|
|
1899
|
+
rejectWrite(
|
|
1900
|
+
new Error(
|
|
1901
|
+
`File "${filename}" exceeds maximum size of ${maxFileSize} bytes`
|
|
1902
|
+
)
|
|
1903
|
+
);
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
const jayFile = {
|
|
1907
|
+
name: filename,
|
|
1908
|
+
type: info.mimeType,
|
|
1909
|
+
size,
|
|
1910
|
+
path: tempPath
|
|
1911
|
+
};
|
|
1912
|
+
if (files[fieldname]) {
|
|
1913
|
+
const existing = files[fieldname];
|
|
1914
|
+
if (Array.isArray(existing)) {
|
|
1915
|
+
existing.push(jayFile);
|
|
1916
|
+
} else {
|
|
1917
|
+
files[fieldname] = [existing, jayFile];
|
|
1918
|
+
}
|
|
1919
|
+
} else {
|
|
1920
|
+
files[fieldname] = jayFile;
|
|
1921
|
+
}
|
|
1922
|
+
resolveWrite();
|
|
1923
|
+
});
|
|
1924
|
+
})
|
|
1925
|
+
);
|
|
1926
|
+
});
|
|
1927
|
+
bb.on("field", (fieldname, value) => {
|
|
1928
|
+
if (errored)
|
|
1929
|
+
return;
|
|
1930
|
+
if (fieldname === "_json") {
|
|
1931
|
+
try {
|
|
1932
|
+
jsonData = JSON.parse(value);
|
|
1933
|
+
} catch {
|
|
1934
|
+
errored = true;
|
|
1935
|
+
reject(new Error("Invalid JSON in _json field"));
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
});
|
|
1939
|
+
bb.on("close", () => {
|
|
1940
|
+
if (errored)
|
|
1941
|
+
return;
|
|
1942
|
+
Promise.all(pendingWrites).then(() => resolve({ body: { ...jsonData, ...files }, tempDir })).catch((err) => reject(err));
|
|
1943
|
+
});
|
|
1944
|
+
bb.on("error", (err) => {
|
|
1945
|
+
if (!errored) {
|
|
1946
|
+
errored = true;
|
|
1947
|
+
reject(err);
|
|
1948
|
+
}
|
|
1949
|
+
});
|
|
1950
|
+
req.pipe(bb);
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
function cleanupTempDir(dir) {
|
|
1954
|
+
try {
|
|
1955
|
+
fs$2.rmSync(dir, { recursive: true, force: true });
|
|
1956
|
+
} catch {
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
function actionBodyParser(options) {
|
|
1960
|
+
const { buildFolder, registry: reg } = options;
|
|
1961
|
+
const registryToUse = reg ?? actionRegistry;
|
|
1843
1962
|
return (req, res, next) => {
|
|
1844
1963
|
if (!req.path.startsWith(ACTION_ENDPOINT_BASE)) {
|
|
1845
1964
|
next();
|
|
@@ -1849,6 +1968,42 @@ function actionBodyParser() {
|
|
|
1849
1968
|
next();
|
|
1850
1969
|
return;
|
|
1851
1970
|
}
|
|
1971
|
+
const contentType = req.headers["content-type"] || "";
|
|
1972
|
+
if (contentType.startsWith("multipart/form-data")) {
|
|
1973
|
+
const actionName = req.path.slice(ACTION_ENDPOINT_BASE.length + 1);
|
|
1974
|
+
const action = registryToUse.get(actionName);
|
|
1975
|
+
if (!action?.acceptsFiles) {
|
|
1976
|
+
res.status(400).json({
|
|
1977
|
+
success: false,
|
|
1978
|
+
error: {
|
|
1979
|
+
code: "FILES_NOT_ACCEPTED",
|
|
1980
|
+
message: `Action '${actionName}' does not accept file uploads. Use .withFiles() on the action builder.`,
|
|
1981
|
+
isActionError: false
|
|
1982
|
+
}
|
|
1983
|
+
});
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
const requestId = crypto.randomUUID();
|
|
1987
|
+
const tempDir = path$1.join(buildFolder, ".tmp", "actions", requestId);
|
|
1988
|
+
const maxFileSize = action.fileOptions?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
1989
|
+
const maxFiles = action.fileOptions?.maxFiles ?? DEFAULT_MAX_FILES;
|
|
1990
|
+
parseMultipart(req, tempDir, maxFileSize, maxFiles).then(({ body: body2, tempDir: td }) => {
|
|
1991
|
+
req.body = body2;
|
|
1992
|
+
req._jayTempDir = td;
|
|
1993
|
+
next();
|
|
1994
|
+
}).catch((err) => {
|
|
1995
|
+
cleanupTempDir(tempDir);
|
|
1996
|
+
res.status(400).json({
|
|
1997
|
+
success: false,
|
|
1998
|
+
error: {
|
|
1999
|
+
code: "MULTIPART_PARSE_ERROR",
|
|
2000
|
+
message: err.message,
|
|
2001
|
+
isActionError: false
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
2004
|
+
});
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
1852
2007
|
let body = "";
|
|
1853
2008
|
req.setEncoding("utf8");
|
|
1854
2009
|
req.on("data", (chunk) => {
|
|
@@ -2018,8 +2173,10 @@ async function scanPluginRoutes(projectRoot, projectRoutes) {
|
|
|
2018
2173
|
);
|
|
2019
2174
|
continue;
|
|
2020
2175
|
}
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2176
|
+
const isLocalComponent = route.component.startsWith(".");
|
|
2177
|
+
const compPath = isLocalComponent ? path__default.resolve(plugin.pluginPath, route.component) : resolvePluginModule(plugin);
|
|
2178
|
+
const componentExport = isLocalComponent ? void 0 : route.component;
|
|
2179
|
+
pluginRoutes.push(createRoute(route.path, jayHtmlPath, compPath, componentExport));
|
|
2023
2180
|
getLogger().info(`[Routes] Plugin "${plugin.name}" provides route ${route.path}`);
|
|
2024
2181
|
}
|
|
2025
2182
|
}
|
|
@@ -2184,7 +2341,11 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
|
|
|
2184
2341
|
query
|
|
2185
2342
|
);
|
|
2186
2343
|
} else {
|
|
2187
|
-
const cachedEntry = await slowRenderCache.get(
|
|
2344
|
+
const cachedEntry = await slowRenderCache.get(
|
|
2345
|
+
route.jayHtmlPath,
|
|
2346
|
+
pageParams,
|
|
2347
|
+
getRouteDir(route)
|
|
2348
|
+
);
|
|
2188
2349
|
if (cachedEntry) {
|
|
2189
2350
|
await handleCachedRequest(
|
|
2190
2351
|
vite,
|
|
@@ -2300,6 +2461,7 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
|
|
|
2300
2461
|
pluginsForPage,
|
|
2301
2462
|
options,
|
|
2302
2463
|
routeToExpressRoute(route),
|
|
2464
|
+
getRouteDir(route),
|
|
2303
2465
|
cachedEntry.slowViewState,
|
|
2304
2466
|
timing,
|
|
2305
2467
|
cachedEntry.preRenderedContent,
|
|
@@ -2374,7 +2536,8 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
|
|
|
2374
2536
|
pageParams,
|
|
2375
2537
|
preRenderResult.preRenderedJayHtml,
|
|
2376
2538
|
renderedSlowly.rendered,
|
|
2377
|
-
carryForward
|
|
2539
|
+
carryForward,
|
|
2540
|
+
getRouteDir(route)
|
|
2378
2541
|
);
|
|
2379
2542
|
getLogger().info(`[SlowRender] Cached pre-rendered jay-html at ${cachedEntry.preRenderedPath}`);
|
|
2380
2543
|
await handleCachedRequest(
|
|
@@ -2495,9 +2658,8 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
|
|
|
2495
2658
|
res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
|
|
2496
2659
|
timing?.end();
|
|
2497
2660
|
}
|
|
2498
|
-
async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, slowViewState, timing, preLoadedContent, headTags) {
|
|
2661
|
+
async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, routePattern, routeDir, slowViewState, timing, preLoadedContent, headTags) {
|
|
2499
2662
|
let pageHtml;
|
|
2500
|
-
const routeDir = path__default.dirname(path__default.relative(options.pagesRootFolder, sourceJayHtmlPath));
|
|
2501
2663
|
try {
|
|
2502
2664
|
let jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
|
|
2503
2665
|
const jayHtmlFilename = path__default.basename(jayHtmlPath);
|
|
@@ -2571,13 +2733,13 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
|
|
|
2571
2733
|
const label = entry.name ? `"${entry.name}" (${freezeId})` : freezeId;
|
|
2572
2734
|
getLogger().info(`[Freeze] Serving frozen page ${label} for ${route.rawRoute} [${format}]`);
|
|
2573
2735
|
try {
|
|
2574
|
-
const
|
|
2736
|
+
const routeDir = getRouteDir(route);
|
|
2737
|
+
const cachedEntry = await slowRenderCache.get(route.jayHtmlPath, pageParams, routeDir);
|
|
2575
2738
|
const jayHtmlPath = cachedEntry?.preRenderedPath ?? route.jayHtmlPath;
|
|
2576
2739
|
const jayHtmlContent = cachedEntry?.preRenderedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
|
|
2577
2740
|
const jayHtmlFilename = path__default.basename(jayHtmlPath);
|
|
2578
2741
|
const jayHtmlDir = path__default.dirname(jayHtmlPath);
|
|
2579
2742
|
const sourceDir = path__default.dirname(route.jayHtmlPath);
|
|
2580
|
-
const routeDir = path__default.dirname(path__default.relative(options.pagesRootFolder, route.jayHtmlPath));
|
|
2581
2743
|
const { injectHeadfullFSTemplates: injectHeadfullFSTemplates2 } = await import("@jay-framework/compiler-jay-html");
|
|
2582
2744
|
const { JAY_IMPORT_RESOLVER: JAY_IMPORT_RESOLVER2 } = await import("@jay-framework/compiler-jay-html");
|
|
2583
2745
|
const fullJayHtml = injectHeadfullFSTemplates2(
|
|
@@ -2782,7 +2944,7 @@ async function mkDevServer(rawOptions) {
|
|
|
2782
2944
|
await lifecycleManager.initialize();
|
|
2783
2945
|
await materializeDynamicContracts(projectRootFolder, vite);
|
|
2784
2946
|
setupServiceHotReload(vite, lifecycleManager);
|
|
2785
|
-
setupActionRouter(vite);
|
|
2947
|
+
setupActionRouter(vite, buildFolder);
|
|
2786
2948
|
const projectRoutes = await initRoutes(pagesRootFolder);
|
|
2787
2949
|
const filteredProjectRoutes = buildFolder ? projectRoutes.filter((route) => !route.jayHtmlPath.startsWith(buildFolder)) : projectRoutes;
|
|
2788
2950
|
const pluginRoutes = await scanPluginRoutes(projectRootFolder, filteredProjectRoutes);
|
|
@@ -2864,8 +3026,8 @@ function setupServiceHotReload(vite, lifecycleManager) {
|
|
|
2864
3026
|
}
|
|
2865
3027
|
});
|
|
2866
3028
|
}
|
|
2867
|
-
function setupActionRouter(vite) {
|
|
2868
|
-
vite.middlewares.use(actionBodyParser());
|
|
3029
|
+
function setupActionRouter(vite, buildFolder) {
|
|
3030
|
+
vite.middlewares.use(actionBodyParser({ buildFolder }));
|
|
2869
3031
|
vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
|
|
2870
3032
|
getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
|
|
2871
3033
|
}
|
|
@@ -2898,6 +3060,28 @@ function setupFreezeEndpoint(vite, freezeStore) {
|
|
|
2898
3060
|
});
|
|
2899
3061
|
getLogger().info("[Freeze] Freeze endpoint mounted at /_jay/freeze");
|
|
2900
3062
|
}
|
|
3063
|
+
function getRouteDir(route) {
|
|
3064
|
+
return route.rawRoute.replace(/^\//, "") || "index";
|
|
3065
|
+
}
|
|
3066
|
+
function getRoutePrefix(jayHtmlPath, pagesRootFolder) {
|
|
3067
|
+
const rel = path__default.relative(pagesRootFolder, path__default.dirname(jayHtmlPath));
|
|
3068
|
+
const segments = rel.split(path__default.sep).filter(Boolean);
|
|
3069
|
+
const staticSegments = [];
|
|
3070
|
+
for (const seg of segments) {
|
|
3071
|
+
if (seg.startsWith("["))
|
|
3072
|
+
break;
|
|
3073
|
+
staticSegments.push(seg);
|
|
3074
|
+
}
|
|
3075
|
+
return "/" + staticSegments.join("/");
|
|
3076
|
+
}
|
|
3077
|
+
function sendPageReload(vite, jayHtmlPath, pagesRootFolder) {
|
|
3078
|
+
const routePrefix = getRoutePrefix(jayHtmlPath, pagesRootFolder);
|
|
3079
|
+
vite.ws.send({
|
|
3080
|
+
type: "custom",
|
|
3081
|
+
event: "jay:page-reload",
|
|
3082
|
+
data: { routePrefix }
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
2901
3085
|
function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectRootFolder) {
|
|
2902
3086
|
const watchedFiles = /* @__PURE__ */ new Set();
|
|
2903
3087
|
const watchLinkedFiles = (files) => {
|
|
@@ -2924,7 +3108,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectR
|
|
|
2924
3108
|
clearServerElementCache();
|
|
2925
3109
|
cache.clear().then(() => {
|
|
2926
3110
|
getLogger().info(`[SlowRender] Cache cleared (jay-html changed: ${changedPath})`);
|
|
2927
|
-
vite
|
|
3111
|
+
sendPageReload(vite, changedPath, pagesRootFolder);
|
|
2928
3112
|
});
|
|
2929
3113
|
return;
|
|
2930
3114
|
}
|
|
@@ -2939,7 +3123,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectR
|
|
|
2939
3123
|
getLogger().info(
|
|
2940
3124
|
`[SlowRender] Cache invalidated for ${jayHtmlPath} (page.ts changed)`
|
|
2941
3125
|
);
|
|
2942
|
-
vite
|
|
3126
|
+
sendPageReload(vite, jayHtmlPath, pagesRootFolder);
|
|
2943
3127
|
});
|
|
2944
3128
|
return;
|
|
2945
3129
|
}
|
|
@@ -2950,7 +3134,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectR
|
|
|
2950
3134
|
getLogger().info(
|
|
2951
3135
|
`[SlowRender] Cache invalidated for ${jayHtmlPath} (contract changed)`
|
|
2952
3136
|
);
|
|
2953
|
-
vite
|
|
3137
|
+
sendPageReload(vite, jayHtmlPath, pagesRootFolder);
|
|
2954
3138
|
});
|
|
2955
3139
|
return;
|
|
2956
3140
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/dev-server",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,23 +23,25 @@
|
|
|
23
23
|
"test:watch": "vitest"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@jay-framework/compiler-jay-stack": "^0.16.
|
|
27
|
-
"@jay-framework/compiler-shared": "^0.16.
|
|
28
|
-
"@jay-framework/component": "^0.16.
|
|
29
|
-
"@jay-framework/fullstack-component": "^0.16.
|
|
30
|
-
"@jay-framework/logger": "^0.16.
|
|
31
|
-
"@jay-framework/runtime": "^0.16.
|
|
32
|
-
"@jay-framework/stack-client-runtime": "^0.16.
|
|
33
|
-
"@jay-framework/stack-route-scanner": "^0.16.
|
|
34
|
-
"@jay-framework/stack-server-runtime": "^0.16.
|
|
35
|
-
"@jay-framework/view-state-merge": "^0.16.
|
|
26
|
+
"@jay-framework/compiler-jay-stack": "^0.16.2",
|
|
27
|
+
"@jay-framework/compiler-shared": "^0.16.2",
|
|
28
|
+
"@jay-framework/component": "^0.16.2",
|
|
29
|
+
"@jay-framework/fullstack-component": "^0.16.2",
|
|
30
|
+
"@jay-framework/logger": "^0.16.2",
|
|
31
|
+
"@jay-framework/runtime": "^0.16.2",
|
|
32
|
+
"@jay-framework/stack-client-runtime": "^0.16.2",
|
|
33
|
+
"@jay-framework/stack-route-scanner": "^0.16.2",
|
|
34
|
+
"@jay-framework/stack-server-runtime": "^0.16.2",
|
|
35
|
+
"@jay-framework/view-state-merge": "^0.16.2",
|
|
36
|
+
"busboy": "^1.6.0",
|
|
36
37
|
"vite": "^5.0.11"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@jay-framework/dev-environment": "^0.16.
|
|
40
|
-
"@jay-framework/jay-cli": "^0.16.
|
|
41
|
-
"@jay-framework/stack-client-runtime": "^0.16.
|
|
40
|
+
"@jay-framework/dev-environment": "^0.16.2",
|
|
41
|
+
"@jay-framework/jay-cli": "^0.16.2",
|
|
42
|
+
"@jay-framework/stack-client-runtime": "^0.16.2",
|
|
42
43
|
"@playwright/test": "^1.58.2",
|
|
44
|
+
"@types/busboy": "^1.5.4",
|
|
43
45
|
"@types/express": "^5.0.2",
|
|
44
46
|
"@types/node": "^22.15.21",
|
|
45
47
|
"jsdom": "^23.2.0",
|