webstarter-cli 0.1.0__py3-none-any.whl

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.
webstarter/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ # webstarter/__init__.py
2
+
3
+ __version__ = "0.1.0"
4
+ __author__ = "Alex" #
5
+
6
+ """
7
+ WebStarter CLI: Herramienta profesional para la creación de entornos web.
8
+ """
webstarter/cli.py ADDED
@@ -0,0 +1,28 @@
1
+ import typer
2
+ from webstarter.commands import create, add, init
3
+ from webstarter.core.logger import Logger
4
+
5
+ # Creamos la instancia principal de Typer
6
+ app = typer.Typer(
7
+ name="webstarter",
8
+ help="WebStarter: Generador profesional de proyectos web desde la CLI.",
9
+ add_completion=False
10
+ )
11
+
12
+ # Registramos los subcomandos de la carpeta commands
13
+ app.command(name="create")(create.execute)
14
+ app.command(name="add")(add.execute)
15
+ app.command(name="init")(init.execute)
16
+
17
+ @app.callback(invoke_without_command=True)
18
+ def main(ctx: typer.Context):
19
+ """
20
+ Herramienta para automatizar la creación de entornos frontend.
21
+ """
22
+ # Si el usuario no invoca ningún subcomando (ej: solo escribe 'webstarter')
23
+ if ctx.invoked_subcommand is None:
24
+ Logger.welcome() # Mostramos el panel visual pro que creamos en logger.py
25
+ print(ctx.get_help())
26
+
27
+ if __name__ == "__main__":
28
+ app()
@@ -0,0 +1,48 @@
1
+ import typer
2
+ from pathlib import Path
3
+ from webstarter.core.logger import Logger
4
+ from webstarter.utils.file_manager import FileManager
5
+
6
+ def execute(
7
+ component: str = typer.Argument(..., help="Componente a añadir (ej: navbar, card, footer)")
8
+ ):
9
+ """
10
+ Añade un componente específico al proyecto actual inyectándolo antes del cierre del body.
11
+ """
12
+ # 1. Detectar el directorio de trabajo actual (donde el usuario quiere añadir el componente)
13
+ cwd = Path.cwd()
14
+ index_path = cwd / "index.html"
15
+
16
+ # 2. Verificación de seguridad: ¿Existe el archivo destino?
17
+ if not index_path.exists():
18
+ Logger.error("No se encontró 'index.html'. Asegúrate de estar en la raíz de un proyecto WebStarter.")
19
+ raise typer.Exit(code=1)
20
+
21
+ # 3. LOCALIZACIÓN DINÁMICA DE PLANTILLAS:
22
+ # Resolvemos la ruta absoluta del paquete instalado para encontrar la carpeta templates
23
+ base_package_path = Path(__file__).resolve().parent.parent
24
+ template_path = base_package_path / "templates" / "components" / f"{component}.html"
25
+
26
+ # 4. Verificación de la plantilla solicitada
27
+ if not template_path.exists():
28
+ Logger.error(f"El componente '{component}' no existe en la biblioteca de WebStarter.")
29
+ raise typer.Exit(code=1)
30
+
31
+ try:
32
+ # 5. Lectura del componente y del proyecto actual
33
+ component_html = template_path.read_text(encoding="utf-8")
34
+ current_index = index_path.read_text(encoding="utf-8")
35
+
36
+ # 6. Lógica de inyección de código
37
+ if "</body>" in current_index:
38
+ # Insertamos el componente con una indentación limpia antes de cerrar el body
39
+ new_index = current_index.replace("</body>", f" {component_html}\n</body>")
40
+ FileManager.write_file(index_path, new_index)
41
+ Logger.success(f"Componente '[bold cyan]{component}[/bold cyan]' añadido con éxito a index.html")
42
+ else:
43
+ Logger.error("Error estructural: No se pudo encontrar la etiqueta </body> en index.html")
44
+ raise typer.Exit(code=1)
45
+
46
+ except Exception as e:
47
+ Logger.error(f"Error inesperado al procesar el archivo: {e}")
48
+ raise typer.Exit(code=1)
@@ -0,0 +1,40 @@
1
+ import typer
2
+ from typing import Optional
3
+ from webstarter.core.generator import ProjectGenerator
4
+ from webstarter.core.logger import Logger
5
+ from webstarter.core.exceptions import WebStarterError
6
+ from webstarter.core.validator import Validator # Importación necesaria
7
+
8
+ def execute(
9
+ name: str = typer.Argument(..., help="Nombre de la carpeta del proyecto"),
10
+ bootstrap: bool = typer.Option(False, "--bootstrap", "-b", help="Incluye soporte para Bootstrap"),
11
+ dark: bool = typer.Option(False, "--dark", "-d", help="Configura el proyecto en modo oscuro")
12
+ ):
13
+ """
14
+ Crea un nuevo proyecto web con una estructura profesional.
15
+ """
16
+ try:
17
+ # VALIDACIÓN PREVENTIVA: Aseguramos que el nombre sea apto
18
+ Validator.validate_name(name)
19
+
20
+ # Instanciamos el generador con las opciones elegidas
21
+ generator = ProjectGenerator(
22
+ project_name=name,
23
+ bootstrap=bootstrap,
24
+ dark_mode=dark
25
+ )
26
+
27
+ # Feedback visual para el usuario
28
+ Logger.info(f"Construyendo proyecto: [bold cyan]{name}[/bold cyan]...")
29
+
30
+ # Ejecución del motor de generación
31
+ generator.build()
32
+
33
+ except WebStarterError as e:
34
+ # Errores controlados de nuestra aplicación
35
+ Logger.error(str(e))
36
+ raise typer.Exit(code=1)
37
+ except Exception as e:
38
+ # Errores inesperados del sistema
39
+ Logger.error(f"Ocurrió un error inesperado: {e}")
40
+ raise typer.Exit(code=1)
@@ -0,0 +1,28 @@
1
+ import typer
2
+ from pathlib import Path
3
+ from webstarter.core.logger import Logger
4
+
5
+ def execute():
6
+ """
7
+ Inicializa una configuración de WebStarter en el directorio actual.
8
+ """
9
+ Logger.info("Inicializando configuración de WebStarter...")
10
+
11
+ # 1. Definimos la ruta del archivo de configuración en el directorio actual
12
+ config_path = Path.cwd() / ".webstarter.json"
13
+
14
+ # 2. Verificamos si ya existe para evitar sobrescribir datos del usuario
15
+ if not config_path.exists():
16
+ try:
17
+ # 3. Creamos un archivo de configuración base
18
+ # Esto permitirá que en el futuro tu CLI sepa qué versión se usó o qué plugins hay
19
+ config_content = '{"version": "0.1.0", "plugins": [], "created_at": "2026"}'
20
+ config_path.write_text(config_content, encoding="utf-8")
21
+
22
+ Logger.success("Archivo de configuración '.webstarter.json' creado exitosamente.")
23
+ Logger.success("Entorno preparado.")
24
+ except Exception as e:
25
+ Logger.error(f"No se pudo crear el archivo de configuración: {e}")
26
+ raise typer.Exit(code=1)
27
+ else:
28
+ Logger.warning("El entorno ya contiene una configuración de WebStarter activa.")
@@ -0,0 +1,16 @@
1
+ # webstarter/core/__init__.py
2
+
3
+ from .generator import ProjectGenerator
4
+ from .logger import Logger
5
+ from .validator import Validator
6
+ from .project_builder import ProjectBuilder
7
+ from .exceptions import WebStarterError
8
+
9
+ # Esto define qué se exporta cuando alguien hace: from webstarter.core import *
10
+ __all__ = [
11
+ "ProjectGenerator",
12
+ "Logger",
13
+ "Validator",
14
+ "ProjectBuilder",
15
+ "WebStarterError"
16
+ ]
@@ -0,0 +1,21 @@
1
+ class WebStarterError(Exception):
2
+ """Error base para la aplicación."""
3
+ pass
4
+
5
+ class ProjectAlreadyExistsError(WebStarterError):
6
+ """Se lanza si la carpeta de destino ya existe."""
7
+ pass
8
+
9
+ class TemplateNotFoundError(WebStarterError):
10
+ """Se lanza si no se encuentra una carpeta de plantilla."""
11
+ pass
12
+
13
+ # --- ADICIONES RECOMENDADAS PARA COMPLETAR EL CORE ---
14
+
15
+ class ValidationError(WebStarterError):
16
+ """Se lanza si el nombre del proyecto o componente es inválido."""
17
+ pass
18
+
19
+ class InjectionError(WebStarterError):
20
+ """Se lanza si falla la inyección de componentes (ej. falta etiqueta </body>)."""
21
+ pass
@@ -0,0 +1,74 @@
1
+ from pathlib import Path
2
+ from jinja2 import Environment, FileSystemLoader
3
+ from webstarter.core.logger import Logger
4
+ from webstarter.utils.file_manager import FileManager
5
+ from webstarter.core.project_builder import ProjectBuilder
6
+ from webstarter.utils.copier import Copier # Importación añadida
7
+
8
+ class ProjectGenerator:
9
+ def __init__(self, project_name: str, bootstrap: bool = False, dark_mode: bool = False, multi_page: bool = False):
10
+ self.project_name = project_name
11
+ self.bootstrap = bootstrap
12
+ self.dark_mode = dark_mode
13
+ self.multi_page = multi_page # Nueva opción para proyectos MPA
14
+ self.target_path = Path.cwd() / project_name
15
+
16
+ # Resolución de ruta absoluta para las plantillas instaladas
17
+ self.template_dir = Path(__file__).resolve().parent.parent / "templates"
18
+
19
+ # Configuración de Jinja2
20
+ self.env = Environment(loader=FileSystemLoader(str(self.template_dir)))
21
+
22
+ def build(self):
23
+ """Ejecuta el flujo completo de construcción del proyecto."""
24
+ try:
25
+ # 1. Crear estructura física de carpetas
26
+ ProjectBuilder.init_structure(self.target_path)
27
+
28
+ # 2. Generar index.html principal
29
+ self._generate_index()
30
+
31
+ # 3. Copiar assets estáticos base (CSS/JS)
32
+ base_templates = self.template_dir / "base"
33
+ FileManager.copy_static_files(base_templates / "css", self.target_path / "css")
34
+ FileManager.copy_static_files(base_templates / "js", self.target_path / "js")
35
+
36
+ # 4. Lógica Multi-Página (Si se solicita)
37
+ if self.multi_page:
38
+ self._generate_multipage()
39
+
40
+ Logger.success(f"Proyecto '[bold cyan]{self.project_name}[/bold cyan]' generado correctamente.")
41
+
42
+ except Exception as e:
43
+ Logger.error(f"Fallo en la construcción del proyecto: {e}")
44
+ raise
45
+
46
+ def _generate_index(self):
47
+ """Renderiza la plantilla index.html aplicando las opciones elegidas."""
48
+ template = self.env.get_template("base/index.html")
49
+ output = template.render(
50
+ title=self.project_name,
51
+ use_bootstrap=self.bootstrap,
52
+ dark_mode=self.dark_mode
53
+ )
54
+ FileManager.write_file(self.target_path / "index.html", output)
55
+
56
+ def _generate_multipage(self):
57
+ """Genera archivos adicionales para una arquitectura de varias páginas."""
58
+ Logger.info("Añadiendo soporte multi-página...")
59
+
60
+ # Renderizar página About (Nosotros)
61
+ template_about = self.env.get_template("multipage/about.html")
62
+ output_about = template_about.render(
63
+ title=self.project_name,
64
+ use_bootstrap=self.bootstrap,
65
+ dark_mode=self.dark_mode
66
+ )
67
+ FileManager.write_file(self.target_path / "about.html", output_about)
68
+
69
+ # Copiar lógica de navegación usando Copier
70
+ # Esto copia el contenido de multipage/js a la carpeta js del proyecto
71
+ multi_js_src = self.template_dir / "multipage"
72
+ if (multi_js_src / "router.js").exists():
73
+ router_content = (multi_js_src / "router.js").read_text(encoding="utf-8")
74
+ FileManager.write_file(self.target_path / "js" / "router.js", router_content)
@@ -0,0 +1,37 @@
1
+ from rich.console import Console
2
+ from rich.panel import Panel
3
+
4
+ # Inicializamos la consola globalmente para este módulo
5
+ console = Console()
6
+
7
+ class Logger:
8
+ @staticmethod
9
+ def welcome():
10
+ """Muestra un panel de bienvenida al iniciar la herramienta."""
11
+ console.print(
12
+ Panel.fit(
13
+ "🚀 [bold cyan]WebStarter CLI[/bold cyan]\n[italic white]Tu generador profesional de proyectos web[/italic white]",
14
+ border_style="blue",
15
+ title="v0.1.0"
16
+ )
17
+ )
18
+
19
+ @staticmethod
20
+ def info(message: str):
21
+ """Muestra un mensaje informativo en azul."""
22
+ console.print(f"[bold blue]INFO:[/bold blue] {message}")
23
+
24
+ @staticmethod
25
+ def success(message: str):
26
+ """Muestra un mensaje de éxito en verde."""
27
+ console.print(f"[bold green]SUCCESS:[/bold green] {message}")
28
+
29
+ @staticmethod
30
+ def error(message: str):
31
+ """Muestra un mensaje de error crítico en rojo."""
32
+ console.print(f"[bold red]ERROR:[/bold red] {message}")
33
+
34
+ @staticmethod
35
+ def warning(message: str):
36
+ """Muestra un aviso o advertencia en amarillo."""
37
+ console.print(f"[bold yellow]WARNING:[/bold yellow] {message}")
@@ -0,0 +1,31 @@
1
+ from pathlib import Path
2
+ from webstarter.utils.file_manager import FileManager
3
+
4
+ class ProjectBuilder:
5
+ """
6
+ Clase responsable de construir la estructura física de directorios
7
+ del nuevo proyecto web.
8
+ """
9
+
10
+ @staticmethod
11
+ def init_structure(base_path: Path):
12
+ """
13
+ Crea el esqueleto de carpetas inicial del proyecto.
14
+
15
+ Args:
16
+ base_path (Path): La ruta raíz donde se creará el proyecto.
17
+ """
18
+ # 1. Crear el directorio raíz del proyecto
19
+ FileManager.create_directory(base_path)
20
+
21
+ # 2. Definir la lista de subcarpetas necesarias para un proyecto profesional
22
+ folders = [
23
+ "css", # Para hojas de estilo
24
+ "js", # Para lógica de scripts
25
+ "assets/img", # Para recursos multimedia e imágenes
26
+ ]
27
+
28
+ # 3. Crear cada subcarpeta de forma iterativa
29
+ for folder in folders:
30
+ subfolder_path = base_path / folder
31
+ FileManager.create_directory(subfolder_path)
@@ -0,0 +1,34 @@
1
+ import re
2
+ from webstarter.core.exceptions import WebStarterError # Usamos nuestra excepción base
3
+
4
+ class Validator:
5
+ """
6
+ Clase encargada de validar las entradas del usuario para asegurar
7
+ la integridad del sistema de archivos.
8
+ """
9
+
10
+ @staticmethod
11
+ def validate_name(name: str) -> bool:
12
+ """
13
+ Valida que el nombre del proyecto sea seguro.
14
+
15
+ Reglas:
16
+ - Solo letras (A-Z, a-z), números (0-9).
17
+ - Permite guiones (-) y guiones bajos (_).
18
+ - No permite espacios ni caracteres especiales.
19
+ """
20
+
21
+ # Expresión regular: permite letras, números, guiones y guiones bajos
22
+ pattern = r'^[a-zA-Z0-9_-]+$'
23
+
24
+ if not re.match(pattern, name):
25
+ raise WebStarterError(
26
+ f"El nombre '{name}' es inválido. "
27
+ "Usa solo letras, números, guiones (-) o guiones bajos (_), sin espacios."
28
+ )
29
+
30
+ # Opcional: Evitar nombres demasiado cortos
31
+ if len(name) < 2:
32
+ raise WebStarterError("El nombre del proyecto es demasiado corto.")
33
+
34
+ return True
@@ -0,0 +1,9 @@
1
+ /* Estilos base de WebStarter */
2
+ body {
3
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
4
+ transition: background-color 0.3s, color 0.3s;
5
+ }
6
+
7
+ .container {
8
+ max-width: 1200px;
9
+ }
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ title }}</title>
7
+
8
+ {% if use_bootstrap %}
9
+ {% include "bootstrap/cdn.html" %}
10
+ {% endif %}
11
+
12
+ <link rel="stylesheet" href="css/style.css">
13
+ </head>
14
+ <body class="{% if dark_mode %}bg-dark text-white{% endif %}">
15
+
16
+ <div class="container mt-5">
17
+ <header class="text-center mb-5">
18
+ <h1>Bienvenido a {{ title }}</h1>
19
+ <p class="lead">Proyecto generado profesionalmente con WebStarter CLI.</p>
20
+ </header>
21
+
22
+ <main>
23
+ </main>
24
+ </div>
25
+
26
+ <script src="js/script.js"></script>
27
+ </body>
28
+ </html>
@@ -0,0 +1 @@
1
+ console.log("WebStarter: ¡Proyecto cargado con éxito!");
@@ -0,0 +1,2 @@
1
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
2
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" defer></script>
@@ -0,0 +1,10 @@
1
+ <nav class="navbar navbar-expand-lg border-bottom mb-4">
2
+ <div class="container-fluid">
3
+ <a class="navbar-brand" href="#">{{ title|default('WebStarter') }}</a>
4
+ <div class="navbar-nav">
5
+ <a class="nav-link active" href="#">Inicio</a>
6
+ <a class="nav-link" href="#">Servicios</a>
7
+ <a class="nav-link" href="#">Contacto</a>
8
+ </div>
9
+ </div>
10
+ </nav>
@@ -0,0 +1,10 @@
1
+ /* Dark Mode Override */
2
+ body.bg-dark {
3
+ background-color: #121212 !important;
4
+ color: #e0e0e0 !important;
5
+ }
6
+
7
+ .card {
8
+ background-color: #1e1e1e;
9
+ border-color: #333;
10
+ }
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Nosotros - {{ title }}</title>
6
+ {% if use_bootstrap %}
7
+ {% include "bootstrap/cdn.html" %}
8
+ {% endif %}
9
+ <link rel="stylesheet" href="css/style.css">
10
+ </head>
11
+ <body class="{% if dark_mode %}bg-dark text-white{% endif %}">
12
+ <div class="container mt-5">
13
+ <h1>Sobre Nosotros</h1>
14
+ <p>Esta es una página secundaria generada automáticamente.</p>
15
+ <a href="index.html" class="btn btn-primary">Volver al Inicio</a>
16
+ </div>
17
+ </body>
18
+ </html>
@@ -0,0 +1,6 @@
1
+ // webstarter/templates/multipage/router.js
2
+ console.log("Módulo de navegación WebStarter cargado");
3
+
4
+ export function navigateTo(page) {
5
+ window.location.href = `${page}.html`;
6
+ }
@@ -0,0 +1,29 @@
1
+ import shutil
2
+ from pathlib import Path
3
+ from webstarter.core.logger import Logger
4
+
5
+ class Copier:
6
+ """
7
+ Clase utilitaria encargada de la transferencia física de directorios
8
+ y archivos de plantillas hacia el proyecto de destino.
9
+ """
10
+
11
+ @staticmethod
12
+ def copy_template_folder(src: Path, dest: Path):
13
+ """
14
+ Copia recursivamente una carpeta completa al destino especificado.
15
+
16
+ Args:
17
+ src (Path): Carpeta de origen (dentro de templates).
18
+ dest (Path): Carpeta de destino (dentro del nuevo proyecto).
19
+ """
20
+ if not src.exists():
21
+ Logger.warning(f"La fuente de plantillas '{src.name}' no existe. Saltando copia.")
22
+ return
23
+
24
+ try:
25
+ # dirs_exist_ok=True permite fusionar carpetas si ya existen
26
+ shutil.copytree(src, dest, dirs_exist_ok=True)
27
+ except Exception as e:
28
+ Logger.error(f"Error crítico al copiar archivos de {src.name}: {e}")
29
+ raise
@@ -0,0 +1,46 @@
1
+ import shutil
2
+ from pathlib import Path
3
+ from webstarter.core.exceptions import ProjectAlreadyExistsError, WebStarterError
4
+ from webstarter.core.logger import Logger
5
+
6
+ class FileManager:
7
+ """
8
+ Clase encargada de las operaciones físicas de entrada/salida (I/O)
9
+ en el sistema de archivos.
10
+ """
11
+
12
+ @staticmethod
13
+ def create_directory(path: Path):
14
+ """Crea un directorio de forma segura."""
15
+ if path.exists():
16
+ # Usamos la excepción personalizada que definiste
17
+ raise ProjectAlreadyExistsError(f"El directorio '{path.name}' ya existe.")
18
+
19
+ try:
20
+ path.mkdir(parents=True, exist_ok=True)
21
+ except PermissionError:
22
+ raise WebStarterError(f"No tienes permisos para crear la carpeta en: {path}")
23
+
24
+ @staticmethod
25
+ def copy_static_files(src: Path, dest: Path):
26
+ """Copia archivos estáticos (CSS, JS, Imágenes) al proyecto."""
27
+ if not src.exists():
28
+ return # Evitamos errores si una carpeta base opcional no existe
29
+
30
+ try:
31
+ shutil.copytree(src, dest, dirs_exist_ok=True)
32
+ except Exception as e:
33
+ Logger.error(f"Error al copiar archivos estáticos: {e}")
34
+
35
+ @staticmethod
36
+ def write_file(path: Path, content: str):
37
+ """Escribe contenido de texto en un archivo con codificación UTF-8."""
38
+ try:
39
+ # Aseguramos que la carpeta que contendrá al archivo exista
40
+ if not path.parent.exists():
41
+ path.parent.mkdir(parents=True, exist_ok=True)
42
+
43
+ path.write_text(content, encoding="utf-8")
44
+ except Exception as e:
45
+ Logger.error(f"No se pudo escribir el archivo {path.name}: {e}")
46
+ raise WebStarterError(f"Fallo en la escritura de: {path}")
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: webstarter-cli
3
+ Version: 0.1.0
4
+ Summary: Herramienta CLI profesional para generar proyectos web HTML/CSS/JS.
5
+ Author-email: Alex <tu-email@ejemplo.com>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: typer[all]
10
+ Requires-Dist: rich
11
+ Requires-Dist: jinja2
12
+ Dynamic: license-file
13
+
14
+ # 🚀 WebStarter CLI
15
+
16
+ **WebStarter** es una herramienta de línea de comandos (CLI) profesional diseñada para automatizar la creación de entornos de desarrollo web frontend. Genera estructuras de proyectos limpias con HTML, CSS y JavaScript en segundos.
17
+
18
+ Desarrollado por **Alex**.
19
+
20
+ ---
21
+
22
+ ## ✨ Características
23
+
24
+ * **Generación Instantánea**: Crea la estructura de carpetas (`css/`, `js/`, `assets/`) de forma automática.
25
+ * **Plantillas Dinámicas**: Usa Jinja2 para renderizar archivos base.
26
+ * **Soporte Bootstrap**: Opción para incluir el CDN de Bootstrap 5 con una sola flag.
27
+ * **Modo Oscuro**: Configuración nativa de temas oscuros desde la creación.
28
+ * **Arquitectura Modular**: Código extensible y fácil de mantener.
29
+ * **Biblioteca de Componentes**: Añade componentes como `navbar` a proyectos existentes.
30
+
31
+ ---
32
+
33
+ ## 🛠️ Instalación
34
+
35
+ Asegúrate de tener Python 3.8 o superior instalado.
36
+
37
+ 1. Clona este repositorio o descarga el código.
38
+ 2. Abre una terminal en la carpeta raíz del proyecto.
39
+ 3. Instala la herramienta de forma local:
40
+
41
+ ```bash
42
+ pip install .
@@ -0,0 +1,27 @@
1
+ webstarter/__init__.py,sha256=ayLA0VEt_L5Ojz4rNKehMBYRnEoQ4f_LgR3R1lEmLjI,160
2
+ webstarter/cli.py,sha256=rqDiJhw7Z4Ox3VZULRfP2r3rvRLXt0rRL0AqyS_tpA8,921
3
+ webstarter/commands/add.py,sha256=r4uy57G8kNg4pjIsgbAjvHpc04ixuNZXlPuCRjBVOmw,2246
4
+ webstarter/commands/create.py,sha256=hAvWoQzd_qclDVRWAWzaARY-J9inkpG5BgmxBIkrtcU,1535
5
+ webstarter/commands/init.py,sha256=uLrvDpc7wM16BofAXZH3itQgoJITccKTXggoV4FYD38,1263
6
+ webstarter/core/__init__.py,sha256=4QKMauBZw0xt0mjsoWJ-BweoesrqqGHaBCpYd2UJWak,424
7
+ webstarter/core/exceptions.py,sha256=vwAZtZGan5UY64pMYBvLBbJJfYTWuauNpuCY_h7vjCU,654
8
+ webstarter/core/generator.py,sha256=8ASdkGM2UCBBhZONLaQ0BMZ8T8SU0GFZDhb1XK2ef8A,3437
9
+ webstarter/core/logger.py,sha256=FwkIAMdhKR2GsOthhafKHL8uYfucRV4Vb5Mpy8t3fHs,1262
10
+ webstarter/core/project_builder.py,sha256=XnunjhlHiqZb9ImbyQr9ND82wxarTMQHK0fDebpDnWQ,1067
11
+ webstarter/core/validator.py,sha256=q19XbgzPp85uAH8bYjvz5GC2cz8DUPrYY4Q1uKy9jNk,1160
12
+ webstarter/templates/base/index.html,sha256=2U0ItvKEbiNqwuaZoAtf2jD81DJRDKKA6bNUh2k3O1k,743
13
+ webstarter/templates/base/css/style.css,sha256=Zovz5syfvYxpSZkKqcztYQSwwcmkoFM3HHZZuK6R0i0,205
14
+ webstarter/templates/base/js/main.js,sha256=vq9Y46ex8yQZjQgq46CmGPc4hoeIETgLPczfbmiu2Ws,58
15
+ webstarter/templates/bootstrap/cdn.html,sha256=RQiRPt2pSjctsk058YGX_--9UGHMpFFu7Zr020ai7pk,210
16
+ webstarter/templates/components/navbar.html,sha256=UE1UAd57KhWfHIEzTDyZsbUhYOePYOwTA1v7P3_z2vo,378
17
+ webstarter/templates/dark/dark-theme.css,sha256=mDkuIbYlCPzKgehC8aSQO-Ke1BXeBffD4QstWekuRwM,189
18
+ webstarter/templates/multipage/about.html,sha256=2r5nPItRpwksqEcTegJAJosV6GXWOp3Js3zJ0ZQUFZ0,566
19
+ webstarter/templates/multipage/router.js,sha256=n7Mk4ejfN73QEeBqlXXLIIz7AD6Yg6VfAV7r8jU1NRc,187
20
+ webstarter/utils/copier.py,sha256=Pm-NxmI4QXORv51TliR7iBDWoci1ygSJ6B-oJWuzp3Q,1024
21
+ webstarter/utils/file_manager.py,sha256=DmmdYDktksX2_8q83WXp19yYr5We4uWFnPC9w5kul_U,1842
22
+ webstarter_cli-0.1.0.dist-info/licenses/LICENSE,sha256=4HhLGFwdWmQqQNlvV8Safxah32c4jPjKK8EHZpmNuBQ,1080
23
+ webstarter_cli-0.1.0.dist-info/METADATA,sha256=gd9bZWED9iEb4ul-0PkGstYk0hY8hnGqigbH0swQ8zg,1472
24
+ webstarter_cli-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
25
+ webstarter_cli-0.1.0.dist-info/entry_points.txt,sha256=09bXLVOhVr9SgYGGAc0Oe626PV5v48nATsgzfHAXy7Q,50
26
+ webstarter_cli-0.1.0.dist-info/top_level.txt,sha256=yDxcNz-fpLJaMjZeLHMhdAcLNtFOH5x7HsURqusQP4k,11
27
+ webstarter_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ webstarter = webstarter.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ webstarter