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()
기능
메타데이터 추출
- 각 마크다운 파일에서
title
과weight
메타데이터를 추출합니다. title
은 제목으로 사용되고,weight
는 TOC 내 항목의 정렬 기준으로 사용됩니다.
경로 포맷팅
- 파일 경로를 URL 형식으로 변환하며, 경로 내 공백을
-
로 대체합니다. 이는 URL의 일관성을 유지하는 데 도움이 됩니다.
TOC 생성
- 지정된 폴더 내의 모든 마크다운 파일과 폴더를 기반으로 TOC를 생성합니다.
- 파일과 폴더는
weight
에 따라 정렬되며, 이 순서대로 TOC에 추가됩니다.
_index.md
파일 업데이트
- 기존의
_index.md
파일에서 TOC를 제거하고 새로 생성한 TOC로 업데이트합니다. .
이나_
로 시작하는 폴더는 제외합니다.