From 0e5873b6ccfba32aa853addddd0b1493851e4faa Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Sun, 1 Feb 2026 03:31:34 +0000 Subject: [PATCH] Initial commit: gitignore-generator v0.1.0 --- src/generator.py | 243 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 src/generator.py diff --git a/src/generator.py b/src/generator.py new file mode 100644 index 0000000..79f3c27 --- /dev/null +++ b/src/generator.py @@ -0,0 +1,243 @@ +"""Gitignore generation from templates.""" +import os +from pathlib import Path +from typing import Dict, List, Optional, Set + + +class GitignoreGenerator: + """Generate .gitignore content from templates.""" + + TEMPLATE_MAP: Dict[str, str] = { + "python": "languages/python.gitignore", + "javascript": "languages/javascript.gitignore", + "typescript": "languages/typescript.gitignore", + "java": "languages/java.gitignore", + "go": "languages/go.gitignore", + "rust": "languages/rust.gitignore", + "dotnet": "languages/dotnet.gitignore", + "php": "languages/php.gitignore", + "ruby": "languages/ruby.gitignore", + "django": "frameworks/django.gitignore", + "flask": "frameworks/flask.gitignore", + "react": "frameworks/react.gitignore", + "vue": "frameworks/vue.gitignore", + "angular": "frameworks/angular.gitignore", + "rails": "frameworks/rails.gitignore", + "laravel": "frameworks/laravel.gitignore", + "spring": "frameworks/spring.gitignore", + "vscode": "ide/vscode.gitignore", + "jetbrains": "ide/jetbrains.gitignore", + "visualstudiocode": "ide/visualstudiocode.gitignore", + "linux": "os/linux.gitignore", + "macos": "os/macos.gitignore", + "windows": "os/windows.gitignore", + "docker": "tools/docker.gitignore", + "gradle": "tools/gradle.gitignore", + "maven": "tools/maven.gitignore", + "jupyter": "tools/jupyter.gitignore", + "terraform": "tools/terraform.gitignore", + } + + def __init__(self, config: Optional["Config"] = None) -> None: + """Initialize generator with optional configuration.""" + self.template_dir = Path(__file__).parent / "templates" + self.selected_templates: Set[str] = set() + self.config = config + + def add_template(self, template_type: str) -> None: + """Add a template type to include.""" + if template_type in self.TEMPLATE_MAP: + self.selected_templates.add(template_type) + + def remove_template(self, template_type: str) -> None: + """Remove a template type from selection.""" + self.selected_templates.discard(template_type) + + def get_template_path(self, template_type: str) -> Optional[Path]: + """Get the path to a template file.""" + template_rel = self.TEMPLATE_MAP.get(template_type) + if template_rel: + template_path = self.template_dir / template_rel + if template_path.exists(): + return template_path + return None + + def load_template(self, template_type: str) -> str: + """Load template content.""" + template_path = self.get_template_path(template_type) + if template_path: + try: + with open(template_path, "r") as f: + return f.read() + except OSError: + return "" + return "" + + def load_template_lines(self, template_type: str) -> List[str]: + """Load template content as list of lines.""" + content = self.load_template(template_type) + lines = content.split("\n") + while lines and not lines[-1]: + lines.pop() + while lines and not lines[0]: + lines.pop(0) + return lines + + def deduplicate_entries( + self, entries: List[str], comments: List[str] = None + ) -> List[str]: + """Remove duplicate entries while preserving order.""" + seen: Set[str] = set() + unique_entries: List[str] = [] + for entry in entries: + normalized = entry.strip().lower() + if normalized and normalized not in seen: + seen.add(normalized) + unique_entries.append(entry) + if comments: + unique_comments = [] + seen_comments = set() + for comment in comments: + if comment not in seen_comments: + seen_comments.add(comment) + unique_comments.append(comment) + return unique_comments + unique_entries + return unique_entries + + def generate(self) -> str: + """Generate combined .gitignore content.""" + all_entries: List[str] = [] + all_comments: List[str] = [] + used_categories: Set[str] = set() + + template_order = [ + "language", + "framework", + "ide", + "os", + "tools", + ] + + category_map = { + "python": "language", + "javascript": "language", + "typescript": "language", + "java": "language", + "go": "language", + "rust": "language", + "dotnet": "language", + "php": "language", + "ruby": "language", + "django": "framework", + "flask": "framework", + "react": "framework", + "vue": "framework", + "angular": "framework", + "rails": "framework", + "laravel": "framework", + "spring": "framework", + "vscode": "ide", + "jetbrains": "ide", + "visualstudiocode": "ide", + "linux": "os", + "macos": "os", + "windows": "os", + "docker": "tools", + "gradle": "tools", + "maven": "tools", + "jupyter": "tools", + "terraform": "tools", + } + + for template_type in sorted(self.selected_templates): + template_path = self.get_template_path(template_type) + if template_path: + try: + category = category_map.get(template_type, "other") + header = f"# {template_type.upper()}" + if header not in all_comments: + all_comments.append(header) + + lines = self.load_template_lines(template_type) + all_entries.extend(lines) + except OSError: + pass + + if not all_entries: + return "" + + combined = self.deduplicate_entries(all_entries, all_comments) + result = "\n".join(combined) + if result and not result.endswith("\n"): + result += "\n" + return result + + def generate_for_types( + self, types: List[str], deduplicate: bool = True + ) -> str: + """Generate .gitignore for specific types.""" + self.selected_templates = set() + for t in types: + self.add_template(t) + return self.generate() + + def get_available_templates(self) -> List[str]: + """Get list of available template types.""" + available = [] + for template_type, template_rel in self.TEMPLATE_MAP.items(): + template_path = self.template_dir / template_rel + if template_path.exists(): + available.append(template_type) + return sorted(available) + + def validate_template_type(self, template_type: str) -> bool: + """Validate if a template type is valid.""" + return template_type in self.TEMPLATE_MAP + + def get_template_categories(self) -> Dict[str, List[str]]: + """Get templates organized by category.""" + categories: Dict[str, List[str]] = { + "language": [], + "framework": [], + "ide": [], + "os": [], + "tools": [], + } + + category_map = { + "python": "language", + "javascript": "language", + "typescript": "language", + "java": "language", + "go": "language", + "rust": "language", + "dotnet": "language", + "php": "language", + "ruby": "language", + "django": "framework", + "flask": "framework", + "react": "framework", + "vue": "framework", + "angular": "framework", + "rails": "framework", + "laravel": "framework", + "spring": "framework", + "vscode": "ide", + "jetbrains": "ide", + "visualstudiocode": "ide", + "linux": "os", + "macos": "os", + "windows": "os", + "docker": "tools", + "gradle": "tools", + "maven": "tools", + "jupyter": "tools", + "terraform": "tools", + } + + for template_type in self.TEMPLATE_MAP: + category = category_map.get(template_type, "other") + if category in categories: + categories[category].append(template_type) + + return categories