bridgetown-core 0.13.0 → 0.14.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bridgetown +0 -25
  3. data/bridgetown-core.gemspec +4 -1
  4. data/lib/bridgetown-core.rb +4 -1
  5. data/lib/bridgetown-core/cleaner.rb +1 -0
  6. data/lib/bridgetown-core/command.rb +10 -4
  7. data/lib/bridgetown-core/commands/console.rb +1 -2
  8. data/lib/bridgetown-core/commands/doctor.rb +1 -2
  9. data/lib/bridgetown-core/commands/new.rb +0 -3
  10. data/lib/bridgetown-core/commands/plugins.rb +169 -0
  11. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +2 -2
  12. data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
  13. data/lib/bridgetown-core/concerns/site/content.rb +111 -0
  14. data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
  15. data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
  16. data/lib/bridgetown-core/concerns/site/renderable.rb +50 -0
  17. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  18. data/lib/bridgetown-core/configuration.rb +2 -9
  19. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
  20. data/lib/bridgetown-core/document.rb +1 -1
  21. data/lib/bridgetown-core/drops/site_drop.rb +1 -1
  22. data/lib/bridgetown-core/external.rb +17 -21
  23. data/lib/bridgetown-core/filters.rb +10 -0
  24. data/lib/bridgetown-core/generators/prototype_generator.rb +1 -1
  25. data/lib/bridgetown-core/hooks.rb +62 -62
  26. data/lib/bridgetown-core/layout.rb +10 -4
  27. data/lib/bridgetown-core/page.rb +9 -2
  28. data/lib/bridgetown-core/plugin.rb +2 -0
  29. data/lib/bridgetown-core/plugin_manager.rb +62 -12
  30. data/lib/bridgetown-core/reader.rb +5 -0
  31. data/lib/bridgetown-core/readers/data_reader.rb +5 -2
  32. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  33. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  34. data/lib/bridgetown-core/renderer.rb +7 -10
  35. data/lib/bridgetown-core/site.rb +20 -463
  36. data/lib/bridgetown-core/utils.rb +1 -27
  37. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -4
  38. data/lib/bridgetown-core/version.rb +2 -2
  39. data/lib/bridgetown-core/watcher.rb +5 -1
  40. data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
  41. data/lib/site_template/plugins/site_builder.rb +4 -0
  42. data/lib/site_template/src/_includes/navbar.html +1 -0
  43. data/lib/site_template/src/posts.md +15 -0
  44. data/lib/site_template/start.js +1 -1
  45. metadata +58 -6
@@ -57,7 +57,6 @@ module Bridgetown
57
57
  assign_highlighter_options!
58
58
  assign_layout_data!
59
59
 
60
- Bridgetown.logger.debug "Pre-Render Hooks:", document.relative_path
61
60
  document.trigger_hooks(:pre_render, payload)
62
61
 
63
62
  render_document
@@ -130,15 +129,13 @@ module Bridgetown
130
129
  # Returns String the converted content.
131
130
  def convert(content)
132
131
  converters.reduce(content) do |output, converter|
133
- begin
134
- converter.convert output
135
- rescue StandardError => e
136
- Bridgetown.logger.error "Conversion error:",
137
- "#{converter.class} encountered an error while "\
138
- "converting '#{document.relative_path}':"
139
- Bridgetown.logger.error("", e.to_s)
140
- raise e
141
- end
132
+ converter.convert output
133
+ rescue StandardError => e
134
+ Bridgetown.logger.error "Conversion error:",
135
+ "#{converter.class} encountered an error while "\
136
+ "converting '#{document.relative_path}':"
137
+ Bridgetown.logger.error("", e.to_s)
138
+ raise e
142
139
  end
143
140
  end
144
141
 
@@ -2,125 +2,43 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Site
5
- attr_reader :root_dir, :source, :dest, :cache_dir, :config
5
+ require_all "bridgetown-core/concerns/site"
6
+
7
+ include Configurable
8
+ include Content
9
+ include Extensible
10
+ include Processable
11
+ include Renderable
12
+ include Writable
13
+
14
+ attr_reader :root_dir, :source, :dest, :cache_dir, :config,
15
+ :regenerator, :liquid_renderer, :components_load_paths,
16
+ :includes_load_paths
6
17
  attr_accessor :layouts, :pages, :static_files,
7
18
  :exclude, :include, :lsi, :highlighter, :permalink_style,
8
- :time, :future, :unpublished, :plugins, :limit_posts,
19
+ :time, :future, :unpublished, :limit_posts,
9
20
  :keep_files, :baseurl, :data, :file_read_opts,
10
- :plugin_manager
11
-
12
- attr_accessor :converters, :generators, :reader
13
- attr_reader :regenerator, :liquid_renderer, :components_load_paths,
14
- :includes_load_paths
21
+ :plugin_manager, :converters, :generators, :reader
15
22
 
16
23
  # Public: Initialize a new Site.
17
24
  #
18
25
  # config - A Hash containing site configuration details.
19
26
  def initialize(config)
20
- # Source and destination may not be changed after the site has been created.
21
- @root_dir = File.expand_path(config["root_dir"]).freeze
22
- @source = File.expand_path(config["source"]).freeze
23
- @dest = File.expand_path(config["destination"]).freeze
24
-
25
27
  self.config = config
26
28
 
27
- @cache_dir = in_root_dir(config["cache_dir"])
29
+ @plugin_manager = PluginManager.new(self)
30
+ @cleaner = Cleaner.new(self)
28
31
  @reader = Reader.new(self)
29
32
  @regenerator = Regenerator.new(self)
30
33
  @liquid_renderer = LiquidRenderer.new(self)
31
34
 
32
- Bridgetown.sites << self
33
-
34
- reset
35
- setup
36
-
37
- Bridgetown::Hooks.trigger :site, :after_init, self
38
- end
39
-
40
- # Public: Set the site's configuration. This handles side-effects caused by
41
- # changing values in the configuration.
42
- #
43
- # config - a Bridgetown::Configuration, containing the new configuration.
44
- #
45
- # Returns the new configuration.
46
- def config=(config)
47
- @config = config.clone
48
-
49
- %w(lsi highlighter baseurl exclude include future unpublished
50
- limit_posts keep_files).each do |opt|
51
- send("#{opt}=", config[opt])
52
- end
53
-
54
- configure_cache
55
- configure_plugins
56
- configure_component_paths
57
- configure_include_paths
58
- configure_file_read_opts
59
-
60
- self.permalink_style = config["permalink"].to_sym
61
-
62
- @config
63
- end
64
-
65
- # Public: Read, process, and write this Site to output.
66
- #
67
- # Returns nothing.
68
- def process
69
- reset
70
- read
71
- generate
72
- render
73
- cleanup
74
- write
75
- print_stats if config["profile"]
76
- end
77
-
78
- def print_stats
79
- Bridgetown.logger.info @liquid_renderer.stats_table
80
- end
81
-
82
- # rubocop:disable Metrics/MethodLength
83
- #
84
- # Reset Site details.
85
- #
86
- # Returns nothing
87
- def reset
88
- self.time = if config["time"]
89
- Utils.parse_date(config["time"].to_s, "Invalid time in bridgetown.config.yml.")
90
- else
91
- Time.now
92
- end
93
- self.layouts = {}
94
- self.pages = []
95
- self.static_files = []
96
- self.data = {}
97
- @post_attr_hash = {}
98
- @site_data = nil
99
- @collections = nil
100
- @documents = nil
101
- @docs_to_write = nil
102
- @regenerator.clear_cache
103
- @liquid_renderer.reset
104
- @site_cleaner = nil
105
- frontmatter_defaults.reset
106
-
107
- raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
108
-
109
- Bridgetown::Cache.clear_if_config_changed config
110
- Bridgetown::Hooks.trigger :site, :after_reset, self
111
- end
112
- # rubocop:enable Metrics/MethodLength
113
-
114
- # Load necessary libraries, plugins, converters, and generators.
115
- #
116
- # Returns nothing.
117
- def setup
118
35
  ensure_not_in_dest
119
36
 
120
- plugin_manager.conscientious_require
37
+ Bridgetown.sites << self
38
+ Bridgetown::Hooks.trigger :site, :after_init, self
121
39
 
122
- self.converters = instantiate_subclasses(Bridgetown::Converter)
123
- self.generators = instantiate_subclasses(Bridgetown::Generator)
40
+ reset # Processable
41
+ setup # Extensible
124
42
  end
125
43
 
126
44
  # Check that the destination dir isn't the source dir or a directory
@@ -134,366 +52,5 @@ module Bridgetown
134
52
  end
135
53
  end
136
54
  end
137
-
138
- # The list of collections and their corresponding Bridgetown::Collection instances.
139
- # If config['collections'] is set, a new instance is created
140
- # for each item in the collection, a new hash is returned otherwise.
141
- #
142
- # Returns a Hash containing collection name-to-instance pairs.
143
- def collections
144
- @collections ||= collection_names.each_with_object({}) do |name, hsh|
145
- hsh[name] = Bridgetown::Collection.new(self, name)
146
- end
147
- end
148
-
149
- # The list of collection names.
150
- #
151
- # Returns an array of collection names from the configuration,
152
- # or an empty array if the `collections` key is not set.
153
- def collection_names
154
- case config["collections"]
155
- when Hash
156
- config["collections"].keys
157
- when Array
158
- config["collections"]
159
- when nil
160
- []
161
- else
162
- raise ArgumentError, "Your `collections` key must be a hash or an array."
163
- end
164
- end
165
-
166
- # Read Site data from disk and load it into internal data structures.
167
- #
168
- # Returns nothing.
169
- def read
170
- reader.read
171
- limit_posts!
172
- Bridgetown::Hooks.trigger :site, :post_read, self
173
- end
174
-
175
- # Run each of the Generators.
176
- #
177
- # Returns nothing.
178
- def generate
179
- generators.each do |generator|
180
- start = Time.now
181
- generator.generate(self)
182
- Bridgetown.logger.debug "Generating:",
183
- "#{generator.class} finished in #{Time.now - start} seconds."
184
- end
185
- end
186
-
187
- # Render the site to the destination.
188
- #
189
- # Returns nothing.
190
- def render
191
- payload = site_payload
192
-
193
- Bridgetown::Hooks.trigger :site, :pre_render, self, payload
194
-
195
- execute_inline_ruby_for_layouts!
196
-
197
- render_docs(payload)
198
- render_pages(payload)
199
-
200
- Bridgetown::Hooks.trigger :site, :post_render, self, payload
201
- end
202
-
203
- # Remove orphaned files and empty directories in destination.
204
- #
205
- # Returns nothing.
206
- def cleanup
207
- site_cleaner.cleanup!
208
- end
209
-
210
- # Write static files, pages, and posts.
211
- #
212
- # Returns nothing.
213
- def write
214
- each_site_file do |item|
215
- item.write(dest) if regenerator.regenerate?(item)
216
- end
217
- regenerator.write_metadata
218
- Bridgetown::Hooks.trigger :site, :post_write, self
219
- end
220
-
221
- def posts
222
- collections["posts"] ||= Collection.new(self, "posts")
223
- end
224
-
225
- # Construct a Hash of Posts indexed by the specified Post attribute.
226
- #
227
- # post_attr - The String name of the Post attribute.
228
- #
229
- # Examples
230
- #
231
- # post_attr_hash('categories')
232
- # # => { 'tech' => [<Post A>, <Post B>],
233
- # # 'ruby' => [<Post B>] }
234
- #
235
- # Returns the Hash: { attr => posts } where
236
- # attr - One of the values for the requested attribute.
237
- # posts - The Array of Posts with the given attr value.
238
- def post_attr_hash(post_attr)
239
- # Build a hash map based on the specified post attribute ( post attr =>
240
- # array of posts ) then sort each array in reverse order.
241
- @post_attr_hash[post_attr] ||= begin
242
- hash = Hash.new { |h, key| h[key] = [] }
243
- posts.docs.each do |p|
244
- p.data[post_attr]&.each { |t| hash[t] << p }
245
- end
246
- hash.each_value { |posts| posts.sort!.reverse! }
247
- hash
248
- end
249
- end
250
-
251
- def tags
252
- post_attr_hash("tags")
253
- end
254
-
255
- def categories
256
- post_attr_hash("categories")
257
- end
258
-
259
- # Prepare site data for site payload. The method maintains backward compatibility
260
- # if the key 'data' is already used in bridgetown.config.yml.
261
- #
262
- # Returns the Hash to be hooked to site.data.
263
- def site_data
264
- @site_data ||= (config["data"] || data)
265
- end
266
-
267
- def metadata
268
- data["site_metadata"] || {}
269
- end
270
-
271
- # The Hash payload containing site-wide data.
272
- #
273
- # Returns the Hash: { "site" => data } where data is a Hash with keys:
274
- # "time" - The Time as specified in the configuration or the
275
- # current time if none was specified.
276
- # "posts" - The Array of Posts, sorted chronologically by post date
277
- # and then title.
278
- # "pages" - The Array of all Pages.
279
- # "html_pages" - The Array of HTML Pages.
280
- # "categories" - The Hash of category values and Posts.
281
- # See Site#post_attr_hash for type info.
282
- # "tags" - The Hash of tag values and Posts.
283
- # See Site#post_attr_hash for type info.
284
- def site_payload
285
- Drops::UnifiedPayloadDrop.new self
286
- end
287
- alias_method :to_liquid, :site_payload
288
-
289
- # Get the implementation class for the given Converter.
290
- # Returns the Converter instance implementing the given Converter.
291
- # klass - The Class of the Converter to fetch.
292
- def find_converter_instance(klass)
293
- @find_converter_instance ||= {}
294
- @find_converter_instance[klass] ||= begin
295
- converters.find { |converter| converter.instance_of?(klass) } || \
296
- raise("No Converters found for #{klass}")
297
- end
298
- end
299
-
300
- # klass - class or module containing the subclasses.
301
- # Returns array of instances of subclasses of parameter.
302
- # Create array of instances of the subclasses of the class or module
303
- # passed in as argument.
304
-
305
- def instantiate_subclasses(klass)
306
- klass.descendants.sort.map do |c|
307
- c.new(config)
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
- documents.select(&:write?)
316
- end
317
-
318
- # Get all the documents
319
- #
320
- # Returns an Array of all Documents
321
- def documents
322
- collections.each_with_object(Set.new) do |(_, collection), set|
323
- set.merge(collection.docs).merge(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 root directory.
359
- #
360
- # paths - (optional) path elements to a file or directory within the
361
- # root directory
362
- #
363
- # Returns a path which is prefixed with the root_dir directory.
364
- def in_root_dir(*paths)
365
- paths.reduce(root_dir) do |base, path|
366
- Bridgetown.sanitized_path(base, path)
367
- end
368
- end
369
-
370
- # Public: Prefix a given path with the source directory.
371
- #
372
- # paths - (optional) path elements to a file or directory within the
373
- # source directory
374
- #
375
- # Returns a path which is prefixed with the source directory.
376
- def in_source_dir(*paths)
377
- paths.reduce(source) do |base, path|
378
- Bridgetown.sanitized_path(base, path)
379
- end
380
- end
381
-
382
- # Public: Prefix a given path with the destination directory.
383
- #
384
- # paths - (optional) path elements to a file or directory within the
385
- # destination directory
386
- #
387
- # Returns a path which is prefixed with the destination directory.
388
- def in_dest_dir(*paths)
389
- paths.reduce(dest) do |base, path|
390
- Bridgetown.sanitized_path(base, path)
391
- end
392
- end
393
-
394
- # Public: Prefix a given path with the cache directory.
395
- #
396
- # paths - (optional) path elements to a file or directory within the
397
- # cache directory
398
- #
399
- # Returns a path which is prefixed with the cache directory.
400
- def in_cache_dir(*paths)
401
- paths.reduce(cache_dir) do |base, path|
402
- Bridgetown.sanitized_path(base, path)
403
- end
404
- end
405
-
406
- # Public: The full path to the directory that houses all the collections registered
407
- # with the current site.
408
- #
409
- # Returns the source directory or the absolute path to the custom collections_dir
410
- def collections_path
411
- dir_str = config["collections_dir"]
412
- @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
413
- end
414
-
415
- private
416
-
417
- # Limits the current posts; removes the posts which exceed the limit_posts
418
- #
419
- # Returns nothing
420
- def limit_posts!
421
- if limit_posts.positive?
422
- limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
423
- posts.docs = posts.docs[-limit, limit]
424
- end
425
- end
426
-
427
- # Returns the Cleaner or creates a new Cleaner if it doesn't
428
- # already exist.
429
- #
430
- # Returns The Cleaner
431
- def site_cleaner
432
- @site_cleaner ||= Cleaner.new(self)
433
- end
434
-
435
- # Disable Marshaling cache to disk in Safe Mode
436
- def configure_cache
437
- Bridgetown::Cache.cache_dir = in_root_dir(config["cache_dir"], "Bridgetown/Cache")
438
- Bridgetown::Cache.disable_disk_cache! if config["disable_disk_cache"]
439
- end
440
-
441
- def configure_plugins
442
- self.plugin_manager = Bridgetown::PluginManager.new(self)
443
- self.plugins = plugin_manager.plugins_path
444
- end
445
-
446
- def configure_component_paths
447
- @components_load_paths = config["components_dir"].then do |dir|
448
- dir.is_a?(Array) ? dir : [dir]
449
- end
450
- @components_load_paths.map! do |dir|
451
- if !!(dir =~ %r!^\.\.?\/!)
452
- # allow ./dir or ../../dir type options
453
- File.expand_path(dir.to_s, root_dir)
454
- else
455
- in_source_dir(dir.to_s)
456
- end
457
- end
458
- end
459
-
460
- def configure_include_paths
461
- @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
462
- end
463
-
464
- def configure_file_read_opts
465
- self.file_read_opts = {}
466
- file_read_opts[:encoding] = config["encoding"] if config["encoding"]
467
- self.file_read_opts = Bridgetown::Utils.merged_file_read_opts(self, {})
468
- end
469
-
470
- def execute_inline_ruby_for_layouts!
471
- return unless config.should_execute_inline_ruby?
472
-
473
- layouts.each_value do |layout|
474
- Bridgetown::Utils::RubyExec.search_data_for_ruby_code(layout, self)
475
- end
476
- end
477
-
478
- def render_docs(payload)
479
- collections.each_value do |collection|
480
- collection.docs.each do |document|
481
- render_regenerated(document, payload)
482
- end
483
- end
484
- end
485
-
486
- def render_pages(payload)
487
- pages.each do |page|
488
- render_regenerated(page, payload)
489
- end
490
- end
491
-
492
- def render_regenerated(document, payload)
493
- return unless regenerator.regenerate?(document)
494
-
495
- document.output = Bridgetown::Renderer.new(self, document, payload).run
496
- document.trigger_hooks(:post_render)
497
- end
498
55
  end
499
56
  end