diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index de9243bf62..0955358272 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0 + uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index 8bb9246f39..6bce275a66 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0 + uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8b1d9e9c0..89a56c13e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Ruby - uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0 + uses: ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651 # v1.245.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.ruby-version b/.ruby-version index 6cb9d3dd0d..f9892605c7 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.3 +3.4.4 diff --git a/.tool-versions b/.tool-versions index a72ead61f3..ca745c6d6d 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -ruby 3.4.3 +ruby 3.4.4 diff --git a/Dockerfile b/Dockerfile index 5a85b1e4eb..2d8a9fee8a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.4.3 +FROM ruby:3.4.4 ENV LANG=C.UTF-8 ENV ENABLE_SERVICE_WORKER=true diff --git a/Dockerfile-alpine b/Dockerfile-alpine index 7075f4c508..6af65c2eb5 100644 --- a/Dockerfile-alpine +++ b/Dockerfile-alpine @@ -1,4 +1,4 @@ -FROM ruby:3.4.3-alpine +FROM ruby:3.4.4-alpine ENV LANG=C.UTF-8 ENV ENABLE_SERVICE_WORKER=true diff --git a/Gemfile b/Gemfile index 21c75e07a3..cbe55ccf8a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source 'https://rubygems.org' -ruby '3.4.3' +ruby '3.4.4' gem 'activesupport', require: false gem 'html-pipeline' diff --git a/Gemfile.lock b/Gemfile.lock index a80e6f6103..cd22f745b6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,14 +78,14 @@ GEM byebug (~> 12.0) pry (>= 0.13, < 0.16) racc (1.8.1) - rack (2.2.14) + rack (2.2.17) rack-protection (3.2.0) base64 (>= 0.1.0) rack (~> 2.2, >= 2.2.4) rack-ssl-enforcer (0.2.9) rack-test (2.2.0) rack (>= 1.3) - rake (13.2.1) + rake (13.3.0) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) @@ -130,7 +130,7 @@ GEM strings-ansi (0.2.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - terser (1.2.5) + terser (1.2.6) execjs (>= 0.3.0, < 3) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) @@ -191,7 +191,7 @@ DEPENDENCIES yajl-ruby RUBY VERSION - ruby 3.4.3p32 + ruby 3.4.4p34 BUNDLED WITH 2.4.6 diff --git a/README.md b/README.md index f4c733d81c..d4f45f2480 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ Made something cool? Feel free to open a PR to add a new row to this table! You | [girishji/devdocs.vim](https://github.com/girishji/devdocs.vim) | Vim plugin & TUI (browse inside Vim) | ![Latest GitHub commit](https://img.shields.io/github/last-commit/girishji/devdocs.vim?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/girishji/devdocs.vim?logo=github&label) | | [romainl/vim-devdocs](https://github.com/romainl/vim-devdocs) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/romainl/vim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/romainl/vim-devdocs?logo=github&label) | | [waiting-for-dev/vim-www](https://github.com/waiting-for-dev/vim-www) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/waiting-for-dev/vim-www?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/waiting-for-dev/vim-www?logo=github&label) | -| [luckasRanarison/nvim-devdocs](https://github.com/luckasRanarison/nvim-devdocs) | Neovim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/luckasRanarison/nvim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/luckasRanarison/nvim-devdocs?logo=github&label) | +| [emmanueltouzery/apidocs.nvim](https://github.com/emmanueltouzery/apidocs.nvim) | Neovim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/emmanueltouzery/apidocs.nvim?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/emmanueltouzery/apidocs.nvim?logo=github&label) | | [toiletbril/dedoc](https://github.com/toiletbril/dedoc) | Terminal based viewer | ![Latest GitHub commit](https://img.shields.io/github/last-commit/toiletbril/dedoc?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/toiletbril/dedoc?logo=github&label) | | [Raycast Devdocs](https://www.raycast.com/djpowers/devdocs) | Raycast extension | Unavailable | Unavailable | | [chrisgrieser/alfred-docs-searches](https://github.com/chrisgrieser/alfred-docs-searches) | Alfred workflow | ![Latest GitHub commit](https://img.shields.io/github/last-commit/chrisgrieser/alfred-docs-searches?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/chrisgrieser/alfred-docs-searches?logo=github&label) | diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index b6c061b01e..1d049a723d 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,4 +1,16 @@ [ + [ + "2025-06-27", + "New documentation: Zsh" + ], + [ + "2025-06-04", + "New documentation: es-toolkit" + ], + [ + "2025-05-28", + "New documentation: Vert.x" + ], [ "2025-02-23", "New documentation: Three.js" diff --git a/assets/stylesheets/global/_classes.scss b/assets/stylesheets/global/_classes.scss index 9e6b933279..3321ff8fba 100644 --- a/assets/stylesheets/global/_classes.scss +++ b/assets/stylesheets/global/_classes.scss @@ -133,6 +133,11 @@ border-color: var(--noteRedBorder); } +%note-gray, %label-gray { + background: var(--boxBackground); + border: 1px solid var(--boxBorder); +} + // // External links // diff --git a/assets/stylesheets/pages/_mdn.scss b/assets/stylesheets/pages/_mdn.scss index 9155df4534..1bc6b8d831 100644 --- a/assets/stylesheets/pages/_mdn.scss +++ b/assets/stylesheets/pages/_mdn.scss @@ -28,12 +28,18 @@ p > code, li > code { @extend %label; } - details { @extend %box; } + details { @extend %note; @extend %box; } + details.baseline-indicator.not { @extend %note; @extend %note-gray; } + details.baseline-indicator.low { @extend %note; @extend %note-blue; } + details.baseline-indicator.high { @extend %note; @extend %note-green; } summary > div { display: inline; } + summary .status-title { font-weight: bold; } > .note, .notecard, // MDN 2021 - .notice, + .notice { + @extend %note; @extend %note-blue; + } .warning, .overheadIndicator, .blockIndicator, diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index 7a6e0a5f5f..133b621b9b 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -59,6 +59,12 @@ curl -L https://github.com/erlang/otp/releases/download/OTP-$RELEASE/otp_doc_htm bsdtar --extract --file - --directory=docs/erlang\~$VERSION/ ``` +## es-toolkit + +```sh +git clone https://github.com/toss/es-toolkit docs/es_toolkit +``` + ## Gnu ### Bash diff --git a/lib/app.rb b/lib/app.rb index 89fe6cf0a3..675936fbac 100644 --- a/lib/app.rb +++ b/lib/app.rb @@ -376,6 +376,8 @@ def modern_browser?(browser) 'angular~1.3' => 'angularjs~1.3', 'angular~1.2' => 'angularjs~1.2', 'codeigniter~3.0' => 'codeigniter~3', + 'pytorch~1' => 'pytorch~1.13', + 'pytorch~2' => 'pytorch', 'webpack~2' => 'webpack' } diff --git a/lib/docs/filters/express/clean_html.rb b/lib/docs/filters/express/clean_html.rb index 01a6390972..eeb98f1d50 100644 --- a/lib/docs/filters/express/clean_html.rb +++ b/lib/docs/filters/express/clean_html.rb @@ -7,14 +7,14 @@ def call i += 1 while n && n = n.previous_element at_css('h1')['data-level'] = i - @doc = at_css('#api-doc, .content') + @doc = at_css('#api-doc, main, .content') + + css('nav').remove # aria-labelledby="sidebar-heading" css('section', 'div.highlighter-rouge').each do |node| node.before(node.children).remove end - @doc = at_css('#page-doc') unless root_page? - if root_page? at_css('h1').remove css('> header', '#menu').remove diff --git a/lib/docs/filters/github/clean_html.rb b/lib/docs/filters/github/clean_html.rb index 4d6da24027..a97f2c3a12 100644 --- a/lib/docs/filters/github/clean_html.rb +++ b/lib/docs/filters/github/clean_html.rb @@ -2,6 +2,11 @@ module Docs class Github class CleanHtmlFilter < Filter def call + # Remove h1 wrapper to render it correctly. + css('.markdown-heading h1').each do |node| + node.parent.replace(node) + end + css('.anchor').each do |node| node.parent['id'] = node['href'].remove('#') node.remove diff --git a/lib/docs/filters/html/entries.rb b/lib/docs/filters/html/entries.rb index b018d9087d..1dcf397a06 100644 --- a/lib/docs/filters/html/entries.rb +++ b/lib/docs/filters/html/entries.rb @@ -5,21 +5,22 @@ class EntriesFilter < Docs::EntriesFilter def get_name name = super - name.sub!('Element.', '').try(:downcase!) - name.sub!('Global attributes.', '').try(:concat, ' (attribute)') + name.sub!('Guides.', '') + name.sub!('How to.', '') + name.sub!('Reference.Elements.', '').try(:downcase!) + name.sub!('Reference.Attributes.', '').try(:concat, ' (attribute)') + name.sub!('Reference.Global attributes.', '').try(:concat, ' (attribute)') name.sub!(/input\.([-\w]+)/, 'input type="\1"') name end def get_type - return 'Miscellaneous' if slug.include?('CORS') || slug.include?('Using') - if at_css('.deprecated', '.non-standard', '.obsolete') - 'Obsolete' - elsif slug.start_with?('Global_attr') - 'Attributes' - elsif slug.start_with?('Element/') - 'Elements' + 'Obsolete' + elsif slug.start_with?('Guides/') + 'Guides' + elsif slug.start_with?('Reference/') + slug.split('/').drop(1).first.sub(/_/, ' ') else 'Miscellaneous' end diff --git a/lib/docs/filters/nginx_lua_module/entries.rb b/lib/docs/filters/nginx_lua_module/entries.rb index e4b1285658..70094b9213 100644 --- a/lib/docs/filters/nginx_lua_module/entries.rb +++ b/lib/docs/filters/nginx_lua_module/entries.rb @@ -4,11 +4,11 @@ class EntriesFilter < Docs::EntriesFilter def additional_entries entries = [] - css('#directives + ul > li > a').each do |node| + css('h2:contains("Directives") + ul > li > a').each do |node| entries << [node.content, node['href'].remove('#'), 'Directives'] end - css('#nginx-api-for-lua + ul > li > a').each do |node| + css('h2:contains("Nginx API for Lua") + ul > li > a').each do |node| next if node.content == 'Introduction' entries << [node.content, node['href'].remove('#'), 'Nginx API for Lua'] end diff --git a/lib/docs/filters/pytorch/entries.rb b/lib/docs/filters/pytorch/entries.rb index b337edbb9b..37e26e3129 100644 --- a/lib/docs/filters/pytorch/entries.rb +++ b/lib/docs/filters/pytorch/entries.rb @@ -1,73 +1,6 @@ module Docs class Pytorch class EntriesFilter < Docs::EntriesFilter - TYPE_REPLACEMENTS = { - "torch.Tensor" => "Tensor", - "torch.nn" => "Neuro Network", - "Probability distributions - torch.distributions" => "Probability Distributions", - "torch" => "Torch", - "Quantization" => "Quantization", - "torch.optim" => "Optimization", - "torch.Storage" => "Storage", - "torch.nn.functional" => "NN Functions", - "torch.cuda" => "CUDA", - "Torch Distributed Elastic" => "Distributed Elastic", - "torch.fx" => "FX", - "TorchScript" => "Torch Script", - "torch.onnx" => "ONNX", - "Distributed communication package - torch.distributed" => "Distributed Communication", - "Automatic differentiation package - torch.autograd" => "Automatic Differentiation", - "torch.linalg" => "Linear Algebra", - "Distributed Checkpoint - torch.distributed.checkpoint" => "Distributed Checkpoint", - "Distributed RPC Framework" => "Distributed RPC", - "torch.special" => "SciPy-like Special", - "torch.package" => "Package", - "torch.backends" => "Backends", - "FullyShardedDataParallel" => "Fully Sharded Data Parallel", - "torch.sparse" => "Sparse Tensors", - "torch.export" => "Traced Graph Export", - "torch.fft" => "Discrete Fourier Transforms", - "torch.utils.data" => "Datasets and Data Loaders", - "torch.monitor" => "Monitor", - "Automatic Mixed Precision package - torch.amp" => "Automatic Mixed Precision", - "torch.utils.tensorboard" => "Tensorboard", - "torch.profiler" => "Profiler", - "torch.mps" => "MPS", - "DDP Communication Hooks" => "DDP Communication Hooks", - "Benchmark Utils - torch.utils.benchmark" => "Benchmark Utils", - "torch.nn.init" => "Parameter Initializations", - "Tensor Parallelism - torch.distributed.tensor.parallel" => "Tensor Parallelism", - "torch.func" => "JAX-like Function Transforms", - "Distributed Optimizers" => "Distributed Optimizers", - "torch.signal" => "SciPy-like Signal", - "torch.futures" => "Miscellaneous", - "torch.utils.cpp_extension" => "Miscellaneous", - "torch.overrides" => "Miscellaneous", - "Generic Join Context Manager" => "Miscellaneous", - "torch.hub" => "Miscellaneous", - "torch.cpu" => "Miscellaneous", - "torch.random" => "Miscellaneous", - "torch.compiler" => "Miscellaneous", - "Pipeline Parallelism" => "Miscellaneous", - "Named Tensors" => "Miscellaneous", - "Multiprocessing package - torch.multiprocessing" => "Miscellaneous", - "torch.utils" => "Miscellaneous", - "torch.library" => "Miscellaneous", - "Tensor Attributes" => "Miscellaneous", - "torch.testing" => "Miscellaneous", - "torch.nested" => "Miscellaneous", - "Understanding CUDA Memory Usage" => "Miscellaneous", - "torch.utils.dlpack" => "Miscellaneous", - "torch.utils.checkpoint" => "Miscellaneous", - "torch.__config__" => "Miscellaneous", - "Type Info" => "Miscellaneous", - "torch.utils.model_zoo" => "Miscellaneous", - "torch.utils.mobile_optimizer" => "Miscellaneous", - "torch._logging" => "Miscellaneous", - "torch.masked" => "Miscellaneous", - "torch.utils.bottleneck" => "Miscellaneous" - } - def get_breadcrumbs css('.pytorch-breadcrumbs > li').map { |node| node.content.delete_suffix(' >').strip @@ -75,18 +8,11 @@ def get_breadcrumbs end def get_name - b = get_breadcrumbs - b[(b[1] == 'torch' ? 2 : 1)..].join('.') + get_breadcrumbs[-1] end def get_type - t = get_breadcrumbs[1] - TYPE_REPLACEMENTS.fetch(t, t) - end - - def include_default_entry? - # Only include API entries to simplify and unify the list - return name.start_with?('torch.') + get_breadcrumbs[1] end def additional_entries @@ -108,8 +34,6 @@ def additional_entries entries << [id + '()', id] when 'py class', 'py attribute', 'py property' entries << [id, id] - when 'footnote brackets', 'field-list simple' - next end end diff --git a/lib/docs/filters/qunit/entries.rb b/lib/docs/filters/qunit/entries.rb index 43de1c5af8..fa4541c45b 100644 --- a/lib/docs/filters/qunit/entries.rb +++ b/lib/docs/filters/qunit/entries.rb @@ -8,7 +8,8 @@ class EntriesFilter < Docs::EntriesFilter 'assert' => '2. Assertions', 'callbacks' => '3. Callback events', 'config' => '4. Configuration', - 'extension' => '5. Extension interface' + 'extension' => '5. Extension interface', + 'reporters' => '6. Reporters', } def get_name at_css('h1').content diff --git a/lib/docs/filters/ramda/entries.rb b/lib/docs/filters/ramda/entries.rb index dcab3c0137..ab85371f8a 100644 --- a/lib/docs/filters/ramda/entries.rb +++ b/lib/docs/filters/ramda/entries.rb @@ -3,7 +3,9 @@ class Ramda class EntriesFilter < Docs::EntriesFilter def additional_entries css('ul.toc li').map do |node| - ["R.#{node['data-name']}", node['data-name'], node['data-category'].sub('Relaction', 'Relation')] + # As of 2025-06-20, `data-category` attribute is missing on https://ramdajs.com/ for Ramda versions < 0.29.0. + # This results in missing type for entries – and docs cannot be generated. + ["R.#{node['data-name']}", node['data-name'], node['data-category']] end end end diff --git a/lib/docs/filters/rust/entries.rb b/lib/docs/filters/rust/entries.rb index db795cb75b..31fa47ba9a 100644 --- a/lib/docs/filters/rust/entries.rb +++ b/lib/docs/filters/rust/entries.rb @@ -4,8 +4,10 @@ class EntriesFilter < Docs::EntriesFilter def get_name if slug.start_with?('book') || slug.start_with?('reference') - name = at_css("#sidebar a[href='#{File.basename(slug)}']") - name ? name.content : 'Introduction' + name = at_css("h2", "h1") + ch1 = slug[/ch(\d+)-(\d+)/, 1] + ch2 = slug[/ch(\d+)-(\d+)/, 2] + name ? "#{ch1}.#{ch2}. #{name.content}" : 'Introduction' elsif slug == 'error-index' 'Compiler Errors' else diff --git a/lib/docs/filters/sanctuary_def/entries.rb b/lib/docs/filters/sanctuary_def/entries.rb index 5226be07c2..25487e557a 100644 --- a/lib/docs/filters/sanctuary_def/entries.rb +++ b/lib/docs/filters/sanctuary_def/entries.rb @@ -39,8 +39,10 @@ def additional_entries when "h3" type = node.text when "h4" + # Parent
's ID set in github/clean_html. + id = node.parent.attributes["id"].value name = node.text.split(' :: ')[0] - id = node.attributes["id"].value + entries << [name, id, type] end end diff --git a/lib/docs/filters/sanctuary_type_classes/clean_html.rb b/lib/docs/filters/sanctuary_type_classes/clean_html.rb index 247dbb39a9..491aa351ac 100644 --- a/lib/docs/filters/sanctuary_type_classes/clean_html.rb +++ b/lib/docs/filters/sanctuary_type_classes/clean_html.rb @@ -8,11 +8,6 @@ def call node.name = 'h3' } - # correct and unify link ids - css('h3').each { |node| - node.attributes["id"].value = node.text.split(' :: ')[0] - } - doc end end diff --git a/lib/docs/filters/sanctuary_type_classes/entries.rb b/lib/docs/filters/sanctuary_type_classes/entries.rb index 8f4e051674..c9ee1aca0d 100644 --- a/lib/docs/filters/sanctuary_type_classes/entries.rb +++ b/lib/docs/filters/sanctuary_type_classes/entries.rb @@ -38,9 +38,9 @@ def additional_entries case node.name when "h2" type = node.text - if node.attributes["id"].value == "type-class-hierarchy" + if node.parent.attributes["id"]&.value == "type-class-hierarchy" name = node.text - id = node.attributes["id"].value + id = node.parent.attributes["id"].value entries << [name, id, type] end when "h4" diff --git a/lib/docs/filters/tailwindcss/clean_html.rb b/lib/docs/filters/tailwindcss/clean_html.rb index 9815cf090a..bdab80b912 100644 --- a/lib/docs/filters/tailwindcss/clean_html.rb +++ b/lib/docs/filters/tailwindcss/clean_html.rb @@ -2,26 +2,38 @@ module Docs class Tailwindcss class CleanHtmlFilter < Filter def call + # Move h1 out of wrapper. + css('h1').each do |node| + doc.prepend_child(node) + end + # Remove main page headers (top level sticky) - css('#__next > .sticky').remove + css('#__next > .sticky', 'div.fixed.inset-x-0.top-0').remove # And anything absolutely positioned (fancy floating navigation elements we don't care about) - css('#__next .absolute').remove + css('#__next .absolute', '.fixed').remove # Remove the left-navigation we scraped css('nav').remove css('svg').remove if root_page? - # Remove the duplicate category name at the top of the page - redundant - at_css('header#header > div:first-child > p:first-child').remove - # Remove the right navigation sidebar - at_css('header#header').parent.css('> div:has(h5:contains("On this page"))').remove + css('header#header', 'p[data-section]').each do |node| + node.parent.css('> div:has(h5:contains("On this page"))').remove # v3 + + node.parent.parent.css('div.max-xl\\:hidden').remove # v4 + end + + # Remove the duplicate category name at the top of the page - redundant + css( + 'header#header > div:first-child > p:first-child', # v3 + 'p[data-section]' # v4 + ).remove # Remove footer + prev/next navigation - at_css('footer').remove + css('footer', '.row-start-5').remove # Handle long lists of class reference that otherwise span several scrolled pages - if class_reference = at_css('#class-reference') + if class_reference = at_css('#class-reference', '#quick-reference') reference_container = class_reference.parent classes_container = reference_container.children.reject{ |child| child == class_reference }.first @@ -33,7 +45,7 @@ def call end # Remove border color preview column as it isn't displayed anyway - if result[:path] == "border-color" and class_reference = at_css('#class-reference') + if result[:path] == "border-color" and class_reference = at_css('#class-reference', '#quick-reference') class_reference.parent.css("thead th:nth-child(3)").remove class_reference.parent.css("tbody td:nth-child(3)").remove end @@ -59,29 +71,48 @@ def call end # Remove buttons to expand lists - those are already expanded and the button is useless - css('div > button:contains("Show all classes")').each do |node| + css( + 'div > button:contains("Show all classes")', + 'div > button:contains("Show more")' + ).each do |node| node.parent.remove end # Remove class examples - not part of devdocs styleguide? (similar to bootstrap) # Refer to https://github.com/freeCodeCamp/devdocs/pull/1534#pullrequestreview-649818936 - css('.not-prose').each do |node| - if node.parent.children.length == 1 - node.parent.remove - else - node.remove - end - end + css('.mt-4.-mb-3', 'figure', 'svg', '.flex.space-x-2').remove - # Properly format code examples + # Properly format code examples. css('pre > code:first-child').each do |node| - node.parent['data-language'] = node['class'][/language-(\w+)/, 1] if node['class'] and node['class'][/language-(\w+)/] - node.parent.content = node.parent.content + # v4 doesn't provide language context, so it must be inferred imperfectly. + node.parent['data-language'] = + if node.content.include?('function') + 'jsx' + elsif node.content.include?(" (https://github.com/damms005/devdocs/commit/8c9fbd859b71a2525b94a35ea994393ce2b6fedb#commitcomment-50091018) + # Remove weird
(https://github.com/damms005/devdocs/commit/8c9fbd859b71a2525b94a35ea994393ce2b6fedb#commitcomment-50091018) css('hr').remove doc diff --git a/lib/docs/filters/tailwindcss/entries.rb b/lib/docs/filters/tailwindcss/entries.rb index ad0bf7b441..5e95d5a3fb 100644 --- a/lib/docs/filters/tailwindcss/entries.rb +++ b/lib/docs/filters/tailwindcss/entries.rb @@ -3,22 +3,35 @@ class Tailwindcss class EntriesFilter < Docs::EntriesFilter def get_type # We are only interested in children list items - selector = "nav li li a[href='#{result[:path]}']" + - anchor = at_css(selector) - category_list = anchor.ancestors('li')[1] - title = category_list.css('h5') + anchor = at_css(get_selector) + title = + if version == '3' + anchor.ancestors('li')[1].css('h5') + else + anchor.ancestors('ul').last.previous_element + end return title.inner_text end def get_name # We are only interested in children list items - selector = "nav li li a[href='#{result[:path]}']" - item = at_css(selector) + item = at_css(get_selector) return item.inner_text end + + private + + def get_selector + if version == '3' + "nav li li a[href='#{result[:path]}']" + else + "nav li a[href*='#{result[:path]}']" + end + end end end end diff --git a/lib/docs/filters/vertx/clean_html.rb b/lib/docs/filters/vertx/clean_html.rb new file mode 100644 index 0000000000..786383f6a4 --- /dev/null +++ b/lib/docs/filters/vertx/clean_html.rb @@ -0,0 +1,40 @@ +module Docs + class Vertx + class CleanHtmlFilter < Filter + def call + css('footer', 'hr', 'header', 'nav', '.navbar', '.topbar', '.bg-bg-warning').remove + xpath('//*[@id="docs-layout"]/div/div[3]').remove + xpath('//*[@id="docs-layout"]/div/div[1]').remove + xpath('//main/div[last()]').remove + xpath('//main/div[1]').remove + css('#changelog').remove if root_page? + + # Set id attributes on

instead of an empty + css('h3').each do |node| + anchor = node.at_css('a') + node['id'] = anchor['id'] if anchor && anchor['id'] + end + + # Make proper table headers + css('td.header').each do |node| + node.name = 'th' + end + + # Remove code highlighting + css('pre').each do |node| + node['data-language'] = node.at_css('code')['data-lang'] if node.at_css('code') + node.content = node.content + end + + # ❗ Skip tags with data: URIs + css('img').each do |img| + src = img['src'] + img.remove if src&.start_with?('data:') + end + + doc + end + end + end +end + diff --git a/lib/docs/filters/vertx/entries.rb b/lib/docs/filters/vertx/entries.rb new file mode 100644 index 0000000000..8155c5d888 --- /dev/null +++ b/lib/docs/filters/vertx/entries.rb @@ -0,0 +1,42 @@ +module Docs + class Vertx + class EntriesFilter < Docs::EntriesFilter + # Determines the default name of the page entry + def get_name + node = at_css('h1') + return nil unless node + + result = node.content.strip + result << ' event' if type == 'Events' + result << '()' if node['class'].to_s.include?('function') + result + end + + # Determines the type of the default entry (used for sidebar grouping) + def get_type + return nil if root_page? + + node = at_xpath('/html/body/div/div/div[2]/main/div[1]/div[1]') + node ? node.text.strip : 'Miscellaneous' + end + + # Returns additional entries from subheadings (usually

) + def additional_entries + # return [] if root_page? + # + # css('h2').map do |node| + # name = node.content.strip + # id = node['id'] + # [name, id, type] + # end + [] + end + + # Determines whether to include the default entry for the page + def include_default_entry? + !at_css('.obsolete') + end + end + end +end + diff --git a/lib/docs/filters/zsh/clean_html.rb b/lib/docs/filters/zsh/clean_html.rb new file mode 100644 index 0000000000..43d65e045c --- /dev/null +++ b/lib/docs/filters/zsh/clean_html.rb @@ -0,0 +1,20 @@ +module Docs + class Zsh + class CleanHtmlFilter < Filter + def call + css('table.header', 'table.menu', 'hr').remove + + # Remove indices from headers. + css('h1', 'h2', 'h3').each do |node| + node.content = node.content.match(/^[\d\.]* (.*)$/)&.captures&.first + end + + css('h2.section ~ a').each do |node| + node.next_element['id'] = node['name'] + end + + doc + end + end + end +end diff --git a/lib/docs/filters/zsh/entries.rb b/lib/docs/filters/zsh/entries.rb new file mode 100644 index 0000000000..02e7dc9602 --- /dev/null +++ b/lib/docs/filters/zsh/entries.rb @@ -0,0 +1,74 @@ +module Docs + class Zsh + class EntriesFilter < Docs::EntriesFilter + def get_name + extract_header_text(at_css('h1.chapter').content) + end + + def additional_entries + entries = [] + used_fns = [] + + css('h2.section').each do |node| + type = get_type + # Linkable anchor sits above

. + a = node.xpath('preceding-sibling::a').last + header_text = extract_header_text(node.content) + + case type + when 'Zsh Modules' + module_name = header_text.match(/The (zsh\/.* Module)/)&.captures&.first + header_text = module_name if module_name.present? + when 'Calendar Function System' + header_text << ' (Calendar)' + end + + entries << [header_text, a['name'], type] unless header_text.start_with?('Description') + end + + # Functions are documented within
elements. + # Names are wrapped in
, details within
. + #
can also contain anchors for the next function. + doc.css('> dl').each do |node| + type = get_type + fn_names = node.css('> dt') + node.css('dd a[name]').each_with_index do |anchor, i| + if fn_names[i].present? && anchor['name'].present? + fn_names[i]['id'] = anchor['name'] + + # Groups of functions are sometimes comma-delimited. + # Strip arguments, flags, etc. from function name. + # Skip flag-only headers. + fn_names[i].inner_html.split(', ').each do |fn| + fn.gsub!(/<(?:tt|var)>(.+?)<\/(?:tt|var)>/, '\1') + fn = fn.split(' ').first + fn.gsub!(/(?:[\[\(]).*(?:[\]\)]).*$/, '') + + # Add context for operators. + fn << " (#{type})" if fn.length == 1 + + if fn.present? && !fn.match?(/^[\-\[]/) && !used_fns.include?(fn) + used_fns << fn + entries << [fn, anchor['name'], type] + end + end + end + end + end + + entries + end + + def get_type + extract_header_text(at_css('h1.chapter').content) + end + + private + + # Extracts text from a string, dropping indices preceding it. + def extract_header_text(str) + str.match(/^[\d\.]* (.*)$/)&.captures&.first + end + end + end +end diff --git a/lib/docs/scrapers/astro.rb b/lib/docs/scrapers/astro.rb index ef2e3e6a7b..40c496e15e 100644 --- a/lib/docs/scrapers/astro.rb +++ b/lib/docs/scrapers/astro.rb @@ -16,7 +16,7 @@ class Astro < UrlScraper options[:skip_patterns] = [/tutorial/, /getting-started/] - self.release = '4.14.2' + self.release = '5.10.2' self.base_url = 'https://docs.astro.build/en/' self.initial_paths = %w(install-and-setup/) diff --git a/lib/docs/scrapers/axios.rb b/lib/docs/scrapers/axios.rb index 6265a9c376..c29b41ba6c 100755 --- a/lib/docs/scrapers/axios.rb +++ b/lib/docs/scrapers/axios.rb @@ -5,7 +5,7 @@ class Axios < UrlScraper home: 'hthttps://axios-http.com/', code: 'https://github.com/axios/axios' } - self.release = '1.7.7' + self.release = '1.9.0' self.base_url = "https://axios-http.com/docs/" self.initial_paths = %w(index intro) options[:skip] = %w(sponsor) diff --git a/lib/docs/scrapers/es_toolkit.rb b/lib/docs/scrapers/es_toolkit.rb new file mode 100644 index 0000000000..85079723a0 --- /dev/null +++ b/lib/docs/scrapers/es_toolkit.rb @@ -0,0 +1,88 @@ +module Docs + class EsToolkit < FileScraper + self.name = "es-toolkit" + self.slug = "es_toolkit" + self.type = "simple" + self.links = { + code: "https://github.com/toss/es-toolkit", + home: "https://es-toolkit.slash.page", + } + + options[:attribution] = <<-HTML + © 2024-2025, Viva Republica
+ Licensed under the MIT License. + HTML + + def get_latest_version(opts) + get_github_tags("toss", "es-toolkit", opts).first["name"] + end + + def build_pages(&block) + internal("docs/intro.md", path: "index", &block) + Dir.chdir(source_directory) do + Dir["docs/reference/**/*.md"] + end.each { internal(_1, &block) } + end + + protected + + def internal(filename, path: nil, &block) + path ||= filename[%r{docs/reference/(.*/.*).md$}, 1] + + # calculate name/type + if path != "index" + name = filename[%r{([^/]+).md$}, 1] + type = path.split("/")[0..-2] + type = type.map(&:capitalize).join(" ") + # really bad way to sort + type = type.gsub(/^(Compat|Error)\b/, "\u2063\\1") # U+2063 INVISIBLE SEPARATOR + else + name = type = nil + end + + # now yield + entries = [Entry.new(name, path, type)] + output = render(filename) + store_path = "#{path}.html" + yield({entries:, output:, path:, store_path:}) + end + + # render/style HTML + def render(filename) + s = md.render(request_one(filename).body) + + # kill all links, they don't work + s.gsub!(%r{
(.*?)}, "\\1") + + # syntax highlighting + s.gsub!(%r{
}, "
")
+
+      # h3 => h4
+      s.gsub!(%r{(}, "\\14>")
+
+      # manually add attribution
+      link = "#{self.class.links[:home]}#{filename.gsub(/^docs/,'').gsub(/md$/,'html')}"
+      s += <<~HTML
+        
+

+ #{options[:attribution]} +
+ + #{link} + +

+
+ HTML + s + end + + def md + @md ||= Redcarpet::Markdown.new( + Redcarpet::Render::HTML, + autolink: true, + fenced_code_blocks: true, + tables: true + ) + end + end +end diff --git a/lib/docs/scrapers/express.rb b/lib/docs/scrapers/express.rb index 724b771ff8..ff6514b5fb 100644 --- a/lib/docs/scrapers/express.rb +++ b/lib/docs/scrapers/express.rb @@ -2,9 +2,7 @@ module Docs class Express < UrlScraper self.name = 'Express' self.type = 'express' - self.release = '4.21.2' self.base_url = 'https://expressjs.com/en/' - self.root_path = '4x/api.html' self.initial_paths = %w( starter/installing.html guide/routing.html @@ -29,6 +27,16 @@ class Express < UrlScraper Licensed under the Creative Commons Attribution-ShareAlike License v3.0. HTML + version do + self.release = '5.1.0' + self.root_path = '5x/api.html' + end + + version '4' do + self.release = '4.21.2' + self.root_path = '4x/api.html' + end + def get_latest_version(opts) get_npm_version('express', opts) end diff --git a/lib/docs/scrapers/git.rb b/lib/docs/scrapers/git.rb index a3a0fff89b..ae04f9bc2b 100644 --- a/lib/docs/scrapers/git.rb +++ b/lib/docs/scrapers/git.rb @@ -1,9 +1,37 @@ module Docs class Git < UrlScraper self.type = 'git' - self.release = '2.48.1' + self.release = '2.50.0' self.base_url = 'https://git-scm.com/docs' - self.initial_paths = %w(/git.html) + self.initial_paths = %w( + /git.html + /git-archimport.html + /git-cherry.html + /git-citool.html + /git-column.html + /git-cvsexportcommit.html + /git-for-each-repo.html + /git-get-tar-commit-id.html + /git-http-fetch.html + /git-http-push.html + /git-merge-file.html + /git-merge-index.html + /git-merge-one-file.html + /git-merge-tree.html + /git-mktree.html + /git-p4.html + /git-pack-redundant.html + /git-quiltimport.html + /git-replay.html + /git-sh-i18n.html + /git-sh-i18n--envsubst.html + /git-sh-setup.html + /git-show-index.html + /git-unpack-file.html + /git-verify-commit.html + /gitformat-index.html + /scalar.html + ) self.links = { home: 'https://git-scm.com/', code: 'https://github.com/git/git' @@ -16,9 +44,9 @@ class Git < UrlScraper options[:skip] = %w(/howto-index.html) # https://github.com/git/git?tab=License-1-ov-file#readme - # NOT https://github.com/git/git-scm.com/blob/main/MIT-LICENSE.txt + # NOT https://github.com/git/git-scm.com/blob/gh-pages/MIT-LICENSE.txt options[:attribution] = <<-HTML - © 2005–2024 Linus Torvalds and others
+ © 2005–2025 Linus Torvalds and others
Licensed under the GNU General Public License version 2. HTML diff --git a/lib/docs/scrapers/github.rb b/lib/docs/scrapers/github.rb index e5b48edbf6..dcadcec865 100644 --- a/lib/docs/scrapers/github.rb +++ b/lib/docs/scrapers/github.rb @@ -16,7 +16,13 @@ def process_response?(response) end def parse(response) - parsed = JSON.parse(response.response_body) + embedded_json = response + .response_body + .match(/react-app\.embeddedData">(.+?)<\/script>/) + &.captures + &.first + parsed = JSON.parse(embedded_json) + [parsed['payload']['blob']['richText'], parsed['title']] end end diff --git a/lib/docs/scrapers/jekyll.rb b/lib/docs/scrapers/jekyll.rb index 1c0534ebe8..d105e3523f 100644 --- a/lib/docs/scrapers/jekyll.rb +++ b/lib/docs/scrapers/jekyll.rb @@ -23,12 +23,12 @@ class Jekyll < UrlScraper } options[:attribution] = <<-HTML - © 2020 Jekyll Core Team and contributors
+ © 2025 Jekyll Core Team and contributors
Licensed under the MIT license. HTML version '4' do - self.release = '4.2.0' + self.release = '4.4.1' end version '3' do diff --git a/lib/docs/scrapers/koa.rb b/lib/docs/scrapers/koa.rb index 94592b8cbb..d61201ca80 100644 --- a/lib/docs/scrapers/koa.rb +++ b/lib/docs/scrapers/koa.rb @@ -2,10 +2,6 @@ module Docs class Koa < Github - self.base_url = 'https://github.com/koajs/koa/tree/master/docs' - self.release = '2.15.0' - - self.root_path = 'api/index.md' self.initial_paths = %w[ error-handling faq @@ -26,20 +22,47 @@ class Koa < Github html_filters.push 'koa/clean_html', 'koa/entries' - options[:skip] = %w[middleware.gif] + options[:skip_patterns] = [/\.gif/] options[:trailing_slash] = false options[:container] = '.markdown-body' - options[:fix_urls] = ->(url) do - url.sub! 'https://koajs.com/#error-handling', Koa.base_url + '/error-handling.md' - url - end + options[:attribution] = <<-HTML © 2020 Koa contributors
Licensed under the MIT License. HTML + version do + self.base_url = 'https://github.com/koajs/koa/blob/v3.0.0/docs' + self.root_path = 'api/index.md' + self.release = '3.0.0' + options[:fix_urls] = ->(url) do + url.sub! 'https://koajs.com/#error-handling', self.base_url + '/error-handling.md' + url + end + end + + version '2' do + self.base_url = 'https://github.com/koajs/koa/blob/v2.16.1/docs' + self.root_path = 'api/index.md' + self.release = '2.16.1' + options[:fix_urls] = ->(url) do + url.sub! 'https://koajs.com/#error-handling', self.base_url + '/error-handling.md' + url + end + end + + version '1' do + self.base_url = 'https://github.com/koajs/koa/blob/1.7.1/docs' + self.root_path = 'api/index.md' + self.release = '1.7.1' + options[:fix_urls] = ->(url) do + url.sub! 'https://koajs.com/#error-handling', self.base_url + '/error-handling.md' + url + end + end + def get_latest_version(opts) get_npm_version('koa', opts) end diff --git a/lib/docs/scrapers/kubectl.rb b/lib/docs/scrapers/kubectl.rb index 732b5211f1..62b556862c 100644 --- a/lib/docs/scrapers/kubectl.rb +++ b/lib/docs/scrapers/kubectl.rb @@ -14,13 +14,13 @@ class Kubectl < UrlScraper options[:container] = '#page-content-wrapper' options[:attribution] = <<-HTML - © 2024 The Kubernetes Authors | Documentation Distributed under CC BY 4.0
- Copyright © 2024 The Linux Foundation ®. All rights reserved. + © 2025 The Kubernetes Authors | Documentation Distributed under CC BY 4.0
+ Copyright © 2025 The Linux Foundation ®. All rights reserved. HTML # latest version has a special URL that does not include the version identifier version do - self.release = "1.31.2" + self.release = "1.33.1" self.base_url = "https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands" end diff --git a/lib/docs/scrapers/kubernetes.rb b/lib/docs/scrapers/kubernetes.rb index 39cefac7cb..d012c15f2e 100644 --- a/lib/docs/scrapers/kubernetes.rb +++ b/lib/docs/scrapers/kubernetes.rb @@ -14,13 +14,13 @@ class Kubernetes < UrlScraper options[:container] = '.td-content' options[:attribution] = <<-HTML - © 2024 The Kubernetes Authors | Documentation Distributed under CC BY 4.0
- Copyright © 2024 The Linux Foundation ®. All rights reserved. + © 2025 The Kubernetes Authors | Documentation Distributed under CC BY 4.0
+ Copyright © 2025 The Linux Foundation ®. All rights reserved. HTML # latest version has a special URL that does not include the version identifier version do - self.release = "1.31.2" + self.release = "1.33.1" self.base_url = "https://kubernetes.io/docs/reference/kubernetes-api/" end diff --git a/lib/docs/scrapers/leaflet.rb b/lib/docs/scrapers/leaflet.rb index a3e5e50242..dbe0c227b0 100644 --- a/lib/docs/scrapers/leaflet.rb +++ b/lib/docs/scrapers/leaflet.rb @@ -14,11 +14,16 @@ class Leaflet < UrlScraper options[:skip_links] = true options[:attribution] = <<-HTML - © 2010–2022 Vladimir Agafonkin
+ © 2010–2025 Vladimir Agafonkin
© 2010–2011, CloudMade
Maps © OpenStreetMap contributors. HTML + version '2.0' do + self.release = '2.0.0-alpha' + self.base_url = "https://leafletjs.com/reference-2.0.0.html" + end + version '1.9' do self.release = '1.9.4' self.base_url = "https://leafletjs.com/reference.html" diff --git a/lib/docs/scrapers/mdn/css.rb b/lib/docs/scrapers/mdn/css.rb index 42ace85097..5247481579 100644 --- a/lib/docs/scrapers/mdn/css.rb +++ b/lib/docs/scrapers/mdn/css.rb @@ -1,6 +1,6 @@ module Docs class Css < Mdn - # release = '2024-11-17' + # release = '2025-06-01' self.name = 'CSS' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/CSS' self.root_path = '/Reference' diff --git a/lib/docs/scrapers/mdn/dom.rb b/lib/docs/scrapers/mdn/dom.rb index 322d65d03f..88facbeda9 100644 --- a/lib/docs/scrapers/mdn/dom.rb +++ b/lib/docs/scrapers/mdn/dom.rb @@ -1,6 +1,6 @@ module Docs class Dom < Mdn - # release = '2025-01-30' + # release = '2025-06-01' self.name = 'Web APIs' self.slug = 'dom' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/API' diff --git a/lib/docs/scrapers/mdn/html.rb b/lib/docs/scrapers/mdn/html.rb index 70b75917ac..fcec226ed5 100644 --- a/lib/docs/scrapers/mdn/html.rb +++ b/lib/docs/scrapers/mdn/html.rb @@ -2,7 +2,7 @@ module Docs class Html < Mdn prepend FixInternalUrlsBehavior - # release = '2024-08-20' + # release = '2025-06-01' self.name = 'HTML' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/HTML' self.links = { @@ -22,11 +22,5 @@ class Html < Mdn '/Element/h5' => '/Element/Heading_Elements', '/Element/h6' => '/Element/Heading_Elements', '/Global_attributes/data-%2A' => '/Global_attributes/data-*' } - - options[:fix_urls] = ->(url) do - url.sub! 'https://developer.mozilla.org/en-US/docs/HTML/', "#{Html.base_url}/" unless url.include?('Content_categories') - url - end - end end diff --git a/lib/docs/scrapers/mdn/javascript.rb b/lib/docs/scrapers/mdn/javascript.rb index 28268b8112..28c0808cee 100644 --- a/lib/docs/scrapers/mdn/javascript.rb +++ b/lib/docs/scrapers/mdn/javascript.rb @@ -3,7 +3,7 @@ class Javascript < Mdn prepend FixInternalUrlsBehavior prepend FixRedirectionsBehavior - # release = '2025-04-15' + # release = '2025-06-01' self.name = 'JavaScript' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference' self.links = { diff --git a/lib/docs/scrapers/mdn/mdn.rb b/lib/docs/scrapers/mdn/mdn.rb index ebc482e7cc..c574345992 100644 --- a/lib/docs/scrapers/mdn/mdn.rb +++ b/lib/docs/scrapers/mdn/mdn.rb @@ -18,7 +18,7 @@ class Mdn < UrlScraper } options[:attribution] = <<-HTML - © 2005–2024 MDN contributors.
+ © 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later. HTML diff --git a/lib/docs/scrapers/nginx.rb b/lib/docs/scrapers/nginx.rb index 3e401c6362..e7af592414 100644 --- a/lib/docs/scrapers/nginx.rb +++ b/lib/docs/scrapers/nginx.rb @@ -2,7 +2,7 @@ module Docs class Nginx < UrlScraper self.name = 'nginx' self.type = 'nginx' - self.release = '1.27.1' + self.release = '1.29.0' self.base_url = 'https://nginx.org/en/docs/' self.links = { home: 'https://nginx.org/', @@ -23,7 +23,7 @@ class Nginx < UrlScraper # http://nginx.org/LICENSE options[:attribution] = <<-HTML © 2002-2021 Igor Sysoev
- © 2011-2024 Nginx, Inc.
+ © 2011-2025 Nginx, Inc.
Licensed under the BSD License. HTML diff --git a/lib/docs/scrapers/nginx_lua_module.rb b/lib/docs/scrapers/nginx_lua_module.rb index 9fcbab00ae..d704c38931 100644 --- a/lib/docs/scrapers/nginx_lua_module.rb +++ b/lib/docs/scrapers/nginx_lua_module.rb @@ -2,8 +2,9 @@ module Docs class NginxLuaModule < Github self.name = 'nginx / Lua Module' self.slug = 'nginx_lua_module' - self.release = '0.10.13' - self.base_url = "https://github.com/openresty/lua-nginx-module/tree/v#{self.release}/" + self.release = '0.10.28' + self.base_url = "https://github.com/openresty/lua-nginx-module/blob/v#{self.release}/" + self.root_path = 'README.markdown' self.links = { code: 'https://github.com/openresty/lua-nginx-module' } @@ -11,13 +12,14 @@ class NginxLuaModule < Github html_filters.push 'nginx_lua_module/clean_html', 'nginx_lua_module/entries', 'title' options[:root_title] = 'ngx_http_lua_module' - options[:container] = '#readme > article' - + options[:container] = '.markdown-body' + options[:max_image_size] = 256_000 options[:attribution] = <<-HTML © 2009–2017 Xiaozhe Wang (chaoslawful)
- © 2009–2018 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.
+ © 2009–2019 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.
Licensed under the BSD License. HTML + options[:skip_patterns] = [/\.png/] def get_latest_version(opts) tags = get_github_tags('openresty', 'lua-nginx-module', opts) diff --git a/lib/docs/scrapers/node.rb b/lib/docs/scrapers/node.rb index d8dc6cb484..ea9af21225 100644 --- a/lib/docs/scrapers/node.rb +++ b/lib/docs/scrapers/node.rb @@ -24,10 +24,15 @@ class Node < UrlScraper HTML version do - self.release = '22.3.0' + self.release = '24.1.0' self.base_url = 'https://nodejs.org/api/' end + version '22 LTS' do + self.release = '22.16.0' + self.base_url = 'https://nodejs.org/dist/latest-v22.x/docs/api/' + end + version '20 LTS' do self.release = '20.15.01' self.base_url = 'https://nodejs.org/dist/latest-v20.x/docs/api/' diff --git a/lib/docs/scrapers/numpy.rb b/lib/docs/scrapers/numpy.rb index 6ecca0a87e..a679caaae2 100644 --- a/lib/docs/scrapers/numpy.rb +++ b/lib/docs/scrapers/numpy.rb @@ -1,4 +1,8 @@ module Docs + # Requires downloading the documents to local disk first. + # Go to https://numpy.org/doc/, click "HTML+zip" to download + # (example url: https://numpy.org/doc/2.2/numpy-html.zip), + # then extract into "docs/numpy~#{version}/" class Numpy < FileScraper self.name = 'NumPy' self.type = 'sphinx' @@ -26,6 +30,18 @@ class Numpy < FileScraper Licensed under the 3-clause BSD License. HTML + version '2.2' do + self.release = '2.2' + self.base_url = "https://numpy.org/doc/#{self.version}/" + options[:container] = nil + end + + version '2.1' do + self.release = '2.1' + self.base_url = "https://numpy.org/doc/#{self.version}/" + options[:container] = nil + end + version '2.0' do self.release = '2.0.1' self.base_url = "https://numpy.org/doc/#{self.version}/" diff --git a/lib/docs/scrapers/octave.rb b/lib/docs/scrapers/octave.rb index 085d6fd804..ee798d58dc 100644 --- a/lib/docs/scrapers/octave.rb +++ b/lib/docs/scrapers/octave.rb @@ -4,8 +4,8 @@ class Octave < UrlScraper self.type = 'octave' self.root_path = 'index.html' self.links = { - home: 'http://www.octave.org/', - code: 'http://www.octave.org/hg/octave' + home: 'https://www.octave.org/', + code: 'https://www.octave.org/hg/octave' } html_filters.push 'octave/clean_html', 'octave/entries', 'title' @@ -23,12 +23,17 @@ class Octave < UrlScraper options[:root_title] = 'GNU Octave' options[:attribution] = <<-HTML - © 1996–2023 The Octave Project Developers
+ © 1996–2025 The Octave Project Developers
Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions. HTML + version '10' do + self.release = '10.1.0' + self.base_url = "https://docs.octave.org/v#{self.release}/" + end + version '9' do self.release = '9.2.0' self.base_url = "https://docs.octave.org/v#{self.release}/" diff --git a/lib/docs/scrapers/pandas.rb b/lib/docs/scrapers/pandas.rb index c95f75618a..872930d31c 100644 --- a/lib/docs/scrapers/pandas.rb +++ b/lib/docs/scrapers/pandas.rb @@ -11,13 +11,15 @@ class Pandas < FileScraper options[:skip] = %w(internals.html release.html contributing.html whatsnew.html) options[:skip_patterns] = [/whatsnew\//] + # https://github.com/pandas-dev/pandas/blob/main/LICENSE options[:attribution] = <<-HTML - © 2008–2022, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
+ © 2008–2011, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
+ © 2011–2025, Open source contributors
Licensed under the 3-clause BSD License. HTML version '2' do - self.release = '2.2.2' + self.release = '2.3.0' self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" html_filters.push 'pandas/clean_html', 'pandas/entries' diff --git a/lib/docs/scrapers/perl.rb b/lib/docs/scrapers/perl.rb index 6c9febb338..2792132b39 100644 --- a/lib/docs/scrapers/perl.rb +++ b/lib/docs/scrapers/perl.rb @@ -19,11 +19,21 @@ class Perl < UrlScraper options[:skip_patterns] = [/\Afunctions/, /\Avariables/, /\.pdf/, /delta/] options[:attribution] = <<-HTML - © 1993–2023 Larry Wall and others
+ © 1993–2025 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation. HTML + version '5.42' do + self.release = '5.42.0' + self.base_url = "https://perldoc.perl.org/#{release}/" + end + + version '5.40' do + self.release = '5.40.2' + self.base_url = "https://perldoc.perl.org/#{release}/" + end + version '5.38' do self.release = '5.38.0' self.base_url = "https://perldoc.perl.org/#{release}/" diff --git a/lib/docs/scrapers/playwright.rb b/lib/docs/scrapers/playwright.rb index 57f908a4b2..8726f2f0b6 100644 --- a/lib/docs/scrapers/playwright.rb +++ b/lib/docs/scrapers/playwright.rb @@ -2,7 +2,7 @@ module Docs class Playwright < UrlScraper self.name = 'Playwright' self.type = 'simple' - self.release = '1.52.0' + self.release = '1.53.2' self.base_url = 'https://playwright.dev/docs/' self.root_path = 'intro' self.links = { diff --git a/lib/docs/scrapers/point_cloud_library.rb b/lib/docs/scrapers/point_cloud_library.rb index 2fdc609de5..7c49965497 100644 --- a/lib/docs/scrapers/point_cloud_library.rb +++ b/lib/docs/scrapers/point_cloud_library.rb @@ -9,7 +9,7 @@ class PointCloudLibrary < UrlScraper self.initial_paths = [ "https://pointclouds.org/documentation/hierarchy.html" ] - self.release = '1.13.0' + self.release = '1.15.0' self.links = { home: 'https://pointclouds.org/', diff --git a/lib/docs/scrapers/postgresql.rb b/lib/docs/scrapers/postgresql.rb index e54cc8a513..6ce5bbf8aa 100644 --- a/lib/docs/scrapers/postgresql.rb +++ b/lib/docs/scrapers/postgresql.rb @@ -17,6 +17,7 @@ class Postgresql < UrlScraper options[:title] = false options[:root_title] = 'PostgreSQL' options[:follow_links] = ->(filter) { filter.initial_page? } + options[:rate_limit] = 200 options[:skip] = %w( index.html @@ -51,12 +52,12 @@ class Postgresql < UrlScraper /\Aunsupported-features/ ] options[:attribution] = <<-HTML - © 1996–2024 The PostgreSQL Global Development Group
+ © 1996–2025 The PostgreSQL Global Development Group
Licensed under the PostgreSQL License. HTML version '17' do - self.release = '17.1' + self.release = '17.5' self.base_url = "https://www.postgresql.org/docs/#{version}/" end diff --git a/lib/docs/scrapers/prettier.rb b/lib/docs/scrapers/prettier.rb index 5e6e0757a2..888a042582 100644 --- a/lib/docs/scrapers/prettier.rb +++ b/lib/docs/scrapers/prettier.rb @@ -2,7 +2,7 @@ module Docs class Prettier < UrlScraper self.name = 'Prettier' self.type = 'simple' - self.release = '3.5.3' + self.release = '3.6.2' self.base_url = 'https://prettier.io/docs/' self.links = { home: 'https://prettier.io/', diff --git a/lib/docs/scrapers/python.rb b/lib/docs/scrapers/python.rb index 781b0df9a2..fa4ca6dbe8 100644 --- a/lib/docs/scrapers/python.rb +++ b/lib/docs/scrapers/python.rb @@ -28,7 +28,7 @@ class Python < UrlScraper HTML version '3.13' do - self.release = '3.13.2' + self.release = '3.13.5' self.base_url = "https://docs.python.org/#{self.version}/" html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' diff --git a/lib/docs/scrapers/pytorch.rb b/lib/docs/scrapers/pytorch.rb index ae79ec36ac..cfa1d51010 100644 --- a/lib/docs/scrapers/pytorch.rb +++ b/lib/docs/scrapers/pytorch.rb @@ -12,20 +12,56 @@ class Pytorch < UrlScraper options[:skip] = ['cpp_index.html', 'deploy.html', 'packages.html', 'py-modindex.html', 'genindex.html'] options[:skip_patterns] = [/\Acommunity/, /\A_modules/, /\Anotes/, /\Aorg\/pytorch\//] + options[:max_image_size] = 1_000_000 options[:attribution] = <<-HTML - © 2024, PyTorch Contributors
+ © 2025, PyTorch Contributors
PyTorch has a BSD-style license, as found in the LICENSE file. HTML - version '2' do + version '2.7' do + self.release = '2.7' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.6' do + self.release = '2.6' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.5' do + self.release = '2.5' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.4' do + self.release = '2.4' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.3' do + self.release = '2.3' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.2' do + self.release = '2.2' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.1' do self.release = '2.1' - self.base_url = "https://pytorch.org/docs/#{release}/" + self.base_url = "https://docs.pytorch.org/docs/#{release}/" + end + + version '2.0' do + self.release = '2.0' + self.base_url = "https://docs.pytorch.org/docs/#{release}/" end - version '1' do + version '1.13' do self.release = '1.13' - self.base_url = "https://pytorch.org/docs/#{release}/" + self.base_url = "https://docs.pytorch.org/docs/#{release}/" end def get_latest_version(opts) diff --git a/lib/docs/scrapers/q.rb b/lib/docs/scrapers/q.rb index a4c449c02d..0ba0cfa937 100644 --- a/lib/docs/scrapers/q.rb +++ b/lib/docs/scrapers/q.rb @@ -1,5 +1,5 @@ module Docs - class Q < Github + class Q < UrlScraper self.name = 'Q' self.release = '1.5.1' self.base_url = 'https://github.com/kriskowal/q/wiki/' @@ -16,7 +16,7 @@ class Q < Github options[:skip_links] = true options[:attribution] = <<-HTML - © 2009–2017 Kristopher Michael Kowal
+ © 2009–2018 Kristopher Michael Kowal
Licensed under the MIT License. HTML diff --git a/lib/docs/scrapers/qunit.rb b/lib/docs/scrapers/qunit.rb index ff13fbdb7b..125605e373 100644 --- a/lib/docs/scrapers/qunit.rb +++ b/lib/docs/scrapers/qunit.rb @@ -4,7 +4,7 @@ module Docs class Qunit < UrlScraper self.name = 'QUnit' self.type = 'qunit' - self.release = '2.21.0' + self.release = '2.24.1' self.base_url = 'https://qunitjs.com/api/' self.root_path = '/' self.links = { diff --git a/lib/docs/scrapers/ramda.rb b/lib/docs/scrapers/ramda.rb index 897b227f21..09a367f6df 100644 --- a/lib/docs/scrapers/ramda.rb +++ b/lib/docs/scrapers/ramda.rb @@ -1,8 +1,6 @@ module Docs class Ramda < UrlScraper self.type = 'ramda' - self.release = '0.27.0' - self.base_url = "https://ramdajs.com/#{release}/docs/" self.links = { home: 'http://ramdajs.com/', code: 'https://github.com/ramda/ramda/' @@ -12,13 +10,17 @@ class Ramda < UrlScraper options[:title] = 'Ramda' options[:attribution] = <<-HTML - © 2013–2020 Scott Sauyet and Michael Hurley
+ © 2013–2025 Scott Sauyet and Michael Hurley
Licensed under the MIT License. HTML + version do + self.release = '0.31.3' + self.base_url = "https://ramdajs.com/#{release}/docs/" + end + def get_latest_version(opts) - doc = fetch_doc('https://ramdajs.com/docs/', opts) - doc.at_css('.navbar-brand > .version').content[1..-1] + get_npm_version('ramda', opts) end end end diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 21de1602c1..8ccdaea376 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -3,7 +3,7 @@ module Docs class Rust < UrlScraper self.type = 'rust' - self.release = '1.86.0' + self.release = '1.88.0' self.base_url = 'https://doc.rust-lang.org/' self.root_path = 'book/index.html' self.initial_paths = %w( diff --git a/lib/docs/scrapers/sass.rb b/lib/docs/scrapers/sass.rb index d0c0a93537..1a1296c04c 100644 --- a/lib/docs/scrapers/sass.rb +++ b/lib/docs/scrapers/sass.rb @@ -1,7 +1,7 @@ module Docs class Sass < UrlScraper self.type = 'yard' - self.release = '1.85.0' + self.release = '1.89.1' self.base_url = 'https://sass-lang.com/documentation' self.root_path = 'index.html' self.links = { diff --git a/lib/docs/scrapers/sqlite.rb b/lib/docs/scrapers/sqlite.rb index 7de3e57f12..c7a4cd36df 100644 --- a/lib/docs/scrapers/sqlite.rb +++ b/lib/docs/scrapers/sqlite.rb @@ -2,7 +2,7 @@ module Docs class Sqlite < UrlScraper self.name = 'SQLite' self.type = 'sqlite' - self.release = '3.49.1' + self.release = '3.50.0' self.base_url = 'https://sqlite.org/' self.root_path = 'docs.html' self.initial_paths = %w(keyword_index.html) diff --git a/lib/docs/scrapers/svelte.rb b/lib/docs/scrapers/svelte.rb index aac8738920..537c2cc063 100644 --- a/lib/docs/scrapers/svelte.rb +++ b/lib/docs/scrapers/svelte.rb @@ -13,7 +13,7 @@ class Svelte < UrlScraper # https://github.com/sveltejs/svelte/blob/master/LICENSE.md options[:attribution] = <<-HTML - © 2016–2024 Rich Harris and contributors
+ © 2016–2025 Rich Harris and contributors
Licensed under the MIT License. HTML @@ -21,7 +21,7 @@ class Svelte < UrlScraper html_filters.push 'svelte/entries', 'svelte/clean_html' version do - self.release = '5.2.3' + self.release = '5.33.11' end version '4' do diff --git a/lib/docs/scrapers/tailwindcss.rb b/lib/docs/scrapers/tailwindcss.rb index 71b84d5f3b..fdf045994c 100644 --- a/lib/docs/scrapers/tailwindcss.rb +++ b/lib/docs/scrapers/tailwindcss.rb @@ -3,9 +3,7 @@ class Tailwindcss < UrlScraper self.name = 'Tailwind CSS' self.type = 'tailwindcss' self.slug = 'tailwindcss' - self.base_url = 'https://tailwindcss.com/docs' self.root_path = '/' - self.release = '3.3.2' self.links = { home: 'https://tailwindcss.com/', code: 'https://github.com/tailwindlabs/tailwindcss' @@ -16,27 +14,6 @@ class Tailwindcss < UrlScraper # Disable the clean text filter which removes empty nodes - we'll do it ourselves more selectively text_filters.replace("clean_text", "tailwindcss/noop") - # Fix redirects from older tailwind 2 docs - options[:fix_urls] = lambda do |url| - if url.include? "installation/" - break "/docs/installation" - end - - if url.end_with? "/breakpoints" - break "/docs/screens#{/#.*$/.match(url)}" - end - if url.end_with? "/adding-base-styles" - break "/docs/adding-custom-styles#adding-base-styles" - end - if url.end_with? "/ring-opacity" - break "/docs/ring-color#changing-the-opacity" - end - - if url.match(/\/colors#?/) - break "/docs/customizing-colors#{/#.*$/.match(url)}" - end - end - options[:skip_patterns] = [ # Skip setup instructions /\/browser-support$/, @@ -49,9 +26,51 @@ class Tailwindcss < UrlScraper #Obtainable from https://github.com/tailwindlabs/tailwindcss/blob/master/LICENSE options[:attribution] = <<-HTML - © 2022 Tailwind Labs Inc. + © Tailwind Labs Inc. HTML + version do + self.release = '4.1.11' + self.base_url = 'https://tailwindcss.com/docs' + + # Fix redirects + options[:fix_urls] = lambda do |url| + if url.include? "installation/" + break "/docs/installation" + end + + if url.end_with? "text-color" + break "/docs/color" + end + end + end + + version '3' do + self.release = '3.4.17' + self.base_url = 'https://v3.tailwindcss.com/docs' + + # Fix redirects from older tailwind 2 docs + options[:fix_urls] = lambda do |url| + if url.include? "installation/" + break "/docs/installation" + end + + if url.end_with? "/breakpoints" + break "/docs/screens#{/#.*$/.match(url)}" + end + if url.end_with? "/adding-base-styles" + break "/docs/adding-custom-styles#adding-base-styles" + end + if url.end_with? "/ring-opacity" + break "/docs/ring-color#changing-the-opacity" + end + + if url.match(/\/colors#?/) + break "/docs/customizing-colors#{/#.*$/.match(url)}" + end + end + end + def get_latest_version(opts) get_latest_github_release('tailwindlabs', 'tailwindcss', opts) end diff --git a/lib/docs/scrapers/vertx.rb b/lib/docs/scrapers/vertx.rb new file mode 100644 index 0000000000..8163098087 --- /dev/null +++ b/lib/docs/scrapers/vertx.rb @@ -0,0 +1,46 @@ +module Docs + class Vertx < UrlScraper + self.name = 'Vert.x' + self.slug = 'vertx' + self.type = 'vertx' + self.links = { + home: 'http://vertx.io', + code: 'https://github.com/eclipse-vertx/vert.x' + } + + html_filters.push 'vertx/entries', 'vertx/clean_html' + + options[:attribution] = <<-HTML + © 2025 Eclipse Vert.x™
+ Eclipse Vert.x™ is open source and dual-licensed under the Eclipse Public License 2.0 and the Apache License 2.0.
+ Website design by Michel Krämer. + HTML + + options[:skip_patterns] = [ + /api/, + /5.0.0/, + /apidocs/, + /blog/, + ] + + version '5' do + self.release = '5.0.0' + self.base_url = "https://vertx.io/docs/" + end + + version '4' do + self.release = '4.5.15' + self.base_url = "https://vertx.io/docs/#{self.release}" + end + + version '3' do + self.release = '3.9.16' + self.base_url = "https://vertx.io/docs/#{self.release}" + end + + def get_latest_version(opts) + doc = fetch_doc('https://repo1.maven.org/maven2/io/vertx/vertx-stack-manager/maven-metadata.xml', opts) + doc.css('version')[-1].text + end + end +end diff --git a/lib/docs/scrapers/vite.rb b/lib/docs/scrapers/vite.rb index ee26d8b9c6..c02b85d894 100644 --- a/lib/docs/scrapers/vite.rb +++ b/lib/docs/scrapers/vite.rb @@ -22,10 +22,15 @@ class Vite < UrlScraper html_filters.push 'vite/entries', 'vite/clean_html' version do - self.release = '6.3.2' + self.release = '7.0.0' self.base_url = 'https://vite.dev/' end + version '6' do + self.release = '6.3.5' + self.base_url = 'https://v6.vite.dev/' + end + version '5' do self.release = '5.4.11' self.base_url = 'https://v5.vite.dev/' diff --git a/lib/docs/scrapers/zsh.rb b/lib/docs/scrapers/zsh.rb new file mode 100644 index 0000000000..b4705960b3 --- /dev/null +++ b/lib/docs/scrapers/zsh.rb @@ -0,0 +1,33 @@ +module Docs + class Zsh < UrlScraper + self.type = 'zsh' + self.release = '5.9.0' + self.base_url = 'https://zsh.sourceforge.io/Doc/Release/' + self.root_path = 'index.html' + self.links = { + home: 'https://zsh.sourceforge.io/', + code: 'https://sourceforge.net/p/zsh/web/ci/master/tree/', + } + + options[:skip] = %w( + zsh_toc.html + zsh_abt.html + The-Z-Shell-Manual.html + Introduction.html + ) + options[:skip_patterns] = [/-Index.html/] + + html_filters.push 'zsh/entries', 'zsh/clean_html' + + options[:attribution] = <<-HTML + The Z Shell is copyright © 1992–2017 Paul Falstad, Richard Coleman, + Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and others.
+ Licensed under the MIT License. + HTML + + def get_latest_version(opts) + body = fetch('https://zsh.sourceforge.io/Doc/Release', opts) + body.scan(/Zsh version ([0-9.]+)/)[0][0] + end + end +end diff --git a/public/icons/docs/es_toolkit/16.png b/public/icons/docs/es_toolkit/16.png new file mode 100644 index 0000000000..d63aff1c35 Binary files /dev/null and b/public/icons/docs/es_toolkit/16.png differ diff --git a/public/icons/docs/es_toolkit/16@2x.png b/public/icons/docs/es_toolkit/16@2x.png new file mode 100644 index 0000000000..40dff7a352 Binary files /dev/null and b/public/icons/docs/es_toolkit/16@2x.png differ diff --git a/public/icons/docs/es_toolkit/SOURCE b/public/icons/docs/es_toolkit/SOURCE new file mode 100644 index 0000000000..90444ebb34 --- /dev/null +++ b/public/icons/docs/es_toolkit/SOURCE @@ -0,0 +1 @@ +https://es-toolkit.slash.page/favicon-100x100.png diff --git a/public/icons/docs/numpy/16.png b/public/icons/docs/numpy/16.png index ca5252ef09..a24fcb151b 100644 Binary files a/public/icons/docs/numpy/16.png and b/public/icons/docs/numpy/16.png differ diff --git a/public/icons/docs/numpy/16@2x.png b/public/icons/docs/numpy/16@2x.png index 1fa447c509..ad2df91191 100644 Binary files a/public/icons/docs/numpy/16@2x.png and b/public/icons/docs/numpy/16@2x.png differ diff --git a/public/icons/docs/numpy/SOURCE b/public/icons/docs/numpy/SOURCE index 709633f233..eb744c2c9e 100644 --- a/public/icons/docs/numpy/SOURCE +++ b/public/icons/docs/numpy/SOURCE @@ -1 +1 @@ -https://github.com/numpy/numpy/blob/master/doc/source/_static/numpylogo.svg +https://github.com/numpy/numpy/tree/main/doc/source/_static/favicon diff --git a/public/icons/docs/vertx/16.png b/public/icons/docs/vertx/16.png new file mode 100644 index 0000000000..75de4cd514 Binary files /dev/null and b/public/icons/docs/vertx/16.png differ diff --git a/public/icons/docs/vertx/16@2x.png b/public/icons/docs/vertx/16@2x.png new file mode 100644 index 0000000000..e7510a59c9 Binary files /dev/null and b/public/icons/docs/vertx/16@2x.png differ diff --git a/public/icons/docs/vertx/SOURCE b/public/icons/docs/vertx/SOURCE new file mode 100644 index 0000000000..81ae591749 --- /dev/null +++ b/public/icons/docs/vertx/SOURCE @@ -0,0 +1 @@ +https://avatars.githubusercontent.com/u/8124623?s=200&v=4 diff --git a/public/icons/docs/zsh/16.png b/public/icons/docs/zsh/16.png new file mode 100644 index 0000000000..05dc56e07e Binary files /dev/null and b/public/icons/docs/zsh/16.png differ diff --git a/public/icons/docs/zsh/16@2x.png b/public/icons/docs/zsh/16@2x.png new file mode 100644 index 0000000000..014d7ab78a Binary files /dev/null and b/public/icons/docs/zsh/16@2x.png differ diff --git a/public/icons/docs/zsh/SOURCE b/public/icons/docs/zsh/SOURCE new file mode 100644 index 0000000000..70cc4aeed8 --- /dev/null +++ b/public/icons/docs/zsh/SOURCE @@ -0,0 +1,2 @@ +https://sourceforge.net/p/zsh/web/ci/master/tree/favicon.png +