Some checks failed
🚀 Build & Deploy / 🔍 Prepare (push) Successful in 8s
Nightly QA / call-qa-workflow (push) Failing after 1m34s
🚀 Build & Deploy / 🧪 QA (push) Has been cancelled
🚀 Build & Deploy / 🚀 Deploy (push) Has been cancelled
🚀 Build & Deploy / 🏗️ Build (push) Has been cancelled
🚀 Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
🚀 Build & Deploy / 🔔 Notify (push) Has been cancelled
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import axios from "axios";
|
|
import * as cheerio from "cheerio";
|
|
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import { execSync } from "child_process";
|
|
|
|
const targetUrl =
|
|
process.argv[2] ||
|
|
process.env.NEXT_PUBLIC_BASE_URL ||
|
|
"http://localhost:3000";
|
|
const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || "mbgrid";
|
|
|
|
async function main() {
|
|
console.log(`\n🚀 Starting HTML Validation for: ${targetUrl}`);
|
|
console.log(`📊 Limit: None (Full Sitemap)\n`);
|
|
|
|
try {
|
|
const sitemapUrl = `${targetUrl.replace(/\/$/, "")}/sitemap.xml`;
|
|
console.log(`📥 Fetching sitemap from ${sitemapUrl}...`);
|
|
|
|
const response = await axios.get(sitemapUrl, {
|
|
headers: { Cookie: `mintel_gatekeeper_session=${gatekeeperPassword}` },
|
|
validateStatus: (status) => status < 400,
|
|
});
|
|
|
|
const $ = cheerio.load(response.data, { xmlMode: true });
|
|
let urls = $("url loc")
|
|
.map((i, el) => $(el).text())
|
|
.get();
|
|
|
|
const urlPattern = /https?:\/\/[^\/]+/;
|
|
urls = [...new Set(urls)]
|
|
.filter((u) => u.startsWith("http"))
|
|
.map((u) => u.replace(urlPattern, targetUrl.replace(/\/$/, "")))
|
|
.sort();
|
|
|
|
console.log(`✅ Found ${urls.length} URLs in sitemap.`);
|
|
|
|
if (urls.length === 0) {
|
|
console.error("❌ No URLs found in sitemap. Is the site up?");
|
|
process.exit(1);
|
|
}
|
|
|
|
const outputDir = path.join(process.cwd(), ".htmlvalidate-tmp");
|
|
if (fs.existsSync(outputDir))
|
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
|
|
console.log(`📥 Fetching HTML for ${urls.length} pages...`);
|
|
for (let i = 0; i < urls.length; i++) {
|
|
const u = urls[i];
|
|
try {
|
|
const res = await axios.get(u, {
|
|
headers: {
|
|
Cookie: `mintel_gatekeeper_session=${gatekeeperPassword}`,
|
|
},
|
|
validateStatus: (status) => status < 400,
|
|
});
|
|
|
|
// Generate a safe filename that retains URL information
|
|
const urlStr = new URL(u);
|
|
const safePath = (urlStr.pathname + urlStr.search).replace(
|
|
/[^a-zA-Z0-9]/g,
|
|
"_",
|
|
);
|
|
const filename = `${safePath || "index"}.html`;
|
|
|
|
fs.writeFileSync(path.join(outputDir, filename), res.data);
|
|
} catch (err: unknown) {
|
|
const errorBody = err instanceof Error ? err.message : String(err);
|
|
console.error(`❌ HTTP Error fetching ${u}: ${errorBody}`);
|
|
throw new Error(`Failed to fetch page: ${u} - ${errorBody}`);
|
|
}
|
|
}
|
|
|
|
console.log(`\n💻 Executing html-validate...`);
|
|
try {
|
|
execSync(`npx html-validate .htmlvalidate-tmp/*.html`, {
|
|
stdio: "inherit",
|
|
});
|
|
console.log(`✅ HTML Validation passed perfectly!`);
|
|
} catch {
|
|
console.error(`❌ HTML Validation found issues.`);
|
|
process.exit(1);
|
|
}
|
|
} catch (error: unknown) {
|
|
const errorBody = error instanceof Error ? error.message : String(error);
|
|
console.error(`\n❌ Error during HTML Validation:`, errorBody);
|
|
process.exit(1);
|
|
} finally {
|
|
const outputDir = path.join(process.cwd(), ".htmlvalidate-tmp");
|
|
if (fs.existsSync(outputDir))
|
|
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
main();
|