ngage 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/exe/ngage +55 -0
  4. data/lib/ngage.rb +3 -0
  5. data/lib/ngage/jekyll.rb +204 -0
  6. data/lib/ngage/jekyll/cleaner.rb +111 -0
  7. data/lib/ngage/jekyll/collection.rb +235 -0
  8. data/lib/ngage/jekyll/command.rb +103 -0
  9. data/lib/ngage/jekyll/commands/build.rb +93 -0
  10. data/lib/ngage/jekyll/commands/clean.rb +45 -0
  11. data/lib/ngage/jekyll/commands/doctor.rb +173 -0
  12. data/lib/ngage/jekyll/commands/help.rb +34 -0
  13. data/lib/ngage/jekyll/commands/new.rb +157 -0
  14. data/lib/ngage/jekyll/commands/new_theme.rb +42 -0
  15. data/lib/ngage/jekyll/commands/serve.rb +354 -0
  16. data/lib/ngage/jekyll/commands/serve/live_reload_reactor.rb +122 -0
  17. data/lib/ngage/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
  18. data/lib/ngage/jekyll/commands/serve/servlet.rb +203 -0
  19. data/lib/ngage/jekyll/commands/serve/websockets.rb +81 -0
  20. data/lib/ngage/jekyll/configuration.rb +391 -0
  21. data/lib/ngage/jekyll/converter.rb +54 -0
  22. data/lib/ngage/jekyll/converters/identity.rb +41 -0
  23. data/lib/ngage/jekyll/converters/markdown.rb +116 -0
  24. data/lib/ngage/jekyll/converters/markdown/kramdown_parser.rb +122 -0
  25. data/lib/ngage/jekyll/converters/smartypants.rb +70 -0
  26. data/lib/ngage/jekyll/convertible.rb +253 -0
  27. data/lib/ngage/jekyll/deprecator.rb +50 -0
  28. data/lib/ngage/jekyll/document.rb +503 -0
  29. data/lib/ngage/jekyll/drops/collection_drop.rb +20 -0
  30. data/lib/ngage/jekyll/drops/document_drop.rb +69 -0
  31. data/lib/ngage/jekyll/drops/drop.rb +209 -0
  32. data/lib/ngage/jekyll/drops/excerpt_drop.rb +15 -0
  33. data/lib/ngage/jekyll/drops/jekyll_drop.rb +32 -0
  34. data/lib/ngage/jekyll/drops/site_drop.rb +56 -0
  35. data/lib/ngage/jekyll/drops/static_file_drop.rb +14 -0
  36. data/lib/ngage/jekyll/drops/unified_payload_drop.rb +26 -0
  37. data/lib/ngage/jekyll/drops/url_drop.rb +89 -0
  38. data/lib/ngage/jekyll/entry_filter.rb +127 -0
  39. data/lib/ngage/jekyll/errors.rb +20 -0
  40. data/lib/ngage/jekyll/excerpt.rb +180 -0
  41. data/lib/ngage/jekyll/external.rb +76 -0
  42. data/lib/ngage/jekyll/filters.rb +390 -0
  43. data/lib/ngage/jekyll/filters/date_filters.rb +110 -0
  44. data/lib/ngage/jekyll/filters/grouping_filters.rb +64 -0
  45. data/lib/ngage/jekyll/filters/url_filters.rb +68 -0
  46. data/lib/ngage/jekyll/frontmatter_defaults.rb +233 -0
  47. data/lib/ngage/jekyll/generator.rb +5 -0
  48. data/lib/ngage/jekyll/hooks.rb +106 -0
  49. data/lib/ngage/jekyll/layout.rb +62 -0
  50. data/lib/ngage/jekyll/liquid_extensions.rb +22 -0
  51. data/lib/ngage/jekyll/liquid_renderer.rb +63 -0
  52. data/lib/ngage/jekyll/liquid_renderer/file.rb +56 -0
  53. data/lib/ngage/jekyll/liquid_renderer/table.rb +98 -0
  54. data/lib/ngage/jekyll/log_adapter.rb +151 -0
  55. data/lib/ngage/jekyll/mime.types +825 -0
  56. data/lib/ngage/jekyll/page.rb +185 -0
  57. data/lib/ngage/jekyll/page_without_a_file.rb +14 -0
  58. data/lib/ngage/jekyll/plugin.rb +92 -0
  59. data/lib/ngage/jekyll/plugin_manager.rb +115 -0
  60. data/lib/ngage/jekyll/publisher.rb +23 -0
  61. data/lib/ngage/jekyll/reader.rb +154 -0
  62. data/lib/ngage/jekyll/readers/collection_reader.rb +22 -0
  63. data/lib/ngage/jekyll/readers/data_reader.rb +75 -0
  64. data/lib/ngage/jekyll/readers/layout_reader.rb +70 -0
  65. data/lib/ngage/jekyll/readers/page_reader.rb +25 -0
  66. data/lib/ngage/jekyll/readers/post_reader.rb +72 -0
  67. data/lib/ngage/jekyll/readers/static_file_reader.rb +25 -0
  68. data/lib/ngage/jekyll/readers/theme_assets_reader.rb +51 -0
  69. data/lib/ngage/jekyll/regenerator.rb +195 -0
  70. data/lib/ngage/jekyll/related_posts.rb +52 -0
  71. data/lib/ngage/jekyll/renderer.rb +266 -0
  72. data/lib/ngage/jekyll/site.rb +476 -0
  73. data/lib/ngage/jekyll/static_file.rb +169 -0
  74. data/lib/ngage/jekyll/stevenson.rb +60 -0
  75. data/lib/ngage/jekyll/tags/highlight.rb +108 -0
  76. data/lib/ngage/jekyll/tags/include.rb +226 -0
  77. data/lib/ngage/jekyll/tags/link.rb +40 -0
  78. data/lib/ngage/jekyll/tags/post_url.rb +104 -0
  79. data/lib/ngage/jekyll/theme.rb +73 -0
  80. data/lib/ngage/jekyll/theme_builder.rb +121 -0
  81. data/lib/ngage/jekyll/url.rb +160 -0
  82. data/lib/ngage/jekyll/utils.rb +370 -0
  83. data/lib/ngage/jekyll/utils/ansi.rb +57 -0
  84. data/lib/ngage/jekyll/utils/exec.rb +26 -0
  85. data/lib/ngage/jekyll/utils/internet.rb +37 -0
  86. data/lib/ngage/jekyll/utils/platforms.rb +82 -0
  87. data/lib/ngage/jekyll/utils/thread_event.rb +31 -0
  88. data/lib/ngage/jekyll/utils/win_tz.rb +75 -0
  89. data/lib/ngage/site_template/.gitignore +5 -0
  90. data/lib/ngage/site_template/404.html +25 -0
  91. data/lib/ngage/site_template/_config.yml +47 -0
  92. data/lib/ngage/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -0
  93. data/lib/ngage/site_template/about.markdown +18 -0
  94. data/lib/ngage/site_template/index.markdown +6 -0
  95. data/lib/ngage/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
  96. data/lib/ngage/theme_template/Gemfile +4 -0
  97. data/lib/ngage/theme_template/LICENSE.txt.erb +21 -0
  98. data/lib/ngage/theme_template/README.md.erb +52 -0
  99. data/lib/ngage/theme_template/_layouts/default.html +1 -0
  100. data/lib/ngage/theme_template/_layouts/page.html +5 -0
  101. data/lib/ngage/theme_template/_layouts/post.html +5 -0
  102. data/lib/ngage/theme_template/example/_config.yml.erb +1 -0
  103. data/lib/ngage/theme_template/example/_post.md +12 -0
  104. data/lib/ngage/theme_template/example/index.html +14 -0
  105. data/lib/ngage/theme_template/example/style.scss +7 -0
  106. data/lib/ngage/theme_template/gitignore.erb +6 -0
  107. data/lib/ngage/theme_template/theme.gemspec.erb +19 -0
  108. data/lib/ngage/version.rb +5 -0
  109. 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