Coverage for /Users/davegaeddert/Development/dropseed/plain/plain-tailwind/plain/tailwind/core.py: 33%
103 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-16 22:04 -0500
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-16 22:04 -0500
1import os
2import platform
3import subprocess
4import sys
6import requests
7import tomlkit
9from plain.runtime import settings
11DEFAULT_CSS = """@tailwind base;
14@tailwind components;
17@tailwind utilities;
18"""
21class Tailwind:
22 @property
23 def target_directory(self) -> str:
24 return str(settings.PLAIN_TEMP_PATH)
26 @property
27 def standalone_path(self) -> str:
28 return os.path.join(self.target_directory, "tailwind")
30 @property
31 def version_lockfile_path(self) -> str:
32 return os.path.join(self.target_directory, "tailwind.version")
34 @property
35 def config_path(self) -> str:
36 return os.path.join(
37 os.path.dirname(self.target_directory), "tailwind.config.js"
38 )
40 @property
41 def src_css_path(self) -> str:
42 return settings.TAILWIND_SRC_PATH
44 @property
45 def dist_css_path(self) -> str:
46 return settings.TAILWIND_DIST_PATH
48 def invoke(self, *args, cwd=None) -> None:
49 result = subprocess.run([self.standalone_path] + list(args), cwd=cwd)
50 if result.returncode != 0:
51 sys.exit(result.returncode)
53 def is_installed(self) -> bool:
54 if not os.path.exists(self.target_directory):
55 os.mkdir(self.target_directory)
56 return os.path.exists(os.path.join(self.target_directory, "tailwind"))
58 def config_exists(self) -> bool:
59 return os.path.exists(self.config_path)
61 def create_config(self):
62 self.invoke("init", self.config_path)
64 def src_css_exists(self) -> bool:
65 return os.path.exists(self.src_css_path)
67 def create_src_css(self):
68 os.makedirs(os.path.dirname(self.src_css_path), exist_ok=True)
69 with open(self.src_css_path, "w") as f:
70 f.write(DEFAULT_CSS)
72 def needs_update(self) -> bool:
73 if not os.path.exists(self.version_lockfile_path):
74 return True
76 with open(self.version_lockfile_path) as f:
77 locked_version = f.read().strip()
79 if locked_version != self.get_version_from_config():
80 return True
82 return False
84 def get_version_from_config(self) -> str:
85 pyproject_path = os.path.join(
86 os.path.dirname(self.target_directory), "pyproject.toml"
87 )
89 if not os.path.exists(pyproject_path):
90 return ""
92 with open(pyproject_path) as f:
93 config = tomlkit.load(f)
94 return (
95 config.get("tool", {})
96 .get("plain", {})
97 .get("tailwind", {})
98 .get("version", "")
99 )
101 def set_version_in_config(self, version):
102 pyproject_path = os.path.join(
103 os.path.dirname(self.target_directory), "pyproject.toml"
104 )
106 with open(pyproject_path) as f:
107 config = tomlkit.load(f)
109 config.setdefault("tool", {}).setdefault("plain", {}).setdefault(
110 "tailwind", {}
111 )["version"] = version
113 with open(pyproject_path, "w") as f:
114 tomlkit.dump(config, f)
116 def download(self, version="") -> str:
117 if version:
118 if not version.startswith("v"):
119 version = f"v{version}"
120 url = f"https://github.com/tailwindlabs/tailwindcss/releases/download/{version}/tailwindcss-{self.detect_platform_slug()}"
121 else:
122 url = f"https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-{self.detect_platform_slug()}"
124 with requests.get(url, stream=True) as response:
125 response.raise_for_status()
126 with open(self.standalone_path, "wb") as f:
127 for chunk in response.iter_content(chunk_size=8192):
128 f.write(chunk)
130 os.chmod(self.standalone_path, 0o755)
132 if not version:
133 # Get the version from the redirect chain (latest -> vX.Y.Z)
134 version = response.history[1].url.split("/")[-2]
136 version = version.lstrip("v")
138 with open(self.version_lockfile_path, "w") as f:
139 f.write(version)
141 return version
143 def install(self, version="") -> str:
144 installed_version = self.download(version)
145 self.set_version_in_config(installed_version)
146 return installed_version
148 @staticmethod
149 def detect_platform_slug() -> str:
150 uname = platform.uname()[0]
152 if uname == "Windows":
153 return "windows-x64.exe"
155 if uname == "Linux" and platform.uname()[4] == "aarch64":
156 return "linux-arm64"
158 if uname == "Linux":
159 return "linux-x64"
161 if uname == "Darwin" and platform.uname().machine == "arm64":
162 return "macos-arm64"
164 if uname == "Darwin":
165 return "macos-x64"
167 raise Exception("Unsupported platform for Tailwind standalone")