bridgetown-core 0.14.1 → 0.15.0

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -1
  3. data/bin/bridgetown +9 -23
  4. data/bridgetown-core.gemspec +3 -1
  5. data/lib/bridgetown-core.rb +9 -2
  6. data/lib/bridgetown-core/commands/apply.rb +73 -0
  7. data/lib/bridgetown-core/commands/base.rb +45 -0
  8. data/lib/bridgetown-core/commands/build.rb +91 -86
  9. data/lib/bridgetown-core/commands/clean.rb +30 -29
  10. data/lib/bridgetown-core/commands/concerns/actions.rb +128 -0
  11. data/lib/bridgetown-core/commands/concerns/build_options.rb +76 -0
  12. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +18 -0
  13. data/lib/bridgetown-core/commands/concerns/summarizable.rb +13 -0
  14. data/lib/bridgetown-core/commands/console.rb +57 -39
  15. data/lib/bridgetown-core/commands/doctor.rb +126 -126
  16. data/lib/bridgetown-core/commands/new.rb +120 -155
  17. data/lib/bridgetown-core/commands/plugins.rb +167 -130
  18. data/lib/bridgetown-core/commands/registrations.rb +16 -0
  19. data/lib/bridgetown-core/commands/serve.rb +219 -215
  20. data/lib/bridgetown-core/concerns/convertible.rb +1 -4
  21. data/lib/bridgetown-core/concerns/site/renderable.rb +1 -2
  22. data/lib/bridgetown-core/drops/document_drop.rb +9 -1
  23. data/lib/bridgetown-core/drops/page_drop.rb +1 -1
  24. data/lib/bridgetown-core/excerpt.rb +4 -1
  25. data/lib/bridgetown-core/generators/prototype_generator.rb +2 -0
  26. data/lib/bridgetown-core/liquid_renderer.rb +1 -0
  27. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -4
  28. data/lib/bridgetown-core/liquid_renderer/file_system.rb +3 -1
  29. data/lib/bridgetown-core/page.rb +3 -18
  30. data/lib/bridgetown-core/plugin_manager.rb +31 -17
  31. data/lib/bridgetown-core/renderer.rb +31 -18
  32. data/lib/bridgetown-core/tags/include.rb +14 -0
  33. data/lib/bridgetown-core/tags/render_content.rb +39 -16
  34. data/lib/bridgetown-core/tags/with.rb +15 -0
  35. data/lib/bridgetown-core/utils.rb +44 -0
  36. data/lib/bridgetown-core/version.rb +2 -2
  37. data/lib/bridgetown-core/watcher.rb +17 -10
  38. data/lib/site_template/Gemfile.erb +19 -0
  39. data/lib/site_template/bridgetown.config.yml +5 -3
  40. data/lib/site_template/package.json +1 -0
  41. data/lib/site_template/src/_components/footer.liquid +3 -0
  42. data/lib/site_template/src/_components/head.liquid +9 -0
  43. data/lib/site_template/src/{_includes/navbar.html → _components/navbar.liquid} +0 -0
  44. data/lib/site_template/src/_layouts/default.html +3 -3
  45. data/lib/site_template/start.js +1 -1
  46. data/lib/site_template/webpack.config.js +3 -3
  47. metadata +53 -19
  48. data/lib/bridgetown-core/command.rb +0 -112
  49. data/lib/bridgetown-core/commands/help.rb +0 -34
  50. data/lib/site_template/src/_components/.keep +0 -0
  51. data/lib/site_template/src/_includes/footer.html +0 -3
  52. data/lib/site_template/src/_includes/head.html +0 -9
@@ -188,13 +188,10 @@ module Bridgetown
188
188
  #
189
189
  # Returns nothing.
190
190
  def do_layout(payload, layouts)
191
- self.output = _renderer.tap do |renderer|
191
+ _renderer.tap do |renderer|
192
192
  renderer.layouts = layouts
193
193
  renderer.payload = payload
194
194
  end.run
195
-
196
- Bridgetown.logger.debug "Post-Render Hooks:", relative_path
197
- Bridgetown::Hooks.trigger hook_owner, :post_render, self
198
195
  ensure
199
196
  @_renderer = nil # this will allow the modifications above to disappear
200
197
  end
@@ -43,8 +43,7 @@ module Bridgetown
43
43
  def render_regenerated(document, payload)
44
44
  return unless regenerator.regenerate?(document)
45
45
 
46
- document.output = Bridgetown::Renderer.new(self, document, payload).run
47
- document.trigger_hooks(:post_render)
46
+ Bridgetown::Renderer.new(self, document, payload).run
48
47
  end
49
48
  end
50
49
  end
@@ -12,7 +12,15 @@ module Bridgetown
12
12
  mutable false
13
13
 
14
14
  def_delegator :@obj, :relative_path, :path
15
- def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url, :date
15
+ def_delegators :@obj,
16
+ :id,
17
+ :output,
18
+ :content,
19
+ :to_s,
20
+ :relative_path,
21
+ :url,
22
+ :date,
23
+ :related_posts
16
24
 
17
25
  private def_delegator :@obj, :data, :fallback_data
18
26
 
@@ -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
@@ -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?
@@ -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
@@ -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
@@ -73,18 +73,6 @@ module Bridgetown
73
73
  end
74
74
  end
75
75
 
76
- # For backwards-compatibility in subclasses that do not redefine
77
- # the `:to_liquid` method, stash existing definition under a new name
78
- #
79
- # TODO: Remove in Bridgetown 5.0
80
- alias_method :legacy_to_liquid, :to_liquid
81
- private :legacy_to_liquid
82
-
83
- # Private
84
- # Subclasses can choose to optimize their `:to_liquid` method by wrapping
85
- # it around this definition.
86
- #
87
- # TODO: Remove in Bridgetown 5.0
88
76
  def liquid_drop
89
77
  @liquid_drop ||= begin
90
78
  defaults = site.frontmatter_defaults.all(relative_path, type)
@@ -94,15 +82,12 @@ module Bridgetown
94
82
  Drops::PageDrop.new(self)
95
83
  end
96
84
  end
97
- private :liquid_drop
98
85
 
99
86
  # Public
100
87
  #
101
88
  # Liquid representation of current page
102
- #
103
- # TODO: Remove optional parameter in Bridgetown 5.0
104
- def to_liquid(attrs = nil)
105
- self.class == Bridgetown::Page ? liquid_drop : legacy_to_liquid(attrs)
89
+ def to_liquid
90
+ liquid_drop
106
91
  end
107
92
 
108
93
  # The full path and filename of the post. Defined in the YAML of the post
@@ -180,7 +165,7 @@ module Bridgetown
180
165
 
181
166
  # The path to the page source file, relative to the site source
182
167
  def relative_path
183
- @relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A\/!, "")
168
+ @relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).delete_prefix("/")
184
169
  end
185
170
 
186
171
  # Obtain destination path.
@@ -2,6 +2,9 @@
2
2
 
3
3
  module Bridgetown
4
4
  class PluginManager
5
+ PLUGINS_GROUP = :bridgetown_plugins
6
+ YARN_DEPENDENCY_REGEXP = %r!(.+)@([^@]*)$!.freeze
7
+
5
8
  attr_reader :site
6
9
 
7
10
  @source_manifests = Set.new
@@ -40,11 +43,9 @@ module Bridgetown
40
43
  if !ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
41
44
  require "bundler"
42
45
 
43
- group_name = :bridgetown_plugins
44
-
45
- required_gems = Bundler.require group_name
46
+ required_gems = Bundler.require PLUGINS_GROUP
46
47
  required_gems.select! do |dep|
47
- (dep.groups & [group_name]).any? && dep.should_include?
48
+ (dep.groups & [PLUGINS_GROUP]).any? && dep.should_include?
48
49
  end
49
50
 
50
51
  install_yarn_dependencies(required_gems)
@@ -67,30 +68,43 @@ module Bridgetown
67
68
  # If that exact package hasn't been installed, execute yarn add
68
69
  #
69
70
  # Returns nothing.
70
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
71
71
  def self.install_yarn_dependencies(required_gems)
72
72
  return unless File.exist?("package.json")
73
73
 
74
74
  package_json = JSON.parse(File.read("package.json"))
75
75
 
76
76
  required_gems.each do |loaded_gem|
77
- next unless loaded_gem.to_spec&.metadata&.dig("yarn-add")
78
-
79
- yarn_add_dependency = loaded_gem.to_spec.metadata["yarn-add"].split("@")
80
- next unless yarn_add_dependency.length == 2
81
-
82
- # check matching version number is see if it's already installed
83
- if package_json["dependencies"]
84
- current_package = package_json["dependencies"].dig(yarn_add_dependency.first)
85
- next unless current_package.nil? || current_package != yarn_add_dependency.last
86
- end
77
+ yarn_dependency = find_yarn_dependency(loaded_gem)
78
+ next unless add_yarn_dependency?(yarn_dependency, package_json)
87
79
 
88
80
  # all right, time to install the package
89
- cmd = "yarn add #{yarn_add_dependency.join("@")}"
81
+ cmd = "yarn add #{yarn_dependency.join("@")}"
90
82
  system cmd
91
83
  end
92
84
  end
93
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
85
+
86
+ def self.find_yarn_dependency(loaded_gem)
87
+ yarn_dependency = loaded_gem.to_spec&.metadata&.dig("yarn-add")&.match(YARN_DEPENDENCY_REGEXP)
88
+ return nil if yarn_dependency&.length != 3 || yarn_dependency[2] == ""
89
+
90
+ yarn_dependency[1..2]
91
+ end
92
+
93
+ def self.add_yarn_dependency?(yarn_dependency, package_json)
94
+ return false if yarn_dependency.nil?
95
+
96
+ # check matching version number is see if it's already installed
97
+ if package_json["dependencies"]
98
+ current_version = package_json["dependencies"].dig(yarn_dependency.first)
99
+ package_requires_updating?(current_version, yarn_dependency.last)
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ def self.package_requires_updating?(current_version, dep_version)
106
+ current_version.nil? || current_version != dep_version && !current_version.include?("/")
107
+ end
94
108
 
95
109
  # Require all .rb files
96
110
  #
@@ -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
 
@@ -58,8 +63,8 @@ module Bridgetown
58
63
  assign_layout_data!
59
64
 
60
65
  document.trigger_hooks(:pre_render, payload)
61
-
62
- render_document
66
+ document.output = render_document
67
+ document.trigger_hooks(:post_render)
63
68
  end
64
69
 
65
70
  # Render the document.
@@ -67,18 +72,14 @@ module Bridgetown
67
72
  # Returns String rendered document output
68
73
  # rubocop: disable Metrics/AbcSize
69
74
  def render_document
70
- info = {
71
- registers: { site: site, page: payload["page"] },
72
- strict_filters: liquid_options["strict_filters"],
73
- strict_variables: liquid_options["strict_variables"],
74
- }
75
+ liquid_context = provide_liquid_context
75
76
 
76
77
  execute_inline_ruby!
77
78
 
78
79
  output = document.content
79
80
  if document.render_with_liquid?
80
81
  Bridgetown.logger.debug "Rendering Liquid:", document.relative_path
81
- output = render_liquid(output, payload, info, document.path)
82
+ output = render_liquid(output, payload, liquid_context, document.path)
82
83
  end
83
84
 
84
85
  Bridgetown.logger.debug "Rendering Markup:", document.relative_path
@@ -87,12 +88,24 @@ module Bridgetown
87
88
 
88
89
  if document.place_in_layout?
89
90
  Bridgetown.logger.debug "Rendering Layout:", document.relative_path
90
- output = place_in_layouts(output, payload, info)
91
+ output = place_in_layouts(output, payload, liquid_context)
91
92
  end
92
93
 
93
94
  output
94
95
  end
95
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
+
96
109
  def execute_inline_ruby!
97
110
  return unless site.config.should_execute_inline_ruby?
98
111
 
@@ -101,21 +114,21 @@ module Bridgetown
101
114
 
102
115
  # rubocop: enable Metrics/AbcSize
103
116
 
104
- # Render the given content with the payload and info
117
+ # Render the given content with the payload and context
105
118
  #
106
119
  # content -
107
120
  # payload -
108
- # info -
121
+ # context -
109
122
  # path - (optional) the path to the file, for use in ex
110
123
  #
111
124
  # Returns String the content, rendered by Liquid.
112
- def render_liquid(content, payload, info, path = nil)
125
+ def render_liquid(content, payload, liquid_context, path = nil)
113
126
  template = site.liquid_renderer.file(path).parse(content)
114
127
  template.warnings.each do |e|
115
128
  Bridgetown.logger.warn "Liquid Warning:",
116
129
  LiquidRenderer.format_error(e, path || document.relative_path)
117
130
  end
118
- template.render!(payload, info)
131
+ template.render!(payload, liquid_context)
119
132
  # rubocop: disable Lint/RescueException
120
133
  rescue Exception => e
121
134
  Bridgetown.logger.error "Liquid Exception:",
@@ -151,7 +164,7 @@ module Bridgetown
151
164
  # Render layouts and place document content inside.
152
165
  #
153
166
  # Returns String rendered content
154
- def place_in_layouts(content, payload, info)
167
+ def place_in_layouts(content, payload, liquid_context)
155
168
  output = content.dup
156
169
  layout = layouts[document.data["layout"].to_s]
157
170
  validate_layout(layout)
@@ -162,7 +175,7 @@ module Bridgetown
162
175
  payload["layout"] = nil
163
176
 
164
177
  while layout
165
- output = render_layout(output, layout, info)
178
+ output = render_layout(output, layout, liquid_context)
166
179
  add_regenerator_dependencies(layout)
167
180
 
168
181
  next unless (layout = site.layouts[layout.data["layout"]])
@@ -189,14 +202,14 @@ module Bridgetown
189
202
  # Render layout content into document.output
190
203
  #
191
204
  # Returns String rendered content
192
- def render_layout(output, layout, info)
205
+ def render_layout(output, layout, liquid_context)
193
206
  payload["content"] = output
194
207
  payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
195
208
 
196
209
  render_liquid(
197
210
  layout.content,
198
211
  payload,
199
- info,
212
+ liquid_context,
200
213
  layout.path
201
214
  )
202
215
  end
@@ -3,6 +3,10 @@
3
3
  module Bridgetown
4
4
  module Tags
5
5
  class IncludeTag < Liquid::Tag
6
+ class << self
7
+ attr_accessor :deprecation_message_shown
8
+ end
9
+
6
10
  VALID_SYNTAX = %r!
7
11
  ([\w-]+)\s*=\s*
8
12
  (?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))
@@ -18,6 +22,14 @@ module Bridgetown
18
22
 
19
23
  def initialize(tag_name, markup, tokens)
20
24
  super
25
+
26
+ unless self.class.deprecation_message_shown
27
+ Bridgetown.logger.warn "NOTICE: the {% include %} tag is deprecated and" \
28
+ " will be removed in Bridgetown 1.0. You should" \
29
+ " use the {% render %} tag instead."
30
+ self.class.deprecation_message_shown = true
31
+ end
32
+
21
33
  matched = markup.strip.match(VARIABLE_SYNTAX)
22
34
  if matched
23
35
  @file = matched["variable"].strip
@@ -200,7 +212,9 @@ module Bridgetown
200
212
  else
201
213
  File.join(site.config["collections_dir"], page_payload["path"])
202
214
  end
215
+ # rubocop:disable Performance/DeleteSuffix
203
216
  resource_path.sub!(%r!/#excerpt\z!, "")
217
+ # rubocop:enable Performance/DeleteSuffix
204
218
  site.in_source_dir File.dirname(resource_path)
205
219
  end
206
220
  end
@@ -3,28 +3,51 @@
3
3
  module Bridgetown
4
4
  module Tags
5
5
  class BlockRenderTag < Liquid::Block
6
- def initialize(tag_name, markup, options)
7
- super
6
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
7
+ def render(context)
8
+ context.stack({}) do
9
+ # unindent the incoming text
10
+ content = Bridgetown::Utils.reindent_for_markdown(super)
8
11
 
9
- @tag = tag_name
10
- @markup = markup
11
- @options = options
12
- end
12
+ regions = gather_content_regions(context)
13
13
 
14
- def render(context)
15
- content = super.gsub(%r!^[ \t]+!, "") # unindent the incoming text
14
+ site = context.registers[:site]
15
+ converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
16
+ markdownified_content = converter.convert(content)
17
+ context["processed_component_content"] = markdownified_content
16
18
 
17
- site = context.registers[:site]
18
- converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
19
- markdownified_content = converter.convert(content)
19
+ render_params = [@markup, "content: processed_component_content"]
20
+ unless regions.empty?
21
+ regions.each do |region_name, region_content|
22
+ region_name = region_name.sub("content_with_region_", "")
20
23
 
21
- context.stack do
22
- context["componentcontent"] = markdownified_content
23
- render_params = "#{@markup}, content: componentcontent"
24
- render_tag = Liquid::Render.parse("render", render_params, @options, @parse_context)
25
- render_tag.render_tag(context, +"")
24
+ if region_name.end_with? ":markdown"
25
+ region_name.sub!(%r!:markdown$!, "")
26
+ context[region_name] = converter.convert(
27
+ Bridgetown::Utils.reindent_for_markdown(region_content)
28
+ )
29
+ else
30
+ context[region_name] = region_content
31
+ end
32
+ render_params.push "#{region_name}: #{region_name}"
33
+ end
34
+ end
35
+
36
+ Liquid::Render.parse("render", render_params.join(","), nil, @parse_context)
37
+ .render_tag(context, +"")
26
38
  end
27
39
  end
40
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
41
+
42
+ private
43
+
44
+ def gather_content_regions(context)
45
+ unless context.scopes[0].keys.find { |k| k.to_s.start_with? "content_with_region_" }
46
+ return {}
47
+ end
48
+
49
+ context.scopes[0].select { |k| k.to_s.start_with? "content_with_region_" }
50
+ end
28
51
  end
29
52
  end
30
53
  end