375 lines
13 KiB
Text
375 lines
13 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
|
||
|
|
# Enhanced Content Build Script (Nushell)
|
||
|
|
# Uses the Rust content_processor with library functions for modularity
|
||
|
|
|
||
|
|
# Import modular library functions
|
||
|
|
use lib/frontmatter.nu *
|
||
|
|
|
||
|
|
# Load environment variables from .env file
|
||
|
|
def load_env_from_file [] {
|
||
|
|
let env_file = ".env"
|
||
|
|
if ($env_file | path exists) {
|
||
|
|
let env_content = (open $env_file | lines)
|
||
|
|
for line in $env_content {
|
||
|
|
# Skip comments and empty lines
|
||
|
|
if not ($line | str trim | str starts-with "#") and not ($line | str trim | is-empty) {
|
||
|
|
# Parse KEY=VALUE format with variable expansion
|
||
|
|
if ($line | str contains "=") {
|
||
|
|
let parts = ($line | split column "=" key value)
|
||
|
|
if ($parts | length) >= 2 {
|
||
|
|
let key = ($parts | first | get key | str trim)
|
||
|
|
let value = ($parts | first | get value | str trim)
|
||
|
|
# Remove quotes and expand ${VARIABLE} patterns
|
||
|
|
let clean_value = ($value | str replace -a '"' '' | str replace -a "'" '')
|
||
|
|
let expanded_value = expand_env_variables $clean_value
|
||
|
|
# Set environment variable
|
||
|
|
load-env {($key): $expanded_value}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Expand ${VARIABLE} patterns in environment values
|
||
|
|
def expand_env_variables [value: string] {
|
||
|
|
# Simplified expansion for common patterns
|
||
|
|
mut result = $value
|
||
|
|
|
||
|
|
# Replace ${SITE_ROOT_PATH} with actual value
|
||
|
|
if ($result | str contains '${SITE_ROOT_PATH}') {
|
||
|
|
let site_root = try { $env.SITE_ROOT_PATH } catch { "." }
|
||
|
|
$result = ($result | str replace -a '${SITE_ROOT_PATH}' $site_root)
|
||
|
|
}
|
||
|
|
|
||
|
|
$result
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get environment variable with default
|
||
|
|
def get_env_var [var_name: string, default_value: string] {
|
||
|
|
try {
|
||
|
|
$env | get $var_name
|
||
|
|
} catch {
|
||
|
|
$default_value
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get enabled content types from content-kinds.toml
|
||
|
|
def get_enabled_content_types [content_dir: string] {
|
||
|
|
let content_kinds_file = $"($content_dir)/content-kinds.toml"
|
||
|
|
|
||
|
|
if not ($content_kinds_file | path exists) {
|
||
|
|
print $"(ansi yellow)⚠️ Warning: content-kinds.toml not found, using defaults(ansi reset)"
|
||
|
|
return ["blog", "recipes"]
|
||
|
|
}
|
||
|
|
|
||
|
|
mut enabled_types = []
|
||
|
|
let toml_content = open $content_kinds_file
|
||
|
|
|
||
|
|
if "content_kinds" in $toml_content {
|
||
|
|
for kind in $toml_content.content_kinds {
|
||
|
|
if $kind.enabled {
|
||
|
|
$enabled_types = ($enabled_types | append $kind.name)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($enabled_types | length) == 0 {
|
||
|
|
["blog", "recipes"]
|
||
|
|
} else {
|
||
|
|
$enabled_types
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show comprehensive help information
|
||
|
|
def show_help [] {
|
||
|
|
print $"
|
||
|
|
(ansi blue)Enhanced Content Build Script(ansi reset)
|
||
|
|
Uses the Rust content_processor for fast, reliable content generation
|
||
|
|
|
||
|
|
(ansi green)USAGE:(ansi reset)
|
||
|
|
./build-content-enhanced.nu [OPTIONS]
|
||
|
|
|
||
|
|
(ansi green)OPTIONS:(ansi reset)
|
||
|
|
-h, --help Show this help message
|
||
|
|
-t, --content-type TYPE Process specific content type only
|
||
|
|
-l, --language LANG Process specific language only
|
||
|
|
-c, --category CATEGORY Process specific category only
|
||
|
|
-f, --file FILE Process specific file with glob pattern support
|
||
|
|
-w, --watch Watch for changes and auto-regenerate
|
||
|
|
--clean Clean output directory before building
|
||
|
|
--copy-to-server Copy generated content to server runtime directory
|
||
|
|
--stats Show content statistics after build
|
||
|
|
--use-legacy Use legacy Nushell processor instead of Rust
|
||
|
|
|
||
|
|
(ansi green)EXAMPLES:(ansi reset)
|
||
|
|
|
||
|
|
# Build all content
|
||
|
|
./build-content-enhanced.nu
|
||
|
|
|
||
|
|
# Build only blog content
|
||
|
|
./build-content-enhanced.nu --content-type blog
|
||
|
|
|
||
|
|
# Build only English blog content
|
||
|
|
./build-content-enhanced.nu --content-type blog --language en
|
||
|
|
|
||
|
|
# Build only rust category posts
|
||
|
|
./build-content-enhanced.nu --content-type blog --language en --category rust
|
||
|
|
|
||
|
|
# Build specific file
|
||
|
|
./build-content-enhanced.nu --file \"blog/en/rust/rust-web-development-2024.md\"
|
||
|
|
|
||
|
|
# Build multiple files with pattern
|
||
|
|
./build-content-enhanced.nu --file \"blog/en/*/rust-*.md\"
|
||
|
|
|
||
|
|
# Clean build with server copy and stats
|
||
|
|
./build-content-enhanced.nu --clean --copy-to-server --stats
|
||
|
|
|
||
|
|
# Watch mode for development
|
||
|
|
./build-content-enhanced.nu --watch
|
||
|
|
|
||
|
|
(ansi green)ENVIRONMENT VARIABLES:(ansi reset)
|
||
|
|
SITE_CONTENT_PATH Source content directory (default: site/content)
|
||
|
|
SITE_PUBLIC_PATH Output directory (default: site/public)
|
||
|
|
SITE_SERVER_CONTENT_ROOT Server runtime directory (default: r)
|
||
|
|
|
||
|
|
(ansi green)GENERATED FILES:(ansi reset)
|
||
|
|
post-name.html Markdown content converted to HTML
|
||
|
|
post-name.json Frontmatter data in JSON format
|
||
|
|
index.json Collection index with all posts and metadata
|
||
|
|
|
||
|
|
(ansi yellow)NOTES:(ansi reset)
|
||
|
|
- Recommended replacement for build-localized-content.nu
|
||
|
|
- Uses fast Rust content_processor by default
|
||
|
|
- All processing options respect project language-agnostic architecture
|
||
|
|
- No hardcoded paths, categories, or content types
|
||
|
|
- Automatically discovers content from configuration files
|
||
|
|
- Hot reload ready for development workflows
|
||
|
|
"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Build cargo command for content processor
|
||
|
|
def build_rust_processor_command [
|
||
|
|
content_type?: string
|
||
|
|
language?: string
|
||
|
|
category?: string
|
||
|
|
file?: string
|
||
|
|
watch: bool = false
|
||
|
|
] {
|
||
|
|
mut cmd = ["cargo", "run", "--features=content-static", "--package", "rustelo-htmx-server", "--bin", "content_processor", "--"]
|
||
|
|
|
||
|
|
if $content_type != null {
|
||
|
|
$cmd = ($cmd | append ["--content-type", $content_type])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $language != null {
|
||
|
|
$cmd = ($cmd | append ["--language", $language])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $category != null {
|
||
|
|
$cmd = ($cmd | append ["--category", $category])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $file != null {
|
||
|
|
$cmd = ($cmd | append ["--file", $file])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $watch {
|
||
|
|
$cmd = ($cmd | append ["--watch"])
|
||
|
|
}
|
||
|
|
|
||
|
|
$cmd
|
||
|
|
}
|
||
|
|
|
||
|
|
# Run the Rust content processor
|
||
|
|
def run_rust_processor [
|
||
|
|
content_type?: string
|
||
|
|
language?: string
|
||
|
|
category?: string
|
||
|
|
file?: string
|
||
|
|
watch: bool = false
|
||
|
|
] {
|
||
|
|
mut cmd_args = []
|
||
|
|
|
||
|
|
if $content_type != null {
|
||
|
|
$cmd_args = ($cmd_args | append ["--content-type", $content_type])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $language != null {
|
||
|
|
$cmd_args = ($cmd_args | append ["--language", $language])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $category != null {
|
||
|
|
$cmd_args = ($cmd_args | append ["--category", $category])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $file != null {
|
||
|
|
$cmd_args = ($cmd_args | append ["--file", $file])
|
||
|
|
}
|
||
|
|
|
||
|
|
if $watch {
|
||
|
|
$cmd_args = ($cmd_args | append ["--watch"])
|
||
|
|
}
|
||
|
|
|
||
|
|
print $"(ansi blue)🔧 Running cargo with args: ($cmd_args | str join ' ')(ansi reset)"
|
||
|
|
|
||
|
|
try {
|
||
|
|
if ($cmd_args | length) > 0 {
|
||
|
|
run-external "cargo" "run" "--features=content-static" "--package" "rustelo-htmx-server" "--bin" "content_processor" "--" ...$cmd_args
|
||
|
|
} else {
|
||
|
|
run-external "cargo" "run" "--features=content-static" "--package" "rustelo-htmx-server" "--bin" "content_processor"
|
||
|
|
}
|
||
|
|
print $"(ansi green)✅ Content processing completed!(ansi reset)"
|
||
|
|
true
|
||
|
|
} catch { |err|
|
||
|
|
print $"(ansi red)❌ Content processing failed: ($err.msg)(ansi reset)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Copy generated content to server runtime directory
|
||
|
|
def copy_to_server_directory [public_dir: string, server_dir: string] {
|
||
|
|
print $"(ansi blue)📋 Copying content to server runtime directory...(ansi reset)"
|
||
|
|
print $"(ansi blue) From: ($public_dir)(ansi reset)"
|
||
|
|
print $"(ansi blue) To: ($server_dir)(ansi reset)"
|
||
|
|
|
||
|
|
# Create server directory if it doesn't exist
|
||
|
|
mkdir $server_dir
|
||
|
|
|
||
|
|
# Copy all content
|
||
|
|
if ($public_dir | path exists) {
|
||
|
|
try {
|
||
|
|
cp -r ($public_dir | path join "*") $server_dir
|
||
|
|
print $"(ansi green)✅ Content copied to server directory(ansi reset)"
|
||
|
|
} catch { |err|
|
||
|
|
print $"(ansi yellow)⚠️ Warning: Could not copy some files: ($err.msg)(ansi reset)"
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
print $"(ansi yellow)⚠️ Warning: Public directory does not exist: ($public_dir)(ansi reset)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show content statistics using library functions
|
||
|
|
def show_enhanced_content_statistics [output_dir: string] {
|
||
|
|
print $"(ansi blue)📊 Enhanced Content Statistics:(ansi reset)"
|
||
|
|
|
||
|
|
if not ($output_dir | path exists) {
|
||
|
|
print $"(ansi yellow)⚠️ Output directory does not exist: ($output_dir)(ansi reset)"
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
# Count generated files by type
|
||
|
|
let html_files = (glob ($output_dir | path join "**/*.html") | length)
|
||
|
|
let json_files = (glob ($output_dir | path join "**/*.json") | length)
|
||
|
|
let index_files = (glob ($output_dir | path join "**/index.json") | length)
|
||
|
|
|
||
|
|
print $" HTML files: ($html_files)"
|
||
|
|
print $" JSON files: ($json_files)"
|
||
|
|
print $" Index files: ($index_files)"
|
||
|
|
|
||
|
|
# Show directory structure using library function if available
|
||
|
|
print $"(ansi blue)📁 Generated Structure:(ansi reset)"
|
||
|
|
try {
|
||
|
|
ls $output_dir | where type == "dir" | each { |item|
|
||
|
|
let dir_name = ($item.name | path basename)
|
||
|
|
print $" 📁 ($dir_name)/"
|
||
|
|
|
||
|
|
# Show subdirectories
|
||
|
|
try {
|
||
|
|
ls $item.name | where type == "dir" | each { |subitem|
|
||
|
|
let subdir_name = ($subitem.name | path basename)
|
||
|
|
let post_count = (glob ($subitem.name | path join "*.md") | length)
|
||
|
|
print $" 📂 ($subdir_name)/ - ($post_count) posts"
|
||
|
|
} | ignore
|
||
|
|
} catch {
|
||
|
|
# Ignore errors accessing subdirectories
|
||
|
|
}
|
||
|
|
} | ignore
|
||
|
|
} catch {
|
||
|
|
print $" Structure listing not available"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Main function with comprehensive options
|
||
|
|
def main [
|
||
|
|
--help(-h) # Show help message
|
||
|
|
--content-type(-t): string # Process specific content type only
|
||
|
|
--language(-l): string # Process specific language only
|
||
|
|
--category(-c): string # Process specific category only
|
||
|
|
--file(-f): string # Process specific file with glob support
|
||
|
|
--watch(-w) # Watch for changes and auto-regenerate
|
||
|
|
--clean # Clean output directory before building
|
||
|
|
--copy-to-server # Copy generated content to server runtime directory
|
||
|
|
--stats # Show content statistics after build
|
||
|
|
--use-legacy # Use legacy Nushell processor instead of Rust
|
||
|
|
] {
|
||
|
|
# Show help if requested
|
||
|
|
if $help {
|
||
|
|
show_help
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
# Load environment variables using library function
|
||
|
|
load_env_from_file
|
||
|
|
|
||
|
|
# Configuration from environment using library function
|
||
|
|
let content_dir = (get_env_var "SITE_CONTENT_PATH" "site/content")
|
||
|
|
let public_dir = (get_env_var "SITE_PUBLIC_PATH" "site/public")
|
||
|
|
let server_dir = (get_env_var "SITE_SERVER_ROOT_CONTENT" "r")
|
||
|
|
let out_dir_path = ($public_dir | path join $server_dir)
|
||
|
|
|
||
|
|
print $"(ansi blue)🚀 Enhanced Content Build System(ansi reset)"
|
||
|
|
print $"(ansi blue)📁 Source: ($content_dir)(ansi reset)"
|
||
|
|
print $"(ansi blue)📂 Output: ($out_dir_path(ansi reset)"
|
||
|
|
|
||
|
|
|
||
|
|
# Clean output directory if requested
|
||
|
|
if $clean {
|
||
|
|
if ($out_dir_path | path exists) {
|
||
|
|
print $"(ansi yellow)🧹 Cleaning output directory: ($out_dir_path)(ansi reset)"
|
||
|
|
rm -rf $out_dir_path
|
||
|
|
}
|
||
|
|
mkdir $out_dir_path
|
||
|
|
}
|
||
|
|
|
||
|
|
# Use Rust content processor only
|
||
|
|
if $use_legacy {
|
||
|
|
print $"(ansi red)❌ Legacy Nushell processor is not supported(ansi reset)"
|
||
|
|
print $"(ansi yellow)💡 Please use the Rust content processor for reliable content processing(ansi reset)"
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
# Use the Rust content processor (only supported method)
|
||
|
|
print $"(ansi blue)🚀 Using Rust content processor...(ansi reset)"
|
||
|
|
|
||
|
|
let success = (run_rust_processor $content_type $language $category $file $watch)
|
||
|
|
|
||
|
|
if not $success {
|
||
|
|
print $"(ansi red)❌ Build failed!(ansi reset)"
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
# Copy to server directory if requested
|
||
|
|
if $copy_to_server {
|
||
|
|
copy_to_server_directory $public_dir $server_dir
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show statistics if requested
|
||
|
|
if $stats {
|
||
|
|
show_enhanced_content_statistics $public_dir
|
||
|
|
}
|
||
|
|
|
||
|
|
# Show completion message
|
||
|
|
if $watch {
|
||
|
|
print $"(ansi yellow)👀 Watch mode active - monitoring for changes...(ansi reset)"
|
||
|
|
print $"(ansi yellow)Press Ctrl+C to stop watching(ansi reset)"
|
||
|
|
} else {
|
||
|
|
print $"(ansi green)🎉 Content build complete!(ansi reset)"
|
||
|
|
print $"(ansi green) Generated files in: ($out_dir_path)(ansi reset)"
|
||
|
|
if $copy_to_server {
|
||
|
|
print $"(ansi green) Server files in: ($server_dir)(ansi reset)"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|