ngage 0.0.0
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/LICENSE +22 -0
- data/exe/ngage +55 -0
- data/lib/ngage.rb +3 -0
- data/lib/ngage/jekyll.rb +204 -0
- data/lib/ngage/jekyll/cleaner.rb +111 -0
- data/lib/ngage/jekyll/collection.rb +235 -0
- data/lib/ngage/jekyll/command.rb +103 -0
- data/lib/ngage/jekyll/commands/build.rb +93 -0
- data/lib/ngage/jekyll/commands/clean.rb +45 -0
- data/lib/ngage/jekyll/commands/doctor.rb +173 -0
- data/lib/ngage/jekyll/commands/help.rb +34 -0
- data/lib/ngage/jekyll/commands/new.rb +157 -0
- data/lib/ngage/jekyll/commands/new_theme.rb +42 -0
- data/lib/ngage/jekyll/commands/serve.rb +354 -0
- data/lib/ngage/jekyll/commands/serve/live_reload_reactor.rb +122 -0
- data/lib/ngage/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
- data/lib/ngage/jekyll/commands/serve/servlet.rb +203 -0
- data/lib/ngage/jekyll/commands/serve/websockets.rb +81 -0
- data/lib/ngage/jekyll/configuration.rb +391 -0
- data/lib/ngage/jekyll/converter.rb +54 -0
- data/lib/ngage/jekyll/converters/identity.rb +41 -0
- data/lib/ngage/jekyll/converters/markdown.rb +116 -0
- data/lib/ngage/jekyll/converters/markdown/kramdown_parser.rb +122 -0
- data/lib/ngage/jekyll/converters/smartypants.rb +70 -0
- data/lib/ngage/jekyll/convertible.rb +253 -0
- data/lib/ngage/jekyll/deprecator.rb +50 -0
- data/lib/ngage/jekyll/document.rb +503 -0
- data/lib/ngage/jekyll/drops/collection_drop.rb +20 -0
- data/lib/ngage/jekyll/drops/document_drop.rb +69 -0
- data/lib/ngage/jekyll/drops/drop.rb +209 -0
- data/lib/ngage/jekyll/drops/excerpt_drop.rb +15 -0
- data/lib/ngage/jekyll/drops/jekyll_drop.rb +32 -0
- data/lib/ngage/jekyll/drops/site_drop.rb +56 -0
- data/lib/ngage/jekyll/drops/static_file_drop.rb +14 -0
- data/lib/ngage/jekyll/drops/unified_payload_drop.rb +26 -0
- data/lib/ngage/jekyll/drops/url_drop.rb +89 -0
- data/lib/ngage/jekyll/entry_filter.rb +127 -0
- data/lib/ngage/jekyll/errors.rb +20 -0
- data/lib/ngage/jekyll/excerpt.rb +180 -0
- data/lib/ngage/jekyll/external.rb +76 -0
- data/lib/ngage/jekyll/filters.rb +390 -0
- data/lib/ngage/jekyll/filters/date_filters.rb +110 -0
- data/lib/ngage/jekyll/filters/grouping_filters.rb +64 -0
- data/lib/ngage/jekyll/filters/url_filters.rb +68 -0
- data/lib/ngage/jekyll/frontmatter_defaults.rb +233 -0
- data/lib/ngage/jekyll/generator.rb +5 -0
- data/lib/ngage/jekyll/hooks.rb +106 -0
- data/lib/ngage/jekyll/layout.rb +62 -0
- data/lib/ngage/jekyll/liquid_extensions.rb +22 -0
- data/lib/ngage/jekyll/liquid_renderer.rb +63 -0
- data/lib/ngage/jekyll/liquid_renderer/file.rb +56 -0
- data/lib/ngage/jekyll/liquid_renderer/table.rb +98 -0
- data/lib/ngage/jekyll/log_adapter.rb +151 -0
- data/lib/ngage/jekyll/mime.types +825 -0
- data/lib/ngage/jekyll/page.rb +185 -0
- data/lib/ngage/jekyll/page_without_a_file.rb +14 -0
- data/lib/ngage/jekyll/plugin.rb +92 -0
- data/lib/ngage/jekyll/plugin_manager.rb +115 -0
- data/lib/ngage/jekyll/publisher.rb +23 -0
- data/lib/ngage/jekyll/reader.rb +154 -0
- data/lib/ngage/jekyll/readers/collection_reader.rb +22 -0
- data/lib/ngage/jekyll/readers/data_reader.rb +75 -0
- data/lib/ngage/jekyll/readers/layout_reader.rb +70 -0
- data/lib/ngage/jekyll/readers/page_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/post_reader.rb +72 -0
- data/lib/ngage/jekyll/readers/static_file_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/theme_assets_reader.rb +51 -0
- data/lib/ngage/jekyll/regenerator.rb +195 -0
- data/lib/ngage/jekyll/related_posts.rb +52 -0
- data/lib/ngage/jekyll/renderer.rb +266 -0
- data/lib/ngage/jekyll/site.rb +476 -0
- data/lib/ngage/jekyll/static_file.rb +169 -0
- data/lib/ngage/jekyll/stevenson.rb +60 -0
- data/lib/ngage/jekyll/tags/highlight.rb +108 -0
- data/lib/ngage/jekyll/tags/include.rb +226 -0
- data/lib/ngage/jekyll/tags/link.rb +40 -0
- data/lib/ngage/jekyll/tags/post_url.rb +104 -0
- data/lib/ngage/jekyll/theme.rb +73 -0
- data/lib/ngage/jekyll/theme_builder.rb +121 -0
- data/lib/ngage/jekyll/url.rb +160 -0
- data/lib/ngage/jekyll/utils.rb +370 -0
- data/lib/ngage/jekyll/utils/ansi.rb +57 -0
- data/lib/ngage/jekyll/utils/exec.rb +26 -0
- data/lib/ngage/jekyll/utils/internet.rb +37 -0
- data/lib/ngage/jekyll/utils/platforms.rb +82 -0
- data/lib/ngage/jekyll/utils/thread_event.rb +31 -0
- data/lib/ngage/jekyll/utils/win_tz.rb +75 -0
- data/lib/ngage/site_template/.gitignore +5 -0
- data/lib/ngage/site_template/404.html +25 -0
- data/lib/ngage/site_template/_config.yml +47 -0
- data/lib/ngage/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -0
- data/lib/ngage/site_template/about.markdown +18 -0
- data/lib/ngage/site_template/index.markdown +6 -0
- data/lib/ngage/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
- data/lib/ngage/theme_template/Gemfile +4 -0
- data/lib/ngage/theme_template/LICENSE.txt.erb +21 -0
- data/lib/ngage/theme_template/README.md.erb +52 -0
- data/lib/ngage/theme_template/_layouts/default.html +1 -0
- data/lib/ngage/theme_template/_layouts/page.html +5 -0
- data/lib/ngage/theme_template/_layouts/post.html +5 -0
- data/lib/ngage/theme_template/example/_config.yml.erb +1 -0
- data/lib/ngage/theme_template/example/_post.md +12 -0
- data/lib/ngage/theme_template/example/index.html +14 -0
- data/lib/ngage/theme_template/example/style.scss +7 -0
- data/lib/ngage/theme_template/gitignore.erb +6 -0
- data/lib/ngage/theme_template/theme.gemspec.erb +19 -0
- data/lib/ngage/version.rb +5 -0
- metadata +328 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
class RelatedPosts
|
|
5
|
+
class << self
|
|
6
|
+
attr_accessor :lsi
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :post, :site
|
|
10
|
+
|
|
11
|
+
def initialize(post)
|
|
12
|
+
@post = post
|
|
13
|
+
@site = post.site
|
|
14
|
+
Jekyll::External.require_with_graceful_fail("classifier-reborn") if site.lsi
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def build
|
|
18
|
+
return [] unless site.posts.docs.size > 1
|
|
19
|
+
|
|
20
|
+
if site.lsi
|
|
21
|
+
build_index
|
|
22
|
+
lsi_related_posts
|
|
23
|
+
else
|
|
24
|
+
most_recent_posts
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def build_index
|
|
29
|
+
self.class.lsi ||= begin
|
|
30
|
+
lsi = ClassifierReborn::LSI.new(:auto_rebuild => false)
|
|
31
|
+
Jekyll.logger.info("Populating LSI...")
|
|
32
|
+
|
|
33
|
+
site.posts.docs.each do |x|
|
|
34
|
+
lsi.add_item(x)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Jekyll.logger.info("Rebuilding index...")
|
|
38
|
+
lsi.build_index
|
|
39
|
+
Jekyll.logger.info("")
|
|
40
|
+
lsi
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def lsi_related_posts
|
|
45
|
+
self.class.lsi.find_related(post, 11)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def most_recent_posts
|
|
49
|
+
@most_recent_posts ||= (site.posts.docs.last(11).reverse - [post]).first(10)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
class Renderer
|
|
5
|
+
attr_reader :document, :site
|
|
6
|
+
attr_writer :layouts, :payload
|
|
7
|
+
|
|
8
|
+
def initialize(site, document, site_payload = nil)
|
|
9
|
+
@site = site
|
|
10
|
+
@document = document
|
|
11
|
+
@payload = site_payload
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Fetches the payload used in Liquid rendering.
|
|
15
|
+
# It can be written with #payload=(new_payload)
|
|
16
|
+
# Falls back to site.site_payload if no payload is set.
|
|
17
|
+
#
|
|
18
|
+
# Returns a Jekyll::Drops::UnifiedPayloadDrop
|
|
19
|
+
def payload
|
|
20
|
+
@payload ||= site.site_payload
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The list of layouts registered for this Renderer.
|
|
24
|
+
# It can be written with #layouts=(new_layouts)
|
|
25
|
+
# Falls back to site.layouts if no layouts are registered.
|
|
26
|
+
#
|
|
27
|
+
# Returns a Hash of String => Jekyll::Layout identified
|
|
28
|
+
# as basename without the extension name.
|
|
29
|
+
def layouts
|
|
30
|
+
@layouts || site.layouts
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Determine which converters to use based on this document's
|
|
34
|
+
# extension.
|
|
35
|
+
#
|
|
36
|
+
# Returns Array of Converter instances.
|
|
37
|
+
def converters
|
|
38
|
+
@converters ||= site.converters.select { |c| c.matches(document.extname) }.sort
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Determine the extname the outputted file should have
|
|
42
|
+
#
|
|
43
|
+
# Returns String the output extname including the leading period.
|
|
44
|
+
def output_ext
|
|
45
|
+
@output_ext ||= (permalink_ext || converter_output_ext)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Prepare payload and render the document
|
|
49
|
+
#
|
|
50
|
+
# Returns String rendered document output
|
|
51
|
+
def run
|
|
52
|
+
Jekyll.logger.debug "Rendering:", document.relative_path
|
|
53
|
+
|
|
54
|
+
assign_pages!
|
|
55
|
+
assign_current_document!
|
|
56
|
+
assign_highlighter_options!
|
|
57
|
+
assign_layout_data!
|
|
58
|
+
|
|
59
|
+
Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path
|
|
60
|
+
document.trigger_hooks(:pre_render, payload)
|
|
61
|
+
|
|
62
|
+
render_document
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Render the document.
|
|
66
|
+
#
|
|
67
|
+
# Returns String rendered document output
|
|
68
|
+
# rubocop: disable AbcSize
|
|
69
|
+
def render_document
|
|
70
|
+
info = {
|
|
71
|
+
:registers => { :site => site, :page => payload["page"] },
|
|
72
|
+
:strict_filters => liquid_options["strict_filters"],
|
|
73
|
+
:strict_variables => liquid_options["strict_variables"],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
output = document.content
|
|
77
|
+
if document.render_with_liquid?
|
|
78
|
+
Jekyll.logger.debug "Rendering Liquid:", document.relative_path
|
|
79
|
+
output = render_liquid(output, payload, info, document.path)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Jekyll.logger.debug "Rendering Markup:", document.relative_path
|
|
83
|
+
output = convert(output.to_s)
|
|
84
|
+
document.content = output
|
|
85
|
+
|
|
86
|
+
if document.place_in_layout?
|
|
87
|
+
Jekyll.logger.debug "Rendering Layout:", document.relative_path
|
|
88
|
+
output = place_in_layouts(output, payload, info)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
output
|
|
92
|
+
end
|
|
93
|
+
# rubocop: enable AbcSize
|
|
94
|
+
|
|
95
|
+
# Convert the document using the converters which match this renderer's document.
|
|
96
|
+
#
|
|
97
|
+
# Returns String the converted content.
|
|
98
|
+
def convert(content)
|
|
99
|
+
converters.reduce(content) do |output, converter|
|
|
100
|
+
begin
|
|
101
|
+
converter.convert output
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
Jekyll.logger.error "Conversion error:",
|
|
104
|
+
"#{converter.class} encountered an error while "\
|
|
105
|
+
"converting '#{document.relative_path}':"
|
|
106
|
+
Jekyll.logger.error("", e.to_s)
|
|
107
|
+
raise e
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Render the given content with the payload and info
|
|
113
|
+
#
|
|
114
|
+
# content -
|
|
115
|
+
# payload -
|
|
116
|
+
# info -
|
|
117
|
+
# path - (optional) the path to the file, for use in ex
|
|
118
|
+
#
|
|
119
|
+
# Returns String the content, rendered by Liquid.
|
|
120
|
+
def render_liquid(content, payload, info, path = nil)
|
|
121
|
+
template = site.liquid_renderer.file(path).parse(content)
|
|
122
|
+
template.warnings.each do |e|
|
|
123
|
+
Jekyll.logger.warn "Liquid Warning:",
|
|
124
|
+
LiquidRenderer.format_error(e, path || document.relative_path)
|
|
125
|
+
end
|
|
126
|
+
template.render!(payload, info)
|
|
127
|
+
# rubocop: disable RescueException
|
|
128
|
+
rescue Exception => e
|
|
129
|
+
Jekyll.logger.error "Liquid Exception:",
|
|
130
|
+
LiquidRenderer.format_error(e, path || document.relative_path)
|
|
131
|
+
raise e
|
|
132
|
+
end
|
|
133
|
+
# rubocop: enable RescueException
|
|
134
|
+
|
|
135
|
+
# Checks if the layout specified in the document actually exists
|
|
136
|
+
#
|
|
137
|
+
# layout - the layout to check
|
|
138
|
+
#
|
|
139
|
+
# Returns Boolean true if the layout is invalid, false if otherwise
|
|
140
|
+
def invalid_layout?(layout)
|
|
141
|
+
!document.data["layout"].nil? && layout.nil? && !(document.is_a? Jekyll::Excerpt)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Render layouts and place document content inside.
|
|
145
|
+
#
|
|
146
|
+
# Returns String rendered content
|
|
147
|
+
def place_in_layouts(content, payload, info)
|
|
148
|
+
output = content.dup
|
|
149
|
+
layout = layouts[document.data["layout"].to_s]
|
|
150
|
+
validate_layout(layout)
|
|
151
|
+
|
|
152
|
+
used = Set.new([layout])
|
|
153
|
+
|
|
154
|
+
# Reset the payload layout data to ensure it starts fresh for each page.
|
|
155
|
+
payload["layout"] = nil
|
|
156
|
+
|
|
157
|
+
while layout
|
|
158
|
+
output = render_layout(output, layout, info)
|
|
159
|
+
add_regenerator_dependencies(layout)
|
|
160
|
+
|
|
161
|
+
next unless (layout = site.layouts[layout.data["layout"]])
|
|
162
|
+
break if used.include?(layout)
|
|
163
|
+
|
|
164
|
+
used << layout
|
|
165
|
+
end
|
|
166
|
+
output
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
# Checks if the layout specified in the document actually exists
|
|
172
|
+
#
|
|
173
|
+
# layout - the layout to check
|
|
174
|
+
# Returns nothing
|
|
175
|
+
def validate_layout(layout)
|
|
176
|
+
if invalid_layout?(layout)
|
|
177
|
+
Jekyll.logger.warn(
|
|
178
|
+
"Build Warning:",
|
|
179
|
+
"Layout '#{document.data["layout"]}' requested "\
|
|
180
|
+
"in #{document.relative_path} does not exist."
|
|
181
|
+
)
|
|
182
|
+
elsif !layout.nil?
|
|
183
|
+
layout_source = layout.path.start_with?(site.source) ? :site : :theme
|
|
184
|
+
Jekyll.logger.debug "Layout source:", layout_source
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Render layout content into document.output
|
|
189
|
+
#
|
|
190
|
+
# Returns String rendered content
|
|
191
|
+
def render_layout(output, layout, info)
|
|
192
|
+
payload["content"] = output
|
|
193
|
+
payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
|
|
194
|
+
|
|
195
|
+
render_liquid(
|
|
196
|
+
layout.content,
|
|
197
|
+
payload,
|
|
198
|
+
info,
|
|
199
|
+
layout.relative_path
|
|
200
|
+
)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def add_regenerator_dependencies(layout)
|
|
204
|
+
return unless document.write?
|
|
205
|
+
|
|
206
|
+
site.regenerator.add_dependency(
|
|
207
|
+
site.in_source_dir(document.path),
|
|
208
|
+
layout.path
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Set page content to payload and assign pager if document has one.
|
|
213
|
+
#
|
|
214
|
+
# Returns nothing
|
|
215
|
+
def assign_pages!
|
|
216
|
+
payload["page"] = document.to_liquid
|
|
217
|
+
payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager))
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Set related posts to payload if document is a post.
|
|
221
|
+
#
|
|
222
|
+
# Returns nothing
|
|
223
|
+
def assign_current_document!
|
|
224
|
+
payload["site"].current_document = document
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Set highlighter prefix and suffix
|
|
228
|
+
#
|
|
229
|
+
# Returns nothing
|
|
230
|
+
def assign_highlighter_options!
|
|
231
|
+
payload["highlighter_prefix"] = converters.first.highlighter_prefix
|
|
232
|
+
payload["highlighter_suffix"] = converters.first.highlighter_suffix
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def assign_layout_data!
|
|
236
|
+
layout = layouts[document.data["layout"]]
|
|
237
|
+
payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def permalink_ext
|
|
241
|
+
document_permalink = document.permalink
|
|
242
|
+
if document_permalink && !document_permalink.end_with?("/")
|
|
243
|
+
permalink_ext = File.extname(document_permalink)
|
|
244
|
+
permalink_ext unless permalink_ext.empty?
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def converter_output_ext
|
|
249
|
+
if output_exts.size == 1
|
|
250
|
+
output_exts.last
|
|
251
|
+
else
|
|
252
|
+
output_exts[-2]
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def output_exts
|
|
257
|
+
@output_exts ||= converters.map do |c|
|
|
258
|
+
c.output_ext(document.extname)
|
|
259
|
+
end.compact
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def liquid_options
|
|
263
|
+
@liquid_options ||= site.config["liquid"]
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
class Site
|
|
5
|
+
attr_reader :source, :dest, :config
|
|
6
|
+
attr_accessor :layouts, :pages, :static_files, :drafts,
|
|
7
|
+
:exclude, :include, :lsi, :highlighter, :permalink_style,
|
|
8
|
+
:time, :future, :unpublished, :safe, :plugins, :limit_posts,
|
|
9
|
+
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
|
|
10
|
+
:gems, :plugin_manager, :theme
|
|
11
|
+
|
|
12
|
+
attr_accessor :converters, :generators, :reader
|
|
13
|
+
attr_reader :regenerator, :liquid_renderer, :includes_load_paths
|
|
14
|
+
|
|
15
|
+
# Public: Initialize a new Site.
|
|
16
|
+
#
|
|
17
|
+
# config - A Hash containing site configuration details.
|
|
18
|
+
def initialize(config)
|
|
19
|
+
# Source and destination may not be changed after the site has been created.
|
|
20
|
+
@source = File.expand_path(config["source"]).freeze
|
|
21
|
+
@dest = File.expand_path(config["destination"]).freeze
|
|
22
|
+
|
|
23
|
+
self.config = config
|
|
24
|
+
|
|
25
|
+
@reader = Reader.new(self)
|
|
26
|
+
@regenerator = Regenerator.new(self)
|
|
27
|
+
@liquid_renderer = LiquidRenderer.new(self)
|
|
28
|
+
|
|
29
|
+
Jekyll.sites << self
|
|
30
|
+
|
|
31
|
+
reset
|
|
32
|
+
setup
|
|
33
|
+
|
|
34
|
+
Jekyll::Hooks.trigger :site, :after_init, self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Public: Set the site's configuration. This handles side-effects caused by
|
|
38
|
+
# changing values in the configuration.
|
|
39
|
+
#
|
|
40
|
+
# config - a Jekyll::Configuration, containing the new configuration.
|
|
41
|
+
#
|
|
42
|
+
# Returns the new configuration.
|
|
43
|
+
def config=(config)
|
|
44
|
+
@config = config.clone
|
|
45
|
+
|
|
46
|
+
%w(safe lsi highlighter baseurl exclude include future unpublished
|
|
47
|
+
show_drafts limit_posts keep_files).each do |opt|
|
|
48
|
+
send("#{opt}=", config[opt])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# keep using `gems` to avoid breaking change
|
|
52
|
+
self.gems = config["plugins"]
|
|
53
|
+
|
|
54
|
+
configure_plugins
|
|
55
|
+
configure_theme
|
|
56
|
+
configure_include_paths
|
|
57
|
+
configure_file_read_opts
|
|
58
|
+
|
|
59
|
+
self.permalink_style = config["permalink"].to_sym
|
|
60
|
+
|
|
61
|
+
@config
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Public: Read, process, and write this Site to output.
|
|
65
|
+
#
|
|
66
|
+
# Returns nothing.
|
|
67
|
+
def process
|
|
68
|
+
reset
|
|
69
|
+
read
|
|
70
|
+
generate
|
|
71
|
+
render
|
|
72
|
+
cleanup
|
|
73
|
+
write
|
|
74
|
+
print_stats if config["profile"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def print_stats
|
|
78
|
+
Jekyll.logger.info @liquid_renderer.stats_table
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Reset Site details.
|
|
82
|
+
#
|
|
83
|
+
# Returns nothing
|
|
84
|
+
def reset
|
|
85
|
+
self.time = if config["time"]
|
|
86
|
+
Utils.parse_date(config["time"].to_s, "Invalid time in _config.yml.")
|
|
87
|
+
else
|
|
88
|
+
Time.now
|
|
89
|
+
end
|
|
90
|
+
self.layouts = {}
|
|
91
|
+
self.pages = []
|
|
92
|
+
self.static_files = []
|
|
93
|
+
self.data = {}
|
|
94
|
+
@site_data = nil
|
|
95
|
+
@collections = nil
|
|
96
|
+
@docs_to_write = nil
|
|
97
|
+
@regenerator.clear_cache
|
|
98
|
+
@liquid_renderer.reset
|
|
99
|
+
@site_cleaner = nil
|
|
100
|
+
|
|
101
|
+
raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
|
|
102
|
+
|
|
103
|
+
Jekyll::Hooks.trigger :site, :after_reset, self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Load necessary libraries, plugins, converters, and generators.
|
|
107
|
+
#
|
|
108
|
+
# Returns nothing.
|
|
109
|
+
def setup
|
|
110
|
+
ensure_not_in_dest
|
|
111
|
+
|
|
112
|
+
plugin_manager.conscientious_require
|
|
113
|
+
|
|
114
|
+
self.converters = instantiate_subclasses(Jekyll::Converter)
|
|
115
|
+
self.generators = instantiate_subclasses(Jekyll::Generator)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Check that the destination dir isn't the source dir or a directory
|
|
119
|
+
# parent to the source dir.
|
|
120
|
+
def ensure_not_in_dest
|
|
121
|
+
dest_pathname = Pathname.new(dest)
|
|
122
|
+
Pathname.new(source).ascend do |path|
|
|
123
|
+
if path == dest_pathname
|
|
124
|
+
raise Errors::FatalException,
|
|
125
|
+
"Destination directory cannot be or contain the Source directory."
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The list of collections and their corresponding Jekyll::Collection instances.
|
|
131
|
+
# If config['collections'] is set, a new instance is created
|
|
132
|
+
# for each item in the collection, a new hash is returned otherwise.
|
|
133
|
+
#
|
|
134
|
+
# Returns a Hash containing collection name-to-instance pairs.
|
|
135
|
+
def collections
|
|
136
|
+
@collections ||= Hash[collection_names.map do |coll|
|
|
137
|
+
[coll, Jekyll::Collection.new(self, coll)]
|
|
138
|
+
end]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The list of collection names.
|
|
142
|
+
#
|
|
143
|
+
# Returns an array of collection names from the configuration,
|
|
144
|
+
# or an empty array if the `collections` key is not set.
|
|
145
|
+
def collection_names
|
|
146
|
+
case config["collections"]
|
|
147
|
+
when Hash
|
|
148
|
+
config["collections"].keys
|
|
149
|
+
when Array
|
|
150
|
+
config["collections"]
|
|
151
|
+
when nil
|
|
152
|
+
[]
|
|
153
|
+
else
|
|
154
|
+
raise ArgumentError, "Your `collections` key must be a hash or an array."
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Read Site data from disk and load it into internal data structures.
|
|
159
|
+
#
|
|
160
|
+
# Returns nothing.
|
|
161
|
+
def read
|
|
162
|
+
reader.read
|
|
163
|
+
limit_posts!
|
|
164
|
+
Jekyll::Hooks.trigger :site, :post_read, self
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Run each of the Generators.
|
|
168
|
+
#
|
|
169
|
+
# Returns nothing.
|
|
170
|
+
def generate
|
|
171
|
+
generators.each do |generator|
|
|
172
|
+
start = Time.now
|
|
173
|
+
generator.generate(self)
|
|
174
|
+
Jekyll.logger.debug "Generating:",
|
|
175
|
+
"#{generator.class} finished in #{Time.now - start} seconds."
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Render the site to the destination.
|
|
180
|
+
#
|
|
181
|
+
# Returns nothing.
|
|
182
|
+
def render
|
|
183
|
+
relative_permalinks_are_deprecated
|
|
184
|
+
|
|
185
|
+
payload = site_payload
|
|
186
|
+
|
|
187
|
+
Jekyll::Hooks.trigger :site, :pre_render, self, payload
|
|
188
|
+
|
|
189
|
+
render_docs(payload)
|
|
190
|
+
render_pages(payload)
|
|
191
|
+
|
|
192
|
+
Jekyll::Hooks.trigger :site, :post_render, self, payload
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Remove orphaned files and empty directories in destination.
|
|
196
|
+
#
|
|
197
|
+
# Returns nothing.
|
|
198
|
+
def cleanup
|
|
199
|
+
site_cleaner.cleanup!
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Write static files, pages, and posts.
|
|
203
|
+
#
|
|
204
|
+
# Returns nothing.
|
|
205
|
+
def write
|
|
206
|
+
each_site_file do |item|
|
|
207
|
+
item.write(dest) if regenerator.regenerate?(item)
|
|
208
|
+
end
|
|
209
|
+
regenerator.write_metadata
|
|
210
|
+
Jekyll::Hooks.trigger :site, :post_write, self
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def posts
|
|
214
|
+
collections["posts"] ||= Collection.new(self, "posts")
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Construct a Hash of Posts indexed by the specified Post attribute.
|
|
218
|
+
#
|
|
219
|
+
# post_attr - The String name of the Post attribute.
|
|
220
|
+
#
|
|
221
|
+
# Examples
|
|
222
|
+
#
|
|
223
|
+
# post_attr_hash('categories')
|
|
224
|
+
# # => { 'tech' => [<Post A>, <Post B>],
|
|
225
|
+
# # 'ruby' => [<Post B>] }
|
|
226
|
+
#
|
|
227
|
+
# Returns the Hash: { attr => posts } where
|
|
228
|
+
# attr - One of the values for the requested attribute.
|
|
229
|
+
# posts - The Array of Posts with the given attr value.
|
|
230
|
+
def post_attr_hash(post_attr)
|
|
231
|
+
# Build a hash map based on the specified post attribute ( post attr =>
|
|
232
|
+
# array of posts ) then sort each array in reverse order.
|
|
233
|
+
hash = Hash.new { |h, key| h[key] = [] }
|
|
234
|
+
posts.docs.each do |p|
|
|
235
|
+
p.data[post_attr]&.each { |t| hash[t] << p }
|
|
236
|
+
end
|
|
237
|
+
hash.each_value { |posts| posts.sort!.reverse! }
|
|
238
|
+
hash
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def tags
|
|
242
|
+
post_attr_hash("tags")
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def categories
|
|
246
|
+
post_attr_hash("categories")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Prepare site data for site payload. The method maintains backward compatibility
|
|
250
|
+
# if the key 'data' is already used in _config.yml.
|
|
251
|
+
#
|
|
252
|
+
# Returns the Hash to be hooked to site.data.
|
|
253
|
+
def site_data
|
|
254
|
+
@site_data ||= (config["data"] || data)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# The Hash payload containing site-wide data.
|
|
258
|
+
#
|
|
259
|
+
# Returns the Hash: { "site" => data } where data is a Hash with keys:
|
|
260
|
+
# "time" - The Time as specified in the configuration or the
|
|
261
|
+
# current time if none was specified.
|
|
262
|
+
# "posts" - The Array of Posts, sorted chronologically by post date
|
|
263
|
+
# and then title.
|
|
264
|
+
# "pages" - The Array of all Pages.
|
|
265
|
+
# "html_pages" - The Array of HTML Pages.
|
|
266
|
+
# "categories" - The Hash of category values and Posts.
|
|
267
|
+
# See Site#post_attr_hash for type info.
|
|
268
|
+
# "tags" - The Hash of tag values and Posts.
|
|
269
|
+
# See Site#post_attr_hash for type info.
|
|
270
|
+
def site_payload
|
|
271
|
+
Drops::UnifiedPayloadDrop.new self
|
|
272
|
+
end
|
|
273
|
+
alias_method :to_liquid, :site_payload
|
|
274
|
+
|
|
275
|
+
# Get the implementation class for the given Converter.
|
|
276
|
+
# Returns the Converter instance implementing the given Converter.
|
|
277
|
+
# klass - The Class of the Converter to fetch.
|
|
278
|
+
def find_converter_instance(klass)
|
|
279
|
+
@find_converter_instance ||= {}
|
|
280
|
+
@find_converter_instance[klass] ||= begin
|
|
281
|
+
converters.find { |converter| converter.instance_of?(klass) } || \
|
|
282
|
+
raise("No Converters found for #{klass}")
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# klass - class or module containing the subclasses.
|
|
287
|
+
# Returns array of instances of subclasses of parameter.
|
|
288
|
+
# Create array of instances of the subclasses of the class or module
|
|
289
|
+
# passed in as argument.
|
|
290
|
+
|
|
291
|
+
def instantiate_subclasses(klass)
|
|
292
|
+
klass.descendants.select { |c| !safe || c.safe }.sort.map do |c|
|
|
293
|
+
c.new(config)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Warns the user if permanent links are relative to the parent
|
|
298
|
+
# directory. As this is a deprecated function of Jekyll.
|
|
299
|
+
#
|
|
300
|
+
# Returns
|
|
301
|
+
def relative_permalinks_are_deprecated
|
|
302
|
+
if config["relative_permalinks"]
|
|
303
|
+
Jekyll.logger.abort_with "Since v3.0, permalinks for pages" \
|
|
304
|
+
" in subfolders must be relative to the" \
|
|
305
|
+
" site source directory, not the parent" \
|
|
306
|
+
" directory. Check https://jekyllrb.com/docs/upgrading/"\
|
|
307
|
+
" for more info."
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Get the to be written documents
|
|
312
|
+
#
|
|
313
|
+
# Returns an Array of Documents which should be written
|
|
314
|
+
def docs_to_write
|
|
315
|
+
@docs_to_write ||= documents.select(&:write?)
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Get all the documents
|
|
319
|
+
#
|
|
320
|
+
# Returns an Array of all Documents
|
|
321
|
+
def documents
|
|
322
|
+
collections.reduce(Set.new) do |docs, (_, collection)|
|
|
323
|
+
docs + collection.docs + collection.files
|
|
324
|
+
end.to_a
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def each_site_file
|
|
328
|
+
%w(pages static_files docs_to_write).each do |type|
|
|
329
|
+
send(type).each do |item|
|
|
330
|
+
yield item
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Returns the FrontmatterDefaults or creates a new FrontmatterDefaults
|
|
336
|
+
# if it doesn't already exist.
|
|
337
|
+
#
|
|
338
|
+
# Returns The FrontmatterDefaults
|
|
339
|
+
def frontmatter_defaults
|
|
340
|
+
@frontmatter_defaults ||= FrontmatterDefaults.new(self)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Whether to perform a full rebuild without incremental regeneration
|
|
344
|
+
#
|
|
345
|
+
# Returns a Boolean: true for a full rebuild, false for normal build
|
|
346
|
+
def incremental?(override = {})
|
|
347
|
+
override["incremental"] || config["incremental"]
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Returns the publisher or creates a new publisher if it doesn't
|
|
351
|
+
# already exist.
|
|
352
|
+
#
|
|
353
|
+
# Returns The Publisher
|
|
354
|
+
def publisher
|
|
355
|
+
@publisher ||= Publisher.new(self)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Public: Prefix a given path with the source directory.
|
|
359
|
+
#
|
|
360
|
+
# paths - (optional) path elements to a file or directory within the
|
|
361
|
+
# source directory
|
|
362
|
+
#
|
|
363
|
+
# Returns a path which is prefixed with the source directory.
|
|
364
|
+
def in_source_dir(*paths)
|
|
365
|
+
paths.reduce(source) do |base, path|
|
|
366
|
+
Jekyll.sanitized_path(base, path)
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Public: Prefix a given path with the theme directory.
|
|
371
|
+
#
|
|
372
|
+
# paths - (optional) path elements to a file or directory within the
|
|
373
|
+
# theme directory
|
|
374
|
+
#
|
|
375
|
+
# Returns a path which is prefixed with the theme root directory.
|
|
376
|
+
def in_theme_dir(*paths)
|
|
377
|
+
return nil unless theme
|
|
378
|
+
|
|
379
|
+
paths.reduce(theme.root) do |base, path|
|
|
380
|
+
Jekyll.sanitized_path(base, path)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Public: Prefix a given path with the destination directory.
|
|
385
|
+
#
|
|
386
|
+
# paths - (optional) path elements to a file or directory within the
|
|
387
|
+
# destination directory
|
|
388
|
+
#
|
|
389
|
+
# Returns a path which is prefixed with the destination directory.
|
|
390
|
+
def in_dest_dir(*paths)
|
|
391
|
+
paths.reduce(dest) do |base, path|
|
|
392
|
+
Jekyll.sanitized_path(base, path)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Public: The full path to the directory that houses all the collections registered
|
|
397
|
+
# with the current site.
|
|
398
|
+
#
|
|
399
|
+
# Returns the source directory or the absolute path to the custom collections_dir
|
|
400
|
+
def collections_path
|
|
401
|
+
dir_str = config["collections_dir"]
|
|
402
|
+
@collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
private
|
|
406
|
+
|
|
407
|
+
# Limits the current posts; removes the posts which exceed the limit_posts
|
|
408
|
+
#
|
|
409
|
+
# Returns nothing
|
|
410
|
+
def limit_posts!
|
|
411
|
+
if limit_posts.positive?
|
|
412
|
+
limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
|
|
413
|
+
posts.docs = posts.docs[-limit, limit]
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Returns the Cleaner or creates a new Cleaner if it doesn't
|
|
418
|
+
# already exist.
|
|
419
|
+
#
|
|
420
|
+
# Returns The Cleaner
|
|
421
|
+
def site_cleaner
|
|
422
|
+
@site_cleaner ||= Cleaner.new(self)
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def configure_plugins
|
|
426
|
+
self.plugin_manager = Jekyll::PluginManager.new(self)
|
|
427
|
+
self.plugins = plugin_manager.plugins_path
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def configure_theme
|
|
431
|
+
self.theme = nil
|
|
432
|
+
return if config["theme"].nil?
|
|
433
|
+
|
|
434
|
+
self.theme =
|
|
435
|
+
if config["theme"].is_a?(String)
|
|
436
|
+
Jekyll::Theme.new(config["theme"])
|
|
437
|
+
else
|
|
438
|
+
Jekyll.logger.warn "Theme:", "value of 'theme' in config should be " \
|
|
439
|
+
"String to use gem-based themes, but got #{config["theme"].class}"
|
|
440
|
+
nil
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def configure_include_paths
|
|
445
|
+
@includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
|
|
446
|
+
@includes_load_paths << theme.includes_path if theme&.includes_path
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
def configure_file_read_opts
|
|
450
|
+
self.file_read_opts = {}
|
|
451
|
+
file_read_opts[:encoding] = config["encoding"] if config["encoding"]
|
|
452
|
+
self.file_read_opts = Jekyll::Utils.merged_file_read_opts(self, {})
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def render_docs(payload)
|
|
456
|
+
collections.each_value do |collection|
|
|
457
|
+
collection.docs.each do |document|
|
|
458
|
+
render_regenerated(document, payload)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def render_pages(payload)
|
|
464
|
+
pages.flatten.each do |page|
|
|
465
|
+
render_regenerated(page, payload)
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def render_regenerated(document, payload)
|
|
470
|
+
return unless regenerator.regenerate?(document)
|
|
471
|
+
|
|
472
|
+
document.output = Jekyll::Renderer.new(self, document, payload).run
|
|
473
|
+
document.trigger_hooks(:post_render)
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
end
|