diff --git a/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts b/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts index 23f3dbb4e..ee93e2f2e 100644 --- a/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts +++ b/B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts @@ -19,10 +19,9 @@ import { appendFordOverlay } from "../B06_Section/B06_Section_UI_Cross_Ford"; import { appendFordPavementOverlay } from "../B06_Section/B06_Section_UI_Cross_Ford_Pavement"; import { appendUnderdrainOverlay } from "../B06_Section/B06_Section_UI_Cross_Underdrain"; import { groundLine } from "../B06_Section/logic/B06_Section_Cross_Wall_Hatch_Geom"; -import { - appendCrossDesignOverlay, - appendPavementOverlay, -} from "../B06_Section/B06_Section_UI_Cross_Design"; +// 허브(`_UI_Cross_Design`)가 아닌 그리기 파일에서 직접 — 허브는 상세 창 · CAD · API 를 끈다(574-1) +import { appendCrossDesignOverlay } from "../B06_Section/B06_Section_UI_Cross_Design_Line"; +import { appendPavementOverlay } from "../B06_Section/B06_Section_UI_Cross_Design_Surface"; import { appendRevetmentOverlay } from "../B06_Section/B06_Section_UI_Cross_Revetment"; import { clipPointsToPolygon, clipSegment, pointInPolygon } from "../A00_Common/b_polygon_clip"; diff --git a/resources/tester/test_574_b07_extent_no_ui.py b/resources/tester/test_574_b07_extent_no_ui.py new file mode 100644 index 000000000..beba7120f --- /dev/null +++ b/resources/tester/test_574_b07_extent_no_ui.py @@ -0,0 +1,68 @@ +"""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