117 lines
3.8 KiB
Bash
Executable File
117 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# projdump.sh - dump project structure and source code into a single Markdown file
|
||
|
||
set -euo pipefail
|
||
|
||
usage() {
|
||
cat >&2 <<EOF
|
||
Использование: $0 <директория>
|
||
|
||
Создаёт один файл:
|
||
<basename>-dump.md — дерево проекта + содержимое файлов в Markdown
|
||
|
||
Игнорирует типичные артефакты сборки, кэши, IDE-файлы и зависимости.
|
||
|
||
Параметры:
|
||
-h, --help Показать эту справку
|
||
EOF
|
||
}
|
||
|
||
if [[ $# -eq 0 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
|
||
usage
|
||
exit 0
|
||
fi
|
||
|
||
TARGET_DIR="$1"
|
||
|
||
if [[ ! -d "$TARGET_DIR" ]]; then
|
||
echo "Ошибка: директория '$TARGET_DIR' не существует." >&2
|
||
exit 1
|
||
fi
|
||
|
||
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
|
||
BASENAME=$(basename "$TARGET_DIR")
|
||
|
||
if ! command -v tree >/dev/null 2>&1; then
|
||
echo "Ошибка: требуется утилита 'tree'. Установите её (например, apt install tree)." >&2
|
||
exit 1
|
||
fi
|
||
|
||
IGNORE_LIST=(
|
||
"third_party" "3rd_party" "3rdparty" ".git" ".svn" ".hg" "node_modules"
|
||
"__pycache__" "*.pyc" ".venv" "venv" "env" ".env" ".idea" ".vscode" ".vs"
|
||
".DS_Store" "Thumbs.db" "dist" "build" "out" "target" ".pytest_cache"
|
||
".mypy_cache" ".next" ".nuxt" ".output" ".svelte-kit" "coverage" ".cache"
|
||
".parcel-cache" ".eslintcache" ".yarn" "yarn-error.log" "package-lock.json"
|
||
"yarn.lock" "pnpm-lock.yaml" "Cargo.lock" "go.sum" ".gradle" "gradle"
|
||
".mvn" "mvnw" "mvnw.cmd" "*.swp" "*.swo" ".stack-work" "_build" "deps"
|
||
".cargo" ".rustup" ".clangd" ".ccls-cache" "CMakeFiles" "CMakeCache.txt"
|
||
"CTestTestfile.cmake" "cmake_install.cmake" "*.dSYM" "*.ilk" "*.pdb"
|
||
"*.obj" "*.o" "*.so" "*.dylib" "*.dll" "*.exe" "*.bin" "*.hex" "*.elf"
|
||
"tags" "TAGS" "GPATH" "GTAGS" "GRTAGS" "GSYMS" ".github" ".gitlab"
|
||
".circleci" ".travis.yml" "Jenkinsfile" "Dockerfile*" "docker-compose*.yml"
|
||
"*.log" "logs" "tmp" "temp" ".terraform" ".tfstate" ".tfstate.backup" "*.tfvars"
|
||
)
|
||
|
||
# === Подготовка find-аргументов ===
|
||
FIND_IGNORE_ARGS=()
|
||
for pat in "${IGNORE_LIST[@]}"; do
|
||
if [[ "$pat" == */* ]]; then
|
||
FIND_IGNORE_ARGS+=(-o -path "*/$pat")
|
||
else
|
||
FIND_IGNORE_ARGS+=(-o -name "$pat")
|
||
fi
|
||
done
|
||
|
||
FIND_CMD=(find "$TARGET_DIR" \( -false "${FIND_IGNORE_ARGS[@]}" \) -prune -o -type f -print0)
|
||
|
||
# === Подготовка шаблонов для tree ===
|
||
TREE_IGNORE_PATTERNS=""
|
||
for pat in "${IGNORE_LIST[@]}"; do
|
||
base_pat="${pat%%/*}"
|
||
if [[ -z "$TREE_IGNORE_PATTERNS" ]]; then
|
||
TREE_IGNORE_PATTERNS="$base_pat"
|
||
else
|
||
TREE_IGNORE_PATTERNS="$TREE_IGNORE_PATTERNS|$base_pat"
|
||
fi
|
||
done
|
||
|
||
OUTPUT_FILE="${BASENAME}-dump.md"
|
||
|
||
{
|
||
echo "# PROJECT: $BASENAME"
|
||
echo "Generated on: $(date)"
|
||
echo -e "\n## PROJECT STRUCTURE"
|
||
echo '```text'
|
||
tree -n -I "$TREE_IGNORE_PATTERNS" "$TARGET_DIR"
|
||
echo '```'
|
||
echo -e "\n---\n"
|
||
|
||
echo "## SOURCE CODE CONTENTS"
|
||
while IFS= read -r -d '' file; do
|
||
# Пропускаем явно бинарные файлы по расширению, если нужно
|
||
# Но пока просто оформляем контент
|
||
|
||
ext="${file##*.}"
|
||
# Маппинг для расширений без которых ИИ грустит
|
||
case "$ext" in
|
||
h|hpp|c|cpp) lang="cpp" ;;
|
||
sh) lang="bash" ;;
|
||
py) lang="python" ;;
|
||
md) lang="markdown" ;;
|
||
*) lang="$ext" ;;
|
||
esac
|
||
|
||
# Если расширения нет
|
||
if [[ "$ext" == "$file" ]]; then lang="text"; fi
|
||
|
||
echo "### File: $file"
|
||
echo " \`\`\`$lang"
|
||
cat "$file"
|
||
echo -e "\n \`\`\`"
|
||
echo -e "\n---\n"
|
||
done < <("${FIND_CMD[@]}")
|
||
} > "$OUTPUT_FILE"
|
||
|
||
echo "Готово: $OUTPUT_FILE"
|