TOC 생성

스크립트

  • update_toc.py
import os
import re
import yaml
import urllib.parse
# YAML 로드 함수
def load_yaml(content):
    return yaml.safe_load(content)
# YAML 덤프 함수
def dump_yaml(metadata):
    return yaml.dump(metadata, sort_keys=False, default_flow_style=False, allow_unicode=True)
# 파일의 프론트메타에서 타이틀과 weight를 추출하는 함수
def extract_metadata(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
            metadata_match = re.match(r"---(.*?)---", content, re.DOTALL)
            if metadata_match:
                metadata = load_yaml(metadata_match.group(1))
                title = metadata.get('title', os.path.splitext(os.path.basename(file_path))[0])
                weight = metadata.get('weight', float('inf'))  # weight가 없으면 무한대
                return title, weight
    except Exception as e:
        print(f"Error extracting metadata from {file_path}: {e}")
    return os.path.splitext(os.path.basename(file_path))[0], float('inf')
# 경로를 URL 형식으로 인코딩하면서 공백을 '-'로 대체하는 함수
def format_url_path(path):
    # 경로 구분자를 '/'로 변환하고 URL 인코딩 수행
    return urllib.parse.quote(path.replace(os.sep, '/')).replace('%20', '-')
# TOC 생성 함수
def generate_toc(folder_path, base_folder_path=None, level=0):
    if base_folder_path is None:
        base_folder_path = folder_path
    toc = []
    indent = "  " * level
    items = []
    # 파일과 디렉토리를 별도로 수집
    for item in sorted(os.listdir(folder_path)):
        item_path = os.path.join(folder_path, item)
        
        if item.startswith('.') or item.startswith('_'):
            continue
        
        if os.path.isdir(item_path):
            # 디렉토리 내 _index.md 파일에서 메타데이터 추출
            index_file_path = os.path.join(item_path, '_index.md')
            if os.path.exists(index_file_path):
                title, weight = extract_metadata(index_file_path)
            else:
                title, weight = item, float('inf')  # 기본 타이틀과 weight 설정
            items.append((title, item_path, weight))
        elif item.endswith('.md') and item != "_index.md":
            title, weight = extract_metadata(item_path)
            items.append((title, item_path, weight))
    
    # weight 순으로 정렬
    items.sort(key=lambda x: (x[2] if x[2] is not None else float('inf'), x[0]))
    for title, item_path, _ in items:
        relative_path = os.path.relpath(item_path, base_folder_path)
        relative_path = format_url_path(relative_path)  # 공백을 '-'로 대체
        
        if os.path.isdir(item_path):
            toc.append(f"{indent}- [{title}]({relative_path}/)")
            toc.append(generate_toc(item_path, base_folder_path, level + 1))
        else:
            relative_path = os.path.splitext(relative_path)[0]  # .md 확장자 제거
            toc.append(f"{indent}- [{title}]({relative_path})")
    
    return "\n".join(toc)
# _index.md 파일 업데이트 함수
def update_index_file(index_file_path, folder_path):
    try:
        with open(index_file_path, 'r', encoding='utf-8') as f:
            original_content = f.read()
        # 기존 TOC 제거 (## Table of Contents 시작 후 --- 또는 다른 헤딩(##) 또는 파일 끝까지 제거)
        stripped_content = re.sub(r"## Table of Contents\n\n(?:- .*\n)*?(?=---|\Z|##)", "", original_content, flags=re.DOTALL).strip()
        # TOC를 생성하고 기존 내용에 덧붙임
        toc = generate_toc(folder_path)
        new_content = stripped_content.strip() + f"\n\n## Table of Contents\n\n{toc}\n---"
        
        # TOC를 항상 업데이트
        with open(index_file_path, 'w', encoding='utf-8') as f:
            f.write(new_content)
        print(f"Updated _index.md in {folder_path}")
        
    except Exception as e:
        print(f"Error updating {index_file_path}: {e}")
# 특정 디렉토리 내의 모든 _index.md 파일 검색 및 업데이트
def update_all_index_files(directory):
    for root, dirs, files in os.walk(directory):
        dirs[:] = [d for d in dirs if not d.startswith('.') and not d.startswith('_')]  # .과 _으로 시작하는 디렉토리 제외
        for file_name in files:
            if file_name == "_index.md":
                index_file_path = os.path.join(root, file_name)
                update_index_file(index_file_path, root)
source_dir = os.environ.get('SOURCE_DIR', os.path.join(os.getcwd(), 'content'))
print("Update_TOC start")
update_all_index_files(source_dir)
print("Update_TOC end")
print()

기능

메타데이터 추출

  • 각 마크다운 파일에서 titleweight 메타데이터를 추출합니다.
  • title은 제목으로 사용되고, weight는 TOC 내 항목의 정렬 기준으로 사용됩니다.

경로 포맷팅

  • 파일 경로를 URL 형식으로 변환하며, 경로 내 공백을 -로 대체합니다. 이는 URL의 일관성을 유지하는 데 도움이 됩니다.

TOC 생성

  • 지정된 폴더 내의 모든 마크다운 파일과 폴더를 기반으로 TOC를 생성합니다.
  • 파일과 폴더는 weight에 따라 정렬되며, 이 순서대로 TOC에 추가됩니다.

_index.md 파일 업데이트

  • 기존의 _index.md 파일에서 TOC를 제거하고 새로 생성한 TOC로 업데이트합니다.
  • .이나 _ 로 시작하는 폴더는 제외합니다.