jekyll_plugin_template 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +35 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE.txt +121 -0
- data/PLUGIN_README.md +178 -0
- data/README.md +105 -0
- data/Rakefile +7 -0
- data/jekyll_plugin_template.gemspec +46 -0
- data/lib/category_combiner.rb +21 -0
- data/lib/category_index_generator.rb +39 -0
- data/lib/dumpers.rb +174 -0
- data/lib/jekyll_block_tag_plugin.rb +113 -0
- data/lib/jekyll_filter_template.rb +41 -0
- data/lib/jekyll_hook_examples.rb +72 -0
- data/lib/jekyll_hooks.rb +242 -0
- data/lib/jekyll_plugin_template/version.rb +5 -0
- data/lib/jekyll_plugin_template.rb +22 -0
- data/lib/jekyll_tag_plugin.rb +113 -0
- data/spec/jekyll_plugin_template_spec.rb +26 -0
- data/spec/nokogiri_test.rb +24 -0
- data/spec/run_this_first_data/lib/old_name/old_name.rb +5 -0
- data/spec/run_this_first_data/lib/old_name/version.rb +5 -0
- data/spec/run_this_first_data/lib/old_name.rb +5 -0
- data/spec/run_this_first_data/old_name.gemspec +9 -0
- data/spec/run_this_first_helper.rb +21 -0
- data/spec/run_this_first_spec.rb +40 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/status_persistence.txt +5 -0
- metadata +203 -0
data/lib/dumpers.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Methods to display Jekyll variable contents
|
4
|
+
module Dumpers
|
5
|
+
# See https://github.com/jekyll/jekyll/blob/master/lib/jekyll/collection.rb
|
6
|
+
# attr_reader :site, :label, :metadata
|
7
|
+
# attr_writer :docs
|
8
|
+
# Metadata is a hash with at least these keys: output[Boolean], permalink[String]
|
9
|
+
# selected methods: collection_dir, directory, entries, exists?, files, filtered_entries, relative_directory
|
10
|
+
def collection_as_string(collection, indent_spaces)
|
11
|
+
indent = " " * indent_spaces
|
12
|
+
result = <<~END_COLLECTION
|
13
|
+
'#{collection.label}' collection within '#{collection.relative_directory}' subdirectory
|
14
|
+
#{indent}Directory: #{collection.directory}
|
15
|
+
#{indent}Does the directory exist and is it not a symlink if in safe mode? #{collection.exists?}
|
16
|
+
#{indent}Collection_dir: #{collection.collection_dir}
|
17
|
+
#{indent}Metadata: #{collection.metadata}
|
18
|
+
#{indent}Static files: #{collection.files}
|
19
|
+
#{indent}Filtered entries: #{collection.filtered_entries}
|
20
|
+
END_COLLECTION
|
21
|
+
result.chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
def count_lines(string)
|
25
|
+
return string.split("\n").length if string
|
26
|
+
|
27
|
+
0
|
28
|
+
end
|
29
|
+
|
30
|
+
# Calling value.to_s blows up when a Jekyll::Excerpt
|
31
|
+
# Error message is unrelated to the problem, makes it hard to track down
|
32
|
+
# Be careful when converting values to string!
|
33
|
+
def safe_to_s(value)
|
34
|
+
return value.content if value.is_a? Jekyll::Excerpt
|
35
|
+
|
36
|
+
value.to_s
|
37
|
+
rescue StandardError => e
|
38
|
+
e.message
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param msg[String]
|
42
|
+
# @param document[Jekyll:Document] https://github.com/jekyll/jekyll/blob/master/lib/jekyll/document.rb
|
43
|
+
# attr_reader :path, :extname, :collection, :type; :site is too big to dump here, we already have it anyway
|
44
|
+
# Selected methods: date
|
45
|
+
def dump_document(logger, msg, document)
|
46
|
+
attributes = attributes_as_string(document, [:@path, :@extname, :@type])
|
47
|
+
data = document.data.map { |k, v| " #{k} = #{safe_to_s(v)}" }
|
48
|
+
logger.info do
|
49
|
+
<<~END_DOC
|
50
|
+
#{msg}
|
51
|
+
document dated #{document.date.to_date}:
|
52
|
+
relative_path: #{document.relative_path}:
|
53
|
+
#{attributes.join("\n")}
|
54
|
+
Is it a draft? #{document.draft?}
|
55
|
+
collection = #{collection_as_string(document.collection, 4)}
|
56
|
+
content not dumped because it would likely be too long
|
57
|
+
site not dumped also
|
58
|
+
data:
|
59
|
+
#{data.join("\n ").rstrip.chomp}
|
60
|
+
END_DOC
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param msg[String]
|
65
|
+
# @param page[Jekyll:Page] https://github.com/jekyll/jekyll/blob/master/lib/jekyll/page.rb
|
66
|
+
# attr_accessor :basename, :content, :data, :ext, :name, :output, :pager, :site
|
67
|
+
# Selected methods: dir, excerpt, path, permalink, url
|
68
|
+
def dump_page(logger, msg, page)
|
69
|
+
attributes = attributes_as_string(page, [:@basename, :@ext, :@name])
|
70
|
+
# site = page.site available if you need it
|
71
|
+
data = page.data.map { |k, v| " #{k} = #{v}" }
|
72
|
+
logger.info do
|
73
|
+
<<~END_PAGE
|
74
|
+
#{msg}\n page at #{page.dir}:
|
75
|
+
#{attributes.join("\n")}
|
76
|
+
Is it HTML? #{page.html?}; is it an index? #{page.index?}
|
77
|
+
Permalink: #{page.permalink}
|
78
|
+
URL: #{page.url}
|
79
|
+
content not dumped because it would likely be too long
|
80
|
+
site not dumped also
|
81
|
+
Excerpt: "#{page.excerpt}"
|
82
|
+
data:
|
83
|
+
#{data.join("\n")}
|
84
|
+
END_PAGE
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param msg[String]
|
89
|
+
# @param payload[Jekyll::Drops::UnifiedPayloadDrop] See https://github.com/jekyll/jekyll/blob/master/lib/jekyll/drops/unified_payload_drop.rb
|
90
|
+
# This is a mutable class.
|
91
|
+
# attr_accessor :content, :page, :layout, :paginator, :highlighter_prefix, :highlighter_suffix
|
92
|
+
# payload.page is a Jekyll::Drops::DocumentDrop, which contains this payload,
|
93
|
+
# see https://github.com/jekyll/jekyll/blob/master/lib/jekyll/drops/document_drop.rb
|
94
|
+
def dump_payload(logger, msg, payload)
|
95
|
+
result = <<~END_INFO
|
96
|
+
#{msg} payload:
|
97
|
+
content contains #{count_lines(payload.content)} lines.#{first_5_lines(payload.content)}
|
98
|
+
layout = #{payload.layout}
|
99
|
+
highlighter_prefix = #{payload.highlighter_prefix}
|
100
|
+
paginator and site not dumped.
|
101
|
+
END_INFO
|
102
|
+
logger.info { result.chomp }
|
103
|
+
end
|
104
|
+
|
105
|
+
def first_5_lines(string)
|
106
|
+
lines = string ? string.split("\n")[0..4] : []
|
107
|
+
return "\n first 5 lines:\n #{lines.join("\n ")}\n" if lines.length.positive?
|
108
|
+
|
109
|
+
""
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param msg[String]
|
113
|
+
# @param site[Jekyll::Site] https://github.com/jekyll/jekyll/blob/master/lib/jekyll/site.rb
|
114
|
+
# attr_accessor :baseurl, :converters, :data, :drafts, :exclude,
|
115
|
+
# :file_read_opts, :future, :gems, :generators, :highlighter,
|
116
|
+
# :include, :inclusions, :keep_files, :layouts, :limit_posts,
|
117
|
+
# :lsi, :pages, :permalink_style, :plugin_manager, :plugins,
|
118
|
+
# :reader, :safe, :show_drafts, :static_files, :theme, :time,
|
119
|
+
# :unpublished
|
120
|
+
# attr_reader :cache_dir, :config, :dest, :filter_cache, :includes_load_paths,
|
121
|
+
# :liquid_renderer, :profiler, :regenerator, :source
|
122
|
+
def dump_site(logger, msg, site) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
123
|
+
logger.info do
|
124
|
+
<<~END_INFO
|
125
|
+
#{msg} site
|
126
|
+
site is of type #{site.class}
|
127
|
+
site.time = #{site.time}
|
128
|
+
END_INFO
|
129
|
+
end
|
130
|
+
env = site.config['env']
|
131
|
+
if env
|
132
|
+
mode = env['JEKYLL_ENV']
|
133
|
+
logger.info { "site.config['env']['JEKYLL_ENV'] = #{mode}" }
|
134
|
+
else
|
135
|
+
logger.info { "site.config['env'] is undefined" }
|
136
|
+
end
|
137
|
+
site.collections.each do |key, _|
|
138
|
+
logger.info { "site.collections.#{key}" }
|
139
|
+
end
|
140
|
+
|
141
|
+
# key env contains all environment variables, quite verbose so output is reduced to just the "env" key
|
142
|
+
logger.info { "site.config has #{site.config.length} entries:" }
|
143
|
+
site.config.sort.each { |key, value| logger.info { " site.config.#{key} = '#{value}'" unless key == "env" } }
|
144
|
+
|
145
|
+
logger.info { "site.data has #{site.data.length} entries:" }
|
146
|
+
site.data.sort.each { |key, value| logger.info { " site.data.#{key} = '#{value}'" } }
|
147
|
+
|
148
|
+
logger.info { "site.documents has #{site.documents.length} entries." }
|
149
|
+
site.documents.each { |key, _value| logger.info "site.documents.#{key}" }
|
150
|
+
|
151
|
+
logger.info do
|
152
|
+
<<~END_INFO
|
153
|
+
site.keep_files has #{site.keep_files.length} entries.
|
154
|
+
site.keep_files: #{site.keep_files.sort}
|
155
|
+
site.pages has #{site.pages.length} entries.
|
156
|
+
END_INFO
|
157
|
+
end
|
158
|
+
|
159
|
+
site.pages.each { |key, _value| logger.info "site.pages.#{key}" }
|
160
|
+
|
161
|
+
logger.info { "site.posts has #{site.posts.docs.length} entries." }
|
162
|
+
site.posts.docs.each { |key, _value| logger.info "site.posts.docs.#{key}" }
|
163
|
+
|
164
|
+
logger.info { "site.tags has #{site.tags.length} entries." }
|
165
|
+
site.tags.sort.each { |key, value| logger.info { "site.tags.#{key} = '#{value}'" } }
|
166
|
+
end
|
167
|
+
|
168
|
+
def attributes_as_string(object, attrs)
|
169
|
+
attrs.map { |attr| " #{attr.to_s.delete_prefix("@")} = #{object.instance_variable_get(attr)}" }
|
170
|
+
end
|
171
|
+
|
172
|
+
module_function :attributes_as_string, :collection_as_string, :count_lines, :dump_document, :dump_page,
|
173
|
+
:dump_payload, :dump_site, :first_5_lines, :safe_to_s
|
174
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll_plugin_logger"
|
4
|
+
require "key_value_parser"
|
5
|
+
require "shellwords"
|
6
|
+
|
7
|
+
module JekyllPluginBlockTagTemplate
|
8
|
+
PLUGIN_NAME = "block_tag_template"
|
9
|
+
end
|
10
|
+
|
11
|
+
# This is the module-level description.
|
12
|
+
#
|
13
|
+
# @example Heading for this example
|
14
|
+
# Describe what this example does
|
15
|
+
# {% block_tag_template "parameter" %}
|
16
|
+
# Hello, world!
|
17
|
+
# {% endblock_tag_template %}
|
18
|
+
#
|
19
|
+
# The Jekyll log level defaults to :info, which means all the Jekyll.logger statements below will not generate output.
|
20
|
+
# You can control the log level when you start Jekyll.
|
21
|
+
# To set the log level to :debug, write an entery into _config.yml, like this:
|
22
|
+
# plugin_loggers:
|
23
|
+
# MyBlock: debug
|
24
|
+
|
25
|
+
module JekyllBlockTagPlugin
|
26
|
+
# This class implements the Jekyll block tag functionality
|
27
|
+
class MyBlock < Liquid::Block
|
28
|
+
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
29
|
+
# @param tag_name [String] the name of the tag, which we already know.
|
30
|
+
# @param argument_string [String] the arguments from the tag, as a single string.
|
31
|
+
# @param _parse_context [Liquid::ParseContext] hash that stores Liquid options.
|
32
|
+
# By default it has two keys: :locale and :line_numbers, the first is a Liquid::I18n object, and the second,
|
33
|
+
# a boolean parameter that determines if error messages should display the line number the error occurred.
|
34
|
+
# This argument is used mostly to display localized error messages on Liquid built-in Tags and Filters.
|
35
|
+
# See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tags
|
36
|
+
# @return [void]
|
37
|
+
def initialize(tag_name, argument_string, parse_context) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
38
|
+
super
|
39
|
+
@logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
40
|
+
|
41
|
+
@argument_string = argument_string
|
42
|
+
|
43
|
+
argv = Shellwords.split(argument_string) # Scans name/value arguments
|
44
|
+
params = KeyValueParser.new.parse(argv) # Extracts key/value pairs, default value for non-existant keys is nil
|
45
|
+
@param1 = params[:param1] # Obtain the value of parameter param1
|
46
|
+
@param2 = params[:param2]
|
47
|
+
@param3 = params[:param3]
|
48
|
+
@param4 = params[:param4]
|
49
|
+
@param5 = params[:param5]
|
50
|
+
@param_x = params[:not_present] # The value of parameters that are present is nil, but displays as the empty string
|
51
|
+
|
52
|
+
@logger.debug do
|
53
|
+
<<~HEREDOC
|
54
|
+
tag_name = '#{tag_name}'
|
55
|
+
argument_string = '#{argument_string}'
|
56
|
+
@param1 = '#{@param1}'
|
57
|
+
@param2 = '#{@param2}'
|
58
|
+
@param3 = '#{@param3}'
|
59
|
+
@param4 = '#{@param4}'
|
60
|
+
@param5 = '#{@param5}'
|
61
|
+
@param_x = '#{@param_x}'
|
62
|
+
params =
|
63
|
+
#{params.map { |k, v| "#{k} = #{v}" }.join("\n ")}
|
64
|
+
HEREDOC
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
REJECTED_ATTRIBUTES = %w[content excerpt next previous].freeze
|
69
|
+
|
70
|
+
# Method prescribed by the Jekyll plugin lifecycle.
|
71
|
+
# @param liquid_context [Liquid::Context]
|
72
|
+
# @return [String]
|
73
|
+
def render(liquid_context) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
74
|
+
content = super # This underdocumented assignment returns the text within the block.
|
75
|
+
|
76
|
+
@site = liquid_context.registers[:site]
|
77
|
+
@config = @site.config
|
78
|
+
@mode = @config.dig("env", "JEKYLL_ENV") || "development"
|
79
|
+
|
80
|
+
# variables defined in pages are stored as hash values in liquid_context
|
81
|
+
_assigned_page_variable = liquid_context['assigned_page_variable']
|
82
|
+
|
83
|
+
# The names of front matter variables are hash keys for @page
|
84
|
+
@page = liquid_context.registers[:page] # @page is a Jekyll::Drops::DocumentDrop
|
85
|
+
# layout = @page['layout']
|
86
|
+
|
87
|
+
@envs = liquid_context.environments.first
|
88
|
+
@layout_hash = @envs['layout']
|
89
|
+
|
90
|
+
@logger.debug do
|
91
|
+
<<~HEREDOC
|
92
|
+
liquid_context.scopes=#{liquid_context.scopes}
|
93
|
+
mode="#{@mode}"
|
94
|
+
page attributes:
|
95
|
+
#{@page.sort
|
96
|
+
.reject { |k, _| REJECTED_ATTRIBUTES.include? k }
|
97
|
+
.map { |k, v| "#{k}=#{v}" }
|
98
|
+
.join("\n ")}
|
99
|
+
HEREDOC
|
100
|
+
end
|
101
|
+
|
102
|
+
# Compute the return value of this Jekyll tag
|
103
|
+
<<~HEREDOC
|
104
|
+
<p style="color: green; background-color: yellow; padding: 1em; border: solid thin grey;">
|
105
|
+
#{content} #{@param1}
|
106
|
+
</p>
|
107
|
+
HEREDOC
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
PluginMetaLogger.instance.info { "Loaded #{JekyllPluginBlockTagTemplate::PLUGIN_NAME} v#{JekyllPluginTemplateVersion::VERSION} plugin." }
|
113
|
+
Liquid::Template.register_tag(JekyllPluginBlockTagTemplate::PLUGIN_NAME, JekyllBlockTagPlugin::MyBlock)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll_plugin_logger"
|
4
|
+
|
5
|
+
# @author Copyright 2020 {https://www.mslinn.com Michael Slinn}
|
6
|
+
# Template for Jekyll filters.
|
7
|
+
module JekyllFilterTemplate
|
8
|
+
class << self
|
9
|
+
attr_accessor :logger
|
10
|
+
end
|
11
|
+
self.logger = PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
12
|
+
|
13
|
+
# This Jekyll filter returns the URL to search Google for the contents of the input string.
|
14
|
+
# @param input_string [String].
|
15
|
+
# @return [String] empty string if input_string has no contents except whitespace.
|
16
|
+
# @example Use.
|
17
|
+
# {{ "joy" | my_filter_template }} => <a href='https://www.google.com/search?q=joy' target='_blank' rel='nofollow'>joy</a>
|
18
|
+
def my_filter_template(input_string)
|
19
|
+
# @context[Liquid::Context] is available here to look up variables defined in front matter, templates, page, etc.
|
20
|
+
|
21
|
+
JekyllFilterTemplate.logger.debug do
|
22
|
+
"Defined filters are: " + self.class # rubocop:disable Style/StringConcatenation
|
23
|
+
.class_variable_get('@@global_strainer')
|
24
|
+
.filter_methods.instance_variable_get('@hash')
|
25
|
+
.map { |k, _v| k }
|
26
|
+
.sort
|
27
|
+
end
|
28
|
+
|
29
|
+
input_string.strip!
|
30
|
+
JekyllFilterTemplate.logger.debug "input_string=#{input_string}"
|
31
|
+
if input_string.empty?
|
32
|
+
""
|
33
|
+
else
|
34
|
+
"<a href='https://www.google.com/search?q=#{input_string}' target='_blank' rel='nofollow'>#{input_string}</a>"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
PluginMetaLogger.instance.logger.info { "Loaded JekyllFilterTemplate v#{JekyllPluginTemplateVersion::VERSION} plugin." }
|
39
|
+
end
|
40
|
+
|
41
|
+
Liquid::Template.register_filter(JekyllFilterTemplate)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/inflector"
|
5
|
+
require 'confidential_info_redactor'
|
6
|
+
require "nokogiri"
|
7
|
+
require "talk_like_a_pirate"
|
8
|
+
|
9
|
+
# Sample Jekyll Hook plugins
|
10
|
+
module JekyllHookExamples
|
11
|
+
def modify_output
|
12
|
+
proc do |webpage|
|
13
|
+
webpage.output.gsub!('Jekyll', 'Awesome Jekyll')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def pirate_translator
|
18
|
+
proc do |webpage|
|
19
|
+
next unless webpage.data['pirate_talk']
|
20
|
+
|
21
|
+
html = Nokogiri.HTML(webpage.output)
|
22
|
+
html.css("p").each do |node|
|
23
|
+
node.content = TalkLikeAPirate.translate(node.content)
|
24
|
+
end
|
25
|
+
webpage.output = html
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def redact
|
30
|
+
proc do |webpage|
|
31
|
+
next unless webpage.data['redact']
|
32
|
+
|
33
|
+
webpage.content = redact_all webpage.content
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def wrap(text)
|
38
|
+
"<span style='background-color: black; color: white; padding: 3pt;'>redacted#{text}</span>"
|
39
|
+
end
|
40
|
+
|
41
|
+
# See https://github.com/diasks2/confidential_info_redactor
|
42
|
+
# Does not handle HTML markeup properly.
|
43
|
+
def redact_all(content)
|
44
|
+
tokens = ConfidentialInfoRedactor::Extractor.new.extract(content)
|
45
|
+
ConfidentialInfoRedactor::Redactor.new(
|
46
|
+
number_text: wrap(" number"), # This redactor is over-eager
|
47
|
+
date_text: wrap(" date"),
|
48
|
+
token_text: wrap(""),
|
49
|
+
tokens: tokens
|
50
|
+
).redact(content)
|
51
|
+
end
|
52
|
+
|
53
|
+
module_function :modify_output, :pirate_translator, :redact, :redact_all, :wrap
|
54
|
+
|
55
|
+
# Uncomment the following lines, rebuild the plugin and view http://localhost:4444/
|
56
|
+
# to see these hooks in action:
|
57
|
+
#
|
58
|
+
# Convert "Jekyll" to "Awesome Jekyll"
|
59
|
+
# Jekyll::Hooks.register(:documents, :post_render, &modify_output)
|
60
|
+
# Jekyll::Hooks.register(:pages, :post_render, &modify_output)
|
61
|
+
|
62
|
+
# Convert "English" to "Pirate Talk"
|
63
|
+
Jekyll::Hooks.register(:documents, :post_render, &pirate_translator)
|
64
|
+
Jekyll::Hooks.register(:pages, :post_render, &pirate_translator)
|
65
|
+
|
66
|
+
# Automatically redacts potentially sensitive information in selected pages
|
67
|
+
# See https://github.com/diasks2/confidential_info_redactor
|
68
|
+
Jekyll::Hooks.register(:documents, :pre_render, &redact)
|
69
|
+
Jekyll::Hooks.register(:pages, :pre_render, &redact)
|
70
|
+
|
71
|
+
PluginMetaLogger.instance.logger.info { "Loaded JekyllHookExamples v#{JekyllPluginTemplateVersion::VERSION} plugin." }
|
72
|
+
end
|
data/lib/jekyll_hooks.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll_plugin_logger"
|
4
|
+
require_relative "jekyll_plugin_template/version"
|
5
|
+
require_relative "dumpers"
|
6
|
+
|
7
|
+
module JekyllPluginHooksName
|
8
|
+
PLUGIN_NAME = "jekyll_plugin_hooks"
|
9
|
+
end
|
10
|
+
|
11
|
+
# The Jekyll processing steps are described in https://jekyllrb.com/tutorials/orderofinterpretation/
|
12
|
+
#
|
13
|
+
# The Jekyll log level defaults to :info, which means all the Jekyll.logger statements below will not generate output.
|
14
|
+
# You can control the log level when you start Jekyll.
|
15
|
+
# To set the log level to :debug, write an entery into _config.yml, like this:
|
16
|
+
# plugin_loggers:
|
17
|
+
# JekyllPluginHooks: debug
|
18
|
+
#
|
19
|
+
# Jekyll::Hooks.register accepts an optional parameter:
|
20
|
+
# :priority determines the load order for the hook plugins.
|
21
|
+
# Valid values are: :lowest, :low, :normal, :high, and :highest.
|
22
|
+
# Highest priority matches are applied first, lowest priority are applied last.
|
23
|
+
# The default value is :normal
|
24
|
+
#
|
25
|
+
# Each hook, except the clean hook, can set a boolean flag, called `site.safe`, that informs Jekyll if this plugin may be safely executed in an environment
|
26
|
+
# where arbitrary code execution is not allowed. This is used by GitHub Pages to determine which
|
27
|
+
# core plugins may be used, and which are unsafe to run. If your plugin does not allow for arbitrary
|
28
|
+
# code execution, set this to true. GitHub Pages still will not load your plugin, but if you submit it
|
29
|
+
# for inclusion in core, it is best for this to be correct!
|
30
|
+
# Default value is false.
|
31
|
+
# The hooks for pages, posts and documents access safe via pages.site.safe, posts.site.safe and documents.site.safe, respectively.
|
32
|
+
module JekyllPluginHooks
|
33
|
+
########## :site hooks
|
34
|
+
# These hooks influence the entire site
|
35
|
+
|
36
|
+
# Called just after the site resets during regeneration
|
37
|
+
# This is the first hook called, so you might think that this is the best place to define loggers.
|
38
|
+
# However, this hook will not be called unless safe mode is OFF, so define loggers in the :site :after_init hook instead
|
39
|
+
Jekyll::Hooks.register(:site, :after_reset, priority: :normal) do |site|
|
40
|
+
@log_site ||= PluginMetaLogger.instance.new_logger(:SiteHooks, PluginMetaLogger.instance.config)
|
41
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :after_reset) invoked." }
|
42
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :after_reset)", site)
|
43
|
+
end
|
44
|
+
|
45
|
+
# This hook is called just after the site initializes.
|
46
|
+
# It is a good place to modify the configuration of the site.
|
47
|
+
# This hook is triggered once per build / serve session.
|
48
|
+
Jekyll::Hooks.register(:site, :after_init, priority: :normal) do |site|
|
49
|
+
@log_clean = PluginMetaLogger.instance.new_logger(:CleanHook, PluginMetaLogger.instance.config)
|
50
|
+
@log_docs = PluginMetaLogger.instance.new_logger(:DocumentHooks, PluginMetaLogger.instance.config)
|
51
|
+
@log_pages = PluginMetaLogger.instance.new_logger(:PageHooks, PluginMetaLogger.instance.config)
|
52
|
+
@log_posts = PluginMetaLogger.instance.new_logger(:PostHooks, PluginMetaLogger.instance.config)
|
53
|
+
@log_site ||= PluginMetaLogger.instance.new_logger(:SiteHooks, PluginMetaLogger.instance.config)
|
54
|
+
|
55
|
+
@log_site.info { "Loaded #{JekyllPluginHooksName::PLUGIN_NAME} v#{JekyllPluginTemplate::VERSION} plugin." }
|
56
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :after_init) invoked." }
|
57
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :after_init)", site)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Called after all source files have been read and loaded from disk.
|
61
|
+
# This is a good hook for enriching posts;
|
62
|
+
# for example, adding links to author pages or adding posts to author pages.
|
63
|
+
Jekyll::Hooks.register(:site, :post_read, priority: :normal) do |site|
|
64
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :post_read) invoked." }
|
65
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :post_read)", site)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Called before rendering the whole site
|
69
|
+
# This is the first hook in the site generation sequence where site['env'] has a value.
|
70
|
+
# Consequently, this is the first hook that defines mode (production, development or test),
|
71
|
+
# because it is derived from site['env']['JEKYLL_ENV']
|
72
|
+
# @param payload [Hash] according to the docs, payload is a hash containing the variables available during rendering; the hash can be modified here.
|
73
|
+
# However, the debugger shows payload has type Jekyll::UnifiedPayloadDrop
|
74
|
+
Jekyll::Hooks.register(:site, :pre_render, priority: :normal) do |site, payload|
|
75
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :pre_render) invoked." }
|
76
|
+
@log_site.debug { dump(":site, :pre_render payload", payload) }
|
77
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :pre_render)", site)
|
78
|
+
Dumpers.dump_payload(@log_site, "Jekyll::Hooks.register(:site, :pre_render)", payload)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Called after rendering the whole site, but before writing any files.
|
82
|
+
# Functionally, this hook is exactly the same as a Jekyll generator.
|
83
|
+
# This hook is also similar to invoking the same method on the :post_render hooks for :documents and :pages:
|
84
|
+
# Jekyll::Hooks.register(:documents, :post_render, &my_method)
|
85
|
+
# Jekyll::Hooks.register(:pages, :post_render, &my_method)
|
86
|
+
# ... with the difference that this hook will be called only once, for the entire site, so you will have to iterate over all of the
|
87
|
+
# :documents and :pages, whereas the :pages and :documents hooks are called once for each page and document.
|
88
|
+
# @param payload [Hash] contains final values of variables after rendering the entire site (useful for sitemaps, feeds, etc).
|
89
|
+
Jekyll::Hooks.register(:site, :post_render, priority: :normal) do |site, payload|
|
90
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :post_render) invoked." }
|
91
|
+
@log_site.debug { dump(":site, :post_render payload", payload) }
|
92
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :post_render)", site)
|
93
|
+
Dumpers.dump_payload(@log_site, "Jekyll::Hooks.register(:site, :post_render)", payload)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Called after writing all of the rendered files to disk
|
97
|
+
Jekyll::Hooks.register(:site, :post_write, priority: :normal) do |site|
|
98
|
+
@log_site.info { "Jekyll::Hooks.register(:site, :post_write) invoked." }
|
99
|
+
Dumpers.dump_site(@log_site, "Jekyll::Hooks.register(:site, :post_write)", site)
|
100
|
+
end
|
101
|
+
|
102
|
+
########## :pages hooks
|
103
|
+
# Pages are web pages that do not belong to a collection, such as posts or drafts.
|
104
|
+
# These hooks provide fine-grained control over all pages in the site.
|
105
|
+
|
106
|
+
# Called whenever a page is initialized
|
107
|
+
Jekyll::Hooks.register(:pages, :post_init, priority: :normal) do |page|
|
108
|
+
@log_pages.info { "Jekyll::Hooks.register(:pages, :post_init) invoked." }
|
109
|
+
Dumpers.dump_page(@log_pages, "Jekyll::Hooks.register(:pages, :post_init)", page)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Called just before rendering a page
|
113
|
+
Jekyll::Hooks.register(:pages, :pre_render, priority: :normal) do |page, payload|
|
114
|
+
@log_pages.info { "Jekyll::Hooks.register(:pages, :pre_render) invoked." }
|
115
|
+
Dumpers.dump_page(@log_pages, "Jekyll::Hooks.register(:pages, :pre_render)", page)
|
116
|
+
Dumpers.dump_payload(@log_pages, ":pages, :pre_render payload", payload)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Called after converting the page content, but before rendering the page layout
|
120
|
+
Jekyll::Hooks.register(:pages, :post_convert, priority: :normal) do |page|
|
121
|
+
@log_pages.info { "Jekyll::Hooks.register(:pages, :post_convert) invoked." }
|
122
|
+
Dumpers.dump_page(@log_pages, "Jekyll::Hooks.register(:pages, :post_convert)", page)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Called after rendering a page, but before writing it to disk
|
126
|
+
Jekyll::Hooks.register(:pages, :post_render, priority: :normal) do |page|
|
127
|
+
page.site.safe = true
|
128
|
+
@log_pages.info { "Jekyll::Hooks.register(:pages, :post_render) invoked." }
|
129
|
+
Dumpers.dump_page(@log_pages, "Jekyll::Hooks.register(:pages, :post_render)", page)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Called after writing a page to disk
|
133
|
+
Jekyll::Hooks.register(:pages, :post_write, priority: :normal) do |page|
|
134
|
+
@log_pages.info { "Jekyll::Hooks.register(:pages, :post_write) invoked." }
|
135
|
+
Dumpers.dump_page(@log_pages, "Jekyll::Hooks.register(:pages, :post_write)", page)
|
136
|
+
end
|
137
|
+
|
138
|
+
########## :documents hooks
|
139
|
+
# Documents are web pages that belong to a collection, for example posts, drafts and custom collections.
|
140
|
+
# These hooks provide fine-grained control over all documents in the site.
|
141
|
+
# If you want to inspect or process all collections in the same way, use these hooks.
|
142
|
+
# If you just want to process a custom collection, use these hooks and filter out the documents
|
143
|
+
# that do not belong to that collection.
|
144
|
+
|
145
|
+
# Called whenever any document is initialized.
|
146
|
+
# Front matter data will not have been assigned yet to documents when this hook is invoked, for example:
|
147
|
+
# categories, description, last_modified_at, tags, title, and slug;
|
148
|
+
# other document attributes that are not yet ready when this hook is invoked include
|
149
|
+
# excerpt and ext (file extension).
|
150
|
+
# The collection attribute will be set properly for this hook.
|
151
|
+
Jekyll::Hooks.register(:documents, :post_init, priority: :normal) do |document|
|
152
|
+
@log_docs.info { "Jekyll::Hooks.register(:documents, :post_init) invoked." }
|
153
|
+
Dumpers.dump_document(@log_docs, "Jekyll::Hooks.register(:documents, :post_init)", document)
|
154
|
+
"stop"
|
155
|
+
end
|
156
|
+
|
157
|
+
# Called just before rendering a document.
|
158
|
+
# Front matter data will have been assigned when this hook is invoked.
|
159
|
+
# Liquid variables are still embedded in the content.
|
160
|
+
# If the document contains markdown (or some other markup),
|
161
|
+
# it will not have been converted to HTML (or whatever the target format is) yet.
|
162
|
+
Jekyll::Hooks.register(:documents, :pre_render, priority: :normal) do |document, payload|
|
163
|
+
@log_docs.info { "Jekyll::Hooks.register(:documents, :pre_render) invoked." }
|
164
|
+
Dumpers.dump_document(@log_docs, "Jekyll::Hooks.register(:documents, :pre_render)", document)
|
165
|
+
Dumpers.dump_payload(@log_docs, ":documents, :pre_render payload", payload)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Called after converting the document content to HTML (or whatever),
|
169
|
+
# but before rendering the document using the layout.
|
170
|
+
Jekyll::Hooks.register(:documents, :post_convert, priority: :normal) do |document|
|
171
|
+
@log_docs.info { "Jekyll::Hooks.register(:documents, :post_convert) invoked." }
|
172
|
+
Dumpers.dump_document(@log_docs, "Jekyll::Hooks.register(:documents, :post_convert)", document)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Called after rendering a document using the layout, but before writing it to disk.
|
176
|
+
# This is your last chance to modify the content.
|
177
|
+
Jekyll::Hooks.register(:documents, :post_render, priority: :normal) do |document|
|
178
|
+
@log_docs.info { "Jekyll::Hooks.register(:documents, :post_render) invoked." }
|
179
|
+
Dumpers.dump_document(@log_docs, "Jekyll::Hooks.register(:documents, :post_render)", document)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Called after writing a document to disk.
|
183
|
+
# Useful for statistics regarding completed renderings.
|
184
|
+
Jekyll::Hooks.register(:documents, :post_write, priority: :normal) do |document|
|
185
|
+
@log_docs.info { "Jekyll::Hooks.register(:documents, :post_write) invoked." }
|
186
|
+
Dumpers.dump_document(@log_docs, "Jekyll::Hooks.register(:documents, :post_write)", document)
|
187
|
+
end
|
188
|
+
|
189
|
+
########## :posts hooks
|
190
|
+
# These hooks provide fine-grained control over all posts **and drafts** in the site without affecting
|
191
|
+
# documents in user-defined collections
|
192
|
+
|
193
|
+
# Called whenever any post is initialized
|
194
|
+
Jekyll::Hooks.register(:posts, :post_init, priority: :normal) do |post|
|
195
|
+
@log_posts.info { "Jekyll::Hooks.register(:posts, :post_init) invoked." }
|
196
|
+
Dumpers.dump_document(@log_posts, "Jekyll::Hooks.register(:posts, :post_init)", post)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Called just before rendering a post
|
200
|
+
Jekyll::Hooks.register(:posts, :pre_render, priority: :normal) do |post, payload|
|
201
|
+
# post is a Jekyll::Document
|
202
|
+
@log_posts.info { "Jekyll::Hooks.register(:posts, :pre_render) invoked." }
|
203
|
+
Dumpers.dump_document(@log_posts, "Jekyll::Hooks.register(:posts, :pre_render)", post)
|
204
|
+
Dumpers.dump_payload(@log_posts, ":posts, :pre_render payload", payload)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Called after converting the post content, but before rendering the post layout.
|
208
|
+
# This hook can be used to make edits to rendered pages,
|
209
|
+
# regardless of whether they were originally written in markdown or HTML.
|
210
|
+
#
|
211
|
+
# Changes must modify post.output, as shown in this example:
|
212
|
+
# Jekyll::Hooks.register(:posts, :post_convert) do |post|
|
213
|
+
# post.output.gsub!('programming PHP', 'banging rocks together')
|
214
|
+
# end
|
215
|
+
Jekyll::Hooks.register(:posts, :post_convert, priority: :normal) do |post|
|
216
|
+
@log_posts.info { "Jekyll::Hooks.register(:posts, :post_convert) invoked." }
|
217
|
+
Dumpers.dump_document(@log_posts, "Jekyll::Hooks.register(:posts, :post_convert)", post)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Called after rendering a post, but before writing it to disk.
|
221
|
+
# Changing `post.conent` has no effect on visible output.
|
222
|
+
Jekyll::Hooks.register(:posts, :post_render, priority: :normal) do |post|
|
223
|
+
@log_posts.info { "Jekyll::Hooks.register(:posts, :post_render) invoked." }
|
224
|
+
Dumpers.dump_document(@log_posts, "Jekyll::Hooks.register(:posts, :post_render)", post)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Called after writing a post to disk
|
228
|
+
Jekyll::Hooks.register(:posts, :post_write, priority: :normal) do |post|
|
229
|
+
@log_posts.info { "Jekyll::Hooks.register(:posts, :post_write) invoked." }
|
230
|
+
Dumpers.dump_document(@log_posts, "Jekyll::Hooks.register(:posts, :post_write)", post)
|
231
|
+
end
|
232
|
+
|
233
|
+
########## :clean hooks
|
234
|
+
# These hooks provide fine-grained control on the list of obsolete files determined
|
235
|
+
# to be deleted during the site's cleanup phase.
|
236
|
+
|
237
|
+
# Called during the cleanup of a site's destination, before the site is built
|
238
|
+
Jekyll::Hooks.register(:clean, :on_obsolete, priority: :normal) do |files|
|
239
|
+
# files has type Array[String]
|
240
|
+
@log_clean.info { "Jekyll::Hooks.register(:clean, :on_obsolete) invoked for #{files}." }
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll"
|
4
|
+
require "jekyll_plugin_logger"
|
5
|
+
require_relative "jekyll_plugin_template/version"
|
6
|
+
require_relative "jekyll_block_tag_plugin"
|
7
|
+
require_relative "jekyll_filter_template"
|
8
|
+
require_relative "jekyll_tag_plugin"
|
9
|
+
require_relative "jekyll_hooks"
|
10
|
+
require_relative "jekyll_hook_examples"
|
11
|
+
require_relative "category_index_generator"
|
12
|
+
require_relative "category_combiner"
|
13
|
+
|
14
|
+
module JekyllPluginTemplate
|
15
|
+
include JekyllBlockTagPlugin
|
16
|
+
include JekyllFilterTemplate
|
17
|
+
include JekyllTagPlugin
|
18
|
+
include JekyllPluginHooks
|
19
|
+
include JekyllHookExamples
|
20
|
+
include CategoryIndexGenerator
|
21
|
+
include CategoryCombiner
|
22
|
+
end
|