705 lines
26 KiB
Bash
705 lines
26 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
|
||
|
|
# Build script for localized content
|
||
|
|
# This script converts markdown files to HTML and deploys them to localized public directories
|
||
|
|
|
||
|
|
set -e
|
||
|
|
|
||
|
|
# Configuration - Use environment variable or fallback to default
|
||
|
|
CONTENT_DIR="${SITE_CONTENT_PATH:-site/content}"
|
||
|
|
PUBLIC_DIR="public"
|
||
|
|
|
||
|
|
# Supported languages
|
||
|
|
LANGUAGES=("en" "es")
|
||
|
|
|
||
|
|
# Function to read content types from content-kinds.toml
|
||
|
|
get_enabled_content_types() {
|
||
|
|
local content_kinds_file="${CONTENT_DIR}/content-kinds.toml"
|
||
|
|
local enabled_types=()
|
||
|
|
|
||
|
|
if [ ! -f "$content_kinds_file" ]; then
|
||
|
|
echo "⚠️ Warning: content-kinds.toml not found at $content_kinds_file, using default types" >&2
|
||
|
|
echo "blog recipes"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
# More robust TOML parsing - process line by line and track state
|
||
|
|
local in_content_kinds_section=false
|
||
|
|
local current_name=""
|
||
|
|
local current_directory=""
|
||
|
|
local current_enabled=""
|
||
|
|
|
||
|
|
while IFS= read -r line; do
|
||
|
|
# Skip comments and empty lines
|
||
|
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||
|
|
[[ -z "${line// }" ]] && continue
|
||
|
|
|
||
|
|
# Check for new content_kinds section
|
||
|
|
if [[ "$line" =~ ^\[\[content_kinds\]\] ]]; then
|
||
|
|
# Process previous section if we have one
|
||
|
|
if [[ "$in_content_kinds_section" == true && "$current_enabled" == "true" && -n "$current_directory" ]]; then
|
||
|
|
if [ -d "${CONTENT_DIR}/${current_directory}" ]; then
|
||
|
|
enabled_types+=("$current_directory")
|
||
|
|
echo "✅ Content type '$current_name' -> directory '${current_directory}' exists" >&2
|
||
|
|
else
|
||
|
|
echo "⚠️ WARNING: Content type '$current_name' enabled in content-kinds.toml but directory '${CONTENT_DIR}/${current_directory}' does not exist - SKIPPING" >&2
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Reset for new section
|
||
|
|
in_content_kinds_section=true
|
||
|
|
current_name=""
|
||
|
|
current_directory=""
|
||
|
|
current_enabled=""
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
# If we're in a content_kinds section, parse the fields
|
||
|
|
if [[ "$in_content_kinds_section" == true ]]; then
|
||
|
|
# Check for another section starting (end current one)
|
||
|
|
if [[ "$line" =~ ^\[\[ ]]; then
|
||
|
|
# Process current section before moving to next
|
||
|
|
if [[ "$current_enabled" == "true" && -n "$current_directory" ]]; then
|
||
|
|
if [ -d "${CONTENT_DIR}/${current_directory}" ]; then
|
||
|
|
enabled_types+=("$current_directory")
|
||
|
|
echo "✅ Content type '$current_name' -> directory '${current_directory}' exists" >&2
|
||
|
|
else
|
||
|
|
echo "⚠️ WARNING: Content type '$current_name' enabled in content-kinds.toml but directory '${CONTENT_DIR}/${current_directory}' does not exist - SKIPPING" >&2
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
in_content_kinds_section=false
|
||
|
|
continue
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Extract fields
|
||
|
|
if [[ "$line" =~ ^name[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then
|
||
|
|
current_name="${BASH_REMATCH[1]}"
|
||
|
|
elif [[ "$line" =~ ^directory[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then
|
||
|
|
current_directory="${BASH_REMATCH[1]}"
|
||
|
|
elif [[ "$line" =~ ^enabled[[:space:]]*=[[:space:]]*true ]]; then
|
||
|
|
current_enabled="true"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
done < "$content_kinds_file"
|
||
|
|
|
||
|
|
# Don't forget to process the last section
|
||
|
|
if [[ "$in_content_kinds_section" == true && "$current_enabled" == "true" && -n "$current_directory" ]]; then
|
||
|
|
if [ -d "${CONTENT_DIR}/${current_directory}" ]; then
|
||
|
|
enabled_types+=("$current_directory")
|
||
|
|
echo "✅ Content type '$current_name' -> directory '${current_directory}' exists" >&2
|
||
|
|
else
|
||
|
|
echo "⚠️ WARNING: Content type '$current_name' enabled in content-kinds.toml but directory '${CONTENT_DIR}/${current_directory}' does not exist - SKIPPING" >&2
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
|
||
|
|
if [ ${#enabled_types[@]} -eq 0 ]; then
|
||
|
|
echo "⚠️ Warning: No enabled content types found, using defaults" >&2
|
||
|
|
echo "blog recipes"
|
||
|
|
else
|
||
|
|
echo "${enabled_types[@]}"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get the script directory and project root
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||
|
|
|
||
|
|
echo "🌍 Building localized markdown content to HTML..."
|
||
|
|
echo "📁 Using content root: $CONTENT_DIR"
|
||
|
|
|
||
|
|
# Get enabled content types from content-kinds.toml
|
||
|
|
CONTENT_TYPES=($(get_enabled_content_types))
|
||
|
|
echo "📋 Enabled content types: ${CONTENT_TYPES[*]}"
|
||
|
|
|
||
|
|
# Build the markdown converter with content-static feature
|
||
|
|
echo "🔧 Building markdown converter..."
|
||
|
|
cd "$PROJECT_ROOT"
|
||
|
|
cargo build --bin markdown_converter --features content-static --quiet
|
||
|
|
|
||
|
|
CONVERTER="./target/debug/markdown_converter"
|
||
|
|
|
||
|
|
# Check if converter was built successfully
|
||
|
|
if [ ! -f "$CONVERTER" ]; then
|
||
|
|
echo "❌ Error: Markdown converter not found. Build may have failed."
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Create public directories for all content types
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
mkdir -p "${PUBLIC_DIR}/${content_type}"
|
||
|
|
echo "📁 Created directory: ${PUBLIC_DIR}/${content_type}"
|
||
|
|
done
|
||
|
|
|
||
|
|
# Function to extract frontmatter from markdown file
|
||
|
|
extract_frontmatter() {
|
||
|
|
local input_file="$1"
|
||
|
|
|
||
|
|
# Extract YAML frontmatter between --- delimiters
|
||
|
|
if head -1 "$input_file" | grep -q "^---$"; then
|
||
|
|
awk '/^---$/{flag++; next} flag==1 && /^---$/{flag++} flag==1{print}' "$input_file"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to extract tags/categories from frontmatter
|
||
|
|
extract_metadata() {
|
||
|
|
local input_file="$1"
|
||
|
|
local metadata_type="$2" # "tags" or "categories"
|
||
|
|
|
||
|
|
local frontmatter=$(extract_frontmatter "$input_file")
|
||
|
|
|
||
|
|
if [ -n "$frontmatter" ]; then
|
||
|
|
# Extract array values (both ["item1", "item2"] and [item1, item2] formats)
|
||
|
|
echo "$frontmatter" | grep -E "^${metadata_type}:" | sed 's/.*: *\[//' | sed 's/\]//' | sed 's/"//g' | sed "s/'//g" | tr ',' '\n' | sed 's/^ *//' | sed 's/ *$//' | grep -v '^$'
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to extract single category from frontmatter
|
||
|
|
extract_single_category() {
|
||
|
|
local input_file="$1"
|
||
|
|
|
||
|
|
local frontmatter=$(extract_frontmatter "$input_file")
|
||
|
|
|
||
|
|
if [ -n "$frontmatter" ]; then
|
||
|
|
# Extract single category value
|
||
|
|
echo "$frontmatter" | grep -E "^category:" | sed 's/category: *"*//' | sed 's/"*$//'
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to convert markdown to HTML
|
||
|
|
convert_markdown() {
|
||
|
|
local input_file="$1"
|
||
|
|
local output_file="$2"
|
||
|
|
local title="$3"
|
||
|
|
local lang="$4"
|
||
|
|
local content_type="$5"
|
||
|
|
|
||
|
|
echo " Converting: $(basename "$input_file") -> $(basename "$output_file")"
|
||
|
|
|
||
|
|
# Extract title from frontmatter if not provided
|
||
|
|
if [ -z "$title" ]; then
|
||
|
|
title=$(grep -m1 "^# " "$input_file" | sed 's/^# //' || echo "$(basename "$input_file" .md)")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Create output directory if it doesn't exist
|
||
|
|
mkdir -p "$(dirname "$output_file")"
|
||
|
|
|
||
|
|
# Convert markdown to HTML
|
||
|
|
$CONVERTER \
|
||
|
|
"$input_file" \
|
||
|
|
-o "$output_file" 2>/dev/null || {
|
||
|
|
echo " ⚠️ Warning: Failed to convert $input_file"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to generate category index.json
|
||
|
|
generate_category_index() {
|
||
|
|
local content_type="$1"
|
||
|
|
local lang="$2"
|
||
|
|
local category="$3"
|
||
|
|
local category_dir="$4"
|
||
|
|
local category_target="$5"
|
||
|
|
local content_source="$6"
|
||
|
|
local index_file="$category_target/index.json"
|
||
|
|
|
||
|
|
echo " 🔍 Generating category index: $category"
|
||
|
|
|
||
|
|
# Get emoji from global meta.toml file
|
||
|
|
local global_meta="${CONTENT_DIR}/${content_type}/meta.toml"
|
||
|
|
local root_emoji="📝"
|
||
|
|
|
||
|
|
if [ -f "$global_meta" ]; then
|
||
|
|
# Extract emoji from [[categories]] section using new global structure
|
||
|
|
root_emoji=$(awk -v cat="$category" '
|
||
|
|
/^\[\[categories\]\]/ { in_category = 1; current_id = ""; current_emoji = ""; next }
|
||
|
|
/^\[\[/ && !/^\[\[categories\]\]/ { in_category = 0; next }
|
||
|
|
in_category && /^id = / {
|
||
|
|
gsub(/^id = "/, "", $0);
|
||
|
|
gsub(/"$/, "", $0);
|
||
|
|
current_id = $0;
|
||
|
|
next
|
||
|
|
}
|
||
|
|
in_category && /^emoji = / {
|
||
|
|
gsub(/^emoji = "/, "", $0);
|
||
|
|
gsub(/"$/, "", $0);
|
||
|
|
current_emoji = $0;
|
||
|
|
next
|
||
|
|
}
|
||
|
|
# Check if we found a matching category
|
||
|
|
in_category && current_id == cat && current_emoji != "" {
|
||
|
|
print current_emoji;
|
||
|
|
exit
|
||
|
|
}
|
||
|
|
# Start of new section resets current values
|
||
|
|
/^\[/ { in_category = 0 }
|
||
|
|
' "$global_meta" || echo "📝")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Read category metadata from category meta.toml
|
||
|
|
local category_title="$category"
|
||
|
|
local category_emoji="$root_emoji" # Use root emoji as default
|
||
|
|
local category_css_class="category-$category"
|
||
|
|
local category_description=""
|
||
|
|
|
||
|
|
if [ -f "$category_dir/meta.toml" ]; then
|
||
|
|
category_title=$(grep '^title = ' "$category_dir/meta.toml" 2>/dev/null | sed 's/title = "//' | sed 's/"//' || echo "$category")
|
||
|
|
# Emoji comes from global meta.toml only (no override from category meta.toml)
|
||
|
|
category_css_class=$(grep '^css_class = ' "$category_dir/meta.toml" 2>/dev/null | sed 's/css_class = "//' | sed 's/"//' || echo "category-$category")
|
||
|
|
category_description=$(grep '^description = ' "$category_dir/meta.toml" 2>/dev/null | sed 's/description = "//' | sed 's/"//' || echo "")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Start JSON structure
|
||
|
|
cat > "$index_file" << EOF
|
||
|
|
{
|
||
|
|
"generated_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
||
|
|
"content_type": "$content_type",
|
||
|
|
"language": "$lang",
|
||
|
|
"category": "$category",
|
||
|
|
"title": "$category_title",
|
||
|
|
"emoji": "$category_emoji",
|
||
|
|
"css_class": "$category_css_class",
|
||
|
|
"description": "$category_description",
|
||
|
|
"posts": [
|
||
|
|
EOF
|
||
|
|
|
||
|
|
local first_post=true
|
||
|
|
|
||
|
|
# Process each markdown file in the category
|
||
|
|
for md_file in "$category_dir"/*.md; do
|
||
|
|
if [ -f "$md_file" ]; then
|
||
|
|
local filename=$(basename "$md_file" .md)
|
||
|
|
local post_id="$filename"
|
||
|
|
|
||
|
|
# Extract frontmatter data
|
||
|
|
local frontmatter_data=""
|
||
|
|
if head -1 "$md_file" | grep -q "^---$"; then
|
||
|
|
frontmatter_data=$(awk '/^---$/{flag++; next} flag==1 && /^---$/{flag++} flag==1{print}' "$md_file")
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Extract basic post info
|
||
|
|
local post_title=$(echo "$frontmatter_data" | grep '^title:' | sed 's/title:[[:space:]]*//' | tr -d '"' || echo "$filename")
|
||
|
|
local post_css_class=$(echo "$frontmatter_data" | grep '^css_class:' | sed 's/css_class:[[:space:]]*//' | tr -d '"' || echo "")
|
||
|
|
local published=$(echo "$frontmatter_data" | grep '^published:' | sed 's/published:[[:space:]]*//' | tr -d '"' || echo "true")
|
||
|
|
|
||
|
|
# Only include published posts
|
||
|
|
if [ "$published" = "true" ]; then
|
||
|
|
if [ "$first_post" = true ]; then
|
||
|
|
first_post=false
|
||
|
|
else
|
||
|
|
echo "," >> "$index_file"
|
||
|
|
fi
|
||
|
|
|
||
|
|
cat >> "$index_file" << EOF
|
||
|
|
{
|
||
|
|
"id": "$post_id",
|
||
|
|
"title": "$post_title",
|
||
|
|
"css_class": "$post_css_class",
|
||
|
|
"html_file": "$filename.html"
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Close JSON structure
|
||
|
|
cat >> "$index_file" << EOF
|
||
|
|
|
||
|
|
]
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
|
||
|
|
echo " ✅ Generated category index: $index_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to get emoji from TOML file
|
||
|
|
get_emoji_from_toml() {
|
||
|
|
local content_type="$1" # "blog" or "prescriptions"
|
||
|
|
local lang="$2"
|
||
|
|
local item_name="$3"
|
||
|
|
local emoji_file="$CONTENT_DIR/$content_type/$lang/categories.toml"
|
||
|
|
|
||
|
|
if [ -f "$emoji_file" ]; then
|
||
|
|
# Simple grep approach to find the emoji for the given item
|
||
|
|
local emoji
|
||
|
|
if [ "$content_type" = "blog" ]; then
|
||
|
|
# Look for the tag in the categories file
|
||
|
|
emoji=$(grep "^$item_name *= *" "$emoji_file" 2>/dev/null | sed 's/.*= *"\([^"]*\)".*/\1/' || echo "🏷️")
|
||
|
|
if [ "$emoji" = "🏷️" ]; then
|
||
|
|
# Try with quotes around the key
|
||
|
|
emoji=$(grep "^\"$item_name\" *= *" "$emoji_file" 2>/dev/null | sed 's/.*= *"\([^"]*\)".*/\1/' || echo "🏷️")
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
# Look for the category in the categories file
|
||
|
|
emoji=$(grep "^\"$item_name\" *= *" "$emoji_file" 2>/dev/null | sed 's/.*= *"\([^"]*\)".*/\1/' || echo "🏷️")
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -n "$emoji" ] && [ "$emoji" != "🏷️" ]; then
|
||
|
|
echo "$emoji"
|
||
|
|
else
|
||
|
|
echo "🏷️" # Default fallback emoji
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
echo "🏷️" # Default fallback emoji
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Function to generate index.json with categories/tags
|
||
|
|
generate_filter_index() {
|
||
|
|
local content_type="$1" # "blog" or "prescriptions"
|
||
|
|
local lang="$2"
|
||
|
|
local source_dir="$3"
|
||
|
|
local target_dir="$4"
|
||
|
|
|
||
|
|
echo " - Generating filter index for $content_type ($lang)..."
|
||
|
|
|
||
|
|
# Create temporary files for metadata collection
|
||
|
|
local temp_file="/tmp/filter_metadata_$$"
|
||
|
|
local count_file="/tmp/filter_counts_$$"
|
||
|
|
local total_files=0
|
||
|
|
|
||
|
|
# Clear temp files
|
||
|
|
> "$temp_file"
|
||
|
|
> "$count_file"
|
||
|
|
|
||
|
|
# Process each markdown file
|
||
|
|
for md_file in "$source_dir"/*.md; do
|
||
|
|
if [ -f "$md_file" ]; then
|
||
|
|
total_files=$((total_files + 1))
|
||
|
|
|
||
|
|
if [ "$content_type" = "blog" ]; then
|
||
|
|
# Extract tags for blog posts
|
||
|
|
extract_metadata "$md_file" "tags" >> "$temp_file"
|
||
|
|
else
|
||
|
|
# Extract categories for other content types (avoid duplicates from single category + categories array)
|
||
|
|
local file_categories="/tmp/file_categories_$$"
|
||
|
|
> "$file_categories"
|
||
|
|
|
||
|
|
# Extract single category
|
||
|
|
local single_category=$(extract_single_category "$md_file")
|
||
|
|
if [ -n "$single_category" ]; then
|
||
|
|
echo "$single_category" >> "$file_categories"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Extract tags array
|
||
|
|
extract_metadata "$md_file" "tags" >> "$file_categories"
|
||
|
|
|
||
|
|
# Add unique categories from this file to temp_file
|
||
|
|
if [ -s "$file_categories" ]; then
|
||
|
|
sort "$file_categories" | uniq >> "$temp_file"
|
||
|
|
fi
|
||
|
|
rm -f "$file_categories"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Count occurrences and sort
|
||
|
|
if [ -s "$temp_file" ]; then
|
||
|
|
sort "$temp_file" | uniq -c | sort -nr > "$count_file"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Generate the filter index JSON
|
||
|
|
local filter_index_file="$target_dir/filter-index.json"
|
||
|
|
|
||
|
|
if [ "$content_type" = "blog" ]; then
|
||
|
|
# Generate blog tags index
|
||
|
|
cat > "$filter_index_file" << EOF
|
||
|
|
{
|
||
|
|
"generated_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
||
|
|
"content_type": "blog",
|
||
|
|
"language": "$lang",
|
||
|
|
"total_posts": $total_files,
|
||
|
|
"tags": [
|
||
|
|
EOF
|
||
|
|
|
||
|
|
local first_tag=true
|
||
|
|
while IFS= read -r line; do
|
||
|
|
if [ -n "$line" ]; then
|
||
|
|
local count=$(echo "$line" | awk '{print $1}')
|
||
|
|
local tag=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^ *//')
|
||
|
|
|
||
|
|
if [ "$first_tag" = true ]; then
|
||
|
|
first_tag=false
|
||
|
|
else
|
||
|
|
echo "," >> "$filter_index_file"
|
||
|
|
fi
|
||
|
|
local emoji=$(get_emoji_from_toml "$content_type" "$lang" "$tag")
|
||
|
|
printf ' {"name": "%s", "count": %d, "emoji": "%s"}' "$tag" "$count" "$emoji" >> "$filter_index_file"
|
||
|
|
fi
|
||
|
|
done < "$count_file"
|
||
|
|
|
||
|
|
cat >> "$filter_index_file" << EOF
|
||
|
|
|
||
|
|
]
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
else
|
||
|
|
# Generate categories index for other content types
|
||
|
|
cat > "$filter_index_file" << EOF
|
||
|
|
{
|
||
|
|
"generated_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
||
|
|
"content_type": "$content_type",
|
||
|
|
"language": "$lang",
|
||
|
|
"total_${content_type}": $total_files,
|
||
|
|
"tags": [
|
||
|
|
EOF
|
||
|
|
|
||
|
|
local first_category=true
|
||
|
|
while IFS= read -r line; do
|
||
|
|
if [ -n "$line" ]; then
|
||
|
|
local count=$(echo "$line" | awk '{print $1}')
|
||
|
|
local category=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^ *//')
|
||
|
|
|
||
|
|
if [ "$first_category" = true ]; then
|
||
|
|
first_category=false
|
||
|
|
else
|
||
|
|
echo "," >> "$filter_index_file"
|
||
|
|
fi
|
||
|
|
local emoji=$(get_emoji_from_toml "$content_type" "$lang" "$category")
|
||
|
|
printf ' {"name": "%s", "count": %d, "emoji": "%s"}' "$category" "$count" "$emoji" >> "$filter_index_file"
|
||
|
|
fi
|
||
|
|
done < "$count_file"
|
||
|
|
|
||
|
|
cat >> "$filter_index_file" << EOF
|
||
|
|
|
||
|
|
]
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Clean up temp files
|
||
|
|
rm -f "$temp_file" "$count_file"
|
||
|
|
|
||
|
|
echo " ✅ Generated filter index: $filter_index_file"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Process content for each type and language
|
||
|
|
echo "📝 Processing content..."
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
echo "🔄 Processing content type: $content_type"
|
||
|
|
|
||
|
|
for lang in "${LANGUAGES[@]}"; do
|
||
|
|
content_source="${CONTENT_DIR}/${content_type}/${lang}"
|
||
|
|
content_target="${PUBLIC_DIR}/${content_type}/${lang}"
|
||
|
|
|
||
|
|
if [ -d "$content_source" ]; then
|
||
|
|
echo " - Processing $content_type content for language: $lang"
|
||
|
|
mkdir -p "$content_target"
|
||
|
|
|
||
|
|
# Convert all markdown files (including category subdirectories)
|
||
|
|
# First, handle any top-level markdown files (backward compatibility)
|
||
|
|
for md_file in "$content_source"/*.md; do
|
||
|
|
if [ -f "$md_file" ]; then
|
||
|
|
filename=$(basename "$md_file" .md)
|
||
|
|
# Use filename as base for HTML file (Rust converter will normalize internally)
|
||
|
|
html_file="$content_target/$filename.html"
|
||
|
|
|
||
|
|
# Extract title from filename (convert dashes to spaces and capitalize)
|
||
|
|
title=$(echo "$filename" | sed 's/-/ /g' | sed 's/\b\w/\U&/g')
|
||
|
|
|
||
|
|
convert_markdown "$md_file" "$html_file" "$title" "$lang" "$content_type"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Process category directories
|
||
|
|
for category_dir in "$content_source"/*/; do
|
||
|
|
if [ -d "$category_dir" ]; then
|
||
|
|
category=$(basename "$category_dir")
|
||
|
|
category_target="$content_target/$category"
|
||
|
|
mkdir -p "$category_target"
|
||
|
|
|
||
|
|
echo " 📁 Processing category: $category"
|
||
|
|
|
||
|
|
# Process category meta.toml if it exists
|
||
|
|
if [ -f "$category_dir/meta.toml" ]; then
|
||
|
|
echo " Processing category meta.toml for $category..."
|
||
|
|
$CONVERTER "$category_dir/meta.toml" -o /dev/null
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Convert all markdown files in the category
|
||
|
|
for md_file in "$category_dir"/*.md; do
|
||
|
|
if [ -f "$md_file" ]; then
|
||
|
|
filename=$(basename "$md_file" .md)
|
||
|
|
# Use filename as base for HTML file
|
||
|
|
html_file="$category_target/$filename.html"
|
||
|
|
|
||
|
|
# Extract title from filename
|
||
|
|
title=$(echo "$filename" | sed 's/-/ /g' | sed 's/\b\w/\U&/g')
|
||
|
|
|
||
|
|
convert_markdown "$md_file" "$html_file" "$title" "$lang" "$content_type"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Generate category index.json if there are posts
|
||
|
|
category_posts_count=$(find "$category_dir" -name "*.md" 2>/dev/null | wc -l)
|
||
|
|
if [ $category_posts_count -gt 0 ]; then
|
||
|
|
generate_category_index "$content_type" "$lang" "$category" "$category_dir" "$category_target" "$content_source"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Copy index.json if it exists
|
||
|
|
if [ -f "$content_source/index.json" ]; then
|
||
|
|
cp "$content_source/index.json" "$content_target/"
|
||
|
|
echo " ✅ ${content_type} index copied for $lang"
|
||
|
|
else
|
||
|
|
echo " ⚠️ Warning: No ${content_type} index.json found for $lang"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Process meta.toml to generate meta.json for API access
|
||
|
|
if [ -f "$content_source/meta.toml" ]; then
|
||
|
|
echo " Processing meta.toml for $content_type ($lang)..."
|
||
|
|
$CONVERTER "$content_source/meta.toml" -o /dev/null
|
||
|
|
else
|
||
|
|
echo " ⚠️ Warning: No ${content_type} meta.toml found for $lang"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Generate filter index (use content_type name for filter generation)
|
||
|
|
generate_filter_index "$content_type" "$lang" "$content_source" "$content_target"
|
||
|
|
|
||
|
|
# Count converted files
|
||
|
|
html_count=$(find "$content_target" -name "*.html" | wc -l)
|
||
|
|
echo " ✅ Converted $html_count ${content_type} items for $lang"
|
||
|
|
else
|
||
|
|
echo " ⚠️ Warning: No ${content_type} content directory found for $lang"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
done
|
||
|
|
|
||
|
|
# Create fallback symlinks for backward compatibility
|
||
|
|
echo "🔗 Creating backward compatibility links..."
|
||
|
|
|
||
|
|
# Create fallback index for each content type (default to English)
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
if [ -f "${PUBLIC_DIR}/${content_type}/en/index.json" ]; then
|
||
|
|
ln -sf "en/index.json" "${PUBLIC_DIR}/${content_type}/index.json" 2>/dev/null || true
|
||
|
|
echo " ✅ Created fallback ${content_type} index"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Validate content structure
|
||
|
|
echo "🔍 Validating content structure..."
|
||
|
|
|
||
|
|
validate_json() {
|
||
|
|
local file="$1"
|
||
|
|
local type="$2"
|
||
|
|
local lang="$3"
|
||
|
|
|
||
|
|
if command -v jq >/dev/null 2>&1; then
|
||
|
|
if jq empty "$file" 2>/dev/null; then
|
||
|
|
echo " ✅ Valid JSON: $type ($lang)"
|
||
|
|
else
|
||
|
|
echo " ❌ Invalid JSON: $type ($lang)"
|
||
|
|
return 1
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
echo " ⚠️ jq not available, skipping JSON validation"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
for lang in "${LANGUAGES[@]}"; do
|
||
|
|
# Validate index for each content type
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
content_index="${PUBLIC_DIR}/${content_type}/${lang}/index.json"
|
||
|
|
if [ -f "$content_index" ]; then
|
||
|
|
validate_json "$content_index" "$content_type" "$lang"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
done
|
||
|
|
|
||
|
|
# Generate content statistics
|
||
|
|
echo "📊 Content Statistics:"
|
||
|
|
|
||
|
|
# Initialize totals for each content type (using eval for better bash compatibility)
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
eval "total_${content_type//[^a-zA-Z0-9]/_}=0"
|
||
|
|
done
|
||
|
|
|
||
|
|
for lang in "${LANGUAGES[@]}"; do
|
||
|
|
echo " Language: $lang"
|
||
|
|
|
||
|
|
# Count HTML files for each content type
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
html_count=$(find "${PUBLIC_DIR}/${content_type}/${lang}" -name "*.html" 2>/dev/null | wc -l)
|
||
|
|
echo " 📝 ${content_type} (HTML): $html_count"
|
||
|
|
# Update total using eval for compatibility
|
||
|
|
var_name="total_${content_type//[^a-zA-Z0-9]/_}"
|
||
|
|
current_total=$(eval echo \$$var_name)
|
||
|
|
eval "$var_name=$((current_total + html_count))"
|
||
|
|
done
|
||
|
|
|
||
|
|
# Count source markdown files for comparison
|
||
|
|
echo -n " 📄 Source files: "
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
md_count=$(find "${CONTENT_DIR}/${content_type}/${lang}" -name "*.md" -type f 2>/dev/null | wc -l)
|
||
|
|
echo -n "$md_count ${content_type}, "
|
||
|
|
done
|
||
|
|
echo ""
|
||
|
|
done
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "📈 Total converted files:"
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
var_name="total_${content_type//[^a-zA-Z0-9]/_}"
|
||
|
|
total=$(eval echo \$$var_name)
|
||
|
|
echo " 📝 ${content_type} HTML files: $total"
|
||
|
|
done
|
||
|
|
|
||
|
|
# Optional: Generate content manifest
|
||
|
|
MANIFEST_FILE="${PUBLIC_DIR}/content-manifest.json"
|
||
|
|
echo "📋 Generating content manifest..."
|
||
|
|
|
||
|
|
if command -v jq >/dev/null 2>&1; then
|
||
|
|
cat > "$MANIFEST_FILE" << EOF
|
||
|
|
{
|
||
|
|
"generated_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
|
||
|
|
"version": "1.0",
|
||
|
|
"languages": $(printf '%s\n' "${LANGUAGES[@]}" | jq -R . | jq -s .),
|
||
|
|
"content_types": $(printf '%s\n' "${CONTENT_TYPES[@]}" | jq -R . | jq -s .),
|
||
|
|
"content_root_path": "$CONTENT_DIR",
|
||
|
|
"build_info": {
|
||
|
|
"source_format": "markdown",
|
||
|
|
"output_format": "html",
|
||
|
|
"converter": "rustelo_markdown_converter",
|
||
|
|
"features": ["syntax-highlighting", "css-classes", "localization"]
|
||
|
|
},
|
||
|
|
"structure": {
|
||
|
|
$(for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
echo " \"$content_type\": {"
|
||
|
|
for lang in "${LANGUAGES[@]}"; do
|
||
|
|
html_count=$(find "${PUBLIC_DIR}/${content_type}/${lang}" -name "*.html" 2>/dev/null | wc -l)
|
||
|
|
md_count=$(find "${CONTENT_DIR}/${content_type}/${lang}" -name "*.md" -type f 2>/dev/null | wc -l)
|
||
|
|
has_index="false"
|
||
|
|
[ -f "${PUBLIC_DIR}/${content_type}/${lang}/index.json" ] && has_index="true"
|
||
|
|
echo " \"$lang\": { \"html_count\": $html_count, \"markdown_count\": $md_count, \"has_index\": $has_index },"
|
||
|
|
done | sed '$ s/,$//'
|
||
|
|
echo " },"
|
||
|
|
done | sed '$ s/,$//')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
EOF
|
||
|
|
echo " ✅ Content manifest generated: $MANIFEST_FILE"
|
||
|
|
else
|
||
|
|
echo " ⚠️ jq not available, skipping manifest generation"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo ""
|
||
|
|
echo "🎉 Build complete! Your localized content has been converted to HTML."
|
||
|
|
echo ""
|
||
|
|
echo "📁 Generated HTML files:"
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
find "${PUBLIC_DIR}/${content_type}" -name "*.html" 2>/dev/null | sort
|
||
|
|
done
|
||
|
|
echo ""
|
||
|
|
echo "Next steps:"
|
||
|
|
echo " 1. Markdown files have been converted to HTML with localization"
|
||
|
|
echo " 2. Your Leptos application can now serve the generated HTML content"
|
||
|
|
echo " 3. Users can switch languages and see content in their preferred language"
|
||
|
|
echo ""
|
||
|
|
echo "Content locations:"
|
||
|
|
for content_type in "${CONTENT_TYPES[@]}"; do
|
||
|
|
echo " 📝 ${content_type} HTML: ${PUBLIC_DIR}/${content_type}/{lang}/*.html"
|
||
|
|
done
|
||
|
|
echo " 📋 Manifest: ${PUBLIC_DIR}/content-manifest.json"
|
||
|
|
echo ""
|
||
|
|
echo "🔄 To rebuild content after making changes, run this script again."
|