bridgetown-core 1.3.4 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -1
  3. data/bin/bridgetown +0 -6
  4. data/bin/bt +6 -0
  5. data/bridgetown-core.gemspec +10 -7
  6. data/lib/bridgetown-core/cleaner.rb +1 -1
  7. data/lib/bridgetown-core/collection.rb +37 -18
  8. data/lib/bridgetown-core/commands/build.rb +9 -22
  9. data/lib/bridgetown-core/commands/concerns/actions.rb +15 -8
  10. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +1 -1
  11. data/lib/bridgetown-core/commands/console.rb +3 -0
  12. data/lib/bridgetown-core/commands/doctor.rb +2 -4
  13. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +1 -1
  14. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +1 -1
  15. data/lib/bridgetown-core/commands/esbuild/update.rb +2 -2
  16. data/lib/bridgetown-core/commands/esbuild.rb +3 -3
  17. data/lib/bridgetown-core/commands/new.rb +32 -39
  18. data/lib/bridgetown-core/commands/start.rb +76 -60
  19. data/lib/bridgetown-core/component.rb +19 -13
  20. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  21. data/lib/bridgetown-core/concerns/prioritizable.rb +11 -1
  22. data/lib/bridgetown-core/concerns/site/configurable.rb +7 -18
  23. data/lib/bridgetown-core/concerns/site/content.rb +10 -9
  24. data/lib/bridgetown-core/concerns/site/extensible.rb +4 -6
  25. data/lib/bridgetown-core/concerns/site/fast_refreshable.rb +161 -0
  26. data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
  27. data/lib/bridgetown-core/concerns/site/renderable.rb +31 -7
  28. data/lib/bridgetown-core/concerns/site/ssr.rb +23 -9
  29. data/lib/bridgetown-core/concerns/site/writable.rb +1 -1
  30. data/lib/bridgetown-core/concerns/transformable.rb +3 -5
  31. data/lib/bridgetown-core/configuration/configuration_dsl.rb +23 -14
  32. data/lib/bridgetown-core/configuration.rb +67 -114
  33. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -2
  34. data/lib/bridgetown-core/configurations/cypress/cypress_tasks +2 -2
  35. data/lib/bridgetown-core/configurations/cypress.rb +1 -1
  36. data/lib/bridgetown-core/configurations/gh-pages/gh-pages.yml +3 -3
  37. data/lib/bridgetown-core/configurations/is-land.rb +1 -1
  38. data/lib/bridgetown-core/configurations/lit.rb +1 -1
  39. data/lib/bridgetown-core/configurations/open-props.rb +1 -1
  40. data/lib/bridgetown-core/configurations/purgecss.rb +1 -1
  41. data/lib/bridgetown-core/configurations/ruby2js.rb +1 -1
  42. data/lib/bridgetown-core/configurations/shoelace.rb +8 -20
  43. data/lib/bridgetown-core/configurations/stimulus.rb +17 -36
  44. data/lib/bridgetown-core/configurations/turbo.rb +1 -2
  45. data/lib/bridgetown-core/converter.rb +38 -10
  46. data/lib/bridgetown-core/converters/erb_templates.rb +9 -26
  47. data/lib/bridgetown-core/converters/identity.rb +1 -1
  48. data/lib/bridgetown-core/converters/liquid_templates.rb +2 -20
  49. data/lib/bridgetown-core/converters/ruby_templates.rb +61 -3
  50. data/lib/bridgetown-core/converters/serbea_templates.rb +12 -24
  51. data/lib/bridgetown-core/current.rb +19 -17
  52. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  53. data/lib/bridgetown-core/drops/drop.rb +3 -3
  54. data/lib/bridgetown-core/drops/relations_drop.rb +3 -2
  55. data/lib/bridgetown-core/drops/site_drop.rb +0 -5
  56. data/lib/bridgetown-core/entry_filter.rb +4 -6
  57. data/lib/bridgetown-core/errors.rb +2 -2
  58. data/lib/bridgetown-core/filters/from_liquid.rb +6 -10
  59. data/lib/bridgetown-core/filters/localization_filters.rb +1 -1
  60. data/lib/bridgetown-core/filters/translation_filters.rb +2 -2
  61. data/lib/bridgetown-core/filters.rb +3 -3
  62. data/lib/bridgetown-core/front_matter/defaults.rb +225 -0
  63. data/lib/bridgetown-core/front_matter/importer.rb +34 -0
  64. data/lib/bridgetown-core/front_matter/loaders/base.rb +29 -0
  65. data/lib/bridgetown-core/front_matter/loaders/ruby.rb +113 -0
  66. data/lib/bridgetown-core/front_matter/loaders/yaml.rb +42 -0
  67. data/lib/bridgetown-core/front_matter/loaders.rb +44 -0
  68. data/lib/bridgetown-core/{utils/ruby_front_matter.rb → front_matter/ruby.rb} +5 -5
  69. data/lib/bridgetown-core/front_matter.rb +11 -0
  70. data/lib/bridgetown-core/generated_page.rb +71 -31
  71. data/lib/bridgetown-core/generators/prototype_generator.rb +30 -18
  72. data/lib/bridgetown-core/helpers.rb +36 -47
  73. data/lib/bridgetown-core/hooks.rb +5 -5
  74. data/lib/bridgetown-core/inflector.rb +40 -0
  75. data/lib/bridgetown-core/kramdown/parser/gfm.rb +2 -2
  76. data/lib/bridgetown-core/layout.rb +3 -3
  77. data/lib/bridgetown-core/log_adapter.rb +12 -13
  78. data/lib/bridgetown-core/log_writer.rb +4 -4
  79. data/lib/bridgetown-core/model/base.rb +17 -17
  80. data/lib/bridgetown-core/model/builder_origin.rb +5 -3
  81. data/lib/bridgetown-core/model/origin.rb +1 -3
  82. data/lib/bridgetown-core/model/repo_origin.rb +12 -14
  83. data/lib/bridgetown-core/plugin.rb +0 -1
  84. data/lib/bridgetown-core/plugin_manager.rb +38 -84
  85. data/lib/bridgetown-core/rack/boot.rb +0 -15
  86. data/lib/bridgetown-core/rack/routes.rb +30 -90
  87. data/lib/bridgetown-core/reader.rb +6 -4
  88. data/lib/bridgetown-core/readers/layout_reader.rb +2 -2
  89. data/lib/bridgetown-core/readers/plugin_content_reader.rb +2 -2
  90. data/lib/bridgetown-core/resource/base.rb +112 -29
  91. data/lib/bridgetown-core/resource/destination.rb +1 -1
  92. data/lib/bridgetown-core/resource/relations.rb +11 -8
  93. data/lib/bridgetown-core/resource/taxonomy_type.rb +3 -1
  94. data/lib/bridgetown-core/resource/transformer.rb +4 -4
  95. data/lib/bridgetown-core/ruby_template_view.rb +44 -28
  96. data/lib/bridgetown-core/signals.rb +95 -0
  97. data/lib/bridgetown-core/site.rb +22 -4
  98. data/lib/bridgetown-core/slot.rb +5 -5
  99. data/lib/bridgetown-core/static_file.rb +5 -7
  100. data/lib/bridgetown-core/tags/asset_path.rb +11 -2
  101. data/lib/bridgetown-core/tags/find.rb +3 -5
  102. data/lib/bridgetown-core/tags/highlight.rb +3 -3
  103. data/lib/bridgetown-core/tags/post_url.rb +1 -1
  104. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -2
  105. data/lib/bridgetown-core/utils/aux.rb +41 -41
  106. data/lib/bridgetown-core/utils/loaders_manager.rb +2 -21
  107. data/lib/bridgetown-core/utils/ruby_exec.rb +17 -0
  108. data/lib/bridgetown-core/utils.rb +46 -110
  109. data/lib/bridgetown-core/watcher.rb +21 -10
  110. data/lib/bridgetown-core.rb +35 -49
  111. data/lib/roda/plugins/bridgetown_server.rb +54 -12
  112. data/lib/roda/plugins/bridgetown_ssr.rb +13 -2
  113. data/lib/roda/plugins/ssg.rb +72 -0
  114. data/lib/site_template/.gitignore +9 -3
  115. data/lib/site_template/Gemfile.erb +3 -3
  116. data/lib/site_template/README.md +3 -4
  117. data/lib/site_template/Rakefile.erb +2 -15
  118. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +2 -2
  119. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +2 -2
  120. data/lib/site_template/config/initializers.rb +60 -22
  121. data/lib/site_template/config/puma.rb +2 -0
  122. data/lib/site_template/package.json.erb +2 -27
  123. data/lib/site_template/src/index.md.erb +3 -3
  124. data/lib/site_template/src/posts.md.erb +8 -8
  125. data/lib/site_template/tmp/pids/.keep +0 -0
  126. metadata +98 -64
  127. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +0 -12
  128. data/lib/bridgetown-core/commands/webpack/setup.rb +0 -4
  129. data/lib/bridgetown-core/commands/webpack/update.rb +0 -24
  130. data/lib/bridgetown-core/commands/webpack/webpack.config.js +0 -31
  131. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +0 -135
  132. data/lib/bridgetown-core/commands/webpack.rb +0 -82
  133. data/lib/bridgetown-core/concerns/front_matter_importer.rb +0 -52
  134. data/lib/bridgetown-core/concerns/liquid_renderable.rb +0 -30
  135. data/lib/bridgetown-core/core_ext/psych.rb +0 -15
  136. data/lib/bridgetown-core/drops/url_drop.rb +0 -152
  137. data/lib/bridgetown-core/frontmatter_defaults.rb +0 -223
  138. data/lib/bridgetown-core/rack/static_indexes.rb +0 -31
  139. data/lib/bridgetown-core/url.rb +0 -166
  140. data/lib/bridgetown-core/utils/ansi.rb +0 -57
  141. data/lib/bridgetown-core/version.rb +0 -6
  142. data/lib/site_template/bridgetown.config.yml +0 -33
@@ -47,10 +47,10 @@ module Bridgetown
47
47
  Layout.label_for_file(file)
48
48
  end
49
49
 
50
- def within(directory, &block)
50
+ def within(directory, &)
51
51
  return unless File.exist?(directory)
52
52
 
53
- Dir.chdir(directory, &block)
53
+ Dir.chdir(directory, &)
54
54
  end
55
55
  end
56
56
  end
@@ -31,8 +31,8 @@ module Bridgetown
31
31
  dir = File.dirname(path.sub("#{content_dir}/", ""))
32
32
  name = File.basename(path)
33
33
 
34
- @content_files << if Utils.has_yaml_header?(path) || Utils.has_rbfm_header?(path)
35
- site.collections.pages.read_resource(path, manifest: manifest)
34
+ @content_files << if FrontMatter::Loaders.front_matter?(path)
35
+ site.collections.pages.read_resource(path, manifest:)
36
36
  else
37
37
  Bridgetown::StaticFile.new(site, content_dir, "/#{dir}", name)
38
38
  end
@@ -3,15 +3,13 @@
3
3
  module Bridgetown
4
4
  module Resource
5
5
  class Base # rubocop:todo Metrics/ClassLength
6
+ using Bridgetown::Refinements
6
7
  include Comparable
8
+ include Bridgetown::RodaCallable
7
9
  include Bridgetown::Publishable
8
10
  include Bridgetown::LayoutPlaceable
9
- include Bridgetown::LiquidRenderable
10
11
  include Bridgetown::Localizable
11
12
 
12
- # @return [HashWithDotAccess::Hash]
13
- attr_reader :data
14
-
15
13
  # @return [Destination]
16
14
  attr_reader :destination
17
15
 
@@ -24,17 +22,29 @@ module Bridgetown
24
22
  # @return [Array<Bridgetown::Slot>]
25
23
  attr_reader :slots
26
24
 
25
+ # @return [Boolean]
26
+ attr_reader :fast_refresh_order
27
+
27
28
  # @return [String]
28
- attr_accessor :content, :untransformed_content, :output
29
+ attr_accessor :untransformed_content
29
30
 
30
- DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
31
+ attr_writer :content
32
+
33
+ # @return [String]
34
+ attr_accessor :output
35
+
36
+ DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!
31
37
 
32
38
  # @param site [Bridgetown::Site]
33
39
  # @param origin [Bridgetown::Resource::Origin]
34
40
  def initialize(model:)
35
41
  @model = model
36
42
  @site = model.site
37
- @data = collection.data? ? HashWithDotAccess::Hash.new : front_matter_defaults
43
+ @data = Signalize.signal(
44
+ collection.data? ? HashWithDotAccess::Hash.new : front_matter_defaults
45
+ )
46
+ # we track content subscriptions under the hood numerically…cleans up internal issues
47
+ @content_signal = Signalize.signal(0)
38
48
  @slots = []
39
49
 
40
50
  trigger_hooks :post_init
@@ -84,17 +94,35 @@ module Bridgetown
84
94
  #
85
95
  # @return [HashWithDotAccess::Hash]
86
96
  def front_matter_defaults
87
- site.frontmatter_defaults.all(
88
- relative_path.to_s,
89
- collection.label.to_sym
90
- ).with_dot_access
97
+ site.frontmatter_defaults.all(relative_path.to_s, collection.label.to_sym).as_dots
98
+ end
99
+
100
+ # @return [HashWithDotAccess::Hash]
101
+ def data
102
+ @data.value
91
103
  end
92
104
 
93
105
  # Merges new data into the existing data hash.
94
106
  #
95
107
  # @param new_data [HashWithDotAccess::Hash]
96
108
  def data=(new_data)
97
- @data = @data.merge(new_data)
109
+ if site.config.fast_refresh && write?
110
+ # TODO: investigate if this would be better:
111
+ # @data.value = front_matter_defaults
112
+ mark_for_fast_refresh!
113
+ end
114
+
115
+ Signalize.batch do
116
+ @content_signal.value += 1
117
+ @data.value = @data.value.merge(new_data)
118
+ end
119
+ @data.peek
120
+ end
121
+
122
+ # @return [String] the resource content minus its layout
123
+ def content
124
+ @content_signal.value # subscribe for Fast Refresh
125
+ @content
98
126
  end
99
127
 
100
128
  # @return [Bridgetown::Resource::Base]
@@ -119,12 +147,33 @@ module Bridgetown
119
147
  end
120
148
  alias_method :read, :read! # TODO: eventually use the bang version only
121
149
 
122
- def transform!
123
- transformer.process! unless collection.data?
150
+ def transform! # rubocop:todo Metrics/CyclomaticComplexity
151
+ internal_error = nil
152
+ @transform_effect_disposal = Signalize.effect do
153
+ if !@fast_refresh_order && @previously_transformed
154
+ self.content = untransformed_content
155
+ @transformer = nil
156
+ mark_for_fast_refresh! if site.config.fast_refresh && write?
157
+ next
158
+ end
159
+
160
+ transformer.process! unless collection.data?
161
+ slots.clear
162
+ @previously_transformed = true
163
+ rescue StandardError, SyntaxError => e
164
+ internal_error = e
165
+ end
166
+
167
+ raise internal_error if internal_error
124
168
 
125
169
  self
126
170
  end
127
171
 
172
+ # Transforms the resource and returns the full output
173
+ #
174
+ # @return [String]
175
+ def call(*) = transform!.output
176
+
128
177
  def trigger_hooks(hook_name, *args)
129
178
  Bridgetown::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
130
179
  Bridgetown::Hooks.trigger :resources, hook_name, self, *args
@@ -222,13 +271,10 @@ module Bridgetown
222
271
  end
223
272
  alias_method :write?, :requires_destination?
224
273
 
225
- # Write the generated Document file to the destination directory.
226
- #
227
- # dest - The String path to the destination dir.
228
- #
229
- # Returns nothing.
274
+ # Write the generated resource file to the destination directory.
230
275
  def write(_dest = nil)
231
276
  destination.write(output)
277
+ unmark_for_fast_refresh!
232
278
  trigger_hooks(:post_write)
233
279
  end
234
280
 
@@ -245,16 +291,16 @@ module Bridgetown
245
291
 
246
292
  def to_h
247
293
  {
248
- id: id,
249
- absolute_url: absolute_url,
250
- relative_path: relative_path,
251
- relative_url: relative_url,
252
- date: date,
253
- data: data,
254
- taxonomies: taxonomies,
255
- untransformed_content: untransformed_content,
256
- content: content,
257
- output: output,
294
+ id:,
295
+ absolute_url:,
296
+ relative_path:,
297
+ relative_url:,
298
+ date:,
299
+ data:,
300
+ taxonomies:,
301
+ untransformed_content:,
302
+ content:,
303
+ output:,
258
304
  }
259
305
  end
260
306
 
@@ -300,6 +346,42 @@ module Bridgetown
300
346
  alias_method :previous_doc, :previous_resource
301
347
  alias_method :previous, :previous_resource
302
348
 
349
+ def deconstruct_keys(...)
350
+ @data.value.deconstruct_keys(...)
351
+ end
352
+
353
+ def mark_for_fast_refresh!
354
+ @fast_refresh_order = site.fast_refresh_ordering
355
+ site.fast_refresh_ordering += 1
356
+ end
357
+
358
+ def unmark_for_fast_refresh!
359
+ @fast_refresh_order = nil
360
+ end
361
+
362
+ def prepare_for_fast_refresh! # rubocop:todo Metrics
363
+ dispose_of_transform_effect
364
+ FileUtils.rm(destination.output_path, force: true) if requires_destination?
365
+ past_values = @data.peek.select do |key|
366
+ key == "categories" || key == "tags" || site.taxonomy_types.keys.any?(key)
367
+ end
368
+ model.attributes = model.origin.read
369
+ read!
370
+ tax_diff = past_values.any? { |k, v| @data.peek[k] != v }
371
+
372
+ if tax_diff && !collection.data?
373
+ # If the taxonomy values are different, we should just abort the fast refresh process.
374
+ unmark_for_fast_refresh!
375
+ false
376
+ else
377
+ true
378
+ end
379
+ end
380
+
381
+ def dispose_of_transform_effect
382
+ @transform_effect_disposal&.()
383
+ end
384
+
303
385
  private
304
386
 
305
387
  def ensure_default_data
@@ -350,6 +432,7 @@ module Bridgetown
350
432
 
351
433
  def import_taxonomies_from_data
352
434
  taxonomies.each_value do |metadata|
435
+ metadata.terms.reject! { _1.resource == self } # clear out for Fash Refresh
353
436
  Array(data[metadata.type.key]).each do |term|
354
437
  metadata.terms << TaxonomyTerm.new(
355
438
  resource: self, label: term, type: metadata.type
@@ -32,7 +32,7 @@ module Bridgetown
32
32
  end
33
33
 
34
34
  def output_path
35
- path = URL.unescape_path(relative_url)
35
+ path = Utils.unencode_uri(relative_url)
36
36
  if resource.site.base_path.present?
37
37
  path = path.delete_prefix resource.site.base_path(strip_slash_only: true)
38
38
  end
@@ -3,6 +3,8 @@
3
3
  module Bridgetown
4
4
  module Resource
5
5
  class Relations
6
+ using Bridgetown::Refinements
7
+
6
8
  # @return [Bridgetown::Resource::Base]
7
9
  attr_reader :resource
8
10
 
@@ -13,6 +15,7 @@ module Bridgetown
13
15
  def initialize(resource)
14
16
  @resource = resource
15
17
  @site = resource.site
18
+ @inflector = site.config.inflector
16
19
  end
17
20
 
18
21
  # @return [HashWithDotAccess::Hash]
@@ -26,7 +29,7 @@ module Bridgetown
26
29
  types = []
27
30
  relation_schema&.each_value do |collections|
28
31
  types << collections
29
- types << Array(collections).map { |item| ActiveSupport::Inflector.pluralize(item) }
32
+ types << Array(collections).map { |item| @inflector.pluralize(item) }
30
33
  end
31
34
  types.flatten.uniq
32
35
  end
@@ -49,13 +52,13 @@ module Bridgetown
49
52
  end
50
53
 
51
54
  def method_missing(type, *args)
52
- return super unless type.to_s.in?(relation_types)
55
+ return super unless type.to_s.within?(relation_types)
53
56
 
54
57
  resources_for_type(type)
55
58
  end
56
59
 
57
60
  def respond_to_missing?(type, *_args)
58
- type.to_s.in?(relation_types)
61
+ type.to_s.within?(relation_types)
59
62
  end
60
63
 
61
64
  def to_liquid
@@ -70,7 +73,7 @@ module Bridgetown
70
73
  relation_schema&.each do |relation_type, collections|
71
74
  collections = Array(collections).then do |collections_arr|
72
75
  collections_arr +
73
- collections_arr.map { |item| ActiveSupport::Inflector.pluralize(item) }
76
+ collections_arr.map { |item| @inflector.pluralize(item) }
74
77
  end.flatten.uniq
75
78
  return relation_type if collections.include?(type.to_s)
76
79
  end
@@ -79,14 +82,14 @@ module Bridgetown
79
82
  # @param type [Symbol]
80
83
  # @return [Bridgetown::Collection]
81
84
  def other_collection_for_type(type)
82
- site.collections[type] || site.collections[ActiveSupport::Inflector.pluralize(type)]
85
+ site.collections[type] || site.collections[@inflector.pluralize(type)]
83
86
  end
84
87
 
85
88
  # @return [Array<String>]
86
89
  def collection_labels
87
90
  [
88
91
  resource.collection.label,
89
- ActiveSupport::Inflector.singularize(resource.collection.label),
92
+ @inflector.singularize(resource.collection.label),
90
93
  ]
91
94
  end
92
95
 
@@ -109,7 +112,7 @@ module Bridgetown
109
112
  label, singular_label = collection_labels
110
113
 
111
114
  other_collection_for_type(type).resources.select do |other_resource|
112
- resource.data.slug.in?(
115
+ resource.data.slug.within?(
113
116
  Array(other_resource.data[label] || other_resource.data[singular_label])
114
117
  )
115
118
  end
@@ -121,7 +124,7 @@ module Bridgetown
121
124
  label, singular_label = collection_labels
122
125
 
123
126
  other_collection_for_type(type).resources.find do |other_resource|
124
- resource.data.slug.in?(
127
+ resource.data.slug.within?(
125
128
  Array(other_resource.data[label] || other_resource.data[singular_label])
126
129
  )
127
130
  end
@@ -3,6 +3,8 @@
3
3
  module Bridgetown
4
4
  module Resource
5
5
  class TaxonomyType
6
+ using Bridgetown::Refinements
7
+
6
8
  # @return [Bridgetown::Site]
7
9
  attr_reader :site
8
10
 
@@ -28,7 +30,7 @@ module Bridgetown
28
30
  def terms
29
31
  site.resources.map do |resource|
30
32
  resource.taxonomies[label].terms
31
- end.flatten.group_by(&:label).with_dot_access
33
+ end.flatten.group_by(&:label).as_dots
32
34
  end
33
35
 
34
36
  def inspect
@@ -38,7 +38,7 @@ module Bridgetown
38
38
  resource.content = transform_content(resource) do |converter, index, output|
39
39
  conversions[index] = {
40
40
  type: :content,
41
- converter: converter,
41
+ converter:,
42
42
  output: Bridgetown.env.production? ? nil : output,
43
43
  output_ext: conversions[index]&.dig(:output_ext) ||
44
44
  converter.output_ext(resource.extname),
@@ -81,7 +81,7 @@ module Bridgetown
81
81
  def output_ext_from_converters
82
82
  @conversions = converters.map do |converter|
83
83
  {
84
- converter: converter,
84
+ converter:,
85
85
  output_ext: converter.output_ext(resource.extname),
86
86
  }
87
87
  end
@@ -102,8 +102,8 @@ module Bridgetown
102
102
  output = transform_with_layout(layout, output, resource) do |converter, layout_output|
103
103
  conversions << {
104
104
  type: :layout,
105
- layout: layout,
106
- converter: converter,
105
+ layout:,
106
+ converter:,
107
107
  output: Bridgetown.env.production? ? nil : layout_output,
108
108
  }
109
109
  end
@@ -1,11 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "digest"
4
+ require "serbea/pipeline"
5
+ require "streamlined/helpers"
6
+ require "streamlined/renderable"
4
7
 
5
8
  module Bridgetown
9
+ module Streamlined
10
+ include ::Streamlined::Renderable
11
+ include Serbea::Pipeline::Helper
12
+ include ERBCapture
13
+
14
+ def helper(name, &helper_block)
15
+ self.class.define_method(name) do |*args, **kwargs, &block|
16
+ helper_block.call(*args, **kwargs, &block)
17
+ end
18
+ end
19
+ alias_method :macro, :helper
20
+ end
21
+
6
22
  class RubyTemplateView
7
23
  require "bridgetown-core/helpers"
8
24
 
25
+ using Bridgetown::Refinements
26
+ include Bridgetown::Streamlined
27
+
9
28
  attr_reader :layout, :resource, :paginator, :site, :content
10
29
  alias_method :page, :resource
11
30
 
@@ -20,33 +39,26 @@ module Bridgetown
20
39
  end
21
40
  @paginator = resource.paginator if resource.respond_to?(:paginator)
22
41
  @site = resource.site
42
+ @support_data_as_view_methods = @site.config.support_data_as_view_methods
23
43
  end
24
44
 
25
- def data
26
- resource.data
27
- end
45
+ def data = resource.data
28
46
 
29
- def partial(_partial_name = nil, **_options)
30
- raise "Must be implemented in a subclass"
31
- end
47
+ def collections = site.collections
48
+
49
+ def site_drop = site.site_payload.site
50
+
51
+ def partial(_partial_name = nil, **_options) = raise("Must be implemented in a subclass")
32
52
 
33
- def render(item, **options, &block)
53
+ def render(item, **options, &)
34
54
  if item.respond_to?(:render_in)
35
- result = item.render_in(self, &block)
55
+ result = item.render_in(self, &)
36
56
  result&.html_safe
37
57
  else
38
- partial(item, **options, &block)&.html_safe
58
+ partial(item, **options, &)&.html_safe
39
59
  end
40
60
  end
41
61
 
42
- def collections
43
- site.collections
44
- end
45
-
46
- def site_drop
47
- site.site_payload.site
48
- end
49
-
50
62
  def liquid_render(component, options = {}, &block)
51
63
  options[:_block_content] = capture(&block) if block && respond_to?(:capture)
52
64
  render_statement = _render_statement(component, options)
@@ -58,16 +70,24 @@ module Bridgetown
58
70
  Bridgetown.logger.warn "Liquid Warning:",
59
71
  LiquidRenderer.format_error(e, path || document.relative_path)
60
72
  end
61
- template.render!(options.deep_stringify_keys, _liquid_context).html_safe
73
+ template.render!(options.as_dots, _liquid_context).html_safe
62
74
  end
63
75
 
64
76
  def helpers
65
77
  @helpers ||= Helpers.new(self, site)
66
78
  end
67
79
 
68
- def method_missing(method_name, *args, **kwargs, &block)
80
+ def data_key?(key, *args, **kwargs)
81
+ return false unless @support_data_as_view_methods
82
+
83
+ args.empty? && kwargs.empty? && !block_given? && data.key?(key)
84
+ end
85
+
86
+ def method_missing(method_name, ...)
69
87
  if helpers.respond_to?(method_name.to_sym)
70
- helpers.send method_name.to_sym, *args, **kwargs, &block
88
+ helpers.send(method_name.to_sym, ...)
89
+ elsif data_key?(method_name, ...)
90
+ data[method_name]
71
91
  else
72
92
  super
73
93
  end
@@ -77,18 +97,14 @@ module Bridgetown
77
97
  helpers.respond_to?(method_name.to_sym, include_private) || super
78
98
  end
79
99
 
80
- def inspect
81
- "#<#{self.class} layout=#{layout&.label} resource=#{resource.relative_path}>"
82
- end
100
+ def inspect = "#<#{self.class} layout=#{layout&.label} resource=#{resource.relative_path}>"
83
101
 
84
102
  private
85
103
 
86
104
  def _render_statement(component, options)
87
- render_statement = if options[:_block_content]
88
- ["{% rendercontent \"#{component}\""]
89
- else
105
+ render_statement = options[:_block_content] ?
106
+ ["{% rendercontent \"#{component}\""] :
90
107
  ["{% render \"#{component}\""]
91
- end
92
108
  unless options.empty?
93
109
  render_statement << ", #{options.keys.map { |k| "#{k}: #{k}" }.join(", ")}"
94
110
  end
@@ -103,7 +119,7 @@ module Bridgetown
103
119
  def _liquid_context
104
120
  {
105
121
  registers: {
106
- site: site,
122
+ site:,
107
123
  page: resource.to_liquid,
108
124
  cached_partials: Bridgetown::Converters::LiquidTemplates.cached_partials,
109
125
  },
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "signalize/struct"
4
+
5
+ class Bridgetown::Signals < Signalize::Struct
6
+ alias_method :__prev_to_h, :to_h
7
+ include Enumerable
8
+ alias_method :to_h, :__prev_to_h
9
+
10
+ def self.signal_accessor(...)
11
+ super
12
+
13
+ return unless members.include?(:members)
14
+
15
+ # So…we need to support site data that could have a `members` key. This is how we do it:
16
+ # by conditionally providing the value the gem class expects when it's gem code, otherwise we
17
+ # provide the `members` signal value.
18
+ define_method :members do
19
+ if caller_locations(1..1).first.path.end_with?("/lib/signalize/struct.rb")
20
+ self.class.members
21
+ else
22
+ members_signal.value
23
+ end
24
+ end
25
+ end
26
+
27
+ def each(...)
28
+ to_h.each(...)
29
+ end
30
+
31
+ def key?(key)
32
+ self.class.members.include?(key.to_sym)
33
+ end
34
+
35
+ def [](key)
36
+ return unless key?(key)
37
+
38
+ send(key)
39
+ end
40
+
41
+ def []=(key, value)
42
+ unless key?(key)
43
+ if instance_of?(Bridgetown::Signals)
44
+ raise Bridgetown::Errors::FatalException,
45
+ "You must use a unique subclass of `Bridgetown::Signals' before adding new members"
46
+ end
47
+
48
+ self.class.signal_accessor(key)
49
+ end
50
+
51
+ send(:"#{key}=", value)
52
+ end
53
+
54
+ def method_missing(key, *value, &block) # rubocop:disable Style/MissingRespondToMissing
55
+ return nil if value.empty? && block.nil?
56
+
57
+ key = key.to_s
58
+ if key.end_with?("=")
59
+ key.chop!
60
+ return self[key] = value[0]
61
+ end
62
+
63
+ super(key.to_sym)
64
+ end
65
+
66
+ # You can access signals and mutate objects (aka push to an array, change a hash value), and by
67
+ # making those changes with a block, this method will track which signals were accessed and resave
68
+ # them with duplicated objects thereby triggering new dependent effects or subscriptions.
69
+ def batch_mutations
70
+ ef = Signalize.effect { yield self }
71
+ node = ef.receiver._sources
72
+ deps = []
73
+ while node
74
+ deps << node._source
75
+ node = node._next_source
76
+ end
77
+
78
+ ef.() # dispose
79
+
80
+ Signalize.batch do
81
+ self.class.members.each do |member_name|
82
+ matching_dep = deps.find { _1 == send(:"#{member_name}_signal") }
83
+ next unless matching_dep
84
+
85
+ Signalize.batch do
86
+ new_value = matching_dep.value.dup
87
+ matching_dep.value = nil
88
+ matching_dep.value = new_value
89
+ end
90
+ end
91
+ end
92
+
93
+ nil
94
+ end
95
+ end
@@ -7,6 +7,7 @@ module Bridgetown
7
7
  include Configurable
8
8
  include Content
9
9
  include Extensible
10
+ include FastRefreshable
10
11
  include Localizable
11
12
  include Processable
12
13
  include Renderable
@@ -22,7 +23,7 @@ module Bridgetown
22
23
  # @return [Bridgetown::Utils::LoadersManager]
23
24
  attr_reader :loaders_manager
24
25
 
25
- attr_reader :cache_dir, :liquid_renderer
26
+ attr_reader :cache_dir, :liquid_renderer, :data, :signals
26
27
 
27
28
  # All files not pages/documents or structured data in the source folder
28
29
  # @return [Array<StaticFile>]
@@ -34,9 +35,9 @@ module Bridgetown
34
35
  # @return [Array<GeneratedPage>]
35
36
  attr_accessor :generated_pages
36
37
 
37
- attr_accessor :permalink_style, :time, :data,
38
+ attr_accessor :permalink_style, :time,
38
39
  :file_read_opts, :plugin_manager, :converters,
39
- :generators, :reader
40
+ :generators, :reader, :fast_refresh_ordering
40
41
 
41
42
  # Initialize a new Site.
42
43
  #
@@ -60,7 +61,7 @@ module Bridgetown
60
61
  @reader = Reader.new(self)
61
62
  @liquid_renderer = LiquidRenderer.new(self)
62
63
 
63
- Bridgetown::Cache.base_cache["site_tmp"] = {}.with_dot_access
64
+ Bridgetown::Cache.base_cache["site_tmp"] = HashWithDotAccess::Hash.new
64
65
  ensure_not_in_dest
65
66
 
66
67
  Bridgetown::Current.sites[@label] = self
@@ -70,6 +71,23 @@ module Bridgetown
70
71
  setup # Extensible
71
72
  end
72
73
 
74
+ def data=(new_data)
75
+ @data = new_data
76
+ data_hash = @data.to_dot_h.transform_keys(&:to_sym)
77
+ @signals = Bridgetown::Signals.define(*data_hash.keys) do
78
+ def inspect # rubocop:disable Lint/NestedMethodDefinition
79
+ var_peeks = instance_variables.filter_map do |var_name|
80
+ var = instance_variable_get(var_name)
81
+ if var.is_a?(Signalize::Signal)
82
+ "#{var_name.to_s.delete_prefix("@")}=#{var.peek.inspect}"
83
+ end
84
+ end.join(", ")
85
+
86
+ "#<Bridgetown::Site::Signals#{object_id}>#{var_peeks.empty? ? nil : " #{var_peeks}"}>"
87
+ end
88
+ end.new(**data_hash)
89
+ end
90
+
73
91
  # Check that the destination dir isn't the source dir or a directory
74
92
  # parent to the source dir.
75
93
  def ensure_not_in_dest