-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvalidate_config.py
More file actions
executable file
·261 lines (207 loc) · 8.22 KB
/
Copy pathvalidate_config.py
File metadata and controls
executable file
·261 lines (207 loc) · 8.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#!/usr/bin/env python3
"""Validate Sparkle Design configuration files.
This script validates the presence and correctness of required configuration
files for Sparkle Design components.
Usage:
validate_config.py [--path /path/to/project]
Checks:
- components.json exists and has @sparkle-design registry
- sparkle.config.json exists (optional but recommended)
- CSS import structure is correct (checks common locations)
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
REQUIRED_REGISTRY_URL = "https://sparkle-design.goodpatch.com/r/{name}.json"
def check_components_json(project_path: Path) -> tuple[bool, list[str]]:
"""Check components.json configuration.
Args:
project_path: Path to the project directory
Returns:
Tuple of (is_valid, list of issues)
"""
issues: list[str] = []
components_json = project_path / "components.json"
if not components_json.exists():
issues.append("❌ components.json not found")
return False, issues
try:
with components_json.open(encoding="utf-8") as f:
config: dict[str, Any] = json.load(f)
except json.JSONDecodeError as e:
issues.append(f"❌ components.json has invalid JSON: {e}")
return False, issues
except Exception as e:
issues.append(f"❌ Error reading components.json: {e}")
return False, issues
# Check for registries field
if "registries" not in config:
issues.append("⚠️ 'registries' field not found in components.json")
issues.append(
" Add: {\"registries\": {\"@sparkle-design\": "
f"\"{REQUIRED_REGISTRY_URL}\"}}}}"
)
return False, issues
registries = config["registries"]
if "@sparkle-design" not in registries:
issues.append("⚠️ @sparkle-design registry not configured")
issues.append(
f" Add: \"@sparkle-design\": \"{REQUIRED_REGISTRY_URL}\""
)
return False, issues
# Verify registry URL
registry_url = registries["@sparkle-design"]
if registry_url != REQUIRED_REGISTRY_URL:
issues.append(f"⚠️ Incorrect registry URL: {registry_url}")
issues.append(f" Expected: {REQUIRED_REGISTRY_URL}")
return False, issues
issues.append("✅ components.json is valid")
return True, issues
def check_sparkle_config(project_path: Path) -> tuple[bool, list[str]]:
"""Check sparkle.config.json configuration.
Args:
project_path: Path to the project directory
Returns:
Tuple of (exists, list of messages)
"""
messages: list[str] = []
sparkle_config = project_path / "sparkle.config.json"
if not sparkle_config.exists():
messages.append("ℹ️ sparkle.config.json not found (optional)")
return False, messages
try:
with sparkle_config.open(encoding="utf-8") as f:
json.load(f)
messages.append("✅ sparkle.config.json exists and is valid JSON")
return True, messages
except json.JSONDecodeError as e:
messages.append(f"❌ sparkle.config.json has invalid JSON: {e}")
return False, messages
except Exception as e:
messages.append(f"❌ Error reading sparkle.config.json: {e}")
return False, messages
def find_css_files(project_path: Path, css_pattern: str) -> list[Path]:
"""Find CSS files matching the pattern in common locations.
Args:
project_path: Path to the project directory
css_pattern: CSS filename to search for (e.g., "sparkle-design.css")
Returns:
List of matching paths
"""
common_locations = [
"src/app",
"src/styles",
"src",
"styles",
"app",
"public/styles",
]
matches = []
for loc in common_locations:
css_path = project_path / loc / css_pattern
if css_path.exists():
matches.append(css_path)
return matches
def check_css_imports(project_path: Path) -> tuple[bool, list[str]]:
"""Check CSS import structure.
Args:
project_path: Path to the project directory
Returns:
Tuple of (is_ok, list of messages)
"""
messages: list[str] = []
# Check for sparkle-design.css (or similar design system CSS)
design_css_names = ["sparkle-design.css", "design-system.css", "theme.css"]
design_css_found = False
design_css_name = "sparkle-design.css"
for css_name in design_css_names:
matches = find_css_files(project_path, css_name)
if matches:
design_css_found = True
design_css_name = css_name
messages.append(f"✅ Found {css_name} at {matches[0].relative_to(project_path)}")
break
if not design_css_found:
messages.append("⚠️ Design system CSS not found in common locations")
messages.append(" Expected filenames: sparkle-design.css, design-system.css, or theme.css")
messages.append(" Generate it with: <pm> dlx sparkle-design-cli")
# Check for globals.css (or similar main CSS file)
globals_names = ["globals.css", "global.css", "main.css", "styles.css"]
globals_found = False
for globals_name in globals_names:
matches = find_css_files(project_path, globals_name)
if matches:
globals_path = matches[0]
try:
content = globals_path.read_text(encoding="utf-8")
# Check if it imports the design system CSS
if design_css_name in content or any(name in content for name in design_css_names):
messages.append(f"✅ {globals_name} imports design system CSS at {globals_path.relative_to(project_path)}")
globals_found = True
else:
messages.append(f"⚠️ {globals_name} found but doesn't import design system CSS")
messages.append(f" Add: @import \"./{design_css_name}\";")
break
except Exception as e:
messages.append(f"⚠️ Error reading {globals_name}: {e}")
break
if not globals_found and design_css_found:
messages.append("⚠️ Main CSS file not found or doesn't import design system CSS")
messages.append(" Expected filenames: globals.css, global.css, main.css, or styles.css")
return (design_css_found and globals_found), messages
def main() -> int:
parser = argparse.ArgumentParser(
description="Validate Sparkle Design configuration",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Validate current directory
python validate_config.py
# Validate specific directory
python validate_config.py --path /path/to/project
""",
)
parser.add_argument(
"--path",
type=Path,
default=Path.cwd(),
help="Path to project directory (default: current directory)",
)
args = parser.parse_args()
if not args.path.exists():
print(f"❌ Error: Directory not found: {args.path}", file=sys.stderr)
return 1
if not args.path.is_dir():
print(f"❌ Error: Not a directory: {args.path}", file=sys.stderr)
return 1
print(f"🔍 Validating Sparkle Design configuration in: {args.path}\n")
# Check components.json
components_valid, components_issues = check_components_json(args.path)
for issue in components_issues:
print(issue)
print()
# Check sparkle.config.json
sparkle_exists, sparkle_messages = check_sparkle_config(args.path)
for msg in sparkle_messages:
print(msg)
print()
# Check CSS imports
css_ok, css_messages = check_css_imports(args.path)
for msg in css_messages:
print(msg)
print()
# Summary
if components_valid and css_ok:
print("✅ Configuration is valid! You can install Sparkle Design components.")
return 0
elif components_valid and not css_ok:
print("⚠️ components.json is valid, but CSS setup has issues. Components may not render correctly.")
return 1
else:
print("❌ Configuration has issues. Please fix them before installing components.")
return 1
if __name__ == "__main__":
raise SystemExit(main())