diff --git a/assets/javascripts/templates/pages/about_tmpl.coffee b/assets/javascripts/templates/pages/about_tmpl.coffee
index a366449a..6fdba573 100644
--- a/assets/javascripts/templates/pages/about_tmpl.coffee
+++ b/assets/javascripts/templates/pages/about_tmpl.coffee
@@ -587,6 +587,11 @@ credits = [
'2006-2021 Andreas Rumpf',
'MIT',
'https://github.com/nim-lang/Nim#license'
+ ], [
+ 'Nix',
+ '2022 NixOS Contributors',
+ 'LGPLv2.1',
+ 'https://github.com/NixOS/nix#license'
], [
'Node.js',
'Joyent, Inc. and other Node contributors
Node.js is a trademark of Joyent, Inc.',
diff --git a/lib/docs/filters/nix/clean_html.rb b/lib/docs/filters/nix/clean_html.rb
new file mode 100644
index 00000000..731fc0cf
--- /dev/null
+++ b/lib/docs/filters/nix/clean_html.rb
@@ -0,0 +1,113 @@
+module Docs
+ class Nix
+ class CleanHtmlFilter < Filter
+ def call
+ if subpath == 'nixpkgs/stable/index.html'
+ new_root = Nokogiri::XML::Node.new 'div', doc.document
+
+ # lib functions
+ lib_sections = xpath("//*[@id='sec-functions-library']/ancestor::section[1]/section/section")
+ lib_sections.css('.titlepage .title').each do |title|
+ title.name = 'h2'
+ strip_section_number(title.children)
+ title['id'] = title.content.gsub(/[^a-zA-Z0-9]/, '-')
+ end
+ lib_sections.css('.example .title strong').each do |title|
+ title.content = title.content.sub(/^Example ([0-9.]+)/, 'Example: ')
+ end
+ new_root.add_child(lib_sections)
+
+ # fetchers
+ fetcher_sections = xpath("//*[@id='chap-pkgs-fetchers']/ancestor::section[1]/section[position()>1]")
+ fetcher_sections.css('.titlepage .title').each do |title|
+ strip_section_number(title.children)
+ prefix_with(title, 'pkgs') if title.name == 'h2'
+ end
+ new_root.add_child(fetcher_sections)
+
+ # trivial builders
+ trivial_sections = xpath("//*[@id='chap-trivial-builders']/ancestor::section[1]/section")
+ trivial_sections.css('.titlepage .title').each do |title|
+ strip_section_number(title.children)
+ prefix_with(title, 'pkgs') if title.name == 'h2'
+ end
+ new_root.add_child(trivial_sections)
+
+ # special builders
+ special_sections = xpath("//*[@id='chap-special']/ancestor::section[1]/section")
+ special_sections.css('.titlepage .title').each do |title|
+ strip_section_number(title.children)
+ if title.name == 'h2'
+ title.children[0].wrap('')
+ prefix_with(title, 'pkgs')
+ end
+ end
+ new_root.add_child(special_sections)
+
+ # image builders
+ image_sections = xpath("//*[@id='chap-images']/ancestor::section[1]/section")
+ image_sections.css('.titlepage .title').each do |title|
+ strip_section_number(title.children)
+ title.children[0].wrap('')
+ end
+ image_sections.each do |section|
+ prefix = section.at_xpath('*[@class="titlepage"]//*[@class="title"]').content
+ next unless ["pkgs.dockerTools", "pkgs.ociTools"].include?(prefix)
+ section.xpath('section/*[@class="titlepage"]//*[@class="title"]').each do |title|
+ prefix_with(title, prefix)
+ title['data-add-to-index'] = ''
+ end
+ end
+ new_root.add_child(image_sections)
+
+ new_root
+ elsif subpath == 'nix/stable/expressions/builtins.html'
+ @doc = doc.at_css('main dl')
+
+ # strip out the first entry, `derivation`, the actual documentation
+ # exists in a separate page
+ derivation_dt = doc.children.at_css('dt')
+ if derivation_dt.content.starts_with?('derivation')
+ derivation_dt.remove
+ doc.children.at_css('dd').remove
+ else
+ raise RuntimeError.new('First entry is not derivation, update the scraper')
+ end
+
+ doc.css('dt').each do |title|
+ title.name = 'h2'
+ unwrap(title.at_css('a'))
+ title.children[0].children.before('builtins.')
+ end
+ doc.css('dd').each do |description|
+ description.name = 'div'
+ end
+
+ doc
+ else
+ doc
+ end
+ end
+
+ def strip_section_number(title_children)
+ while title_children.first.content == ''
+ title_children.shift.remove
+ end
+ first_text = title_children.first
+ return unless first_text.text?
+ first_text.content = first_text.content.sub(/[0-9.]+ ?/, '')
+ first_text.remove if first_text.blank?
+ end
+
+ def prefix_with(title_node, text)
+ title_node.css('code').each do |code|
+ code.content = "#{text}.#{code.content}" unless code.content.starts_with?("#{text}.")
+ end
+ end
+
+ def unwrap(node)
+ node.replace(node.inner_html)
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/nix/entries.rb b/lib/docs/filters/nix/entries.rb
new file mode 100644
index 00000000..f4ff093a
--- /dev/null
+++ b/lib/docs/filters/nix/entries.rb
@@ -0,0 +1,22 @@
+module Docs
+ class Nix
+ class EntriesFilter < Docs::EntriesFilter
+ def include_default_entry?
+ false
+ end
+
+ def additional_entries
+ css('h2, h3[data-add-to-index]').flat_map do |node|
+ node.css('code').map do |code|
+ title = code.content
+ index = title.rindex('.')
+ type = title[0...index]
+ name = title.match(/^[^\s]+/)[0]
+
+ [name, node['id'], type]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/nix.rb b/lib/docs/scrapers/nix.rb
new file mode 100644
index 00000000..ae549818
--- /dev/null
+++ b/lib/docs/scrapers/nix.rb
@@ -0,0 +1,31 @@
+module Docs
+ class Nix < UrlScraper
+ self.type = 'simple'
+ self.release = '2.8.0'
+ self.base_url = 'https://nixos.org/manual/'
+ self.root_path = 'nix/stable/expressions/builtins.html'
+ self.initial_paths = %w(
+ nix/stable/expressions/builtins.html
+ nixpkgs/stable/index.html)
+ self.links = {
+ home: 'https://nixos.org/',
+ code: 'https://github.com/NixOS/nix'
+ }
+
+ html_filters.push 'nix/clean_html', 'nix/entries'
+
+ options[:skip_links] = true
+
+ options[:attribution] = <<-HTML
+ © 2022 NixOS Contributors
+ Licensed under the LGPL License.
+ HTML
+
+ def get_latest_version(opts)
+ doc = fetch_doc('https://nixos.org/manual/nix/stable/', opts)
+ json = JSON.parse(doc.at_css('body')['data-nix-channels'])
+ channel = json.find { |c| c['channel'] == 'stable' }
+ channel['version']
+ end
+ end
+end
diff --git a/public/icons/docs/nix/16.png b/public/icons/docs/nix/16.png
new file mode 100644
index 00000000..13ad550b
Binary files /dev/null and b/public/icons/docs/nix/16.png differ
diff --git a/public/icons/docs/nix/16@2x.png b/public/icons/docs/nix/16@2x.png
new file mode 100644
index 00000000..5c1d1368
Binary files /dev/null and b/public/icons/docs/nix/16@2x.png differ
diff --git a/public/icons/docs/nix/SOURCE b/public/icons/docs/nix/SOURCE
new file mode 100644
index 00000000..c2f6c583
--- /dev/null
+++ b/public/icons/docs/nix/SOURCE
@@ -0,0 +1 @@
+https://github.com/NixOS/nixos-homepage/blob/master/logo/nixos-logo-only-hires.png