bridgetown-core 1.0.0.alpha10 → 1.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/bin/bridgetown +6 -1
  4. data/bridgetown-core.gemspec +3 -3
  5. data/lib/bridgetown-core/collection.rb +6 -6
  6. data/lib/bridgetown-core/commands/base.rb +18 -18
  7. data/lib/bridgetown-core/commands/build.rb +1 -1
  8. data/lib/bridgetown-core/commands/clean.rb +2 -2
  9. data/lib/bridgetown-core/commands/console.rb +1 -0
  10. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +27 -0
  11. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +216 -0
  12. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +47 -0
  13. data/lib/bridgetown-core/commands/esbuild/setup.rb +4 -0
  14. data/lib/bridgetown-core/commands/esbuild/update.rb +4 -0
  15. data/lib/bridgetown-core/commands/esbuild.rb +83 -0
  16. data/lib/bridgetown-core/commands/new.rb +80 -10
  17. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +1 -1
  18. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  19. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +1 -1
  20. data/lib/bridgetown-core/commands/webpack.rb +3 -3
  21. data/lib/bridgetown-core/component.rb +9 -3
  22. data/lib/bridgetown-core/concerns/site/configurable.rb +6 -0
  23. data/lib/bridgetown-core/concerns/site/content.rb +4 -4
  24. data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
  25. data/lib/bridgetown-core/concerns/site/processable.rb +23 -4
  26. data/lib/bridgetown-core/concerns/site/renderable.rb +27 -16
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
  28. data/lib/bridgetown-core/concerns/transformable.rb +62 -0
  29. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  30. data/lib/bridgetown-core/configurations/purgecss.rb +2 -1
  31. data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
  32. data/lib/bridgetown-core/configurations/stimulus.rb +41 -12
  33. data/lib/bridgetown-core/configurations/tailwindcss/css_imports.css +5 -0
  34. data/lib/bridgetown-core/configurations/tailwindcss.rb +31 -2
  35. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +48 -0
  36. data/lib/bridgetown-core/configurations/turbo.rb +15 -5
  37. data/lib/bridgetown-core/configurations/vercel/vercel.json +45 -0
  38. data/lib/bridgetown-core/configurations/vercel/vercel_url.rb +12 -0
  39. data/lib/bridgetown-core/configurations/vercel.rb +4 -0
  40. data/lib/bridgetown-core/converters/erb_templates.rb +6 -8
  41. data/lib/bridgetown-core/converters/ruby_templates.rb +1 -1
  42. data/lib/bridgetown-core/converters/serbea_templates.rb +5 -8
  43. data/lib/bridgetown-core/drops/drop.rb +1 -1
  44. data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
  45. data/lib/bridgetown-core/errors.rb +21 -0
  46. data/lib/bridgetown-core/generated_page.rb +81 -17
  47. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
  48. data/lib/bridgetown-core/helpers.rb +3 -2
  49. data/lib/bridgetown-core/hooks.rb +51 -20
  50. data/lib/bridgetown-core/model/base.rb +24 -1
  51. data/lib/bridgetown-core/model/origin.rb +4 -6
  52. data/lib/bridgetown-core/model/repo_origin.rb +48 -0
  53. data/lib/bridgetown-core/rack/boot.rb +5 -9
  54. data/lib/bridgetown-core/rack/roda.rb +4 -5
  55. data/lib/bridgetown-core/rack/routes.rb +44 -10
  56. data/lib/bridgetown-core/rack/static_indexes.rb +2 -0
  57. data/lib/bridgetown-core/resource/base.rb +3 -1
  58. data/lib/bridgetown-core/resource/transformer.rb +21 -85
  59. data/lib/bridgetown-core/ruby_template_view.rb +11 -0
  60. data/lib/bridgetown-core/site.rb +5 -0
  61. data/lib/bridgetown-core/tags/{webpack_path.rb → asset_path.rb} +7 -9
  62. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -1
  63. data/lib/bridgetown-core/utils/loaders_manager.rb +6 -0
  64. data/lib/bridgetown-core/utils.rb +88 -30
  65. data/lib/bridgetown-core/version.rb +1 -1
  66. data/lib/bridgetown-core/watcher.rb +74 -70
  67. data/lib/bridgetown-core.rb +2 -1
  68. data/lib/site_template/Gemfile.erb +17 -11
  69. data/lib/site_template/README.md +2 -2
  70. data/lib/site_template/{Rakefile → Rakefile.erb} +15 -0
  71. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.erb +11 -0
  72. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.rb +5 -0
  73. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +15 -0
  74. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +7 -0
  75. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +7 -0
  76. data/lib/site_template/TEMPLATES/erb/_partials/_footer.erb +3 -0
  77. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +10 -0
  78. data/lib/site_template/{src → TEMPLATES/liquid}/_components/footer.liquid +0 -0
  79. data/lib/site_template/TEMPLATES/liquid/_components/head.liquid +10 -0
  80. data/lib/site_template/TEMPLATES/liquid/_components/navbar.liquid +11 -0
  81. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/default.liquid +2 -2
  82. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/page.liquid +0 -0
  83. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/post.liquid +0 -0
  84. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.rb +5 -0
  85. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.serb +11 -0
  86. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +15 -0
  87. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +7 -0
  88. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +7 -0
  89. data/lib/site_template/TEMPLATES/serbea/_partials/_footer.serb +3 -0
  90. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +10 -0
  91. data/lib/site_template/frontend/javascript/index.js.erb +7 -3
  92. data/lib/site_template/frontend/styles/index.css +71 -6
  93. data/lib/site_template/package.json.erb +24 -9
  94. data/lib/site_template/ruby-version.erb +1 -0
  95. data/lib/site_template/server/roda_app.rb +5 -3
  96. data/lib/site_template/server/routes/hello.rb.sample +1 -1
  97. data/lib/site_template/src/images/logo.svg +91 -0
  98. data/lib/site_template/src/index.md.erb +22 -0
  99. data/lib/site_template/src/posts.md.erb +28 -0
  100. metadata +57 -23
  101. data/lib/bridgetown-core/configurations/swup.rb +0 -37
  102. data/lib/bridgetown-core/renderer.rb +0 -169
  103. data/lib/site_template/frontend/styles/index.scss +0 -17
  104. data/lib/site_template/src/_components/head.liquid +0 -10
  105. data/lib/site_template/src/_components/navbar.liquid +0 -5
  106. data/lib/site_template/src/_layouts/home.liquid +0 -7
  107. data/lib/site_template/src/images/.keep +0 -1
  108. data/lib/site_template/src/index.md +0 -7
  109. data/lib/site_template/src/posts.md +0 -14
@@ -17,34 +17,59 @@ module Bridgetown
17
17
 
18
18
  DEFAULT_PRIORITY = 20
19
19
 
20
- # compatibility layer for octopress-hooks users
21
20
  PRIORITY_MAP = {
22
21
  low: 10,
23
22
  normal: 20,
24
23
  high: 30,
25
24
  }.freeze
26
25
 
27
- # initial empty hooks
28
26
  @registry = {}
29
27
 
30
28
  NotAvailable = Class.new(RuntimeError)
31
29
  Uncallable = Class.new(RuntimeError)
32
30
 
33
- # Ensure the priority is a Fixnum
34
31
  def self.priority_value(priority)
35
32
  return priority if priority.is_a?(Integer)
36
33
 
37
34
  PRIORITY_MAP[priority] || DEFAULT_PRIORITY
38
35
  end
39
36
 
40
- # register hook(s) to be called later
37
+ # Sort registered hooks according to priority and load order
38
+ #
39
+ # @param hooks [Array<HookRegistration>]
40
+ def self.prioritized_hooks(hooks)
41
+ grouped_hooks = hooks.group_by(&:priority)
42
+ grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
43
+ end
44
+
45
+ # Register one or more hooks which may be triggered later for a particular event
46
+ #
47
+ # @param owners [Symbol, Array<Symbol>] name of the owner (`:site`, `:resource`, etc.)
48
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
49
+ # @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
50
+ # Default is normal (20)
51
+ # @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
52
+ # Default is true.
53
+ # @yield the block will be called when the event is triggered. Typically it receives at
54
+ # least one argument.
55
+ # @yieldparam obj the object which triggered the event hook
41
56
  def self.register(owners, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
42
57
  Array(owners).each do |owner|
43
58
  register_one(owner, event, priority: priority, reloadable: reloadable, &block)
44
59
  end
45
60
  end
46
61
 
47
- # register a single hook to be called later
62
+ # Register a hook which may be triggered later for a particular event
63
+ #
64
+ # @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
65
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
66
+ # @param priority [Integer, Symbol] either `:low`, `:normal`, or `:high`, or an integer.
67
+ # Default is normal (20)
68
+ # @param reloadable [Boolean] whether the hook should be removed prior to a site reload.
69
+ # Default is true.
70
+ # @yield the block will be called when the event is triggered. Typically it receives at
71
+ # least one argument.
72
+ # @yieldparam obj the object which triggered the event hook
48
73
  def self.register_one(owner, event, priority: DEFAULT_PRIORITY, reloadable: true, &block)
49
74
  @registry[owner] ||= []
50
75
 
@@ -68,10 +93,28 @@ module Bridgetown
68
93
  block
69
94
  end
70
95
 
71
- def self.remove_hook(owner, _event, block)
96
+ # Delete a previously-registered hook
97
+ #
98
+ # @param owners [Symbol] name of the owner (`:site`, `:resource`, etc.)
99
+ # @param block [Proc] the exact block used originally to register the hook
100
+ def self.remove_hook(owner, block)
72
101
  @registry[owner].delete_if { |item| item.block == block }
73
102
  end
74
103
 
104
+ # Clear all hooks marked as reloadable from the registry
105
+ def self.clear_reloadable_hooks
106
+ Bridgetown.logger.debug("Clearing reloadable hooks")
107
+
108
+ @registry.each_value do |hooks|
109
+ hooks.delete_if(&:reloadable)
110
+ end
111
+ end
112
+
113
+ # Trigger all registered hooks for a particular owner and event.
114
+ # Any arguments after the initial two will be directly passed along to the hooks.
115
+ #
116
+ # @param owner [Symbol] name of the owner (`:site`, `:resource`, etc.)
117
+ # @param event [Symbol] name of the event (`:pre_read`, `:post_render`, etc.)
75
118
  def self.trigger(owner, event, *args) # rubocop:disable Metrics/CyclomaticComplexity
76
119
  # proceed only if there are hooks to call
77
120
  hooks = @registry[owner]&.select { |item| item.event == event }
@@ -79,25 +122,13 @@ module Bridgetown
79
122
 
80
123
  prioritized_hooks(hooks).each do |hook|
81
124
  if ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
82
- hook_info = args[0].respond_to?(:url) ? args[0].relative_path : hook.block
125
+ hook_info = args[0].respond_to?(:relative_path) ? args[0].relative_path : hook.block
83
126
  Bridgetown.logger.debug("Triggering hook:", "#{owner}:#{event} for #{hook_info}")
84
127
  end
85
128
  hook.block.call(*args)
86
129
  end
87
- end
88
-
89
- def self.prioritized_hooks(hooks)
90
- # sort hooks according to priority and load order
91
- grouped_hooks = hooks.group_by(&:priority)
92
- grouped_hooks.keys.sort.reverse.map { |priority| grouped_hooks[priority] }.flatten
93
- end
94
130
 
95
- def self.clear_reloadable_hooks
96
- Bridgetown.logger.debug("Clearing reloadable hooks")
97
-
98
- @registry.each_value do |hooks|
99
- hooks.delete_if(&:reloadable)
100
- end
131
+ true
101
132
  end
102
133
  end
103
134
  end
@@ -69,8 +69,23 @@ module Bridgetown
69
69
  attributes[:_origin_]
70
70
  end
71
71
 
72
+ def origin=(new_origin)
73
+ attributes[:_id_] = new_origin.id
74
+ attributes[:_origin_] = new_origin
75
+ end
76
+
72
77
  def persisted?
73
- id && origin.exists?
78
+ (id && origin&.exists?) == true
79
+ end
80
+
81
+ def save
82
+ unless origin.respond_to?(:write)
83
+ raise "`#{origin.class}' doesn't allow writing of model objects"
84
+ end
85
+
86
+ run_callbacks :save do
87
+ origin.write(self)
88
+ end
74
89
  end
75
90
 
76
91
  # @return [Bridgetown::Resource::Base]
@@ -100,11 +115,19 @@ module Bridgetown
100
115
  attributes[:_collection_]
101
116
  end
102
117
 
118
+ def collection=(new_collection)
119
+ attributes[:_collection_] = new_collection
120
+ end
121
+
103
122
  # @return [String]
104
123
  def content
105
124
  attributes[:_content_]
106
125
  end
107
126
 
127
+ def content=(new_content)
128
+ attributes[:_content_] = new_content
129
+ end
130
+
108
131
  def attributes
109
132
  @attributes ||= HashWithDotAccess::Hash.new
110
133
  end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # See bottom of file for specific origin requires...
4
-
5
3
  module Bridgetown
6
4
  module Model
7
5
  # Abstract Superclass
8
6
  class Origin
9
7
  extend ActiveSupport::DescendantsTracker
10
8
 
9
+ EAGER_LOAD_DESCENDANTS = %i(BuilderOrigin RepoOrigin PluginOrigin).freeze
10
+
11
11
  # @return [String]
12
12
  attr_accessor :id
13
13
 
@@ -42,9 +42,7 @@ module Bridgetown
42
42
  raise "Implement #exists? in a subclass of Bridgetown::Model::Origin"
43
43
  end
44
44
  end
45
+
46
+ Origin::EAGER_LOAD_DESCENDANTS.each { const_get _1 }
45
47
  end
46
48
  end
47
-
48
- require "bridgetown-core/model/builder_origin"
49
- require "bridgetown-core/model/repo_origin"
50
- require "bridgetown-core/model/plugin_origin"
@@ -25,6 +25,18 @@ module Bridgetown
25
25
  def data_file_extensions
26
26
  %w(.yaml .yml .json .csv .tsv .rb).freeze
27
27
  end
28
+
29
+ # Initializes a new repo object using a collection and a relative source path.
30
+ # You'll need to use this when you want to create and save a model to the source.
31
+ #
32
+ # @param collection [Bridgetown::Collection, String, Symbol] either a collection
33
+ # label or Collection object
34
+ # @param relative_path [Pathname, String] the source path of the file to save
35
+ def new_with_collection_path(collection, relative_path)
36
+ collection = collection.label if collection.is_a?(Bridgetown::Collection)
37
+
38
+ new("repo://#{collection}.collection/#{relative_path}")
39
+ end
28
40
  end
29
41
 
30
42
  def read
@@ -46,6 +58,23 @@ module Bridgetown
46
58
  @data
47
59
  end
48
60
 
61
+ def write(model)
62
+ if File.exist?(original_path) && !Bridgetown::Utils.has_yaml_header?(original_path)
63
+ raise Bridgetown::Errors::InvalidYAMLFrontMatterError,
64
+ "Only existing files containing YAML front matter can be overwritten by the model"
65
+ end
66
+
67
+ contents = "#{front_matter_to_yaml(model)}---\n\n#{model.content}"
68
+
69
+ # Create folders if necessary
70
+ dir = File.dirname(original_path)
71
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
72
+
73
+ File.write(original_path, contents)
74
+
75
+ true
76
+ end
77
+
49
78
  def url
50
79
  @url ||= URI.parse(id)
51
80
  end
@@ -121,6 +150,25 @@ module Bridgetown
121
150
  raise error
122
151
  end
123
152
  end
153
+
154
+ def front_matter_to_yaml(model)
155
+ data = model.data_attributes.to_h
156
+ data = data.deep_merge(data) do |_, _, v|
157
+ case v
158
+ when DateTime
159
+ v.to_time
160
+ when Symbol
161
+ v.to_s
162
+ else
163
+ v
164
+ end
165
+ end
166
+ data.each do |k, v|
167
+ data.delete(k) if v.nil?
168
+ end
169
+
170
+ data.to_yaml
171
+ end
124
172
  end
125
173
  end
126
174
  end
@@ -51,15 +51,11 @@ module Bridgetown
51
51
  loader.do_not_eager_load(File.join(server_folder, "roda_app.rb"))
52
52
 
53
53
  unless ENV["BRIDGETOWN_ENV"] == "production"
54
- begin
55
- Listen.to(server_folder) do |_modified, _added, _removed|
56
- loader.reload
57
- loader.eager_load
58
- Bridgetown::Rack::Routes.reload_subclasses
59
- end.start
60
- # interrupt isn't handled well by the listener
61
- rescue ThreadError # rubocop:disable Lint/SuppressedException
62
- end
54
+ Listen.to(server_folder) do |_modified, _added, _removed|
55
+ loader.reload
56
+ loader.eager_load
57
+ Bridgetown::Rack::Routes.reload_subclasses
58
+ end.start
63
59
  end
64
60
  end
65
61
 
@@ -23,6 +23,7 @@ module Bridgetown
23
23
  plugin :json
24
24
  plugin :json_parser
25
25
  plugin :cookies
26
+ plugin :streaming
26
27
  plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
27
28
  plugin :not_found do
28
29
  output_folder = Bridgetown::Current.preloaded_configuration.destination
@@ -39,7 +40,7 @@ module Bridgetown
39
40
  "500 Internal Server Error"
40
41
  end
41
42
 
42
- def _roda_run_main_route(r) # rubocop:disable Naming/MethodParameterName
43
+ before do
43
44
  if self.class.opts[:bridgetown_site]
44
45
  # The site had previously been initialized via the bridgetown_ssr plugin
45
46
  Bridgetown::Current.site ||= self.class.opts[:bridgetown_site]
@@ -47,14 +48,12 @@ module Bridgetown
47
48
  Bridgetown::Current.preloaded_configuration ||=
48
49
  self.class.opts[:bridgetown_preloaded_config]
49
50
 
50
- r.public
51
+ request.public
51
52
 
52
- r.root do
53
+ request.root do
53
54
  output_folder = Bridgetown::Current.preloaded_configuration.destination
54
55
  File.read(File.join(output_folder, "index.html"))
55
56
  end
56
-
57
- super
58
57
  end
59
58
 
60
59
  # Helper shorthand for Bridgetown::Current.site
@@ -2,6 +2,12 @@
2
2
 
3
3
  module Bridgetown
4
4
  module Rack
5
+ @interrupted = false
6
+
7
+ class << self
8
+ attr_accessor :interrupted
9
+ end
10
+
5
11
  class Routes
6
12
  class << self
7
13
  attr_accessor :tracked_subclasses, :router_block
@@ -37,7 +43,7 @@ module Bridgetown
37
43
  def start!(roda_app)
38
44
  if Bridgetown.env.development? &&
39
45
  !Bridgetown::Current.preloaded_configuration.skip_live_reload
40
- setup_live_reload roda_app.request
46
+ setup_live_reload roda_app
41
47
  end
42
48
 
43
49
  Bridgetown::Rack::Routes.tracked_subclasses&.each_value do |klass|
@@ -51,15 +57,31 @@ module Bridgetown
51
57
  nil
52
58
  end
53
59
 
54
- def setup_live_reload(request)
55
- request.get "_bridgetown/live_reload" do
56
- {
57
- last_mod: File.stat(
58
- File.join(Bridgetown::Current.preloaded_configuration.destination, "index.html")
59
- ).mtime.to_i,
60
- }
61
- rescue StandardError => e
62
- { last_mod: 0, error: e.message }
60
+ def setup_live_reload(app) # rubocop:disable Metrics/AbcSize
61
+ sleep_interval = 0.2
62
+ file_to_check = File.join(app.class.opts[:bridgetown_preloaded_config].destination,
63
+ "index.html")
64
+
65
+ app.request.get "_bridgetown/live_reload" do
66
+ app.response["Content-Type"] = "text/event-stream"
67
+
68
+ @_mod = File.exist?(file_to_check) ? File.stat(file_to_check).mtime.to_i : 0
69
+ app.stream async: true do |out|
70
+ # 5 second intervals so Puma's threads aren't all exausted
71
+ (5 / sleep_interval).to_i.times do
72
+ break if Bridgetown::Rack.interrupted
73
+
74
+ new_mod = File.exist?(file_to_check) ? File.stat(file_to_check).mtime.to_i : 0
75
+ if @_mod < new_mod
76
+ out << "data: reloaded!\n\n"
77
+ break
78
+ else
79
+ out << "data: #{new_mod}\n\n"
80
+ end
81
+
82
+ sleep sleep_interval
83
+ end
84
+ end
63
85
  end
64
86
  end
65
87
  end
@@ -86,3 +108,15 @@ module Bridgetown
86
108
  end
87
109
  end
88
110
  end
111
+
112
+ if Bridgetown.env.development? &&
113
+ !Bridgetown::Current.preloaded_configuration.skip_live_reload
114
+ Puma::Launcher.class_eval do
115
+ alias_method :_old_stop, :stop
116
+ def stop
117
+ Bridgetown::Rack.interrupted = true
118
+
119
+ _old_stop
120
+ end
121
+ end
122
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "roda/plugins/public"
4
4
 
5
+ # NOTE: once this upstreamed PR is merged in we can remove this file.
6
+ # https://github.com/jeremyevans/roda/pull/215
5
7
  Roda::RodaPlugins::Public::RequestMethods.module_eval do
6
8
  SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact) # rubocop:disable Lint/ConstantDefinitionInBlock
7
9
  def public_path_segments(path) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -30,7 +30,7 @@ module Bridgetown
30
30
  def initialize(model:)
31
31
  @model = model
32
32
  @site = model.site
33
- @data = front_matter_defaults
33
+ @data = collection.data? ? HashWithDotAccess::Hash.new : front_matter_defaults
34
34
 
35
35
  trigger_hooks :post_init
36
36
  end
@@ -284,12 +284,14 @@ module Bridgetown
284
284
  collection.resources[pos + 1] if pos && pos < collection.resources.length - 1
285
285
  end
286
286
  alias_method :next_doc, :next_resource
287
+ alias_method :next, :next_resource
287
288
 
288
289
  def previous_resource
289
290
  pos = collection.resources.index { |item| item.equal?(self) }
290
291
  collection.resources[pos - 1] if pos&.positive?
291
292
  end
292
293
  alias_method :previous_doc, :previous_resource
294
+ alias_method :previous, :previous_resource
293
295
 
294
296
  private
295
297
 
@@ -3,6 +3,8 @@
3
3
  module Bridgetown
4
4
  module Resource
5
5
  class Transformer
6
+ include Transformable
7
+
6
8
  # @return [Array<Hash>]
7
9
  attr_reader :conversions
8
10
 
@@ -30,10 +32,18 @@ module Bridgetown
30
32
  permalink_ext || output_ext
31
33
  end
32
34
 
33
- def process!
35
+ def process! # rubocop:disable Metrics/AbcSize
34
36
  Bridgetown.logger.debug "Transforming:", resource.relative_path
35
37
  resource.around_hook :render do
36
- run_conversions
38
+ resource.content = transform_content(resource) do |converter, index, output|
39
+ conversions[index] = {
40
+ type: :content,
41
+ converter: converter,
42
+ output: Bridgetown.env.production? ? nil : output,
43
+ output_ext: conversions[index]&.dig(:output_ext) ||
44
+ converter.output_ext(resource.extname),
45
+ }
46
+ end
37
47
  resource.place_in_layout? ? place_into_layouts : resource.output = resource.content.dup
38
48
  end
39
49
  end
@@ -85,95 +95,21 @@ module Bridgetown
85
95
  .fetch(:output_ext)
86
96
  end
87
97
 
88
- # @return [Array<Bridgetown::Layout>]
89
- def validated_layouts
90
- layout = site.layouts[resource.data.layout]
91
- warn_on_missing_layout layout, resource.data.layout
92
-
93
- layout_list = Set.new([layout])
94
- while layout
95
- layout_name = layout.data.layout
96
- layout = site.layouts[layout_name]
97
- warn_on_missing_layout layout, layout_name
98
-
99
- layout_list << layout
100
- end
101
-
102
- layout_list.to_a.compact
103
- end
104
-
105
- def warn_on_missing_layout(layout, layout_name)
106
- return unless layout.nil? && layout_name
107
-
108
- Bridgetown.logger.warn(
109
- "Build Warning:",
110
- "Layout '#{layout_name}' requested via #{resource.relative_path} does not exist."
111
- )
112
- end
113
-
114
- ### Transformation Actions
115
-
116
- def run_conversions # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
117
- input = resource.content.to_s
118
-
119
- # @param content [String]
120
- # @param converter [Bridgetown::Converter]
121
- resource.content = converters.each_with_index.inject(input) do |content, (converter, index)|
122
- output = if converter.method(:convert).arity == 1
123
- converter.convert content
124
- else
125
- converter.convert content, resource
126
- end
127
- conversions[index] = {
128
- type: :content,
129
- converter: converter,
130
- output: Bridgetown.env.production? ? nil : output,
131
- output_ext: conversions[index]&.dig(:output_ext) ||
132
- converter.output_ext(resource.extname),
133
- }
134
- output.html_safe
135
- rescue StandardError => e
136
- Bridgetown.logger.error "Conversion error:",
137
- "#{converter.class} encountered an error while "\
138
- "converting `#{resource.relative_path}'"
139
- raise e
140
- end
141
- end
142
-
143
98
  def place_into_layouts
144
99
  Bridgetown.logger.debug "Placing in Layouts:", resource.relative_path
145
100
  output = resource.content.dup
146
- validated_layouts.each do |layout|
147
- output = run_layout_conversions layout, output
101
+ site.validated_layouts_for(resource, resource.data.layout).each do |layout|
102
+ output = transform_with_layout(layout, output, resource) do |converter, layout_output|
103
+ conversions << {
104
+ type: :layout,
105
+ layout: layout,
106
+ converter: converter,
107
+ output: Bridgetown.env.production? ? nil : layout_output,
108
+ }
109
+ end
148
110
  end
149
111
  resource.output = output
150
112
  end
151
-
152
- def run_layout_conversions(layout, output)
153
- layout_converters = site.matched_converters_for_convertible(layout)
154
- layout_input = layout.content.dup
155
-
156
- layout_converters.inject(layout_input) do |content, converter|
157
- next(content) unless [2, -2].include?(converter.method(:convert).arity) # rubocop:disable Performance/CollectionLiteralInLoop
158
-
159
- layout.current_document = resource
160
- layout.current_document_output = output
161
- layout_output = converter.convert content, layout
162
-
163
- conversions << {
164
- type: :layout,
165
- layout: layout,
166
- converter: converter,
167
- output: Bridgetown.env.production? ? nil : layout_output,
168
- }
169
- layout_output
170
- rescue StandardError => e
171
- Bridgetown.logger.error "Conversion error:",
172
- "#{converter.class} encountered an error while "\
173
- "converting `#{resource.relative_path}'"
174
- raise e
175
- end
176
- end
177
113
  end
178
114
  end
179
115
  end
@@ -73,6 +73,10 @@ module Bridgetown
73
73
  helpers.respond_to?(method_name.to_sym, include_private) || super
74
74
  end
75
75
 
76
+ def inspect
77
+ "#<#{self.class} layout=#{layout&.label} resource=#{resource.relative_path}>"
78
+ end
79
+
76
80
  private
77
81
 
78
82
  def _render_statement(component, options)
@@ -103,5 +107,12 @@ module Bridgetown
103
107
  strict_variables: site.config["liquid"]["strict_variables"],
104
108
  }
105
109
  end
110
+
111
+ def _partial_path(partial_name, ext)
112
+ partial_name = partial_name.split("/").tap { _1.last.prepend("_") }.join("/")
113
+
114
+ # TODO: see if there's a workaround for this to speed up performance
115
+ site.in_source_dir(site.config[:partials_dir], "#{partial_name}.#{ext}")
116
+ end
106
117
  end
107
118
  end
@@ -53,6 +53,7 @@ module Bridgetown
53
53
  @reader = Reader.new(self)
54
54
  @liquid_renderer = LiquidRenderer.new(self)
55
55
 
56
+ Bridgetown::Cache.base_cache["site_tmp"] = {}.with_dot_access
56
57
  ensure_not_in_dest
57
58
 
58
59
  Bridgetown::Current.site = self
@@ -74,6 +75,10 @@ module Bridgetown
74
75
  end
75
76
  end
76
77
 
78
+ def tmp_cache
79
+ Bridgetown::Cache.base_cache["site_tmp"]
80
+ end
81
+
77
82
  def inspect
78
83
  "#<Bridgetown::Site #{metadata.inspect.delete_prefix("{").delete_suffix("}")}>"
79
84
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Bridgetown
4
4
  module Tags
5
- # A helper class to help find the path to webpack asset inside of a webpack
5
+ # A helper class to help find the path to assets inside of a webpack or esbuild
6
6
  # manifest file.
7
- class WebpackPath < Liquid::Tag
7
+ class AssetPath < Liquid::Tag
8
8
  # @param tag_name [String] Name of the tag
9
9
  # @param asset_type [String] The type of asset to parse (js, css)
10
10
  # @param options [Hash] An options hash
@@ -17,23 +17,21 @@ module Bridgetown
17
17
  @asset_type = asset_type.strip
18
18
  end
19
19
 
20
- # Render an asset path based on the Webpack manifest file
20
+ # Render an asset path based on the frontend manifest file
21
21
  # @param context [Liquid::Context] Context passed to the tag
22
22
  #
23
23
  # @return [String] Returns "MISSING_WEBPACK_MANIFEST" if the manifest
24
- # file isn't found
24
+ # file isn't found
25
25
  # @return [String] Returns a blank string if the asset isn't found
26
26
  # @return [String] Returns the path to the asset if no issues parsing
27
- #
28
- # @raise [WebpackAssetError] if unable to find css or js in the manifest
29
- # file
30
27
  def render(context)
31
28
  @context = context
32
29
  site = context.registers[:site]
33
- Bridgetown::Utils.parse_webpack_manifest_file(site, @asset_type) || ""
30
+ Bridgetown::Utils.parse_frontend_manifest_file(site, @asset_type) || ""
34
31
  end
35
32
  end
36
33
  end
37
34
  end
38
35
 
39
- Liquid::Template.register_tag("webpack_path", Bridgetown::Tags::WebpackPath)
36
+ Liquid::Template.register_tag("asset_path", Bridgetown::Tags::AssetPath)
37
+ Liquid::Template.register_tag("webpack_path", Bridgetown::Tags::AssetPath)
@@ -15,7 +15,8 @@ namespace :frontend do
15
15
  run_process "Frontend", :yellow, "bundle exec bridgetown frontend:dev"
16
16
  end
17
17
  if sidecar
18
- sleep 4 # give Webpack time to boot before returning control to the start command
18
+ # give FE bundler time to boot before returning control to the start command
19
+ sleep Bridgetown::Utils.frontend_bundler_type == :esbuild ? 3 : 4
19
20
  else
20
21
  trap("INT") do
21
22
  Bridgetown::Utils::Aux.kill_processes
@@ -33,6 +33,12 @@ module Bridgetown
33
33
  return
34
34
  end
35
35
 
36
+ if defined?(ActiveSupport::RubyFeatures) && ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
37
+ ActiveSupport::DescendantsTracker.clear([value.superclass])
38
+ return
39
+ end
40
+
41
+ # TODO: this could probably be refactored to work like the above
36
42
  ActiveSupport::DescendantsTracker.class_variable_get(
37
43
  :@@direct_descendants
38
44
  )[value.superclass]&.reject! { _1 == value }