From 61c4df932e4eaaced20866846e06524ea1dd7530 Mon Sep 17 00:00:00 2001 From: gujishh Date: Fri, 14 Aug 2026 16:35:23 +0900 Subject: [PATCH] fix(docs): normalize example link paths on Windows Normalize platform-specific separators before applying the slash-based example mapping so test:docs validates framework example links consistently on Windows. Add a cross-platform integration regression for the example link path. --- scripts/verify-links.test.ts | 32 ++++++++++++++++++++++++++++++++ scripts/verify-links.ts | 13 +++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 scripts/verify-links.test.ts diff --git a/scripts/verify-links.test.ts b/scripts/verify-links.test.ts new file mode 100644 index 00000000000..224570eff77 --- /dev/null +++ b/scripts/verify-links.test.ts @@ -0,0 +1,32 @@ +import { strict as assert } from 'node:assert' +import { execFileSync } from 'node:child_process' +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { test } from 'node:test' + +const verifyLinks = fileURLToPath(new URL('./verify-links.ts', import.meta.url)) + +test('accepts framework example links on every platform', () => { + const fixture = mkdtempSync(join(tmpdir(), 'tanstack-query-verify-links-')) + + try { + mkdirSync(join(fixture, 'docs/framework/react'), { recursive: true }) + mkdirSync(join(fixture, 'examples/react/basic'), { recursive: true }) + writeFileSync( + join(fixture, 'docs/framework/react/quick-start.md'), + '[Basic example](./examples/basic)\n', + ) + + const output = execFileSync( + process.execPath, + ['--experimental-strip-types', verifyLinks], + { cwd: fixture, encoding: 'utf8' }, + ) + + assert.match(output, /No broken links found/) + } finally { + rmSync(fixture, { recursive: true, force: true }) + } +}) diff --git a/scripts/verify-links.ts b/scripts/verify-links.ts index 8a1c40ddc51..0ad4a312413 100644 --- a/scripts/verify-links.ts +++ b/scripts/verify-links.ts @@ -1,5 +1,5 @@ import { existsSync, readFileSync, statSync } from 'node:fs' -import { extname, resolve } from 'node:path' +import { extname, resolve, sep } from 'node:path' import { glob } from 'tinyglobby' // @ts-ignore Could not find a declaration file for module 'markdown-link-extractor'. import markdownLinkExtractor from 'markdown-link-extractor' @@ -27,6 +27,10 @@ function stripExtension(p: string): string { return p.replace(`${extname(p)}`, '') } +export function normalizePathForMatching(path: string): string { + return path.split(sep).join('/') +} + function relativeLinkExists(link: string, file: string): boolean { // Remove hash if present const linkWithoutHash = link.split('#')[0] @@ -53,14 +57,15 @@ function relativeLinkExists(link: string, file: string): boolean { return false } - // Check if this is an example path - const isExample = absPath.includes('/examples/') + // Normalize only the path used by the slash-based example mapping. + const normalizedAbsPath = normalizePathForMatching(absPath) + const isExample = normalizedAbsPath.includes('/examples/') let exists = false if (isExample) { // Transform /docs/framework/{framework}/examples/ to /examples/{framework}/ - absPath = absPath.replace( + absPath = normalizedAbsPath.replace( /\/docs\/framework\/([^/]+)\/examples\//, '/examples/$1/', )