mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
doc quality
This commit is contained in:
@@ -3,548 +3,365 @@
|
||||
##############################################################################
|
||||
# Documentation Quality Checker
|
||||
# Analyzes project documentation and provides a quality score (0-100%)
|
||||
#
|
||||
# Metrics evaluated:
|
||||
# - README files coverage
|
||||
# - Documentation structure
|
||||
# - Code comments coverage
|
||||
# - JSDoc/TypeDoc annotations
|
||||
# - Type annotations
|
||||
# - Example files
|
||||
# - Architecture documentation
|
||||
# - Security documentation
|
||||
# Usage: doc-quality-checker.sh [project-path] [verbose]
|
||||
##############################################################################
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Scoring weights
|
||||
declare -A WEIGHTS=(
|
||||
[readme_coverage]=15
|
||||
[docs_structure]=15
|
||||
[comments_coverage]=15
|
||||
[jsdoc_coverage]=15
|
||||
[type_annotations]=10
|
||||
[examples]=10
|
||||
[architecture_docs]=10
|
||||
[security_docs]=10
|
||||
)
|
||||
|
||||
PROJECT_ROOT="${1:-.}"
|
||||
VERBOSE="${2:-false}"
|
||||
|
||||
# Initialize metrics
|
||||
declare -A METRICS
|
||||
# Initialize metrics storage
|
||||
declare -A scores
|
||||
|
||||
##############################################################################
|
||||
# Utility Functions
|
||||
##############################################################################
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_metric() {
|
||||
local name=$1
|
||||
local score=$2
|
||||
local max=$3
|
||||
local percentage=0
|
||||
|
||||
if (( max > 0 )); then
|
||||
percentage=$((score * 100 / max))
|
||||
fi
|
||||
|
||||
if (( percentage >= 80 )); then
|
||||
color=$GREEN
|
||||
elif (( percentage >= 60 )); then
|
||||
color=$YELLOW
|
||||
else
|
||||
color=$RED
|
||||
fi
|
||||
|
||||
printf " %-30s %3d/%3d (${color}%3d%%${NC})\n" "$name:" "$score" "$max" "$percentage"
|
||||
}
|
||||
info() { echo "[*] $1"; }
|
||||
success() { echo "[+] $1"; }
|
||||
warn() { echo "[-] $1"; }
|
||||
|
||||
##############################################################################
|
||||
# README Coverage Check
|
||||
# Check README Coverage
|
||||
##############################################################################
|
||||
|
||||
check_readme_coverage() {
|
||||
check_readme() {
|
||||
info "Checking README coverage..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking README coverage..."
|
||||
|
||||
local readme_count=0
|
||||
local total_dirs=0
|
||||
|
||||
# Count directories with README files (sample scan)
|
||||
while IFS= read -r dir; do
|
||||
if [[ -d "$dir" ]]; then
|
||||
((total_dirs++))
|
||||
if [[ -f "$dir/README.md" || -f "$dir/README.txt" || -f "$dir/README" ]]; then
|
||||
((readme_count++))
|
||||
fi
|
||||
fi
|
||||
done < <(find "$PROJECT_ROOT" -maxdepth 2 -type d 2>/dev/null)
|
||||
|
||||
# Check main README
|
||||
if [[ -f "$PROJECT_ROOT/README.md" ]]; then
|
||||
((score += 30))
|
||||
((score += 40))
|
||||
[[ $VERBOSE == "true" ]] && echo " Main README found"
|
||||
fi
|
||||
|
||||
if [[ -f "$PROJECT_ROOT/docs/README.md" || -f "$PROJECT_ROOT/docs/INDEX.md" ]]; then
|
||||
((score += 20))
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " Docs index found"
|
||||
fi
|
||||
|
||||
# Score based on readme ratio
|
||||
if (( total_dirs > 1 )); then
|
||||
local ratio=$((readme_count * 100 / total_dirs))
|
||||
if (( ratio > 30 )); then
|
||||
((score += 30))
|
||||
elif (( ratio > 15 )); then
|
||||
((score += 15))
|
||||
else
|
||||
((score += 5))
|
||||
fi
|
||||
# Count READMEs in subdirectories
|
||||
local readme_dirs=$(find "$PROJECT_ROOT" -maxdepth 2 -name "README.md" 2>/dev/null | wc -l)
|
||||
if (( readme_dirs > 2 )); then
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " Multiple READMEs found: $readme_dirs"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " README files found: $readme_count/$total_dirs directories"
|
||||
|
||||
METRICS[readme_coverage]=$((score > max ? max : score))
|
||||
(( score > 100 )) && score=100
|
||||
scores[readme]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Documentation Structure Check
|
||||
# Check Documentation Structure
|
||||
##############################################################################
|
||||
|
||||
check_docs_structure() {
|
||||
info "Checking documentation structure..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking documentation structure..."
|
||||
|
||||
# Check for organized docs directory
|
||||
if [[ -d "$PROJECT_ROOT/docs" ]]; then
|
||||
((score += 30))
|
||||
((score += 35))
|
||||
[[ $VERBOSE == "true" ]] && echo " docs/ directory exists"
|
||||
|
||||
# Count subdirectories
|
||||
local subdirs=0
|
||||
while IFS= read -r -d '' dir; do
|
||||
[[ -d "$dir" ]] && ((subdirs++))
|
||||
done < <(find "$PROJECT_ROOT/docs" -maxdepth 1 -type d -print0 2>/dev/null)
|
||||
|
||||
if (( subdirs >= 3 )); then
|
||||
((score += 20))
|
||||
elif (( subdirs >= 1 )); then
|
||||
((score += 10))
|
||||
# Check for subdirectories
|
||||
local subdirs=$(find "$PROJECT_ROOT/docs" -maxdepth 1 -type d | wc -l)
|
||||
if (( subdirs > 2 )); then
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " Organized docs structure: $subdirs dirs"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Documentation subdirectories: $subdirs"
|
||||
fi
|
||||
|
||||
# Check for key documentation files
|
||||
local found_docs=0
|
||||
if [[ -f "$PROJECT_ROOT/docs/architecture" || -f "$PROJECT_ROOT/docs/architecture.md" || -d "$PROJECT_ROOT/docs/architecture" ]]; then
|
||||
((found_docs++))
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/docs/guide.md" || -d "$PROJECT_ROOT/docs/guides" ]]; then
|
||||
((found_docs++))
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/docs/index.md" || -f "$PROJECT_ROOT/docs/INDEX.md" ]]; then
|
||||
((found_docs++))
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/PRD.md" || -f "$PROJECT_ROOT/docs/PRD.md" ]]; then
|
||||
((found_docs++))
|
||||
# Check for key doc files
|
||||
if [[ -f "$PROJECT_ROOT/PRD.md" ]]; then
|
||||
((score += 15))
|
||||
[[ $VERBOSE == "true" ]] && echo " PRD.md found"
|
||||
fi
|
||||
|
||||
((score += found_docs * 10))
|
||||
if [[ -f "$PROJECT_ROOT/docs/ARCHITECTURE.md" || -f "$PROJECT_ROOT/docs/architecture.md" ]]; then
|
||||
((score += 20))
|
||||
[[ $VERBOSE == "true" ]] && echo " Architecture documentation found"
|
||||
fi
|
||||
|
||||
METRICS[docs_structure]=$((score > max ? max : score))
|
||||
(( score > 100 )) && score=100
|
||||
scores[structure]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Comments Coverage Check
|
||||
# Check Code Comments
|
||||
##############################################################################
|
||||
|
||||
check_comments_coverage() {
|
||||
check_comments() {
|
||||
info "Checking code comments coverage..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking code comments coverage..."
|
||||
local ts_files=$(find "$PROJECT_ROOT" -name "*.ts" -o -name "*.tsx" | grep -v node_modules | grep -v build | wc -l)
|
||||
|
||||
local total_files=0
|
||||
local commented_files=0
|
||||
local total_lines=0
|
||||
local comment_lines=0
|
||||
|
||||
# Sample scan of TypeScript/JavaScript files
|
||||
while IFS= read -r file; do
|
||||
if [[ -f "$file" ]]; then
|
||||
((total_files++))
|
||||
|
||||
local file_lines=0
|
||||
file_lines=$(wc -l < "$file" 2>/dev/null || echo 0)
|
||||
((total_lines += file_lines))
|
||||
|
||||
# Count comment lines
|
||||
local file_comments=0
|
||||
file_comments=$(grep -c '^\s*//' "$file" 2>/dev/null || true)
|
||||
file_comments=$((file_comments + $(grep -c '^\s*\*' "$file" 2>/dev/null || true)))
|
||||
|
||||
if (( file_comments > 0 )); then
|
||||
if (( ts_files > 0 )); then
|
||||
local commented_files=0
|
||||
while IFS= read -r file; do
|
||||
local comments=$(grep -c '^\s*//' "$file" 2>/dev/null || true)
|
||||
if (( comments > 0 )); then
|
||||
((commented_files++))
|
||||
((comment_lines += file_comments))
|
||||
fi
|
||||
fi
|
||||
done < <(find "$PROJECT_ROOT" \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -not -path "*/node_modules/*" -not -path "*/build/*" 2>/dev/null | head -80)
|
||||
|
||||
if (( total_files > 0 )); then
|
||||
local file_ratio=$((commented_files * 100 / total_files))
|
||||
score=$((file_ratio * 70 / 100))
|
||||
done < <(find "$PROJECT_ROOT" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -not -path "*/build/*" 2>/dev/null | head -50)
|
||||
|
||||
score=$((commented_files * 100 / ts_files))
|
||||
(( score > 100 )) && score=100
|
||||
[[ $VERBOSE == "true" ]] && echo " Commented files: $commented_files/$ts_files"
|
||||
fi
|
||||
|
||||
if (( total_lines > 0 )); then
|
||||
local comment_ratio=$((comment_lines * 100 / total_lines))
|
||||
score=$((score + comment_ratio * 30 / 100))
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Commented files: $commented_files/$total_files"
|
||||
[[ $VERBOSE == "true" ]] && echo " Comment density: $(( comment_lines * 100 / (total_lines + 1) ))%"
|
||||
|
||||
METRICS[comments_coverage]=$((score > max ? max : score))
|
||||
scores[comments]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# JSDoc/TypeDoc Coverage Check
|
||||
# Check JSDoc Coverage
|
||||
##############################################################################
|
||||
|
||||
check_jsdoc_coverage() {
|
||||
check_jsdoc() {
|
||||
info "Checking JSDoc coverage..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking JSDoc/TypeDoc coverage..."
|
||||
local jsdocs=$(find "$PROJECT_ROOT" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -not -path "*/build/*" 2>/dev/null | xargs grep -l '^\s*/\*\*' 2>/dev/null | wc -l)
|
||||
|
||||
local jsdoc_blocks=0
|
||||
local files_scanned=0
|
||||
|
||||
# Count JSDoc blocks in files
|
||||
while IFS= read -r file; do
|
||||
if [[ -f "$file" ]]; then
|
||||
((files_scanned++))
|
||||
local blocks=$(grep -c '^\s*/\*\*' "$file" 2>/dev/null || true)
|
||||
((jsdoc_blocks += blocks))
|
||||
fi
|
||||
done < <(find "$PROJECT_ROOT" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -not -path "*/build/*" 2>/dev/null | head -50)
|
||||
|
||||
if (( files_scanned > 0 )); then
|
||||
score=$((jsdoc_blocks * 100 / files_scanned))
|
||||
# Cap at 100%
|
||||
(( score > max )) && score=$max
|
||||
if (( jsdocs > 0 )); then
|
||||
score=$((jsdocs * 20))
|
||||
(( score > 100 )) && score=100
|
||||
[[ $VERBOSE == "true" ]] && echo " JSDoc blocks found: $jsdocs files"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " JSDoc blocks: $jsdoc_blocks in $files_scanned files"
|
||||
|
||||
METRICS[jsdoc_coverage]=$score
|
||||
scores[jsdoc]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Type Annotations Check
|
||||
# Check Type Annotations
|
||||
##############################################################################
|
||||
|
||||
check_type_annotations() {
|
||||
check_types() {
|
||||
info "Checking type annotations..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking type annotations..."
|
||||
local ts_files=$(find "$PROJECT_ROOT" -name "*.ts" -o -name "*.tsx" | grep -v node_modules | grep -v build | wc -l)
|
||||
|
||||
local well_typed=0
|
||||
local total_files=0
|
||||
|
||||
# Check TypeScript files for type annotations
|
||||
while IFS= read -r file; do
|
||||
if [[ -f "$file" ]]; then
|
||||
((total_files++))
|
||||
|
||||
# Look for type annotations
|
||||
local annotations=$(grep -c ':\s*[A-Z][a-zA-Z]*\|:\s*{' "$file" 2>/dev/null || true)
|
||||
|
||||
if (( annotations > 3 )); then
|
||||
((well_typed++))
|
||||
fi
|
||||
if [[ -f "$PROJECT_ROOT/tsconfig.json" ]]; then
|
||||
((score += 50))
|
||||
[[ $VERBOSE == "true" ]] && echo " tsconfig.json found"
|
||||
|
||||
# Check for strict mode
|
||||
if grep -q '"strict".*true' "$PROJECT_ROOT/tsconfig.json" 2>/dev/null; then
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " Strict mode enabled"
|
||||
fi
|
||||
done < <(find "$PROJECT_ROOT" \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -not -path "*/build/*" 2>/dev/null | head -50)
|
||||
|
||||
if (( total_files > 0 )); then
|
||||
score=$((well_typed * 100 / total_files))
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Well-typed files: $well_typed/$total_files"
|
||||
if (( ts_files > 10 )); then
|
||||
((score += 20))
|
||||
[[ $VERBOSE == "true" ]] && echo " Good number of TypeScript files: $ts_files"
|
||||
fi
|
||||
|
||||
METRICS[type_annotations]=$score
|
||||
(( score > 100 )) && score=100
|
||||
scores[types]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Examples Check
|
||||
# Check Examples
|
||||
##############################################################################
|
||||
|
||||
check_examples() {
|
||||
info "Checking examples..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking example files..."
|
||||
|
||||
# Look for example files
|
||||
local examples=$(find "$PROJECT_ROOT" \( -name "*example*" -o -name "*demo*" -o -name "*sample*" \) -type f 2>/dev/null | grep -v node_modules | wc -l)
|
||||
|
||||
if (( examples > 0 )); then
|
||||
if [[ -d "$PROJECT_ROOT/examples" ]]; then
|
||||
((score += 40))
|
||||
[[ $VERBOSE == "true" ]] && echo " examples/ directory found"
|
||||
fi
|
||||
|
||||
# Check for example/guide directories
|
||||
if [[ -d "$PROJECT_ROOT/docs/guides" || -d "$PROJECT_ROOT/examples" ]]; then
|
||||
if [[ -d "$PROJECT_ROOT/docs/guides" ]]; then
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " guides/ directory found"
|
||||
fi
|
||||
|
||||
# Check for test files
|
||||
local tests=$(find "$PROJECT_ROOT" \( -name "*.spec.*" -o -name "*.test.*" \) -type f 2>/dev/null | grep -v node_modules | wc -l)
|
||||
if (( tests > 5 )); then
|
||||
local test_files=$(find "$PROJECT_ROOT" \( -name "*.spec.*" -o -name "*.test.*" \) -not -path "*/node_modules/*" 2>/dev/null | wc -l)
|
||||
if (( test_files > 10 )); then
|
||||
((score += 30))
|
||||
[[ $VERBOSE == "true" ]] && echo " Test files as examples: $test_files"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Example files: $examples, Tests: $tests"
|
||||
|
||||
METRICS[examples]=$((score > max ? max : score))
|
||||
(( score > 100 )) && score=100
|
||||
scores[examples]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Architecture Documentation Check
|
||||
# Check Architecture Docs
|
||||
##############################################################################
|
||||
|
||||
check_architecture_docs() {
|
||||
check_architecture() {
|
||||
info "Checking architecture documentation..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking architecture documentation..."
|
||||
|
||||
local found_arch=0
|
||||
|
||||
# Check for architecture documentation
|
||||
for pattern in "ARCHITECTURE" "architecture" "DESIGN" "design" "implementation" "IMPLEMENTATION"; do
|
||||
if [[ -f "$PROJECT_ROOT/docs/${pattern}.md" || -d "$PROJECT_ROOT/docs/$pattern" || -f "$PROJECT_ROOT/$pattern.md" ]]; then
|
||||
((found_arch++))
|
||||
((score += 25))
|
||||
local arch_files=0
|
||||
for pattern in ARCHITECTURE architecture DESIGN design "implementation" "IMPLEMENTATION"; do
|
||||
if [[ -f "$PROJECT_ROOT/docs/${pattern}.md" || -f "$PROJECT_ROOT/$pattern.md" || -d "$PROJECT_ROOT/docs/$pattern" ]]; then
|
||||
((arch_files++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for design docs
|
||||
local design_files=$(find "$PROJECT_ROOT/docs" \( -iname "*design*" -o -iname "*architecture*" -o -iname "*implementation*" \) 2>/dev/null | wc -l)
|
||||
if (( design_files > 0 )); then
|
||||
((score += 25))
|
||||
if (( arch_files > 0 )); then
|
||||
score=$((arch_files * 25))
|
||||
(( score > 100 )) && score=100
|
||||
[[ $VERBOSE == "true" ]] && echo " Architecture documents: $arch_files found"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Architecture docs found: $found_arch"
|
||||
|
||||
METRICS[architecture_docs]=$((score > max ? max : score))
|
||||
scores[architecture]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Security Documentation Check
|
||||
# Check Security Docs
|
||||
##############################################################################
|
||||
|
||||
check_security_docs() {
|
||||
check_security() {
|
||||
info "Checking security documentation..."
|
||||
local score=0
|
||||
local max=100
|
||||
|
||||
log_info "Checking security documentation..."
|
||||
|
||||
# Check for security documentation
|
||||
if [[ -f "$PROJECT_ROOT/docs/SECURITY.md" || -f "$PROJECT_ROOT/SECURITY.md" || -f "$PROJECT_ROOT/security.md" ]]; then
|
||||
((score += 40))
|
||||
if [[ -f "$PROJECT_ROOT/docs/SECURITY.md" || -f "$PROJECT_ROOT/SECURITY.md" ]]; then
|
||||
((score += 50))
|
||||
[[ $VERBOSE == "true" ]] && echo " SECURITY.md found"
|
||||
fi
|
||||
|
||||
# Check for security mentions in docs
|
||||
local security_refs=$(grep -r "security\|authentication\|authorization\|encryption" "$PROJECT_ROOT/docs" 2>/dev/null | wc -l)
|
||||
if (( security_refs > 0 )); then
|
||||
((score += 30))
|
||||
if [[ -f "$PROJECT_ROOT/LICENSE" ]]; then
|
||||
((score += 25))
|
||||
[[ $VERBOSE == "true" ]] && echo " LICENSE file found"
|
||||
fi
|
||||
|
||||
# Check for LICENSE
|
||||
if [[ -f "$PROJECT_ROOT/LICENSE" || -f "$PROJECT_ROOT/LICENSE.md" ]]; then
|
||||
((score += 30))
|
||||
local security_refs=$(grep -r "security\|authentication\|authorization" "$PROJECT_ROOT/docs" 2>/dev/null | wc -l)
|
||||
if (( security_refs > 5 )); then
|
||||
((score += 25))
|
||||
[[ $VERBOSE == "true" ]] && echo " Security references: $security_refs"
|
||||
fi
|
||||
|
||||
[[ $VERBOSE == "true" ]] && echo " Security references: $security_refs"
|
||||
|
||||
METRICS[security_docs]=$((score > max ? max : score))
|
||||
(( score > 100 )) && score=100
|
||||
scores[security]=$score
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Calculate Overall Score
|
||||
##############################################################################
|
||||
|
||||
calculate_score() {
|
||||
local total_score=0
|
||||
local total_weight=0
|
||||
calculate_overall() {
|
||||
local total=0
|
||||
local count=0
|
||||
|
||||
log_info "Calculating overall score..."
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} Documentation Quality Metrics${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
|
||||
for metric in "${!WEIGHTS[@]}"; do
|
||||
local metric_score=${METRICS[$metric]:-0}
|
||||
local weight=${WEIGHTS[$metric]}
|
||||
local contribution=$((metric_score * weight / 100))
|
||||
|
||||
total_score=$((total_score + contribution))
|
||||
total_weight=$((total_weight + weight))
|
||||
|
||||
# Format metric name nicely
|
||||
local display_name=$(echo "$metric" | sed 's/_/ /g')
|
||||
print_metric "$display_name" "$metric_score" "100"
|
||||
for metric in "${!scores[@]}"; do
|
||||
((total += scores[$metric]))
|
||||
((count++))
|
||||
done
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
|
||||
if (( total_weight > 0 )); then
|
||||
total_score=$((total_score / total_weight))
|
||||
fi
|
||||
|
||||
printf " %-30s %3d%%\n" "Overall Quality Score:" "$total_score"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
|
||||
echo "$total_score"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Get Quality Rating
|
||||
##############################################################################
|
||||
|
||||
get_quality_rating() {
|
||||
local score=$1
|
||||
|
||||
if (( score >= 90 )); then
|
||||
echo "EXCELLENT ⭐⭐⭐⭐⭐"
|
||||
elif (( score >= 75 )); then
|
||||
echo "GOOD ⭐⭐⭐⭐"
|
||||
elif (( score >= 60 )); then
|
||||
echo "FAIR ⭐⭐⭐"
|
||||
elif (( score >= 45 )); then
|
||||
echo "NEEDS IMPROVEMENT ⭐⭐"
|
||||
if (( count > 0 )); then
|
||||
echo $((total / count))
|
||||
else
|
||||
echo "CRITICAL ⭐"
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Recommendations
|
||||
# Print Results
|
||||
##############################################################################
|
||||
|
||||
print_recommendations() {
|
||||
local score=$1
|
||||
print_results() {
|
||||
local overall=$1
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} Recommendations${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
echo "╔════════════════════════════════════════════╗"
|
||||
echo "║ Documentation Quality Assessment (0-100%) ║"
|
||||
echo "╚════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
if (( ${METRICS[readme_coverage]:-0} < 60 )); then
|
||||
log_warning "Add README files to key directories"
|
||||
echo "Metrics:"
|
||||
printf " %-25s %3d%%\n" "README Coverage" "${scores[readme]:-0}"
|
||||
printf " %-25s %3d%%\n" "Documentation Structure" "${scores[structure]:-0}"
|
||||
printf " %-25s %3d%%\n" "Code Comments" "${scores[comments]:-0}"
|
||||
printf " %-25s %3d%%\n" "JSDoc Coverage" "${scores[jsdoc]:-0}"
|
||||
printf " %-25s %3d%%\n" "Type Annotations" "${scores[types]:-0}"
|
||||
printf " %-25s %3d%%\n" "Examples/Guides" "${scores[examples]:-0}"
|
||||
printf " %-25s %3d%%\n" "Architecture Docs" "${scores[architecture]:-0}"
|
||||
printf " %-25s %3d%%\n" "Security Docs" "${scores[security]:-0}"
|
||||
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────"
|
||||
printf " %-25s %3d%%\n" "OVERALL SCORE" "$overall"
|
||||
echo "────────────────────────────────────────────"
|
||||
|
||||
# Quality Rating
|
||||
echo ""
|
||||
if (( overall >= 90 )); then
|
||||
echo "Quality Rating: EXCELLENT (90-100%)"
|
||||
elif (( overall >= 75 )); then
|
||||
echo "Quality Rating: GOOD (75-89%)"
|
||||
elif (( overall >= 60 )); then
|
||||
echo "Quality Rating: FAIR (60-74%)"
|
||||
elif (( overall >= 45 )); then
|
||||
echo "Quality Rating: NEEDS IMPROVEMENT (45-59%)"
|
||||
else
|
||||
echo "Quality Rating: CRITICAL (<45%)"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[docs_structure]:-0} < 60 )); then
|
||||
log_warning "Organize documentation in docs/ directory with subdirectories"
|
||||
# Recommendations
|
||||
echo ""
|
||||
echo "Recommendations:"
|
||||
|
||||
if (( ${scores[readme]:-0} < 60 )); then
|
||||
echo " - Add comprehensive README files to key directories"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[comments_coverage]:-0} < 60 )); then
|
||||
log_warning "Increase inline code comments (aim for 5-10% comment density)"
|
||||
if (( ${scores[structure]:-0} < 60 )); then
|
||||
echo " - Organize documentation in /docs with subdirectories"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[jsdoc_coverage]:-0} < 60 )); then
|
||||
log_warning "Add JSDoc comments to exported functions and interfaces"
|
||||
if (( ${scores[comments]:-0} < 60 )); then
|
||||
echo " - Increase inline code comments (5-10% density recommended)"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[type_annotations]:-0} < 70 )); then
|
||||
log_warning "Improve type annotations - avoid implicit 'any' types"
|
||||
if (( ${scores[jsdoc]:-0} < 60 )); then
|
||||
echo " - Add JSDoc comments to exported functions/interfaces"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[examples]:-0} < 50 )); then
|
||||
log_warning "Create example files and demo code for key features"
|
||||
if (( ${scores[types]:-0} < 60 )); then
|
||||
echo " - Enable TypeScript strict mode and add type annotations"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[architecture_docs]:-0} < 50 )); then
|
||||
log_warning "Document system architecture, design decisions, and diagrams"
|
||||
if (( ${scores[examples]:-0} < 50 )); then
|
||||
echo " - Create examples/ directory with demo code"
|
||||
fi
|
||||
|
||||
if (( ${METRICS[security_docs]:-0} < 50 )); then
|
||||
log_warning "Create security documentation, best practices, and policies"
|
||||
if (( ${scores[architecture]:-0} < 50 )); then
|
||||
echo " - Write architecture documentation and design docs"
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════${NC}"
|
||||
if (( ${scores[security]:-0} < 50 )); then
|
||||
echo " - Add SECURITY.md and document best practices"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Main Execution
|
||||
# Main
|
||||
##############################################################################
|
||||
|
||||
main() {
|
||||
echo ""
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Documentation Quality Checker ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
|
||||
info "Documentation Quality Checker"
|
||||
info "Analyzing: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
log_info "Analyzing project: $PROJECT_ROOT"
|
||||
echo ""
|
||||
|
||||
# Run all checks
|
||||
check_readme_coverage
|
||||
check_readme
|
||||
check_docs_structure
|
||||
check_comments_coverage
|
||||
check_jsdoc_coverage
|
||||
check_type_annotations
|
||||
check_comments
|
||||
check_jsdoc
|
||||
check_types
|
||||
check_examples
|
||||
check_architecture_docs
|
||||
check_security_docs
|
||||
check_architecture
|
||||
check_security
|
||||
|
||||
echo ""
|
||||
|
||||
# Calculate and display score
|
||||
local final_score
|
||||
final_score=$(calculate_score)
|
||||
|
||||
echo ""
|
||||
echo -e "Quality Rating: $(get_quality_rating $final_score)"
|
||||
echo ""
|
||||
|
||||
# Print recommendations
|
||||
print_recommendations $final_score
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Usage:${NC} doc-quality-checker.sh [path] [verbose]"
|
||||
echo -e " path: Project root directory (default: current directory)"
|
||||
echo -e " verbose: Set to 'true' for detailed output"
|
||||
echo ""
|
||||
local overall=$(calculate_overall)
|
||||
print_results "$overall"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user