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
@@ -10,6 +10,8 @@ module Bridgetown
10
10
  high: 10,
11
11
  }.freeze
12
12
 
13
+ SourceManifest = Struct.new(:origin, :components, :content, :layouts, keyword_init: true)
14
+
13
15
  #
14
16
 
15
17
  def self.inherited(const)
@@ -2,8 +2,33 @@
2
2
 
3
3
  module Bridgetown
4
4
  class PluginManager
5
+ PLUGINS_GROUP = :bridgetown_plugins
6
+
5
7
  attr_reader :site
6
8
 
9
+ @source_manifests = Set.new
10
+ @registered_plugins = Set.new
11
+
12
+ def self.add_source_manifest(source_manifest)
13
+ unless source_manifest.is_a?(Bridgetown::Plugin::SourceManifest)
14
+ raise "You must add a SourceManifest instance"
15
+ end
16
+
17
+ @source_manifests << source_manifest
18
+ end
19
+
20
+ def self.new_source_manifest(*args)
21
+ add_source_manifest(Bridgetown::Plugin::SourceManifest.new(*args))
22
+ end
23
+
24
+ def self.add_registered_plugin(gem_or_plugin_file)
25
+ @registered_plugins << gem_or_plugin_file
26
+ end
27
+
28
+ class << self
29
+ attr_reader :source_manifests, :registered_plugins
30
+ end
31
+
7
32
  # Create an instance of this class.
8
33
  #
9
34
  # site - the instance of Bridgetown::Site we're concerned with
@@ -13,22 +38,23 @@ module Bridgetown
13
38
  @site = site
14
39
  end
15
40
 
16
- # Require all the plugins which are allowed.
17
- #
18
- # Returns nothing
19
- def conscientious_require
20
- require_plugin_files
21
- end
22
-
23
41
  def self.require_from_bundler
24
42
  if !ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
25
43
  require "bundler"
26
44
 
27
- Bundler.setup
28
- required_gems = Bundler.require(:bridgetown_plugins)
45
+ required_gems = Bundler.require PLUGINS_GROUP
46
+ required_gems.select! do |dep|
47
+ (dep.groups & [PLUGINS_GROUP]).any? && dep.should_include?
48
+ end
49
+
29
50
  install_yarn_dependencies(required_gems)
30
- message = "Required #{required_gems.map(&:name).join(", ")}"
31
- Bridgetown.logger.debug("PluginManager:", message)
51
+
52
+ required_gems.each do |installed_gem|
53
+ add_registered_plugin installed_gem
54
+ end
55
+
56
+ Bridgetown.logger.debug("PluginManager:",
57
+ "Required #{required_gems.map(&:name).join(", ")}")
32
58
  ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] = "true"
33
59
 
34
60
  true
@@ -47,28 +73,69 @@ module Bridgetown
47
73
  package_json = JSON.parse(File.read("package.json"))
48
74
 
49
75
  required_gems.each do |loaded_gem|
50
- next unless loaded_gem.to_spec&.metadata&.dig("yarn-add")
51
-
52
- yarn_add_dependency = loaded_gem.to_spec.metadata["yarn-add"].split("@")
53
- next unless yarn_add_dependency.length == 2
54
-
55
- # check matching version number is see if it's already installed
56
- current_package = package_json["dependencies"].dig(yarn_add_dependency.first)
57
- next unless current_package.nil? || current_package != yarn_add_dependency.last
76
+ yarn_dependency = find_yarn_dependency(loaded_gem)
77
+ next unless add_yarn_dependency?(yarn_dependency, package_json)
58
78
 
59
79
  # all right, time to install the package
60
- cmd = "yarn add #{yarn_add_dependency.join("@")}"
80
+ cmd = "yarn add #{yarn_dependency.join("@")}"
61
81
  system cmd
62
82
  end
63
83
  end
64
84
 
85
+ def self.find_yarn_dependency(loaded_gem)
86
+ yarn_dependency = loaded_gem.to_spec&.metadata&.dig("yarn-add")&.split("@")
87
+ return nil if yarn_dependency&.length != 2
88
+
89
+ yarn_dependency
90
+ end
91
+
92
+ def self.add_yarn_dependency?(yarn_dependency, package_json)
93
+ return false if yarn_dependency.nil?
94
+
95
+ # check matching version number is see if it's already installed
96
+ if package_json["dependencies"]
97
+ current_version = package_json["dependencies"].dig(yarn_dependency.first)
98
+ package_requires_updating?(current_version, yarn_dependency.last)
99
+ else
100
+ true
101
+ end
102
+ end
103
+
104
+ def self.package_requires_updating?(current_version, dep_version)
105
+ current_version.nil? || current_version != dep_version && !current_version.include?("/")
106
+ end
107
+
65
108
  # Require all .rb files
66
109
  #
67
110
  # Returns nothing.
68
111
  def require_plugin_files
69
112
  plugins_path.each do |plugin_search_path|
70
113
  plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb"))
71
- Bridgetown::External.require_with_graceful_fail(plugin_files)
114
+
115
+ # Require "site_builder.rb" first if present so subclasses can all
116
+ # inherit from SiteBuilder without needing explicit require statements
117
+ sorted_plugin_files = plugin_files.select do |path|
118
+ path.include?("site_builder.rb")
119
+ end + plugin_files.reject do |path|
120
+ path.include?("site_builder.rb")
121
+ end
122
+
123
+ sorted_plugin_files.each do |plugin_file|
124
+ self.class.add_registered_plugin plugin_file
125
+ end
126
+ Bridgetown::External.require_with_graceful_fail(sorted_plugin_files)
127
+ end
128
+ end
129
+
130
+ # Reload .rb plugin files via the watcher
131
+ def reload_plugin_files
132
+ plugins_path.each do |plugin_search_path|
133
+ plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb"))
134
+ Array(plugin_files).each do |name|
135
+ Bridgetown.logger.debug "Reloading:", name.to_s
136
+ self.class.add_registered_plugin name
137
+ load name
138
+ end
72
139
  end
73
140
  end
74
141
 
@@ -11,6 +11,7 @@ module Bridgetown
11
11
  # Read Site data from disk and load it into internal data structures.
12
12
  #
13
13
  # Returns nothing.
14
+ # rubocop:disable Metrics/AbcSize
14
15
  def read
15
16
  @site.layouts = LayoutReader.new(site).read
16
17
  read_directories
@@ -18,7 +19,11 @@ module Bridgetown
18
19
  sort_files!
19
20
  @site.data = DataReader.new(site).read(site.config["data_dir"])
20
21
  CollectionReader.new(site).read
22
+ Bridgetown::PluginManager.source_manifests.map(&:content).compact.each do |plugin_content_dir|
23
+ PluginContentReader.new(site, plugin_content_dir).read
24
+ end
21
25
  end
26
+ # rubocop:enable Metrics/AbcSize
22
27
 
23
28
  # Sorts posts, pages, and static files.
24
29
  def sort_files!
@@ -5,7 +5,7 @@ module Bridgetown
5
5
  attr_reader :site, :content
6
6
  def initialize(site)
7
7
  @site = site
8
- @content = {}
8
+ @content = ActiveSupport::HashWithIndifferentAccess.new
9
9
  @entry_filter = EntryFilter.new(site)
10
10
  end
11
11
 
@@ -41,7 +41,10 @@ module Bridgetown
41
41
  next if @entry_filter.symlink?(path)
42
42
 
43
43
  if File.directory?(path)
44
- read_data_to(path, data[sanitize_filename(entry)] = {})
44
+ read_data_to(
45
+ path,
46
+ data[sanitize_filename(entry)] = ActiveSupport::HashWithIndifferentAccess.new
47
+ )
45
48
  else
46
49
  key = sanitize_filename(File.basename(entry, ".*"))
47
50
  data[key] = read_data_file(path)
@@ -14,6 +14,13 @@ module Bridgetown
14
14
  Layout.new(site, layout_directory, layout_file)
15
15
  end
16
16
 
17
+ Bridgetown::PluginManager.source_manifests.map(&:layouts).compact.each do |plugin_layouts|
18
+ layout_entries(plugin_layouts).each do |layout_file|
19
+ @layouts[layout_name(layout_file)] ||= \
20
+ Layout.new(site, plugin_layouts, layout_file, from_plugin: true)
21
+ end
22
+ end
23
+
17
24
  @layouts
18
25
  end
19
26
 
@@ -23,8 +30,8 @@ module Bridgetown
23
30
 
24
31
  private
25
32
 
26
- def layout_entries
27
- entries_in layout_directory
33
+ def layout_entries(dir = layout_directory)
34
+ entries_in dir
28
35
  end
29
36
 
30
37
  def entries_in(dir)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class PluginContentReader
5
+ attr_reader :site, :content_dir
6
+
7
+ def initialize(site, plugin_content_dir)
8
+ @site = site
9
+ @content_dir = plugin_content_dir
10
+ @content_files = Set.new
11
+ end
12
+
13
+ def read
14
+ return unless content_dir
15
+
16
+ Find.find(content_dir) do |path|
17
+ next if File.directory?(path)
18
+
19
+ if File.symlink?(path)
20
+ Bridgetown.logger.warn "Plugin content reader:", "Ignored symlinked asset: #{path}"
21
+ else
22
+ read_content_file(path)
23
+ end
24
+ end
25
+ end
26
+
27
+ def read_content_file(path)
28
+ dir = File.dirname(path.sub("#{content_dir}/", ""))
29
+ name = File.basename(path)
30
+
31
+ @content_files << if Utils.has_yaml_header?(path)
32
+ Bridgetown::Page.new(site, content_dir, dir, name, from_plugin: true)
33
+ else
34
+ Bridgetown::StaticFile.new(site, content_dir, "/#{dir}", name)
35
+ end
36
+
37
+ add_to(site.pages, Bridgetown::Page)
38
+ add_to(site.static_files, Bridgetown::StaticFile)
39
+ end
40
+
41
+ def add_to(content_type, klass)
42
+ existing_paths = content_type.map(&:relative_path).compact
43
+ @content_files.select { |item| item.is_a?(klass) }.each do |item|
44
+ content_type << item unless existing_paths.include?(item.relative_path)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -5,11 +5,16 @@ module Bridgetown
5
5
  attr_reader :document, :site
6
6
  attr_writer :layouts, :payload
7
7
 
8
+ class << self
9
+ attr_accessor :cached_partials
10
+ end
11
+
8
12
  def initialize(site, document, site_payload = nil)
9
13
  @site = site
10
14
  @document = document
11
15
  @payload = site_payload
12
16
  @layouts = nil
17
+ self.class.cached_partials ||= {}
13
18
  end
14
19
 
15
20
  # Fetches the payload used in Liquid rendering.
@@ -48,7 +53,7 @@ module Bridgetown
48
53
 
49
54
  # Prepare payload and render the document
50
55
  #
51
- # Returns String rendered document output
56
+ # Returns nothing
52
57
  def run
53
58
  Bridgetown.logger.debug "Rendering:", document.relative_path
54
59
 
@@ -57,10 +62,9 @@ module Bridgetown
57
62
  assign_highlighter_options!
58
63
  assign_layout_data!
59
64
 
60
- Bridgetown.logger.debug "Pre-Render Hooks:", document.relative_path
61
65
  document.trigger_hooks(:pre_render, payload)
62
-
63
- render_document
66
+ document.output = render_document
67
+ document.trigger_hooks(:post_render)
64
68
  end
65
69
 
66
70
  # Render the document.
@@ -68,18 +72,14 @@ module Bridgetown
68
72
  # Returns String rendered document output
69
73
  # rubocop: disable Metrics/AbcSize
70
74
  def render_document
71
- info = {
72
- registers: { site: site, page: payload["page"] },
73
- strict_filters: liquid_options["strict_filters"],
74
- strict_variables: liquid_options["strict_variables"],
75
- }
75
+ liquid_context = provide_liquid_context
76
76
 
77
77
  execute_inline_ruby!
78
78
 
79
79
  output = document.content
80
80
  if document.render_with_liquid?
81
81
  Bridgetown.logger.debug "Rendering Liquid:", document.relative_path
82
- output = render_liquid(output, payload, info, document.path)
82
+ output = render_liquid(output, payload, liquid_context, document.path)
83
83
  end
84
84
 
85
85
  Bridgetown.logger.debug "Rendering Markup:", document.relative_path
@@ -88,12 +88,24 @@ module Bridgetown
88
88
 
89
89
  if document.place_in_layout?
90
90
  Bridgetown.logger.debug "Rendering Layout:", document.relative_path
91
- output = place_in_layouts(output, payload, info)
91
+ output = place_in_layouts(output, payload, liquid_context)
92
92
  end
93
93
 
94
94
  output
95
95
  end
96
96
 
97
+ def provide_liquid_context
98
+ {
99
+ registers: {
100
+ site: site,
101
+ page: payload["page"],
102
+ cached_partials: self.class.cached_partials,
103
+ },
104
+ strict_filters: liquid_options["strict_filters"],
105
+ strict_variables: liquid_options["strict_variables"],
106
+ }
107
+ end
108
+
97
109
  def execute_inline_ruby!
98
110
  return unless site.config.should_execute_inline_ruby?
99
111
 
@@ -102,21 +114,21 @@ module Bridgetown
102
114
 
103
115
  # rubocop: enable Metrics/AbcSize
104
116
 
105
- # Render the given content with the payload and info
117
+ # Render the given content with the payload and context
106
118
  #
107
119
  # content -
108
120
  # payload -
109
- # info -
121
+ # context -
110
122
  # path - (optional) the path to the file, for use in ex
111
123
  #
112
124
  # Returns String the content, rendered by Liquid.
113
- def render_liquid(content, payload, info, path = nil)
125
+ def render_liquid(content, payload, liquid_context, path = nil)
114
126
  template = site.liquid_renderer.file(path).parse(content)
115
127
  template.warnings.each do |e|
116
128
  Bridgetown.logger.warn "Liquid Warning:",
117
129
  LiquidRenderer.format_error(e, path || document.relative_path)
118
130
  end
119
- template.render!(payload, info)
131
+ template.render!(payload, liquid_context)
120
132
  # rubocop: disable Lint/RescueException
121
133
  rescue Exception => e
122
134
  Bridgetown.logger.error "Liquid Exception:",
@@ -130,15 +142,13 @@ module Bridgetown
130
142
  # Returns String the converted content.
131
143
  def convert(content)
132
144
  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
145
+ converter.convert output
146
+ rescue StandardError => e
147
+ Bridgetown.logger.error "Conversion error:",
148
+ "#{converter.class} encountered an error while "\
149
+ "converting '#{document.relative_path}':"
150
+ Bridgetown.logger.error("", e.to_s)
151
+ raise e
142
152
  end
143
153
  end
144
154
 
@@ -154,7 +164,7 @@ module Bridgetown
154
164
  # Render layouts and place document content inside.
155
165
  #
156
166
  # Returns String rendered content
157
- def place_in_layouts(content, payload, info)
167
+ def place_in_layouts(content, payload, liquid_context)
158
168
  output = content.dup
159
169
  layout = layouts[document.data["layout"].to_s]
160
170
  validate_layout(layout)
@@ -165,7 +175,7 @@ module Bridgetown
165
175
  payload["layout"] = nil
166
176
 
167
177
  while layout
168
- output = render_layout(output, layout, info)
178
+ output = render_layout(output, layout, liquid_context)
169
179
  add_regenerator_dependencies(layout)
170
180
 
171
181
  next unless (layout = site.layouts[layout.data["layout"]])
@@ -192,14 +202,14 @@ module Bridgetown
192
202
  # Render layout content into document.output
193
203
  #
194
204
  # Returns String rendered content
195
- def render_layout(output, layout, info)
205
+ def render_layout(output, layout, liquid_context)
196
206
  payload["content"] = output
197
207
  payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
198
208
 
199
209
  render_liquid(
200
210
  layout.content,
201
211
  payload,
202
- info,
212
+ liquid_context,
203
213
  layout.path
204
214
  )
205
215
  end
@@ -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