bridgetown-core 0.16.0.beta1 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -0
  3. data/bridgetown-core.gemspec +3 -1
  4. data/lib/bridgetown-core.rb +45 -29
  5. data/lib/bridgetown-core/collection.rb +5 -1
  6. data/lib/bridgetown-core/commands/apply.rb +2 -2
  7. data/lib/bridgetown-core/commands/concerns/actions.rb +2 -1
  8. data/lib/bridgetown-core/commands/console.rb +4 -4
  9. data/lib/bridgetown-core/commands/new.rb +1 -1
  10. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  11. data/lib/bridgetown-core/concerns/liquid_renderable.rb +10 -0
  12. data/lib/bridgetown-core/concerns/site/configurable.rb +24 -22
  13. data/lib/bridgetown-core/concerns/site/content.rb +46 -33
  14. data/lib/bridgetown-core/concerns/site/extensible.rb +14 -13
  15. data/lib/bridgetown-core/concerns/site/localizable.rb +24 -0
  16. data/lib/bridgetown-core/concerns/site/processable.rb +12 -11
  17. data/lib/bridgetown-core/concerns/site/renderable.rb +35 -28
  18. data/lib/bridgetown-core/concerns/site/writable.rb +7 -15
  19. data/lib/bridgetown-core/concerns/validatable.rb +2 -2
  20. data/lib/bridgetown-core/configuration.rb +14 -6
  21. data/lib/bridgetown-core/converter.rb +0 -42
  22. data/lib/bridgetown-core/converters/erb_templates.rb +93 -17
  23. data/lib/bridgetown-core/converters/liquid_templates.rb +96 -0
  24. data/lib/bridgetown-core/converters/markdown.rb +0 -3
  25. data/lib/bridgetown-core/document.rb +34 -21
  26. data/lib/bridgetown-core/drops/site_drop.rb +5 -1
  27. data/lib/bridgetown-core/drops/unified_payload_drop.rb +0 -1
  28. data/lib/bridgetown-core/drops/url_drop.rb +19 -3
  29. data/lib/bridgetown-core/excerpt.rb +1 -1
  30. data/lib/bridgetown-core/filters.rb +37 -55
  31. data/lib/bridgetown-core/filters/condition_helpers.rb +56 -0
  32. data/lib/bridgetown-core/frontmatter_defaults.rb +17 -0
  33. data/lib/bridgetown-core/generators/prototype_generator.rb +42 -25
  34. data/lib/bridgetown-core/helpers.rb +84 -0
  35. data/lib/bridgetown-core/liquid_renderer.rb +1 -1
  36. data/lib/bridgetown-core/log_writer.rb +2 -2
  37. data/lib/bridgetown-core/page.rb +8 -2
  38. data/lib/bridgetown-core/plugin_manager.rb +44 -3
  39. data/lib/bridgetown-core/reader.rb +2 -4
  40. data/lib/bridgetown-core/readers/collection_reader.rb +1 -0
  41. data/lib/bridgetown-core/readers/data_reader.rb +4 -3
  42. data/lib/bridgetown-core/readers/defaults_reader.rb +27 -0
  43. data/lib/bridgetown-core/readers/layout_reader.rb +1 -0
  44. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  45. data/lib/bridgetown-core/readers/post_reader.rb +29 -15
  46. data/lib/bridgetown-core/readers/static_file_reader.rb +1 -0
  47. data/lib/bridgetown-core/renderer.rb +42 -160
  48. data/lib/bridgetown-core/ruby_template_view.rb +26 -8
  49. data/lib/bridgetown-core/site.rb +14 -2
  50. data/lib/bridgetown-core/tags/find.rb +86 -0
  51. data/lib/bridgetown-core/tags/t.rb +14 -0
  52. data/lib/bridgetown-core/tags/webpack_path.rb +6 -41
  53. data/lib/bridgetown-core/utils.rb +69 -2
  54. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -1
  55. data/lib/bridgetown-core/version.rb +2 -2
  56. data/lib/bridgetown-core/watcher.rb +1 -0
  57. data/lib/site_template/src/_layouts/{default.html → default.liquid} +0 -0
  58. data/lib/site_template/src/_layouts/{home.html → home.liquid} +0 -0
  59. data/lib/site_template/src/_layouts/{page.html → page.liquid} +0 -0
  60. data/lib/site_template/src/_layouts/{post.html → post.liquid} +0 -0
  61. data/lib/site_template/src/images/.keep +1 -0
  62. metadata +47 -10
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class DefaultsReader
5
+ attr_reader :site, :path_defaults
6
+
7
+ def initialize(site)
8
+ @site = site
9
+ @path_defaults = HashWithDotAccess::Hash.new
10
+ end
11
+
12
+ def read
13
+ return unless File.directory?(site.source)
14
+
15
+ entries = Dir.chdir(site.source) do
16
+ Dir["**/_defaults.{yaml,yml,json}"]
17
+ end
18
+
19
+ entries.each do |entry|
20
+ path = @site.in_source_dir(entry)
21
+ @path_defaults[File.dirname(path) + File::SEPARATOR] = SafeYAML.load_file(path)
22
+ end
23
+
24
+ @path_defaults
25
+ end
26
+ end
27
+ end
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class LayoutReader
5
5
  attr_reader :site
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  @layouts = {}
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class PageReader
5
5
  attr_reader :site, :dir, :unfiltered_content
6
+
6
7
  def initialize(site, dir)
7
8
  @site = site
8
9
  @dir = dir
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class PostReader
5
5
  attr_reader :site, :unfiltered_content
6
+
6
7
  def initialize(site)
7
8
  @site = site
8
9
  end
@@ -20,13 +21,13 @@ module Bridgetown
20
21
  # Read all the files in <source>/<dir>/<magic_dir> and create a new
21
22
  # Document object with each one insofar as it matches the regexp matcher.
22
23
  #
23
- # dir - The String relative path of the directory to read.
24
+ # @param dir [String] relative path of the directory to read.
24
25
  #
25
- # Returns nothing.
26
+ # @return [Array<Document, StaticFile>]
26
27
  def read_publishable(dir, magic_dir, matcher)
27
28
  read_content(dir, magic_dir, matcher)
28
- .tap { |entries| entries.select { |entry| entry.respond_to?(:read) }.each(&:read) }
29
- .select { |entry| processable?(entry) }
29
+ .tap { |items| items.select { |item| item.respond_to?(:read) }.each(&:read) }
30
+ .select { |item| item_added_to_site?(item) }
30
31
  end
31
32
 
32
33
  # Read all the content files from <source>/<dir>/magic_dir
@@ -69,26 +70,39 @@ module Bridgetown
69
70
  )
70
71
  end
71
72
 
72
- def processable?(doc)
73
- return true if doc.is_a?(StaticFile)
73
+ def processable?(item)
74
+ return true if item.is_a?(StaticFile)
74
75
 
75
- if doc.content.nil?
76
- Bridgetown.logger.debug "Skipping:", "Content in #{doc.relative_path} is nil"
76
+ if item.content.nil?
77
+ Bridgetown.logger.debug "Skipping:", "Content in #{item.relative_path} is nil"
77
78
  false
78
- elsif !doc.content.valid_encoding?
79
- Bridgetown.logger.debug "Skipping:", "#{doc.relative_path} is not valid UTF-8"
79
+ elsif !item.content.valid_encoding?
80
+ Bridgetown.logger.debug "Skipping:", "#{item.relative_path} is not valid UTF-8"
80
81
  false
81
82
  else
82
- publishable?(doc)
83
+ publishable?(item)
83
84
  end
84
85
  end
85
86
 
86
- def publishable?(doc)
87
- site.publisher.publish?(doc).tap do |will_publish|
88
- if !will_publish && site.publisher.hidden_in_the_future?(doc)
89
- Bridgetown.logger.warn "Skipping:", "#{doc.relative_path} has a future date"
87
+ def publishable?(item)
88
+ site.publisher.publish?(item).tap do |will_publish|
89
+ if !will_publish && site.publisher.hidden_in_the_future?(item)
90
+ Bridgetown.logger.warn "Skipping:", "#{item.relative_path} has a future date"
90
91
  end
91
92
  end
92
93
  end
94
+
95
+ def item_added_to_site?(item)
96
+ return false unless processable?(item)
97
+
98
+ if item.is_a?(Document)
99
+ site.posts.docs << item
100
+ elsif item.is_a?(StaticFile)
101
+ site.posts.files << item
102
+ site.static_files << item
103
+ end
104
+
105
+ true
106
+ end
93
107
  end
94
108
  end
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class StaticFileReader
5
5
  attr_reader :site, :dir, :unfiltered_content
6
+
6
7
  def initialize(site, dir)
7
8
  @site = site
8
9
  @dir = dir
@@ -3,37 +3,10 @@
3
3
  module Bridgetown
4
4
  class Renderer
5
5
  attr_reader :document, :site
6
- attr_writer :layouts, :payload
7
6
 
8
- class << self
9
- attr_accessor :cached_partials
10
- end
11
-
12
- def initialize(site, document, site_payload = nil)
7
+ def initialize(site, document)
13
8
  @site = site
14
9
  @document = document
15
- @payload = site_payload
16
- @layouts = nil
17
- self.class.cached_partials ||= {}
18
- end
19
-
20
- # Fetches the payload used in Liquid rendering.
21
- # It can be written with #payload=(new_payload)
22
- # Falls back to site.site_payload if no payload is set.
23
- #
24
- # Returns a Bridgetown::Drops::UnifiedPayloadDrop
25
- def payload
26
- @payload ||= site.site_payload
27
- end
28
-
29
- # The list of layouts registered for this Renderer.
30
- # It can be written with #layouts=(new_layouts)
31
- # Falls back to site.layouts if no layouts are registered.
32
- #
33
- # Returns a Hash of String => Bridgetown::Layout identified
34
- # as basename without the extension name.
35
- def layouts
36
- @layouts || site.layouts
37
10
  end
38
11
 
39
12
  # Determine which converters to use based on this document's
@@ -41,7 +14,13 @@ module Bridgetown
41
14
  #
42
15
  # Returns Array of Converter instances.
43
16
  def converters
44
- @converters ||= site.converters.select { |c| c.matches(document.extname) }.sort
17
+ @converters ||= site.converters.select do |converter|
18
+ if converter.method(:matches).arity == 1
19
+ converter.matches(document.extname)
20
+ else
21
+ converter.matches(document.extname, document)
22
+ end
23
+ end.sort
45
24
  end
46
25
 
47
26
  # Determine the extname the outputted file should have
@@ -51,93 +30,42 @@ module Bridgetown
51
30
  @output_ext ||= (permalink_ext || converter_output_ext)
52
31
  end
53
32
 
54
- # Prepare payload and render the document
33
+ # Run hooks and render the document
55
34
  #
56
35
  # Returns nothing
57
36
  def run
58
37
  Bridgetown.logger.debug "Rendering:", document.relative_path
59
38
 
60
- assign_pages!
61
- # TODO: this can be eliminated I think:
62
- assign_current_document!
63
- assign_highlighter_options!
64
- assign_layout_data!
65
-
66
- document.trigger_hooks(:pre_render, payload)
39
+ document.trigger_hooks :pre_render
67
40
  document.output = render_document
68
- document.trigger_hooks(:post_render)
41
+ document.trigger_hooks :post_render
69
42
  end
70
43
 
71
44
  # Render the document.
72
45
  #
73
46
  # Returns String rendered document output
74
- # rubocop: disable Metrics/AbcSize
75
47
  def render_document
76
- liquid_context = provide_liquid_context
77
-
78
48
  execute_inline_ruby!
79
49
 
80
50
  output = document.content
81
- if document.render_with_liquid?
82
- Bridgetown.logger.debug "Rendering Liquid:", document.relative_path
83
- output = render_liquid(output, payload, liquid_context, document.path)
84
- end
85
-
86
51
  Bridgetown.logger.debug "Rendering Markup:", document.relative_path
87
52
  output = convert(output.to_s, document)
88
53
  document.content = output
89
54
 
90
55
  if document.place_in_layout?
91
56
  Bridgetown.logger.debug "Rendering Layout:", document.relative_path
92
- output = place_in_layouts(output, payload, liquid_context)
57
+ output = place_in_layouts(output)
93
58
  end
94
59
 
95
60
  output
96
61
  end
97
62
 
98
- def provide_liquid_context
99
- {
100
- registers: {
101
- site: site,
102
- page: payload["page"],
103
- cached_partials: self.class.cached_partials,
104
- },
105
- strict_filters: liquid_options["strict_filters"],
106
- strict_variables: liquid_options["strict_variables"],
107
- }
108
- end
109
-
110
63
  def execute_inline_ruby!
111
64
  return unless site.config.should_execute_inline_ruby?
112
65
 
113
66
  Bridgetown::Utils::RubyExec.search_data_for_ruby_code(document, self)
114
67
  end
115
68
 
116
- # rubocop: enable Metrics/AbcSize
117
-
118
- # Render the given content with the payload and context
119
- #
120
- # content -
121
- # payload -
122
- # context -
123
- # path - (optional) the path to the file, for use in ex
124
- #
125
- # Returns String the content, rendered by Liquid.
126
- def render_liquid(content, payload, liquid_context, path = nil)
127
- template = site.liquid_renderer.file(path).parse(content)
128
- template.warnings.each do |e|
129
- Bridgetown.logger.warn "Liquid Warning:",
130
- LiquidRenderer.format_error(e, path || document.relative_path)
131
- end
132
- template.render!(payload, liquid_context)
133
- # rubocop: disable Lint/RescueException
134
- rescue Exception => e
135
- Bridgetown.logger.error "Liquid Exception:",
136
- LiquidRenderer.format_error(e, path || document.relative_path)
137
- raise e
138
- end
139
- # rubocop: enable Lint/RescueException
140
-
141
69
  # Convert the document using the converters which match this renderer's document.
142
70
  #
143
71
  # Returns String the converted content.
@@ -157,30 +85,18 @@ module Bridgetown
157
85
  end
158
86
  end
159
87
 
160
- # Checks if the layout specified in the document actually exists
161
- #
162
- # layout - the layout to check
163
- #
164
- # Returns Boolean true if the layout is invalid, false if otherwise
165
- def invalid_layout?(layout)
166
- !document.data["layout"].nil? && layout.nil? && !(document.is_a? Bridgetown::Excerpt)
167
- end
168
-
169
88
  # Render layouts and place document content inside.
170
89
  #
171
90
  # Returns String rendered content
172
- def place_in_layouts(content, payload, liquid_context)
91
+ def place_in_layouts(content)
173
92
  output = content.dup
174
- layout = layouts[document.data["layout"].to_s]
93
+ layout = site.layouts[document.data["layout"]]
175
94
  validate_layout(layout)
176
95
 
177
96
  used = Set.new([layout])
178
97
 
179
- # Reset the payload layout data to ensure it starts fresh for each page.
180
- payload["layout"] = nil
181
-
182
98
  while layout
183
- output = render_layout(output, layout, liquid_context)
99
+ output = render_layout(output, layout)
184
100
  add_regenerator_dependencies(layout)
185
101
 
186
102
  next unless (layout = site.layouts[layout.data["layout"]])
@@ -198,47 +114,45 @@ module Bridgetown
198
114
  # layout - the layout to check
199
115
  # Returns nothing
200
116
  def validate_layout(layout)
201
- return unless invalid_layout?(layout)
117
+ return unless document.data["layout"].present? &&
118
+ layout.nil? &&
119
+ !(document.is_a? Bridgetown::Excerpt)
202
120
 
203
121
  Bridgetown.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
204
122
  "in #{document.relative_path} does not exist."
205
123
  end
206
124
 
125
+ def converters_for_layout(layout)
126
+ site.converters.select do |converter|
127
+ if converter.method(:matches).arity == 1
128
+ converter.matches(layout.ext)
129
+ else
130
+ converter.matches(layout.ext, layout)
131
+ end
132
+ end.sort
133
+ end
134
+
207
135
  # Render layout content into document.output
208
136
  #
209
137
  # Returns String rendered content
210
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
211
- def render_layout(output, layout, liquid_context)
212
- if layout.render_with_liquid?
213
- payload["content"] = output
214
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
138
+ def render_layout(output, layout)
139
+ layout_converters = converters_for_layout(layout)
215
140
 
216
- render_liquid(
217
- layout.content,
218
- payload,
219
- liquid_context,
220
- layout.path
221
- )
222
- else
223
- layout_converters ||= site.converters.select { |c| c.matches(layout.ext) }.sort
224
-
225
- layout_content = layout.content.dup
226
- layout_converters.reduce(layout_content) do |layout_output, converter|
227
- next(layout_output) unless converter.method(:convert).arity == 2
141
+ layout_content = layout.content.dup
142
+ layout_converters.reduce(layout_content) do |layout_output, converter|
143
+ next(layout_output) unless converter.method(:convert).arity == 2
228
144
 
229
- layout.current_document = document
230
- layout.current_document_output = output
231
- converter.convert layout_output, layout
232
- rescue StandardError => e
233
- Bridgetown.logger.error "Conversion error:",
234
- "#{converter.class} encountered an error while "\
235
- "converting '#{document.relative_path}':"
236
- Bridgetown.logger.error("", e.to_s)
237
- raise e
238
- end
145
+ layout.current_document = document
146
+ layout.current_document_output = output
147
+ converter.convert layout_output, layout
239
148
  end
149
+ rescue StandardError => e
150
+ Bridgetown.logger.error "Conversion error:",
151
+ "#{converter.class} encountered an error while "\
152
+ "converting '#{document.relative_path}':"
153
+ Bridgetown.logger.error("", e.to_s)
154
+ raise e
240
155
  end
241
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
242
156
 
243
157
  def add_regenerator_dependencies(layout)
244
158
  return unless document.write?
@@ -249,34 +163,6 @@ module Bridgetown
249
163
  )
250
164
  end
251
165
 
252
- # Set page content to payload and assign pager if document has one.
253
- #
254
- # Returns nothing
255
- def assign_pages!
256
- payload["page"] = document.to_liquid
257
- payload["paginator"] = (document.pager.to_liquid if document.respond_to?(:pager))
258
- end
259
-
260
- # Set related posts to payload if document is a post.
261
- #
262
- # Returns nothing
263
- def assign_current_document!
264
- payload["site"].current_document = document
265
- end
266
-
267
- # Set highlighter prefix and suffix
268
- #
269
- # Returns nothing
270
- def assign_highlighter_options!
271
- payload["highlighter_prefix"] = converters.first.highlighter_prefix
272
- payload["highlighter_suffix"] = converters.first.highlighter_suffix
273
- end
274
-
275
- def assign_layout_data!
276
- layout = layouts[document.data["layout"]]
277
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {}) if layout
278
- end
279
-
280
166
  def permalink_ext
281
167
  document_permalink = document.permalink
282
168
  if document_permalink && !document_permalink.end_with?("/")
@@ -298,9 +184,5 @@ module Bridgetown
298
184
  c.output_ext(document.extname)
299
185
  end.compact
300
186
  end
301
-
302
- def liquid_options
303
- @liquid_options ||= site.config["liquid"]
304
- end
305
187
  end
306
188
  end
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest"
4
+ require "active_support/core_ext/hash/keys"
4
5
 
5
6
  module Bridgetown
6
7
  class RubyTemplateView
7
- class Helpers
8
- include Bridgetown::Filters
9
- end
8
+ require "bridgetown-core/helpers"
10
9
 
11
- attr_reader :layout, :page, :site, :content
10
+ attr_reader :layout, :page, :paginator, :site, :content
12
11
 
13
12
  def initialize(convertible)
14
13
  if convertible.is_a?(Layout)
@@ -16,8 +15,10 @@ module Bridgetown
16
15
  @page = layout.current_document
17
16
  @content = layout.current_document_output
18
17
  else
18
+ @layout = convertible.site.layouts[convertible.data["layout"]]
19
19
  @page = convertible
20
20
  end
21
+ @paginator = page.paginator if page.respond_to?(:paginator)
21
22
  @site = page.site
22
23
  end
23
24
 
@@ -25,11 +26,20 @@ module Bridgetown
25
26
  raise "Must be implemented in a subclass"
26
27
  end
27
28
 
29
+ def render(item, options = {}, &block)
30
+ if item.respond_to?(:render_in)
31
+ item.render_in(self, &block)
32
+ else
33
+ partial(item, options, &block)
34
+ end
35
+ end
36
+
28
37
  def site_drop
29
38
  site.site_payload.site
30
39
  end
31
40
 
32
41
  def liquid_render(component, options = {})
42
+ options[:_block_content] = yield if block_given?
33
43
  render_statement = _render_statement(component, options)
34
44
 
35
45
  template = site.liquid_renderer.file(
@@ -43,7 +53,7 @@ module Bridgetown
43
53
  end
44
54
 
45
55
  def helpers
46
- @helpers ||= Helpers.new
56
+ @helpers ||= Helpers.new(self, site)
47
57
  end
48
58
 
49
59
  def method_missing(method, *args, &block)
@@ -61,11 +71,19 @@ module Bridgetown
61
71
  private
62
72
 
63
73
  def _render_statement(component, options)
64
- render_statement = ["{% render \"#{component}\""]
74
+ render_statement = if options[:_block_content]
75
+ ["{% rendercontent \"#{component}\""]
76
+ else
77
+ ["{% render \"#{component}\""]
78
+ end
65
79
  unless options.empty?
66
80
  render_statement << ", " + options.keys.map { |k| "#{k}: #{k}" }.join(", ")
67
81
  end
68
82
  render_statement << " %}"
83
+ if options[:_block_content]
84
+ render_statement << options[:_block_content]
85
+ render_statement << "{% endrendercontent %}"
86
+ end
69
87
  render_statement.join
70
88
  end
71
89
 
@@ -73,8 +91,8 @@ module Bridgetown
73
91
  {
74
92
  registers: {
75
93
  site: site,
76
- page: page,
77
- cached_partials: Bridgetown::Renderer.cached_partials,
94
+ page: page.to_liquid,
95
+ cached_partials: Bridgetown::Converters::LiquidTemplates.cached_partials,
78
96
  },
79
97
  strict_filters: site.config["liquid"]["strict_filters"],
80
98
  strict_variables: site.config["liquid"]["strict_variables"],