Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

feat: broken link checker #181

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { defineConfig } from "astro/config";
import svelte from "@astrojs/svelte";
import mdx from "@astrojs/mdx";
import fs from "fs/promises";
import astroExpressiveCode from 'astro-expressive-code'
import path from "path";
import astroExpressiveCode from "astro-expressive-code";

// TODO(@maxu): Remove these once we have them migrated!
import react from "@astrojs/react";
import preact from "@astrojs/preact";
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import {generateIndexPages} from './src/generate-indexes'
import { generateIndexPages } from "./src/generate-indexes";
import { brokenLinkIntegration } from "./src/utils/brokenLinkIntegration.mjs";

const headingIcon = (node) => {
let e = new HTMLSpanElement();
Expand All @@ -19,7 +21,14 @@ const headingIcon = (node) => {

// https://astro.build/config
export default defineConfig({
integrations: [astroExpressiveCode(), mdx(), svelte(), preact(), react()],
integrations: [
astroExpressiveCode(),
mdx(),
svelte(),
preact(),
react(),
brokenLinkIntegration()
],
site: "https://deta.space/",
base: "/",
markdown: {
Expand All @@ -29,7 +38,7 @@ export default defineConfig({
]
},
experimental: {
viewTransitions: true
viewTransitions: true
},
vite: {
build: {
Expand Down
71 changes: 71 additions & 0 deletions src/utils/brokenLinkIntegration.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import fs from "fs/promises";
import path from "path";

/**
* Small astro integration to scan all generated pages links &
* match them with known routes to find (possibly) broken links.
* @returns
*/
export const brokenLinkIntegration = () => {
return {
name: "broken-link-checker",
hooks: {
"astro:build:done": async ({ pages }) => {
pages = pages.map(
(e) => `/${e.pathname.endsWith("/") ? e.pathname.slice(0, -1) : e.pathname}`
);
// https://deta.space/docs/en/build/fundamentals/app-lifecycle


console.log("Results for possibly broken links:");

const searchPath = "./dist/docs";

let srcFiles = [];
async function getFiles(dir = "./") {
const entries = await fs.readdir(dir, { withFileTypes: true });

const files = entries
.filter((entry) => !entry.isDirectory())
.map((file) => ({ ...file, dir: dir + file.name }));

const folders = entries.filter((entry) => entry.isDirectory());
for (const folder of folders)
files.push(...(await getFiles(`${path.join(dir, folder.name)}`)));

return files;
}
srcFiles = (await getFiles(searchPath)).map((e) => path.join(e.path, e.name));

for (const srcFile of srcFiles) {
const content = await fs.readFile(srcFile, "utf-8");

const pageLinks = Array.from(
content.matchAll(/<a\s[^>]*\bhref="([^#"][^"]*)"/g),
(m) => m[1]
)
.filter((e) => !["/discovery", "/blog"].includes(e))
.filter(
(l) =>
l.startsWith("https://deta.space/docs") ||
(!l.startsWith("https") &&
!l.startsWith("http") &&
!l.startsWith("mailto") &&
!l.startsWith("tel") &&
!l.startsWith("data") &&
!l.startsWith("./"))
)
.map((l) => (l.startsWith("https://deta.space/docs") ? l.slice(18) : l))
.map((l) => (l.indexOf("#") > -1 ? l.slice(0, l.indexOf("#")) : l))
.map((l) => (l.endsWith("/") ? l.slice(0, -1) : l));

for (const pLink of pageLinks) {
if (!pages.includes(pLink)) {
console.log(` - ${pLink} in ${srcFile}`);
}
}
}
}
}
};
};