152 lines
5.0 KiB
JavaScript
Executable File
152 lines
5.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* update-nav-from-sitemap.js
|
|
*
|
|
* This script reads the sitemap.xml file and updates the navigation menu in header.html
|
|
* to include all story pages found in the sitemap.
|
|
*
|
|
* No external dependencies required.
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Configuration
|
|
const SITEMAP_PATH = path.join(__dirname, 'sitemap.xml');
|
|
const HEADER_PATH = path.join(__dirname, 'includes', 'header.html');
|
|
const STORIES_PATH_PREFIX = '/stories/';
|
|
const STORIES_PATH_EXCLUDE = ['index.html', 'story-with-includes.html', 'template-story.html'];
|
|
|
|
// Helper function to get a friendly name from a filename
|
|
function getFriendlyName(filename) {
|
|
// Remove .html extension
|
|
let name = filename.replace('.html', '');
|
|
|
|
// Replace hyphens with spaces
|
|
name = name.replace(/-/g, ' ');
|
|
|
|
// Capitalize first letter of each word
|
|
return name.split(' ')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
// Simple XML parser for sitemap (no external dependencies)
|
|
function extractUrlsFromSitemap(sitemapContent) {
|
|
const urls = [];
|
|
const matches = sitemapContent.match(/<loc>([^<]+)<\/loc>/g) || [];
|
|
|
|
for (const match of matches) {
|
|
const url = match.replace('<loc>', '').replace('</loc>', '');
|
|
urls.push(url);
|
|
}
|
|
|
|
return urls;
|
|
}
|
|
|
|
// Main function
|
|
async function updateNavFromSitemap() {
|
|
console.log('Updating navigation menu from sitemap.xml...');
|
|
|
|
try {
|
|
// Read sitemap.xml
|
|
const sitemapContent = fs.readFileSync(SITEMAP_PATH, 'utf8');
|
|
const urls = extractUrlsFromSitemap(sitemapContent);
|
|
|
|
// Extract story URLs from sitemap
|
|
const storyPages = [];
|
|
|
|
for (const url of urls) {
|
|
// Check if this is a story page
|
|
if (url.includes(STORIES_PATH_PREFIX)) {
|
|
const filename = url.split('/').pop();
|
|
|
|
// Skip excluded files
|
|
if (!STORIES_PATH_EXCLUDE.includes(filename)) {
|
|
const filePath = path.join(__dirname, 'stories', filename);
|
|
let isWritten = true;
|
|
|
|
try {
|
|
if (fs.existsSync(filePath)) {
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
// Check if the file contains the placeholder notice
|
|
if (content.includes('class="placeholder-notice"') || content.includes("class='placeholder-notice'")) {
|
|
isWritten = false;
|
|
}
|
|
} else {
|
|
console.warn(`Warning: File not found: ${filePath}`);
|
|
}
|
|
} catch (err) {
|
|
console.warn(`Error reading file ${filePath}: ${err.message}`);
|
|
}
|
|
|
|
storyPages.push({
|
|
filename,
|
|
url: STORIES_PATH_PREFIX + filename,
|
|
name: getFriendlyName(filename),
|
|
isWritten
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort story pages alphabetically by name
|
|
storyPages.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
// Read header.html
|
|
let headerContent = fs.readFileSync(HEADER_PATH, 'utf8');
|
|
|
|
// Find the stories dropdown content
|
|
const dropdownStartMarker = '<a href="/stories/" id="nav-stories">Stories</a>';
|
|
const dropdownEndMarker = '</div>';
|
|
|
|
const dropdownStartIndex = headerContent.indexOf(dropdownStartMarker);
|
|
if (dropdownStartIndex === -1) {
|
|
throw new Error('Could not find stories dropdown in header.html');
|
|
}
|
|
|
|
// Find the start of the dropdown content
|
|
const contentStartIndex = headerContent.indexOf('<div class="dropdown-content">', dropdownStartIndex);
|
|
if (contentStartIndex === -1) {
|
|
throw new Error('Could not find dropdown content in header.html');
|
|
}
|
|
|
|
// Find the end of the dropdown content
|
|
const contentEndIndex = headerContent.indexOf(dropdownEndMarker, contentStartIndex);
|
|
if (contentEndIndex === -1) {
|
|
throw new Error('Could not find end of dropdown content in header.html');
|
|
}
|
|
|
|
// Generate new dropdown content
|
|
let newDropdownContent = '<div class="dropdown-content">\n';
|
|
storyPages.forEach(page => {
|
|
const navId = 'nav-' + page.filename.replace('.html', '').replace(/-/g, '');
|
|
const className = page.isWritten ? 'nav-story-written' : 'nav-story-tbd';
|
|
newDropdownContent += ` <a href="${page.url}" id="${navId}" class="${className}">${page.name}</a>\n`;
|
|
});
|
|
|
|
// Replace dropdown content in header.html
|
|
const newHeaderContent =
|
|
headerContent.substring(0, contentStartIndex) +
|
|
newDropdownContent +
|
|
' ' + dropdownEndMarker +
|
|
headerContent.substring(contentEndIndex + dropdownEndMarker.length);
|
|
|
|
// Write updated header.html
|
|
fs.writeFileSync(HEADER_PATH, newHeaderContent, 'utf8');
|
|
|
|
console.log(`Updated navigation menu with ${storyPages.length} story pages`);
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error updating navigation menu:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Execute if run directly
|
|
if (require.main === module) {
|
|
updateNavFromSitemap();
|
|
}
|
|
|
|
module.exports = { updateNavFromSitemap };
|