* Rename WebSocket message types for better organization * Improve state handling with dedicated methods like broadcast_state * Restructure frontend components and remove unused code
103 lines
3.3 KiB
Python
Executable File
103 lines
3.3 KiB
Python
Executable File
#!/usr/bin/env python
|
||
import importlib
|
||
import inspect
|
||
import os
|
||
import sys
|
||
from enum import Enum, IntEnum, StrEnum
|
||
|
||
# Adjust these for your project
|
||
PY_MODULE = "draft.constants" # where your enums live
|
||
OUTPUT_PATH = "frontend/src/apps/draft/constants.js"
|
||
|
||
# Optionally allow running from any cwd
|
||
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||
PROJECT_ROOT = os.path.abspath(os.path.join(PROJECT_ROOT, ".."))
|
||
if PROJECT_ROOT not in sys.path:
|
||
sys.path.insert(0, PROJECT_ROOT)
|
||
|
||
|
||
def js_quote(s: str) -> str:
|
||
return s.replace("\\", "\\\\").replace('"', '\\"')
|
||
|
||
|
||
def titleize(name: str) -> str:
|
||
# e.g., "DETERMINE_ORDER" -> "Determine Order"
|
||
return name.replace("_", " ").title()
|
||
|
||
|
||
def emit_header():
|
||
return "// AUTO-GENERATED. Do not edit by hand.\n" \
|
||
"// Run: python scripts/generate_js_constants.py\n\n"
|
||
|
||
|
||
def emit_str_enum(name: str, enum_cls) -> str:
|
||
"""
|
||
Emit a JS object for StrEnum:
|
||
export const DraftMessage = { KEY: "value", ... };
|
||
"""
|
||
lines = [f"export const {name} = {{"] # ESM export
|
||
for member in enum_cls:
|
||
lines.append(f' {member.name}: "{js_quote(member.value)}",')
|
||
lines.append("};\n")
|
||
return "\n".join(lines)
|
||
|
||
|
||
def emit_int_enum(name: str, enum_cls) -> str:
|
||
"""
|
||
Emit a JS object + labels + ordered list for IntEnum:
|
||
export const DraftPhase = { KEY: number, ... };
|
||
export const DraftPhaseLabel = { [number]: "Pretty", ... };
|
||
export const DraftPhasesOrdered = [numbers...];
|
||
"""
|
||
lines = [f"export const {name} = {{"] # ESM export
|
||
items = list(enum_cls)
|
||
# object map
|
||
for member in items:
|
||
lines.append(f" {member.name}: {int(member.value)},")
|
||
lines.append("};\n")
|
||
|
||
# label map (use .pretty_name if you added it; else derive from name or __str__)
|
||
lines.append(f"export const {name}Label = {{")
|
||
for member in items:
|
||
if hasattr(member, "pretty_name"):
|
||
label = getattr(member, "pretty_name")
|
||
else:
|
||
# fall back: __str__ if you overload it, else Title Case of name
|
||
label = str(member)
|
||
if label == f"{enum_cls.__name__}.{member.name}":
|
||
label = titleize(member.name)
|
||
lines.append(f' [{name}.{member.name}]: "{js_quote(label)}",')
|
||
lines.append("};\n")
|
||
|
||
# ordered list
|
||
ordered = sorted(items, key=lambda m: int(m.value))
|
||
ordered_vals = ", ".join(f"{name}.{m.name}" for m in ordered)
|
||
lines.append(f"export const {name}sOrdered = [{ordered_vals}];\n")
|
||
return "\n".join(lines)
|
||
|
||
|
||
def main():
|
||
mod = importlib.import_module(PY_MODULE)
|
||
out = [emit_header()]
|
||
|
||
# Pick which enums to export. You can filter here if you don’t want all.
|
||
for name, obj in inspect.getmembers(mod):
|
||
ignore_classes = [Enum, IntEnum, StrEnum]
|
||
if inspect.isclass(obj) and issubclass(obj, Enum) and not obj in ignore_classes:
|
||
# Skip helper classes that aren’t actual Enums
|
||
if name.startswith("_"):
|
||
continue
|
||
if issubclass(obj, IntEnum):
|
||
out.append(emit_int_enum(name, obj))
|
||
else:
|
||
out.append(emit_str_enum(name, obj))
|
||
|
||
os.makedirs(os.path.dirname(OUTPUT_PATH), exist_ok=True)
|
||
with open(OUTPUT_PATH, "w", encoding="utf-8") as f:
|
||
f.write("\n".join(out))
|
||
|
||
print(f"✅ Wrote {OUTPUT_PATH}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |