@reckona/mreact-compiler 0.0.153 → 0.0.155

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.
@@ -1 +1 @@
1
- {"version":3,"file":"emit-server.d.ts","sourceRoot":"","sources":["../src/emit-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwD,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqBrE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACzC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AASD,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAsB,GAAG,UAAU,CA8GpF"}
1
+ {"version":3,"file":"emit-server.d.ts","sourceRoot":"","sources":["../src/emit-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwD,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAqBrE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACzC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAUD,wBAAgB,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,iBAAsB,GAAG,UAAU,CAoHpF"}
@@ -8,6 +8,7 @@ import { emitOxcCompatObjectChildren, oxcServerStringReactNodeRenderHelperPlaceh
8
8
  // name through every signature. Reset at the top of `emitServer`.
9
9
  let currentUrlSafeHelperName = "_urlAttrSafe";
10
10
  let currentClientBoundaryHelperName;
11
+ let currentCompatChildHelperName;
11
12
  let currentSpreadAttributesHelperName = "_renderSpreadAttributes";
12
13
  export function emitServer(ir, options = {}) {
13
14
  const escapeHelperName = allocateEscapeHelperName(ir);
@@ -24,12 +25,16 @@ export function emitServer(ir, options = {}) {
24
25
  const clientBoundaryHelperName = usesClientBoundary(ir)
25
26
  ? allocateHelperName(ir, "_renderClientBoundary")
26
27
  : undefined;
28
+ const compatChildHelperName = usesCompatChildRender(ir)
29
+ ? allocateHelperName(ir, "_renderCompatChild")
30
+ : undefined;
27
31
  const spreadAttributesHelperName = allocateHelperName(ir, "_renderSpreadAttributes");
28
32
  const outAccumulatorName = allocateHelperName(ir, "_out");
29
33
  const urlSafeHelperName = allocateHelperName(ir, "_urlAttrSafe");
30
34
  currentUrlSafeHelperName = urlSafeHelperName;
31
35
  setOxcServerStringUrlSafeHelperName(urlSafeHelperName);
32
36
  currentClientBoundaryHelperName = clientBoundaryHelperName;
37
+ currentCompatChildHelperName = compatChildHelperName;
33
38
  currentSpreadAttributesHelperName = spreadAttributesHelperName;
34
39
  const helper = emitEscapeHtmlHelper(escapeHelperName);
35
40
  // Inline URL-scheme guard mirroring packages/server/src/url-safety.ts.
@@ -77,7 +82,7 @@ export function emitServer(ir, options = {}) {
77
82
  ? ""
78
83
  : `import { ${options.escape.batchImportName} as ${escapeBatchHelperName} } from ${stringLiteral(options.escape.batchImportSource)};`;
79
84
  const userImports = emitUserImports(ir);
80
- const contextImport = emitContextImport(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName);
85
+ const contextImport = emitContextImport(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName, compatChildHelperName);
81
86
  const moduleStatements = emitModuleStatements(ir);
82
87
  const code = createCodeBuilder();
83
88
  code.section(userImports);
@@ -91,14 +96,17 @@ export function emitServer(ir, options = {}) {
91
96
  code.section(components);
92
97
  return {
93
98
  code: code.toString(),
94
- imports: collectContextImports(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName),
99
+ imports: collectContextImports(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName, compatChildHelperName),
95
100
  };
96
101
  }
97
- function emitContextImport(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName) {
102
+ function emitContextImport(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName, compatChildHelperName) {
98
103
  const specifiers = [
99
104
  reactNodeRenderHelperName === undefined
100
105
  ? undefined
101
106
  : `renderToString as ${reactNodeRenderHelperName}`,
107
+ compatChildHelperName === undefined
108
+ ? undefined
109
+ : `renderChildToString as ${compatChildHelperName}`,
102
110
  contextProviderHelperName === undefined
103
111
  ? undefined
104
112
  : `renderContextProviderToString as ${contextProviderHelperName}`,
@@ -110,11 +118,12 @@ function emitContextImport(contextProviderHelperName, contextConsumerHelperName,
110
118
  ? ""
111
119
  : `import { ${specifiers.join(", ")} } from "@reckona/mreact-compat";`;
112
120
  }
113
- function collectContextImports(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName) {
121
+ function collectContextImports(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName, compatChildHelperName) {
114
122
  const specifiers = [
115
123
  reactNodeRenderHelperName === undefined ? undefined : "renderToString",
116
124
  contextProviderHelperName === undefined ? undefined : "renderContextProviderToString",
117
125
  contextConsumerHelperName === undefined ? undefined : "renderContextConsumerToString",
126
+ compatChildHelperName === undefined ? undefined : "renderChildToString",
118
127
  ].filter((specifier) => specifier !== undefined);
119
128
  return specifiers.length === 0 ? [] : [{ source: "@reckona/mreact-compat", specifiers }];
120
129
  }
@@ -179,6 +188,9 @@ function collectHtmlStatements(node, outVar, escapeHelperName, escapeBatchHelper
179
188
  if (node.renderMode === "react-node" && reactNodeRenderHelperName !== undefined) {
180
189
  return [`${outVar} += ${reactNodeRenderHelperName}(() => (${node.code}));`];
181
190
  }
191
+ if (node.renderMode === "compat-child" && currentCompatChildHelperName !== undefined) {
192
+ return [`${outVar} += ${currentCompatChildHelperName}(${node.code});`];
193
+ }
182
194
  return [`${outVar} += ${escapeHelperName}(${node.code});`];
183
195
  }
184
196
  if (node.kind === "conditional") {
@@ -364,6 +376,9 @@ function collectHtmlParts(node, escapeHelperName, escapeBatchHelperName, asyncCo
364
376
  if (node.renderMode === "react-node" && reactNodeRenderHelperName !== undefined) {
365
377
  return [`${reactNodeRenderHelperName}(() => (${node.code}))`];
366
378
  }
379
+ if (node.renderMode === "compat-child" && currentCompatChildHelperName !== undefined) {
380
+ return [`${currentCompatChildHelperName}(${node.code})`];
381
+ }
367
382
  return [`${escapeHelperName}(${node.code})`];
368
383
  }
369
384
  if (node.kind === "conditional") {
@@ -514,7 +529,9 @@ function collectHtmlAttributeParts(tagName, attr, escapeHelperName, escapeBatchH
514
529
  }
515
530
  if (attr.name === "style") {
516
531
  return [
517
- emitDynamicStyleAttributeExpression(attr.code, escapeHelperName, escapeBatchHelperName),
532
+ attr.serialization === "compat"
533
+ ? emitCompatDynamicStyleAttributeExpression(attr.code, escapeHelperName)
534
+ : emitDynamicStyleAttributeExpression(attr.code, escapeHelperName, escapeBatchHelperName),
518
535
  ];
519
536
  }
520
537
  if (isDangerousHtmlAttribute(htmlName)) {
@@ -525,7 +542,11 @@ function collectHtmlAttributeParts(tagName, attr, escapeHelperName, escapeBatchH
525
542
  `(() => { const _value = (${attr.code}); if (_value == null || _value === false) return ""; if (typeof _value === "object" && _value !== null && typeof _value.__html === "string") return ${stringLiteral(` ${htmlName}="`)} + ${escapeHelperName}(_value.__html) + ${stringLiteral('"')}; return ""; })()`,
526
543
  ];
527
544
  }
528
- return [emitDynamicAttributeExpression(htmlName, attr.code, escapeHelperName)];
545
+ return [
546
+ attr.serialization === "compat" && !isUrlAttribute(htmlName)
547
+ ? emitCompatDynamicAttributeExpression(htmlName, attr.code, escapeHelperName)
548
+ : emitDynamicAttributeExpression(htmlName, attr.code, escapeHelperName),
549
+ ];
529
550
  }
530
551
  function collectElementAttributeParts(tagName, attrs, escapeHelperName, escapeBatchHelperName, dynamicAttributes, attributeScan = scanElementAttributes(tagName, attrs)) {
531
552
  if (dynamicAttributes === "emit" && attrs.some((attr) => attr.kind === "spread-attr")) {
@@ -620,6 +641,29 @@ function emitDynamicAttributeExpression(name, code, escapeHelperName) {
620
641
  ? `(() => { const _value = (${code}); return _value == null ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_value) + ${stringLiteral('"')}; })()`
621
642
  : `(() => { const _value = (${code}); return _value == null || _value === false ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_value === true ? "" : _value) + ${stringLiteral('"')}; })()`;
622
643
  }
644
+ // Mirrors packages/react-compat/src/server-render.ts renderHtmlAttribute for
645
+ // dynamic values: functions and objects drop, booleans serialize as
646
+ // "true"/"false" for booleanish-string and data attributes, and false drops
647
+ // elsewhere. Byte parity with the interpreter is pinned by tests.
648
+ function emitCompatDynamicAttributeExpression(name, code, escapeHelperName) {
649
+ const lowerCased = name.toLowerCase();
650
+ const booleanishOrData = lowerCased.startsWith("aria-") ||
651
+ lowerCased.startsWith("data-") ||
652
+ lowerCased === "contenteditable" ||
653
+ lowerCased === "draggable" ||
654
+ lowerCased === "spellcheck";
655
+ const booleanBranch = booleanishOrData
656
+ ? `return ${stringLiteral(` ${name}="`)} + (_value ? "true" : "false") + ${stringLiteral('"')};`
657
+ : `return _value ? ${stringLiteral(` ${name}=""`)} : "";`;
658
+ return `(() => { const _value = (${code}); if (_value == null || typeof _value === "function") return ""; if (typeof _value === "boolean") { ${booleanBranch} } if (typeof _value === "object") return ""; return ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_value) + ${stringLiteral('"')}; })()`;
659
+ }
660
+ // Mirrors packages/react-compat/src/server-render.ts renderStyleAttribute and
661
+ // renderCssValue: skips null/boolean/empty entries and appends px to nonzero
662
+ // numeric values outside the react unitless list.
663
+ function emitCompatDynamicStyleAttributeExpression(code, escapeHelperName) {
664
+ const unitlessCheck = '_styleName === "flex" || _styleName === "fontWeight" || _styleName === "lineHeight" || _styleName === "opacity" || _styleName === "order" || _styleName === "zIndex" || _styleName === "zoom"';
665
+ return `(() => { const _value = (${code}); if (_value == null || typeof _value !== "object") return ""; let _style = ""; for (const _styleName in _value) { const _styleValue = _value[_styleName]; if (_styleValue == null || typeof _styleValue === "boolean" || _styleValue === "") continue; const _cssName = _styleName.startsWith("--") ? _styleName : _styleName.replace(/[A-Z]/g, (_char) => "-" + _char.toLowerCase()); const _css = typeof _styleValue !== "number" || _styleValue === 0 || (${unitlessCheck}) ? String(_styleValue) : _styleValue + "px"; _style += (_style === "" ? "" : ";") + ${escapeHelperName}(_cssName) + ":" + ${escapeHelperName}(_css); } return _style === "" ? "" : ${stringLiteral(' style="')} + _style + ${stringLiteral('"')}; })()`;
666
+ }
623
667
  function emitDynamicStyleAttributeExpression(code, escapeHelperName, escapeBatchHelperName) {
624
668
  const staticStyleExpression = emitStaticStyleObjectAttributeExpression(code, escapeHelperName);
625
669
  if (staticStyleExpression !== undefined) {
@@ -704,14 +748,18 @@ function emitBatchedSimpleChildrenExpression(children, escapeBatchHelperName) {
704
748
  if (escapeBatchHelperName === undefined) {
705
749
  return undefined;
706
750
  }
707
- const dynamicChildren = children.filter((child) => child.kind === "expr" && child.renderMode !== "html" && child.renderMode !== "react-node");
751
+ const dynamicChildren = children.filter((child) => child.kind === "expr" &&
752
+ child.renderMode !== "html" &&
753
+ child.renderMode !== "react-node" &&
754
+ child.renderMode !== "compat-child");
708
755
  if (dynamicChildren.length < 2) {
709
756
  return undefined;
710
757
  }
711
758
  if (children.some((child) => child.kind !== "text" &&
712
759
  !(child.kind === "expr" &&
713
760
  child.renderMode !== "html" &&
714
- child.renderMode !== "react-node"))) {
761
+ child.renderMode !== "react-node" &&
762
+ child.renderMode !== "compat-child"))) {
715
763
  return undefined;
716
764
  }
717
765
  const values = dynamicChildren.map((child) => child.code);
@@ -885,6 +933,31 @@ function replaceOxcServerStringReactNodeRenderHelper(code, helperName) {
885
933
  function usesClientBoundary(ir) {
886
934
  return ir.components.some((component) => containsClientBoundary(component.root));
887
935
  }
936
+ function usesCompatChildRender(ir) {
937
+ return ir.components.some((component) => containsCompatChildRender(component.root));
938
+ }
939
+ function containsCompatChildRender(node) {
940
+ if (node.kind === "expr") {
941
+ return node.renderMode === "compat-child";
942
+ }
943
+ if (node.kind === "conditional") {
944
+ return [...node.whenTrue, ...node.whenFalse].some(containsCompatChildRender);
945
+ }
946
+ if (node.kind === "list" || node.kind === "element" || node.kind === "fragment") {
947
+ return node.children.some(containsCompatChildRender);
948
+ }
949
+ if (node.kind === "async-boundary") {
950
+ return [
951
+ ...node.children,
952
+ ...(node.placeholderChildren ?? []),
953
+ ...(node.catchChildren ?? []),
954
+ ].some(containsCompatChildRender);
955
+ }
956
+ if (node.kind === "component") {
957
+ return node.children.some(containsCompatChildRender);
958
+ }
959
+ return false;
960
+ }
888
961
  function emitClientBoundaryHelper(name) {
889
962
  const propsHelperName = `${name}$hasNonSerializableProps`;
890
963
  return [