From 35a252299e099f8450117f886b42e9da46ee9942 Mon Sep 17 00:00:00 2001 From: FatimaSami <84532632+fatima-Sami55@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:10:09 +0500 Subject: [PATCH] fix: ensure sitemap.xml emits a single XML declaration Google Search Console rejects duplicated prologs that browsers still accept. Strip any declarations, emit exactly one, validate , and fail the build if the invariant breaks. --- public/sitemap.xml | 64 ++++++++++++++++++------------------ scripts/sitemap-generator.js | 29 +++++++++++++--- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/public/sitemap.xml b/public/sitemap.xml index 2d85e11..7c7b036 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,193 +2,193 @@ https://physicshub.github.io/ - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 1.0 https://physicshub.github.io/contribute - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/simulations - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.9 https://physicshub.github.io/about - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.7 https://physicshub.github.io/blog - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.9 https://physicshub.github.io/blog/create - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.7 https://physicshub.github.io/blog/what-is-physics - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/class-12-physics-complete-guide - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/physics-bouncing-ball-comprehensive-educational-guide - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/comprehensive-guide-to-vector-operations - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/ball-uniformly-accelerated-motion - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/ball-free-fall-comprehensive-guide - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/spring-connection - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/physics-of-pendulum-explained - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/projectile-parabolic-motion - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/physics-behind-three-body-problem - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/blog/pi-from-block-collisions-explained - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z monthly 0.8 https://physicshub.github.io/simulations/BouncingBall - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/VectorsOperations - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/BallAcceleration - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/BallGravity - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/SpringConnection - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/SimplePendulum - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/ParabolicMotion - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/InclinedPlane - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/CircularMotion - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/ThreeBody - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/HorizontalSpring - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/DoublePendulum - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/CollisionSimulation - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/test - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 https://physicshub.github.io/simulations/PiCollisions - 2026-07-21T00:00:00.000Z + 2026-07-27T00:00:00.000Z weekly 0.7 diff --git a/scripts/sitemap-generator.js b/scripts/sitemap-generator.js index ce435cc..d8cfc9e 100644 --- a/scripts/sitemap-generator.js +++ b/scripts/sitemap-generator.js @@ -74,14 +74,35 @@ async function generateSitemap() { const rawXml = (await streamToPromise(sitemap)).toString(); - // Add XML declaration and format + // Format only — SitemapStream already emits an XML declaration. + // A previous version prepended a second , which browsers + // tolerate but Google Search Console rejects as invalid XML (#107). const formatted = xmlFormat(rawXml, { indentation: " ", collapseContent: true, lineSeparator: "\n", }); - const xmlOutput = formatted; + // Guarantee exactly one XML declaration, even if a dependency starts + // emitting none or more than one. (GSC rejects duplicated prologs.) + const body = formatted.replace(/<\?xml[^?]*\?>\s*/gi, "").trimStart(); + if (!body.startsWith(" after stripping XML declarations" + ); + } + + let xmlOutput = `\n${body}`; + if (!xmlOutput.endsWith("\n")) { + xmlOutput += "\n"; + } + + const declarationCount = (xmlOutput.match(/<\?xml\b/gi) || []).length; + if (declarationCount !== 1) { + throw new Error( + `Sitemap must contain exactly one XML declaration, found ${declarationCount}` + ); + } // Ensure public directory exists const publicDir = join(__dirname, "../public"); @@ -91,13 +112,13 @@ async function generateSitemap() { // Write to public directory const publicPath = join(publicDir, `${sitemapName}.xml`); - writeFileSync(publicPath, xmlOutput); + writeFileSync(publicPath, xmlOutput, "utf8"); console.log(`✅ Sitemap generated with ${allRoutes.length} links in public/`); // Also write to out/ if it exists (for deployment consistency) const outDir = join(__dirname, "../out"); if (existsSync(outDir)) { - writeFileSync(join(outDir, `${sitemapName}.xml`), xmlOutput); + writeFileSync(join(outDir, `${sitemapName}.xml`), xmlOutput, "utf8"); console.log(`✅ Sitemap copied to ./out/`); }