@reckona/mreact-compiler 0.0.120 → 0.0.121

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,EAKV,QAAQ,EACT,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAkBrE,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,CACxB,EAAE,EAAE,QAAQ,EACZ,OAAO,GAAE,iBAAsB,GAC9B,UAAU,CA8GZ"}
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;AAkBrE,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,CA6GpF"}
@@ -11,9 +11,7 @@ let currentClientBoundaryHelperName;
11
11
  let currentSpreadAttributesHelperName = "_renderSpreadAttributes";
12
12
  export function emitServer(ir, options = {}) {
13
13
  const escapeHelperName = allocateEscapeHelperName(ir);
14
- const escapeBatchHelperName = options.escape === undefined
15
- ? undefined
16
- : allocateHelperName(ir, "_escapeHtmlBatch");
14
+ const escapeBatchHelperName = options.escape === undefined ? undefined : allocateHelperName(ir, "_escapeHtmlBatch");
17
15
  const contextProviderHelperName = usesContextProvider(ir)
18
16
  ? allocateHelperName(ir, "_renderContextProviderToString")
19
17
  : undefined;
@@ -114,16 +112,10 @@ function emitContextImport(contextProviderHelperName, contextConsumerHelperName,
114
112
  function collectContextImports(contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName) {
115
113
  const specifiers = [
116
114
  reactNodeRenderHelperName === undefined ? undefined : "renderToString",
117
- contextProviderHelperName === undefined
118
- ? undefined
119
- : "renderContextProviderToString",
120
- contextConsumerHelperName === undefined
121
- ? undefined
122
- : "renderContextConsumerToString",
115
+ contextProviderHelperName === undefined ? undefined : "renderContextProviderToString",
116
+ contextConsumerHelperName === undefined ? undefined : "renderContextConsumerToString",
123
117
  ].filter((specifier) => specifier !== undefined);
124
- return specifiers.length === 0
125
- ? []
126
- : [{ source: "@reckona/mreact-compat", specifiers }];
118
+ return specifiers.length === 0 ? [] : [{ source: "@reckona/mreact-compat", specifiers }];
127
119
  }
128
120
  function emitUserImports(ir) {
129
121
  return ir.userImports.join("\n");
@@ -154,7 +146,7 @@ function emitComponent(component, escapeHelperName, escapeBatchHelperName, outAc
154
146
  function emitHtmlExpression(node, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName) {
155
147
  const parts = collectHtmlParts(node, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName);
156
148
  if (parts.length === 0) {
157
- return "\"\"";
149
+ return '""';
158
150
  }
159
151
  return parts.join(" + ");
160
152
  }
@@ -291,8 +283,12 @@ function collectHtmlStatements(node, outVar, escapeHelperName, escapeBatchHelper
291
283
  if (isClientBoundaryPlaceholder(node)) {
292
284
  const helperName = currentClientBoundaryHelperName;
293
285
  if (helperName !== undefined) {
286
+ const boundaryProps = emitPropsObject(node.props, [], escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName);
287
+ const fallbackHtml = shouldRenderClientBoundaryFallback(node)
288
+ ? emitComponentCallExpression(node.name, boundaryProps, asyncComponentNames)
289
+ : emitHtmlExpressionFromChildren(node.children, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName);
294
290
  return [
295
- `${outVar} += ${helperName}(${stringLiteral(node.name)}, ${emitPropsObject(node.props, [], escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName)}, ${emitHtmlExpressionFromChildren(node.children, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName)});`,
291
+ `${outVar} += ${helperName}(${stringLiteral(node.name)}, ${boundaryProps}, ${fallbackHtml});`,
296
292
  ];
297
293
  }
298
294
  return [`${outVar} += ${stringLiteral(clientBoundaryPlaceholder(node))};`];
@@ -341,9 +337,7 @@ function collectHtmlStatements(node, outVar, escapeHelperName, escapeBatchHelper
341
337
  return statements;
342
338
  }
343
339
  const childrenExpression = emitBatchedSimpleChildrenExpression(node.children, escapeBatchHelperName);
344
- const childSelectedValueCode = node.tagName === "select"
345
- ? attributeScan.formValueAttributeCode
346
- : undefined;
340
+ const childSelectedValueCode = node.tagName === "select" ? attributeScan.formValueAttributeCode : undefined;
347
341
  if (childrenExpression !== undefined && childSelectedValueCode === undefined) {
348
342
  statements.push(`${outVar} += ${childrenExpression};`);
349
343
  }
@@ -391,7 +385,9 @@ function collectHtmlParts(node, escapeHelperName, escapeBatchHelperName, asyncCo
391
385
  // Synchronous list — imperative accumulator avoids the per-render
392
386
  // callback allocation, the intermediate `.map()` result array, and the
393
387
  // trailing `.join("")` call.
394
- return [emitSyncListIife(node, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName)];
388
+ return [
389
+ emitSyncListIife(node, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName),
390
+ ];
395
391
  }
396
392
  if (node.kind === "fragment") {
397
393
  return node.children.flatMap((child) => collectHtmlParts(child, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName));
@@ -453,9 +449,7 @@ function collectHtmlParts(node, escapeHelperName, escapeBatchHelperName, asyncCo
453
449
  }
454
450
  const childrenExpression = emitBatchedSimpleChildrenExpression(node.children, escapeBatchHelperName);
455
451
  const attributeScan = scanElementAttributes(node.tagName, node.attributes);
456
- const childSelectedValueCode = node.tagName === "select"
457
- ? attributeScan.formValueAttributeCode
458
- : undefined;
452
+ const childSelectedValueCode = node.tagName === "select" ? attributeScan.formValueAttributeCode : undefined;
459
453
  const selectedAttributePart = collectOptionSelectedAttributePart(node, selectedValueCode);
460
454
  const dangerousInnerHtml = emitDangerouslySetInnerHtmlExpression(node.attributes);
461
455
  const childrenParts = dangerousInnerHtml !== undefined
@@ -509,14 +503,16 @@ function collectHtmlAttributeParts(tagName, attr, escapeHelperName, escapeBatchH
509
503
  return [];
510
504
  }
511
505
  if (attr.name === "style") {
512
- return [emitDynamicStyleAttributeExpression(attr.code, escapeHelperName, escapeBatchHelperName)];
506
+ return [
507
+ emitDynamicStyleAttributeExpression(attr.code, escapeHelperName, escapeBatchHelperName),
508
+ ];
513
509
  }
514
510
  if (isDangerousHtmlAttribute(htmlName)) {
515
511
  // Dynamic srcdoc must arrive as `{ __html: "..." }`. Drop anything
516
512
  // else at runtime so a value computed from a loader cannot inject
517
513
  // executable HTML into the iframe document.
518
514
  return [
519
- `(() => { 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 ""; })()`,
515
+ `(() => { 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 ""; })()`,
520
516
  ];
521
517
  }
522
518
  return [emitDynamicAttributeExpression(htmlName, attr.code, escapeHelperName)];
@@ -575,8 +571,7 @@ function scanElementAttributes(tagName, attrs) {
575
571
  if ((tagName === "textarea" || tagName === "select") && attr.name === "value") {
576
572
  valueAttributeCode = readFormValueAttributeCode(attr);
577
573
  }
578
- else if ((tagName === "textarea" || tagName === "select") &&
579
- attr.name === "defaultValue") {
574
+ else if ((tagName === "textarea" || tagName === "select") && attr.name === "defaultValue") {
580
575
  defaultValueAttributeCode = readFormValueAttributeCode(attr);
581
576
  }
582
577
  }
@@ -598,7 +593,7 @@ function emitDynamicAttributeExpression(name, code, escapeHelperName) {
598
593
  // returns the value when safe and `undefined` when the attribute
599
594
  // should be dropped. Using an IIFE here is necessary because we
600
595
  // need to capture the value once and branch on the helper output.
601
- return `(() => { const _value = (${code}); if (_value == null || _value === false) return ""; const _checked = ${currentUrlSafeHelperName}(${stringLiteral(name)}, _value === true ? "" : _value); return _checked === undefined ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_checked) + ${stringLiteral("\"")}; })()`;
596
+ return `(() => { const _value = (${code}); if (_value == null || _value === false) return ""; const _checked = ${currentUrlSafeHelperName}(${stringLiteral(name)}, _value === true ? "" : _value); return _checked === undefined ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_checked) + ${stringLiteral('"')}; })()`;
602
597
  }
603
598
  const inlineExpr = simpleSideEffectFreeExpression(code);
604
599
  if (inlineExpr !== undefined) {
@@ -606,9 +601,9 @@ function emitDynamicAttributeExpression(name, code, escapeHelperName) {
606
601
  // Safe because `simpleSideEffectFreeExpression` only matches expressions
607
602
  // whose evaluation has no observable side effects (identifier read,
608
603
  // member chain, literal, this).
609
- return `(${inlineExpr} == null || ${inlineExpr} === false ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(${inlineExpr} === true ? "" : ${inlineExpr}) + ${stringLiteral("\"")})`;
604
+ return `(${inlineExpr} == null || ${inlineExpr} === false ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(${inlineExpr} === true ? "" : ${inlineExpr}) + ${stringLiteral('"')})`;
610
605
  }
611
- return `(() => { const _value = (${code}); return _value == null || _value === false ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_value === true ? "" : _value) + ${stringLiteral("\"")}; })()`;
606
+ return `(() => { const _value = (${code}); return _value == null || _value === false ? "" : ${stringLiteral(` ${name}="`)} + ${escapeHelperName}(_value === true ? "" : _value) + ${stringLiteral('"')}; })()`;
612
607
  }
613
608
  function emitDynamicStyleAttributeExpression(code, escapeHelperName, escapeBatchHelperName) {
614
609
  const staticStyleExpression = emitStaticStyleObjectAttributeExpression(code, escapeHelperName);
@@ -618,7 +613,7 @@ function emitDynamicStyleAttributeExpression(code, escapeHelperName, escapeBatch
618
613
  const escapedPair = escapeBatchHelperName === undefined
619
614
  ? `${escapeHelperName}(_cssName) + ":" + ${escapeHelperName}(_styleValue === true ? "" : _styleValue)`
620
615
  : `(() => { const _escaped = ${escapeBatchHelperName}([_cssName, _styleValue === true ? "" : _styleValue]); return _escaped[0] + ":" + _escaped[1]; })()`;
621
- return `(() => { const _value = (${code}); if (_value == null || _value === false) return ""; if (typeof _value === "string") { const _style = ${escapeHelperName}(_value); return _style === "" ? "" : ${stringLiteral(" style=\"")} + _style + ${stringLiteral("\"")}; } const _style = Object.entries(_value).filter(([, _styleValue]) => _styleValue != null && _styleValue !== false).map(([_styleName, _styleValue]) => { const _cssName = String(_styleName).startsWith("--") ? String(_styleName) : String(_styleName).replace(/[A-Z]/g, (_char) => "-" + _char.toLowerCase()); return ${escapedPair}; }).join(";"); return _style === "" ? "" : ${stringLiteral(" style=\"")} + _style + ${stringLiteral("\"")}; })()`;
616
+ return `(() => { const _value = (${code}); if (_value == null || _value === false) return ""; if (typeof _value === "string") { const _style = ${escapeHelperName}(_value); return _style === "" ? "" : ${stringLiteral(' style="')} + _style + ${stringLiteral('"')}; } const _style = Object.entries(_value).filter(([, _styleValue]) => _styleValue != null && _styleValue !== false).map(([_styleName, _styleValue]) => { const _cssName = String(_styleName).startsWith("--") ? String(_styleName) : String(_styleName).replace(/[A-Z]/g, (_char) => "-" + _char.toLowerCase()); return ${escapedPair}; }).join(";"); return _style === "" ? "" : ${stringLiteral(' style="')} + _style + ${stringLiteral('"')}; })()`;
622
617
  }
623
618
  function emitStaticStyleObjectAttributeExpression(code, escapeHelperName) {
624
619
  const entries = parseStaticStyleObjectLiteral(code);
@@ -646,7 +641,7 @@ function emitStaticStyleObjectAttributeExpression(code, escapeHelperName) {
646
641
  // Stage A — needSep tracking with inline string accumulator, no intermediate
647
642
  // array allocation and no `.join(";")` per render.
648
643
  const statements = entries.map((entry) => `{ const _v = (${entry.valueCode}); if (_v != null && _v !== false) _style += (_style === "" ? "" : ";") + ${stringLiteral(`${entry.cssName}:`)} + ${escapeHelperName}(_v === true ? "" : _v); }`);
649
- return `(() => { let _style = ""; ${statements.join(" ")} return _style === "" ? "" : ${stringLiteral(" style=\"")} + _style + ${stringLiteral("\"")}; })()`;
644
+ return `(() => { let _style = ""; ${statements.join(" ")} return _style === "" ? "" : ${stringLiteral(' style="')} + _style + ${stringLiteral('"')}; })()`;
650
645
  }
651
646
  function collectTextareaValueParts(node, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName, attributeScan = scanElementAttributes(node.tagName, node.attributes)) {
652
647
  const valueCode = attributeScan.formValueAttributeCode;
@@ -668,7 +663,9 @@ function collectOptionSelectedAttributePart(node, selectedValueCode) {
668
663
  function findOptionValueCode(node) {
669
664
  const valueAttr = node.attributes.find((attr) => attr.kind !== "spread-attr" && attr.name === "value");
670
665
  if (valueAttr !== undefined && valueAttr.kind !== "event" && valueAttr.kind !== "spread-attr") {
671
- return valueAttr.kind === "static-attr" ? stringLiteral(valueAttr.value) : `(${valueAttr.code})`;
666
+ return valueAttr.kind === "static-attr"
667
+ ? stringLiteral(valueAttr.value)
668
+ : `(${valueAttr.code})`;
672
669
  }
673
670
  return node.children.every((child) => child.kind === "text")
674
671
  ? stringLiteral(node.children.map((child) => child.value).join(""))
@@ -697,7 +694,9 @@ function emitBatchedSimpleChildrenExpression(children, escapeBatchHelperName) {
697
694
  return undefined;
698
695
  }
699
696
  if (children.some((child) => child.kind !== "text" &&
700
- !(child.kind === "expr" && child.renderMode !== "html" && child.renderMode !== "react-node"))) {
697
+ !(child.kind === "expr" &&
698
+ child.renderMode !== "html" &&
699
+ child.renderMode !== "react-node"))) {
701
700
  return undefined;
702
701
  }
703
702
  const values = dynamicChildren.map((child) => child.code);
@@ -714,7 +713,7 @@ function emitBatchedSimpleChildrenExpression(children, escapeBatchHelperName) {
714
713
  }
715
714
  function emitHtmlExpressionFromChildren(children, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName) {
716
715
  if (children.length === 0) {
717
- return "\"\"";
716
+ return '""';
718
717
  }
719
718
  return emitHtmlExpression({ kind: "fragment", children }, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName);
720
719
  }
@@ -775,24 +774,27 @@ function emitCompatRuntimePropsObject(props, children = []) {
775
774
  }
776
775
  function emitComponentCallExpression(name, propsCode, asyncComponentNames) {
777
776
  const call = `${name}(${propsCode})`;
778
- return asyncComponentNames.has(name) ? `(await ${call})` : call;
777
+ return emitRenderableHtmlExpression(asyncComponentNames.has(name) ? `(await ${call})` : call);
778
+ }
779
+ function emitRenderableHtmlExpression(code) {
780
+ return `((_value) => _value == null || typeof _value === "boolean" ? "" : _value)(${code})`;
779
781
  }
780
782
  function isClientBoundaryPlaceholder(node) {
781
783
  return node.clientReference !== undefined;
782
784
  }
785
+ function shouldRenderClientBoundaryFallback(node) {
786
+ return node.clientReference?.ssrFallback === true;
787
+ }
783
788
  function clientBoundaryPlaceholder(node) {
784
789
  return `<!--mreact-client-boundary:${escapeHtml(node.name)}-->`;
785
790
  }
786
791
  function collectAsyncServerComponentNames(components) {
787
- const names = new Set(components
788
- .filter((component) => component.async === true)
789
- .map((component) => component.name));
792
+ const names = new Set(components.filter((component) => component.async === true).map((component) => component.name));
790
793
  let changed = true;
791
794
  while (changed) {
792
795
  changed = false;
793
796
  for (const component of components) {
794
- if (!names.has(component.name) &&
795
- containsAsyncServerOperation(component.root, names)) {
797
+ if (!names.has(component.name) && containsAsyncServerOperation(component.root, names)) {
796
798
  names.add(component.name);
797
799
  changed = true;
798
800
  }