v0.166.0
  1import sys
  2from pathlib import Path
  3
  4import click
  5from plain.assets.finders import _APP_ASSETS_DIR
  6from plain.cli import register_cli
  7
  8from .deps import Dependency, get_deps
  9from .exceptions import DependencyError
 10
 11VENDOR_DIR = _APP_ASSETS_DIR / "vendor"
 12
 13
 14@register_cli("vendor")
 15@click.group()
 16def cli() -> None:
 17    """Manage vendored CSS/JS dependencies"""
 18
 19
 20@cli.command()
 21def sync() -> None:
 22    """Clear vendored assets and re-download"""
 23    click.secho("Clearing existing vendored dependencies...", bold=True)
 24    if VENDOR_DIR.exists():
 25        for path in VENDOR_DIR.iterdir():
 26            path.unlink()
 27
 28    deps = get_deps()
 29    if not deps:
 30        click.echo(
 31            "No vendored dependencies found in pyproject.toml. Use [tool.plain.vendor.dependencies]"
 32        )
 33        return
 34
 35    errors = []
 36
 37    for dep in deps:
 38        click.secho(f"Installing {dep.name}...", bold=True, nl=False)
 39        try:
 40            vendored_path = dep.install()
 41            vendored_path = vendored_path.relative_to(Path.cwd())
 42
 43            click.secho(f" {dep.installed}", fg="green", nl=False)
 44            click.secho(f" -> {vendored_path}")
 45        except DependencyError as e:
 46            click.secho(f"  {e}", fg="red")
 47            errors.append(e)
 48
 49    if errors:
 50        click.secho("Failed to install some dependencies.", fg="red")
 51        sys.exit(1)
 52
 53
 54@cli.command()
 55@click.argument("name", nargs=-1, default=None)
 56def update(name: tuple[str, ...]) -> None:
 57    """Update vendored dependencies in pyproject.toml"""
 58    deps = get_deps()
 59    if not deps:
 60        click.echo(
 61            "No vendored dependencies found in pyproject.toml. Use [tool.plain.vendor.dependencies]"
 62        )
 63        return
 64
 65    errors = []
 66
 67    if name:
 68        deps = [dep for dep in deps if dep.name in name]
 69        if len(deps) != len(name):
 70            not_found = set(name) - {dep.name for dep in deps}
 71            raise click.UsageError(
 72                f"Some dependencies not found: {', '.join(not_found)}"
 73            )
 74
 75    for dep in deps:
 76        click.secho(f"Updating {dep.name} {dep.installed}...", bold=True, nl=False)
 77        try:
 78            vendored_path = dep.update()
 79            vendored_path = vendored_path.relative_to(Path.cwd())
 80
 81            click.secho(f" {dep.installed}", fg="green", nl=False)
 82            click.secho(f" -> {vendored_path}")
 83        except DependencyError as e:
 84            click.secho(f"  {e}", fg="red")
 85            errors.append(e)
 86
 87    if errors:
 88        click.secho("Failed to install some dependencies.", fg="red")
 89        sys.exit(1)
 90
 91
 92@cli.command()
 93@click.argument("url")
 94@click.option("--name", help="Name of the dependency")
 95@click.option("--sourcemap/--no-sourcemap", default=True, help="Download sourcemap")
 96def add(url: str, name: str | None, sourcemap: bool) -> None:
 97    """Add a new vendored dependency to pyproject.toml"""
 98    if not name:
 99        name = url.split("/")[-1].split("?")[0].split("#")[0]
100
101    dep = Dependency(name, url=url, sourcemap=sourcemap)
102
103    click.secho(f"Installing {dep.name}", bold=True, nl=False)
104
105    try:
106        vendored_path = dep.update()
107    except DependencyError as e:
108        click.secho(f"  {e}", fg="red")
109        sys.exit(1)
110
111    vendored_path = vendored_path.relative_to(Path.cwd())
112
113    click.secho(f" {dep.installed}", fg="green", nl=False)
114    click.secho(f" -> {vendored_path}")
115
116    if not dep.installed:
117        click.secho(
118            "No version was parsed from the url. You can configure it manually if you need to.",
119            fg="yellow",
120        )