bridgetown-core 0.15.0 → 0.16.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +14 -0
  3. data/bridgetown-core.gemspec +1 -0
  4. data/lib/bridgetown-core.rb +6 -1
  5. data/lib/bridgetown-core/concerns/data_accessible.rb +19 -0
  6. data/lib/bridgetown-core/concerns/layout_placeable.rb +17 -0
  7. data/lib/bridgetown-core/concerns/liquid_renderable.rb +20 -0
  8. data/lib/bridgetown-core/concerns/publishable.rb +10 -0
  9. data/lib/bridgetown-core/concerns/site/configurable.rb +62 -31
  10. data/lib/bridgetown-core/concerns/site/content.rb +88 -29
  11. data/lib/bridgetown-core/concerns/site/extensible.rb +15 -12
  12. data/lib/bridgetown-core/concerns/site/processable.rb +12 -10
  13. data/lib/bridgetown-core/concerns/site/renderable.rb +22 -2
  14. data/lib/bridgetown-core/concerns/site/writable.rb +16 -2
  15. data/lib/bridgetown-core/concerns/validatable.rb +59 -0
  16. data/lib/bridgetown-core/configuration.rb +1 -0
  17. data/lib/bridgetown-core/converter.rb +34 -0
  18. data/lib/bridgetown-core/converters/erb_templates.rb +61 -0
  19. data/lib/bridgetown-core/converters/markdown.rb +6 -23
  20. data/lib/bridgetown-core/converters/smartypants.rb +0 -10
  21. data/lib/bridgetown-core/document.rb +8 -52
  22. data/lib/bridgetown-core/errors.rb +2 -0
  23. data/lib/bridgetown-core/excerpt.rb +1 -6
  24. data/lib/bridgetown-core/filters.rb +2 -0
  25. data/lib/bridgetown-core/layout.rb +24 -1
  26. data/lib/bridgetown-core/liquid_renderer/file_system.rb +1 -1
  27. data/lib/bridgetown-core/page.rb +33 -24
  28. data/lib/bridgetown-core/regenerator.rb +1 -1
  29. data/lib/bridgetown-core/renderer.rb +38 -12
  30. data/lib/bridgetown-core/ruby_template_view.rb +84 -0
  31. data/lib/bridgetown-core/tags/class_map.rb +90 -0
  32. data/lib/bridgetown-core/tags/webpack_path.rb +48 -16
  33. data/lib/bridgetown-core/version.rb +2 -2
  34. metadata +24 -3
  35. data/lib/bridgetown-core/concerns/convertible.rb +0 -235
@@ -16,5 +16,7 @@ module Bridgetown
16
16
  PostURLError = Class.new(FatalException)
17
17
  InvalidURLError = Class.new(FatalException)
18
18
  InvalidConfigurationError = Class.new(FatalException)
19
+
20
+ WebpackAssetError = Class.new(FatalException)
19
21
  end
20
22
  end
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class Excerpt
5
5
  extend Forwardable
6
+ include LiquidRenderable
6
7
 
7
8
  attr_accessor :doc
8
9
  attr_accessor :content, :ext
@@ -91,12 +92,6 @@ module Bridgetown
91
92
  false
92
93
  end
93
94
 
94
- def render_with_liquid?
95
- return false if data["render_with_liquid"] == false
96
-
97
- !(yaml_file? || !Utils.has_liquid_construct?(content))
98
- end
99
-
100
95
  protected
101
96
 
102
97
  # Internal: Extract excerpt from the content
@@ -30,6 +30,7 @@ module Bridgetown
30
30
  ).convert(input.to_s)
31
31
  end
32
32
 
33
+ # TODO: This should be removed, there is no Sass converter
33
34
  # Convert a Sass string into CSS output.
34
35
  #
35
36
  # input - The Sass String to convert.
@@ -41,6 +42,7 @@ module Bridgetown
41
42
  ).convert(input)
42
43
  end
43
44
 
45
+ # TODO: This should be removed, there is no Scss converter
44
46
  # Convert a Scss string into CSS output.
45
47
  #
46
48
  # input - The Scss String to convert.
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Layout
5
- include Convertible
5
+ include DataAccessible
6
+ include LiquidRenderable
7
+ include Validatable
6
8
 
7
9
  # Gets the Site object.
8
10
  attr_reader :site
@@ -25,6 +27,12 @@ module Bridgetown
25
27
  # Gets/Sets the content of this layout.
26
28
  attr_accessor :content
27
29
 
30
+ # Gets/Sets the current document (for layout-compatible converters)
31
+ attr_accessor :current_document
32
+
33
+ # Gets/Sets the document output (for layout-compatible converters)
34
+ attr_accessor :current_document_output
35
+
28
36
  # Initialize a new Layout.
29
37
  #
30
38
  # site - The Site.
@@ -51,6 +59,14 @@ module Bridgetown
51
59
  read_yaml(base, name)
52
60
  end
53
61
 
62
+ # The inspect string for this document.
63
+ # Includes the relative path and the collection label.
64
+ #
65
+ # Returns the inspect string for this document.
66
+ def inspect
67
+ "#<#{self.class} #{@path}>"
68
+ end
69
+
54
70
  # Extract information from the layout filename.
55
71
  #
56
72
  # name - The String filename of the layout file.
@@ -59,5 +75,12 @@ module Bridgetown
59
75
  def process(name)
60
76
  self.ext = File.extname(name)
61
77
  end
78
+
79
+ # Provide this Layout's data to a Hash suitable for use by Liquid.
80
+ #
81
+ # Returns the Hash representation of this Layout.
82
+ def to_liquid
83
+ data
84
+ end
62
85
  end
63
86
  end
@@ -32,7 +32,7 @@ module Bridgetown
32
32
 
33
33
  # Last path in the list wins
34
34
  LiquidComponent.parse(
35
- ::File.read(found_paths.last, site.file_read_opts)
35
+ ::File.read(found_paths.last, **site.file_read_opts)
36
36
  ).content
37
37
  end
38
38
  end
@@ -2,7 +2,11 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Page
5
- include Convertible
5
+ include DataAccessible
6
+ include LayoutPlaceable
7
+ include LiquidRenderable
8
+ include Publishable
9
+ include Validatable
6
10
 
7
11
  attr_writer :dir
8
12
  attr_accessor :site, :pager
@@ -11,15 +15,6 @@ module Bridgetown
11
15
 
12
16
  alias_method :extname, :ext
13
17
 
14
- # Attributes for Liquid templates
15
- ATTRIBUTES_FOR_LIQUID = %w(
16
- content
17
- dir
18
- name
19
- path
20
- url
21
- ).freeze
22
-
23
18
  # A set of extensions that are considered HTML or HTML-like so we
24
19
  # should not alter them, this includes .xhtml through XHTM5.
25
20
 
@@ -143,19 +138,6 @@ module Bridgetown
143
138
  self.basename = name[0..-ext.length - 1].gsub(%r!\.*\z!, "")
144
139
  end
145
140
 
146
- # Add any necessary layouts to this post
147
- #
148
- # layouts - The Hash of {"name" => "layout"}.
149
- # site_payload - The site payload Hash.
150
- #
151
- # Returns String rendered page.
152
- def render(layouts, site_payload)
153
- site_payload["page"] = to_liquid
154
- site_payload["paginator"] = pager.to_liquid
155
-
156
- do_layout(site_payload, layouts)
157
- end
158
-
159
141
  # The path to the source file
160
142
  #
161
143
  # Returns the path to the source file
@@ -168,6 +150,16 @@ module Bridgetown
168
150
  @relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).delete_prefix("/")
169
151
  end
170
152
 
153
+ # FIXME: spinning up a new Renderer object just to get an extension
154
+ # seems excessive
155
+ #
156
+ # The output extension of the page.
157
+ #
158
+ # Returns the output extension
159
+ def output_ext
160
+ @output_ext ||= Bridgetown::Renderer.new(site, self).output_ext
161
+ end
162
+
171
163
  # Obtain destination path.
172
164
  #
173
165
  # dest - The String path to the destination dir.
@@ -180,9 +172,22 @@ module Bridgetown
180
172
  path
181
173
  end
182
174
 
175
+ # Write the generated page file to the destination directory.
176
+ #
177
+ # dest - The String path to the destination dir.
178
+ #
179
+ # Returns nothing.
180
+ def write(dest)
181
+ path = destination(dest)
182
+ FileUtils.mkdir_p(File.dirname(path))
183
+ Bridgetown.logger.debug "Writing:", path
184
+ File.write(path, output, mode: "wb")
185
+ Bridgetown::Hooks.trigger :pages, :post_write, self
186
+ end
187
+
183
188
  # Returns the object as a debug String.
184
189
  def inspect
185
- "#<#{self.class} @relative_path=#{relative_path.inspect}>"
190
+ "#<#{self.class} #{relative_path}>"
186
191
  end
187
192
 
188
193
  # Returns the Boolean of whether this Page is HTML or not.
@@ -199,6 +204,10 @@ module Bridgetown
199
204
  Bridgetown::Hooks.trigger :pages, hook_name, self, *args
200
205
  end
201
206
 
207
+ def type
208
+ :pages
209
+ end
210
+
202
211
  def write?
203
212
  true
204
213
  end
@@ -163,7 +163,7 @@ module Bridgetown
163
163
  end
164
164
 
165
165
  def regenerate_page?(document)
166
- document.asset_file? || document.data["regenerate"] ||
166
+ document.data["regenerate"] ||
167
167
  source_modified_or_dest_missing?(
168
168
  site.in_source_dir(document.relative_path), document.destination(@site.dest)
169
169
  )
@@ -58,6 +58,7 @@ module Bridgetown
58
58
  Bridgetown.logger.debug "Rendering:", document.relative_path
59
59
 
60
60
  assign_pages!
61
+ # TODO: this can be eliminated I think:
61
62
  assign_current_document!
62
63
  assign_highlighter_options!
63
64
  assign_layout_data!
@@ -83,7 +84,7 @@ module Bridgetown
83
84
  end
84
85
 
85
86
  Bridgetown.logger.debug "Rendering Markup:", document.relative_path
86
- output = convert(output.to_s)
87
+ output = convert(output.to_s, document)
87
88
  document.content = output
88
89
 
89
90
  if document.place_in_layout?
@@ -140,9 +141,13 @@ module Bridgetown
140
141
  # Convert the document using the converters which match this renderer's document.
141
142
  #
142
143
  # Returns String the converted content.
143
- def convert(content)
144
+ def convert(content, document)
144
145
  converters.reduce(content) do |output, converter|
145
- converter.convert output
146
+ if converter.method(:convert).arity == 1
147
+ converter.convert output
148
+ else
149
+ converter.convert output, document
150
+ end
146
151
  rescue StandardError => e
147
152
  Bridgetown.logger.error "Conversion error:",
148
153
  "#{converter.class} encountered an error while "\
@@ -202,17 +207,38 @@ module Bridgetown
202
207
  # Render layout content into document.output
203
208
  #
204
209
  # Returns String rendered content
210
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
205
211
  def render_layout(output, layout, liquid_context)
206
- payload["content"] = output
207
- payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
208
-
209
- render_liquid(
210
- layout.content,
211
- payload,
212
- liquid_context,
213
- layout.path
214
- )
212
+ if layout.render_with_liquid?
213
+ payload["content"] = output
214
+ payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
215
+
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
228
+
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
239
+ end
215
240
  end
241
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
216
242
 
217
243
  def add_regenerator_dependencies(layout)
218
244
  return unless document.write?
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Bridgetown
6
+ class RubyTemplateView
7
+ class Helpers
8
+ include Bridgetown::Filters
9
+ end
10
+
11
+ attr_reader :layout, :page, :site, :content
12
+
13
+ def initialize(convertible)
14
+ if convertible.is_a?(Layout)
15
+ @layout = convertible
16
+ @page = layout.current_document
17
+ @content = layout.current_document_output
18
+ else
19
+ @page = convertible
20
+ end
21
+ @site = page.site
22
+ end
23
+
24
+ def partial(_partial_name, _options = {})
25
+ raise "Must be implemented in a subclass"
26
+ end
27
+
28
+ def site_drop
29
+ site.site_payload.site
30
+ end
31
+
32
+ def liquid_render(component, options = {})
33
+ render_statement = _render_statement(component, options)
34
+
35
+ template = site.liquid_renderer.file(
36
+ "#{page.path}.#{Digest::SHA2.hexdigest(render_statement)}"
37
+ ).parse(render_statement)
38
+ template.warnings.each do |e|
39
+ Bridgetown.logger.warn "Liquid Warning:",
40
+ LiquidRenderer.format_error(e, path || document.relative_path)
41
+ end
42
+ template.render!(options.deep_stringify_keys, _liquid_context)
43
+ end
44
+
45
+ def helpers
46
+ @helpers ||= Helpers.new
47
+ end
48
+
49
+ def method_missing(method, *args, &block)
50
+ if helpers.respond_to?(method.to_sym)
51
+ helpers.send method.to_sym, *args, &block
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ def respond_to_missing?(method, include_private = false)
58
+ helpers.respond_to?(method.to_sym, include_private) || super
59
+ end
60
+
61
+ private
62
+
63
+ def _render_statement(component, options)
64
+ render_statement = ["{% render \"#{component}\""]
65
+ unless options.empty?
66
+ render_statement << ", " + options.keys.map { |k| "#{k}: #{k}" }.join(", ")
67
+ end
68
+ render_statement << " %}"
69
+ render_statement.join
70
+ end
71
+
72
+ def _liquid_context
73
+ {
74
+ registers: {
75
+ site: site,
76
+ page: page,
77
+ cached_partials: Bridgetown::Renderer.cached_partials,
78
+ },
79
+ strict_filters: site.config["liquid"]["strict_filters"],
80
+ strict_variables: site.config["liquid"]["strict_variables"],
81
+ }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Bridgetown
6
+ module Tags
7
+ # A ClassMap class is meant to take a hash and append styles based on if the
8
+ # value is truthy or falsy
9
+ #
10
+ # @example
11
+ # center-var = true
12
+ # small-var = nil
13
+ #
14
+ # # input
15
+ # <div class="{% class_map has-centered-text: center-var, is-small: small-var %}">
16
+ # Text
17
+ # </div>
18
+ #
19
+ # # output
20
+ # <div class="has-centered-text">
21
+ # Text
22
+ # </div>
23
+ class ClassMap < Liquid::Tag
24
+ # @see https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
25
+ FALSE_VALUES = [
26
+ nil, "nil", "NIL", false, 0, "0", :"0", "f", :f, "F", :F, "false",
27
+ false, "FALSE", :FALSE,
28
+ ].to_set.freeze
29
+
30
+ # @param tag_name [String] The name to use for the tag
31
+ # @param input [String] The input to the tag
32
+ # @param tokens [Hash] A hash of config tokens for Liquid.
33
+ #
34
+ #
35
+ # @return [ClassMap] Returns a ClassMap object
36
+ def initialize(tag_name, input, tokens)
37
+ super
38
+ @input = input
39
+ end
40
+
41
+ def render(context)
42
+ class_map(@input, context)
43
+ end
44
+
45
+ private
46
+
47
+ def class_map(string, context)
48
+ ary = []
49
+
50
+ string.split(%r!,\s+!).each do |item|
51
+ kv_pair = item.split(%r!:\s+!)
52
+ klass = kv_pair[0]
53
+ variable = kv_pair[1]
54
+
55
+ # Check if a user wants the opposite of the variable
56
+ if variable[0] == "!"
57
+ check_opposite = true
58
+ variable.slice!(1..-1)
59
+ end
60
+
61
+ variable = find_variable(context, variable)
62
+
63
+ if check_opposite
64
+ ary.push(klass) if FALSE_VALUES.include?(variable)
65
+ else
66
+ ary.push(klass) unless FALSE_VALUES.include?(variable)
67
+ end
68
+ end
69
+
70
+ ary.join(" ")
71
+
72
+ # Gracefully handle if syntax is improper
73
+ rescue NoMethodError
74
+ "invalid-class-map"
75
+ end
76
+
77
+ def find_variable(context, variable)
78
+ lookup = context
79
+
80
+ variable.split(".").each do |value|
81
+ lookup = lookup[value.strip]
82
+ end
83
+
84
+ lookup || nil
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ Liquid::Template.register_tag("class_map", Bridgetown::Tags::ClassMap)