Skip to content

Commit a090d0f

Browse files
authored
Merge pull request #2749 from TiberiusNemesis/docs/tanstack-query-router
feat(tanstack): add TanStack Query and TanStack Router documentation
2 parents ad6325c + 0c9673f commit a090d0f

12 files changed

Lines changed: 202 additions & 0 deletions

File tree

‎assets/javascripts/news.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
[
2+
[
3+
"2026-10-09",
4+
"New documentations: <a href=\"/tanstack_query/\">TanStack Query</a>, <a href=\"/tanstack_router/\">TanStack Router</a>"
5+
],
26
[
37
"2026-09-04",
48
"New documentation: <a href=\"/valibot/\">Valibot</a>"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
module Docs
2+
class Tanstack
3+
class CleanHtmlFilter < Filter
4+
LANGUAGES = {
5+
'tsx' => 'jsx',
6+
'ts' => 'typescript',
7+
'js' => 'javascript',
8+
'shell' => 'bash',
9+
'sh' => 'bash',
10+
'zsh' => 'bash'
11+
}
12+
13+
def call
14+
title = at_css('h1')
15+
@doc = at_css('.prose')
16+
doc.prepend_child(title)
17+
18+
css('.anchor-heading-link', 'button').remove
19+
20+
css('.codeblock').each do |node|
21+
pre = node.at_css('pre')
22+
label = node.at_css('> div:first-child > div').try(:content).try(:strip)
23+
if label.present? && label != pre['data-language']
24+
node.before(%(<div class="_pre-heading">#{CGI.escapeHTML(label)}</div>))
25+
end
26+
node.replace(pre)
27+
end
28+
29+
css('pre').each do |node|
30+
node.content = node.content
31+
node.remove_attribute('class')
32+
node['data-language'] = LANGUAGES[node['data-language']] if LANGUAGES.key?(node['data-language'])
33+
end
34+
35+
css('span.border.rounded[class~="bg-gray-500/10"]').each do |node|
36+
node.name = 'code'
37+
node.remove_attribute('class')
38+
end
39+
40+
css('.markdown-alert').each do |node|
41+
node['class'] = '_note'
42+
node.at_css('.markdown-alert-title').try(:name=, 'strong')
43+
end
44+
45+
css('input[type="checkbox"]').each do |node|
46+
node.replace(node['checked'] ? '☑' : '☐')
47+
end
48+
49+
# Strip Tailwind classes and tab widget attributes, then unwrap the bare layout divs
50+
css('*').each do |node|
51+
node.remove_attribute('class') unless node['class'].try(:start_with?, '_')
52+
node.remove_attribute('id') if node['id'].try(:start_with?, ':')
53+
node.attributes.each_key { |name| node.remove_attribute(name) if name.start_with?('aria-', 'data-tab', 'data-content') || name == 'role' }
54+
end
55+
56+
css('div:not([class])').each do |node|
57+
node.replace(node.children)
58+
end
59+
60+
doc
61+
end
62+
end
63+
end
64+
end
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
module Docs
2+
class Tanstack
3+
class EntriesFilter < Docs::EntriesFilter
4+
TYPES_BY_PREFIX = {
5+
'reference/functions/' => 'Functions',
6+
'reference/classes/' => 'Classes',
7+
'reference/interfaces/' => 'Interfaces',
8+
'reference/type-aliases/' => 'Type Aliases',
9+
'reference/variables/' => 'Variables',
10+
'guides/' => 'Guides',
11+
'guide/' => 'Guides',
12+
'plugins/' => 'Plugins',
13+
'installation/' => 'Installation',
14+
'routing/' => 'Routing',
15+
'integrations/' => 'Integrations',
16+
'how-to/' => 'How-to',
17+
'eslint/' => 'ESLint'
18+
}
19+
20+
# Router symbol pages are titled "useNavigate hook", "Link component", etc.
21+
SYMBOL_TYPES = {
22+
'hook' => 'Hooks',
23+
'function' => 'Functions',
24+
'component' => 'Components',
25+
'type' => 'Types',
26+
'class' => 'Classes',
27+
'interface' => 'Interfaces'
28+
}
29+
30+
GETTING_STARTED = %w(installation quick-start devtools faq comparison decisions-on-dx typescript graphql react-native)
31+
32+
def get_name
33+
return 'API Reference' if page_path == 'reference/index'
34+
name = heading_text(at_css('h1'))
35+
symbol_type ? name.sub(/\s+\S+\z/, '') : name
36+
end
37+
38+
def get_type
39+
return 'API Reference' if page_path == 'reference/index'
40+
return symbol_type || 'API' if page_path.start_with?('api/')
41+
TYPES_BY_PREFIX.each { |prefix, type| return type if page_path.start_with?(prefix) }
42+
GETTING_STARTED.include?(page_path) ? 'Getting Started' : 'Other'
43+
end
44+
45+
def additional_entries
46+
return [] unless page_path.start_with?('guides/', 'guide/')
47+
48+
css('.prose h2[id]').each_with_object([]) do |node, entries|
49+
text = heading_text(node)
50+
entries << ["#{name}: #{text}", node['id']] unless text.empty? || text.casecmp?('Further Reading')
51+
end
52+
end
53+
54+
private
55+
56+
def page_path
57+
@page_path ||= slug.remove(%r{\Aframework/react/})
58+
end
59+
60+
def symbol_type
61+
return unless page_path.start_with?('api/router/')
62+
words = heading_text(at_css('h1')).split
63+
SYMBOL_TYPES[words.last.downcase] if words.length > 1
64+
end
65+
66+
def heading_text(node)
67+
node.content.remove('#').squish
68+
end
69+
end
70+
end
71+
end

‎lib/docs/scrapers/tanstack.rb‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module Docs
2+
class Tanstack < UrlScraper
3+
self.abstract = true
4+
self.type = 'simple'
5+
6+
html_filters.push 'tanstack/entries', 'tanstack/clean_html'
7+
8+
options[:attribution] = <<-HTML
9+
&copy; 2021-present Tanner Linsley<br>
10+
Licensed under the MIT License.
11+
HTML
12+
13+
private
14+
15+
# tanstack.com sometimes stalls a connection, which hangs the crawl without a timeout
16+
def request_options
17+
super.merge(timeout: 60, connecttimeout: 15)
18+
end
19+
end
20+
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module Docs
2+
class TanstackQuery < Tanstack
3+
self.name = 'TanStack Query'
4+
self.slug = 'tanstack_query'
5+
self.release = '5.104.1'
6+
self.base_url = 'https://tanstack.com/query/latest/docs/'
7+
self.root_path = 'framework/react/overview'
8+
self.initial_paths = %w(eslint/eslint-plugin-query)
9+
self.links = {
10+
home: 'https://tanstack.com/query',
11+
code: 'https://github.com/TanStack/query'
12+
}
13+
14+
# React only, the other adapters duplicate the same pages
15+
options[:only_patterns] = [/\Aframework\/react\//, /\Aeslint\//]
16+
options[:skip_patterns] = [/\.md\z/, /examples/]
17+
18+
def get_latest_version(opts)
19+
get_npm_version('@tanstack/react-query', opts)
20+
end
21+
end
22+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Docs
2+
class TanstackRouter < Tanstack
3+
self.name = 'TanStack Router'
4+
self.slug = 'tanstack_router'
5+
self.release = '1.170.41'
6+
self.base_url = 'https://tanstack.com/router/latest/docs/'
7+
self.root_path = 'overview'
8+
self.links = {
9+
home: 'https://tanstack.com/router',
10+
code: 'https://github.com/TanStack/router'
11+
}
12+
13+
options[:skip_patterns] = [/\.md\z/, /examples/, /framework\/solid/]
14+
15+
def get_latest_version(opts)
16+
get_npm_version('@tanstack/react-router', opts)
17+
end
18+
end
19+
end
782 Bytes
Loading
1.5 KB
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://tanstack.com/favicon-light.svg
782 Bytes
Loading

0 commit comments

Comments
 (0)