pierrejeambrun commented on code in PR #54060:
URL: https://github.com/apache/airflow/pull/54060#discussion_r2251658703


##########
dev/react-plugin-tools/bootstrap.py:
##########
@@ -24,18 +24,13 @@
 necessary configuration files, dependencies, and basic structure for 
development
 with the same tooling as used in Airflow's core UI.
 """
-
-from __future__ import annotations
-
 import argparse
 import re
 import shutil
 import sys
 from pathlib import Path
 
-
 def get_template_dir() -> Path:
-    """Get the template directory path."""

Review Comment:
   Why removing that?



##########
dev/react-plugin-tools/bootstrap.py:
##########
@@ -24,18 +24,13 @@
 necessary configuration files, dependencies, and basic structure for 
development
 with the same tooling as used in Airflow's core UI.
 """
-
-from __future__ import annotations
-

Review Comment:
   Why removing that? This is required by airflow pre-commit hook.



##########
dev/react-plugin-tools/react_plugin_template/ai_agents_rules/prompt-guidelines.md:
##########


Review Comment:
   No, we shouldn't have a rule on how to prompt the AI.
   
   The content of these files should be best practices / coding style / 
guidelines on how to develop a good react plugin following best practices. (You 
can take a look at airflow Core UI source to see what are the patterns we use 
most).
   
   For instance 'useEffect' should be avoided as much as possible. Displaying 
markdown should use our custom component instead of the one from 
`react-markdown` directly, how to write test file, when to write them.
   
   Commands on how to run `lintint`, and tests to help the agent automatically 
perform checks on suggestions, 
   
   Coding style about functional component (class component shouldn't be used 
at all) and so on.



##########
dev/react-plugin-tools/react_plugin_template/ai_agents_rules/review-checklist.md:
##########


Review Comment:
   I would remove this file altogether.



##########
dev/react-plugin-tools/bootstrap.py:
##########
@@ -45,55 +40,77 @@ def get_template_dir() -> Path:
 
     return template_dir
 
-
 def replace_template_variables(content: str, project_name: str) -> str:
-    """Replace template variables in file content."""
     return content.replace("{{PROJECT_NAME}}", project_name)
 
-
 def remove_apache_license_header(content: str, file_extension: str) -> str:
-    """Remove Apache license header from file content based on file type."""
     if file_extension in [".ts", ".tsx", ".js", ".jsx"]:
         license_pattern = r"/\*!\s*\*\s*Licensed to the Apache Software 
Foundation.*?\*/\s*"
         content = re.sub(license_pattern, "", content, flags=re.DOTALL)
-    elif file_extension in [".md"]:
-        license_pattern = r"<!--\s*Licensed to the Apache Software 
Foundation.*?-->\s*"
-        content = re.sub(license_pattern, "", content, flags=re.DOTALL)
-    elif file_extension in [".html"]:
+    elif file_extension in [".md", ".html"]:
         license_pattern = r"<!--\s*Licensed to the Apache Software 
Foundation.*?-->\s*"
         content = re.sub(license_pattern, "", content, flags=re.DOTALL)
-
     return content
 
-
-def copy_template_files(template_dir: Path, project_path: Path, project_name: 
str) -> None:
-    for item in template_dir.rglob("*"):
-        if item.is_file():
-            # Calculate relative path from template root
-            rel_path = item.relative_to(template_dir)
-            target_path = project_path / rel_path
-
-            target_path.parent.mkdir(parents=True, exist_ok=True)
-
-            with open(item, encoding="utf-8") as f:
-                content = f.read()
-
-            content = replace_template_variables(content, project_name)
-
-            file_extension = item.suffix.lower()
-            content = remove_apache_license_header(content, file_extension)
-
-            with open(target_path, "w", encoding="utf-8") as f:
-                f.write(content)
-
-            print(f"  Created: {rel_path}")
-
+def copy_and_process_file(src: Path, dest: Path, project_name: str) -> None:
+    dest.parent.mkdir(parents=True, exist_ok=True)
+    
+    with open(src, encoding="utf-8") as f:
+        content = f.read()
+    
+    content = replace_template_variables(content, project_name)
+    content = remove_apache_license_header(content, src.suffix.lower())
+    
+    with open(dest, "w", encoding="utf-8") as f:
+        f.write(content)
+
+def copy_directory_recursive(src_dir: Path, dest_dir: Path, project_name: str, 
skip_dirs: set = None) -> None:
+    """Recursively copy directory while processing files and skipping 
specified directories."""
+    if skip_dirs is None:
+        skip_dirs = set()
+    
+    for item in src_dir.iterdir():
+        # Skip directories that should be excluded
+        if item.is_dir() and item.name in skip_dirs:
+            print(f"✗ Skipping directory: {item.name}")
+            continue
+            
+        dest_item = dest_dir / item.name
+        
+        if item.is_dir():
+            dest_item.mkdir(parents=True, exist_ok=True)
+            copy_directory_recursive(item, dest_item, project_name, skip_dirs)
+        else:
+            # Process text files, copy binary files as-is
+            try:
+                copy_and_process_file(item, dest_item, project_name)
+            except UnicodeDecodeError:
+                # Binary file, copy as-is
+                shutil.copy2(item, dest_item)
+
+def copy_template_files(template_dir: Path, project_path: Path, project_name: 
str, include_ai_rules: bool) -> None:
+    
+    # Determine which directories to skip
+    skip_dirs = set()
+    if not include_ai_rules:
+        skip_dirs.add("ai_agents_rules")
+    
+    # Copy all files and directories, skipping excluded ones
+    copy_directory_recursive(template_dir, project_path, project_name, 
skip_dirs)
+    
+    # Provide feedback
+    if include_ai_rules:
+        ai_src = template_dir / "ai_agents_rules"
+        if ai_src.exists():

Review Comment:
   Folder should exist, don't even handle the case. If it does not, we should 
raise an exception and stop.



##########
dev/react-plugin-tools/react_plugin_template/ai_agents_rules/README.md:
##########


Review Comment:
   Nice readme. Thanks.



##########
dev/react-plugin-tools/bootstrap.py:
##########
@@ -105,75 +122,61 @@ def bootstrap_react_plugin(args) -> None:
         print("Error: Project name should only contain letters, numbers, 
hyphens, and underscores")
         sys.exit(1)
 
-    print(f"Creating React plugin project: {project_name}")
-    print(f"Target directory: {project_path}")
-    print(f"Template directory: {template_dir}")
+    # AI rules prompt
+    ai_rules_answer = input("Include AI coding rules? [y/N]: ").strip().lower()
+    include_ai_rules = ai_rules_answer in ("y", "yes")
+
+    print(f"\nCreating React plugin: {project_name}")
+    print(f"Location: {project_path}")
+    print(f"Include AI rules: {'YES' if include_ai_rules else 'NO'}")
 
     project_path.mkdir(parents=True, exist_ok=True)
 
     try:
-        # Copy template files
-        print("Copying template files...")
-        copy_template_files(template_dir, project_path, project_name)
+        print("\nCopying files...")
+        copy_template_files(template_dir, project_path, project_name, 
include_ai_rules)
 
-        print(f"\n✅ Successfully created {project_name}!")
+        print(f"\n✅ SUCCESS: Created {project_name}")
         print("\nNext steps:")
         print(f"  cd {target_dir}")
         print("  pnpm install")
         print("  pnpm dev")
-        print("\nHappy coding! 🚀")
+        
+        if include_ai_rules:
+            print("\n🤖 AI rules location: ai_agents_rules/")
+            print("For Cursor: Move to .cursor/rules/")
+            print("For VSCode: Move to .github/copilot/")
 
     except Exception as e:
-        print(f"Error creating project: {e}")
+        print(f"\n❌ ERROR: {e}")
         if project_path.exists():
             shutil.rmtree(project_path)
         sys.exit(1)
 
-
 def main():
     """Main CLI entry point."""
     parser = argparse.ArgumentParser(
-        description="Bootstrap a new React UI plugin project",
-        formatter_class=argparse.RawDescriptionHelpFormatter,
-        epilog="""
-Examples:
-  python bootstrap.py my-plugin
-  python bootstrap.py my-plugin --dir /path/to/projects/my-plugin
-
-This will create a new React project with all the necessary configuration
-files, dependencies, and structure needed for Airflow plugin development.
-        """,
+        description="Create a new React plugin project",
+        epilog="Example: python bootstrap.py my-plugin --dir ./projects"
     )
-
     parser.add_argument(
         "name",
-        help="Name of the React plugin project (letters, numbers, hyphens, and 
underscores only)",
+        help="Project name (letters, numbers, hyphens, underscores only)"

Review Comment:
   Why changing that?



##########
dev/react-plugin-tools/react_plugin_template/ai_agents_rules/README.md:
##########
@@ -0,0 +1,31 @@
+# AI Coding Rules for React Projects
+
+## What's This?
+These files help AI coding assistants (like Cursor, GitHub Copilot or VS Code) 
use various AI LLM models and this file helps you generate better and beginner 
friendly React code for your project. 
+
+## How to Use With Your IDE
+
+### For Cursor (VS Code Clone) IDE:
+1. Create a `.cursor` folder in your project root
+2. Make a `rules` subfolder: `.cursor/rules/`
+3. Copy ALL these `.md` files into that folder

Review Comment:
   Should files be renamed to the `.mdc` extension? does cursor work with `.md` 
file directly?



##########
dev/react-plugin-tools/react_plugin_template/ai_agents_rules/prompt-guidelines.md:
##########
@@ -0,0 +1,139 @@
+# React Plugin Prompt Guidelines for Beginners
+
+Welcome to your new Airflow React plugin project!  
+This guide will help you understand on how you can prompt better, use the AI 
rules in an effective, beginner friendly manner and even edit the AI rules to 
match your project's needs.
+
+---
+
+## What Are Prompts and AI Rules?
+
+- **Prompts** are instructions, questions or maybe detailed descriptions 
related to your React code base issue's description which you give (explain) to 
AI models or agents (like Copilot, ChatGPT, etc.) to help automate, review your 
code base, or help you with the code generation and content.
+- **AI Rules** help keep your plugin smart, ethical, and easy to use.  
+  They can check code style, suggest improvements, and even help debug! 
+  For example:
+  ```jsx
+  // AI can help improve this code
+  function Button() {
+    return <button>Click</button>
+  }
+  
+  // AI suggestion: Add props and aria-label
+  function Button({ onClick, label }) {
+    return (
+      <button onClick={onClick} aria-label={label}>
+        {label}
+      </button>
+    )
+  }
+  ```
+
+---
+
+## How to Use This Guide
+
+**Read through each section below for better understanding.**
+1. **Try out the examples** by copying, pasting, and editing them in your code 
or markdown files.
+2. **Don't be afraid to ask questions!** Everyone hesitates in the beginning 
and thats okay! Just know that everyone was a beginner once - you can ask for 
help:
+   - Write comments in your code with questions
+   - Open GitHub issues to get community help
+   - Use markdown files to document your questions
+   - Join the Discord community for help and support from the core 
contributors and mentors.
+
+   Remember: The only "silly" question is the one you don't ask! 
+
+---
+
+## Setting Up Your React Plugin
+
+1. **Install Node.js and pnpm**  
+   If you haven’t yet, download Node.js and install pnpm:
+   ```
+   npm install -g pnpm
+   ```
+
+2. **Install dependencies**
+   ```
+   pnpm install
+   ```
+
+3. **Start the development server**
+   ```
+   pnpm dev
+   ```
+   Open the link shown in your terminal to view your plugin live!
+
+---
+
+## Writing Good Prompts for AI Agents
+
+### Example 1: Asking for Code Help
+```
+
+How do I display a button in React that says "Click Me"?
+```
+
+### Example 2: Requesting a Review
+```
+Can you review my React component for best practices and accessibility?
+```
+
+### Example 3: Fixing an Error
+```
+Why am I seeing 'undefined is not a function' in my React code?
+```
+
+### Example 4: Generating Documentation
+```
+Write a README section for my React plugin that explains how users can install 
and use it.
+```
+
+---
+
+## Beginner-Friendly React Tips
+
+- **JSX is just HTML in JavaScript!**
+  ```jsx
+  <button>Click Me</button>
+  ```
+
+- **State lets you remember stuff!**
+  ```jsx
+  const [count, setCount] = useState(0);
+  ```
+
+- **Props pass info to components!**
+  ```jsx
+  <Welcome name="Alice" />
+  ```
+
+- **Use helpful comments:**
+  ```jsx
+  // This button increases the count
+  <button onClick={() => setCount(count + 1)}>Increase</button>
+  ```
+

Review Comment:
   That's more the idea, but this is too basic and broad, it shouldn't be 
specified.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to