787 lines
24 KiB
PowerShell
787 lines
24 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Build script for static website generator
|
|
|
|
.DESCRIPTION
|
|
This script processes Markdown content files and generates a static HTML website.
|
|
It dynamically detects content sections, processes blog posts, and generates
|
|
navigation menus. The output is a fully functional static website in the dist/ folder.
|
|
|
|
Features:
|
|
- Dynamic section detection from content/ folder
|
|
- Markdown to HTML conversion
|
|
- Blog post processing with front matter
|
|
- Automatic image handling
|
|
- Responsive navigation generation
|
|
|
|
.REQUIREMENTS
|
|
- PowerShell 7+ (for better UTF-8 handling)
|
|
- Markdown files in content/ folder
|
|
- Images in images/ folder
|
|
|
|
.NOTES
|
|
Output: Generated files are placed in dist/ directory
|
|
#>
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION
|
|
# ============================================================================
|
|
|
|
# Get the directory where this script is located
|
|
$projectRoot = $PSScriptRoot
|
|
|
|
# Define source and output directories
|
|
$sourceDir = Join-Path $projectRoot "content" # Markdown content source
|
|
$templateFile = Join-Path $projectRoot "template.html" # Temporary template file
|
|
$imagesDir = Join-Path $projectRoot "images" # Source images directory
|
|
|
|
# Output directory - can be overridden with OUTPUT_DIR environment variable
|
|
$outputDir = if ($env:OUTPUT_DIR) {
|
|
Join-Path $projectRoot $env:OUTPUT_DIR
|
|
} else {
|
|
Join-Path $projectRoot "dist" # Default output directory
|
|
}
|
|
|
|
# Get current year for copyright
|
|
$year = (Get-Date).Year
|
|
|
|
# Site configuration - customize these values
|
|
$siteTitle = "My Website"
|
|
$siteHeader = "Welcome to My Website"
|
|
$footerText = "My Website"
|
|
|
|
# Temporary output files (will be moved to dist/ at the end)
|
|
$tempOutputFile = Join-Path $projectRoot "index.html"
|
|
$tempBlogOutputFile = Join-Path $projectRoot "blog.html"
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Finds the background image file in the images directory
|
|
|
|
.DESCRIPTION
|
|
Searches for any file matching "background.*" pattern and returns
|
|
the relative path for use in CSS background-image property.
|
|
|
|
.PARAMETER imagesDir
|
|
Path to the images directory
|
|
|
|
.OUTPUTS
|
|
String - Relative path to background image (e.g., "images/background.jpg")
|
|
Empty string if no background image found
|
|
#>
|
|
function Get-BackgroundImagePath {
|
|
param (
|
|
[string]$imagesDir
|
|
)
|
|
|
|
$backgroundImages = Get-ChildItem (Join-Path $imagesDir "background.*") -ErrorAction SilentlyContinue
|
|
if ($backgroundImages.Count -gt 0) {
|
|
return "images/" + $backgroundImages[0].Name
|
|
}
|
|
return "" # Return empty string if no background image found
|
|
}
|
|
|
|
# Create output directory if it doesn't exist
|
|
if (-not (Test-Path $outputDir)) {
|
|
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
|
}
|
|
|
|
# Copy static files to dist first
|
|
Copy-Item "styles.css" -Destination $outputDir -Force
|
|
if (Test-Path "images") {
|
|
Copy-Item "images" -Destination $outputDir -Recurse -Force
|
|
}
|
|
|
|
# Set output files after copying
|
|
$outputFile = Join-Path $outputDir "index.html"
|
|
$blogOutputFile = Join-Path $outputDir "blog.html"
|
|
|
|
# Get background image path
|
|
$backgroundPath = Get-BackgroundImagePath -imagesDir $imagesDir
|
|
$backgroundStyle = if ($backgroundPath) {
|
|
"background-image: url('$backgroundPath');"
|
|
} else {
|
|
"" # No background image
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Generates navigation menu HTML from section IDs
|
|
|
|
.DESCRIPTION
|
|
Creates an unordered list of navigation links for all detected sections,
|
|
plus a Blog link. Section names are capitalized for display.
|
|
|
|
.PARAMETER sections
|
|
Array of section IDs (e.g., @("about", "services", "contact"))
|
|
|
|
.OUTPUTS
|
|
String - HTML navigation menu items
|
|
#>
|
|
function Get-NavigationHtml {
|
|
param (
|
|
[array]$sections
|
|
)
|
|
|
|
$navItems = @()
|
|
foreach ($section in $sections) {
|
|
# Capitalize first letter of section name
|
|
$sectionName = $section.Substring(0,1).ToUpper() + $section.Substring(1)
|
|
$navItems += " <li><a href=`"#$section`">$sectionName</a></li>"
|
|
}
|
|
# Always add Blog link
|
|
$navItems += " <li><a href=`"blog.html`">Blog</a></li>"
|
|
return $navItems -join "`n"
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Finds all numbered images for a section
|
|
|
|
.DESCRIPTION
|
|
Searches for images matching the pattern {sectionId}{number}.*
|
|
(e.g., "about1.webp", "about2.jpg", "services3.png")
|
|
and returns them as a hashtable indexed by number.
|
|
|
|
.PARAMETER sectionId
|
|
The section identifier (e.g., "about", "services")
|
|
|
|
.OUTPUTS
|
|
Hashtable - Images indexed by number (e.g., @{1="about1.webp"; 2="about2.jpg"})
|
|
|
|
.EXAMPLE
|
|
Get-SectionImages -sectionId "about"
|
|
Returns: @{1="about1.webp"; 2="about2.jpg"; 3="about3.jpg"}
|
|
#>
|
|
function Get-SectionImages {
|
|
param (
|
|
[string]$sectionId
|
|
)
|
|
|
|
# Get all files matching the pattern sectionX.* (e.g., about1.webp, about2.png)
|
|
$images = Get-ChildItem (Join-Path $imagesDir "$sectionId[0-9]*.*") -ErrorAction SilentlyContinue
|
|
|
|
# Create a hashtable to store images by number
|
|
$imagesByNumber = @{}
|
|
|
|
foreach ($image in $images) {
|
|
# Extract the number from the filename using regex
|
|
# Matches: sectionId followed by one or more digits
|
|
if ($image.BaseName -match "$sectionId(\d+)") {
|
|
$number = [int]$matches[1]
|
|
$imagesByNumber[$number] = $image.Name
|
|
}
|
|
}
|
|
|
|
return $imagesByNumber
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Converts a Markdown file to an HTML section
|
|
|
|
.DESCRIPTION
|
|
Processes a Markdown file and converts it to a full HTML section with:
|
|
- Section heading extracted from first # heading
|
|
- Content blocks separated by "---" dividers
|
|
- Alternating left/right image alignment
|
|
- Automatic image assignment from images directory
|
|
|
|
.PARAMETER file
|
|
Path to the Markdown file to convert
|
|
|
|
.PARAMETER sectionId
|
|
The section ID to use (e.g., "about", "services")
|
|
|
|
.OUTPUTS
|
|
String - Complete HTML section markup
|
|
|
|
.NOTES
|
|
Markdown format:
|
|
- First line: # Section Title
|
|
- Content blocks separated by "---"
|
|
- Each block becomes a section-content div with text and image
|
|
#>
|
|
function Convert-MarkdownToSection {
|
|
param (
|
|
[string]$file,
|
|
[string]$sectionId
|
|
)
|
|
|
|
# Read markdown content and split by "---" dividers
|
|
$content = Get-Content $file -Raw
|
|
$sections = $content -split "---" | ForEach-Object { $_.Trim() }
|
|
|
|
# Get available numbered images for this section (e.g., about1.webp, about2.jpg)
|
|
$sectionImages = Get-SectionImages -sectionId $sectionId
|
|
|
|
# Extract title from first section's # heading, or capitalize section ID as fallback
|
|
$title = if ($sections[0] -match "^#\s+(.+)$") {
|
|
$matches[1]
|
|
} else {
|
|
$sectionId.Substring(0,1).ToUpper() + $sectionId.Substring(1)
|
|
}
|
|
|
|
# Start building section HTML
|
|
$sectionHtml = @"
|
|
<section id="$sectionId" class="snap-section">
|
|
<h2>$title</h2>
|
|
<div class="content-container">
|
|
"@
|
|
|
|
# Process each content section (separated by "---")
|
|
for ($i = 0; $i -lt $sections.Count; $i++) {
|
|
$text = $sections[$i]
|
|
|
|
# Image selection priority:
|
|
# 1. Explicit markdown image syntax: 
|
|
# 2. Numbered section image (e.g., about1.webp for first block)
|
|
# 3. First available numbered image if exact match not found
|
|
# 4. Default fallback: sectionId.webp
|
|
$imageMatch = $text -match '!\[([^\]]*)\]\(([^\)]+)\)'
|
|
$imageSrc = if ($imageMatch) {
|
|
$text = $text -replace '!\[([^\]]*)\]\(([^\)]+)\)', '' # Remove image markdown from text
|
|
$matches[2] # Use specified image path
|
|
} elseif ($sectionImages.ContainsKey($i + 1)) {
|
|
"images/" + $sectionImages[$i + 1] # Use numbered section image (e.g., about1.webp)
|
|
} elseif ($sectionImages.Count -gt 0) {
|
|
# Use first available image if numbered image doesn't exist
|
|
$sortedKeys = $sectionImages.Keys | Sort-Object
|
|
$firstKey = $sortedKeys[0]
|
|
"images/" + $sectionImages[$firstKey]
|
|
} else {
|
|
"images/$sectionId.webp" # Default fallback
|
|
}
|
|
|
|
# Generate alt text: use markdown alt text if provided, otherwise generate descriptive text
|
|
$imageAlt = if ($imageMatch) {
|
|
$matches[1] # Use specified alt text from markdown
|
|
} else {
|
|
"$sectionId image $($i + 1)" # Default alt text
|
|
}
|
|
|
|
# Remove title heading from first section (already extracted above)
|
|
$text = $text -replace "^#\s+.+`n", ""
|
|
|
|
# Convert markdown links to HTML anchor tags
|
|
$text = $text -replace '\[([^\]]+)\]\(([^\)]+)\)', '<a href="$2">$1</a>'
|
|
|
|
# Alternate alignment: even indices = left-align (image on right), odd = right-align (image on left)
|
|
$alignment = if ($i % 2 -eq 0) { "left-align" } else { "right-align" }
|
|
|
|
$sectionHtml += @"
|
|
<div class="section-content $alignment">
|
|
<div class="text-content">
|
|
<p>$text</p>
|
|
</div>
|
|
<div class="image-content">
|
|
<img src="$imageSrc" alt="$imageAlt">
|
|
</div>
|
|
</div>
|
|
"@
|
|
}
|
|
|
|
$sectionHtml += @"
|
|
</div>
|
|
</section>
|
|
"@
|
|
|
|
return $sectionHtml
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN BUILD PROCESS - INDEX PAGE
|
|
# ============================================================================
|
|
|
|
# Dynamically detect all .md files in content folder (excluding blog subdirectory)
|
|
# Each .md file becomes a section on the index page
|
|
$contentHtml = ""
|
|
$markdownFiles = Get-ChildItem -Path $sourceDir -Filter "*.md" -File | Where-Object { $_.DirectoryName -eq $sourceDir }
|
|
$sectionIds = @()
|
|
|
|
Write-Host "Found $($markdownFiles.Count) section markdown files:"
|
|
foreach ($file in $markdownFiles) {
|
|
$sectionId = $file.BaseName # Use filename (without .md) as section ID
|
|
$sectionIds += $sectionId
|
|
Write-Host " - $sectionId"
|
|
# Convert each markdown file to an HTML section
|
|
$contentHtml += Convert-MarkdownToSection -file $file.FullName -sectionId $sectionId
|
|
}
|
|
|
|
# Generate navigation menu HTML from detected sections
|
|
$navigationHtml = Get-NavigationHtml -sections $sectionIds
|
|
|
|
# Create HTML template with placeholders
|
|
$template = @"
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>$siteTitle</title>
|
|
<style>
|
|
body::before {
|
|
$backgroundStyle
|
|
}
|
|
</style>
|
|
<link rel="stylesheet" href="styles.css">
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>$siteHeader</h1>
|
|
<nav>
|
|
<ul>
|
|
{{navigation}}
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="snap-container">
|
|
{{content}}
|
|
</main>
|
|
|
|
<footer>
|
|
<p>© $year $footerText. All rights reserved.</p>
|
|
</footer>
|
|
</body>
|
|
</html>
|
|
"@
|
|
|
|
# Replace template placeholders with actual content
|
|
$finalHtml = $template -replace "{{content}}", $contentHtml -replace "{{navigation}}", $navigationHtml
|
|
$finalHtml | Out-File $tempOutputFile -Encoding utf8
|
|
|
|
Write-Host "Building site from $projectRoot"
|
|
Write-Host "Content directory: $sourceDir"
|
|
Write-Host "Images directory: $imagesDir"
|
|
|
|
Write-Host "Build complete! Output saved to $tempOutputFile"
|
|
|
|
# ============================================================================
|
|
# BLOG BUILD PROCESS
|
|
# ============================================================================
|
|
|
|
# Blog posts are stored in content/blog/ subdirectory
|
|
$blogDir = Join-Path $sourceDir "blog"
|
|
$blogOutputFile = Join-Path $outputDir "blog.html"
|
|
|
|
Write-Host "building blog"
|
|
Write-Host "Blog dir: $blogDir"
|
|
Write-Host "Blog output file: $blogOutputFile"
|
|
|
|
# Blog template
|
|
$blogTemplate = @"
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>$siteTitle - Blog</title>
|
|
<style>
|
|
body::before {
|
|
$backgroundStyle
|
|
}
|
|
</style>
|
|
<link rel="stylesheet" href="styles.css">
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>$siteTitle</h1>
|
|
<nav>
|
|
<ul>
|
|
{{navigation}}
|
|
</ul>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="blog-container">
|
|
<div class="blog-grid">
|
|
{{content}}
|
|
</div>
|
|
{{pagination}}
|
|
</main>
|
|
|
|
<footer>
|
|
<p>© $year $footerText. All rights reserved.</p>
|
|
</footer>
|
|
</body>
|
|
</html>
|
|
"@
|
|
|
|
# Check if blog directory exists
|
|
Write-Host "Building blog from: $blogDir"
|
|
if (Test-Path $blogDir) {
|
|
Write-Host "Blog directory exists"
|
|
$files = Get-ChildItem (Join-Path $blogDir "*.md")
|
|
Write-Host "Found blog files: $($files.Count)"
|
|
$files | ForEach-Object { Write-Host " - $($_.Name)" }
|
|
} else {
|
|
Write-Host "Blog directory does not exist at: $blogDir"
|
|
# Create the directory
|
|
New-Item -ItemType Directory -Path $blogDir -Force
|
|
Write-Host "Created blog directory"
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Finds the image file for a blog post
|
|
|
|
.DESCRIPTION
|
|
Searches for blog post images using multiple strategies:
|
|
1. Image name specified in front matter
|
|
2. Post filename (without extension)
|
|
|
|
.PARAMETER imageName
|
|
Image name from front matter (e.g., "blog1")
|
|
|
|
.PARAMETER postFileName
|
|
Name of the blog post file (e.g., "ai-tools.md")
|
|
|
|
.PARAMETER imagesDir
|
|
Path to images directory
|
|
|
|
.OUTPUTS
|
|
String - Relative path to image (e.g., "images/blog1.webp")
|
|
$null if no image found
|
|
#>
|
|
function Get-BlogPostImage {
|
|
param (
|
|
[string]$imageName,
|
|
[string]$postFileName,
|
|
[string]$imagesDir
|
|
)
|
|
|
|
# Build search list: try specified name first, then post filename
|
|
$searchNames = @()
|
|
if ($imageName) {
|
|
$searchNames += $imageName
|
|
}
|
|
$postBaseName = [System.IO.Path]::GetFileNameWithoutExtension($postFileName)
|
|
$searchNames += $postBaseName
|
|
|
|
# Search for image files matching any of the names
|
|
foreach ($name in $searchNames) {
|
|
$imageFiles = Get-ChildItem (Join-Path $imagesDir "$name.*") -ErrorAction SilentlyContinue
|
|
if ($imageFiles.Count -gt 0) {
|
|
return "images/" + $imageFiles[0].Name
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Converts Markdown text to HTML with proper formatting
|
|
|
|
.DESCRIPTION
|
|
Processes Markdown syntax and converts to HTML:
|
|
- Headings (#, ##, ###, ####)
|
|
- Links [text](url)
|
|
- Lists (bulleted and numbered)
|
|
- Code blocks and inline code
|
|
- Paragraphs
|
|
|
|
.PARAMETER markdown
|
|
Markdown text to convert
|
|
|
|
.OUTPUTS
|
|
String - HTML formatted text
|
|
#>
|
|
function Convert-MarkdownToHtml {
|
|
param (
|
|
[string]$markdown
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($markdown)) {
|
|
return ""
|
|
}
|
|
|
|
$html = $markdown
|
|
|
|
# Convert code blocks first (before other processing)
|
|
$html = $html -replace '(?ms)```(\w+)?\r?\n(.*?)```', '<pre><code>$2</code></pre>'
|
|
|
|
# Convert inline code (`code`)
|
|
$html = $html -replace '`([^`]+)`', '<code>$1</code>'
|
|
|
|
# Convert headings (##, ###, etc.)
|
|
$html = $html -replace '(?m)^####\s+(.+)$', '<h4>$1</h4>'
|
|
$html = $html -replace '(?m)^###\s+(.+)$', '<h3>$1</h3>'
|
|
$html = $html -replace '(?m)^##\s+(.+)$', '<h2>$1</h2>'
|
|
$html = $html -replace '(?m)^#\s+(.+)$', '<h1>$1</h1>'
|
|
|
|
# Convert markdown links [text](url)
|
|
$html = $html -replace '\[([^\]]+)\]\(([^\)]+)\)', '<a href="$2">$1</a>'
|
|
|
|
# Process line by line to handle lists properly
|
|
$lines = $html -split "`r?`n"
|
|
$output = @()
|
|
$inList = $false
|
|
$listType = ""
|
|
$listItems = @()
|
|
|
|
foreach ($line in $lines) {
|
|
$trimmed = $line.Trim()
|
|
|
|
# Check for numbered list
|
|
if ($trimmed -match '^\d+\.\s+(.+)$') {
|
|
if (-not $inList -or $listType -ne "ol") {
|
|
# Close previous list if exists
|
|
if ($inList) {
|
|
$output += "</$listType>"
|
|
}
|
|
$inList = $true
|
|
$listType = "ol"
|
|
$listItems = @()
|
|
}
|
|
$listItems += "<li>$($matches[1])</li>"
|
|
}
|
|
# Check for bullet list
|
|
elseif ($trimmed -match '^[-*]\s+(.+)$') {
|
|
if (-not $inList -or $listType -ne "ul") {
|
|
# Close previous list if exists
|
|
if ($inList) {
|
|
$output += "</$listType>"
|
|
}
|
|
$inList = $true
|
|
$listType = "ul"
|
|
$listItems = @()
|
|
}
|
|
$listItems += "<li>$($matches[1])</li>"
|
|
}
|
|
# Empty line - close list if open
|
|
elseif ([string]::IsNullOrWhiteSpace($trimmed)) {
|
|
if ($inList) {
|
|
$output += "<$listType>" + ($listItems -join "`n") + "</$listType>"
|
|
$inList = $false
|
|
$listType = ""
|
|
$listItems = @()
|
|
}
|
|
$output += ""
|
|
}
|
|
# Regular content line
|
|
else {
|
|
# Close list if open
|
|
if ($inList) {
|
|
$output += "<$listType>" + ($listItems -join "`n") + "</$listType>"
|
|
$inList = $false
|
|
$listType = ""
|
|
$listItems = @()
|
|
}
|
|
$output += $trimmed
|
|
}
|
|
}
|
|
|
|
# Close any open list
|
|
if ($inList) {
|
|
$output += "<$listType>" + ($listItems -join "`n") + "</$listType>"
|
|
}
|
|
|
|
# Join lines and split into paragraphs
|
|
$joined = $output -join "`n"
|
|
$paragraphs = $joined -split "`n`n" | Where-Object { $_.Trim() -ne "" }
|
|
$processedParagraphs = @()
|
|
|
|
foreach ($para in $paragraphs) {
|
|
$para = $para.Trim()
|
|
if ($para -ne "") {
|
|
# Don't wrap if it's already a block element
|
|
if ($para -match '^<(pre|h[1-4]|ul|ol|p)') {
|
|
$processedParagraphs += $para
|
|
} else {
|
|
$processedParagraphs += "<p>$para</p>"
|
|
}
|
|
}
|
|
}
|
|
|
|
return $processedParagraphs -join "`n"
|
|
}
|
|
|
|
<#
|
|
.SYNOPSIS
|
|
Converts a blog post Markdown file to HTML
|
|
|
|
.DESCRIPTION
|
|
Processes blog posts with front matter (YAML-like metadata between --- markers):
|
|
- Extracts title, date, author, tags, image
|
|
- Converts markdown content to HTML
|
|
- Generates complete blog post article HTML
|
|
|
|
.PARAMETER file
|
|
Path to blog post Markdown file
|
|
|
|
.PARAMETER imagesDir
|
|
Path to images directory for blog post images
|
|
|
|
.OUTPUTS
|
|
String - Complete HTML article markup
|
|
|
|
.NOTES
|
|
Front matter format:
|
|
---
|
|
title: Post Title
|
|
date: YYYY-MM-DD
|
|
author: Author Name
|
|
tags: [tag1, tag2, tag3]
|
|
image: imageName
|
|
---
|
|
#>
|
|
function Convert-BlogPostToHtml {
|
|
param (
|
|
[string]$file,
|
|
[string]$imagesDir
|
|
)
|
|
|
|
Write-Host "Converting file: $file"
|
|
$content = Get-Content $file -Raw
|
|
Write-Host "Content length: $($content.Length)"
|
|
|
|
# Updated regex pattern to match exactly three dashes
|
|
if ($content -match "(?ms)^---[\r\n](.*?)[\r\n]---[\r\n](.*)$") {
|
|
Write-Host "Found front matter"
|
|
$frontMatter = $matches[1]
|
|
$content = $matches[2].Trim()
|
|
|
|
Write-Host "Front matter: $frontMatter"
|
|
Write-Host "Content after front matter: $content"
|
|
|
|
# Parse front matter into hashtable
|
|
$metadata = @{}
|
|
foreach ($line in ($frontMatter -split "`n")) {
|
|
if ($line -match "^(\w+):\s*(.*)$") {
|
|
$key = $matches[1]
|
|
$value = $matches[2].Trim()
|
|
if ($key -eq "tags") {
|
|
$value = $value.Trim("[]").Split(",").ForEach({ $_.Trim() })
|
|
}
|
|
$metadata[$key] = $value
|
|
Write-Host "Parsed metadata: $key = $value"
|
|
}
|
|
}
|
|
|
|
# Find blog post image
|
|
$imagePath = Get-BlogPostImage -imageName $metadata.image -postFileName (Split-Path $file -Leaf) -imagesDir $imagesDir
|
|
$imageHtml = if ($imagePath) {
|
|
"<div class=`"blog-post-image`"><img src=`"$imagePath`" alt=`"$($metadata.title)`"></div>"
|
|
} else {
|
|
""
|
|
}
|
|
|
|
# Convert tags to HTML
|
|
$tagsHtml = $metadata.tags | ForEach-Object {
|
|
"<span class='blog-tag'>$_</span>"
|
|
}
|
|
|
|
# Process content sections with proper markdown formatting
|
|
$sections = $content -split "---" | ForEach-Object { $_.Trim() }
|
|
$processedContent = $sections | ForEach-Object {
|
|
if ($_.Trim() -ne "") {
|
|
Convert-MarkdownToHtml -markdown $_
|
|
}
|
|
} | Where-Object { $_ -ne "" }
|
|
|
|
$html = @"
|
|
<article class="blog-post">
|
|
<div class="blog-post-header">
|
|
<h2 class="blog-post-title">$($metadata.title)</h2>
|
|
<div class="blog-post-meta">
|
|
<span>$($metadata.date)</span> • <span>$($metadata.author)</span>
|
|
</div>
|
|
<div class="blog-post-tags">
|
|
$($tagsHtml -join '')
|
|
</div>
|
|
</div>
|
|
$imageHtml
|
|
<div class="blog-post-content">
|
|
$($processedContent -join "`n")
|
|
</div>
|
|
</article>
|
|
"@
|
|
Write-Host "Generated HTML length: $($html.Length)"
|
|
return $html
|
|
}
|
|
Write-Host "No front matter found in file"
|
|
return ""
|
|
}
|
|
|
|
# Process blog posts: read all .md files, extract dates, and sort by date (newest first)
|
|
$blogPosts = Get-ChildItem (Join-Path $blogDir "*.md") | ForEach-Object {
|
|
Write-Host "Processing blog post: $($_.Name)"
|
|
$content = Get-Content $_.FullName -Raw
|
|
Write-Host "Content read: $($content.Length) characters"
|
|
|
|
# Updated regex pattern to match exactly three dashes
|
|
if ($content -match "(?ms)^---[\r\n](.*?)[\r\n]---") {
|
|
Write-Host "Found front matter in $($_.Name)"
|
|
$frontMatter = $matches[1]
|
|
Write-Host "Front matter: $frontMatter"
|
|
|
|
if ($frontMatter -match "date:\s*(.*)") {
|
|
Write-Host "Found date: $($matches[1])"
|
|
$date = [datetime]::Parse($matches[1])
|
|
@{
|
|
File = $_
|
|
Date = $date
|
|
}
|
|
} else {
|
|
Write-Host "No date found in front matter"
|
|
}
|
|
} else {
|
|
Write-Host "No front matter found in $($_.Name)"
|
|
}
|
|
} | Sort-Object { $_.Date } -Descending
|
|
|
|
Write-Host "Sorted blog posts: $($blogPosts.Count)"
|
|
|
|
# Convert each blog post to HTML and combine
|
|
$blogHtml = ""
|
|
foreach ($post in $blogPosts) {
|
|
Write-Host "Converting blog post to HTML: $($post.File.Name)"
|
|
$html = Convert-BlogPostToHtml -file $post.File.FullName -imagesDir $imagesDir
|
|
Write-Host "Generated HTML length: $($html.Length)"
|
|
$blogHtml += $html
|
|
}
|
|
|
|
Write-Host "Total blog HTML length: $($blogHtml.Length)"
|
|
|
|
# Generate blog navigation (same as main page)
|
|
$blogNavHtml = Get-NavigationHtml -sections $sectionIds
|
|
|
|
# Generate final blog page HTML by replacing template placeholders
|
|
$finalBlogHtml = $blogTemplate -replace "{{content}}", $blogHtml -replace "{{pagination}}", "" -replace "{{navigation}}", $blogNavHtml
|
|
$finalBlogHtml | Out-File $tempBlogOutputFile -Encoding utf8
|
|
|
|
Write-Host "Blog build complete"
|
|
|
|
# ============================================================================
|
|
# FINAL OUTPUT - COPY FILES TO DIST DIRECTORY
|
|
# ============================================================================
|
|
|
|
# Create output directory if it doesn't exist
|
|
if (-not (Test-Path $outputDir)) {
|
|
New-Item -ItemType Directory -Path $outputDir | Out-Null
|
|
}
|
|
|
|
# Copy all built files to dist directory
|
|
Copy-Item $tempOutputFile -Destination (Join-Path $outputDir "index.html") -Force
|
|
Copy-Item $tempBlogOutputFile -Destination (Join-Path $outputDir "blog.html") -Force
|
|
Copy-Item "styles.css" -Destination $outputDir -Force
|
|
|
|
# Copy images directory recursively
|
|
if (Test-Path "images") {
|
|
Copy-Item "images" -Destination $outputDir -Recurse -Force
|
|
}
|
|
|
|
# Clean up temporary files created during build
|
|
Remove-Item $tempOutputFile -Force -ErrorAction SilentlyContinue
|
|
Remove-Item $tempBlogOutputFile -Force -ErrorAction SilentlyContinue
|
|
|
|
Write-Host "Files copied to dist directory"
|
|
Write-Host "Build complete! Website is ready in: $outputDir"
|
|
|