Files
HappyOnigiri-ComfyUI-Meld/check_scripts/check_import_paths.py
T

147 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""
Check that ComfyUI scripts (api.js, app.js) are imported via absolute paths.
Enforces agent-config/rules/web.mdc: ComfyUI scripts must use /scripts/api.js and
/scripts/app.js. Relative paths (e.g. ../../../scripts/api.js) break the build
when files are moved.
Scope: ui/src/**/*.{ts,tsx}
Excluded: types.d.ts (declaration file; tsc needs relative patterns for resolution)
"""
import os
import re
import sys
# Matches canonical ComfyUI script imports only:
# - static: from "/scripts/api.js" / import "/scripts/app.js"
# - dynamic: import("/scripts/api.js") / import("/scripts/app.js")
# Captures the import path in group "specifier".
PATTERN = re.compile(
r"""
(?:
(?:from|import)\s+['"](?P<static>/scripts/(?:api|app)\.js)['"]
|
import\(\s*['"](?P<dynamic>/scripts/(?:api|app)\.js)['"]\s*\)
)
""",
re.VERBOSE,
)
ALLOWED_SPECIFIERS = {"/scripts/api.js", "/scripts/app.js"}
def is_relative(path: str) -> bool:
"""Return True when specifier does not use absolute-leading slash."""
return not path.startswith("/")
def check_file(filepath: str) -> tuple[list[tuple[int, str, str]], bool]:
"""Check a single file and return (lint_errors, had_read_error)."""
errors: list[tuple[int, str, str]] = []
had_read_error = False
try:
with open(filepath, encoding="utf-8") as f:
for i, line in enumerate(f, 1):
# Static import forms: from "..." / import "..."
for static_match in re.finditer(r'(?:from|import)\s+[\'"]([^\'"]+)[\'"]', line):
specifier = static_match.group(1)
if "scripts/" not in specifier:
continue
if is_relative(specifier):
errors.append(
(
i,
line.strip(),
f'Use absolute path "/scripts/..." instead of "{specifier}"',
)
)
continue
if specifier not in ALLOWED_SPECIFIERS:
errors.append(
(
i,
line.strip(),
f'Only "/scripts/api.js" and "/scripts/app.js" are allowed, got "{specifier}"',
)
)
# Dynamic import form: import("...")
for dynamic_match in re.finditer(r'import\(\s*[\'"]([^\'"]+)[\'"]\s*\)', line):
specifier = dynamic_match.group(1)
if "scripts/" not in specifier:
continue
if is_relative(specifier):
errors.append(
(
i,
line.strip(),
f'Use absolute path "/scripts/..." instead of "{specifier}"',
)
)
continue
if specifier not in ALLOWED_SPECIFIERS:
errors.append(
(
i,
line.strip(),
f'Only "/scripts/api.js" and "/scripts/app.js" are allowed, got "{specifier}"',
)
)
except Exception as e:
had_read_error = True
print(f"Error reading {filepath}: {e}")
return errors, had_read_error
def main() -> None:
target_dir = os.path.join("ui", "src")
base_dir = os.getcwd()
search_path = os.path.join(base_dir, target_dir)
if not os.path.exists(search_path):
print(f"Directory not found: {search_path}")
sys.exit(1)
print(f"Checking ComfyUI script import paths in {target_dir}...")
has_errors = False
read_error_count = 0
ignore_files = {"types.d.ts"}
for root, _, files in os.walk(search_path):
for file in files:
if not (file.endswith(".ts") or file.endswith(".tsx")):
continue
if file in ignore_files:
continue
filepath = os.path.join(root, file)
rel_path = os.path.relpath(filepath, base_dir)
errors, had_read_error = check_file(filepath)
if had_read_error:
read_error_count += 1
if errors:
has_errors = True
print(f"\n[!] Relative ComfyUI script import in {rel_path}:")
for line_num, line_content, reason in errors:
print(f" Line {line_num}: {line_content}")
print(f" -> {reason}")
if read_error_count > 0:
print(f"\nError: Failed to read {read_error_count} file(s) during import path check.")
sys.exit(1)
if has_errors:
print("\nError: Use absolute paths for ComfyUI scripts.")
print(' Allowed: from "/scripts/api.js" or from "/scripts/app.js"')
print(' Forbidden: from "../../scripts/api.js" or similar relative paths')
print(" See agent-config/rules/web.mdc (Import Paths section)")
sys.exit(1)
else:
print("Import path check passed.")
sys.exit(0)
if __name__ == "__main__":
main()