bridgetown-core 0.20.0 → 0.21.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bridgetown-core.rb +3 -0
  3. data/lib/bridgetown-core/collection.rb +13 -10
  4. data/lib/bridgetown-core/component.rb +178 -0
  5. data/lib/bridgetown-core/concerns/front_matter_importer.rb +52 -0
  6. data/lib/bridgetown-core/concerns/site/content.rb +2 -3
  7. data/lib/bridgetown-core/concerns/site/writable.rb +1 -1
  8. data/lib/bridgetown-core/concerns/validatable.rb +0 -4
  9. data/lib/bridgetown-core/configuration.rb +10 -9
  10. data/lib/bridgetown-core/converter.rb +9 -0
  11. data/lib/bridgetown-core/converters/erb_templates.rb +50 -34
  12. data/lib/bridgetown-core/converters/markdown.rb +1 -1
  13. data/lib/bridgetown-core/converters/ruby_templates.rb +17 -0
  14. data/lib/bridgetown-core/drops/relations_drop.rb +23 -0
  15. data/lib/bridgetown-core/drops/resource_drop.rb +3 -1
  16. data/lib/bridgetown-core/drops/unified_payload_drop.rb +1 -0
  17. data/lib/bridgetown-core/filters/from_liquid.rb +23 -0
  18. data/lib/bridgetown-core/helpers.rb +48 -9
  19. data/lib/bridgetown-core/layout.rb +27 -12
  20. data/lib/bridgetown-core/model/origin.rb +1 -1
  21. data/lib/bridgetown-core/model/{file_origin.rb → repo_origin.rb} +32 -25
  22. data/lib/bridgetown-core/reader.rb +2 -2
  23. data/lib/bridgetown-core/renderer.rb +1 -1
  24. data/lib/bridgetown-core/resource/base.rb +69 -27
  25. data/lib/bridgetown-core/resource/relations.rb +132 -0
  26. data/lib/bridgetown-core/resource/taxonomy_term.rb +10 -1
  27. data/lib/bridgetown-core/resource/taxonomy_type.rb +9 -0
  28. data/lib/bridgetown-core/resource/transformer.rb +14 -12
  29. data/lib/bridgetown-core/ruby_template_view.rb +7 -11
  30. data/lib/bridgetown-core/utils.rb +8 -1
  31. data/lib/bridgetown-core/utils/ruby_exec.rb +6 -9
  32. data/lib/bridgetown-core/utils/ruby_front_matter.rb +39 -0
  33. data/lib/bridgetown-core/version.rb +2 -2
  34. data/lib/bridgetown-core/watcher.rb +1 -1
  35. data/lib/site_template/package.json.erb +2 -2
  36. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +1 -1
  37. data/lib/site_template/webpack.config.js.erb +3 -1
  38. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c58653595832ef2fb9e6bbea3f147a0b3be9edae554d169e974570b84565a507
4
- data.tar.gz: 7af409e9a01570321171cf1afc562c00cacdcc6e0dce98f2b8eac4988a60e8d6
3
+ metadata.gz: 02c8827143ec0abe2adfa081ef1fe7723d8d000c4ecec5c6da1ddaa2ef9a8cdf
4
+ data.tar.gz: 2c81ef34075b80ce956f43b57b01b400852d4a30e3eca8587785febc5c82a850
5
5
  SHA512:
6
- metadata.gz: b21d9c304dd99dd306b70e97e172d1ec172f0d1fa48034610cc3d9e6bc9c40b98e7fe10479226103cb2ff501c22c7c2841f20630235262cc7214661be62bcf3e
7
- data.tar.gz: cab7afb0f441e412850ce648d6e7ca9a3996f106b0800c31ef24a0cf63af9db770498f307da275860c0572d6d6f2a5f1e56570d10349bb11cd4f17f96b3266ee
6
+ metadata.gz: f86157dcd3f2fb1139e490fcc1341e2508f4811bda806171e2374e4753cf14c6c121abbb2ceeb6536a925bfd1d1ecba44c6d35fb6ebd6c0bcad3b4ace8d73912
7
+ data.tar.gz: 9262363b23785be1ad111a611d7660201dd3e948e331fdcf5395afd8fde91f1cb075ba13d1729fcd537cd15e3d209c8016145af04c13f054f87bb37d46406617
@@ -38,6 +38,7 @@ require "active_support/core_ext/object/deep_dup"
38
38
  require "active_support/core_ext/object/inclusion"
39
39
  require "active_support/core_ext/string/inflections"
40
40
  require "active_support/core_ext/string/inquiry"
41
+ require "active_support/core_ext/string/output_safety"
41
42
  require "active_support/core_ext/string/starts_ends_with"
42
43
  require "active_support/current_attributes"
43
44
  require "active_support/descendants_tracker"
@@ -80,6 +81,7 @@ end
80
81
  module Bridgetown
81
82
  autoload :Cleaner, "bridgetown-core/cleaner"
82
83
  autoload :Collection, "bridgetown-core/collection"
84
+ autoload :Component, "bridgetown-core/component"
83
85
  autoload :Configuration, "bridgetown-core/configuration"
84
86
  autoload :DataAccessible, "bridgetown-core/concerns/data_accessible"
85
87
  autoload :Deprecator, "bridgetown-core/deprecator"
@@ -91,6 +93,7 @@ module Bridgetown
91
93
  # TODO: this is a poorly named, unclear class. Relocate to Utils:
92
94
  autoload :External, "bridgetown-core/external"
93
95
  autoload :FrontmatterDefaults, "bridgetown-core/frontmatter_defaults"
96
+ autoload :FrontMatterImporter, "bridgetown-core/concerns/front_matter_importer"
94
97
  autoload :Hooks, "bridgetown-core/hooks"
95
98
  autoload :Layout, "bridgetown-core/layout"
96
99
  autoload :LayoutPlaceable, "bridgetown-core/concerns/layout_placeable"
@@ -78,7 +78,8 @@ module Bridgetown
78
78
  if site.uses_resource?
79
79
  next if File.basename(file_path).starts_with?("_")
80
80
 
81
- if label == "data" || Utils.has_yaml_header?(full_path)
81
+ if label == "data" || Utils.has_yaml_header?(full_path) ||
82
+ Utils.has_rbfm_header?(full_path)
82
83
  read_resource(full_path)
83
84
  else
84
85
  read_static_file(file_path, full_path)
@@ -256,7 +257,7 @@ module Bridgetown
256
257
  sanitized_segment = sanitize_filename.(File.basename(segment, ".*"))
257
258
  hsh = nested.empty? ? data_contents : data_contents.dig(*nested)
258
259
  hsh[sanitized_segment] = if index == segments.length - 1
259
- data_resource.data.array || data_resource.data
260
+ data_resource.data.rows || data_resource.data
260
261
  else
261
262
  {}
262
263
  end
@@ -279,6 +280,16 @@ module Bridgetown
279
280
  data_contents
280
281
  end
281
282
 
283
+ # Read in resource from repo path
284
+ # @param full_path [String]
285
+ def read_resource(full_path)
286
+ id = "repo://#{label}.collection/" + Addressable::URI.escape(
287
+ Pathname(full_path).relative_path_from(Pathname(site.source)).to_s
288
+ )
289
+ resource = Bridgetown::Model::Base.find(id).to_resource.read!
290
+ resources << resource if site.unpublished || resource.published?
291
+ end
292
+
282
293
  private
283
294
 
284
295
  def container
@@ -290,14 +301,6 @@ module Bridgetown
290
301
  docs << doc if site.unpublished || doc.published?
291
302
  end
292
303
 
293
- def read_resource(full_path)
294
- id = "file://#{label}.collection/" + Addressable::URI.escape(
295
- Pathname(full_path).relative_path_from(Pathname(site.source)).to_s
296
- )
297
- resource = Bridgetown::Model::Base.find(id).to_resource.read!
298
- resources << resource if site.unpublished || resource.published?
299
- end
300
-
301
304
  def sort_docs!
302
305
  if metadata["sort_by"].is_a?(String)
303
306
  sort_docs_by_key!
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class Component
5
+ extend Forwardable
6
+
7
+ def_delegators :@view_context, :helpers, :liquid_render, :partial
8
+
9
+ # @return [Bridgetown::Site]
10
+ attr_reader :site # will be nil unless you explicitly set a `@site` ivar
11
+
12
+ # @return [Bridgetown::RubyTemplateView, Bridgetown::Component]
13
+ attr_reader :view_context
14
+
15
+ class << self
16
+ attr_accessor :source_location
17
+
18
+ def inherited(child)
19
+ # Code cribbed from ViewComponent by GitHub:
20
+ # Derive the source location of the component Ruby file from the call stack
21
+ child.source_location = caller_locations(1, 10).reject do |l|
22
+ l.label == "inherited"
23
+ end[0].absolute_path
24
+
25
+ super
26
+ end
27
+
28
+ # Return the appropriate template renderer for a given extension.
29
+ # TODO: make this extensible
30
+ #
31
+ # @param ext [String] erb, slim, etc.
32
+ def renderer_for_ext(ext, &block)
33
+ case ext
34
+ when "erb"
35
+ include ERBCapture
36
+ Tilt::ErubiTemplate.new(component_template_path,
37
+ outvar: "@_erbout",
38
+ bufval: "Bridgetown::OutputBuffer.new",
39
+ engine_class: Bridgetown::ERBEngine,
40
+ &block)
41
+ when "serb" # requires serbea
42
+ include Serbea::Helpers
43
+ Tilt::SerbeaTemplate.new(component_template_path, &block)
44
+ when "slim" # requires bridgetown-slim
45
+ Slim::Template.new(component_template_path, &block)
46
+ when "haml" # requires bridgetown-haml
47
+ Tilt::HamlTemplate.new(component_template_path, &block)
48
+ else
49
+ raise NameError
50
+ end
51
+ rescue NameError, LoadError
52
+ raise "No component rendering engine could be found for .#{ext} templates"
53
+ end
54
+
55
+ # Find the first matching template path based on source location and extension.
56
+ #
57
+ # @return [String]
58
+ def component_template_path
59
+ stripped_path = File.join(
60
+ File.dirname(source_location),
61
+ File.basename(source_location, ".*")
62
+ )
63
+ supported_template_extensions.each do |ext|
64
+ test_path = "#{stripped_path}.#{ext}"
65
+ return test_path if File.exist?(test_path)
66
+
67
+ test_path = "#{stripped_path}.html.#{ext}"
68
+ return test_path if File.exist?(test_path)
69
+ end
70
+
71
+ raise "No matching templates could be found in #{File.dirname(source_location)}"
72
+ end
73
+
74
+ # Read the template file.
75
+ #
76
+ # @return [String]
77
+ def component_template_content
78
+ File.read(component_template_path)
79
+ end
80
+
81
+ # A list of extensions supported by the renderer
82
+ # TODO: make this extensible
83
+ #
84
+ # @return [Array<String>]
85
+ def supported_template_extensions
86
+ %w(erb serb slim haml)
87
+ end
88
+ end
89
+
90
+ # If a content block was originally passed into via `render`, capture its output.
91
+ #
92
+ # @return [String] or nil
93
+ def content
94
+ @_content ||= begin
95
+ view_context.capture(self, &@_content_block) if @_content_block
96
+ end
97
+ end
98
+
99
+ # Provide a render helper for evaluation within the component context.
100
+ #
101
+ # @param item [Object] a component supporting `render_in` or a partial name
102
+ # @param options [Hash] passed to the `partial` helper if needed
103
+ # @return [String]
104
+ def render(item, options = {}, &block)
105
+ if item.respond_to?(:render_in)
106
+ result = ""
107
+ capture do # this ensures no leaky interactions between BT<=>VC blocks
108
+ result = item.render_in(self, &block)
109
+ end
110
+ result&.html_safe
111
+ else
112
+ partial(item, options, &block)&.html_safe
113
+ end
114
+ end
115
+
116
+ # This is where the magic happens. Render the component within a view context.
117
+ #
118
+ # @param view_context [Bridgetown::RubyTemplateView]
119
+ def render_in(view_context, &block)
120
+ @view_context = view_context
121
+ @_content_block = block
122
+
123
+ if render?
124
+ before_render
125
+ template
126
+ else
127
+ ""
128
+ end
129
+ rescue StandardError => e
130
+ Bridgetown.logger.error "Component error:",
131
+ "#{self.class} encountered an error while "\
132
+ "rendering `#{self.class.component_template_path}'"
133
+ raise e
134
+ end
135
+
136
+ # Subclasses can override this method to return a string from their own
137
+ # template handling.
138
+ def template
139
+ call || _renderer.render(self)
140
+ end
141
+
142
+ # Typically not used but here as a compatibility nod toward ViewComponent.
143
+ def call
144
+ nil
145
+ end
146
+
147
+ # Subclasses can override this method to perform tasks before a render.
148
+ def before_render; end
149
+
150
+ # Subclasses can override this method to determine if the component should
151
+ # be rendered based on initialized data or other logic.
152
+ def render?
153
+ true
154
+ end
155
+
156
+ def _renderer
157
+ # TODO: figure out a way to compile templates for increased performance
158
+ @_renderer ||= begin
159
+ ext = File.extname(self.class.component_template_path).delete_prefix(".")
160
+ self.class.renderer_for_ext(ext) { self.class.component_template_content }
161
+ end
162
+ end
163
+
164
+ # rubocop:disable Style/MissingRespondToMissing
165
+ ruby2_keywords def method_missing(method, *args, &block)
166
+ if helpers.respond_to?(method.to_sym)
167
+ helpers.send method.to_sym, *args, &block
168
+ else
169
+ super
170
+ end
171
+ end
172
+
173
+ def respond_to_missing?(method, include_private = false)
174
+ helpers.respond_to?(method.to_sym, include_private) || super
175
+ end
176
+ # rubocop:enable Style/MissingRespondToMissing
177
+ end
178
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatterImporter
5
+ # Requires klass#content and klass#front_matter_line_count accessors
6
+ def self.included(klass)
7
+ klass.include Bridgetown::Utils::RubyFrontMatterDSL
8
+ end
9
+
10
+ YAML_HEADER = %r!\A---\s*\n!.freeze
11
+ YAML_BLOCK = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
12
+ RUBY_HEADER = %r!\A[~`#\-]{3,}(?:ruby|<%|{%)\s*\n!.freeze
13
+ RUBY_BLOCK =
14
+ %r!#{RUBY_HEADER.source}(.*?\n?)^((?:%>|%})?[~`#\-]{3,}\s*$\n?)!m.freeze
15
+
16
+ def read_front_matter(file_path) # rubocop:todo Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
17
+ file_contents = File.read(
18
+ file_path, **Bridgetown::Utils.merged_file_read_opts(Bridgetown::Current.site, {})
19
+ )
20
+ yaml_content = file_contents.match(YAML_BLOCK)
21
+ if !yaml_content && Bridgetown::Current.site.config.should_execute_inline_ruby?
22
+ ruby_content = file_contents.match(RUBY_BLOCK)
23
+ end
24
+
25
+ if yaml_content
26
+ self.content = yaml_content.post_match
27
+ self.front_matter_line_count = yaml_content[1].lines.size - 1
28
+ SafeYAML.load(yaml_content[1])
29
+ elsif ruby_content
30
+ # rbfm header + content underneath
31
+ self.content = ruby_content.post_match
32
+ self.front_matter_line_count = ruby_content[1].lines.size
33
+ process_ruby_data(ruby_content[1], file_path, 2)
34
+ elsif Bridgetown::Utils.has_rbfm_header?(file_path)
35
+ process_ruby_data(File.read(file_path).lines[1..-1].join("\n"), file_path, 2)
36
+ elsif is_a?(Layout)
37
+ self.content = file_contents
38
+ {}
39
+ else
40
+ yaml_data = SafeYAML.load_file(file_path)
41
+ yaml_data.is_a?(Array) ? { rows: yaml_data } : yaml_data
42
+ end
43
+ end
44
+
45
+ def process_ruby_data(rubycode, file_path, starting_line)
46
+ ruby_data = instance_eval(rubycode, file_path.to_s, starting_line)
47
+ ruby_data.is_a?(Array) ? { rows: ruby_data } : ruby_data.to_h
48
+ rescue StandardError => e
49
+ raise "Ruby code isn't returning an array, or object which responds to `to_h' (#{e.message})"
50
+ end
51
+ end
52
+ end
@@ -173,9 +173,8 @@ class Bridgetown::Site
173
173
  documents.select(&:write?)
174
174
  end
175
175
 
176
- # Get all documents.
177
- # @return [Array<Document>] an array of documents from the
178
- # configuration
176
+ # Get all loaded resources.
177
+ # @return [Array<Bridgetown::Resource::Base>] an array of resources
179
178
  def resources
180
179
  collections.each_with_object(Set.new) do |(_, collection), set|
181
180
  set.merge(collection.resources)
@@ -21,7 +21,7 @@ class Bridgetown::Site
21
21
  end
22
22
 
23
23
  # Yields all content objects while looping through {#pages},
24
- # {#static_files_to_write}, and {#docs_to_write}.
24
+ # {#static_files_to_write}, {#docs_to_write}, {#resources_to_write}.
25
25
  #
26
26
  # @yieldparam item [Document, Page, StaticFile]
27
27
  #
@@ -3,10 +3,6 @@
3
3
  module Bridgetown
4
4
  # TODO: to be retired once the Resource engine is made official
5
5
  module Validatable
6
- # FIXME: there should be ONE TRUE METHOD to read the YAML frontmatter
7
- # in the entire project. Both this and the equivalent Document method
8
- # should be extracted and generalized.
9
- #
10
6
  # Read the YAML frontmatter.
11
7
  #
12
8
  # base - The String path to the dir containing the file.
@@ -74,15 +74,16 @@ module Bridgetown
74
74
  },
75
75
 
76
76
  "kramdown" => {
77
- "auto_ids" => true,
78
- "toc_levels" => (1..6).to_a,
79
- "entity_output" => "as_char",
80
- "smart_quotes" => "lsquo,rsquo,ldquo,rdquo",
81
- "input" => "GFM",
82
- "hard_wrap" => false,
83
- "guess_lang" => true,
84
- "footnote_nr" => 1,
85
- "show_warnings" => false,
77
+ "auto_ids" => true,
78
+ "toc_levels" => (1..6).to_a,
79
+ "entity_output" => "as_char",
80
+ "smart_quotes" => "lsquo,rsquo,ldquo,rdquo",
81
+ "input" => "GFM",
82
+ "hard_wrap" => false,
83
+ "guess_lang" => true,
84
+ "footnote_nr" => 1,
85
+ "show_warnings" => false,
86
+ "include_extraction_tags" => false,
86
87
  },
87
88
  }.each_with_object(Configuration.new) { |(k, v), hsh| hsh[k] = v.freeze }.freeze
88
89
 
@@ -53,6 +53,15 @@ module Bridgetown
53
53
  ".html"
54
54
  end
55
55
 
56
+ def line_start(convertible)
57
+ if convertible.is_a?(Bridgetown::Resource::Base) &&
58
+ convertible.model.origin.respond_to?(:front_matter_line_count)
59
+ convertible.model.origin.front_matter_line_count + 4
60
+ else
61
+ 1
62
+ end
63
+ end
64
+
56
65
  def inspect
57
66
  "#<#{self.class}#{self.class.extname_list ? " #{self.class.extname_list.join(", ")}" : nil}>"
58
67
  end
@@ -3,14 +3,26 @@
3
3
  require "tilt/erubi"
4
4
 
5
5
  module Bridgetown
6
- class ERBBuffer < String
7
- def concat_to_s(input)
8
- concat input.to_s
6
+ class OutputBuffer < ActiveSupport::SafeBuffer
7
+ def initialize(*)
8
+ super
9
+ encode!
9
10
  end
10
11
 
11
- alias_method :safe_append=, :concat_to_s
12
- alias_method :append=, :concat_to_s
13
- alias_method :safe_expr_append=, :concat_to_s
12
+ def <<(value)
13
+ return self if value.nil?
14
+
15
+ super(value.to_s)
16
+ end
17
+ alias_method :append=, :<<
18
+
19
+ def safe_expr_append=(val)
20
+ return self if val.nil? # rubocop:disable Lint/ReturnInVoidContext
21
+
22
+ safe_concat val.to_s
23
+ end
24
+
25
+ alias_method :safe_append=, :safe_concat
14
26
  end
15
27
 
16
28
  class ERBEngine < Erubi::Engine
@@ -22,24 +34,47 @@ module Bridgetown
22
34
  @src << ";" unless code[Erubi::RANGE_LAST] == "\n"
23
35
  end
24
36
 
37
+ def add_text(text)
38
+ return if text.empty?
39
+
40
+ src << bufvar << ".safe_append='"
41
+ src << text.gsub(%r{['\\]}, '\\\\\&')
42
+ src << "'.freeze;"
43
+ end
44
+
25
45
  # pulled from Rails' ActionView
26
46
  BLOCK_EXPR = %r!\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z!.freeze
27
47
 
28
48
  def add_expression(indicator, code)
49
+ src << bufvar << if (indicator == "==") || @escape
50
+ ".safe_expr_append="
51
+ else
52
+ ".append="
53
+ end
54
+
29
55
  if BLOCK_EXPR.match?(code)
30
- src << "#{@bufvar}.append= " << code
56
+ src << " " << code
31
57
  else
32
- super
58
+ src << "(" << code << ");"
33
59
  end
34
60
  end
61
+ end
35
62
 
36
- # Don't allow == to output escaped strings, as that's the opposite of Rails
37
- def add_expression_result_escaped(code)
38
- add_expression_result(code)
63
+ module ERBCapture
64
+ def capture(*args)
65
+ previous_buffer_state = @_erbout
66
+ @_erbout = OutputBuffer.new
67
+ result = yield(*args)
68
+ result = @_erbout.presence || result
69
+ @_erbout = previous_buffer_state
70
+
71
+ result.is_a?(String) ? ERB::Util.h(result) : result
39
72
  end
40
73
  end
41
74
 
42
75
  class ERBView < RubyTemplateView
76
+ include ERBCapture
77
+
43
78
  def h(input)
44
79
  Erubi.h(input)
45
80
  end
@@ -55,30 +90,10 @@ module Bridgetown
55
90
  Tilt::ErubiTemplate.new(
56
91
  site.in_source_dir(site.config[:partials_dir], "#{partial_name}.erb"),
57
92
  outvar: "@_erbout",
58
- bufval: "Bridgetown::ERBBuffer.new",
93
+ bufval: "Bridgetown::OutputBuffer.new",
59
94
  engine_class: ERBEngine
60
95
  ).render(self, options)
61
96
  end
62
-
63
- def markdownify(input = nil, &block)
64
- content = Bridgetown::Utils.reindent_for_markdown(
65
- block.nil? ? input.to_s : capture(&block)
66
- )
67
- converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
68
- result = converter.convert(content).strip
69
- result.respond_to?(:html_safe) ? result.html_safe : result
70
- end
71
-
72
- def capture(*args, &block)
73
- return capture_in_view_component(*args, &block) if @in_view_component
74
-
75
- previous_buffer_state = @_erbout
76
- @_erbout = ERBBuffer.new
77
- result = yield(*args)
78
- @_erbout = previous_buffer_state
79
-
80
- result.respond_to?(:html_safe) ? result.html_safe : result
81
- end
82
97
  end
83
98
 
84
99
  module Converters
@@ -100,14 +115,15 @@ module Bridgetown
100
115
 
101
116
  erb_renderer = Tilt::ErubiTemplate.new(
102
117
  convertible.relative_path,
118
+ line_start(convertible),
103
119
  outvar: "@_erbout",
104
- bufval: "Bridgetown::ERBBuffer.new",
120
+ bufval: "Bridgetown::OutputBuffer.new",
105
121
  engine_class: ERBEngine
106
122
  ) { content }
107
123
 
108
124
  if convertible.is_a?(Bridgetown::Layout)
109
125
  erb_renderer.render(erb_view) do
110
- convertible.current_document_output
126
+ convertible.current_document_output.html_safe
111
127
  end
112
128
  else
113
129
  erb_renderer.render(erb_view)