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