@jay-framework/dev-server 0.15.4 → 0.15.6

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.
Files changed (2) hide show
  1. package/dist/index.js +85 -46
  2. package/package.json +14 -14
package/dist/index.js CHANGED
@@ -10,13 +10,12 @@ import { createRequire } from "module";
10
10
  import "@jay-framework/compiler-shared";
11
11
  import * as path from "node:path";
12
12
  import path__default from "node:path";
13
- import { JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, parseContract, slowRenderTransform, discoverHeadlessInstances, resolveHeadlessInstances } from "@jay-framework/compiler-jay-html";
13
+ import { JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, parseContract, slowRenderTransform, discoverHeadlessInstances, assignCoordinatesToJayHtml, resolveHeadlessInstances } from "@jay-framework/compiler-jay-html";
14
14
  import { getLogger, getDevLogger } from "@jay-framework/logger";
15
15
  import { createRequire as createRequire$1 } from "node:module";
16
16
  import * as fs from "node:fs";
17
- import crypto from "node:crypto";
18
17
  import { scanRoutes, routeToExpressRoute } from "@jay-framework/stack-route-scanner";
19
- import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, clearServerElementCache, getServiceRegistry, materializeContracts, loadPageParts, renderFastChangingData, generateClientScript, getClientInitData, generateSSRPageHtml, validateForEachInstances, slowRenderInstances } from "@jay-framework/stack-server-runtime";
18
+ import { discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, actionRegistry, discoverAndRegisterActions, discoverAllPluginActions, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, DevSlowlyChangingPhase, SlowRenderCache, preparePluginClientInits, clearServerElementCache, getServiceRegistry, materializeContracts, loadPageParts, renderFastChangingData, mergeHeadTags, generateClientScript, getClientInitData, generateSSRPageHtml, validateForEachInstances, slowRenderInstances } from "@jay-framework/stack-server-runtime";
20
19
  import fs$1 from "node:fs/promises";
21
20
  import { pathToFileURL } from "node:url";
22
21
  const s$1 = createRequire(import.meta.url), e$1 = s$1("typescript"), c$1 = new Proxy(e$1, {
@@ -1201,7 +1200,7 @@ function createPluginClientImportResolver(options = {}) {
1201
1200
  const pluginDetector = options.pluginDetector || createDefaultPluginDetector();
1202
1201
  return {
1203
1202
  name: "jay-stack:plugin-client-import",
1204
- enforce: "pre",
1203
+ enforce: "post",
1205
1204
  configResolved(config) {
1206
1205
  projectRoot = config.root || projectRoot;
1207
1206
  isSSRBuild = !!config.build?.ssr;
@@ -1366,8 +1365,6 @@ async function createViteServer(options) {
1366
1365
  clearScreen = true,
1367
1366
  httpServer
1368
1367
  } = options;
1369
- const instanceId = crypto.randomBytes(4).toString("hex");
1370
- const cacheDir = path__default.join(projectRoot, "node_modules", `.vite-${instanceId}`);
1371
1368
  const vite = await createServer({
1372
1369
  // Don't start HTTP server - we use middleware mode
1373
1370
  server: {
@@ -1383,8 +1380,6 @@ async function createViteServer(options) {
1383
1380
  base,
1384
1381
  // Root directory for module resolution
1385
1382
  root: pagesRoot,
1386
- // Isolate dep optimization cache per instance
1387
- cacheDir,
1388
1383
  // SSR configuration
1389
1384
  ssr: {
1390
1385
  // Mark jay-framework packages as external so Vite uses Node's require
@@ -1400,13 +1395,6 @@ async function createViteServer(options) {
1400
1395
  logLevel,
1401
1396
  clearScreen
1402
1397
  });
1403
- const originalClose = vite.close.bind(vite);
1404
- vite.close = async () => {
1405
- await originalClose();
1406
- const { rm } = await import("node:fs/promises");
1407
- await rm(cacheDir, { recursive: true, force: true }).catch(() => {
1408
- });
1409
- };
1410
1398
  return vite;
1411
1399
  }
1412
1400
  async function createViteForCli(options) {
@@ -1766,6 +1754,10 @@ function createActionRouter(options) {
1766
1754
  });
1767
1755
  return;
1768
1756
  }
1757
+ const timing = getDevLogger()?.startRequest(
1758
+ requestMethod,
1759
+ ACTION_ENDPOINT_BASE + "/" + actionName
1760
+ );
1769
1761
  const result = await registry.execute(actionName, input);
1770
1762
  if (requestMethod === "GET" && result.success) {
1771
1763
  const cacheHeaders = registry.getCacheHeaders(actionName);
@@ -1785,6 +1777,7 @@ function createActionRouter(options) {
1785
1777
  error: result.error
1786
1778
  });
1787
1779
  }
1780
+ timing?.end();
1788
1781
  };
1789
1782
  }
1790
1783
  function getStatusCodeForError(code, isActionError) {
@@ -1838,6 +1831,8 @@ function actionBodyParser() {
1838
1831
  });
1839
1832
  };
1840
1833
  }
1834
+ let _watchLinkedFiles = () => {
1835
+ };
1841
1836
  async function initRoutes(pagesBaseFolder) {
1842
1837
  return await scanRoutes(pagesBaseFolder, {
1843
1838
  jayHtmlFilename: "page.jay-html",
@@ -1861,7 +1856,9 @@ function defaults(options) {
1861
1856
  disableSSR: options.disableSSR,
1862
1857
  jayRollupConfig: {
1863
1858
  ...options.jayRollupConfig || {},
1864
- tsConfigFilePath
1859
+ tsConfigFilePath,
1860
+ pagesRoot: pagesRootFolder,
1861
+ buildFolder
1865
1862
  },
1866
1863
  httpServer: options.httpServer
1867
1864
  };
@@ -2001,7 +1998,14 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2001
1998
  timing?.end();
2002
1999
  return;
2003
2000
  }
2004
- const { parts: pageParts, clientTrackByMap, usedPackages } = pagePartsResult.val;
2001
+ const {
2002
+ parts: pageParts,
2003
+ clientTrackByMap,
2004
+ usedPackages,
2005
+ linkedCssFiles,
2006
+ linkedComponentFiles
2007
+ } = pagePartsResult.val;
2008
+ _watchLinkedFiles([...linkedCssFiles || [], ...linkedComponentFiles || []]);
2005
2009
  const pluginsForPage = filterPluginsForPage(
2006
2010
  allPluginClientInits,
2007
2011
  allPluginsWithInit,
@@ -2030,6 +2034,7 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2030
2034
  }
2031
2035
  const fastViewState = renderedFast.rendered;
2032
2036
  const fastCarryForward = renderedFast.carryForward;
2037
+ const headTags = renderedFast.headTags ?? mergeHeadTags(cachedEntry.carryForward?.__slowHeadTags ?? []);
2033
2038
  await sendResponse(
2034
2039
  vite,
2035
2040
  res,
@@ -2045,7 +2050,8 @@ async function handleCachedRequest(vite, route, options, cachedEntry, pageParams
2045
2050
  options,
2046
2051
  cachedEntry.slowViewState,
2047
2052
  timing,
2048
- cachedEntry.preRenderedContent
2053
+ cachedEntry.preRenderedContent,
2054
+ headTags
2049
2055
  );
2050
2056
  }
2051
2057
  async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRenderCache, pageParams, pageProps, allPluginClientInits, allPluginsWithInit, projectInit, res, url, timing, query = {}) {
@@ -2064,6 +2070,8 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
2064
2070
  timing?.end();
2065
2071
  return;
2066
2072
  }
2073
+ const { linkedCssFiles: initCss, linkedComponentFiles: initComps } = initialPartsResult.val;
2074
+ _watchLinkedFiles([...initCss || [], ...initComps || []]);
2067
2075
  const slowStart = Date.now();
2068
2076
  const renderedSlowly = await slowlyPhase.runSlowlyForPage(
2069
2077
  pageParams,
@@ -2081,11 +2089,13 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
2081
2089
  timing?.end();
2082
2090
  return;
2083
2091
  }
2092
+ const partKeys = initialPartsResult.val.parts.map((p) => p.key).filter((k) => !!k);
2084
2093
  const preRenderResult = await preRenderJayHtml(
2085
2094
  route,
2086
2095
  renderedSlowly.rendered,
2087
2096
  initialPartsResult.val.headlessContracts,
2088
- initialPartsResult.val.headlessInstanceComponents
2097
+ initialPartsResult.val.headlessInstanceComponents,
2098
+ partKeys
2089
2099
  );
2090
2100
  timing?.recordSlowRender(Date.now() - slowStart);
2091
2101
  if (!preRenderResult) {
@@ -2154,8 +2164,11 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2154
2164
  usedPackages,
2155
2165
  headlessInstanceComponents,
2156
2166
  discoveredInstances,
2157
- forEachInstances
2167
+ forEachInstances,
2168
+ linkedCssFiles,
2169
+ linkedComponentFiles
2158
2170
  } = pagePartsResult.val;
2171
+ _watchLinkedFiles([...linkedCssFiles || [], ...linkedComponentFiles || []]);
2159
2172
  const pluginsForPage = filterPluginsForPage(
2160
2173
  allPluginClientInits,
2161
2174
  allPluginsWithInit,
@@ -2229,7 +2242,7 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
2229
2242
  res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
2230
2243
  timing?.end();
2231
2244
  }
2232
- async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState, timing, preLoadedContent) {
2245
+ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, pageParts, viewState, carryForward, clientTrackByMap, projectInit, pluginsForPage, options, slowViewState, timing, preLoadedContent, headTags) {
2233
2246
  let pageHtml;
2234
2247
  const routeDir = path__default.dirname(path__default.relative(options.pagesRootFolder, sourceJayHtmlPath));
2235
2248
  try {
@@ -2258,7 +2271,10 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
2258
2271
  {
2259
2272
  enableAutomation: !options.disableAutomation,
2260
2273
  slowViewState
2261
- }
2274
+ },
2275
+ // Pass source directory for headfull FS file resolution when using pre-rendered path
2276
+ jayHtmlDir !== sourceDir ? sourceDir : void 0,
2277
+ headTags
2262
2278
  );
2263
2279
  } catch (err) {
2264
2280
  getLogger().warn(`[SSR] Failed, falling back to client rendering: ${err.message}`);
@@ -2289,7 +2305,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
2289
2305
  res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
2290
2306
  timing?.end();
2291
2307
  }
2292
- async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents) {
2308
+ async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = []) {
2293
2309
  const jayHtmlContent = await fs$1.readFile(route.jayHtmlPath, "utf-8");
2294
2310
  const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
2295
2311
  let contract;
@@ -2310,7 +2326,8 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2310
2326
  return void 0;
2311
2327
  }
2312
2328
  }
2313
- if (!contract && slowViewState && Object.keys(slowViewState).length > 0) {
2329
+ const hasPageLevelSlowData = slowViewState && Object.keys(slowViewState).some((k) => !partKeys.includes(k));
2330
+ if (!contract && hasPageLevelSlowData) {
2314
2331
  getLogger().warn(
2315
2332
  `[SlowRender] Page ${route.jayHtmlPath} has slow ViewState but no contract. Without a contract, slow bindings cannot be resolved. Move data to withFastRender or add a .jay-contract file with phase annotations.`
2316
2333
  );
@@ -2342,10 +2359,15 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2342
2359
  let forEachInstances;
2343
2360
  if (headlessInstanceComponents.length > 0) {
2344
2361
  const discoveryResult = discoverHeadlessInstances(preRenderedJayHtml);
2345
- preRenderedJayHtml = discoveryResult.preRenderedJayHtml;
2346
- if (discoveryResult.forEachInstances.length > 0) {
2362
+ const htmlWithRefs = discoveryResult.preRenderedJayHtml;
2363
+ const headlessContractNameSet = new Set(
2364
+ headlessInstanceComponents.map((c2) => c2.contractName)
2365
+ );
2366
+ preRenderedJayHtml = assignCoordinatesToJayHtml(htmlWithRefs, headlessContractNameSet);
2367
+ const finalDiscovery = discoverHeadlessInstances(preRenderedJayHtml);
2368
+ if (finalDiscovery.forEachInstances.length > 0) {
2347
2369
  const validationErrors = validateForEachInstances(
2348
- discoveryResult.forEachInstances,
2370
+ finalDiscovery.forEachInstances,
2349
2371
  headlessInstanceComponents
2350
2372
  );
2351
2373
  if (validationErrors.length > 0) {
@@ -2354,11 +2376,11 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
2354
2376
  );
2355
2377
  return void 0;
2356
2378
  }
2357
- forEachInstances = discoveryResult.forEachInstances;
2379
+ forEachInstances = finalDiscovery.forEachInstances;
2358
2380
  }
2359
- if (discoveryResult.instances.length > 0) {
2381
+ if (finalDiscovery.instances.length > 0) {
2360
2382
  const slowResult = await slowRenderInstances(
2361
- discoveryResult.instances,
2383
+ finalDiscovery.instances,
2362
2384
  headlessInstanceComponents
2363
2385
  );
2364
2386
  if (slowResult) {
@@ -2430,16 +2452,8 @@ async function mkDevServer(rawOptions) {
2430
2452
  const options = defaults(rawOptions);
2431
2453
  const { publicBaseUrlPath, pagesRootFolder, projectRootFolder, buildFolder, jayRollupConfig } = options;
2432
2454
  if (buildFolder) {
2433
- try {
2434
- const entries = await fs$1.readdir(buildFolder);
2435
- for (const entry of entries) {
2436
- if (entry !== "pre-rendered") {
2437
- await fs$1.rm(path__default.join(buildFolder, entry), { recursive: true, force: true }).catch(() => {
2438
- });
2439
- }
2440
- }
2441
- } catch {
2442
- }
2455
+ await fs$1.rm(buildFolder, { recursive: true, force: true }).catch(() => {
2456
+ });
2443
2457
  }
2444
2458
  const viteLogLevel = options.logLevel === "silent" ? "silent" : options.logLevel === "verbose" ? "info" : "warn";
2445
2459
  const lifecycleManager = new ServiceLifecycleManager(projectRootFolder);
@@ -2462,7 +2476,11 @@ async function mkDevServer(rawOptions) {
2462
2476
  const slowlyPhase = new DevSlowlyChangingPhase();
2463
2477
  const slowRenderCacheDir = path__default.join(buildFolder, "pre-rendered");
2464
2478
  const slowRenderCache = new SlowRenderCache(slowRenderCacheDir, pagesRootFolder);
2465
- setupSlowRenderCacheInvalidation(vite, slowRenderCache, pagesRootFolder);
2479
+ _watchLinkedFiles = setupSlowRenderCacheInvalidation(
2480
+ vite,
2481
+ slowRenderCache,
2482
+ pagesRootFolder
2483
+ );
2466
2484
  const projectInit = lifecycleManager.getProjectInit() ?? void 0;
2467
2485
  const pluginsWithInit = lifecycleManager.getPluginsWithInit();
2468
2486
  const pluginClientInits = preparePluginClientInits(pluginsWithInit);
@@ -2521,19 +2539,39 @@ function setupActionRouter(vite) {
2521
2539
  vite.middlewares.use(ACTION_ENDPOINT_BASE, createActionRouter());
2522
2540
  getLogger().info(`[Actions] Action router mounted at ${ACTION_ENDPOINT_BASE}`);
2523
2541
  }
2524
- function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
2542
+ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder, projectRootFolder) {
2543
+ const watchedFiles = /* @__PURE__ */ new Set();
2544
+ const watchLinkedFiles = (files) => {
2545
+ for (const file of files) {
2546
+ if (watchedFiles.has(file))
2547
+ continue;
2548
+ watchedFiles.add(file);
2549
+ vite.watcher.add(file);
2550
+ getLogger().info(`[SlowRender] Watching: ${file}`);
2551
+ }
2552
+ };
2525
2553
  vite.watcher.on("change", (changedPath) => {
2526
- if (!changedPath.startsWith(pagesRootFolder)) {
2554
+ if (watchedFiles.has(changedPath)) {
2555
+ clearServerElementCache();
2556
+ cache.clear().then(() => {
2557
+ getLogger().info(
2558
+ `[SlowRender] Cache cleared (linked file changed: ${changedPath})`
2559
+ );
2560
+ vite.ws.send({ type: "full-reload" });
2561
+ });
2527
2562
  return;
2528
2563
  }
2529
- if (changedPath.endsWith(".jay-html")) {
2564
+ if (changedPath.endsWith(".jay-html") && changedPath.startsWith(pagesRootFolder)) {
2530
2565
  clearServerElementCache();
2531
- cache.invalidate(changedPath).then(() => {
2532
- getLogger().info(`[SlowRender] Cache invalidated for ${changedPath}`);
2566
+ cache.clear().then(() => {
2567
+ getLogger().info(`[SlowRender] Cache cleared (jay-html changed: ${changedPath})`);
2533
2568
  vite.ws.send({ type: "full-reload" });
2534
2569
  });
2535
2570
  return;
2536
2571
  }
2572
+ if (!changedPath.startsWith(pagesRootFolder)) {
2573
+ return;
2574
+ }
2537
2575
  if (changedPath.endsWith("page.ts")) {
2538
2576
  const dir = path__default.dirname(changedPath);
2539
2577
  const jayHtmlPath = path__default.join(dir, "page.jay-html");
@@ -2558,6 +2596,7 @@ function setupSlowRenderCacheInvalidation(vite, cache, pagesRootFolder) {
2558
2596
  return;
2559
2597
  }
2560
2598
  });
2599
+ return watchLinkedFiles;
2561
2600
  }
2562
2601
  export {
2563
2602
  ACTION_ENDPOINT_BASE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.15.4",
3
+ "version": "0.15.6",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -23,22 +23,22 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@jay-framework/compiler-jay-stack": "^0.15.4",
27
- "@jay-framework/compiler-shared": "^0.15.4",
28
- "@jay-framework/component": "^0.15.4",
29
- "@jay-framework/fullstack-component": "^0.15.4",
30
- "@jay-framework/logger": "^0.15.4",
31
- "@jay-framework/runtime": "^0.15.4",
32
- "@jay-framework/stack-client-runtime": "^0.15.4",
33
- "@jay-framework/stack-route-scanner": "^0.15.4",
34
- "@jay-framework/stack-server-runtime": "^0.15.4",
35
- "@jay-framework/view-state-merge": "^0.15.4",
26
+ "@jay-framework/compiler-jay-stack": "^0.15.6",
27
+ "@jay-framework/compiler-shared": "^0.15.6",
28
+ "@jay-framework/component": "^0.15.6",
29
+ "@jay-framework/fullstack-component": "^0.15.6",
30
+ "@jay-framework/logger": "^0.15.6",
31
+ "@jay-framework/runtime": "^0.15.6",
32
+ "@jay-framework/stack-client-runtime": "^0.15.6",
33
+ "@jay-framework/stack-route-scanner": "^0.15.6",
34
+ "@jay-framework/stack-server-runtime": "^0.15.6",
35
+ "@jay-framework/view-state-merge": "^0.15.6",
36
36
  "vite": "^5.0.11"
37
37
  },
38
38
  "devDependencies": {
39
- "@jay-framework/dev-environment": "^0.15.4",
40
- "@jay-framework/jay-cli": "^0.15.4",
41
- "@jay-framework/stack-client-runtime": "^0.15.4",
39
+ "@jay-framework/dev-environment": "^0.15.6",
40
+ "@jay-framework/jay-cli": "^0.15.6",
41
+ "@jay-framework/stack-client-runtime": "^0.15.6",
42
42
  "@playwright/test": "^1.58.2",
43
43
  "@types/express": "^5.0.2",
44
44
  "@types/node": "^22.15.21",