Initial upload: ScaffoldForge CLI tool with full codebase, tests, and CI/CD

This commit is contained in:
2026-02-04 05:37:11 +00:00
parent 2865eb2ebe
commit d2d4d93f66

View File

@@ -0,0 +1,222 @@
"""Code generation functionality."""
from typing import Any, Dict, List
from scaffoldforge.parsers import IssueData
from scaffoldforge.templates import TemplateEngine
from scaffoldforge.generators.models import FileSpec
class CodeGenerator:
"""Generator for file content using templates."""
DEFAULT_FILES = {
"python": ["main.py", "utils.py", "models.py", "cli.py"],
"javascript": ["index.js", "utils.js"],
"go": ["main.go", "utils.go"],
"rust": ["main.rs", "lib.rs"],
}
CONFIG_FILES = {
"python": ["pyproject.toml", "requirements.txt"],
"javascript": ["package.json", ".eslintrc.json"],
"go": ["go.mod"],
"rust": ["Cargo.toml"],
}
def __init__(self, template_engine: TemplateEngine, issue_data: IssueData):
"""Initialize the code generator.
Args:
template_engine: TemplateEngine instance.
issue_data: Parsed issue data.
"""
self.template_engine = template_engine
self.issue_data = issue_data
def generate_all_files(
self, language: str, issue_data: IssueData
) -> List[FileSpec]:
"""Generate all project files.
Args:
language: Programming language.
issue_data: Parsed issue data.
Returns:
List of FileSpec objects.
"""
files = []
context = self.template_engine.get_template_context(issue_data)
files.extend(self._generate_source_files(language, context))
files.extend(self._generate_config_files(language, context))
return files
def _generate_source_files(
self, language: str, context: Dict[str, Any]
) -> List[FileSpec]:
"""Generate source code files.
Args:
language: Programming language.
context: Template context.
Returns:
List of FileSpec objects.
"""
files = []
source_templates = self.DEFAULT_FILES.get(language, [])
for template_name in source_templates:
try:
content = self.template_engine.render(
template_name, context, language
)
extension = self._get_extension(language)
filename = f"{template_name}{extension}" if not template_name.endswith(extension) else template_name
path = self._get_source_path(template_name, language)
files.append(FileSpec(path=path, content=content))
except ValueError:
files.append(self._create_empty_file(template_name, language, context))
return files
def _generate_config_files(
self, language: str, context: Dict[str, Any]
) -> List[FileSpec]:
"""Generate configuration files.
Args:
language: Programming language.
context: Template context.
Returns:
List of FileSpec objects.
"""
files = []
config_templates = self.CONFIG_FILES.get(language, [])
for config_name in config_templates:
try:
content = self.template_engine.render(
config_name, context, language
)
files.append(FileSpec(path=config_name, content=content))
except ValueError:
pass
return files
def _create_empty_file(
self, template_name: str, language: str, context: Dict[str, Any]
) -> FileSpec:
"""Create an empty file with TODO comments.
Args:
template_name: Name of the template.
language: Programming language.
context: Template context.
Returns:
FileSpec object.
"""
extension = self._get_extension(language)
filename = f"{template_name}{extension}"
path = self._get_source_path(template_name, language)
todo_items = self.issue_data.get_todo_items()
content = self._generate_todo_content(
language, template_name, todo_items
)
return FileSpec(path=path, content=content)
def _generate_todo_content(
self, language: str, template_name: str, todo_items: List[str]
) -> str:
"""Generate TODO comments for a file.
Args:
language: Programming language.
template_name: Name of the template.
todo_items: List of TODO items.
Returns:
TODO comment content.
"""
todo_header = f"# TODO Items from GitHub Issue #{self.issue_data.number}"
lines = [todo_header]
for item in todo_items[:5]:
lines.append(f"# TODO: {item}")
if language == "python":
return f'"""{template_name} - {self.issue_data.title}"""\n\n' + "\n".join(
lines
)
elif language in ("javascript",):
return f"/**\n * {template_name}\n */\n\n" + "\n".join(lines)
elif language == "go":
return f"// {template_name}\n\n" + "\n".join(lines)
elif language == "rust":
return f"// {template_name}\n\n" + "\n".join(lines)
else:
return "\n".join(lines)
def _get_extension(self, language: str) -> str:
"""Get file extension for language.
Args:
language: Programming language.
Returns:
File extension with dot.
"""
extensions = {
"python": ".py",
"javascript": ".js",
"go": ".go",
"rust": ".rs",
}
return extensions.get(language, ".txt")
def _get_source_path(self, template_name: str, language: str) -> str:
"""Get the path for a source file.
Args:
template_name: Name of the template.
language: Programming language.
Returns:
File path.
"""
src_templates = {"models", "utils", "handlers", "lib"}
if template_name.lower() in src_templates:
return f"src/{template_name}{self._get_extension(language)}"
return f"{template_name}{self._get_extension(language)}"
def generate_single_file(
self, filename: str, language: str, extra_context: Dict[str, Any] = None
) -> str:
"""Generate content for a single file.
Args:
filename: Name of the file.
language: Programming language.
extra_context: Additional template context.
Returns:
Generated file content.
"""
context = self.template_engine.get_template_context(self.issue_data)
if extra_context:
context.update(extra_context)
try:
return self.template_engine.render(filename, context, language)
except ValueError:
empty_file = self._create_empty_file(filename, language, {})
return empty_file.content