ReviewOS

also looking at this

stacks/bunpress

fix: name the document a refused Markdown link is in, and report all of them

#95
Open glennmichael123 wants to merge fix/collect-every-refused-link into main
3 files +195 -7
packages/bunpress/bin/cli.tsmodified+25-2
Changes to packages/bunpress/bin/cli.ts
@@ -13,7 +13,8 @@ import { generateRssFeed } from '../src/rss'
1313import { buildSearchIndex, SEARCH_INDEX_PATH } from '../src/search-index'
1414import { generateSitemap } from '../src/sitemap'
1515import { siteUrl } from '../src/site-url'
16import { createMarkdownPageCatalog, findMarkdownFiles } from '../src/markdown-pages'
16import type { MarkdownLinkFailure } from '../src/markdown-pages'
17import { createMarkdownPageCatalog, describeLinkFailures, findMarkdownFiles } from '../src/markdown-pages'
1718import { withBuildOutput } from '../src/build-output'
1819import { verifyBuildManifest, writeBuildManifest } from '../src/build-manifest'
1920import { cleanCommand } from './commands/clean'
@@ -224,11 +225,28 @@ export async function buildDocs(options: CliOption = {}): Promise<boolean> {
224225 const catalog = createMarkdownPageCatalog(markdownFiles, docsDir, i18n)
225226 const renderTargets = catalog.pages
226227
228 /*
229 * Every refused link, not the first one.
230 *
231 * Throwing on the first meant a documentation set with N dead links took
232 * N builds to clean up: fix one, rebuild, learn the next. Each run is a
233 * full build, and the count is unknowable until the last one passes.
234 * That is felt most on the first build after adopting the check, when a
235 * set that has never been link-checked is checked all at once.
236 *
237 * Collecting changes nothing about what is published. The throw below
238 * still happens inside `withBuildOutput`, before anything is moved into
239 * place, so a build with a dead link still refuses and still leaves the
240 * previous complete output alone.
241 */
242 const linkFailures: MarkdownLinkFailure[] = []
243 const collectLinkFailures = (failure: MarkdownLinkFailure): void => { linkFailures.push(failure) }
244
227245 for (const target of renderTargets) {
228246 const markdown = await Bun.file(target.sourceFile).text()
229247
230248 // Convert markdown to HTML (handles frontmatter, hero, features, etc.)
231 const { html, frontmatter } = await markdownToHtml(markdown, docsDir, { page: target, catalog })
249 const { html, frontmatter } = await markdownToHtml(markdown, docsDir, { page: target, catalog, collectLinkFailures })
232250
233251 const relativePath = target.relativePath
234252 const currentPath = `/${relativePath}`
@@ -267,6 +285,11 @@ export async function buildDocs(options: CliOption = {}): Promise<boolean> {
267285 }
268286 }
269287
288 // Reported together, and only once every page has had its turn, so the
289 // list is the whole truth rather than the first thing encountered.
290 if (linkFailures.length > 0)
291 throw new Error(describeLinkFailures(linkFailures))
292
270293 if (verbose) {
271294 console.log(`Processed ${markdownFiles.length} markdown files into ${renderTargets.length} pages.`)
272295 }