Skip to content

Conversation

@codesmith17
Copy link

@codesmith17 codesmith17 commented Oct 31, 2025

## Summary
This PR fixes issue #1165 where JSX elements using member expressions with lowercase variable names (e.g., `form.Button`) were incorrectly treated as HTML element strings instead of React component identifiers.

## The Problem
The compiler was only checking if element names started with an uppercase letter, which broke common library patterns like Tanstack Forms that use lowercase variables with member expressions:

```tsx
// This was broken before:
const form = useForm();
<form.CancelButton>Cancel</form.CancelButton> // Treated as string, broke UI

Users were forced to use uppercase variable names as a workaround:

// Workaround that worked:
const FormContent = useForm();
<FormContent.CancelButton>Cancel</FormContent.CancelButton> // Worked correctly

The Solution

Updated jsx-scope-inject.ts (line 58-63) to check for member expressions (containing dots) before checking case sensitivity:

// Before (broken):
const as = /^[A-Z]/.test(originalJsxElementName)
  ? t.identifier(originalJsxElementName)
  : originalJsxElementName;

// After (fixed):
const isMemberExpression = originalJsxElementName.includes(".");
const isComponent = /^[A-Z]/.test(originalJsxElementName);
const as = isMemberExpression || isComponent
  ? t.identifier(originalJsxElementName)
  : originalJsxElementName;

Changes

  • Updated packages/compiler/src/jsx-scope-inject.ts to recognize member expressions
  • Added test component demonstrating both cases work correctly
  • Now both form.Button and Form.Button are correctly treated as component identifiers

Testing

Tested with the vite-project demo showing both lowercase and uppercase member expressions render and function correctly with full styling and interactivity preserved.

Impact

This allows developers to use standard library conventions without being forced to use uppercase variable names as a workaround. Fixes compatibility with:

  • Tanstack Forms (form.Field, form.Input, etc.)
  • Headless UI patterns (dialog.Panel, menu.Item, etc.)
  • Any library using lowercase + member expressions

Fixes #1165

codesmith17 and others added 2 commits October 31, 2025 18:01
This fixes issue lingodotdev#1165 where JSX elements using member expressions with
lowercase variable names (e.g., form.Button) were incorrectly treated as
HTML element strings instead of React component identifiers.

The compiler was only checking if element names started with an uppercase
letter, which broke common library patterns like Tanstack Forms that use
lowercase variables with member expressions.

Changes:
- Updated jsx-scope-inject.ts to check for member expressions (containing
  dots) before checking case sensitivity
- Now both 'form.Button' and 'Form.Button' are correctly treated as
  component identifiers
- Added test component demonstrating the fix works with both cases

This allows developers to use standard library conventions without being
forced to use uppercase variable names as a workaround.

Fixes lingodotdev#1165
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR attempts to fix an issue where JSX elements using member expressions with lowercase variable names (e.g., form.Button) were incorrectly treated as HTML element strings instead of React component identifiers. The fix adds a check for member expressions (containing dots) before the uppercase check in the compiler.

Key changes:

  • Updated jsx-scope-inject.ts to detect member expressions using .includes(".")
  • Added a test component demonstrating both lowercase and uppercase member expression patterns

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/compiler/src/jsx-scope-inject.ts Added member expression detection logic to determine when JSX elements should be treated as component references
demo/vite-project/src/components/tanstack-form-test.tsx Added demo component showcasing the fix for Tanstack Forms pattern with both lowercase and uppercase variable names

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 61 to 63
const as = isMemberExpression || isComponent
? t.identifier(originalJsxElementName)
: originalJsxElementName;
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using t.identifier() with a dotted string like "form.Button" creates an invalid AST node because JavaScript identifiers cannot contain dots. For member expressions, you need to build a proper member expression AST structure.

Consider creating a helper function that parses the dotted string and builds the correct AST:

function createMemberExpressionFromString(str: string): t.Expression {
  const parts = str.split('.');
  if (parts.length === 1) {
    return t.identifier(parts[0]);
  }
  
  let expr: t.Expression = t.identifier(parts[0]);
  for (let i = 1; i < parts.length; i++) {
    expr = t.memberExpression(expr, t.identifier(parts[i]));
  }
  return expr;
}

Then use it like:

const as = isMemberExpression || isComponent
  ? createMemberExpressionFromString(originalJsxElementName)
  : originalJsxElementName;

This will correctly generate form.Button as a member expression AST node instead of an invalid identifier.

Copilot uses AI. Check for mistakes.
<h2>Tanstack Forms + Lingo.dev Compiler Issue #1165</h2>

<div style={{ marginBottom: '40px' }}>
<h3>Broken: Lowercase variable name (form)</h3>
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The heading says "Broken" but this test file is included in the PR that fixes the issue. After the fix, this case should work correctly. Consider updating the heading to something like "Previously broken: Lowercase variable name (form) - now fixed" to accurately reflect the current state.

Suggested change
<h3>Broken: Lowercase variable name (form)</h3>
<h3>Previously broken: Lowercase variable name (form) - now fixed</h3>

Copilot uses AI. Check for mistakes.

// Add $as prop
const as = /^[A-Z]/.test(originalJsxElementName)
// Check if it's a member expression (contains dot) or starts with uppercase
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comment could be more specific about the rationale. Consider expanding it to explain why member expressions need special handling:

// Check if it's a member expression (contains dot) or starts with uppercase
// Member expressions (e.g., form.Button) and uppercase names (e.g., Button) 
// should be treated as component references, not HTML element strings

This makes it clearer for future maintainers why both conditions are checked.

Suggested change
// Check if it's a member expression (contains dot) or starts with uppercase
// Check if it's a member expression (contains dot) or starts with uppercase.
// Member expressions (e.g., form.Button) and uppercase names (e.g., Button)
// should be treated as component references, not HTML element strings.

Copilot uses AI. Check for mistakes.
- Add createMemberExpressionFromString() helper to build correct AST nodes
- Fix bug where t.identifier() was incorrectly used with dotted strings like 'form.Button'
- Improve code comments to explain member expression handling rationale
- Update demo heading to reflect that the issue is now fixed

This addresses all review comments from PR lingodotdev#1474
@codesmith17
Copy link
Author

Addressed the comments @maxprilutskiy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

React Tanstack Forms + Compiler: Buttons lose styling and functionality with standard lowercase syntax

1 participant