bridgetown-core 0.13.0 → 0.15.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -1
  3. data/bin/bridgetown +9 -48
  4. data/bridgetown-core.gemspec +6 -2
  5. data/lib/bridgetown-core.rb +13 -3
  6. data/lib/bridgetown-core/cleaner.rb +1 -0
  7. data/lib/bridgetown-core/commands/apply.rb +73 -0
  8. data/lib/bridgetown-core/commands/base.rb +45 -0
  9. data/lib/bridgetown-core/commands/build.rb +91 -86
  10. data/lib/bridgetown-core/commands/clean.rb +30 -29
  11. data/lib/bridgetown-core/commands/concerns/actions.rb +123 -0
  12. data/lib/bridgetown-core/commands/concerns/build_options.rb +76 -0
  13. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +18 -0
  14. data/lib/bridgetown-core/commands/concerns/summarizable.rb +13 -0
  15. data/lib/bridgetown-core/commands/console.rb +46 -39
  16. data/lib/bridgetown-core/commands/doctor.rb +126 -127
  17. data/lib/bridgetown-core/commands/new.rb +120 -158
  18. data/lib/bridgetown-core/commands/plugins.rb +206 -0
  19. data/lib/bridgetown-core/commands/registrations.rb +16 -0
  20. data/lib/bridgetown-core/commands/serve.rb +214 -215
  21. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +3 -6
  22. data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
  23. data/lib/bridgetown-core/concerns/site/content.rb +111 -0
  24. data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
  25. data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
  26. data/lib/bridgetown-core/concerns/site/renderable.rb +49 -0
  27. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  28. data/lib/bridgetown-core/configuration.rb +2 -9
  29. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
  30. data/lib/bridgetown-core/document.rb +1 -1
  31. data/lib/bridgetown-core/drops/page_drop.rb +1 -1
  32. data/lib/bridgetown-core/drops/site_drop.rb +1 -1
  33. data/lib/bridgetown-core/excerpt.rb +4 -1
  34. data/lib/bridgetown-core/external.rb +17 -21
  35. data/lib/bridgetown-core/filters.rb +10 -0
  36. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -1
  37. data/lib/bridgetown-core/hooks.rb +62 -62
  38. data/lib/bridgetown-core/layout.rb +10 -4
  39. data/lib/bridgetown-core/liquid_renderer.rb +1 -0
  40. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -4
  41. data/lib/bridgetown-core/liquid_renderer/file_system.rb +3 -1
  42. data/lib/bridgetown-core/page.rb +11 -19
  43. data/lib/bridgetown-core/plugin.rb +2 -0
  44. data/lib/bridgetown-core/plugin_manager.rb +88 -21
  45. data/lib/bridgetown-core/reader.rb +5 -0
  46. data/lib/bridgetown-core/readers/data_reader.rb +5 -2
  47. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  48. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  49. data/lib/bridgetown-core/renderer.rb +38 -28
  50. data/lib/bridgetown-core/site.rb +20 -463
  51. data/lib/bridgetown-core/tags/include.rb +12 -0
  52. data/lib/bridgetown-core/tags/render_content.rb +29 -16
  53. data/lib/bridgetown-core/tags/with.rb +15 -0
  54. data/lib/bridgetown-core/utils.rb +45 -27
  55. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -4
  56. data/lib/bridgetown-core/version.rb +2 -2
  57. data/lib/bridgetown-core/watcher.rb +21 -10
  58. data/lib/site_template/Gemfile.erb +19 -0
  59. data/lib/site_template/package.json +1 -0
  60. data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
  61. data/lib/site_template/plugins/site_builder.rb +4 -0
  62. data/lib/site_template/src/_components/footer.html +3 -0
  63. data/lib/site_template/src/_components/head.html +9 -0
  64. data/lib/site_template/src/{_includes → _components}/navbar.html +1 -0
  65. data/lib/site_template/src/_layouts/default.html +3 -3
  66. data/lib/site_template/src/posts.md +15 -0
  67. data/lib/site_template/start.js +1 -1
  68. data/lib/site_template/webpack.config.js +3 -3
  69. metadata +90 -18
  70. data/lib/bridgetown-core/command.rb +0 -106
  71. data/lib/bridgetown-core/commands/help.rb +0 -34
  72. data/lib/site_template/src/_components/.keep +0 -0
  73. data/lib/site_template/src/_includes/footer.html +0 -3
  74. data/lib/site_template/src/_includes/head.html +0 -9
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Writable
5
+ # Remove orphaned files and empty directories in destination.
6
+ #
7
+ # Returns nothing.
8
+ def cleanup
9
+ @cleaner.cleanup!
10
+ end
11
+
12
+ # Write static files, pages, and posts.
13
+ #
14
+ # Returns nothing.
15
+ def write
16
+ each_site_file do |item|
17
+ item.write(dest) if regenerator.regenerate?(item)
18
+ end
19
+ regenerator.write_metadata
20
+ Bridgetown::Hooks.trigger :site, :post_write, self
21
+ end
22
+
23
+ def each_site_file
24
+ %w(pages static_files docs_to_write).each do |type|
25
+ send(type).each do |item|
26
+ yield item
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,7 +4,7 @@ module Bridgetown
4
4
  # TODO: refactor this whole object! Already had to fix obscure
5
5
  # bugs just making minor changes, and all the indirection is
6
6
  # quite hard to decipher. -JW
7
- class Configuration < Hash
7
+ class Configuration < ActiveSupport::HashWithIndifferentAccess
8
8
  # Default options. Overridden by values in bridgetown.config.yml.
9
9
  # Strings rather than symbols are used for compatibility with YAML.
10
10
  DEFAULTS = {
@@ -91,7 +91,7 @@ module Bridgetown
91
91
  #
92
92
  # Returns a Configuration filled with defaults.
93
93
  def from(user_config)
94
- Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
94
+ Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config])
95
95
  .merge_environment_specific_options!
96
96
  .add_default_collections
97
97
  .add_default_excludes
@@ -99,13 +99,6 @@ module Bridgetown
99
99
  end
100
100
  end
101
101
 
102
- # Public: Turn all keys into string
103
- #
104
- # Return a copy of the hash where all its keys are strings
105
- def stringify_keys
106
- each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v }
107
- end
108
-
109
102
  def get_config_value_with_override(config_key, override)
110
103
  override[config_key] || self[config_key] || DEFAULTS[config_key]
111
104
  end
@@ -11,8 +11,6 @@ module Kramdown
11
11
  attr_reader :options, :parser
12
12
 
13
13
  # The implementation is basically the core logic in +Kramdown::Document#initialize+
14
- #
15
- # rubocop:disable Naming/MemoizedInstanceVariableName
16
14
  def setup(options)
17
15
  @cache ||= {}
18
16
 
@@ -36,7 +34,6 @@ module Kramdown
36
34
  end
37
35
  end
38
36
  end
39
- # rubocop:enable Naming/MemoizedInstanceVariableName
40
37
 
41
38
  private
42
39
 
@@ -59,7 +59,7 @@ module Bridgetown
59
59
  # Returns a Hash containing the data. An empty hash is returned if
60
60
  # no data was read.
61
61
  def data
62
- @data ||= {}
62
+ @data ||= ActiveSupport::HashWithIndifferentAccess.new
63
63
  end
64
64
 
65
65
  # Merge some data in with this document's data.
@@ -7,7 +7,7 @@ module Bridgetown
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegators :@obj, :content, :dir, :name, :path, :url
10
+ def_delegators :@obj, :content, :dir, :name, :path, :url, :pager
11
11
  private def_delegator :@obj, :data, :fallback_data
12
12
  end
13
13
  end
@@ -7,7 +7,7 @@ module Bridgetown
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :site_data, :data
10
+ def_delegator :@obj, :data
11
11
  def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
12
12
 
13
13
  private def_delegator :@obj, :config, :fallback_data
@@ -81,7 +81,10 @@ module Bridgetown
81
81
  end
82
82
 
83
83
  def output
84
- @output ||= Renderer.new(doc.site, self, site.site_payload).run
84
+ @output || (
85
+ Renderer.new(doc.site, self, site.site_payload).run
86
+ @output
87
+ )
85
88
  end
86
89
 
87
90
  def place_in_layout?
@@ -10,13 +10,11 @@ module Bridgetown
10
10
  #
11
11
  def require_if_present(names)
12
12
  Array(names).each do |name|
13
- begin
14
- require name
15
- rescue LoadError
16
- Bridgetown.logger.debug "Couldn't load #{name}. Skipping."
17
- yield(name, version_constraint(name)) if block_given?
18
- false
19
- end
13
+ require name
14
+ rescue LoadError
15
+ Bridgetown.logger.debug "Couldn't load #{name}. Skipping."
16
+ yield(name, version_constraint(name)) if block_given?
17
+ false
20
18
  end
21
19
  end
22
20
 
@@ -38,23 +36,21 @@ module Bridgetown
38
36
  #
39
37
  def require_with_graceful_fail(names)
40
38
  Array(names).each do |name|
41
- begin
42
- Bridgetown.logger.debug "Requiring:", name.to_s
43
- require name
44
- rescue LoadError => e
45
- Bridgetown.logger.error "Dependency Error:", <<~MSG
46
- Yikes! It looks like you don't have #{name} or one of its dependencies installed.
47
- In order to use Bridgetown as currently configured, you'll need to install this gem.
39
+ Bridgetown.logger.debug "Requiring:", name.to_s
40
+ require name
41
+ rescue LoadError => e
42
+ Bridgetown.logger.error "Dependency Error:", <<~MSG
43
+ Yikes! It looks like you don't have #{name} or one of its dependencies installed.
44
+ In order to use Bridgetown as currently configured, you'll need to install this gem.
48
45
 
49
- If you've run Bridgetown with `bundle exec`, ensure that you have included the #{name}
50
- gem in your Gemfile as well.
46
+ If you've run Bridgetown with `bundle exec`, ensure that you have included the #{name}
47
+ gem in your Gemfile as well.
51
48
 
52
- The full error message from Ruby is: '#{e.message}'
49
+ The full error message from Ruby is: '#{e.message}'
53
50
 
54
- If you run into trouble, you can find helpful resources at https://bridgetownrb.com/help/!
55
- MSG
56
- raise Bridgetown::Errors::MissingDependencyException, name
57
- end
51
+ If you run into trouble, you can find helpful resources at https://www.bridgetownrb.com/docs/community/
52
+ MSG
53
+ raise Bridgetown::Errors::MissingDependencyException, name
58
54
  end
59
55
  end
60
56
  end
@@ -63,6 +63,16 @@ module Bridgetown
63
63
  Utils.slugify(input, mode: mode)
64
64
  end
65
65
 
66
+ # Titleize a slug or identifier string.
67
+ #
68
+ # input - The string to titleize.
69
+ #
70
+ # Returns a transformed string with spaces and capitalized words.
71
+ # See Utils.titleize_slug for more detail.
72
+ def titleize(input)
73
+ Utils.titleize_slug(input)
74
+ end
75
+
66
76
  # XML escape a string for use. Replaces any special characters with
67
77
  # appropriate HTML entity replacements.
68
78
  #
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Bridgetown::Hooks.register :pages, :post_init do |page|
3
+ Bridgetown::Hooks.register :pages, :post_init, reloadable: false do |page|
4
4
  if page.class != Bridgetown::PrototypePage && page.data["prototype"].is_a?(Hash)
5
5
  Bridgetown::PrototypeGenerator.add_matching_template(page)
6
6
  end
@@ -63,6 +63,8 @@ module Bridgetown
63
63
  new_page
64
64
  end
65
65
 
66
+ # TODO: this would be a great use of .try
67
+ # document.try(:collection).try(:label) == @configured_collection
66
68
  def terms_matching_pages(search_term)
67
69
  selected_docs = @site.documents.select do |document|
68
70
  document.respond_to?(:collection) && document.collection.label == @configured_collection
@@ -2,6 +2,19 @@
2
2
 
3
3
  module Bridgetown
4
4
  module Hooks
5
+ HookRegistration = Struct.new(
6
+ :owner,
7
+ :event,
8
+ :priority,
9
+ :reloadable,
10
+ :block,
11
+ keyword_init: true
12
+ ) do
13
+ def to_s
14
+ "#{owner}:#{event} for #{block}"
15
+ end
16
+ end
17
+
5
18
  DEFAULT_PRIORITY = 20
6
19
 
7
20
  # compatibility layer for octopress-hooks users
@@ -12,51 +25,11 @@ module Bridgetown
12
25
  }.freeze
13
26
 
14
27
  # initial empty hooks
15
- @registry = {
16
- site: {
17
- after_init: [],
18
- after_reset: [],
19
- post_read: [],
20
- pre_render: [],
21
- post_render: [],
22
- post_write: [],
23
- },
24
- pages: {
25
- post_init: [],
26
- pre_render: [],
27
- post_render: [],
28
- post_write: [],
29
- },
30
- posts: {
31
- post_init: [],
32
- pre_render: [],
33
- post_render: [],
34
- post_write: [],
35
- },
36
- documents: {
37
- post_init: [],
38
- pre_render: [],
39
- post_render: [],
40
- post_write: [],
41
- },
42
- clean: {
43
- on_obsolete: [],
44
- },
45
- }
46
-
47
- # map of all hooks and their priorities
48
- @hook_priority = {}
28
+ @registry = {}
49
29
 
50
30
  NotAvailable = Class.new(RuntimeError)
51
31
  Uncallable = Class.new(RuntimeError)
52
32
 
53
- # register hook(s) to be called later, public API
54
- def self.register(owners, event, priority: DEFAULT_PRIORITY, &block)
55
- Array(owners).each do |owner|
56
- register_one(owner, event, priority_value(priority), &block)
57
- end
58
- end
59
-
60
33
  # Ensure the priority is a Fixnum
61
34
  def self.priority_value(priority)
62
35
  return priority if priority.is_a?(Integer)
@@ -64,39 +37,66 @@ module Bridgetown
64
37
  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
65
38
  end
66
39
 
67
- # register a single hook to be called later, internal API
68
- def self.register_one(owner, event, priority, &block)
69
- @registry[owner] ||= {
70
- post_init: [],
71
- pre_render: [],
72
- post_render: [],
73
- post_write: [],
74
- }
75
-
76
- unless @registry[owner][event]
77
- raise NotAvailable, "Invalid hook. #{owner} supports only the " \
78
- "following hooks #{@registry[owner].keys.inspect}"
40
+ # register hook(s) to be called later
41
+ def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
42
+ Array(owners).each do |owner|
43
+ register_one(owner, event, priority: priority, reloadable: reloadable, &block)
79
44
  end
45
+ end
46
+
47
+ # register a single hook to be called later
48
+ def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
49
+ @registry[owner] ||= []
80
50
 
81
51
  raise Uncallable, "Hooks must respond to :call" unless block.respond_to? :call
82
52
 
83
- insert_hook owner, event, priority, &block
53
+ @registry[owner] << HookRegistration.new(
54
+ owner: owner,
55
+ event: event,
56
+ priority: priority_value(priority),
57
+ reloadable: reloadable,
58
+ block: block
59
+ )
60
+ if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
61
+ if Bridgetown.respond_to?(:logger)
62
+ Bridgetown.logger.debug("Registering hook:", @registry[owner].last.to_s)
63
+ else
64
+ p "Registering hook:", @registry[owner].last.to_s
65
+ end
66
+ end
67
+
68
+ block
84
69
  end
85
70
 
86
- def self.insert_hook(owner, event, priority, &block)
87
- @hook_priority[block] = [-priority, @hook_priority.size]
88
- @registry[owner][event] << block
71
+ def self.remove_hook(owner, _event, block)
72
+ @registry[owner].delete_if { |item| item.block == block }
89
73
  end
90
74
 
91
- # interface for Bridgetown core components to trigger hooks
92
75
  def self.trigger(owner, event, *args)
93
76
  # proceed only if there are hooks to call
94
- hooks = @registry.dig(owner, event)
77
+ hooks = @registry[owner]&.select { |item| item.event == event }
95
78
  return if hooks.nil? || hooks.empty?
96
79
 
97
- # sort and call hooks according to priority and load order
98
- hooks.sort_by { |h| @hook_priority[h] }.each do |hook|
99
- hook.call(*args)
80
+ prioritized_hooks(hooks).each do |hook|
81
+ if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
82
+ hook_info = args[0]&.respond_to?(:url) ? args[0].relative_path : hook.block
83
+ Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
84
+ end
85
+ hook.block.call(*args)
86
+ end
87
+ end
88
+
89
+ def self.prioritized_hooks(hooks)
90
+ # sort hooks according to priority and load order
91
+ grouped_hooks = hooks.group_by(&:priority)
92
+ grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
93
+ end
94
+
95
+ def self.clear_reloadable_hooks
96
+ Bridgetown.logger.debug("Clearing reloadable hooks")
97
+
98
+ @registry.each_value do |hooks|
99
+ hooks.delete_if(&:reloadable)
100
100
  end
101
101
  end
102
102
  end
@@ -29,14 +29,20 @@ module Bridgetown
29
29
  #
30
30
  # site - The Site.
31
31
  # base - The String path to the source.
32
- # name - The String filename of the post file.
33
- def initialize(site, base, name)
32
+ # name - The String filename of the layout file.
33
+ # from_plugin - true if the layout comes from a Gem-based plugin folder.
34
+ def initialize(site, base, name, from_plugin: false)
34
35
  @site = site
35
36
  @base = base
36
37
  @name = name
37
38
 
38
- @base_dir = site.source
39
- @path = site.in_source_dir(base, name)
39
+ if from_plugin
40
+ @base_dir = base.sub("/layouts", "")
41
+ @path = File.join(base, name)
42
+ else
43
+ @base_dir = site.source
44
+ @path = site.in_source_dir(base, name)
45
+ end
40
46
  @relative_path = @path.sub(@base_dir, "")
41
47
 
42
48
  self.data = {}
@@ -26,6 +26,7 @@ module Bridgetown
26
26
  def reset
27
27
  @stats = {}
28
28
  @cache = {}
29
+ Renderer.cached_partials = {}
29
30
  end
30
31
 
31
32
  def file(filename)
@@ -10,11 +10,8 @@ module Bridgetown
10
10
 
11
11
  def parse(content)
12
12
  measure_time do
13
- # Remove extraneous indentation for rendercontent tags
14
- processed_content = content.gsub(%r!^[ \t]+{%-? rendercontent!, "{% rendercontent")
15
-
16
13
  @renderer.cache[@filename] ||= Liquid::Template.parse(
17
- processed_content, line_numbers: true
14
+ content, line_numbers: true
18
15
  )
19
16
  end
20
17
  @template = @renderer.cache[@filename]
@@ -31,7 +31,9 @@ module Bridgetown
31
31
  raise Liquid::FileSystemError, "No such template '#{template_path}'" if found_paths.empty?
32
32
 
33
33
  # Last path in the list wins
34
- ::File.read(found_paths.last, site.file_read_opts)
34
+ LiquidComponent.parse(
35
+ ::File.read(found_paths.last, site.file_read_opts)
36
+ ).content
35
37
  end
36
38
  end
37
39
  end
@@ -35,12 +35,18 @@ module Bridgetown
35
35
  # base - The String path to the source.
36
36
  # dir - The String path between the source and the file.
37
37
  # name - The String filename of the file.
38
- def initialize(site, base, dir, name)
38
+ # from_plugin - true if the Page file is located in a Gem-based plugin folder
39
+ # rubocop:disable Metrics/ParameterLists
40
+ def initialize(site, base, dir, name, from_plugin: false)
39
41
  @site = site
40
42
  @base = base
41
43
  @dir = dir
42
44
  @name = name
43
- @path = site.in_source_dir(base, dir, name)
45
+ @path = if from_plugin
46
+ File.join(base, dir, name)
47
+ else
48
+ site.in_source_dir(base, dir, name)
49
+ end
44
50
 
45
51
  process(name)
46
52
  read_yaml(PathManager.join(base, dir), name)
@@ -51,6 +57,7 @@ module Bridgetown
51
57
 
52
58
  Bridgetown::Hooks.trigger :pages, :post_init, self
53
59
  end
60
+ # rubocop:enable Metrics/ParameterLists
54
61
 
55
62
  # The generated directory into which the page will be placed
56
63
  # upon generation. This is derived from the permalink or, if
@@ -66,18 +73,6 @@ module Bridgetown
66
73
  end
67
74
  end
68
75
 
69
- # For backwards-compatibility in subclasses that do not redefine
70
- # the `:to_liquid` method, stash existing definition under a new name
71
- #
72
- # TODO: Remove in Bridgetown 5.0
73
- alias_method :legacy_to_liquid, :to_liquid
74
- private :legacy_to_liquid
75
-
76
- # Private
77
- # Subclasses can choose to optimize their `:to_liquid` method by wrapping
78
- # it around this definition.
79
- #
80
- # TODO: Remove in Bridgetown 5.0
81
76
  def liquid_drop
82
77
  @liquid_drop ||= begin
83
78
  defaults = site.frontmatter_defaults.all(relative_path, type)
@@ -87,15 +82,12 @@ module Bridgetown
87
82
  Drops::PageDrop.new(self)
88
83
  end
89
84
  end
90
- private :liquid_drop
91
85
 
92
86
  # Public
93
87
  #
94
88
  # Liquid representation of current page
95
- #
96
- # TODO: Remove optional parameter in Bridgetown 5.0
97
- def to_liquid(attrs = nil)
98
- self.class == Bridgetown::Page ? liquid_drop : legacy_to_liquid(attrs)
89
+ def to_liquid
90
+ liquid_drop
99
91
  end
100
92
 
101
93
  # The full path and filename of the post. Defined in the YAML of the post