"""B07 횡단 덧그림 서버 묶음(`b07-extent`)이 화면 코드를 끌지 않음 (574-1). 묶음 입구에서 값 import 를 따라가 닿는 TS 파일에 확인 창 · CAD · 상세 창 · API · 부품 상자 · B06 허브(`_UI_Cross_Design`)가 없어야 함. `import type` 은 지워지므로 뺌. """ import re from pathlib import Path ROOT = Path(__file__).resolve().parents[2] ENTRY = ROOT / "B07_DesignDetail" / "B07_DesignDetail_Cross_Extent_Node.ts" ALIAS = {"@util/": ROOT / "common_util", "@ui/": ROOT / "ui_template"} _IMPORT = re.compile( r"^\s*(?:import|export)\s+(type\s+)?(?:([^;]*?)\s+from\s+)?[\"']([^\"']+)[\"']", re.M | re.S, ) FORBIDDEN = re.compile( r"ui_template|_Cad|Cross_Detail|_Api_|B06_Section_UI_Cross_Design\.ts$|three" ) def _type_only(names: str | None) -> bool: inner = (names or "").strip() if not inner.startswith("{"): return False parts = [p.strip() for p in inner.strip("{} \n").split(",") if p.strip()] return bool(parts) and all(p.startswith("type ") for p in parts) def _resolve(origin: Path, spec: str) -> Path | None: for prefix, base in ALIAS.items(): if spec.startswith(prefix): target = base / spec[len(prefix) :] break else: if not spec.startswith("."): return None # node:fs · 라이브러리 target = origin.parent / spec for suffix in (".ts", ""): path = Path(str(target) + suffix).resolve() if path.is_file(): return path return None def _reachable() -> set[Path]: seen, queue = {ENTRY.resolve()}, [ENTRY.resolve()] while queue: current = queue.pop() if current.suffix != ".ts": continue for match in _IMPORT.finditer(current.read_text(encoding="utf-8")): type_kw, names, spec = match.groups() if type_kw or _type_only(names): continue found = _resolve(current, spec) if found and found not in seen: seen.add(found) queue.append(found) return seen def test_extent_bundle_pulls_no_screen_code(): reached = _reachable() names = {p.relative_to(ROOT).as_posix() for p in reached} assert "B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts" in names # 걷기가 실제로 돎 bad = sorted(n for n in names if FORBIDDEN.search(n)) assert not bad, bad