@jay-framework/dev-server 0.24.2 → 0.24.4

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 +84 -30
  2. package/package.json +15 -15
package/dist/index.js CHANGED
@@ -1616,6 +1616,11 @@ var __defProp2 = Object.defineProperty;
1616
1616
  var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1617
1617
  var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
1618
1618
  const require$3 = createRequire(import.meta.url);
1619
+ function resolveClientImportPath(route) {
1620
+ if (route.clientCompPath) return route.clientCompPath;
1621
+ if (route.componentExport) return route.compPath.replace(/index\.js$/, "index.client.js");
1622
+ return route.compPath;
1623
+ }
1619
1624
  async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfig, options) {
1620
1625
  const exists = await fs__default.access(route.compPath, fs__default.constants.F_OK).then(() => true).catch(() => false);
1621
1626
  const parts = [];
@@ -1628,7 +1633,7 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
1628
1633
  `${route.compPath} does not export "${exportName}". The file exists but the expected export is missing — remove the file or add the export.`
1629
1634
  );
1630
1635
  } else {
1631
- const clientImportPath = route.componentExport ? route.compPath.replace(/index\.js$/, "index.client.js") : route.compPath;
1636
+ const clientImportPath = resolveClientImportPath(route);
1632
1637
  parts.push({
1633
1638
  compDefinition: pageComponent,
1634
1639
  clientImport: `import {${exportName}} from '${clientImportPath}'`,
@@ -1913,7 +1918,7 @@ function normalizeActionEntry(entry) {
1913
1918
  if (typeof entry === "string") {
1914
1919
  return { name: entry };
1915
1920
  }
1916
- return { name: entry.name, action: entry.action };
1921
+ return { name: entry.name, action: entry.action, devOnly: entry.devOnly };
1917
1922
  }
1918
1923
  function loadPluginManifest(pluginDir) {
1919
1924
  const pluginYamlPath = path$1.join(pluginDir, "plugin.yaml");
@@ -1995,6 +2000,18 @@ ${clientParts.map((part) => " " + part.clientPart).join(",\n")}
1995
2000
  slowViewStateDecl
1996
2001
  };
1997
2002
  }
2003
+ function buildPageReloadHmrScript() {
2004
+ return `
2005
+ if (import.meta.hot) {
2006
+ import.meta.hot.on('jay:page-reload', (data) => {
2007
+ const prefix = data.routePrefix;
2008
+ const pathname = window.location.pathname;
2009
+ if (pathname === prefix || pathname.startsWith(prefix + '/')) {
2010
+ window.location.reload();
2011
+ }
2012
+ });
2013
+ }`;
2014
+ }
1998
2015
  function buildFreezeScript(routePattern) {
1999
2016
  const routePatternLiteral = routePattern ? `'${routePattern}'` : "undefined";
2000
2017
  return `
@@ -2070,15 +2087,7 @@ function buildFreezeScript(routePattern) {
2070
2087
  }
2071
2088
 
2072
2089
  // Targeted page reload: only reload if this page matches the changed route prefix
2073
- if (import.meta.hot) {
2074
- import.meta.hot.on('jay:page-reload', (data) => {
2075
- const prefix = data.routePrefix;
2076
- const pathname = window.location.pathname;
2077
- if (pathname === prefix || pathname.startsWith(prefix + '/')) {
2078
- window.location.reload();
2079
- }
2080
- });
2081
- }`;
2090
+ ${buildPageReloadHmrScript()}`;
2082
2091
  }
2083
2092
  function buildAutomationWrap(options, mode) {
2084
2093
  const { enableAutomation = true, slowViewState } = options;
@@ -2312,7 +2321,7 @@ ${titleTag}${headExtras ? headExtras + "\n" : ""} </head>
2312
2321
  </body>
2313
2322
  </html>`;
2314
2323
  }
2315
- async function generateFrozenPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir, format = "page", freezeName) {
2324
+ async function generateFrozenPageHtml(vite, jayHtmlContent, jayHtmlFilename, jayHtmlDir, viewState, buildFolder, projectRoot, routeDir, tsConfigFilePath, sourceDir, format = "page", freezeName, options) {
2316
2325
  const jayHtmlPath = path__default.join(jayHtmlDir, jayHtmlFilename);
2317
2326
  let cached = serverModuleCache.get(jayHtmlPath);
2318
2327
  if (!cached) {
@@ -2358,6 +2367,9 @@ ${ssrHtml}`;
2358
2367
  const cssLink = cached.cssHref ? ` <link rel="stylesheet" href="${cached.cssHref}" />` : "";
2359
2368
  const headExtras = [cssLink, frozenHeadTagsHtml].filter((_) => _).join("\n");
2360
2369
  const label = freezeName ? ` — ${freezeName}` : "";
2370
+ const devHmrScript = options?.injectDevHmr === true ? `
2371
+ <script type="module">${buildPageReloadHmrScript()}
2372
+ <\/script>` : "";
2361
2373
  return `<!doctype html>
2362
2374
  <html lang="en">
2363
2375
  <head>
@@ -2381,7 +2393,7 @@ ${headExtras ? headExtras + "\n" : ""} <style>
2381
2393
  </style>
2382
2394
  </head>
2383
2395
  <body>
2384
- <div id="target">${ssrHtml}</div>
2396
+ <div id="target">${ssrHtml}</div>${devHmrScript}
2385
2397
  </body>
2386
2398
  </html>`;
2387
2399
  }
@@ -2762,9 +2774,18 @@ async function registerNpmPluginActions(packageName, pluginConfig, pluginDir, re
2762
2774
  } else {
2763
2775
  pluginModule = await import(packageName);
2764
2776
  }
2777
+ let toolsModule;
2778
+ const loadToolsModule = async () => {
2779
+ if (!toolsModule) {
2780
+ const toolsEntry = `${packageName}/tools`;
2781
+ toolsModule = viteServer ? await viteServer.ssrLoadModule(toolsEntry) : await import(toolsEntry);
2782
+ }
2783
+ return toolsModule;
2784
+ };
2765
2785
  for (const entry of pluginConfig.actions) {
2766
- const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
2767
- const actionExport = pluginModule[actionName];
2786
+ const { name: actionName, action: actionPath, devOnly } = normalizeActionEntry(entry);
2787
+ const sourceModule = devOnly ? await loadToolsModule() : pluginModule;
2788
+ const actionExport = sourceModule[actionName];
2768
2789
  if (actionExport && isJayAction(actionExport)) {
2769
2790
  registry.register(actionExport);
2770
2791
  const registeredName = actionExport.actionName;
@@ -2836,6 +2857,13 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
2836
2857
  return [];
2837
2858
  }
2838
2859
  }
2860
+ const resolveLocalToolsModulePath = () => {
2861
+ for (const candidate of ["tools.ts", "tools.js"]) {
2862
+ const candidatePath = path.join(pluginPath, candidate);
2863
+ if (fs.existsSync(candidatePath)) return candidatePath;
2864
+ }
2865
+ return null;
2866
+ };
2839
2867
  try {
2840
2868
  let pluginModule;
2841
2869
  if (viteServer) {
@@ -2843,9 +2871,23 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
2843
2871
  } else {
2844
2872
  pluginModule = await import(modulePath);
2845
2873
  }
2874
+ let toolsModule;
2875
+ const loadToolsModule = async () => {
2876
+ if (toolsModule) return toolsModule;
2877
+ const toolsPath = resolveLocalToolsModulePath();
2878
+ if (!toolsPath) {
2879
+ getLogger().warn(
2880
+ `[Actions] Plugin "${pluginName}" declares a devOnly action but has no tools.ts/tools.js module`
2881
+ );
2882
+ return void 0;
2883
+ }
2884
+ toolsModule = viteServer ? await viteServer.ssrLoadModule(toolsPath) : await import(toolsPath);
2885
+ return toolsModule;
2886
+ };
2846
2887
  for (const entry of pluginConfig.actions) {
2847
- const { name: actionName, action: actionPath } = normalizeActionEntry(entry);
2848
- const actionExport = pluginModule[actionName];
2888
+ const { name: actionName, action: actionPath, devOnly } = normalizeActionEntry(entry);
2889
+ const sourceModule = devOnly ? await loadToolsModule() : pluginModule;
2890
+ const actionExport = sourceModule?.[actionName];
2849
2891
  if (actionExport && isJayAction(actionExport)) {
2850
2892
  registry.register(actionExport);
2851
2893
  const registeredName = actionExport.actionName;
@@ -4212,11 +4254,19 @@ async function scanPluginRoutes(projectRoot, projectRoutes) {
4212
4254
  continue;
4213
4255
  }
4214
4256
  const isLocalComponent = route.component.startsWith(".");
4215
- const compPath = isLocalComponent ? path__default.resolve(plugin.pluginPath, route.component) : resolvePluginModule(plugin);
4257
+ const compPath = isLocalComponent ? path__default.resolve(plugin.pluginPath, route.component) : resolvePluginModule(plugin, route.devOnly === true ? "./tools" : ".");
4216
4258
  const componentExport = isLocalComponent ? void 0 : route.component;
4259
+ const clientCompPath = isLocalComponent ? void 0 : resolvePluginExport(plugin.pluginPath, "./client");
4260
+ if (!isLocalComponent && !clientCompPath) {
4261
+ getLogger().error(
4262
+ `[Routes] Plugin "${plugin.name}" route ${route.path}: no "./client" export — hydration cannot load a browser-safe bundle. Add "./client" to package.json exports.`
4263
+ );
4264
+ continue;
4265
+ }
4217
4266
  pluginRoutes.push(
4218
4267
  createRoute(route.path, jayHtmlPath, compPath, componentExport, {
4219
- devOnly: route.devOnly === true
4268
+ devOnly: route.devOnly === true,
4269
+ clientCompPath
4220
4270
  })
4221
4271
  );
4222
4272
  getLogger().info(
@@ -4254,12 +4304,12 @@ function resolvePluginExport(pluginPath, exportSubpath) {
4254
4304
  }
4255
4305
  return void 0;
4256
4306
  }
4257
- function resolvePluginModule(plugin) {
4307
+ function resolvePluginModule(plugin, exportKey = ".") {
4258
4308
  const pkgJsonPath = path__default.join(plugin.pluginPath, "package.json");
4259
4309
  if (fs__default$1.existsSync(pkgJsonPath)) {
4260
4310
  try {
4261
4311
  const pkg = JSON.parse(fs__default$1.readFileSync(pkgJsonPath, "utf-8"));
4262
- const mainExport = pkg.exports?.["."];
4312
+ const mainExport = pkg.exports?.[exportKey];
4263
4313
  const mainPath = typeof mainExport === "string" ? mainExport : mainExport?.default || mainExport?.import || pkg.main;
4264
4314
  if (mainPath) {
4265
4315
  const resolved = path__default.join(plugin.pluginPath, mainPath);
@@ -4376,6 +4426,7 @@ function mkRoute(route, vite, slowlyPhase, options, slowRenderCache, freezeStore
4376
4426
  pageParams,
4377
4427
  query["format"] === "fragment" ? "fragment" : "page",
4378
4428
  res,
4429
+ url,
4379
4430
  timing
4380
4431
  );
4381
4432
  return;
@@ -4582,9 +4633,7 @@ async function handlePreRenderRequest(vite, route, options, slowlyPhase, slowRen
4582
4633
  );
4583
4634
  if (renderedSlowly.kind !== "PhaseOutput") {
4584
4635
  timing?.recordSlowRender(Date.now() - slowStart);
4585
- if (renderedSlowly.kind === "ClientError") {
4586
- handleOtherResponseCodes(res, renderedSlowly);
4587
- }
4636
+ handleOtherResponseCodes(res, renderedSlowly);
4588
4637
  timing?.end();
4589
4638
  return;
4590
4639
  }
@@ -4694,9 +4743,7 @@ async function handleClientOnlyRequest(vite, route, options, slowlyPhase, pagePa
4694
4743
  );
4695
4744
  if (renderedSlowly.kind !== "PhaseOutput") {
4696
4745
  timing?.recordSlowRender(Date.now() - slowStart);
4697
- if (renderedSlowly.kind === "ClientError") {
4698
- handleOtherResponseCodes(res, renderedSlowly);
4699
- }
4746
+ handleOtherResponseCodes(res, renderedSlowly);
4700
4747
  timing?.end();
4701
4748
  return;
4702
4749
  }
@@ -4828,7 +4875,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
4828
4875
  res.status(200).set({ "Content-Type": "text/html" }).send(compiledPageHtml);
4829
4876
  timing?.end();
4830
4877
  }
4831
- async function handleFrozenRequest(vite, route, options, freezeStore, slowRenderCache, freezeId, pageParams, format, res, timing) {
4878
+ async function handleFrozenRequest(vite, route, options, freezeStore, slowRenderCache, freezeId, pageParams, format, res, url, timing) {
4832
4879
  const entry = await freezeStore.get(freezeId);
4833
4880
  if (!entry) {
4834
4881
  getLogger().warn(`[Freeze] Freeze "${freezeId}" not found`);
@@ -4851,6 +4898,7 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
4851
4898
  sourceDir,
4852
4899
  JAY_IMPORT_RESOLVER
4853
4900
  );
4901
+ const injectDevHmr = format === "page";
4854
4902
  const html = await generateFrozenPageHtml(
4855
4903
  vite,
4856
4904
  fullJayHtml,
@@ -4863,13 +4911,19 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
4863
4911
  options.jayRollupConfig?.tsConfigFilePath,
4864
4912
  sourceDir,
4865
4913
  format,
4866
- entry.name
4914
+ entry.name,
4915
+ injectDevHmr ? { injectDevHmr: true } : void 0
4867
4916
  );
4868
4917
  const headers = { "Content-Type": "text/html" };
4869
4918
  if (format === "fragment") {
4870
4919
  headers["Access-Control-Allow-Origin"] = "*";
4871
4920
  }
4872
- res.status(200).set(headers).send(html);
4921
+ if (injectDevHmr) {
4922
+ const compiledHtml = await vite.transformIndexHtml(url || "/", html);
4923
+ res.status(200).set(headers).send(compiledHtml);
4924
+ } else {
4925
+ res.status(200).set(headers).send(html);
4926
+ }
4873
4927
  } catch (err) {
4874
4928
  getLogger().warn(`[Freeze] Failed to render frozen page: ${err.message}`);
4875
4929
  res.status(500).send(`Failed to render frozen page: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.24.2",
3
+ "version": "0.24.4",
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.24.2",
27
- "@jay-framework/compiler-shared": "^0.24.2",
28
- "@jay-framework/component": "^0.24.2",
29
- "@jay-framework/fullstack-component": "^0.24.2",
30
- "@jay-framework/logger": "^0.24.2",
31
- "@jay-framework/runtime": "^0.24.2",
32
- "@jay-framework/stack-client-runtime": "^0.24.2",
33
- "@jay-framework/stack-route-scanner": "^0.24.2",
34
- "@jay-framework/stack-server-build": "^0.24.2",
35
- "@jay-framework/stack-server-runtime": "^0.24.2",
36
- "@jay-framework/view-state-merge": "^0.24.2",
26
+ "@jay-framework/compiler-jay-stack": "^0.24.4",
27
+ "@jay-framework/compiler-shared": "^0.24.4",
28
+ "@jay-framework/component": "^0.24.4",
29
+ "@jay-framework/fullstack-component": "^0.24.4",
30
+ "@jay-framework/logger": "^0.24.4",
31
+ "@jay-framework/runtime": "^0.24.4",
32
+ "@jay-framework/stack-client-runtime": "^0.24.4",
33
+ "@jay-framework/stack-route-scanner": "^0.24.4",
34
+ "@jay-framework/stack-server-build": "^0.24.4",
35
+ "@jay-framework/stack-server-runtime": "^0.24.4",
36
+ "@jay-framework/view-state-merge": "^0.24.4",
37
37
  "busboy": "^1.6.0",
38
38
  "vite": "^5.0.11"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.24.2",
42
- "@jay-framework/jay-cli": "^0.24.2",
43
- "@jay-framework/stack-client-runtime": "^0.24.2",
41
+ "@jay-framework/dev-environment": "^0.24.4",
42
+ "@jay-framework/jay-cli": "^0.24.4",
43
+ "@jay-framework/stack-client-runtime": "^0.24.4",
44
44
  "@playwright/test": "^1.58.2",
45
45
  "@types/busboy": "^1.5.4",
46
46
  "@types/express": "^5.0.2",