@reckona/mreact-compiler 0.0.145 → 0.0.146

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.
@@ -322,7 +322,7 @@ function emitComponent(
322
322
  helperNames: CompatHelperNames,
323
323
  dev: boolean,
324
324
  ): string {
325
- const directTextBindings = collectDirectTextBindings(component);
325
+ const directTextBindings = collectDirectTextBindings(component, helperNames);
326
326
  const body = component.bodyStatements.map((statement) =>
327
327
  ` ${rewriteDirectTextBindingStatement(statement, directTextBindings, helperNames)}`
328
328
  );
@@ -560,8 +560,12 @@ function emitComponentProps(
560
560
  return `{ ${entries.join(", ")} }`;
561
561
  }
562
562
 
563
- function collectDirectTextBindings(component: ComponentIr): DirectTextBinding[] {
563
+ function collectDirectTextBindings(
564
+ component: ComponentIr,
565
+ helperNames?: CompatHelperNames,
566
+ ): DirectTextBinding[] {
564
567
  const candidates: DirectTextBinding[] = [];
568
+ const allocator = createNameAllocator(collectReservedComponentLocalNames(component, helperNames));
565
569
 
566
570
  for (const statement of component.bodyStatements) {
567
571
  const match = statement.match(
@@ -575,14 +579,27 @@ function collectDirectTextBindings(component: ComponentIr): DirectTextBinding[]
575
579
 
576
580
  candidates.push({
577
581
  stateName,
578
- tupleName: `_${stateName}StateTuple`,
579
- bindingName: `_${stateName}TextBinding`,
582
+ tupleName: allocator(`_${stateName}StateTuple`),
583
+ bindingName: allocator(`_${stateName}TextBinding`),
580
584
  });
581
585
  }
582
586
 
583
587
  return candidates.filter((candidate) => directTextBindingIsSafe(component, candidate));
584
588
  }
585
589
 
590
+ function collectReservedComponentLocalNames(
591
+ component: ComponentIr,
592
+ helperNames?: CompatHelperNames,
593
+ ): string[] {
594
+ return [
595
+ component.name,
596
+ component.exportName,
597
+ ...component.parameters,
598
+ ...component.bindingNames,
599
+ ...Object.values(helperNames ?? {}).filter((name): name is string => name !== undefined),
600
+ ];
601
+ }
602
+
586
603
  function directTextBindingIsSafe(
587
604
  component: ComponentIr,
588
605
  candidate: DirectTextBinding,
@@ -601,6 +618,11 @@ function directTextBindingIsSafe(
601
618
  return;
602
619
  }
603
620
 
621
+ if (nodeHasStructuralIdentifierUse(node, candidate.stateName)) {
622
+ unsafe = true;
623
+ return;
624
+ }
625
+
604
626
  if (node.kind === "element") {
605
627
  for (const attr of node.attributes) {
606
628
  if (attr.kind === "static-attr") {
@@ -627,6 +649,51 @@ function directTextBindingIsSafe(
627
649
  return directTextUses === 1 && !unsafe && hasDirectTextBindingHost(component.root, candidate);
628
650
  }
629
651
 
652
+ function nodeHasStructuralIdentifierUse(node: JsxNodeIr, stateName: string): boolean {
653
+ if (node.kind === "conditional") {
654
+ return containsIdentifier(node.conditionCode, stateName);
655
+ }
656
+
657
+ if (node.kind === "list") {
658
+ return [
659
+ node.itemsCode,
660
+ node.keyCode,
661
+ ...(node.bodyStatements ?? []),
662
+ ].some((code) => code !== undefined && containsIdentifier(code, stateName));
663
+ }
664
+
665
+ if (node.kind === "component") {
666
+ return (
667
+ (node.keyCode !== undefined && containsIdentifier(node.keyCode, stateName)) ||
668
+ node.props.some((prop) => {
669
+ if (prop.kind === "render-prop") {
670
+ return false;
671
+ }
672
+
673
+ return containsIdentifier(prop.code, stateName);
674
+ })
675
+ );
676
+ }
677
+
678
+ if (node.kind === "element") {
679
+ return node.keyCode !== undefined && containsIdentifier(node.keyCode, stateName);
680
+ }
681
+
682
+ if (node.kind === "async-boundary") {
683
+ return [
684
+ node.valueCode,
685
+ node.placeholderTagCode,
686
+ node.catchName,
687
+ ].some((code) => code !== undefined && containsIdentifier(code, stateName));
688
+ }
689
+
690
+ if (node.kind === "fragment") {
691
+ return (node.bodyStatements ?? []).some((statement) => containsIdentifier(statement, stateName));
692
+ }
693
+
694
+ return false;
695
+ }
696
+
630
697
  function isDirectTextBindingDeclaration(statement: string, stateName: string): boolean {
631
698
  return new RegExp(
632
699
  `^\\s*const\\s+\\[\\s*${stateName}\\s*,\\s*[A-Za-z_$][\\w$]*\\s*\\]\\s*=\\s*useState\\(.+\\);\\s*$`,
@@ -625,7 +625,7 @@ function emitAppendStatements(
625
625
  );
626
626
  }
627
627
 
628
- return ` await ${part.name}(${sinkName}, ${emitPropsObject(part.props, part.children, part.escapeHelperName)});`;
628
+ return ` await ${part.name}(${sinkName}, ${emitPropsObject(part.props, part.children, part.escapeHelperName, part.name)});`;
629
629
  }
630
630
 
631
631
  if (part.kind === "react-node") {
@@ -721,7 +721,7 @@ function emitSyncPartAsAppendStatement(
721
721
  );
722
722
  }
723
723
 
724
- return `${indent}await ${part.name}(${sinkName}, ${emitPropsObject(part.props, part.children, part.escapeHelperName)});`;
724
+ return `${indent}await ${part.name}(${sinkName}, ${emitPropsObject(part.props, part.children, part.escapeHelperName, part.name)});`;
725
725
  }
726
726
 
727
727
  if (part.kind === "react-node") {
@@ -868,7 +868,7 @@ function tryEmitPartAsStringExpression(
868
868
  }
869
869
  if (part.kind === "component" && part.hydrationId === undefined) {
870
870
  return emitRenderableHtmlExpression(
871
- `${part.name}(${emitPropsObject(part.props, part.children, part.escapeHelperName)})`,
871
+ `${part.name}(${emitPropsObject(part.props, part.children, part.escapeHelperName, part.name)})`,
872
872
  );
873
873
  }
874
874
  // Non-compat component parts require `await sink-write`; lists with
@@ -2491,6 +2491,7 @@ function emitPropsObject(
2491
2491
  props: ComponentPropIr[],
2492
2492
  children: JsxNodeIr[] = [],
2493
2493
  escapeHelperName = "_escapeHtml",
2494
+ componentName?: string,
2494
2495
  ): string {
2495
2496
  const entries = props.map((prop) => {
2496
2497
  if (prop.kind === "spread-prop") {
@@ -2505,17 +2506,21 @@ function emitPropsObject(
2505
2506
  });
2506
2507
 
2507
2508
  if (children.length > 0) {
2509
+ const childrenExpression =
2510
+ emitStreamRendererFromChildren(children, escapeHelperName) ??
2511
+ emitHtmlExpressionFromChildren(children, escapeHelperName);
2508
2512
  entries.push(
2509
- `children: ${
2510
- emitStreamRendererFromChildren(children, escapeHelperName) ??
2511
- emitHtmlExpressionFromChildren(children, escapeHelperName)
2512
- }`,
2513
+ `children: ${isRouterLinkComponentName(componentName) ? `${componentName}.trustedHtml(${childrenExpression})` : childrenExpression}`,
2513
2514
  );
2514
2515
  }
2515
2516
 
2516
2517
  return `{ ${entries.join(", ")} }`;
2517
2518
  }
2518
2519
 
2520
+ function isRouterLinkComponentName(name: string | undefined): name is string {
2521
+ return name !== undefined && (name === "Link" || name.endsWith(".Link"));
2522
+ }
2523
+
2519
2524
  function emitCompatRuntimePropsObject(
2520
2525
  props: ComponentPropIr[],
2521
2526
  children: JsxNodeIr[] = [],
@@ -559,6 +559,7 @@ function collectHtmlStatements(
559
559
  contextProviderHelperName,
560
560
  contextConsumerHelperName,
561
561
  reactNodeRenderHelperName,
562
+ node.name,
562
563
  ),
563
564
  asyncComponentNames,
564
565
  )};`,
@@ -1467,6 +1468,7 @@ function emitPropsObject(
1467
1468
  contextProviderHelperName?: string,
1468
1469
  contextConsumerHelperName?: string,
1469
1470
  reactNodeRenderHelperName?: string,
1471
+ componentName?: string,
1470
1472
  ): string {
1471
1473
  const entries = props.map((prop) => {
1472
1474
  if (prop.kind === "spread-prop") {
@@ -1481,14 +1483,28 @@ function emitPropsObject(
1481
1483
  });
1482
1484
 
1483
1485
  if (children.length > 0) {
1486
+ const childrenExpression = emitHtmlExpressionFromChildren(
1487
+ children,
1488
+ escapeHelperName,
1489
+ escapeBatchHelperName,
1490
+ asyncComponentNames,
1491
+ dynamicAttributes,
1492
+ contextProviderHelperName,
1493
+ contextConsumerHelperName,
1494
+ reactNodeRenderHelperName,
1495
+ );
1484
1496
  entries.push(
1485
- `children: ${emitHtmlExpressionFromChildren(children, escapeHelperName, escapeBatchHelperName, asyncComponentNames, dynamicAttributes, contextProviderHelperName, contextConsumerHelperName, reactNodeRenderHelperName)}`,
1497
+ `children: ${isRouterLinkComponentName(componentName) ? `${componentName}.trustedHtml(${childrenExpression})` : childrenExpression}`,
1486
1498
  );
1487
1499
  }
1488
1500
 
1489
1501
  return `{ ${entries.join(", ")} }`;
1490
1502
  }
1491
1503
 
1504
+ function isRouterLinkComponentName(name: string | undefined): name is string {
1505
+ return name !== undefined && (name === "Link" || name.endsWith(".Link"));
1506
+ }
1507
+
1492
1508
  function emitCompatRuntimePropsObject(
1493
1509
  props: ComponentPropIr[],
1494
1510
  children: JsxNodeIr[] = [],