118 lines
3.5 KiB
Ruby
118 lines
3.5 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
require "jekyll/hooks"
|
||
|
require "jekyll/document"
|
||
|
require "json"
|
||
|
|
||
|
##
|
||
|
# This singleton facilitates production of an indexable JSON representation of the content to populate a data source
|
||
|
# to provide search functionality.
|
||
|
|
||
|
module Jekyll::ContentIndexer
|
||
|
|
||
|
##
|
||
|
# The collection that will get stores as the output
|
||
|
|
||
|
@data = []
|
||
|
|
||
|
##
|
||
|
# Pattern to identify documents that should be excluded based on their URL
|
||
|
|
||
|
@excluded_paths = /\.(css|js|json|map|xml|txt|yml)$/i.freeze
|
||
|
|
||
|
##
|
||
|
# Pattern to identify block HTML tags (not comprehensive)
|
||
|
|
||
|
@html_block_tags = /\s*<[?\/]?(article|blockquote|d[dlt]|div|fieldset|form|h|li|main|nav|[ou]l|p|section|table|t[rd]).*?>\s*/im.freeze
|
||
|
|
||
|
##
|
||
|
# Pattern to identify certain HTML tags whose content should be excluded from indexing
|
||
|
|
||
|
@html_excluded_tags = /\s*<(head|style|script|h1).*?>.*?<\/\1>/im.freeze
|
||
|
|
||
|
##
|
||
|
# Initializes the singleton by recording the site
|
||
|
|
||
|
def self.init(site)
|
||
|
@site = site
|
||
|
end
|
||
|
|
||
|
##
|
||
|
# Processes a Document or Page and adds it to the collection
|
||
|
|
||
|
def self.add(page)
|
||
|
return if @excluded_paths.match(page.url)
|
||
|
|
||
|
content = page.content
|
||
|
.gsub(@html_excluded_tags, ' ') # Strip certain HTML blocks
|
||
|
.gsub(@html_block_tags, "\n") # Strip some block HTML tags, replacing with newline
|
||
|
.gsub(/\s*<[?\/!]?[a-z]+.*?>\s*/im, ' ') # Strip all remaining HTML tags
|
||
|
.gsub(/\s*[\r\n]+\s*/, "\n") # Clean line-breaks
|
||
|
.gsub(/\s{2,}/, ' ') # Trim long spaces
|
||
|
.gsub(/\s+([.:;,)!\]?])/, '\1') # Remove spaces before some punctuations
|
||
|
.strip # Trim leading and tailing whitespaces
|
||
|
|
||
|
return if content.empty?
|
||
|
|
||
|
url = @site.config["baseurl"] + page.url
|
||
|
|
||
|
# Produce a breadcrumb
|
||
|
ancestors = []
|
||
|
if page.instance_of?(Jekyll::Document)
|
||
|
ancestors.push(@site.config.dig("just_the_docs", "collections", page.collection&.label, "name"))
|
||
|
end
|
||
|
|
||
|
ancestors.push(page.data["grand_parent"]) unless
|
||
|
page.data["grand_parent"].nil? ||
|
||
|
page.data["grand_parent"]&.empty? ||
|
||
|
ancestors.include?(page.data["grand_parent"]) # Make sure collection name is not added
|
||
|
|
||
|
ancestors.push(page.data["parent"]) unless
|
||
|
page.data["parent"].nil? ||
|
||
|
page.data["parent"]&.empty? ||
|
||
|
ancestors.include?(page.data["parent"]) # Make sure collection name is not added
|
||
|
|
||
|
data = {
|
||
|
url: url,
|
||
|
title: page.data["title"],
|
||
|
content: content,
|
||
|
ancestors: ancestors,
|
||
|
type: "DOCS"
|
||
|
}
|
||
|
|
||
|
@data.push(data)
|
||
|
end
|
||
|
|
||
|
##
|
||
|
# Saves the collection as a JSON file
|
||
|
|
||
|
def self.save
|
||
|
File.open(File.join(@site.config["destination"], "search-index.json"), 'w') do |f|
|
||
|
f.puts JSON.pretty_generate(@data)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# Before any Document or Page is processed, initialize the ContentIndexer
|
||
|
|
||
|
Jekyll::Hooks.register :site, :pre_render do |site|
|
||
|
Jekyll::ContentIndexer.init(site)
|
||
|
end
|
||
|
|
||
|
# Process a Page as soon as its content is ready
|
||
|
|
||
|
Jekyll::Hooks.register :pages, :post_convert do |page|
|
||
|
Jekyll::ContentIndexer.add(page)
|
||
|
end
|
||
|
|
||
|
# Process a Document as soon as its content is ready
|
||
|
|
||
|
Jekyll::Hooks.register :documents, :post_convert do |document|
|
||
|
Jekyll::ContentIndexer.add(document)
|
||
|
end
|
||
|
|
||
|
# Save the produced collection after Jekyll is done writing all its stuff
|
||
|
|
||
|
Jekyll::Hooks.register :site, :post_write do |_|
|
||
|
Jekyll::ContentIndexer.save()
|
||
|
end
|