Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain-tailwind/plain/tailwind/core.py: 33%

103 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1import os 

2import platform 

3import subprocess 

4import sys 

5 

6import requests 

7import tomlkit 

8 

9from plain.runtime import settings 

10 

11DEFAULT_CSS = """@tailwind base; 

12 

13 

14@tailwind components; 

15 

16 

17@tailwind utilities; 

18""" 

19 

20 

21class Tailwind: 

22 @property 

23 def target_directory(self) -> str: 

24 return str(settings.PLAIN_TEMP_PATH) 

25 

26 @property 

27 def standalone_path(self) -> str: 

28 return os.path.join(self.target_directory, "tailwind") 

29 

30 @property 

31 def version_lockfile_path(self) -> str: 

32 return os.path.join(self.target_directory, "tailwind.version") 

33 

34 @property 

35 def config_path(self) -> str: 

36 return os.path.join( 

37 os.path.dirname(self.target_directory), "tailwind.config.js" 

38 ) 

39 

40 @property 

41 def src_css_path(self) -> str: 

42 return settings.TAILWIND_SRC_PATH 

43 

44 @property 

45 def dist_css_path(self) -> str: 

46 return settings.TAILWIND_DIST_PATH 

47 

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) 

52 

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")) 

57 

58 def config_exists(self) -> bool: 

59 return os.path.exists(self.config_path) 

60 

61 def create_config(self): 

62 self.invoke("init", self.config_path) 

63 

64 def src_css_exists(self) -> bool: 

65 return os.path.exists(self.src_css_path) 

66 

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) 

71 

72 def needs_update(self) -> bool: 

73 if not os.path.exists(self.version_lockfile_path): 

74 return True 

75 

76 with open(self.version_lockfile_path) as f: 

77 locked_version = f.read().strip() 

78 

79 if locked_version != self.get_version_from_config(): 

80 return True 

81 

82 return False 

83 

84 def get_version_from_config(self) -> str: 

85 pyproject_path = os.path.join( 

86 os.path.dirname(self.target_directory), "pyproject.toml" 

87 ) 

88 

89 if not os.path.exists(pyproject_path): 

90 return "" 

91 

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 ) 

100 

101 def set_version_in_config(self, version): 

102 pyproject_path = os.path.join( 

103 os.path.dirname(self.target_directory), "pyproject.toml" 

104 ) 

105 

106 with open(pyproject_path) as f: 

107 config = tomlkit.load(f) 

108 

109 config.setdefault("tool", {}).setdefault("plain", {}).setdefault( 

110 "tailwind", {} 

111 )["version"] = version 

112 

113 with open(pyproject_path, "w") as f: 

114 tomlkit.dump(config, f) 

115 

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()}" 

123 

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) 

129 

130 os.chmod(self.standalone_path, 0o755) 

131 

132 if not version: 

133 # Get the version from the redirect chain (latest -> vX.Y.Z) 

134 version = response.history[1].url.split("/")[-2] 

135 

136 version = version.lstrip("v") 

137 

138 with open(self.version_lockfile_path, "w") as f: 

139 f.write(version) 

140 

141 return version 

142 

143 def install(self, version="") -> str: 

144 installed_version = self.download(version) 

145 self.set_version_in_config(installed_version) 

146 return installed_version 

147 

148 @staticmethod 

149 def detect_platform_slug() -> str: 

150 uname = platform.uname()[0] 

151 

152 if uname == "Windows": 

153 return "windows-x64.exe" 

154 

155 if uname == "Linux" and platform.uname()[4] == "aarch64": 

156 return "linux-arm64" 

157 

158 if uname == "Linux": 

159 return "linux-x64" 

160 

161 if uname == "Darwin" and platform.uname().machine == "arm64": 

162 return "macos-arm64" 

163 

164 if uname == "Darwin": 

165 return "macos-x64" 

166 

167 raise Exception("Unsupported platform for Tailwind standalone")