@jay-framework/dev-server 0.24.6 → 0.25.0

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 +205 -26
  2. package/package.json +15 -15
package/dist/index.js CHANGED
@@ -7,13 +7,13 @@ import { createRequire } from "module";
7
7
  import "@jay-framework/compiler-shared";
8
8
  import * as path from "node:path";
9
9
  import path__default from "node:path";
10
- import { parseJayFile, JAY_IMPORT_RESOLVER, injectHeadfullFSTemplates, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction, generateServerElementFile, parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
10
+ import { parseJayFile, JAY_IMPORT_RESOLVER, discoverHeadlessInstances, assignCoordinatesToJayHtml, parseAction, generateServerElementFile, parseContract, slowRenderTransform } from "@jay-framework/compiler-jay-html";
11
11
  import { getLogger, getDevLogger } from "@jay-framework/logger";
12
12
  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 { actionRegistry, scanPlugins, serializeHeadTags, mergeHeadTags, discoverPluginsWithInit, sortPluginsByDependencies, executePluginServerInits, runInitCallbacks, runShutdownCallbacks, clearLifecycleCallbacks, clearServiceRegistry, clearClientInitData, runLoadParams, DevSlowlyChangingPhase, preparePluginClientInits, registerService, getServiceRegistry, parseCookies, renderFastChangingData, getClientInitData, slowRenderInstances, normalizeAndResolveInstanceProps } from "@jay-framework/stack-server-runtime";
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";
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";
@@ -22,6 +22,7 @@ import path$1 from "path";
22
22
  import YAML from "yaml";
23
23
  import crypto, { randomUUID } from "node:crypto";
24
24
  import { isJayAction, isJayStreamAction, createJayService } from "@jay-framework/fullstack-component";
25
+ import { parse } from "node-html-parser";
25
26
  import { pathToFileURL } from "node:url";
26
27
  import Busboy from "busboy";
27
28
  import crypto$1 from "crypto";
@@ -1667,7 +1668,14 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
1667
1668
  let clientModuleImport;
1668
1669
  let isNpmPackage;
1669
1670
  if (headlessImport.structural) {
1670
- continue;
1671
+ compDefinition = makePassthroughInstanceComponent(
1672
+ (headlessImport.contract?.tags ?? []).map((t) => ({
1673
+ name: t.tag,
1674
+ phase: t.phase
1675
+ }))
1676
+ );
1677
+ isNpmPackage = false;
1678
+ clientModuleImport = "";
1671
1679
  } else {
1672
1680
  const modulePath = isLocalModule ? path__default.resolve(dirName, module) : require$3.resolve(module, { paths: require$3.resolve.paths(dirName) });
1673
1681
  compDefinition = (await vite.ssrLoadModule(modulePath))[name];
@@ -1708,11 +1716,7 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
1708
1716
  contract: hi.contract,
1709
1717
  contractPath: hi.contractPath
1710
1718
  }));
1711
- const jayHtmlForDiscovery = injectHeadfullFSTemplates(
1712
- jayHtmlSource,
1713
- dirName,
1714
- JAY_IMPORT_RESOLVER
1715
- );
1719
+ const jayHtmlForDiscovery = jayHtmlSource;
1716
1720
  let discoveryResult;
1717
1721
  if (headlessInstanceComponents.length > 0) {
1718
1722
  const firstDiscovery = discoverHeadlessInstances(jayHtmlForDiscovery);
@@ -1749,6 +1753,8 @@ async function loadPageParts(vite, route, pagesBase, projectBase, jayRollupConfi
1749
1753
  var __defProp22 = Object.defineProperty;
1750
1754
  var __defNormalProp22 = (obj, key, value) => key in obj ? __defProp22(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1751
1755
  var __publicField22 = (obj, key, value) => __defNormalProp22(obj, key + "", value);
1756
+ const JAY_EXTENSION = ".jay-html";
1757
+ const JAY_CONTRACT_EXTENSION = ".jay-contract";
1752
1758
  class JayAtomicType {
1753
1759
  constructor(name) {
1754
1760
  __publicField22(this, "kind", 0);
@@ -2927,6 +2933,169 @@ async function discoverPluginActions(pluginPath, projectRoot, registry = actionR
2927
2933
  }
2928
2934
  return registeredActions;
2929
2935
  }
2936
+ function readTemplateMeta(jayHtmlPath) {
2937
+ let content;
2938
+ try {
2939
+ content = fs.readFileSync(jayHtmlPath, "utf-8");
2940
+ } catch {
2941
+ return {};
2942
+ }
2943
+ const root = parse(content);
2944
+ const ref = root.querySelector('script[type="application/jay-data"]')?.getAttribute("contract");
2945
+ const contractRef = ref ? path.resolve(path.dirname(jayHtmlPath), ref) : void 0;
2946
+ const rawTitle = root.querySelector("title")?.text?.trim();
2947
+ const staticTitle = rawTitle ? rawTitle.replace(/\{[^}]*\}/g, "").trim() : "";
2948
+ return { contractRef, title: staticTitle.length > 0 ? staticTitle : void 0 };
2949
+ }
2950
+ function listTemplatesForContractFile(contractFile) {
2951
+ if (!contractFile) return [];
2952
+ const dir = path.dirname(contractFile);
2953
+ const target = path.resolve(contractFile);
2954
+ const contractBasename = path.basename(contractFile, JAY_CONTRACT_EXTENSION);
2955
+ let names;
2956
+ try {
2957
+ names = fs.readdirSync(dir);
2958
+ } catch {
2959
+ return [];
2960
+ }
2961
+ const variants = [];
2962
+ for (const f of names) {
2963
+ if (!f.endsWith(JAY_EXTENSION)) continue;
2964
+ const jayHtmlPath = path.join(dir, f);
2965
+ const meta = readTemplateMeta(jayHtmlPath);
2966
+ if (meta.contractRef !== target) continue;
2967
+ const stem = path.basename(f, JAY_EXTENSION);
2968
+ variants.push({
2969
+ path: jayHtmlPath,
2970
+ variant: stem === contractBasename ? "" : stem,
2971
+ ...meta.title && { title: meta.title }
2972
+ });
2973
+ }
2974
+ return variants.sort((a, b) => a.variant.localeCompare(b.variant));
2975
+ }
2976
+ function readContractField(contractFile, field) {
2977
+ try {
2978
+ const parsed = YAML.parse(fs.readFileSync(contractFile, "utf-8"));
2979
+ const value = parsed?.[field];
2980
+ return typeof value === "string" ? value : void 0;
2981
+ } catch {
2982
+ return void 0;
2983
+ }
2984
+ }
2985
+ function findContractFilesSync(dir) {
2986
+ const out = [];
2987
+ let entries;
2988
+ try {
2989
+ entries = fs.readdirSync(dir, { withFileTypes: true });
2990
+ } catch {
2991
+ return out;
2992
+ }
2993
+ for (const entry of entries) {
2994
+ const full = path.join(dir, entry.name);
2995
+ if (entry.isDirectory()) out.push(...findContractFilesSync(full));
2996
+ else if (entry.name.endsWith(JAY_CONTRACT_EXTENSION)) out.push(full);
2997
+ }
2998
+ return out.sort();
2999
+ }
3000
+ function buildDesignSystemIndex(options) {
3001
+ const { projectRoot, plugins, resolvePluginContractPath } = options;
3002
+ const componentsBase = options.componentsBase ? path.resolve(projectRoot, options.componentsBase) : path.join(projectRoot, "src", "components");
3003
+ const rel = (p) => "./" + path.relative(projectRoot, p).replace(/\\/g, "/");
3004
+ const makeEntry = (contractFile, source, pluginName, nameOverride) => {
3005
+ const name = nameOverride ?? readContractField(contractFile, "name") ?? path.basename(contractFile, JAY_CONTRACT_EXTENSION);
3006
+ const description = readContractField(contractFile, "description");
3007
+ const templates = listTemplatesForContractFile(contractFile).map((t) => ({
3008
+ ...t,
3009
+ path: rel(t.path)
3010
+ }));
3011
+ return {
3012
+ name,
3013
+ contractPath: rel(contractFile),
3014
+ ...description && { description },
3015
+ source,
3016
+ ...pluginName && { plugin: pluginName },
3017
+ templates
3018
+ };
3019
+ };
3020
+ const components = [];
3021
+ for (const contractFile of findContractFilesSync(componentsBase)) {
3022
+ components.push(makeEntry(contractFile, "local", void 0));
3023
+ }
3024
+ for (const [, plugin] of plugins) {
3025
+ const contracts = plugin.manifest.contracts;
3026
+ if (!Array.isArray(contracts)) continue;
3027
+ for (const contract of contracts) {
3028
+ const spec = contract.contract;
3029
+ if (!spec) continue;
3030
+ const contractFile = resolvePluginContractPath(plugin, spec);
3031
+ components.push(makeEntry(contractFile, "plugin", plugin.name, contract.name));
3032
+ }
3033
+ }
3034
+ return { components };
3035
+ }
3036
+ function toKebabCase$1(str) {
3037
+ return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[\s_]+/g, "-").replace(/^-/, "");
3038
+ }
3039
+ function renderAddMenu(index) {
3040
+ const lines = [];
3041
+ lines.push("# Design-system elements you can add");
3042
+ lines.push("");
3043
+ lines.push(
3044
+ "Generated by `jay-stack agent-kit`. Each component ships a contract and one or more"
3045
+ );
3046
+ lines.push(
3047
+ "`.jay-html` templates you can flatten into a page as a `<jay:X>` region. Pick a variant,"
3048
+ );
3049
+ lines.push("paste its import + region, then run `jay-stack sync` to flatten the markup. See");
3050
+ lines.push("`designer/design-system-guide.md`.");
3051
+ lines.push("");
3052
+ if (index.components.length === 0) {
3053
+ lines.push("_No design-system components found._");
3054
+ lines.push("");
3055
+ return lines.join("\n");
3056
+ }
3057
+ for (const component of index.components) {
3058
+ lines.push(
3059
+ component.description ? `## ${component.name} — ${component.description}` : `## ${component.name}`
3060
+ );
3061
+ lines.push("");
3062
+ if (component.templates.length === 0) {
3063
+ lines.push(
3064
+ "_No template yet (COMPONENT-NO-TEMPLATE) — this component can't be flattened until it ships a `.jay-html`._"
3065
+ );
3066
+ lines.push("");
3067
+ continue;
3068
+ }
3069
+ lines.push("| Variant | Title | Template |");
3070
+ lines.push("| --- | --- | --- |");
3071
+ for (const template of component.templates) {
3072
+ const variant = template.variant === "" ? "(default)" : template.variant;
3073
+ lines.push(`| ${variant} | ${template.title ?? ""} | ${template.path} |`);
3074
+ }
3075
+ lines.push("");
3076
+ const first = component.templates[0];
3077
+ const regionTag = toKebabCase$1(component.name);
3078
+ const contractAttr = component.source === "plugin" ? component.name : component.contractPath;
3079
+ lines.push("```html");
3080
+ lines.push("<script");
3081
+ lines.push(' type="application/jay-headless"');
3082
+ if (component.source === "plugin" && component.plugin) {
3083
+ lines.push(` plugin="${component.plugin}"`);
3084
+ }
3085
+ lines.push(` contract="${contractAttr}"`);
3086
+ lines.push(` template="${first.path}"`);
3087
+ lines.push("><\/script>");
3088
+ lines.push("");
3089
+ lines.push(
3090
+ `<jay:${regionTag} ref="${regionTag}"><!-- flattened copy; edit freely --></jay:${regionTag}>`
3091
+ );
3092
+ lines.push("```");
3093
+ lines.push("");
3094
+ lines.push("Then run `jay-stack sync` to flatten it.");
3095
+ lines.push("");
3096
+ }
3097
+ return lines.join("\n");
3098
+ }
2930
3099
  const require$1 = createRequire(import.meta.url);
2931
3100
  async function executeDynamicGenerator(plugin, config, projectRoot, services, verbose, viteServer) {
2932
3101
  const { pluginPath, name: pluginName, isLocal, packageName } = plugin;
@@ -3056,6 +3225,15 @@ function resolveActionFilePath(actionPath, packageName, pluginPath, isLocal, pro
3056
3225
  function toKebabCase(str) {
3057
3226
  return str.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "");
3058
3227
  }
3228
+ function readComponentsBase(projectRoot) {
3229
+ try {
3230
+ const parsed = YAML.parse(fs.readFileSync(path.join(projectRoot, ".jay"), "utf-8"));
3231
+ const base = parsed?.devServer?.componentsBase;
3232
+ if (typeof base === "string" && base.length > 0) return base;
3233
+ } catch {
3234
+ }
3235
+ return "./src/components";
3236
+ }
3059
3237
  async function materializeContracts(options, services = /* @__PURE__ */ new Map()) {
3060
3238
  const {
3061
3239
  projectRoot,
@@ -3275,13 +3453,26 @@ async function materializeContracts(options, services = /* @__PURE__ */ new Map(
3275
3453
  ...data.commands?.length && { commands: data.commands }
3276
3454
  }))
3277
3455
  };
3456
+ const designSystem = buildDesignSystemIndex({
3457
+ projectRoot,
3458
+ componentsBase: readComponentsBase(projectRoot),
3459
+ plugins,
3460
+ resolvePluginContractPath: (plugin, spec) => resolveStaticContractPath(plugin, spec, projectRoot)
3461
+ });
3462
+ pluginsIndex.designSystemIndex = "./design-system-index.yaml";
3278
3463
  fs.mkdirSync(outputDir, { recursive: true });
3279
3464
  const agentKitDir = path.dirname(outputDir);
3280
3465
  const pluginsIndexPath = path.join(agentKitDir, "plugins-index.yaml");
3281
3466
  fs.writeFileSync(pluginsIndexPath, YAML.stringify(pluginsIndex), "utf-8");
3467
+ const designSystemIndexPath = path.join(agentKitDir, "design-system-index.yaml");
3468
+ fs.writeFileSync(designSystemIndexPath, YAML.stringify(designSystem), "utf-8");
3469
+ const addMenuPath = path.join(agentKitDir, "design-system.md");
3470
+ fs.writeFileSync(addMenuPath, renderAddMenu(designSystem), "utf-8");
3282
3471
  if (verbose) {
3283
3472
  getLogger().info(`
3284
3473
  ✅ Plugins index written to: ${pluginsIndexPath}`);
3474
+ getLogger().info(`✅ Design-system index written to: ${designSystemIndexPath}`);
3475
+ getLogger().info(`✅ Design-system add-menu written to: ${addMenuPath}`);
3285
3476
  }
3286
3477
  return {
3287
3478
  pluginsIndex,
@@ -4812,8 +5003,7 @@ async function sendResponse(vite, res, url, jayHtmlPath, sourceJayHtmlPath, page
4812
5003
  let pageHtml;
4813
5004
  try {
4814
5005
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
4815
- let jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
4816
- jayHtmlContent = injectHeadfullFSTemplates(jayHtmlContent, jayHtmlDir, JAY_IMPORT_RESOLVER);
5006
+ const jayHtmlContent = preLoadedContent ?? await fs__default.readFile(jayHtmlPath, "utf-8");
4817
5007
  pageHtml = await generateSSRPageHtml(
4818
5008
  vite,
4819
5009
  jayHtmlContent,
@@ -4893,15 +5083,10 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
4893
5083
  const jayHtmlFilename = path__default.basename(jayHtmlPath);
4894
5084
  const jayHtmlDir = path__default.dirname(jayHtmlPath);
4895
5085
  const sourceDir = path__default.dirname(route.jayHtmlPath);
4896
- const fullJayHtml = injectHeadfullFSTemplates(
4897
- jayHtmlContent,
4898
- sourceDir,
4899
- JAY_IMPORT_RESOLVER
4900
- );
4901
5086
  const injectDevHmr = format === "page";
4902
5087
  const html = await generateFrozenPageHtml(
4903
5088
  vite,
4904
- fullJayHtml,
5089
+ jayHtmlContent,
4905
5090
  jayHtmlFilename,
4906
5091
  jayHtmlDir,
4907
5092
  entry.viewState,
@@ -4931,13 +5116,8 @@ async function handleFrozenRequest(vite, route, options, freezeStore, slowRender
4931
5116
  timing?.end();
4932
5117
  }
4933
5118
  async function preRenderJayHtml(route, slowViewState, headlessContracts, headlessInstanceComponents, partKeys = [], pageParams = {}, pageProps = { language: "en", url: "" }) {
4934
- const jayHtmlContent = await fs__default.readFile(route.jayHtmlPath, "utf-8");
5119
+ const jayHtmlWithTemplates = await fs__default.readFile(route.jayHtmlPath, "utf-8");
4935
5120
  const sourceDir = path__default.dirname(route.jayHtmlPath);
4936
- const jayHtmlWithTemplates = injectHeadfullFSTemplates(
4937
- jayHtmlContent,
4938
- sourceDir,
4939
- JAY_IMPORT_RESOLVER
4940
- );
4941
5121
  let contract;
4942
5122
  const contractPath = route.jayHtmlPath.replace(".jay-html", ".jay-contract");
4943
5123
  try {
@@ -4994,16 +5174,15 @@ async function preRenderJayHtml(route, slowViewState, headlessContracts, headles
4994
5174
  instancePhaseData = {
4995
5175
  discovered: finalDiscovery.instances.filter((i) => componentByContractName.has(i.contractName)).map((i) => {
4996
5176
  const comp = componentByContractName.get(i.contractName);
4997
- const normalizedProps = normalizeAndResolveInstanceProps(
5177
+ const rawProps = normalizeInstancePropNames(
4998
5178
  Object.fromEntries(
4999
5179
  Object.entries(i.props).map(([k, v]) => [k, String(v)])
5000
5180
  ),
5001
- comp.contract?.props,
5002
- { pageViewState: slowViewState, pageParams, pageProps }
5181
+ comp.contract?.props
5003
5182
  );
5004
5183
  return {
5005
5184
  contractName: i.contractName,
5006
- props: normalizedProps,
5185
+ props: rawProps,
5007
5186
  coordinate: i.coordinate
5008
5187
  };
5009
5188
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/dev-server",
3
- "version": "0.24.6",
3
+ "version": "0.25.0",
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.6",
27
- "@jay-framework/compiler-shared": "^0.24.6",
28
- "@jay-framework/component": "^0.24.6",
29
- "@jay-framework/fullstack-component": "^0.24.6",
30
- "@jay-framework/logger": "^0.24.6",
31
- "@jay-framework/runtime": "^0.24.6",
32
- "@jay-framework/stack-client-runtime": "^0.24.6",
33
- "@jay-framework/stack-route-scanner": "^0.24.6",
34
- "@jay-framework/stack-server-build": "^0.24.6",
35
- "@jay-framework/stack-server-runtime": "^0.24.6",
36
- "@jay-framework/view-state-merge": "^0.24.6",
26
+ "@jay-framework/compiler-jay-stack": "^0.25.0",
27
+ "@jay-framework/compiler-shared": "^0.25.0",
28
+ "@jay-framework/component": "^0.25.0",
29
+ "@jay-framework/fullstack-component": "^0.25.0",
30
+ "@jay-framework/logger": "^0.25.0",
31
+ "@jay-framework/runtime": "^0.25.0",
32
+ "@jay-framework/stack-client-runtime": "^0.25.0",
33
+ "@jay-framework/stack-route-scanner": "^0.25.0",
34
+ "@jay-framework/stack-server-build": "^0.25.0",
35
+ "@jay-framework/stack-server-runtime": "^0.25.0",
36
+ "@jay-framework/view-state-merge": "^0.25.0",
37
37
  "busboy": "^1.6.0",
38
38
  "vite": "^5.0.11"
39
39
  },
40
40
  "devDependencies": {
41
- "@jay-framework/dev-environment": "^0.24.6",
42
- "@jay-framework/jay-cli": "^0.24.6",
43
- "@jay-framework/stack-client-runtime": "^0.24.6",
41
+ "@jay-framework/dev-environment": "^0.25.0",
42
+ "@jay-framework/jay-cli": "^0.25.0",
43
+ "@jay-framework/stack-client-runtime": "^0.25.0",
44
44
  "@playwright/test": "^1.58.2",
45
45
  "@types/busboy": "^1.5.4",
46
46
  "@types/express": "^5.0.2",