plain.vendor
Download those CDN scripts and styles.
What about source maps?
It's fairly common right now to get an error during plain build
that says it can't find the source map for one of your vendored files.
Right now, the fix is add the source map itself to your vendored dependencies too.
In the future plain vendor
might discover those during the vendoring process and download them automatically with the compiled files.
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 click.secho(
74 f"Some dependencies not found: {', '.join(not_found)}", fg="red"
75 )
76 exit(1)
77
78 for dep in deps:
79 click.secho(f"Updating {dep.name} {dep.installed}...", bold=True, nl=False)
80 try:
81 vendored_path = dep.update()
82 vendored_path = vendored_path.relative_to(Path.cwd())
83
84 click.secho(f" {dep.installed}", fg="green", nl=False)
85 click.secho(f" -> {vendored_path}")
86 except DependencyError as e:
87 click.secho(f" {e}", fg="red")
88 errors.append(e)
89
90 if errors:
91 click.secho("Failed to install some dependencies.", fg="red")
92 exit(1)
93
94
95@cli.command()
96@click.argument("url")
97@click.option("--name", help="Name of the dependency")
98@click.option("--sourcemap/--no-sourcemap", default=True, help="Download sourcemap")
99def add(url, name, sourcemap):
100 """Add a new vendored dependency to pyproject.toml"""
101 if not name:
102 name = url.split("/")[-1]
103
104 dep = Dependency(name, url=url, sourcemap=sourcemap)
105
106 click.secho(f"Installing {dep.name}", bold=True, nl=False)
107
108 try:
109 vendored_path = dep.update()
110 except DependencyError as e:
111 click.secho(f" {e}", fg="red")
112 exit(1)
113
114 vendored_path = vendored_path.relative_to(Path.cwd())
115
116 click.secho(f" {dep.installed}", fg="green", nl=False)
117 click.secho(f" -> {vendored_path}")
118
119 if not dep.installed:
120 click.secho(
121 "No version was parsed from the url. You can configure it manually if you need to.",
122 fg="yellow",
123 )