Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion docs/framework/react/guides/prefetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ Because data fetching in the component tree itself can easily lead to request wa

In this approach, you explicitly declare for each _route_ what data is going to be needed for that component tree, ahead of time. Because Server Rendering has traditionally needed all data to be loaded before rendering starts, this has been the dominating approach for SSR'd apps for a long time. This is still a common approach and you can read more about it in the [Server Rendering & Hydration guide](../ssr.md).

For now, let's focus on the client side case and look at an example of how you can make this work with [Tanstack Router](https://tanstack.com/router). These examples leave out a lot of setup and boilerplate to stay concise, you can check out a [full React Query example](https://tanstack.com/router/.latest/docs/framework/react/examples/basic-react-query-file-based) over in the [Tanstack Router docs](https://tanstack.com/router/latest/docs).
For now, let's focus on the client side case and look at an example of how you can make this work with [Tanstack Router](https://tanstack.com/router). These examples leave out a lot of setup and boilerplate to stay concise, you can check out a [full React Query example](../examples/basic-react-query-file-based) over in the [Tanstack Router docs](https://tanstack.com/router/latest/docs).

When integrating at the router level, you can choose to either _block_ rendering of that route until all data is present, or you can start a prefetch but not await the result. That way, you can start rendering the route as soon as possible. You can also mix these two approaches and await some critical data, but start rendering before all the secondary data has finished loading. In this example, we'll configure an `/article` route to not render until the article data has finished loading, as well as start prefetching comments as soon as possible, but not block rendering the route if comments haven't finished loading yet.

Expand Down
2 changes: 1 addition & 1 deletion docs/framework/solid/guides/prefetching.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ replace:
'useQuery[(]': 'useQuery(() => ',
'useQueries[(]': 'useQueries(() => ',
'useInfiniteQuery[(]': 'useInfiniteQuery(() => ',
'/docs/framework/react/examples/basic-react-query-file-based': '/docs/framework/solid/examples/basic-solid-query-file-based',
'../examples/basic-react-query-file-based': '../examples/basic-solid-query-file-based',
}
---
85 changes: 44 additions & 41 deletions scripts/verify-links.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { existsSync, readFileSync, statSync } from 'node:fs'
import path, { resolve } from 'node:path'
import { extname, resolve } 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'

const errors: Array<{
file: string
link: string
resolvedPath: string
reason: string
}> = []

function isRelativeLink(link: string) {
return (
link &&
!link.startsWith(`/`) &&
!link.startsWith('http://') &&
!link.startsWith('https://') &&
!link.startsWith('//') &&
Expand All @@ -15,39 +22,33 @@ function isRelativeLink(link: string) {
)
}

function normalizePath(p: string): string {
// Remove any trailing .md
p = p.replace(`${path.extname(p)}`, '')
return p
/** Remove any trailing .md */
function stripExtension(p: string): string {
return p.replace(`${extname(p)}`, '')
}

function fileExistsForLink(
link: string,
markdownFile: string,
errors: Array<any>,
): boolean {
function relativeLinkExists(link: string, file: string): boolean {
// Remove hash if present
const filePart = link.split('#')[0]
const linkWithoutHash = link.split('#')[0]
// If the link is empty after removing hash, it's not a file
if (!filePart) return false

// Normalize the markdown file path
markdownFile = normalizePath(markdownFile)
if (!linkWithoutHash) return false

// Normalize the path
const normalizedPath = normalizePath(filePart)
// Strip the file/link extensions
const filePath = stripExtension(file)
const linkPath = stripExtension(linkWithoutHash)

// Resolve the path relative to the markdown file's directory
let absPath = resolve(markdownFile, normalizedPath)
// Nav up a level to simulate how links are resolved on the web
let absPath = resolve(filePath, '..', linkPath)

// Ensure the resolved path is within /docs
const docsRoot = resolve('docs')
if (!absPath.startsWith(docsRoot)) {
errors.push({
link,
markdownFile,
file,
resolvedPath: absPath,
reason: 'navigates above /docs, invalid',
reason: 'Path outside /docs',
})
return false
}
Expand Down Expand Up @@ -76,42 +77,44 @@ function fileExistsForLink(
if (!exists) {
errors.push({
link,
markdownFile,
file,
resolvedPath: absPath,
reason: 'not found',
reason: 'Not found',
})
}
return exists
}

async function findMarkdownLinks() {
async function verifyMarkdownLinks() {
// Find all markdown files in docs directory
const markdownFiles = await glob('docs/**/*.md', {
ignore: ['**/node_modules/**'],
})

console.log(`Found ${markdownFiles.length} markdown files\n`)

const errors: Array<any> = []

// Process each file
for (const file of markdownFiles) {
const content = readFileSync(file, 'utf-8')
const links: Array<any> = markdownLinkExtractor(content)

const filteredLinks = links.filter((link: any) => {
if (typeof link === 'string') {
return isRelativeLink(link)
} else if (link && typeof link.href === 'string') {
return isRelativeLink(link.href)
}
return false
const links: Array<string> = markdownLinkExtractor(content)

const relativeLinks = links.filter((link: string) => {
return isRelativeLink(link)
})

if (filteredLinks.length > 0) {
filteredLinks.forEach((link) => {
const href = typeof link === 'string' ? link : link.href
fileExistsForLink(href, file, errors)
if (relativeLinks.length > 0) {
relativeLinks.forEach((link) => {
if (!link.startsWith('.')) {
errors.push({
link,
file,
resolvedPath: '',
reason: 'Does not start with ./ or ../',
})
return
}

relativeLinkExists(link, file)
})
}
}
Expand All @@ -120,7 +123,7 @@ async function findMarkdownLinks() {
console.log(`\n❌ Found ${errors.length} broken links:`)
errors.forEach((err) => {
console.log(
`${err.link}\n in: ${err.markdownFile}\n path: ${err.resolvedPath}\n why: ${err.reason}\n`,
`${err.file}\n link: ${err.link}\n resolved: ${err.resolvedPath}\n why: ${err.reason}\n`,
)
})
process.exit(1)
Expand All @@ -129,4 +132,4 @@ async function findMarkdownLinks() {
}
}

findMarkdownLinks().catch(console.error)
verifyMarkdownLinks().catch(console.error)
Loading