282 lines
14 KiB
Python
282 lines
14 KiB
Python
#!/usr/bin/env python3
|
||
|
||
import os
|
||
import re
|
||
from pathlib import Path
|
||
from typing import Set, Dict, List, Tuple
|
||
import subprocess
|
||
|
||
class FinalDartFileAnalyzer:
|
||
def __init__(self, project_root: str):
|
||
self.project_root = Path(project_root)
|
||
self.lib_path = self.project_root / "lib"
|
||
|
||
# 확실히 사용되지 않는 파일들 (분석 결과 기반)
|
||
self.definitely_unused = [
|
||
# Models - Equipment 관련 (백엔드 스키마 변경으로 미사용)
|
||
"lib/data/models/equipment/equipment_in_request.dart",
|
||
"lib/data/models/equipment/equipment_io_response.dart",
|
||
"lib/data/models/equipment/equipment_list_dto.dart",
|
||
"lib/data/models/equipment/equipment_out_request.dart",
|
||
"lib/data/models/equipment/equipment_request.dart",
|
||
"lib/data/models/equipment/equipment_response.dart",
|
||
"lib/data/models/equipment_history_companies_link_dto.dart",
|
||
"lib/models/user_phone_field.dart",
|
||
|
||
# Administrator (UI에서 연결되지 않음)
|
||
"lib/screens/administrator/administrator_list.dart",
|
||
|
||
# Deprecated UI Components
|
||
"lib/screens/common/custom_widgets/autocomplete_dropdown.dart",
|
||
"lib/screens/common/custom_widgets/category_data.dart",
|
||
"lib/screens/common/custom_widgets/category_selection_field.dart",
|
||
"lib/screens/common/custom_widgets/date_picker_field.dart",
|
||
"lib/screens/common/custom_widgets/highlight_text.dart",
|
||
"lib/screens/common/widgets/address_input.dart",
|
||
"lib/screens/common/widgets/autocomplete_dropdown_field.dart",
|
||
"lib/screens/common/widgets/category_autocomplete_field.dart",
|
||
"lib/screens/common/widgets/company_branch_dropdown.dart",
|
||
|
||
# Company widgets (복잡한 UI에서 단순화됨)
|
||
"lib/screens/company/widgets/company_branch_dialog.dart",
|
||
"lib/screens/company/widgets/company_form_header.dart",
|
||
"lib/screens/company/widgets/company_info_card.dart",
|
||
"lib/screens/company/widgets/company_name_autocomplete.dart",
|
||
"lib/screens/company/widgets/duplicate_company_dialog.dart",
|
||
"lib/screens/company/widgets/map_dialog.dart",
|
||
|
||
# Equipment widgets (Phase 4에서 단순화됨)
|
||
"lib/screens/equipment/widgets/autocomplete_text_field.dart",
|
||
"lib/screens/equipment/widgets/custom_dropdown_field.dart",
|
||
"lib/screens/equipment/widgets/equipment_basic_info_section.dart",
|
||
"lib/screens/equipment/widgets/equipment_history_panel.dart",
|
||
"lib/screens/equipment/widgets/equipment_out_info.dart",
|
||
"lib/screens/equipment/widgets/equipment_status_chip.dart",
|
||
|
||
# Inventory components
|
||
"lib/screens/inventory/components/stock_level_indicator.dart",
|
||
"lib/screens/inventory/controllers/equipment_history_controller.dart",
|
||
|
||
# Model components (단순화됨)
|
||
"lib/screens/model/components/model_grouped_table.dart",
|
||
"lib/screens/model/components/model_vendor_cascade.dart",
|
||
|
||
# Rent (단순화됨)
|
||
"lib/screens/rent/rent_list_screen_simple.dart",
|
||
|
||
# Deprecated services
|
||
"lib/services/health_check_service_web.dart",
|
||
|
||
# Unused repositories
|
||
"lib/data/repositories/lookups_repository_impl.dart",
|
||
"lib/data/repositories/rent_repository.dart",
|
||
"lib/domain/repositories/lookups_repository.dart",
|
||
|
||
# Shadcn widgets (별도 컴포넌트로 대체됨)
|
||
"lib/widgets/shadcn/shad_date_picker.dart",
|
||
"lib/widgets/shadcn/shad_dialog.dart",
|
||
"lib/widgets/shadcn/shad_select.dart",
|
||
"lib/widgets/shadcn/shad_table.dart",
|
||
|
||
# Utils (안전함)
|
||
"lib/utils/address_constants.dart",
|
||
"lib/utils/equipment_display_helper.dart",
|
||
"lib/utils/formatters/business_number_formatter.dart",
|
||
"lib/utils/user_utils.dart",
|
||
|
||
# UseCase 통합 파일들 (개별 파일로 분리됨)
|
||
"lib/domain/usecases/auth/auth_usecases.dart",
|
||
"lib/domain/usecases/company/company_usecases.dart",
|
||
"lib/domain/usecases/equipment/equipment_usecases.dart",
|
||
"lib/domain/usecases/lookups/get_lookups_by_type.dart",
|
||
"lib/domain/usecases/lookups/initialize_lookups.dart",
|
||
"lib/domain/usecases/user/delete_user_usecase.dart",
|
||
"lib/domain/usecases/user/get_user_detail_usecase.dart",
|
||
"lib/domain/usecases/user/reset_password_usecase.dart",
|
||
"lib/domain/usecases/user/toggle_user_status_usecase.dart",
|
||
"lib/domain/usecases/user/update_user_usecase.dart",
|
||
"lib/domain/usecases/user/user_usecases.dart",
|
||
"lib/domain/usecases/warehouse_location/warehouse_location_usecases.dart",
|
||
]
|
||
|
||
# 검토가 필요한 파일들
|
||
self.needs_review = [
|
||
# Core utils (사용될 수 있음)
|
||
"lib/core/utils/formatters.dart",
|
||
"lib/core/utils/login_diagnostics.dart",
|
||
"lib/core/utils/validators.dart",
|
||
|
||
# Migration 관련 (일회성이지만 중요)
|
||
"lib/core/migrations/execute_migration.dart",
|
||
"lib/core/migrations/license_to_maintenance_migration.dart",
|
||
"lib/core/migrations/maintenance_data_validator.dart",
|
||
|
||
# Theme (사용될 수 있음)
|
||
"lib/core/theme/shadcn_theme.dart",
|
||
]
|
||
|
||
def check_git_status(self) -> List[str]:
|
||
"""Git에서 삭제된 dart 파일들 확인"""
|
||
try:
|
||
result = subprocess.run(['git', 'status', '--porcelain'],
|
||
cwd=self.project_root,
|
||
capture_output=True,
|
||
text=True)
|
||
git_status = result.stdout
|
||
|
||
deleted_files = []
|
||
for line in git_status.split('\n'):
|
||
if line.startswith('D ') and line.endswith('.dart'):
|
||
deleted_files.append(line[3:]) # Remove 'D ' prefix
|
||
|
||
return deleted_files
|
||
except Exception as e:
|
||
print(f"Git status 확인 실패: {e}")
|
||
return []
|
||
|
||
def verify_file_usage(self, file_path: str) -> Tuple[bool, List[str]]:
|
||
"""파일이 실제로 사용되는지 grep으로 확인"""
|
||
path_obj = Path(file_path)
|
||
if not path_obj.exists():
|
||
return False, ["파일이 존재하지 않음"]
|
||
|
||
# 파일명에서 클래스/타입 이름 추출
|
||
filename = path_obj.stem
|
||
possible_names = [
|
||
filename,
|
||
''.join(word.capitalize() for word in filename.split('_')),
|
||
filename.replace('_', ''),
|
||
]
|
||
|
||
references = []
|
||
try:
|
||
for name in possible_names:
|
||
result = subprocess.run(
|
||
['grep', '-r', '-l', name, 'lib/'],
|
||
cwd=self.project_root,
|
||
capture_output=True,
|
||
text=True
|
||
)
|
||
if result.returncode == 0:
|
||
found_files = result.stdout.strip().split('\n')
|
||
# 자기 자신과 생성 파일 제외
|
||
filtered_files = [f for f in found_files
|
||
if f != file_path and
|
||
not f.endswith('.g.dart') and
|
||
not f.endswith('.freezed.dart')]
|
||
references.extend(filtered_files)
|
||
except Exception:
|
||
pass
|
||
|
||
return len(references) > 0, list(set(references))
|
||
|
||
def generate_final_report(self) -> None:
|
||
"""최종 삭제 권장 보고서 생성"""
|
||
print("\n" + "="*80)
|
||
print("FLUTTER 프로젝트 사용되지 않는 파일 최종 분석 보고서")
|
||
print("="*80)
|
||
print(f"📂 프로젝트: {self.project_root}")
|
||
print(f"📅 분석 일시: 2025-08-29 (Phase 10 완료 후)")
|
||
|
||
# Git 상태 확인
|
||
deleted_files = self.check_git_status()
|
||
|
||
print(f"\n📋 Git Status:")
|
||
if deleted_files:
|
||
print(f" 🗑️ 이미 삭제된 파일: {len(deleted_files)}개")
|
||
for deleted in deleted_files[:5]:
|
||
print(f" - {deleted}")
|
||
if len(deleted_files) > 5:
|
||
print(f" ... 및 {len(deleted_files) - 5}개 더")
|
||
else:
|
||
print(" ✅ 삭제된 dart 파일 없음")
|
||
|
||
print(f"\n🎯 삭제 권장 파일 분석:")
|
||
print(f" ✅ 안전 삭제 가능: {len(self.definitely_unused)}개")
|
||
print(f" ⚠️ 검토 후 삭제: {len(self.needs_review)}개")
|
||
|
||
# 카테고리별 안전 삭제 가능 파일들
|
||
categories = {
|
||
'Equipment Models (백엔드 스키마 변경)': [f for f in self.definitely_unused if 'equipment/' in f or 'equipment_' in f],
|
||
'Administrator (UI 미연결)': [f for f in self.definitely_unused if 'administrator' in f],
|
||
'Deprecated UI Components': [f for f in self.definitely_unused if 'widgets/' in f or 'custom_widgets/' in f],
|
||
'Company Widgets (단순화됨)': [f for f in self.definitely_unused if 'company/widgets/' in f],
|
||
'Equipment Widgets (Phase 4 단순화)': [f for f in self.definitely_unused if 'equipment/widgets/' in f],
|
||
'Shadcn Components (대체됨)': [f for f in self.definitely_unused if 'widgets/shadcn/' in f],
|
||
'UseCase 통합 파일들': [f for f in self.definitely_unused if 'usecases/' in f and ('_usecases.dart' in f or 'lookups/' in f)],
|
||
'기타 Utils 및 Services': [f for f in self.definitely_unused if f not in sum([
|
||
[f for f in self.definitely_unused if 'equipment/' in f or 'equipment_' in f],
|
||
[f for f in self.definitely_unused if 'administrator' in f],
|
||
[f for f in self.definitely_unused if 'widgets/' in f or 'custom_widgets/' in f],
|
||
[f for f in self.definitely_unused if 'company/widgets/' in f],
|
||
[f for f in self.definitely_unused if 'equipment/widgets/' in f],
|
||
[f for f in self.definitely_unused if 'widgets/shadcn/' in f],
|
||
[f for f in self.definitely_unused if 'usecases/' in f and ('_usecases.dart' in f or 'lookups/' in f)],
|
||
], [])],
|
||
}
|
||
|
||
print(f"\n📂 카테고리별 안전 삭제 가능 파일:")
|
||
total_safe = 0
|
||
for category, files in categories.items():
|
||
if files:
|
||
print(f"\n📁 {category} ({len(files)}개):")
|
||
for file_path in sorted(files)[:5]: # 처음 5개만 표시
|
||
print(f" ✅ {file_path}")
|
||
if len(files) > 5:
|
||
print(f" ... 및 {len(files) - 5}개 더")
|
||
total_safe += len(files)
|
||
|
||
print(f"\n⚠️ 검토 필요 파일들 ({len(self.needs_review)}개):")
|
||
for file_path in self.needs_review:
|
||
print(f" ⚠️ {file_path}")
|
||
if 'formatters.dart' in file_path:
|
||
print(f" → 유틸리티 함수들이 다른 곳에서 사용될 수 있음")
|
||
elif 'migration' in file_path:
|
||
print(f" → 마이그레이션 완료 후 삭제 가능")
|
||
elif 'theme' in file_path:
|
||
print(f" → 테마 정의 파일, 실제 사용 여부 확인 필요")
|
||
|
||
print(f"\n🚀 즉시 실행 가능한 삭제 명령어:")
|
||
print(f"# 1단계: 안전 삭제 가능 파일들 (백업 권장)")
|
||
|
||
# 10개씩 나누어 삭제 명령 생성
|
||
chunks = [self.definitely_unused[i:i+10] for i in range(0, len(self.definitely_unused), 10)]
|
||
for i, chunk in enumerate(chunks, 1):
|
||
print(f"\n# 1단계-{i}: Equipment/UI 관련 파일들 ({len(chunk)}개)")
|
||
files_str = ' \\\n '.join(f'"{f}"' for f in chunk)
|
||
print(f"rm {files_str}")
|
||
|
||
print(f"\n# 2단계: 생성 파일들 정리 (자동 재생성됨)")
|
||
print(f"flutter packages pub run build_runner clean")
|
||
print(f"flutter packages pub run build_runner build --delete-conflicting-outputs")
|
||
|
||
print(f"\n# 3단계: 분석 재실행으로 효과 확인")
|
||
print(f"flutter analyze")
|
||
|
||
print(f"\n💡 삭제 후 예상 효과:")
|
||
print(f" 📊 파일 수: 290개 → {290 - len(self.definitely_unused)}개 (약 {len(self.definitely_unused)}개 감소)")
|
||
print(f" 🧹 코드 정리: 사용되지 않는 Equipment 모델, UI 컴포넌트, UseCase 파일 제거")
|
||
print(f" 🚀 빌드 성능: 불필요한 파일 컴파일 시간 단축")
|
||
print(f" 📦 앱 크기: 미사용 코드 제거로 번들 크기 최적화")
|
||
|
||
print(f"\n⚠️ 중요 주의사항:")
|
||
print(f" 1. 삭제 전 git commit으로 백업 생성 필수")
|
||
print(f" 2. 삭제 후 flutter analyze와 flutter test 실행")
|
||
print(f" 3. 문제 발생 시 git reset --hard HEAD~1로 복원")
|
||
print(f" 4. Phase 10 완료 상태에서 안전한 파일들만 선별됨")
|
||
|
||
print(f"\n🎯 권장 삭제 순서:")
|
||
print(f" 1️⃣ Equipment 관련 모델 파일 (8개) - 백엔드 스키마 변경으로 확실히 미사용")
|
||
print(f" 2️⃣ UI 위젯 컴포넌트 (20+ 개) - Phase 4-7에서 단순화로 미사용 확인")
|
||
print(f" 3️⃣ UseCase 통합 파일 (10개) - 개별 파일로 분리되어 미사용")
|
||
print(f" 4️⃣ 기타 Utils 및 Services (나머지) - import 없음 확인됨")
|
||
|
||
print("\n" + "="*80)
|
||
|
||
def main():
|
||
project_root = "/Users/maximilian.j.sul/Documents/flutter/superport"
|
||
analyzer = FinalDartFileAnalyzer(project_root)
|
||
analyzer.generate_final_report()
|
||
|
||
if __name__ == "__main__":
|
||
main() |