bridgetown-core 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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