Fix manifest icon references and add file reference linter

Manifest fixes:
- Updated all icon references from .png to .svg files
- Changed MIME types from image/png to image/svg+xml
- Removed missing screenshots section
- Fixed HTML favicon references to use SVG files

New reference linter (lint-references.py):
- Validates all file references in HTML and manifest files
- Checks that referenced files actually exist
- Prevents broken links and missing assets
- Integrated into justfile as 'validate-references'
- Added to check-all command for comprehensive validation

Justfile enhancements:
- Added validate-references command
- Updated check-all to include reference validation
- Enhanced validate command with reference checking

Documentation updates:
- Updated CLAUDE.md with reference validation info
- Added file reference checking to code quality standards

This prevents issues where manifest/HTML reference non-existent files,
improving PWA reliability and catching asset mismatches early.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2025-08-18 20:18:51 +02:00
commit 35c7d0bdc9
5 changed files with 179 additions and 31 deletions

View file

@ -68,6 +68,9 @@ bun run check # Check formatting without modifying
# Python
black server.py # Format Python code
pylint server.py # Lint Python code
# File References
just validate-references # Check HTML/manifest file references exist
```
### Icon Generation
@ -164,10 +167,11 @@ node app/create-icons.js # Generate PNG icons
- Print styles
### Code Quality
- ESLint: No errors, minimal warnings
- ESLint: No errors, no warnings
- Prettier: All files formatted
- Black: Python code formatted
- Pylint: Score 10/10
- File References: All HTML/manifest references exist
## Common Tasks

View file

@ -10,9 +10,9 @@
<meta name="theme-color" content="#1a1a2e" />
<title>GlitchCraft - Artisanal Text Corruption</title>
<link rel="manifest" href="manifest.json" />
<link rel="icon" type="image/png" sizes="192x192" href="icons/icon-192.png" />
<link rel="icon" type="image/png" sizes="512x512" href="icons/icon-512.png" />
<link rel="apple-touch-icon" href="icons/icon-192.png" />
<link rel="icon" type="image/svg+xml" sizes="192x192" href="icons/icon-192.svg" />
<link rel="icon" type="image/svg+xml" sizes="512x512" href="icons/icon-512.svg" />
<link rel="apple-touch-icon" href="icons/icon-192.svg" />
<link rel="stylesheet" href="styles.css" />
</head>
<body>

View file

@ -9,54 +9,47 @@
"orientation": "portrait-primary",
"icons": [
{
"src": "icons/icon-72.png",
"src": "icons/icon-72.svg",
"sizes": "72x72",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-96.png",
"src": "icons/icon-96.svg",
"sizes": "96x96",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-128.png",
"src": "icons/icon-128.svg",
"sizes": "128x128",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-144.png",
"src": "icons/icon-144.svg",
"sizes": "144x144",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-152.png",
"src": "icons/icon-152.svg",
"sizes": "152x152",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-192.png",
"src": "icons/icon-192.svg",
"sizes": "192x192",
"type": "image/png",
"type": "image/svg+xml",
"purpose": "any maskable"
},
{
"src": "icons/icon-384.png",
"src": "icons/icon-384.svg",
"sizes": "384x384",
"type": "image/png"
"type": "image/svg+xml"
},
{
"src": "icons/icon-512.png",
"src": "icons/icon-512.svg",
"sizes": "512x512",
"type": "image/png",
"type": "image/svg+xml",
"purpose": "any maskable"
}
],
"categories": ["utilities", "productivity"],
"screenshots": [
{
"src": "screenshots/screenshot1.png",
"type": "image/png",
"sizes": "1280x720"
}
]
"categories": ["utilities", "productivity"]
}

View file

@ -55,8 +55,8 @@ lint-all: lint-js lint-python
format-all: format-python format
@echo "✓ All formatting completed"
# Check everything (linting + formatting)
check-all: lint-all check-format
# Check everything (linting + formatting + references)
check-all: lint-all check-format validate-references
@echo "✓ All checks passed"
# PWA icon generation
@ -97,8 +97,12 @@ validate-html:
validate-manifest:
python3 -m json.tool app/manifest.json > /dev/null && echo "✓ Manifest is valid JSON"
# Check file references (HTML, manifest)
validate-references:
python3 lint-references.py
# Run all validations
validate: validate-html validate-manifest check-all
validate: validate-html validate-manifest validate-references check-all
@echo "✓ All validations passed"
# Git helpers

147
lint-references.py Executable file
View file

@ -0,0 +1,147 @@
#!/usr/bin/env python3
"""
Reference validator for GlitchCraft
Checks that all file references in HTML and manifest actually exist
"""
import json
import os
import re
import sys
from pathlib import Path
def check_file_exists(file_path, base_dir):
"""Check if a file exists relative to base directory."""
full_path = base_dir / file_path
return full_path.exists()
def extract_html_references(html_content):
"""Extract file references from HTML content."""
references = []
# Link href attributes
for match in re.finditer(r'<link[^>]+href=["\']([^"\']+)["\']', html_content):
href = match.group(1)
if not href.startswith(('http://', 'https://', '//', 'data:', '#')):
references.append(href)
# Script src attributes
for match in re.finditer(r'<script[^>]+src=["\']([^"\']+)["\']', html_content):
src = match.group(1)
if not src.startswith(('http://', 'https://', '//', 'data:')):
references.append(src)
# Image src attributes
for match in re.finditer(r'<img[^>]+src=["\']([^"\']+)["\']', html_content):
src = match.group(1)
if not src.startswith(('http://', 'https://', '//', 'data:')):
references.append(src)
return references
def extract_manifest_references(manifest_data):
"""Extract file references from manifest JSON."""
references = []
# Icon sources
if 'icons' in manifest_data:
for icon in manifest_data['icons']:
if 'src' in icon:
references.append(icon['src'])
# Screenshot sources
if 'screenshots' in manifest_data:
for screenshot in manifest_data['screenshots']:
if 'src' in screenshot:
references.append(screenshot['src'])
return references
def check_html_file(file_path, base_dir):
"""Check all references in an HTML file."""
errors = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
references = extract_html_references(content)
for ref in references:
if not check_file_exists(ref, base_dir):
errors.append(f"Missing file: {ref}")
except Exception as e:
errors.append(f"Error reading {file_path}: {e}")
return errors
def check_manifest_file(file_path, base_dir):
"""Check all references in a manifest file."""
errors = []
try:
with open(file_path, 'r', encoding='utf-8') as f:
manifest_data = json.load(f)
references = extract_manifest_references(manifest_data)
for ref in references:
if not check_file_exists(ref, base_dir):
errors.append(f"Missing file: {ref}")
except Exception as e:
errors.append(f"Error reading {file_path}: {e}")
return errors
def main():
"""Main function to check all references."""
script_dir = Path(__file__).parent
app_dir = script_dir / 'app'
print("🔍 Checking file references...")
total_errors = 0
# Check HTML files
html_files = list(app_dir.glob('*.html'))
for html_file in html_files:
errors = check_html_file(html_file, app_dir)
if errors:
print(f"\n{html_file.name}:")
for error in errors:
print(f"{error}")
total_errors += 1
else:
print(f"{html_file.name}")
# Check manifest file
manifest_file = app_dir / 'manifest.json'
if manifest_file.exists():
errors = check_manifest_file(manifest_file, app_dir)
if errors:
print(f"\n❌ manifest.json:")
for error in errors:
print(f"{error}")
total_errors += 1
else:
print("✅ manifest.json")
# Summary
if total_errors == 0:
print(f"\n🎉 All file references are valid!")
return 0
else:
print(f"\n💥 Found {total_errors} reference error(s)")
return 1
if __name__ == '__main__':
sys.exit(main())