diff --git a/src/controllers/sitemapControllers.js b/src/controllers/sitemapControllers.js index 5a36562..02f44ee 100644 --- a/src/controllers/sitemapControllers.js +++ b/src/controllers/sitemapControllers.js @@ -12,7 +12,6 @@ const xmlTpl = Handlebars.compile(xmlTplSrc); async function getSitemapHtml(req, res, next) { - re; try { const sitemap = await sitemapService.getCompleteSitemap(); const context = { diff --git a/src/services/sitemapService.js b/src/services/sitemapService.js index ba33660..2cd0abc 100644 --- a/src/services/sitemapService.js +++ b/src/services/sitemapService.js @@ -46,37 +46,37 @@ } } - async getStaticPages() { - try { - const filenames = await fs.readdir(this.pagesPath); - const pages = []; + // async getStaticPages() { + // try { + // const filenames = await fs.readdir(this.pagesPath); + // const pages = []; - for (const file of filenames) { - const fullPath = path.join(this.pagesPath, file); - const stat = await fs.stat(fullPath); - if (stat.isDirectory()) continue; + // for (const file of filenames) { + // const fullPath = path.join(this.pagesPath, file); + // const stat = await fs.stat(fullPath); + // if (stat.isDirectory()) continue; - const raw = await fs.readFile(fullPath, "utf8"); - const { data: frontmatter } = matter(raw); + // const raw = await fs.readFile(fullPath, "utf8"); + // const { data: frontmatter } = matter(raw); - if (!frontmatter.published) continue; + // if (!frontmatter.published) continue; - pages.push({ - id: hash(frontmatter), - loc: `/${frontmatter.slug || file.replace(/\.(md|mdx|handlebars)$/, "")}`, - title: frontmatter.title || "", - lastmod: frontmatter.updated || frontmatter.date || null, - changefreq: "monthly", - priority: 0.7, - }); - } + // pages.push({ + // id: hash(frontmatter), + // loc: `/${frontmatter.slug || file.replace(/\.(md|mdx|handlebars)$/, "")}`, + // title: frontmatter.title || "", + // lastmod: frontmatter.updated || frontmatter.date || null, + // changefreq: "monthly", + // priority: 0.7, + // }); + // } - return pages; - } catch (err) { - console.warn("Failed to load static pages:", err); - return []; - } - } + // return pages; + // } catch (err) { + // console.warn("Failed to load static pages:", err); + // return []; + // } + // } async getAllTags() { const tagMap = new Map(); diff --git a/src/services/tagsService.js b/src/services/tagsService.js index 6599da6..dbd9a31 100644 --- a/src/services/tagsService.js +++ b/src/services/tagsService.js @@ -3,17 +3,61 @@ const matter = require("gray-matter"); const { glob } = require("glob"); const createExcerpt = require("../utils/createExcerpt"); -const { winstonLogger } = require("../utils/createExcerpt"); +const { winstonLogger } = require("../utils/logging"); const CONTENT_ROOT = path.resolve(__dirname, "../../content"); const pattern = `${CONTENT_ROOT}/**/*.md`; -const buildTagRegex = (tag) => - new RegExp(`^${tag.replace(/[-\s]/g, "[-\\s]")}$`, "i"); +/** + * Escapes special characters and handles flexible hyphen/space matching + * @param {string} tag - The raw tag (e.g., "c++") + * @returns {RegExp} + */ +const buildTagRegex = (tag) => { + // 1. Escape all special regex characters (.*+?^${}()|[\]\) + const escaped = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + // 2. Apply your existing logic to allow flexible hyphens/spaces + const flexible = escaped.replace(/[-\s]/g, "[-\\s]"); + + return new RegExp(`^${flexible}$`, "i"); +}; const hash = require("../utils/hash"); const sitemapService = require("../services/sitemapService"); +// async function getPostsByTag(tag) { +// const allUrls = await sitemapService.getAllUrls(); +// const files = await glob(pattern); +// const tagRegex = buildTagRegex(tag); + +// const matchedPosts = []; + +// for (const filePath of files) { +// try { +// const raw = await fs.readFile(filePath, "utf-8"); +// const { data: frontmatter, content } = matter(raw); +// const fileHash = hash(frontmatter); + +// if (frontmatter.published !== true) continue; +// if (!Array.isArray(frontmatter.tags)) continue; +// if (!frontmatter.tags.some((t) => tagRegex.test(t))) continue; + +// const urlMatches = allUrls.find((url) => url.id == fileHash); +// matchedPosts.push({ +// title: frontmatter.title || "Untitled", +// loc: urlMatches.loc, +// date: frontmatter.date || null, +// excerpt: createExcerpt(content, 200), +// }); +// } catch (e) { +// // Prevent the entire route from going down due to one bad file +// winstonLogger.error("File path", filePath, e.stack); +// } +// } + +// return matchedPosts.sort((a, b) => new Date(b.date) - new Date(a.date)); +// } async function getPostsByTag(tag) { const allUrls = await sitemapService.getAllUrls(); const files = await glob(pattern); @@ -25,22 +69,30 @@ try { const raw = await fs.readFile(filePath, "utf-8"); const { data: frontmatter, content } = matter(raw); - const fileHash = hash(frontmatter); if (frontmatter.published !== true) continue; if (!Array.isArray(frontmatter.tags)) continue; if (!frontmatter.tags.some((t) => tagRegex.test(t))) continue; + const fileHash = hash(frontmatter); + + // Look for the URL by hash const urlMatches = allUrls.find((url) => url.id == fileHash); + + // FALLBACK: If hash lookup fails, construct a temporary loc from slug + // This prevents the 404 if the sitemap hasn't refreshed the hash + const loc = urlMatches + ? urlMatches.loc + : `/${frontmatter.slug || path.basename(filePath, ".md")}`; + matchedPosts.push({ title: frontmatter.title || "Untitled", - loc: urlMatches.loc, + loc: loc, date: frontmatter.date || null, excerpt: createExcerpt(content, 200), }); } catch (e) { - // Prevent the entire route from going down due to one bad file - winstonLogger.error("File path", filePath, e.stack); + winstonLogger.error(`Error processing ${filePath}: ${e.message}`); } } diff --git a/src/utils/qualifyLinks.js b/src/utils/qualifyLinks.js index 5334879..6c2cca5 100644 --- a/src/utils/qualifyLinks.js +++ b/src/utils/qualifyLinks.js @@ -1,5 +1,8 @@ const { baseUrl } = require("../utils/baseUrl"); +const isDisabled = (item) => item?.disabled ?? false; +const isEnabled = (item) => !isDisabled(item); + function qualifyLink(href) { if (!href) return href; // Return unchanged if href is absolute URL or protocol-relative @@ -8,20 +11,8 @@ return baseUrl + href; } -function qualifyNavLinks(links) { - return links.map((link) => { - const qualified = { ...link }; - if (qualified.href) { - qualified.href = qualifyLink(qualified.href); - } - if (qualified.submenu) { - qualified.submenu = qualifyNavLinks(qualified.submenu); - } - return qualified; - }); -} const mapMenuTree = (links, transformFn) => { - return links.map((link) => { + return links.filter(isEnabled).map((link) => { const processed = transformFn({ ...link }); if (processed.submenu) { processed.submenu = mapMenuTree(processed.submenu, transformFn); @@ -40,7 +31,7 @@ } function qualifySitemapLinks(links) { - return links.map((item) => { + return links.filter(isEnabled).map((item) => { const qualified = { ...item }; if (typeof qualified.loc === "string") { @@ -55,4 +46,9 @@ }); } -module.exports = { qualifyNavLinks, qualifySitemapLinks, qualifyLink }; +module.exports = { + qualifyNavLinks, + mapMenuTree, + qualifySitemapLinks, + qualifyLink, +};