@jay-framework/compiler-jay-html 0.19.6 → 0.19.8

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
@@ -180,6 +180,12 @@ interface JayHtmlHeadLink {
180
180
  href: string;
181
181
  attributes: Record<string, string>;
182
182
  }
183
+ interface JayHtmlScript {
184
+ src?: string;
185
+ inline?: string;
186
+ attributes: Record<string, string>;
187
+ position: 'head' | 'body';
188
+ }
183
189
  interface JayHtmlSourceFile extends CompilerSourceFile {
184
190
  format: SourceFileFormat.JayHtml;
185
191
  baseElementName: string;
@@ -214,6 +220,7 @@ interface JayHtmlSourceFile extends CompilerSourceFile {
214
220
  */
215
221
  clientTrackByMap?: Record<string, string>;
216
222
  headMeta?: JayHtmlHeadMeta;
223
+ scripts?: JayHtmlScript[];
217
224
  }
218
225
 
219
226
  declare function parseTemplateParts(value: string): TemplatePart[];
@@ -601,4 +608,4 @@ declare function resolveHeadlessInstances(preRenderedJayHtml: string, instanceDa
601
608
  */
602
609
  declare function hasSlowPhaseProperties(contract: Contract | undefined): boolean;
603
610
 
604
- export { type ActionDefinition, type AssignCoordinatesOptions, type AssignCoordinatesResult, type Contract, type ContractImportInfo, type ContractParam, type ContractParamKind, type ContractProp, type ContractResolver, type ContractTag, ContractTagType, type DiscoveredHeadlessInstance, type EnumToImport, type ForEachHeadlessInstance, type HeadlessContractInfo, type HeadlessInstanceDiscoveryResult, type HeadlessInstanceResolvedData, JAY_IMPORT_RESOLVER, type JayContractImportLink, type JayHtmlSourceFile, type JayImportResolver, type PhaseViewStates, type RenderingPhase, type ServerElementOptions, type SlowRenderInput, type SlowRenderOutput, assignCoordinates, assignCoordinatesToJayHtml, buildInstanceCoordinateKey, compileAction, compileContract, contractToAllPhaseViewStates, contractToImportsViewStateAndRefs, contractToPhaseViewState, createPhaseContract, defaultContractResolver, discoverHeadlessInstances, filterTagsByPhase, generateElementBridgeFile, generateElementDefinitionFile, generateElementFile, generateElementFileReactTarget, generateElementHydrateFile, generateSandboxRootFile, generateServerElementFile, generateTypes, getEffectivePhase, getJayHtmlImports, getLinkedContractDir, hasSlowPhaseProperties, htmlElementTagNameMap, injectHeadfullFSTemplates, isTagInPhase, loadLinkedContract, parseAction, parseContract, parseEnumValues, parseIsEnum, parseJayFile, parseTemplateParts, renderRefsType, resolveHeadlessInstances, slowRenderTransform, validateContractPhases };
611
+ export { type ActionDefinition, type AssignCoordinatesOptions, type AssignCoordinatesResult, type Contract, type ContractImportInfo, type ContractParam, type ContractParamKind, type ContractProp, type ContractResolver, type ContractTag, ContractTagType, type DiscoveredHeadlessInstance, type EnumToImport, type ForEachHeadlessInstance, type HeadlessContractInfo, type HeadlessInstanceDiscoveryResult, type HeadlessInstanceResolvedData, JAY_IMPORT_RESOLVER, type JayContractImportLink, type JayHtmlScript, type JayHtmlSourceFile, type JayImportResolver, type PhaseViewStates, type RenderingPhase, type ServerElementOptions, type SlowRenderInput, type SlowRenderOutput, assignCoordinates, assignCoordinatesToJayHtml, buildInstanceCoordinateKey, compileAction, compileContract, contractToAllPhaseViewStates, contractToImportsViewStateAndRefs, contractToPhaseViewState, createPhaseContract, defaultContractResolver, discoverHeadlessInstances, filterTagsByPhase, generateElementBridgeFile, generateElementDefinitionFile, generateElementFile, generateElementFileReactTarget, generateElementHydrateFile, generateSandboxRootFile, generateServerElementFile, generateTypes, getEffectivePhase, getJayHtmlImports, getLinkedContractDir, hasSlowPhaseProperties, htmlElementTagNameMap, injectHeadfullFSTemplates, isTagInPhase, loadLinkedContract, parseAction, parseContract, parseEnumValues, parseIsEnum, parseJayFile, parseTemplateParts, renderRefsType, resolveHeadlessInstances, slowRenderTransform, validateContractPhases };
package/dist/index.js CHANGED
@@ -14657,16 +14657,52 @@ function mergeContractStubRefs(templateRefs, contractRefs) {
14657
14657
  );
14658
14658
  }
14659
14659
  }
14660
- if (stubRefs.length === 0)
14660
+ const mergedChildren = { ...templateRefs.children };
14661
+ for (const [childName, contractChild] of Object.entries(contractRefs.children)) {
14662
+ if (mergedChildren[childName]) {
14663
+ mergedChildren[childName] = mergeContractStubRefs(
14664
+ mergedChildren[childName],
14665
+ contractChild
14666
+ );
14667
+ } else {
14668
+ mergedChildren[childName] = markAllRefsAsStubs(contractChild);
14669
+ }
14670
+ }
14671
+ const childrenChanged = Object.keys(mergedChildren).length !== Object.keys(templateRefs.children).length || Object.keys(mergedChildren).some((k) => mergedChildren[k] !== templateRefs.children[k]);
14672
+ if (stubRefs.length === 0 && !childrenChanged)
14661
14673
  return templateRefs;
14662
14674
  return mkRefsTree(
14663
14675
  [...templateRefs.refs, ...stubRefs],
14664
- templateRefs.children,
14676
+ mergedChildren,
14665
14677
  templateRefs.repeated,
14666
14678
  templateRefs.imported?.refsTypeName,
14667
14679
  templateRefs.imported?.repeatedRefsTypeName
14668
14680
  );
14669
14681
  }
14682
+ function markAllRefsAsStubs(tree) {
14683
+ const stubbedRefs = tree.refs.map(
14684
+ (ref) => mkRef(
14685
+ ref.originalName,
14686
+ ref.originalName,
14687
+ camelCase(`ref ${ref.originalName}`),
14688
+ ref.repeated,
14689
+ true,
14690
+ ref.viewStateType,
14691
+ ref.elementType
14692
+ )
14693
+ );
14694
+ const stubbedChildren = {};
14695
+ for (const [name, child] of Object.entries(tree.children)) {
14696
+ stubbedChildren[name] = markAllRefsAsStubs(child);
14697
+ }
14698
+ return mkRefsTree(
14699
+ stubbedRefs,
14700
+ stubbedChildren,
14701
+ tree.repeated,
14702
+ tree.imported?.refsTypeName,
14703
+ tree.imported?.repeatedRefsTypeName
14704
+ );
14705
+ }
14670
14706
  const COORD_ATTR = "jay-coordinate-base";
14671
14707
  function expandContractType(renderedElement, baseName) {
14672
14708
  const contractPattern = new RegExp(
@@ -18264,7 +18300,11 @@ function parseHeadfullImports(elements, validations, filePath, options, importRe
18264
18300
  const exportedTypes = importResolver.analyzeExportedTypes(importedFile, options);
18265
18301
  for (const name of names) {
18266
18302
  const exportedType = exportedTypes.find((_) => _.name === name.name);
18267
- if (exportedType && exportedType !== JayUnknown)
18303
+ if (exportedType instanceof JayComponentType && exportedType.fullStack) {
18304
+ validations.push(
18305
+ `${name.name} from ${module} is a full-stack component (makeJayStackComponent). Create a .jay-contract file for the component and add a contract attribute to the import`
18306
+ );
18307
+ } else if (exportedType && exportedType !== JayUnknown)
18268
18308
  name.type = new JayImportedType(name.as ? name.as : name.name, exportedType);
18269
18309
  else if (exportedType === JayUnknown)
18270
18310
  validations.push(
@@ -18746,6 +18786,49 @@ async function parseHeadfullFSImports(elements, validations, filePath, importRes
18746
18786
  function normalizeFilename(filename) {
18747
18787
  return filename.replace(".jay-html", "");
18748
18788
  }
18789
+ function isLocalSrc(src) {
18790
+ return src.startsWith("./") || src.startsWith("../") || !src.includes("://") && !src.startsWith("//");
18791
+ }
18792
+ function validateAndCollectScripts(root, validations) {
18793
+ const scripts = [];
18794
+ const allScripts = root.querySelectorAll("script");
18795
+ for (const script of allScripts) {
18796
+ const type2 = script.getAttribute("type");
18797
+ if (type2?.startsWith("application/jay-"))
18798
+ continue;
18799
+ const jayScript = script.getAttribute("jay-script");
18800
+ const src = script.getAttribute("src");
18801
+ const inline = script.textContent?.trim();
18802
+ const isInHead = !!script.closest("head");
18803
+ if (src && isLocalSrc(src)) {
18804
+ validations.push(
18805
+ `Local script imports are not supported in jay-html. Move the script logic into page.ts with makeJayStackComponent. See designer/script-tags.md.`
18806
+ );
18807
+ continue;
18808
+ }
18809
+ if (jayScript === "allow") {
18810
+ const attributes2 = { ...script.attributes };
18811
+ delete attributes2["jay-script"];
18812
+ delete attributes2["src"];
18813
+ if (src) {
18814
+ scripts.push({ src, attributes: attributes2, position: isInHead ? "head" : "body" });
18815
+ } else if (inline) {
18816
+ scripts.push({ inline, attributes: attributes2, position: isInHead ? "head" : "body" });
18817
+ }
18818
+ continue;
18819
+ }
18820
+ if (src) {
18821
+ validations.push(
18822
+ `External scripts should be explicitly marked. If this script is required (e.g., analytics or tag manager), add jay-script="allow". Prefer page.ts for page behavior. See designer/script-tags.md.`
18823
+ );
18824
+ } else if (inline) {
18825
+ validations.push(
18826
+ `Inline scripts are not supported in jay-html. Use page.ts with makeJayStackComponent for page behavior. If this is a third-party script that must be included as-is, add jay-script="allow". See designer/script-tags.md.`
18827
+ );
18828
+ }
18829
+ }
18830
+ return scripts;
18831
+ }
18749
18832
  function parseHeadLinks(root, excludeCssLinks = false) {
18750
18833
  const allLinks = root.querySelectorAll("head link");
18751
18834
  return allLinks.filter((link) => {
@@ -18945,6 +19028,7 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
18945
19028
  const excludeCssLinks = !!filePath;
18946
19029
  const headLinks = parseHeadLinks(root, excludeCssLinks);
18947
19030
  const headMeta = parseHeadMeta(root);
19031
+ const scripts = validateAndCollectScripts(root, validations);
18948
19032
  validations.push(...cssResult.validations);
18949
19033
  if (validations.length > 0)
18950
19034
  return new WithValidations(void 0, validations);
@@ -18980,7 +19064,8 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
18980
19064
  hasInlineData: jayYaml.hasInlineData,
18981
19065
  serverTrackByMap: Object.keys(serverTrackByMap).length > 0 ? serverTrackByMap : void 0,
18982
19066
  clientTrackByMap: Object.keys(clientTrackByMap).length > 0 ? clientTrackByMap : void 0,
18983
- headMeta
19067
+ headMeta,
19068
+ scripts: scripts.length > 0 ? scripts : void 0
18984
19069
  },
18985
19070
  validations
18986
19071
  );
@@ -19872,6 +19957,11 @@ ${context.indent.firstLine} ${createBody})`,
19872
19957
  childContent.refs
19873
19958
  );
19874
19959
  }
19960
+ if (isConditional(element) && isForEach(element)) {
19961
+ return new RenderFragment("", Imports.none(), [
19962
+ `"if" and "forEach" cannot be on the same element. Wrap the forEach in a separate element: <div if="..."><div forEach="...">...</div></div>`
19963
+ ]);
19964
+ }
19875
19965
  if (isConditional(element) && context.interactivePaths.size > 0) {
19876
19966
  const condition = element.getAttribute("if");
19877
19967
  const viewStateVars = new Variables(
@@ -20412,7 +20502,21 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
20412
20502
  childRefs.push(frag.refs);
20413
20503
  }
20414
20504
  } else {
20415
- const frag = renderHydrateNode(child, context);
20505
+ const htmlChild2 = child;
20506
+ const isComponent = getComponentName(
20507
+ htmlChild2.rawTagName,
20508
+ context.importedSymbols,
20509
+ context.headlessContractNames
20510
+ ) !== null;
20511
+ const isNonInteractiveConditional = isConditional(htmlChild2) && !conditionIsInteractive(htmlChild2.getAttribute("if"), context.interactivePaths);
20512
+ const needsWrap = !isComponent && !isNonInteractiveConditional;
20513
+ const frag = needsWrap ? renderHydrateElementContent(
20514
+ htmlChild2,
20515
+ context,
20516
+ buildRenderContext(context),
20517
+ null,
20518
+ true
20519
+ ) : renderHydrateNode(child, context);
20416
20520
  if (frag.rendered.trim()) {
20417
20521
  childParts.push(frag.rendered);
20418
20522
  childImports = childImports.plus(frag.imports);
@@ -21296,6 +21400,10 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
21296
21400
  mkRefsTree([], {})
21297
21401
  );
21298
21402
  }
21403
+ } else if (isConditional(htmlElement) && isForEach(htmlElement)) {
21404
+ return new RenderFragment("", Imports.none(), [
21405
+ `"if" and "forEach" cannot be on the same element. Wrap the forEach in a separate element: <div if="..."><div forEach="...">...</div></div>`
21406
+ ]);
21299
21407
  } else if (isConditional(htmlElement)) {
21300
21408
  let condition = htmlElement.getAttribute("if");
21301
21409
  let childElement = renderHtmlElement(htmlElement, {
@@ -22401,6 +22509,11 @@ function renderServerElement(element, context) {
22401
22509
  if (componentMatch !== null && componentMatch.kind === "headless-instance") {
22402
22510
  return renderServerHeadlessInstance(element, context, componentMatch.name);
22403
22511
  }
22512
+ if (isConditional(element) && isForEach(element)) {
22513
+ return new RenderFragment("", Imports.none(), [
22514
+ `"if" and "forEach" cannot be on the same element. Wrap the forEach in a separate element: <div if="..."><div forEach="...">...</div></div>`
22515
+ ]);
22516
+ }
22404
22517
  if (isConditional(element)) {
22405
22518
  const condition = element.getAttribute("if");
22406
22519
  const renderedCondition = parseServerCondition(condition, variables);
@@ -22888,7 +23001,7 @@ function renderServerElementContent(element, context, options) {
22888
23001
  }
22889
23002
  }
22890
23003
  const refName = element.attributes.ref ? camelCase(element.attributes.ref) : null;
22891
- const needsCoordinate = options?.isRoot === true || isConditional(element) || dynamicTextFragment !== null || refName !== null || hasDynamicAttributeBindings(element, variables) || hasInteractiveChildElements(childNodes) || hasMixedContentDynamicTextInteractive(childNodes, context.interactivePaths);
23004
+ const needsCoordinate = options?.isRoot === true || isConditional(element) || dynamicTextFragment !== null || refName !== null || hasDynamicAttributeBindings(element, variables) || hasInteractiveChildElements(childNodes) || hasMixedContentDynamicTextInteractive(childNodes, context.interactivePaths) || context.parentHasInteractiveChildren;
22892
23005
  const coordTemplate = needsCoordinate ? element.getAttribute(COORD_ATTR) : null;
22893
23006
  const isVoid = voidElements.has(element.rawTagName.toLowerCase());
22894
23007
  const closeTag = isVoid ? " />" : ">";
@@ -22926,7 +23039,8 @@ function renderServerElementContent(element, context, options) {
22926
23039
  } else {
22927
23040
  const childContext = {
22928
23041
  ...context,
22929
- indent: new Indent(indent.curr + " ")
23042
+ indent: new Indent(indent.curr + " "),
23043
+ parentHasInteractiveChildren: hasInteractiveChildElements(childNodes)
22930
23044
  };
22931
23045
  const asyncGroups = collectAsyncGroups(childNodes);
22932
23046
  const processedAsyncProps = /* @__PURE__ */ new Set();
@@ -23077,7 +23191,8 @@ function generateServerElementFile(jayFile, _options) {
23077
23191
  headlessInstanceCounter: { count: 0 },
23078
23192
  varMappings: {},
23079
23193
  insideForEach: false,
23080
- interactivePaths: buildInteractivePaths(jayFile.contract)
23194
+ interactivePaths: buildInteractivePaths(jayFile.contract),
23195
+ parentHasInteractiveChildren: false
23081
23196
  };
23082
23197
  const rendered = renderServerElementContent(rootElement.val, context, {
23083
23198
  isRoot: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/compiler-jay-html",
3
- "version": "0.19.6",
3
+ "version": "0.19.8",
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.19.6",
38
- "@jay-framework/compiler-shared": "^0.19.6",
39
- "@jay-framework/component": "^0.19.6",
40
- "@jay-framework/logger": "^0.19.6",
41
- "@jay-framework/runtime": "^0.19.6",
42
- "@jay-framework/secure": "^0.19.6",
37
+ "@jay-framework/compiler-analyze-exported-types": "^0.19.8",
38
+ "@jay-framework/compiler-shared": "^0.19.8",
39
+ "@jay-framework/component": "^0.19.8",
40
+ "@jay-framework/logger": "^0.19.8",
41
+ "@jay-framework/runtime": "^0.19.8",
42
+ "@jay-framework/secure": "^0.19.8",
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.19.6",
56
- "@jay-framework/dev-environment": "^0.19.6",
55
+ "@jay-framework/4-react": "^0.19.8",
56
+ "@jay-framework/dev-environment": "^0.19.8",
57
57
  "@testing-library/jest-dom": "^6.2.0",
58
58
  "@types/js-beautify": "^1",
59
59
  "@types/node": "^20.11.5",