"""Generate the two files the frontend derives from the live node schema. Both would otherwise be hand-maintained copies of what ``relight.py`` already says, and a silent drift in either is a frontend that lies about the node: * ``tests/frontend/fixtures/schema.json`` - widget names, order and defaults, which the JS tests build their fake nodes from. * ``web/relight_presets.js`` - which widgets each preset overrides, so ``relight_ui.js`` can grey exactly those and no others. ``--check`` fails if either committed file is stale, and ``tests/test_relight.py`` runs that check. python scripts/dump_frontend_fixture.py # rewrite both python scripts/dump_frontend_fixture.py --check # fail if either is stale """ import argparse import json import pathlib import sys REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent FIXTURE = REPO_ROOT / "tests" / "frontend" / "fixtures" / "schema.json" PRESETS_MODULE = REPO_ROOT / "web" / "relight_presets.js" # The stub must win over any real ComfyUI on the path, exactly as in conftest. sys.path.insert(0, str(REPO_ROOT / "tests" / "stubs")) sys.path.insert(1, str(REPO_ROOT)) from relight import ReLight # noqa: E402 def build(): schema = ReLight.define_schema() widgets = [] for spec in schema.inputs: if spec.id in ("image", "mask"): continue # link inputs, never widgets entry = {"name": spec.id, "default": spec.default} options = spec.kwargs.get("options") if options is not None: entry["options"] = list(options) widgets.append(entry) return {"node_class": schema.node_id, "widgets": widgets} def build_presets_module(): """`web/relight_presets.js`: which widgets each preset takes control of.""" keys = {name: sorted(preset) for name, preset in ReLight.PRESETS.items()} values = { name: {key: preset[key] for key in sorted(preset)} for name, preset in ReLight.PRESETS.items() } keys_body = json.dumps(keys, indent=4, sort_keys=False) values_body = json.dumps(values, indent=4, sort_keys=False) return ( "/*\n" " * GENERATED by scripts/dump_frontend_fixture.py - do not edit.\n" " *\n" " * Which widgets each preset overrides, and what it sets them to.\n" " * relight_ui.js greys exactly these while a preset is selected, because a\n" " * control a preset has taken over is one whose value on the node is being\n" " * ignored - and a widget you can still drag that changes nothing is worse\n" " * than one that looks unavailable.\n" " *\n" " * The values are here because a greyed widget cannot show its own: the\n" " * ComfyUI frontend blanks `_displayValue` for any disabled widget. Without\n" " * them a greyed row is an empty bar. relight_ui.js writes the preset's\n" " * value into the widget's label instead, which still paints.\n" " *\n" " * Derived from ReLight.PRESETS so it cannot drift; the --check run in\n" " * tests/test_relight.py fails if this file is stale.\n" " */\n\n" f"export const PRESET_KEYS = {keys_body};\n\n" "/** What each preset sets those widgets to, for the greyed-out labels. */\n" f"export const PRESET_VALUES = {values_body};\n\n" "/** Widgets a preset may name but that `preserve_positioning` hands back. */\n" f"export const GEOMETRY_KEYS = {json.dumps(sorted(ReLight.GEOMETRY_KEYS), indent=4)};\n\n" "/** The widget a preset scales rather than replaces. */\n" f"export const STRENGTH_KEY = {json.dumps(ReLight.STRENGTH_KEY)};\n" ) def main(argv=None): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--check", action="store_true", help="fail if a generated file is stale") args = parser.parse_args(argv) generated = { FIXTURE: json.dumps(build(), indent=2) + "\n", PRESETS_MODULE: build_presets_module(), } if args.check: stale = [ path for path, payload in generated.items() if not path.exists() or path.read_text(encoding="utf-8") != payload ] for path in stale: print(f"{path.relative_to(REPO_ROOT).as_posix()} is stale; re-run this script") return 1 if stale else 0 for path, payload in generated.items(): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(payload, encoding="utf-8") print(f"wrote {path.relative_to(REPO_ROOT).as_posix()}") return 0 if __name__ == "__main__": raise SystemExit(main())