Merge branch 'main' into add/threejs-docs

pull/2392/head
Simon Legner 1 day ago
commit 74aa661e3a

@ -13,13 +13,13 @@ jobs:
steps: steps:
- uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@540484a3c0f308b08619664ec40bf6c371d172c3 # v1.205.0 uses: ruby/setup-ruby@32110d4e311bd8996b2a82bf2a43b714ccc91777 # v1.221.0
with: with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Run tests - name: Run tests
run: bundle exec rake run: bundle exec rake
- name: Deploy to Heroku - name: Deploy to Heroku
uses: akhileshns/heroku-deploy@581dd286c962b6972d427fcf8980f60755c15520 # v3.13.15 uses: akhileshns/heroku-deploy@e3eb99d45a8e2ec5dca08735e089607befa4bf28 # v3.14.15
with: with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}} heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: "devdocs" heroku_app_name: "devdocs"

@ -11,7 +11,7 @@ jobs:
steps: steps:
- uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@540484a3c0f308b08619664ec40bf6c371d172c3 # v1.205.0 uses: ruby/setup-ruby@32110d4e311bd8996b2a82bf2a43b714ccc91777 # v1.221.0
with: with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Generate report - name: Generate report

@ -11,7 +11,7 @@ jobs:
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Ruby - name: Set up Ruby
uses: ruby/setup-ruby@540484a3c0f308b08619664ec40bf6c371d172c3 # v1.205.0 uses: ruby/setup-ruby@32110d4e311bd8996b2a82bf2a43b714ccc91777 # v1.221.0
with: with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Run tests - name: Run tests

@ -1 +1 @@
3.3.6 3.4.2

@ -1 +1 @@
ruby 3.3.6 ruby 3.4.2

@ -1,4 +1,4 @@
Copyright 2013-2024 Thibaut Courouble and other contributors Copyright 2013-2025 Thibaut Courouble and other contributors
This Source Code Form is subject to the terms of the Mozilla Public This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this License, v. 2.0. If a copy of the MPL was not distributed with this

@ -1,4 +1,4 @@
FROM ruby:3.3.6 FROM ruby:3.4.2
ENV LANG=C.UTF-8 ENV LANG=C.UTF-8
ENV ENABLE_SERVICE_WORKER=true ENV ENABLE_SERVICE_WORKER=true

@ -1,4 +1,4 @@
FROM ruby:3.3.6-alpine FROM ruby:3.4.2-alpine
ENV LANG=C.UTF-8 ENV LANG=C.UTF-8
ENV ENABLE_SERVICE_WORKER=true ENV ENABLE_SERVICE_WORKER=true

@ -1,5 +1,5 @@
source 'https://rubygems.org' source 'https://rubygems.org'
ruby '3.3.6' ruby '3.4.2'
gem 'activesupport', require: false gem 'activesupport', require: false
gem 'html-pipeline' gem 'html-pipeline'

@ -36,7 +36,8 @@ GEM
exifr (1.4.0) exifr (1.4.0)
ffi (1.15.5) ffi (1.15.5)
fspath (3.1.2) fspath (3.1.2)
highline (2.0.3) highline (3.1.2)
reline
html-pipeline (2.14.3) html-pipeline (2.14.3)
activesupport (>= 2) activesupport (>= 2)
nokogiri (>= 1.4) nokogiri (>= 1.4)
@ -53,6 +54,7 @@ GEM
image_optim (~> 0.19) image_optim (~> 0.19)
image_size (3.3.0) image_size (3.3.0)
in_threads (1.6.0) in_threads (1.6.0)
io-console (0.8.0)
logger (1.6.2) logger (1.6.2)
method_source (1.0.0) method_source (1.0.0)
mini_portile2 (2.8.8) mini_portile2 (2.8.8)
@ -61,7 +63,7 @@ GEM
mustermann (3.0.3) mustermann (3.0.3)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
newrelic_rpm (8.16.0) newrelic_rpm (8.16.0)
nokogiri (1.17.2) nokogiri (1.18.3)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
options (2.3.2) options (2.3.2)
@ -76,7 +78,7 @@ GEM
byebug (~> 11.0) byebug (~> 11.0)
pry (>= 0.13, < 0.15) pry (>= 0.13, < 0.15)
racc (1.8.1) racc (1.8.1)
rack (2.2.10) rack (2.2.11)
rack-protection (3.2.0) rack-protection (3.2.0)
base64 (>= 0.1.0) base64 (>= 0.1.0)
rack (~> 2.2, >= 2.2.4) rack (~> 2.2, >= 2.2.4)
@ -88,6 +90,8 @@ GEM
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
redcarpet (3.6.0) redcarpet (3.6.0)
reline (0.6.0)
io-console (~> 0.5)
rexml (3.3.9) rexml (3.3.9)
rouge (1.11.1) rouge (1.11.1)
rr (3.1.1) rr (3.1.1)
@ -126,14 +130,14 @@ GEM
strings-ansi (0.2.0) strings-ansi (0.2.0)
terminal-table (3.0.2) terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
terser (1.2.4) terser (1.2.5)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)
thin (1.8.2) thin (1.8.2)
daemons (~> 1.0, >= 1.0.9) daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4) eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3) rack (>= 1, < 3)
thor (1.3.2) thor (1.3.2)
tilt (2.4.0) tilt (2.6.0)
tty-pager (0.14.0) tty-pager (0.14.0)
strings (~> 0.2.0) strings (~> 0.2.0)
tty-screen (~> 0.8) tty-screen (~> 0.8)
@ -187,7 +191,7 @@ DEPENDENCIES
yajl-ruby yajl-ruby
RUBY VERSION RUBY VERSION
ruby 3.3.6p108 ruby 3.4.2p28
BUNDLED WITH BUNDLED WITH
2.4.6 2.4.6

@ -25,12 +25,12 @@ Unless you wish to contribute to the project, we recommend using the hosted vers
The easiest way to run DevDocs locally is using Docker: The easiest way to run DevDocs locally is using Docker:
```sh ```sh
docker run --name devdocs -d -p 9292:9292 ghcr.io/freecodcamp/devdocs:latest docker run --name devdocs -d -p 9292:9292 ghcr.io/freecodecamp/devdocs:latest
``` ```
This will start DevDocs at [localhost:9292](http://localhost:9292). We provide both regular and Alpine-based images: This will start DevDocs at [localhost:9292](http://localhost:9292). We provide both regular and Alpine-based images:
- `ghcr.io/freecodcamp/devdocs:latest` - Standard image - `ghcr.io/freecodecamp/devdocs:latest` - Standard image
- `ghcr.io/freecodcamp/devdocs:latest-alpine` - Alpine-based (smaller size) - `ghcr.io/freecodecamp/devdocs:latest-alpine` - Alpine-based (smaller size)
Images are automatically built and updated monthly with the latest documentation. Images are automatically built and updated monthly with the latest documentation.
@ -195,7 +195,7 @@ Made something cool? Feel free to open a PR to add a new row to this table! You
## Copyright / License ## Copyright / License
Copyright 20132024 Thibaut Courouble and [other contributors](https://github.com/freeCodeCamp/devdocs/graphs/contributors) Copyright 20132025 Thibaut Courouble and [other contributors](https://github.com/freeCodeCamp/devdocs/graphs/contributors)
This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](./COPYRIGHT) and [LICENSE](./LICENSE) files. This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](./COPYRIGHT) and [LICENSE](./LICENSE) files.

@ -1,5 +1,5 @@
/* /*
* Copyright 2013-2024 Thibaut Courouble and other contributors * Copyright 2013-2025 Thibaut Courouble and other contributors
* *
* This source code is licensed under the terms of the Mozilla * This source code is licensed under the terms of the Mozilla
* Public License, v. 2.0, a copy of which may be obtained at: * Public License, v. 2.0, a copy of which may be obtained at:

@ -457,13 +457,13 @@ $.noop = function () {};
$.popup = function (value) { $.popup = function (value) {
try { try {
window.open(value.href || value, "_blank", "noopener");
} catch (error) {
const win = window.open(); const win = window.open();
if (win.opener) { if (win.opener) {
win.opener = null; win.opener = null;
} }
win.location = value.href || value; win.location = value.href || value;
} catch (error) {
window.open(value.href || value, "_blank");
} }
}; };
@ -526,21 +526,3 @@ $.highlight = function (el, options) {
el.classList.add(options.className); el.classList.add(options.className);
setTimeout(() => el.classList.remove(options.className), options.delay); setTimeout(() => el.classList.remove(options.className), options.delay);
}; };
$.copyToClipboard = function (string) {
let result;
const textarea = document.createElement("textarea");
textarea.style.position = "fixed";
textarea.style.opacity = 0;
textarea.value = string;
document.body.appendChild(textarea);
try {
textarea.select();
result = !!document.execCommand("copy");
} catch (error) {
result = false;
} finally {
document.body.removeChild(textarea);
}
return result;
};

@ -1,4 +1,8 @@
[ [
[
"2025-02-16",
"New documentation: <a href=\"/openlayers/\">OpenLayers</a>"
],
[ [
"2024-11-23", "2024-11-23",
"New documentation: <a href=\"/duckdb/\">DuckDB</a>" "New documentation: <a href=\"/duckdb/\">DuckDB</a>"

@ -32,7 +32,7 @@ app.templates.aboutPage = function () {
<h2 class="_block-heading" id="copyright">Copyright and License</h2> <h2 class="_block-heading" id="copyright">Copyright and License</h2>
<p class="_note"> <p class="_note">
<strong>Copyright 2013&ndash;2024 Thibaut Courouble and <a href="https://github.com/freeCodeCamp/devdocs/graphs/contributors">other contributors</a></strong><br> <strong>Copyright 2013&ndash;2025 Thibaut Courouble and <a href="https://github.com/freeCodeCamp/devdocs/graphs/contributors">other contributors</a></strong><br>
This software is licensed under the terms of the Mozilla Public License v2.0.<br> This software is licensed under the terms of the Mozilla Public License v2.0.<br>
You may obtain a copy of the source code at <a href="https://github.com/freeCodeCamp/devdocs">github.com/freeCodeCamp/devdocs</a>.<br> You may obtain a copy of the source code at <a href="https://github.com/freeCodeCamp/devdocs">github.com/freeCodeCamp/devdocs</a>.<br>
For more information, see the <a href="https://github.com/freeCodeCamp/devdocs/blob/main/COPYRIGHT">COPYRIGHT</a> For more information, see the <a href="https://github.com/freeCodeCamp/devdocs/blob/main/COPYRIGHT">COPYRIGHT</a>

@ -96,16 +96,9 @@ app.views.EntryPage = class EntryPage extends app.View {
return content; return content;
} }
const links = (() => { const links = Object.entries(this.entry.doc.links).map(([link, url]) => {
const result = []; return `<a href="${url}" class="_links-link">${EntryPage.LINKS[link]}</a>`;
for (var link in this.entry.doc.links) { });
var url = this.entry.doc.links[link];
result.push(
`<a href="${url}" class="_links-link">${EntryPage.LINKS[link]}</a>`,
);
}
return result;
})();
return `<p class="_links">${links.join("")}</p>${content}`; return `<p class="_links">${links.join("")}</p>${content}`;
} }
@ -203,8 +196,8 @@ app.views.EntryPage = class EntryPage extends app.View {
} }
restore() { restore() {
let path; const path = this.entry.filePath();
if (this.cacheMap[(path = this.entry.filePath())]) { if (this.cacheMap[[path]]) {
this.render(this.cacheMap[path], true); this.render(this.cacheMap[path], true);
return true; return true;
} }
@ -217,18 +210,17 @@ app.views.EntryPage = class EntryPage extends app.View {
this.load(); this.load();
} else if (target.classList.contains("_pre-clip")) { } else if (target.classList.contains("_pre-clip")) {
$.stopEvent(event); $.stopEvent(event);
target.classList.add( navigator.clipboard.writeText(target.parentNode.textContent).then(
$.copyToClipboard(target.parentNode.textContent) () => target.classList.add("_pre-clip-success"),
? "_pre-clip-success" () => target.classList.add("_pre-clip-error"),
: "_pre-clip-error",
); );
setTimeout(() => (target.className = "_pre-clip"), 2000); setTimeout(() => (target.className = "_pre-clip"), 2000);
} }
} }
onAltC() { onAltC() {
let link; const link = this.find("._attribution:last-child ._attribution-link");
if (!(link = this.find("._attribution:last-child ._attribution-link"))) { if (!link) {
return; return;
} }
console.log(link.href + location.hash); console.log(link.href + location.hash);
@ -236,8 +228,8 @@ app.views.EntryPage = class EntryPage extends app.View {
} }
onAltO() { onAltO() {
let link; const link = this.find("._attribution:last-child ._attribution-link");
if (!(link = this.find("._attribution:last-child ._attribution-link"))) { if (!link) {
return; return;
} }
this.delay(() => $.popup(link.href + location.hash)); this.delay(() => $.popup(link.href + location.hash));

@ -3,7 +3,7 @@
//= depend_on sprites/docs.json //= depend_on sprites/docs.json
/*! /*!
* Copyright 2013-2024 Thibaut Courouble and other contributors * Copyright 2013-2025 Thibaut Courouble and other contributors
* *
* This source code is licensed under the terms of the Mozilla * This source code is licensed under the terms of the Mozilla
* Public License, v. 2.0, a copy of which may be obtained at: * Public License, v. 2.0, a copy of which may be obtained at:
@ -97,6 +97,7 @@
'pages/nushell', 'pages/nushell',
'pages/octave', 'pages/octave',
'pages/openjdk', 'pages/openjdk',
'pages/openlayers',
'pages/perl', 'pages/perl',
'pages/phalcon', 'pages/phalcon',
'pages/phaser', 'pages/phaser',

@ -28,6 +28,9 @@
p > code, li > code { @extend %label; } p > code, li > code { @extend %label; }
details { @extend %box; }
summary > div { display: inline; }
> .note, > .note,
.notecard, // MDN 2021 .notecard, // MDN 2021
.notice, .notice,

@ -0,0 +1,10 @@
._openlayers {
@extend %simple;
.nameContainer {
@extend %block-label;
> * { display: inline-block; margin: 0; }
> .tag-source { float: right; }
}
.card { @extend %box; margin-bottom: 1rem; padding: 1rem; }
.signature, .type-signature { @extend %code; }
}

@ -254,6 +254,17 @@ done
### Nokogiri ### Nokogiri
### Ruby / Minitest ### Ruby / Minitest
```sh
git clone https://github.com/seattlerb/minitest
cd minitest/
bundle install
bundle add rdoc hoe
bundle exec rak docs
cd ..
cp -r minitest/docs $DEVDOCS/docs/minitest
```
### Ruby on Rails ### Ruby on Rails
* Download a release at https://github.com/rails/rails/releases or clone https://github.com/rails/rails.git (checkout to the branch of the rails' version that is going to be scraped) * Download a release at https://github.com/rails/rails/releases or clone https://github.com/rails/rails.git (checkout to the branch of the rails' version that is going to be scraped)
* Open `railties/lib/rails/api/task.rb` and comment out any code related to sdoc (`configure_sdoc`) * Open `railties/lib/rails/api/task.rb` and comment out any code related to sdoc (`configure_sdoc`)

@ -7,8 +7,10 @@ module Docs
css('devsite-feature-tooltip').remove css('devsite-feature-tooltip').remove
css('devsite-thumb-rating').remove css('devsite-thumb-rating').remove
css('devsite-toc').remove css('devsite-toc').remove
css('devsite-feedback').remove
css('a.button-with-icon').remove css('a.button-with-icon').remove
css('button.devsite-heading-link').remove css('button.devsite-heading-link').remove
css('.devsite-article-body > span:first-child[style="float: right; line-height: 36px"]').remove
doc doc
end end

@ -42,7 +42,7 @@ module Docs
def fix_url(url) def fix_url(url)
if context[:redirections] if context[:redirections]
url = URL.parse(url) url = URL.parse(url)
path = url.path.downcase path = url.path ? url.path.downcase : nil
if context[:redirections].key?(path) if context[:redirections].key?(path)
url.path = context[:redirections][path] url.path = context[:redirections][path]

@ -16,8 +16,12 @@ module Docs
name name
else else
return at_css('h1').content.strip unless at_css('.type-name') return at_css('h1').content.strip unless at_css('.type-name')
name = at_css('.type-name').children.last.content.strip name = at_css('.type-name').children.reject { |n| n.matches?('.kind') }
name.map! { |n| n.text.strip }
name.reject! &:empty?
name = name.join
name.remove! %r{\(.*\)} name.remove! %r{\(.*\)}
name.strip!
name name
end end
end end

@ -34,7 +34,9 @@ module Docs
node.name = 'h3' node.name = 'h3'
node['id'] = id node['id'] = id
source_href = node.at_css('a.icon-action[title="View Source"]').attr('href') a = node.at_css('a.icon-action[title="View Source"]')
a ||= node.at_css('a.icon-action[aria-label="View Source"]')
source_href = a.attr('href')
node.content = node.at_css('.signature').inner_text node.content = node.at_css('.signature').inner_text
node << %(<a href="#{source_href}" class="source">Source</a>) node << %(<a href="#{source_href}" class="source">Source</a>)

@ -3,7 +3,7 @@ module Docs
class CleanHtmlFilter < Filter class CleanHtmlFilter < Filter
def call def call
i = 1 i = 1
n = at_css("#navmenu a[href='#{result[:path].split('/').last}']").parent n = at_css("#navmenu .submenu-content a[href='#{result[:path].split('/').last}']").parent
i += 1 while n && n = n.previous_element i += 1 while n && n = n.previous_element
at_css('h1')['data-level'] = i at_css('h1')['data-level'] = i

@ -0,0 +1,18 @@
module Docs
class Openlayers
class CleanHtmlFilter < Filter
def call
@doc = at_css('section')
at_css('h2').name = 'h1' if at_css('h2')
css('pre.prettyprint').each do |node|
node['data-language'] = node['class'].include?('html') ? 'html' : 'js'
node.content = node.content
end
doc
end
end
end
end

@ -0,0 +1,23 @@
module Docs
class Openlayers
class EntriesFilter < Docs::EntriesFilter
def get_name
at_css('h2').text.split('~').last.strip
end
def get_type
slug[/ol_([^_]+)_/, 1] or 'ol'
end
def additional_entries
css('h4.name').each_with_object [] do |node, entries|
node['id'] = node.previous_element['id']
next if node.at_css('.inherited')
name = node.children.find {|n| n.text? }.text.strip
name.prepend "#{self.name}."
entries << [name, node['id']]
end
end
end
end
end

@ -2,7 +2,7 @@ module Docs
class Phpunit class Phpunit
class CleanHtmlFilter < Filter class CleanHtmlFilter < Filter
def call def call
@doc = at_css('.section') if not root_page? @doc = at_css('section') if not root_page?
css('pre').each do |node| css('pre').each do |node|
node['class'] = 'highlight' node['class'] = 'highlight'

@ -13,8 +13,11 @@ module Docs
meta = Nokogiri::XML::Node.new 'dl', doc.document meta = Nokogiri::XML::Node.new 'dl', doc.document
meta['class'] = 'meta' meta['class'] = 'meta'
if parent = at_css('#parent-class-section') parent = at_css('#parent-class-section')
meta << %(<dt>Parent:</dt><dd class="meta-parent">#{parent.at_css('.link').inner_html.strip}</dd>) if parent && link = parent.at_css('.link')
meta << %(<dt>Parent:</dt><dd class="meta-parent">#{link.inner_html.strip}</dd>)
elsif parent && link = parent.at_css('a')
meta << %(<dt>Parent:</dt><dd class="meta-parent">#{link.to_html}</dd>)
end end
if includes = at_css('#includes-section') if includes = at_css('#includes-section')

@ -71,7 +71,7 @@ module Docs
# name from the HTML because companion object classes may be broken out into # name from the HTML because companion object classes may be broken out into
# their own entries (by the source documentation). When that happens, # their own entries (by the source documentation). When that happens,
# we want to group these classes (like `scala.reflect.api.Annotations.Annotation`) # we want to group these classes (like `scala.reflect.api.Annotations.Annotation`)
# under the package name, and not the fully-qualfied name which would # under the package name, and not the fully-qualified name which would
# include the companion object. # include the companion object.
def package_name def package_name
name = package_drop_last(slug_parts) name = package_drop_last(slug_parts)

@ -3,7 +3,8 @@ module Docs
class EntriesFilter < Docs::EntriesFilter class EntriesFilter < Docs::EntriesFilter
def get_name def get_name
name = at_css('h1').content.strip name = at_css('h1').content.strip
name.remove! "\u{00b6}" name.delete_suffix! ""
name.delete_suffix! "#"
if slug.start_with?('api') if slug.start_with?('api')
name.remove! 'Module: ' name.remove! 'Module: '

@ -2,6 +2,7 @@ module Docs
class ScikitLearn class ScikitLearn
class CleanHtmlFilter < Filter class CleanHtmlFilter < Filter
def call def call
@doc = at_css('main article', 'main')
if root_page? if root_page?
css('.row').each do |node| css('.row').each do |node|
html = '<dl>' html = '<dl>'

@ -46,6 +46,9 @@ module Docs
node.replace("<p>#{node.to_html}</p>") node.replace("<p>#{node.to_html}</p>")
end end
css('span[slot="popout-heading"]').remove
css('span[slot="popout-contents"]').remove
doc doc
end end
end end

@ -248,6 +248,10 @@ module Docs
"text": "usePreferredReducedMotion", "text": "usePreferredReducedMotion",
"link": "/core/usePreferredReducedMotion/" "link": "/core/usePreferredReducedMotion/"
}, },
{
"text": "usePreferredReducedTransparency",
"link": "/core/usePreferredReducedTransparency/"
},
{ {
"text": "useScreenOrientation", "text": "useScreenOrientation",
"link": "/core/useScreenOrientation/" "link": "/core/useScreenOrientation/"
@ -264,6 +268,10 @@ module Docs
"text": "useShare", "text": "useShare",
"link": "/core/useShare/" "link": "/core/useShare/"
}, },
{
"text": "useSSRWidth",
"link": "/core/useSSRWidth/"
},
{ {
"text": "useStyleTag", "text": "useStyleTag",
"link": "/core/useStyleTag/" "link": "/core/useStyleTag/"
@ -313,6 +321,10 @@ module Docs
"text": "onClickOutside", "text": "onClickOutside",
"link": "/core/onClickOutside/" "link": "/core/onClickOutside/"
}, },
{
"text": "onElementRemoval",
"link": "/core/onElementRemoval/"
},
{ {
"text": "onKeyStroke", "text": "onKeyStroke",
"link": "/core/onKeyStroke/" "link": "/core/onKeyStroke/"
@ -788,6 +800,10 @@ module Docs
{ {
"text": "Time", "text": "Time",
"items": [ "items": [
{
"text": "useCountdown",
"link": "/core/useCountdown/"
},
{ {
"text": "useDateFormat", "text": "useDateFormat",
"link": "/shared/useDateFormat/" "link": "/shared/useDateFormat/"

@ -12,6 +12,11 @@ module Docs
Licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. Licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License.
HTML HTML
version '8.0' do
self.release = '8.0.0'
self.base_url = 'https://bazel.build/versions/8.0.0/reference/be/'
end
version '7.0' do version '7.0' do
self.release = '7.0.0' self.release = '7.0.0'
self.base_url = 'https://bazel.build/versions/7.0.0/reference/be/' self.base_url = 'https://bazel.build/versions/7.0.0/reference/be/'

@ -2,7 +2,7 @@ module Docs
class Crystal < UrlScraper class Crystal < UrlScraper
include MultipleBaseUrls include MultipleBaseUrls
self.type = 'crystal' self.type = 'crystal'
self.release = '1.14.0' self.release = '1.15.1'
self.base_urls = [ self.base_urls = [
"https://crystal-lang.org/api/#{release}/", "https://crystal-lang.org/api/#{release}/",
"https://crystal-lang.org/reference/#{release[0..2]}/", "https://crystal-lang.org/reference/#{release[0..2]}/",
@ -21,6 +21,7 @@ module Docs
options[:skip_patterns] = [ options[:skip_patterns] = [
%r{\ACrystal/System/}, %r{\ACrystal/System/},
%r{\ACrystal/PointerPairingHeap/},
%r{\AIO/Evented.html\z}, %r{\AIO/Evented.html\z},
%r{\ARegex/PCRE2.html\z} %r{\ARegex/PCRE2.html\z}
] ]
@ -34,7 +35,7 @@ module Docs
HTML HTML
else else
<<-HTML <<-HTML
&copy; 2012&ndash;2024 Manas Technology Solutions.<br> &copy; 2012&ndash;2025 Manas Technology Solutions.<br>
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.
HTML HTML
end end

@ -96,7 +96,7 @@ module Docs
def get_latest_version(opts) def get_latest_version(opts)
doc = fetch_doc('https://docs.docker.com/engine/release-notes/', opts) doc = fetch_doc('https://docs.docker.com/engine/release-notes/', opts)
latest_version = doc.at_css('.DocSearch-content > h2 > a').content.strip latest_version = doc.at_css('h2.scroll-mt-20 > a').content.strip
latest_version.rpartition(' ')[-1] latest_version.rpartition(' ')[-1]
end end
end end

@ -30,6 +30,18 @@ module Docs
"https://hexdocs.pm/mix/#{self.class.release}/Mix.html" ] "https://hexdocs.pm/mix/#{self.class.release}/Mix.html" ]
end end
version '1.18' do
self.release = '1.18.1'
self.base_urls = [
"https://hexdocs.pm/elixir/#{release}/",
"https://hexdocs.pm/eex/#{release}/",
"https://hexdocs.pm/ex_unit/#{release}/",
"https://hexdocs.pm/iex/#{release}/",
"https://hexdocs.pm/logger/#{release}/",
"https://hexdocs.pm/mix/#{release}/"
]
end
version '1.17' do version '1.17' do
self.release = '1.17.2' self.release = '1.17.2'
self.base_urls = [ self.base_urls = [

@ -2,7 +2,7 @@ module Docs
class Express < UrlScraper class Express < UrlScraper
self.name = 'Express' self.name = 'Express'
self.type = 'express' self.type = 'express'
self.release = '4.18.1' self.release = '4.21.2'
self.base_url = 'https://expressjs.com/en/' self.base_url = 'https://expressjs.com/en/'
self.root_path = '4x/api.html' self.root_path = '4x/api.html'
self.initial_paths = %w( self.initial_paths = %w(

@ -2,7 +2,7 @@ module Docs
class Fastapi < UrlScraper class Fastapi < UrlScraper
self.name = 'FastAPI' self.name = 'FastAPI'
self.type = 'fastapi' self.type = 'fastapi'
self.release = '0.111.1' self.release = '0.115.6'
self.base_url = 'https://fastapi.tiangolo.com/' self.base_url = 'https://fastapi.tiangolo.com/'
self.root_path = '/' self.root_path = '/'
self.links = { self.links = {

@ -18,6 +18,11 @@ module Docs
Licensed under the BSD 3-clause License. Licensed under the BSD 3-clause License.
HTML HTML
version do
self.release = '3.1.1'
self.base_url = "https://flask.palletsprojects.com/en/stable/"
end
version '3.0' do version '3.0' do
self.release = '3.0.x' self.release = '3.0.x'
self.base_url = "https://flask.palletsprojects.com/en/#{self.release}/" self.base_url = "https://flask.palletsprojects.com/en/#{self.release}/"

@ -1,7 +1,7 @@
module Docs module Docs
class Git < UrlScraper class Git < UrlScraper
self.type = 'git' self.type = 'git'
self.release = '2.47.1' self.release = '2.48.1'
self.base_url = 'https://git-scm.com/docs' self.base_url = 'https://git-scm.com/docs'
self.initial_paths = %w(/git.html) self.initial_paths = %w(/git.html)
self.links = { self.links = {

@ -1,7 +1,7 @@
module Docs module Docs
class Go < UrlScraper class Go < UrlScraper
self.type = 'go' self.type = 'go'
self.release = '1.23.0' self.release = '1.24.0'
self.base_url = 'https://golang.org/pkg/' self.base_url = 'https://golang.org/pkg/'
self.links = { self.links = {
home: 'https://golang.org/', home: 'https://golang.org/',
@ -10,10 +10,19 @@ module Docs
# Run godoc locally, since https://golang.org/pkg/ redirects to https://pkg.go.dev/std with rate limiting / scraping protection. # Run godoc locally, since https://golang.org/pkg/ redirects to https://pkg.go.dev/std with rate limiting / scraping protection.
# podman run --net host --rm -it docker.io/golang:1.23.0 # podman run --net host --rm -it docker.io/golang:1.24.0
#podman# go install golang.org/x/tools/cmd/godoc@latest #podman# go install golang.org/x/tools/cmd/godoc@latest
#podman# rm -r /usr/local/go/test/ #podman# rm -r /usr/local/go/test/
#podman# godoc -http 0.0.0.0:6060 -v #podman# godoc -http 0.0.0.0:6060 -v
# or using alpine
# podman run --net host --rm -it alpine:latest
#podman# apk add curl
#podman# curl -LO https://go.dev/dl/go1.24.0.linux-amd64.tar.gz
#podman# tar xf go1.24.0.linux-amd64.tar.gz
#podman# go/bin/go install golang.org/x/tools/cmd/godoc@latest
#podman# /root/go/bin/godoc -http 0.0.0.0:6060 -v
self.base_url = 'http://localhost:6060/pkg/' self.base_url = 'http://localhost:6060/pkg/'
html_filters.push 'clean_local_urls' html_filters.push 'clean_local_urls'

@ -59,7 +59,7 @@ module Docs
end end
version '9' do version '9' do
self.release = '9.4.2' self.release = '9.12.1'
self.base_url = "https://downloads.haskell.org/~ghc/#{release}/docs/" self.base_url = "https://downloads.haskell.org/~ghc/#{release}/docs/"
options[:container] = ->(filter) {filter.subpath.start_with?('users_guide') ? '.document' : '#content'} options[:container] = ->(filter) {filter.subpath.start_with?('users_guide') ? '.document' : '#content'}
end end

@ -2,7 +2,7 @@ module Docs
class JqueryUi < Jquery class JqueryUi < Jquery
self.name = 'jQuery UI' self.name = 'jQuery UI'
self.slug = 'jqueryui' self.slug = 'jqueryui'
self.release = '1.13.0' self.release = '1.14.1'
self.base_url = 'https://api.jqueryui.com' self.base_url = 'https://api.jqueryui.com'
self.root_path = '/category/all' self.root_path = '/category/all'

@ -1,6 +1,6 @@
module Docs module Docs
class Dom < Mdn class Dom < Mdn
# release = '2023-08-20' # release = '2025-01-30'
self.name = 'Web APIs' self.name = 'Web APIs'
self.slug = 'dom' self.slug = 'dom'
self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/API' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/API'

@ -3,7 +3,7 @@ module Docs
prepend FixInternalUrlsBehavior prepend FixInternalUrlsBehavior
prepend FixRedirectionsBehavior prepend FixRedirectionsBehavior
# release = '2024-11-18' # release = '2025-01-30'
self.name = 'JavaScript' self.name = 'JavaScript'
self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference'
self.links = { self.links = {

@ -47,8 +47,7 @@ module Docs
end end
def get_latest_version(opts) def get_latest_version(opts)
doc = fetch_doc('https://docs.meteor.com/#/full/', opts) get_npm_version('meteor', opts)
doc.at_css('select.version-select > option').content
end end
end end
end end

@ -23,9 +23,7 @@ module Docs
def get_latest_version(opts) def get_latest_version(opts)
doc = fetch_doc('https://nixos.org/manual/nix/stable/', opts) doc = fetch_doc('https://nixos.org/manual/nix/stable/', opts)
json = JSON.parse(doc.at_css('body')['data-nix-channels']) doc.at_css('a.active')['href'].scan(/([0-9.]+)/)[0][0]
channel = json.find { |c| c['channel'] == 'stable' }
channel['version']
end end
end end
end end

@ -0,0 +1,24 @@
module Docs
class Openlayers < UrlScraper
self.name = 'OpenLayers'
self.type = 'openlayers'
self.slug = 'openlayers'
self.release = '10.4.0'
self.base_url = "https://openlayers.org/en/latest/apidoc/"
self.links = {
home: 'https://openlayers.org/',
code: 'https://github.com/openlayers/openlayers'
}
html_filters.push 'openlayers/entries', 'openlayers/clean_html'
options[:attribution] = <<-HTML
&copy; 2005-present, OpenLayers Contributors All rights reserved.
Licensed under the BSD 2-Clause License.
HTML
def get_latest_version(opts)
get_npm_version('ol', opts)
end
end
end

@ -17,12 +17,21 @@ module Docs
options[:title] = false options[:title] = false
options[:attribution] = <<-HTML options[:attribution] = <<-HTML
&copy; 2005&ndash;2020 Sebastian Bergmann<br> &copy; 2005&ndash;2025 Sebastian Bergmann<br>
Licensed under the Creative Commons Attribution 3.0 Unported License. Licensed under the Creative Commons Attribution 3.0 Unported License.
HTML HTML
FILTERS = %w(phpunit/clean_html phpunit/entries title) FILTERS = %w(phpunit/clean_html phpunit/entries title)
version do
self.release = '12.0'
self.base_url = "https://docs.phpunit.de/en/#{release}/"
html_filters.push FILTERS
options[:container] = '.document'
end
version '9' do version '9' do
self.release = '9.5' self.release = '9.5'
self.base_url = "https://phpunit.readthedocs.io/en/#{release}/" self.base_url = "https://phpunit.readthedocs.io/en/#{release}/"
@ -77,8 +86,8 @@ module Docs
def get_latest_version(opts) def get_latest_version(opts)
doc = fetch_doc('https://phpunit.readthedocs.io/', opts) doc = fetch_doc('https://phpunit.readthedocs.io/', opts)
label = doc.at_css('.rst-current-version').content.strip label = doc.at_css('meta[name="readthedocs-version-slug"]')["content"]
label.scan(/v: ([0-9.]+)/)[0][0] label
end end
end end

@ -1,14 +1,9 @@
module Docs module Docs
class Minitest < Rdoc class Minitest < Rdoc
# Instructions:
# 1. Run "gem update rdoc hoe"
# 2. Download the source code at https://github.com/seattlerb/minitest
# 3. Run "rake docs" (in the Minitest directory)
# 4. Copy the "docs" directory to "docs/minitest"
self.name = 'Ruby / Minitest' self.name = 'Ruby / Minitest'
self.slug = 'minitest' self.slug = 'minitest'
self.release = '5.20.0' self.release = '5.25.4'
self.links = { self.links = {
code: 'https://github.com/minitest/minitest' code: 'https://github.com/minitest/minitest'
} }

@ -75,6 +75,10 @@ module Docs
end end
end end
version '8.0' do
self.release = '8.0.1'
end
version '7.2' do version '7.2' do
self.release = '7.2.1' self.release = '7.2.1'
end end

@ -63,12 +63,16 @@ module Docs
/\AXMP/] /\AXMP/]
options[:attribution] = <<-HTML options[:attribution] = <<-HTML
Ruby Core &copy; 1993&ndash;2022 Yukihiro Matsumoto<br> Ruby Core &copy; 1993&ndash;2024 Yukihiro Matsumoto<br>
Licensed under the Ruby License.<br> Licensed under the Ruby License.<br>
Ruby Standard Library &copy; contributors<br> Ruby Standard Library &copy; contributors<br>
Licensed under their own licenses. Licensed under their own licenses.
HTML HTML
version '3.4' do
self.release = '3.4.1'
end
version '3.3' do version '3.3' do
self.release = '3.3.0' self.release = '3.3.0'
end end

@ -22,8 +22,7 @@ module Docs
def get_latest_version(opts) def get_latest_version(opts)
body = fetch('http://download.redis.io/redis-stable/00-RELEASENOTES', opts) body = fetch('http://download.redis.io/redis-stable/00-RELEASENOTES', opts)
body = body.lines[1..-1].join body.scan(/Redis Community Edition ([0-9.]+)/)[0][0]
body.scan(/Redis ([0-9.]+)/)[0][0]
end end
private private

@ -3,7 +3,7 @@
module Docs module Docs
class Rust < UrlScraper class Rust < UrlScraper
self.type = 'rust' self.type = 'rust'
self.release = '1.83.0' self.release = '1.84.1'
self.base_url = 'https://doc.rust-lang.org/' self.base_url = 'https://doc.rust-lang.org/'
self.root_path = 'book/index.html' self.root_path = 'book/index.html'
self.initial_paths = %w( self.initial_paths = %w(

@ -1,7 +1,7 @@
module Docs module Docs
class Sass < UrlScraper class Sass < UrlScraper
self.type = 'yard' self.type = 'yard'
self.release = '1.82.9' self.release = '1.85.0'
self.base_url = 'https://sass-lang.com/documentation' self.base_url = 'https://sass-lang.com/documentation'
self.root_path = 'index.html' self.root_path = 'index.html'
self.links = { self.links = {
@ -17,16 +17,16 @@ module Docs
options[:trailing_slash] = false options[:trailing_slash] = false
options[:attribution] = <<-HTML options[:attribution] = <<-HTML
&copy; 2006&ndash;2024 the Sass team, and numerous contributors<br> &copy; 2006&ndash;2025 the Sass team, and numerous contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
private
def get_latest_version(opts) def get_latest_version(opts)
get_npm_version('sass', opts) get_npm_version('sass', opts)
end end
private
def parse(response) def parse(response)
response.body.gsub! '<span class="widont">&nbsp;</span>', '&nbsp;' response.body.gsub! '<span class="widont">&nbsp;</span>', '&nbsp;'
end end

@ -3,8 +3,10 @@ module Docs
self.name = 'scikit-image' self.name = 'scikit-image'
self.slug = 'scikit_image' self.slug = 'scikit_image'
self.type = 'sphinx' self.type = 'sphinx'
self.release = '0.18.1' self.release = '0.25.0'
self.base_url = 'https://scikit-image.org/docs/0.18.x/' v = self.release[/\d+\.\d+/]
self.base_url = "https://scikit-image.org/docs/#{v}.x/"
self.initial_paths = %w(/ /api/ /user_guide/)
self.links = { self.links = {
home: 'https://scikit-image.org/', home: 'https://scikit-image.org/',
code: 'https://github.com/scikit-image/scikit-image' code: 'https://github.com/scikit-image/scikit-image'
@ -12,7 +14,7 @@ module Docs
html_filters.push 'scikit_image/entries', 'sphinx/clean_html' html_filters.push 'scikit_image/entries', 'sphinx/clean_html'
options[:container] = '.span9' options[:container] = 'main article'
options[:skip] = %w(api_changes.html) options[:skip] = %w(api_changes.html)
options[:only_patterns] = [/\Aapi/, /\Auser_guide/] options[:only_patterns] = [/\Aapi/, /\Auser_guide/]

@ -3,8 +3,9 @@ module Docs
self.name = 'scikit-learn' self.name = 'scikit-learn'
self.slug = 'scikit_learn' self.slug = 'scikit_learn'
self.type = 'sphinx' self.type = 'sphinx'
self.release = '1.1.3' self.release = '1.6.1'
self.base_url = "https://scikit-learn.org/1.1/" v = self.release[/\d+\.\d+/]
self.base_url = "https://scikit-learn.org/#{v}/"
self.root_path = 'index.html' self.root_path = 'index.html'
self.force_gzip = true self.force_gzip = true
self.links = { self.links = {
@ -14,7 +15,6 @@ module Docs
html_filters.push 'scikit_learn/entries', 'scikit_learn/clean_html', 'sphinx/clean_html', 'title' html_filters.push 'scikit_learn/entries', 'scikit_learn/clean_html', 'sphinx/clean_html', 'title'
options[:container] = ->(filter) { filter.root_page? ? 'body > .container' : '#sk-page-content-wrapper > .body' }
options[:skip] = %w(modules/generated/sklearn.experimental.enable_iterative_imputer.html options[:skip] = %w(modules/generated/sklearn.experimental.enable_iterative_imputer.html
modules/generated/sklearn.experimental.enable_hist_gradient_boosting.html) modules/generated/sklearn.experimental.enable_hist_gradient_boosting.html)
options[:only_patterns] = [/\Amodules/, /\Adatasets/, /\Atutorial/, /\Aauto_examples/] options[:only_patterns] = [/\Amodules/, /\Adatasets/, /\Atutorial/, /\Aauto_examples/]
@ -24,7 +24,7 @@ module Docs
options[:max_image_size] = 256_000 options[:max_image_size] = 256_000
options[:attribution] = <<-HTML options[:attribution] = <<-HTML
&copy; 2007&ndash;2022 The scikit-learn developers<br> &copy; 2007&ndash;2025 The scikit-learn developers<br>
Licensed under the 3-clause BSD License. Licensed under the 3-clause BSD License.
HTML HTML

@ -19,6 +19,11 @@ module Docs
Code samples licensed under the Apache 2.0 License. Code samples licensed under the Apache 2.0 License.
HTML HTML
version do
self.release = "2.18.0"
self.base_url = "https://www.tensorflow.org/api_docs/python/tf"
end
version '2.9' do version '2.9' do
self.release = "2.9.1" self.release = "2.9.1"
self.base_url = "https://www.tensorflow.org/versions/r#{version}/api_docs/python/tf" self.base_url = "https://www.tensorflow.org/versions/r#{version}/api_docs/python/tf"

@ -3,6 +3,11 @@ module Docs
self.name = 'TensorFlow C++' self.name = 'TensorFlow C++'
self.slug = 'tensorflow_cpp' self.slug = 'tensorflow_cpp'
version do
self.release = "2.18.0"
self.base_url = "https://www.tensorflow.org/api_docs/cc"
end
version '2.9' do version '2.9' do
self.release = "2.9.1" self.release = "2.9.1"
self.base_url = "https://www.tensorflow.org/versions/r#{version}/api_docs/cc" self.base_url = "https://www.tensorflow.org/versions/r#{version}/api_docs/cc"

@ -1,7 +1,7 @@
module Docs module Docs
class Trio < UrlScraper class Trio < UrlScraper
self.type = 'simple' self.type = 'simple'
self.release = '0.22.2' self.release = '0.29.0'
self.base_url = "https://trio.readthedocs.io/en/v#{self.release}/" self.base_url = "https://trio.readthedocs.io/en/v#{self.release}/"
self.root_path = 'index.html' self.root_path = 'index.html'
self.links = { self.links = {
@ -25,7 +25,7 @@ module Docs
def get_latest_version(opts) def get_latest_version(opts)
doc = fetch_doc('https://trio.readthedocs.io/en/stable/', opts) doc = fetch_doc('https://trio.readthedocs.io/en/stable/', opts)
doc.at_css('.rst-other-versions a[href^="/en/v"]').content[1..-1] doc.at_css('div.trio-version').content[0..-1]
end end
end end
end end

@ -22,7 +22,7 @@ module Docs
html_filters.push 'vite/entries', 'vite/clean_html' html_filters.push 'vite/entries', 'vite/clean_html'
version do version do
self.release = '6.0.1' self.release = '6.1.0'
self.base_url = 'https://vite.dev/' self.base_url = 'https://vite.dev/'
end end

@ -22,7 +22,7 @@ module Docs
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
self.release = '12.0.0' self.release = '12.5.0'
self.base_url = 'https://vueuse.org/' self.base_url = 'https://vueuse.org/'
self.initial_paths = %w(functions.html) self.initial_paths = %w(functions.html)
html_filters.push 'vueuse/entries', 'vite/clean_html', 'vueuse/clean_html' html_filters.push 'vueuse/entries', 'vite/clean_html', 'vueuse/clean_html'

@ -17,6 +17,11 @@ module Docs
Licensed under the BSD 3-clause License. Licensed under the BSD 3-clause License.
HTML HTML
version do
self.release = '3.1.1'
self.base_url = "https://werkzeug.palletsprojects.com/en/latest/"
end
version '3.0' do version '3.0' do
self.release = '3.0.x' self.release = '3.0.x'
self.base_url = "https://werkzeug.palletsprojects.com/en/#{self.release}/" self.base_url = "https://werkzeug.palletsprojects.com/en/#{self.release}/"

@ -239,7 +239,7 @@ class DocsCLI < Thor
['index.json', 'meta.json'].each do |filename| ['index.json', 'meta.json'].each do |filename|
json = "https://documents.devdocs.io/#{doc.path}/#{filename}?#{time}" json = "https://documents.devdocs.io/#{doc.path}/#{filename}?#{time}"
begin begin
URI.open(json) do |file| URI.open(json, "Accept-Encoding" => "identity") do |file|
mutex.synchronize do mutex.synchronize do
path = File.join(dir, filename) path = File.join(dir, filename)
File.write(path, file.read) File.write(path, file.read)

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

@ -0,0 +1,2 @@
https://github.com/openlayers
https://avatars.githubusercontent.com/u/240579?s=64

@ -64,7 +64,7 @@ class ManifestTest < Minitest::Spec
it "includes the doc's meta representation" do it "includes the doc's meta representation" do
json = manifest.as_json json = manifest.as_json
assert_equal 1, json.length assert_equal 1, json.length
assert_equal "{\"name\"=>\"Test\", \"db_size\"=>776533, :attribution=>\"foo\", :alias=>nil}", json[0].to_s assert_equal "{\"name\" => \"Test\", \"db_size\" => 776533, attribution: \"foo\", alias: nil}", json[0].to_s
end end
end end

Loading…
Cancel
Save