bridgetown-core 1.1.0 → 1.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bridgetown +1 -3
  3. data/bridgetown-core.gemspec +7 -6
  4. data/lib/bridgetown-core/collection.rb +5 -2
  5. data/lib/bridgetown-core/commands/base.rb +1 -1
  6. data/lib/bridgetown-core/commands/build.rb +18 -5
  7. data/lib/bridgetown-core/commands/clean.rb +1 -1
  8. data/lib/bridgetown-core/commands/concerns/actions.rb +2 -4
  9. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +7 -8
  10. data/lib/bridgetown-core/commands/console.rb +20 -1
  11. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +5 -0
  12. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +8 -7
  13. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +1 -1
  14. data/lib/bridgetown-core/commands/plugins.rb +46 -32
  15. data/lib/bridgetown-core/commands/serve.rb +1 -2
  16. data/lib/bridgetown-core/commands/start.rb +0 -8
  17. data/lib/bridgetown-core/component.rb +50 -0
  18. data/lib/bridgetown-core/concerns/site/configurable.rb +22 -18
  19. data/lib/bridgetown-core/concerns/site/localizable.rb +2 -6
  20. data/lib/bridgetown-core/concerns/site/processable.rb +0 -1
  21. data/lib/bridgetown-core/concerns/site/ssr.rb +0 -1
  22. data/lib/bridgetown-core/concerns/transformable.rb +5 -2
  23. data/lib/bridgetown-core/configuration/configuration_dsl.rb +146 -0
  24. data/lib/bridgetown-core/configuration.rb +90 -15
  25. data/lib/bridgetown-core/configurations/gh-pages/gh-pages.yml +3 -1
  26. data/lib/bridgetown-core/configurations/lit/esbuild-plugins.js +5 -2
  27. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  28. data/lib/bridgetown-core/converter.rb +8 -0
  29. data/lib/bridgetown-core/converters/identity.rb +2 -0
  30. data/lib/bridgetown-core/converters/liquid_templates.rb +1 -0
  31. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +1 -1
  32. data/lib/bridgetown-core/converters/markdown.rb +2 -0
  33. data/lib/bridgetown-core/current.rb +19 -5
  34. data/lib/bridgetown-core/deprecator.rb +4 -37
  35. data/lib/bridgetown-core/filters.rb +1 -3
  36. data/lib/bridgetown-core/generated_page.rb +5 -0
  37. data/lib/bridgetown-core/helpers.rb +119 -8
  38. data/lib/bridgetown-core/hooks.rb +1 -0
  39. data/lib/bridgetown-core/kramdown/parser/gfm.rb +1 -1
  40. data/lib/bridgetown-core/layout.rb +1 -1
  41. data/lib/bridgetown-core/model/base.rb +11 -12
  42. data/lib/bridgetown-core/model/builder_origin.rb +1 -1
  43. data/lib/bridgetown-core/model/origin.rb +5 -1
  44. data/lib/bridgetown-core/model/plugin_origin.rb +1 -1
  45. data/lib/bridgetown-core/model/repo_origin.rb +7 -7
  46. data/lib/bridgetown-core/plugin.rb +2 -6
  47. data/lib/bridgetown-core/plugin_manager.rb +120 -41
  48. data/lib/bridgetown-core/rack/boot.rb +3 -7
  49. data/lib/bridgetown-core/rack/logger.rb +1 -0
  50. data/lib/bridgetown-core/rack/roda.rb +39 -45
  51. data/lib/bridgetown-core/rack/routes.rb +2 -6
  52. data/lib/bridgetown-core/rack/static_indexes.rb +1 -2
  53. data/lib/bridgetown-core/reader.rb +39 -50
  54. data/lib/bridgetown-core/readers/layout_reader.rb +1 -1
  55. data/lib/bridgetown-core/resource/base.rb +17 -1
  56. data/lib/bridgetown-core/ruby_template_view.rb +12 -8
  57. data/lib/bridgetown-core/site.rb +10 -3
  58. data/lib/bridgetown-core/slot.rb +41 -0
  59. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +23 -9
  60. data/lib/bridgetown-core/utils/ansi.rb +1 -1
  61. data/lib/bridgetown-core/utils/aux.rb +7 -6
  62. data/lib/bridgetown-core/utils/initializers.rb +45 -0
  63. data/lib/bridgetown-core/utils/loaders_manager.rb +2 -2
  64. data/lib/bridgetown-core/utils/require_gems.rb +11 -35
  65. data/lib/bridgetown-core/utils/ruby_front_matter.rb +2 -3
  66. data/lib/bridgetown-core/{converters/smartypants.rb → utils/smarty_pants_converter.rb} +5 -24
  67. data/lib/bridgetown-core/utils.rb +1 -0
  68. data/lib/bridgetown-core/version.rb +2 -2
  69. data/lib/bridgetown-core/watcher.rb +1 -8
  70. data/lib/bridgetown-core.rb +89 -6
  71. data/lib/roda/plugins/bridgetown_boot.rb +25 -0
  72. data/lib/roda/plugins/bridgetown_ssr.rb +23 -0
  73. data/lib/roda/plugins/initializers.rb +17 -0
  74. data/lib/site_template/.gitignore +1 -0
  75. data/lib/site_template/Gemfile.erb +1 -1
  76. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +2 -2
  77. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +1 -1
  78. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +1 -1
  79. data/lib/site_template/TEMPLATES/liquid/_layouts/default.liquid +2 -2
  80. data/lib/site_template/TEMPLATES/liquid/_layouts/page.liquid +1 -1
  81. data/lib/site_template/TEMPLATES/liquid/_layouts/post.liquid +1 -1
  82. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +2 -2
  83. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +1 -1
  84. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +1 -1
  85. data/lib/site_template/config/initializers.rb +43 -0
  86. data/lib/site_template/package.json.erb +1 -1
  87. data/lib/site_template/server/roda_app.rb +2 -8
  88. metadata +17 -9
@@ -14,6 +14,8 @@ module Bridgetown
14
14
 
15
15
  Context = Struct.new(:registers)
16
16
 
17
+ # @param view [Bridgetown::RubyTemplateView]
18
+ # @param site [Bridgetown::Site]
17
19
  def initialize(view, site)
18
20
  @view = view
19
21
  @site = site
@@ -96,23 +98,35 @@ module Bridgetown
96
98
  # @return [String] the anchor tag HTML
97
99
  # @raise [ArgumentError] if the file cannot be found
98
100
  def link_to(text, relative_path, options = {})
101
+ segments = attributes_from_options({ href: url_for(relative_path) }.merge(options))
102
+
103
+ safe("<a #{segments}>#{text}</a>")
104
+ end
105
+
106
+ # Create a set of attributes from a hash.
107
+ #
108
+ # @param options [Hash] key-value pairs of HTML attributes
109
+ # @return [String]
110
+ def attributes_from_options(options)
99
111
  segments = []
100
- segments << "a"
101
- segments << "href=\"#{url_for(relative_path)}\""
102
112
  options.each do |attr, option|
103
- attr = attr.to_s.tr("_", "-")
104
- segments << "#{attr}=\"#{Utils.xml_escape(option)}\""
113
+ attr = dashed(attr)
114
+ if option.is_a?(Hash)
115
+ option = option.transform_keys { |key| "#{attr}-#{dashed(key)}" }
116
+ segments << attributes_from_options(option)
117
+ else
118
+ segments << attribute_segment(attr, option)
119
+ end
105
120
  end
106
- # TODO: this might leak an XSS string into text, need to check
107
- safe("<#{segments.join(" ")}>#{text}</a>")
121
+ safe(segments.join(" "))
108
122
  end
109
123
 
110
124
  # Forward all arguments to I18n.t method
111
125
  #
112
126
  # @return [String] the translated string
113
127
  # @see I18n
114
- def t(*args)
115
- I18n.send :t, *args
128
+ def t(*args, **kwargs)
129
+ I18n.send :t, *args, **kwargs
116
130
  end
117
131
 
118
132
  # For template contexts where ActiveSupport's output safety is loaded, we
@@ -124,6 +138,103 @@ module Bridgetown
124
138
  input.to_s.html_safe
125
139
  end
126
140
  alias_method :raw, :safe
141
+
142
+ # Define a new content slot
143
+ #
144
+ # @param name [String, Symbol] name of the slot
145
+ # @param input [String] content if not supplying a block
146
+ # @param replace [Boolean] set to true to replace any previously defined slot with same name
147
+ # @param transform [Boolean] set to false to avoid template-based transforms (Markdown, etc.)
148
+ # @return [void]
149
+ def slot(name, input = nil, replace: false, transform: true, &block)
150
+ content = Bridgetown::Utils.reindent_for_markdown(
151
+ block.nil? ? input.to_s : view.capture(&block)
152
+ )
153
+
154
+ resource = if view.respond_to?(:resource)
155
+ # We're in a resource rendering context
156
+ view.resource
157
+ elsif view.respond_to?(:view_context)
158
+ # We're in a component rendering context, although it's
159
+ # likely the component's own `slot` method will be called
160
+ # in this context
161
+ view.view_context.resource
162
+ end
163
+
164
+ name = name.to_s
165
+ resource.slots.reject! { _1.name == name } if replace
166
+ resource.slots << Slot.new(
167
+ name: name,
168
+ content: content,
169
+ context: resource,
170
+ transform: transform
171
+ )
172
+
173
+ nil
174
+ end
175
+
176
+ # Render out a content slot
177
+ #
178
+ # @param name [String, Symbol] name of the slot
179
+ # @param input [String] default content if slot isn't defined and no block provided
180
+ # @return [String]
181
+ def slotted(name, default_input = nil, &default_block) # rubocop:todo Metrics
182
+ resource = if view.respond_to?(:resource)
183
+ view.resource
184
+ elsif view.respond_to?(:view_context)
185
+ view.view_context.resource
186
+ end
187
+
188
+ return unless resource
189
+
190
+ name = name.to_s
191
+ filtered_slots = resource.slots.select do |slot|
192
+ slot.name == name
193
+ end
194
+
195
+ return filtered_slots.map(&:content).join.html_safe if filtered_slots.length.positive?
196
+
197
+ default_block.nil? ? default_input.to_s : view.capture(&default_block)
198
+ end
199
+
200
+ # Check if a content slot has been defined
201
+ #
202
+ # @return [Boolean]
203
+ def slotted?(name)
204
+ resource = if view.respond_to?(:resource)
205
+ view.resource
206
+ elsif view.respond_to?(:view_context)
207
+ view.view_context.resource
208
+ end
209
+
210
+ return unless resource
211
+
212
+ name = name.to_s
213
+ resource.slots.any? do |slot|
214
+ slot.name == name
215
+ end
216
+ end
217
+
218
+ private
219
+
220
+ # Covert an underscored value into a dashed string.
221
+ #
222
+ # @example "foo_bar_baz" => "foo-bar-baz"
223
+ #
224
+ # @param value [String|Symbol]
225
+ # @return [String]
226
+ def dashed(value)
227
+ value.to_s.tr("_", "-")
228
+ end
229
+
230
+ # Create an attribute segment for a tag.
231
+ #
232
+ # @param attr [String] the HTML attribute name
233
+ # @param value [String] the attribute value
234
+ # @return [String]
235
+ def attribute_segment(attr, value)
236
+ "#{attr}=\"#{Utils.xml_escape(value)}\""
237
+ end
127
238
  end
128
239
  end
129
240
  end
@@ -70,6 +70,7 @@ module Bridgetown
70
70
  # @yield the block will be called when the event is triggered. Typically it receives at
71
71
  # least one argument.
72
72
  # @yieldparam obj the object which triggered the event hook
73
+ # @return [Proc] the block that was pased in
73
74
  def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
74
75
  @registry[owner] ||= []
75
76
 
@@ -1,4 +1,4 @@
1
- # Frozen-string-literal: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "kramdown-parser-gfm"
4
4
 
@@ -92,7 +92,7 @@ module Bridgetown
92
92
 
93
93
  # Returns the contents as a String.
94
94
  def to_s
95
- output || content || ""
95
+ content || ""
96
96
  end
97
97
 
98
98
  # Accessor for data properties by Liquid.
@@ -10,16 +10,14 @@ module Bridgetown
10
10
  define_model_callbacks :load, :save, :destroy
11
11
 
12
12
  class << self
13
- def find(id)
14
- unless Bridgetown::Current.site
15
- raise "A Bridgetown site must be initialized and added to Current"
16
- end
13
+ def find(id, site: Bridgetown::Current.site)
14
+ raise "A Bridgetown site must be initialized and added to Current" unless site
17
15
 
18
- origin = origin_for_id(id)
16
+ origin = origin_for_id(id, site: site)
19
17
  klass_for_id(id, origin: origin).new(origin.read)
20
18
  end
21
19
 
22
- def origin_for_id(id)
20
+ def origin_for_id(id, site: Bridgetown::Current.site)
23
21
  scheme = URI.parse(id).scheme
24
22
  origin_klass = Origin.descendants.find do |klass|
25
23
  klass.handle_scheme?(scheme)
@@ -27,7 +25,7 @@ module Bridgetown
27
25
 
28
26
  raise "No origin could be found for #{id}" unless origin_klass
29
27
 
30
- origin_klass.new(id)
28
+ origin_klass.new(id, site: site)
31
29
  end
32
30
 
33
31
  def klass_for_id(id, origin: nil)
@@ -40,14 +38,15 @@ module Bridgetown
40
38
  origin ||= origin_for_id(id)
41
39
  origin.verify_model?(self)
42
40
  end
43
- end
44
41
 
45
- class << self
42
+ # @param builder [Bridgetown::Builder]
46
43
  def build(builder, collection_name, path, data)
44
+ site = builder.site
47
45
  data = Bridgetown::Model::BuilderOrigin.new(
48
- Bridgetown::Model::BuilderOrigin.id_for_builder_path(builder, path)
46
+ Bridgetown::Model::BuilderOrigin.id_for_builder_path(builder, path),
47
+ site: site
49
48
  ).read do
50
- data[:_collection_] = Bridgetown::Current.site.collections[collection_name]
49
+ data[:_collection_] = site.collections[collection_name]
51
50
  data
52
51
  end
53
52
  new(data)
@@ -106,7 +105,7 @@ module Bridgetown
106
105
  # override if need be
107
106
  # @return [Bridgetown::Site]
108
107
  def site
109
- Bridgetown::Current.site
108
+ origin.site
110
109
  end
111
110
 
112
111
  # @return [Bridgetown::Collection]
@@ -16,7 +16,7 @@ module Bridgetown
16
16
  end
17
17
  end
18
18
 
19
- def initialize(id)
19
+ def initialize(id, site: Bridgetown::Current.site)
20
20
  super
21
21
  @relative_path = Pathname.new(url.path.delete_prefix("/"))
22
22
  end
@@ -11,13 +11,17 @@ module Bridgetown
11
11
  # @return [String]
12
12
  attr_accessor :id
13
13
 
14
+ # @return [Bridgetown::Site]
15
+ attr_accessor :site
16
+
14
17
  # You must implement in subclasses
15
18
  def self.handle_scheme?(_scheme)
16
19
  false
17
20
  end
18
21
 
19
- def initialize(id)
22
+ def initialize(id, site: Bridgetown::Current.site)
20
23
  self.id = id
24
+ self.site = site
21
25
  end
22
26
 
23
27
  # You can override in subclass
@@ -12,7 +12,7 @@ module Bridgetown
12
12
  def manifest
13
13
  @manifest ||= begin
14
14
  manifest_origin = Addressable::URI.unescape(url.path.delete_prefix("/")).split("/").first
15
- Bridgetown::PluginManager.source_manifests.find do |manifest|
15
+ site.config.source_manifests.find do |manifest|
16
16
  manifest.origin.to_s == manifest_origin
17
17
  end.tap do |manifest|
18
18
  raise "Unable to locate a source manifest for #{manifest_origin}" unless manifest
@@ -32,10 +32,10 @@ module Bridgetown
32
32
  # @param collection [Bridgetown::Collection, String, Symbol] either a collection
33
33
  # label or Collection object
34
34
  # @param relative_path [Pathname, String] the source path of the file to save
35
- def new_with_collection_path(collection, relative_path)
35
+ def new_with_collection_path(collection, relative_path, site: Bridgetown::Current.site)
36
36
  collection = collection.label if collection.is_a?(Bridgetown::Collection)
37
37
 
38
- new("repo://#{collection}.collection/#{relative_path}")
38
+ new("repo://#{collection}.collection/#{relative_path}", site: site)
39
39
  end
40
40
  end
41
41
 
@@ -93,11 +93,11 @@ module Bridgetown
93
93
  else
94
94
  "pages"
95
95
  end
96
- @collection = Bridgetown::Current.site.collections[collection_name]
96
+ @collection = site.collections[collection_name]
97
97
  end
98
98
 
99
99
  def original_path
100
- @original_path ||= relative_path.expand_path(Bridgetown::Current.site.source)
100
+ @original_path ||= relative_path.expand_path(site.source)
101
101
  end
102
102
 
103
103
  def exists?
@@ -118,7 +118,7 @@ module Bridgetown
118
118
  rows:
119
119
  CSV.read(original_path,
120
120
  headers: true,
121
- encoding: Bridgetown::Current.site.config["encoding"]).map(&:to_hash),
121
+ encoding: site.config["encoding"]).map(&:to_hash),
122
122
  }
123
123
  when ".tsv"
124
124
  {
@@ -126,7 +126,7 @@ module Bridgetown
126
126
  CSV.read(original_path,
127
127
  col_sep: "\t",
128
128
  headers: true,
129
- encoding: Bridgetown::Current.site.config["encoding"]).map(&:to_hash),
129
+ encoding: site.config["encoding"]).map(&:to_hash),
130
130
  }
131
131
  when ".rb"
132
132
  process_ruby_data(File.read(original_path), original_path, 1)
@@ -145,7 +145,7 @@ module Bridgetown
145
145
  "could not read file #{original_path}: #{error.message}"
146
146
  end
147
147
 
148
- if Bridgetown::Current.site.config["strict_front_matter"] ||
148
+ if site.config["strict_front_matter"] ||
149
149
  error.is_a?(Bridgetown::Errors::FatalException)
150
150
  raise error
151
151
  end
@@ -13,13 +13,9 @@ module Bridgetown
13
13
  lowest: -100,
14
14
  }.freeze
15
15
 
16
- SourceManifest = Struct.new(:origin, :components, :content, :layouts, keyword_init: true)
17
-
18
- # Initialize a new plugin. This should be overridden by the subclass.
19
- #
20
- # config - The Hash of configuration options.
16
+ # Initialize a new plugin. This should be overridden by the subclass (generator or converter)
21
17
  #
22
- # Returns a new instance.
18
+ # @param config [Bridgetown::Configuration] the configuration for the site
23
19
  def initialize(config = {}) # rubocop:disable Style/RedundantInitialize
24
20
  # no-op for default
25
21
  end
@@ -2,24 +2,42 @@
2
2
 
3
3
  module Bridgetown
4
4
  class PluginManager
5
- PLUGINS_GROUP = :bridgetown_plugins
5
+ LEGACY_PLUGINS_GROUP = :bridgetown_plugins
6
6
  YARN_DEPENDENCY_REGEXP = %r!(.+)@([^@]*)$!.freeze
7
7
 
8
8
  attr_reader :site, :loaders_manager
9
9
 
10
- @source_manifests = Set.new
11
10
  @registered_plugins = Set.new
12
11
 
12
+ # @param source_manifest [Bridgetown::Configuration::SourceManifest]
13
13
  def self.add_source_manifest(source_manifest)
14
- unless source_manifest.is_a?(Bridgetown::Plugin::SourceManifest)
14
+ unless source_manifest.is_a?(Bridgetown::Configuration::SourceManifest)
15
15
  raise "You must add a SourceManifest instance"
16
16
  end
17
17
 
18
- @source_manifests << source_manifest
18
+ unless Bridgetown::Current.preloaded_configuration
19
+ raise "A preloaded configuration must be present before adding source manifests"
20
+ end
21
+
22
+ Bridgetown::Deprecator.deprecation_message(
23
+ "The #{source_manifest.origin} plugin should switch from using `add_source_manifest' to " \
24
+ "the `source_manifest` initializer method"
25
+ )
26
+
27
+ Bridgetown::Current.preloaded_configuration.source_manifests << source_manifest
19
28
  end
20
29
 
21
- def self.new_source_manifest(*args, **kwargs)
22
- add_source_manifest(Bridgetown::Plugin::SourceManifest.new(*args, **kwargs))
30
+ def self.new_source_manifest(*_args, **kwargs)
31
+ unless Bridgetown::Current.preloaded_configuration
32
+ raise "A preloaded configuration must be present before adding source manifests"
33
+ end
34
+
35
+ Bridgetown::Deprecator.deprecation_message(
36
+ "The #{kwargs[:origin]} plugin should switch from using `new_source_manifest' to the " \
37
+ "`source_manifest` initializer method"
38
+ )
39
+
40
+ add_source_manifest(Bridgetown::Configuration::SourceManifest.new(**kwargs))
23
41
  end
24
42
 
25
43
  def self.add_registered_plugin(gem_or_plugin_file)
@@ -27,56 +45,101 @@ module Bridgetown
27
45
  end
28
46
 
29
47
  class << self
30
- attr_reader :source_manifests, :registered_plugins
31
- end
48
+ attr_reader :registered_plugins
32
49
 
33
- # Create an instance of this class.
34
- #
35
- # site - the instance of Bridgetown::Site we're concerned with
36
- #
37
- # Returns nothing
38
- def initialize(site)
39
- @site = site
40
- @loaders_manager = Bridgetown::Utils::LoadersManager.new(site.config)
50
+ def bundler_specs
51
+ @bundler_specs ||= Bundler.load.requested_specs
52
+ end
53
+
54
+ def source_manifests
55
+ Bridgetown::Deprecator.deprecation_message(
56
+ "Use the configuration's `source_manifests` method instead of the plugin manager"
57
+ )
58
+
59
+ Bridgetown::Current.preloaded_configuration.source_manifests
60
+ end
41
61
  end
42
62
 
43
- def self.require_from_bundler(skip_yarn: false) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
44
- # NOTE: investigate why this ENV var is really necessary
63
+ def self.setup_bundler(skip_yarn: false)
45
64
  if !ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] && File.file?("Gemfile")
46
65
  require "bundler"
47
66
 
48
- required_gems = Bundler.require(PLUGINS_GROUP).select do |dep|
49
- (dep.groups & [PLUGINS_GROUP]).any? && dep.should_include?
50
- end
51
-
52
- install_yarn_dependencies(required_gems) unless skip_yarn
67
+ require_relative "utils/initializers"
68
+ load_determined_bundler_environment(skip_yarn: skip_yarn)
53
69
 
54
- required_gems.each do |installed_gem|
55
- add_registered_plugin installed_gem
56
- end
57
-
58
- Bridgetown.logger.debug("PluginManager:",
59
- "Required #{required_gems.map(&:name).join(", ")}")
60
70
  ENV["BRIDGETOWN_NO_BUNDLER_REQUIRE"] = "true"
61
-
62
71
  true
63
72
  else
64
73
  false
65
74
  end
66
75
  end
76
+ class << self
77
+ alias_method :require_from_bundler, :setup_bundler
78
+ end
67
79
 
68
- # Iterates through loaded plugins and finds yard-add gemspec metadata.
80
+ def self.load_determined_bundler_environment(skip_yarn: false)
81
+ boot_file = File.join("config", "boot.rb")
82
+
83
+ if File.file?(boot_file)
84
+ # We'll let config/boot.rb take care of Bundler setup
85
+ require File.expand_path(boot_file)
86
+ elsif File.file?(File.join("config", "initializers.rb"))
87
+ # We'll just make sure the default and environmental gems are available.
88
+ # Note: the default Bundler config will set up all gem groups,
89
+ # see: https://bundler.io/guides/groups.html
90
+ Bundler.setup(:default, Bridgetown.env)
91
+ else
92
+ # Only setup and require :bridgetown_plugins
93
+ legacy_yarn_and_register(legacy_require, skip_yarn: skip_yarn)
94
+ end
95
+ end
96
+
97
+ def self.require_gem(name)
98
+ Bridgetown::Utils::RequireGems.require_with_graceful_fail(name)
99
+ plugins = Bridgetown::PluginManager.install_yarn_dependencies(name: name)
100
+
101
+ plugin_to_register = if plugins.length == 1
102
+ plugins.first
103
+ else
104
+ bundler_specs.find do |loaded_gem|
105
+ loaded_gem.to_spec&.name == name.to_s
106
+ end
107
+ end
108
+ add_registered_plugin plugin_to_register
109
+
110
+ Bridgetown.logger.debug("PluginManager:",
111
+ "Registered #{plugin_to_register.name}")
112
+ end
113
+
114
+ def self.legacy_require
115
+ Bundler.require(LEGACY_PLUGINS_GROUP).select do |dep|
116
+ (dep.groups & [LEGACY_PLUGINS_GROUP]).any? && dep.should_include?
117
+ end
118
+ end
119
+
120
+ def self.legacy_yarn_and_register(required_gems, skip_yarn: false)
121
+ install_yarn_dependencies(required_gems) unless skip_yarn
122
+
123
+ required_gems.each do |installed_gem|
124
+ add_registered_plugin installed_gem
125
+ end
126
+
127
+ Bridgetown.logger.debug("PluginManager:",
128
+ "Required #{required_gems.map(&:name).join(", ")}")
129
+ end
130
+
131
+ # Iterates through loaded gems and finds yard-add gemspec metadata.
69
132
  # If that exact package hasn't been installed, execute yarn add
70
133
  #
71
- # Returns nothing.
72
- def self.install_yarn_dependencies(required_gems, single_gemname = nil)
73
- return unless File.exist?("package.json")
134
+ # @return [Bundler::SpecSet]
135
+ def self.install_yarn_dependencies(required_gems = bundler_specs, name: nil)
136
+ return required_gems unless File.exist?("package.json")
74
137
 
75
138
  package_json = JSON.parse(File.read("package.json"))
76
139
 
77
- gems_to_search = if single_gemname
140
+ gems_to_search = if name
78
141
  required_gems.select do |loaded_gem|
79
- loaded_gem.to_spec&.name == single_gemname.to_s
142
+ loaded_gem.to_spec&.name == name.to_s
80
143
  end
81
144
  else
82
145
  required_gems
@@ -90,6 +153,8 @@ module Bridgetown
90
153
  cmd = "yarn add #{yarn_dependency.join("@")}"
91
154
  system cmd
92
155
  end
156
+
157
+ gems_to_search
93
158
  end
94
159
 
95
160
  def self.find_yarn_dependency(loaded_gem)
@@ -115,9 +180,16 @@ module Bridgetown
115
180
  current_version.nil? || (current_version != dep_version && !current_version.include?("/"))
116
181
  end
117
182
 
118
- # Require all .rb files
183
+ # Provides a plugin manager for the site
184
+ #
185
+ # @param site [Bridgetown::Site]
186
+ def initialize(site)
187
+ @site = site
188
+ end
189
+
190
+ # Finds and registers plugins in the local folder(s)
119
191
  #
120
- # Returns nothing.
192
+ # @return [void]
121
193
  def require_plugin_files
122
194
  plugins_path.each do |plugin_search_path|
123
195
  plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb"))
@@ -135,11 +207,18 @@ module Bridgetown
135
207
  end
136
208
  next if site.config[:plugins_use_zeitwerk]
137
209
 
210
+ Deprecator.deprecation_message(
211
+ "The `plugins_use_zeitwerk' configuration option will be removed in the next version " \
212
+ "of Bridgetown (aka will be permanently set to \"true\")"
213
+ )
138
214
  Bridgetown::Utils::RequireGems.require_with_graceful_fail(sorted_plugin_files)
139
215
  end
140
216
  end
141
217
 
142
- # Reload .rb plugin files via the watcher
218
+ # Reloads .rb plugin files via the watcher
219
+ # DEPRECATED (not necessary with Zeitwerk)
220
+ #
221
+ # @return [void]
143
222
  def reload_plugin_files
144
223
  return if site.config[:plugins_use_zeitwerk]
145
224
 
@@ -153,9 +232,9 @@ module Bridgetown
153
232
  end
154
233
  end
155
234
 
156
- # Public: Setup the plugin search path
235
+ # Expands the path(s) of the plugins_dir config value
157
236
  #
158
- # Returns an Array of plugin search paths
237
+ # @return [Array<String>] one or more plugin search paths
159
238
  def plugins_path
160
239
  if site.config["plugins_dir"].eql? Bridgetown::Configuration::DEFAULTS["plugins_dir"]
161
240
  [site.in_root_dir(site.config["plugins_dir"])]
@@ -22,19 +22,16 @@ module Bridgetown
22
22
  # Start up the Roda Rack application and the Zeitwerk autoloaders. Ensure the
23
23
  # Roda app is provided the preloaded Bridgetown site configuration. Handle
24
24
  # any uncaught Roda errors.
25
- #
26
- # @param [Bridgetown::Rack::Roda] optional, defaults to the `RodaApp` constant
27
- def self.boot(roda_app = nil)
25
+ def self.boot(*)
28
26
  self.loaders_manager =
29
27
  Bridgetown::Utils::LoadersManager.new(Bridgetown::Current.preloaded_configuration)
28
+ Bridgetown::Current.preloaded_configuration.run_initializers! context: :server
30
29
  autoload_server_folder
31
- (roda_app || RodaApp).opts[:bridgetown_preloaded_config] =
32
- Bridgetown::Current.preloaded_configuration
33
30
  rescue Roda::RodaError => e
34
31
  if e.message.include?("sessions plugin :secret option")
35
32
  raise Bridgetown::Errors::InvalidConfigurationError,
36
33
  "The Roda sessions plugin can't find a valid secret. Run `bin/bridgetown secret' " \
37
- "and put the key in a ENV var you can use to configure the session in `roda_app.rb'"
34
+ "and put the key in a ENV var you can use to configure the session in the Roda app"
38
35
  end
39
36
 
40
37
  raise e
@@ -45,7 +42,6 @@ module Bridgetown
45
42
  root: Bridgetown::Current.preloaded_configuration.root_dir
46
43
  )
47
44
  server_folder = File.join(root, "server")
48
- # Bridgetown::Current.preloaded_configuration.autoload_paths << server_folder
49
45
 
50
46
  Bridgetown::Hooks.register_one(
51
47
  :loader, :post_setup, reloadable: false
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "logger"
4
+ require "bridgetown-core/log_writer"
4
5
 
5
6
  module Bridgetown
6
7
  module Rack