Plain is headed towards 1.0! Subscribe for development updates →

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        )