@jay-framework/compiler-jay-html 0.20.0 → 0.22.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.
package/dist/index.d.cts CHANGED
@@ -29,6 +29,7 @@ interface ContractProp {
29
29
  name: string;
30
30
  dataType: JayType;
31
31
  required?: boolean;
32
+ phase?: RenderingPhase;
32
33
  description?: Array<string>;
33
34
  default?: string;
34
35
  }
@@ -174,6 +175,7 @@ interface JayHeadlessImports {
174
175
  contract?: Contract;
175
176
  contractPath?: string;
176
177
  metadata?: Record<string, unknown>;
178
+ headlessProps?: Record<string, string>;
177
179
  }
178
180
  interface JayHtmlHeadLink {
179
181
  rel: string;
package/dist/index.js CHANGED
@@ -2,8 +2,8 @@ import { JayImportedType, isObjectType as isObjectType$1, isImportedType, isRecu
2
2
  import path from "path";
3
3
  import { getLogger } from "@jay-framework/logger";
4
4
  import fs from "fs/promises";
5
- import { analyzeExportedTypes } from "@jay-framework/compiler-analyze-exported-types";
6
5
  import fs$1 from "fs";
6
+ import { analyzeExportedTypes } from "@jay-framework/compiler-analyze-exported-types";
7
7
  import { createRequire } from "module";
8
8
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
9
9
  function getDefaultExportFromCjs(x) {
@@ -18324,6 +18324,13 @@ function parseHeadfullImports(elements, validations, filePath, options, importRe
18324
18324
  }
18325
18325
  });
18326
18326
  }
18327
+ function dedentYaml(text2) {
18328
+ const lines = text2.split("\n").filter((l) => l.trim().length > 0);
18329
+ if (lines.length === 0)
18330
+ return "";
18331
+ const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
18332
+ return lines.map((l) => l.slice(minIndent)).join("\n");
18333
+ }
18327
18334
  async function parseHeadlessImports(elements, validations, filePath, importResolver, projectRoot) {
18328
18335
  const result = [];
18329
18336
  for await (const element of elements) {
@@ -18338,6 +18345,23 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18338
18345
  validations.push("headless import must specify contract attribute");
18339
18346
  continue;
18340
18347
  }
18348
+ let headlessProps;
18349
+ const propsText = dedentYaml(element.textContent ?? "");
18350
+ if (propsText) {
18351
+ try {
18352
+ const parsed = jsYaml.load(propsText);
18353
+ if (parsed && typeof parsed === "object") {
18354
+ headlessProps = {};
18355
+ for (const [k, v] of Object.entries(parsed)) {
18356
+ headlessProps[k] = String(v);
18357
+ }
18358
+ }
18359
+ } catch (e2) {
18360
+ validations.push(
18361
+ `Failed to parse props YAML in <script type="application/jay-headless" contract="${contractAttr}">: ${e2.message}`
18362
+ );
18363
+ }
18364
+ }
18341
18365
  const resolveResult = importResolver.resolvePluginComponent(
18342
18366
  pluginAttr,
18343
18367
  contractAttr,
@@ -18446,7 +18470,8 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18446
18470
  codeLink,
18447
18471
  contract: loadedContract,
18448
18472
  contractPath: contractFile,
18449
- metadata: contractMetadata
18473
+ metadata: contractMetadata,
18474
+ headlessProps
18450
18475
  });
18451
18476
  });
18452
18477
  } catch (e2) {
@@ -18893,6 +18918,26 @@ function parseHeadMeta(root) {
18893
18918
  }
18894
18919
  return { title, meta, links };
18895
18920
  }
18921
+ function resolveNestedCssImports(css, cssDir, visited) {
18922
+ if (!css.includes("@import"))
18923
+ return css;
18924
+ const seen = visited ?? /* @__PURE__ */ new Set();
18925
+ return css.replace(/@import\s+(?:url\(\s*)?['"]([^'"]+)['"]\s*\)?;?/g, (match, importPath) => {
18926
+ if (importPath.startsWith("http") || importPath.startsWith("//"))
18927
+ return match;
18928
+ const resolved = path.resolve(cssDir, importPath);
18929
+ if (seen.has(resolved))
18930
+ return `/* circular import: ${importPath} */`;
18931
+ seen.add(resolved);
18932
+ try {
18933
+ const imported = fs$1.readFileSync(resolved, "utf-8");
18934
+ return `/* @import ${importPath} */
18935
+ ${resolveNestedCssImports(imported, path.dirname(resolved), seen)}`;
18936
+ } catch {
18937
+ return `/* @import not found: ${importPath} */`;
18938
+ }
18939
+ });
18940
+ }
18896
18941
  async function extractCss(root, filePath) {
18897
18942
  const cssParts = [];
18898
18943
  const validations = [];
@@ -18909,8 +18954,12 @@ async function extractCss(root, filePath) {
18909
18954
  linkedCssFiles.push(cssFilePath);
18910
18955
  try {
18911
18956
  const cssContent = await fs.readFile(cssFilePath, "utf-8");
18957
+ const resolvedCss = resolveNestedCssImports(
18958
+ cssContent,
18959
+ path.dirname(cssFilePath)
18960
+ );
18912
18961
  cssParts.push(`/* External CSS: ${href} */
18913
- ${cssContent}`);
18962
+ ${resolvedCss}`);
18914
18963
  } catch (error) {
18915
18964
  validations.push(`CSS file not found or unreadable: ${href}`);
18916
18965
  }
@@ -19715,10 +19764,22 @@ function parseProp(prop) {
19715
19764
  return new WithValidations(void 0, validations);
19716
19765
  }
19717
19766
  const description = parseDescription(prop.description);
19767
+ let phase = "slow";
19768
+ if (prop.phase) {
19769
+ const validPhases = ["slow", "fast", "fast+interactive"];
19770
+ if (!validPhases.includes(prop.phase)) {
19771
+ validations.push(
19772
+ `Prop [${prop.name}] has invalid phase [${prop.phase}]. Valid phases are: ${validPhases.join(", ")}`
19773
+ );
19774
+ } else {
19775
+ phase = prop.phase;
19776
+ }
19777
+ }
19718
19778
  const contractProp = {
19719
19779
  name: prop.name,
19720
19780
  dataType,
19721
19781
  ...prop.required && { required: prop.required },
19782
+ phase,
19722
19783
  ...description && { description },
19723
19784
  ...prop.default !== void 0 && { default: prop.default }
19724
19785
  };
@@ -19762,11 +19823,18 @@ function parseContract(contractYaml, fileName) {
19762
19823
  });
19763
19824
  }
19764
19825
  let parsedParams;
19765
- if (parsedYaml.params && typeof parsedYaml.params === "object" && !Array.isArray(parsedYaml.params)) {
19766
- parsedParams = Object.entries(parsedYaml.params).map(([name, value]) => ({
19767
- name,
19768
- kind: typeof value === "string" && value.endsWith("?") ? "optional" : typeof value === "string" && value.endsWith("[]") ? "catch-all" : "required"
19769
- }));
19826
+ if (parsedYaml.params && typeof parsedYaml.params === "object") {
19827
+ if (Array.isArray(parsedYaml.params)) {
19828
+ parsedParams = parsedYaml.params.map((p) => ({
19829
+ name: p.name,
19830
+ kind: p.kind === "optional" ? "optional" : p.kind === "catch-all" ? "catch-all" : "required"
19831
+ }));
19832
+ } else {
19833
+ parsedParams = Object.entries(parsedYaml.params).map(([name, value]) => ({
19834
+ name,
19835
+ kind: typeof value === "string" && value.endsWith("?") ? "optional" : typeof value === "string" && value.endsWith("[]") ? "catch-all" : "required"
19836
+ }));
19837
+ }
19770
19838
  if (parsedParams.length === 0)
19771
19839
  parsedParams = void 0;
19772
19840
  }
@@ -20681,14 +20749,24 @@ function renderHydrate(types2, body, importStatements, elementType, preRenderTyp
20681
20749
  if (!rootElement.val) {
20682
20750
  return new RenderFragment("", Imports.none(), rootElement.validations);
20683
20751
  }
20684
- let renderedHydrate = renderHydrateElementContent(
20685
- rootElement.val,
20686
- context,
20687
- buildRenderContext(context),
20688
- null,
20689
- true
20690
- // forceAdopt — root element always needs adoption
20752
+ const rootComponentMatch = getComponentName(
20753
+ rootElement.val.rawTagName,
20754
+ context.importedSymbols,
20755
+ context.headlessContractNames
20691
20756
  );
20757
+ let renderedHydrate;
20758
+ if (rootComponentMatch !== null && rootComponentMatch.kind === "headless-instance") {
20759
+ renderedHydrate = renderHydrateElement(rootElement.val, context);
20760
+ } else {
20761
+ renderedHydrate = renderHydrateElementContent(
20762
+ rootElement.val,
20763
+ context,
20764
+ buildRenderContext(context),
20765
+ null,
20766
+ true
20767
+ // forceAdopt — root element always needs adoption
20768
+ );
20769
+ }
20692
20770
  renderedHydrate = optimizeRefs(renderedHydrate, headlessImports);
20693
20771
  const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
20694
20772
  renderedHydrate.refs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/compiler-jay-html",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "author": "",
36
36
  "dependencies": {
37
- "@jay-framework/compiler-analyze-exported-types": "^0.20.0",
38
- "@jay-framework/compiler-shared": "^0.20.0",
39
- "@jay-framework/component": "^0.20.0",
40
- "@jay-framework/logger": "^0.20.0",
41
- "@jay-framework/runtime": "^0.20.0",
42
- "@jay-framework/secure": "^0.20.0",
37
+ "@jay-framework/compiler-analyze-exported-types": "^0.22.0",
38
+ "@jay-framework/compiler-shared": "^0.22.0",
39
+ "@jay-framework/component": "^0.22.0",
40
+ "@jay-framework/logger": "^0.22.0",
41
+ "@jay-framework/runtime": "^0.22.0",
42
+ "@jay-framework/secure": "^0.22.0",
43
43
  "@types/js-yaml": "^4.0.9",
44
44
  "change-case": "^4.1.2",
45
45
  "he": "^1.2.0",
@@ -52,8 +52,8 @@
52
52
  },
53
53
  "devDependencies": {
54
54
  "@caiogondim/strip-margin": "^1.0.0",
55
- "@jay-framework/4-react": "^0.20.0",
56
- "@jay-framework/dev-environment": "^0.20.0",
55
+ "@jay-framework/4-react": "^0.22.0",
56
+ "@jay-framework/dev-environment": "^0.22.0",
57
57
  "@testing-library/jest-dom": "^6.2.0",
58
58
  "@types/js-beautify": "^1",
59
59
  "@types/node": "^20.11.5",