bridgetown-core 1.3.4 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bridgetown +0 -6
  3. data/bin/bt +6 -0
  4. data/bridgetown-core.gemspec +9 -6
  5. data/lib/bridgetown-core/cleaner.rb +1 -1
  6. data/lib/bridgetown-core/collection.rb +30 -18
  7. data/lib/bridgetown-core/commands/build.rb +9 -22
  8. data/lib/bridgetown-core/commands/concerns/actions.rb +15 -8
  9. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +1 -1
  10. data/lib/bridgetown-core/commands/console.rb +3 -0
  11. data/lib/bridgetown-core/commands/doctor.rb +2 -4
  12. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +1 -1
  13. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +1 -1
  14. data/lib/bridgetown-core/commands/esbuild/update.rb +2 -2
  15. data/lib/bridgetown-core/commands/esbuild.rb +3 -3
  16. data/lib/bridgetown-core/commands/new.rb +32 -39
  17. data/lib/bridgetown-core/commands/start.rb +76 -60
  18. data/lib/bridgetown-core/component.rb +19 -13
  19. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  20. data/lib/bridgetown-core/concerns/prioritizable.rb +11 -1
  21. data/lib/bridgetown-core/concerns/site/configurable.rb +7 -18
  22. data/lib/bridgetown-core/concerns/site/content.rb +10 -9
  23. data/lib/bridgetown-core/concerns/site/extensible.rb +4 -6
  24. data/lib/bridgetown-core/concerns/site/fast_refreshable.rb +161 -0
  25. data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
  26. data/lib/bridgetown-core/concerns/site/renderable.rb +31 -7
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +23 -9
  28. data/lib/bridgetown-core/concerns/site/writable.rb +1 -1
  29. data/lib/bridgetown-core/concerns/transformable.rb +3 -5
  30. data/lib/bridgetown-core/configuration/configuration_dsl.rb +23 -14
  31. data/lib/bridgetown-core/configuration.rb +67 -114
  32. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -2
  33. data/lib/bridgetown-core/configurations/cypress/cypress_tasks +2 -2
  34. data/lib/bridgetown-core/configurations/cypress.rb +1 -1
  35. data/lib/bridgetown-core/configurations/gh-pages/gh-pages.yml +3 -3
  36. data/lib/bridgetown-core/configurations/is-land.rb +1 -1
  37. data/lib/bridgetown-core/configurations/lit.rb +1 -1
  38. data/lib/bridgetown-core/configurations/open-props.rb +1 -1
  39. data/lib/bridgetown-core/configurations/purgecss.rb +1 -1
  40. data/lib/bridgetown-core/configurations/ruby2js.rb +1 -1
  41. data/lib/bridgetown-core/configurations/shoelace.rb +8 -20
  42. data/lib/bridgetown-core/configurations/stimulus.rb +17 -36
  43. data/lib/bridgetown-core/configurations/turbo.rb +1 -2
  44. data/lib/bridgetown-core/converter.rb +38 -10
  45. data/lib/bridgetown-core/converters/erb_templates.rb +9 -26
  46. data/lib/bridgetown-core/converters/identity.rb +1 -1
  47. data/lib/bridgetown-core/converters/liquid_templates.rb +2 -20
  48. data/lib/bridgetown-core/converters/ruby_templates.rb +61 -3
  49. data/lib/bridgetown-core/converters/serbea_templates.rb +12 -24
  50. data/lib/bridgetown-core/current.rb +19 -17
  51. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  52. data/lib/bridgetown-core/drops/drop.rb +3 -3
  53. data/lib/bridgetown-core/drops/relations_drop.rb +3 -2
  54. data/lib/bridgetown-core/drops/site_drop.rb +0 -5
  55. data/lib/bridgetown-core/entry_filter.rb +4 -6
  56. data/lib/bridgetown-core/errors.rb +2 -2
  57. data/lib/bridgetown-core/filters/from_liquid.rb +6 -10
  58. data/lib/bridgetown-core/filters/localization_filters.rb +1 -1
  59. data/lib/bridgetown-core/filters/translation_filters.rb +2 -2
  60. data/lib/bridgetown-core/filters.rb +3 -3
  61. data/lib/bridgetown-core/front_matter/defaults.rb +225 -0
  62. data/lib/bridgetown-core/front_matter/importer.rb +34 -0
  63. data/lib/bridgetown-core/front_matter/loaders/base.rb +29 -0
  64. data/lib/bridgetown-core/front_matter/loaders/ruby.rb +113 -0
  65. data/lib/bridgetown-core/front_matter/loaders/yaml.rb +42 -0
  66. data/lib/bridgetown-core/front_matter/loaders.rb +44 -0
  67. data/lib/bridgetown-core/{utils/ruby_front_matter.rb → front_matter/ruby.rb} +5 -5
  68. data/lib/bridgetown-core/front_matter.rb +11 -0
  69. data/lib/bridgetown-core/generated_page.rb +71 -31
  70. data/lib/bridgetown-core/generators/prototype_generator.rb +30 -18
  71. data/lib/bridgetown-core/helpers.rb +36 -47
  72. data/lib/bridgetown-core/hooks.rb +5 -5
  73. data/lib/bridgetown-core/inflector.rb +40 -0
  74. data/lib/bridgetown-core/kramdown/parser/gfm.rb +2 -2
  75. data/lib/bridgetown-core/layout.rb +3 -3
  76. data/lib/bridgetown-core/log_adapter.rb +12 -13
  77. data/lib/bridgetown-core/log_writer.rb +4 -4
  78. data/lib/bridgetown-core/model/base.rb +17 -17
  79. data/lib/bridgetown-core/model/builder_origin.rb +5 -3
  80. data/lib/bridgetown-core/model/origin.rb +1 -3
  81. data/lib/bridgetown-core/model/repo_origin.rb +12 -14
  82. data/lib/bridgetown-core/plugin.rb +0 -1
  83. data/lib/bridgetown-core/plugin_manager.rb +27 -84
  84. data/lib/bridgetown-core/rack/boot.rb +0 -15
  85. data/lib/bridgetown-core/rack/routes.rb +30 -90
  86. data/lib/bridgetown-core/reader.rb +6 -4
  87. data/lib/bridgetown-core/readers/layout_reader.rb +2 -2
  88. data/lib/bridgetown-core/readers/plugin_content_reader.rb +2 -2
  89. data/lib/bridgetown-core/resource/base.rb +112 -29
  90. data/lib/bridgetown-core/resource/destination.rb +1 -1
  91. data/lib/bridgetown-core/resource/relations.rb +11 -8
  92. data/lib/bridgetown-core/resource/taxonomy_type.rb +3 -1
  93. data/lib/bridgetown-core/resource/transformer.rb +4 -4
  94. data/lib/bridgetown-core/ruby_template_view.rb +44 -28
  95. data/lib/bridgetown-core/signals.rb +95 -0
  96. data/lib/bridgetown-core/site.rb +22 -4
  97. data/lib/bridgetown-core/slot.rb +5 -5
  98. data/lib/bridgetown-core/static_file.rb +5 -7
  99. data/lib/bridgetown-core/tags/asset_path.rb +11 -2
  100. data/lib/bridgetown-core/tags/find.rb +3 -5
  101. data/lib/bridgetown-core/tags/highlight.rb +3 -3
  102. data/lib/bridgetown-core/tags/post_url.rb +1 -1
  103. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -2
  104. data/lib/bridgetown-core/utils/aux.rb +41 -41
  105. data/lib/bridgetown-core/utils/loaders_manager.rb +2 -21
  106. data/lib/bridgetown-core/utils/ruby_exec.rb +17 -0
  107. data/lib/bridgetown-core/utils.rb +46 -110
  108. data/lib/bridgetown-core/version.rb +2 -2
  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/tmp/pids/.keep +0 -0
  125. metadata +98 -63
  126. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +0 -12
  127. data/lib/bridgetown-core/commands/webpack/setup.rb +0 -4
  128. data/lib/bridgetown-core/commands/webpack/update.rb +0 -24
  129. data/lib/bridgetown-core/commands/webpack/webpack.config.js +0 -31
  130. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +0 -135
  131. data/lib/bridgetown-core/commands/webpack.rb +0 -82
  132. data/lib/bridgetown-core/concerns/front_matter_importer.rb +0 -52
  133. data/lib/bridgetown-core/concerns/liquid_renderable.rb +0 -30
  134. data/lib/bridgetown-core/core_ext/psych.rb +0 -15
  135. data/lib/bridgetown-core/drops/url_drop.rb +0 -152
  136. data/lib/bridgetown-core/frontmatter_defaults.rb +0 -223
  137. data/lib/bridgetown-core/rack/static_indexes.rb +0 -31
  138. data/lib/bridgetown-core/url.rb +0 -166
  139. data/lib/bridgetown-core/utils/ansi.rb +0 -57
  140. data/lib/site_template/bridgetown.config.yml +0 -33
@@ -40,7 +40,7 @@ module Bridgetown
40
40
  # See Utils.slugify for more detail.
41
41
  def slugify(input, mode = nil)
42
42
  mode = @context.registers[:site].config.slugify_mode if mode.nil?
43
- Utils.slugify(input, mode: mode)
43
+ Utils.slugify(input, mode:)
44
44
  end
45
45
 
46
46
  # Titleize a slug or identifier string.
@@ -395,8 +395,8 @@ module Bridgetown
395
395
  end
396
396
  end
397
397
 
398
- FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!.freeze
399
- INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!.freeze
398
+ FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!
399
+ INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!
400
400
  private_constant :FLOAT_LIKE, :INTEGER_LIKE
401
401
 
402
402
  # return numeric values as numbers for proper sorting
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ # This class handles custom defaults for front matter settings.
6
+ # It is exposed via the frontmatter_defaults method on the site class.
7
+ class Defaults
8
+ # @return [Bridgetown::Site]
9
+ attr_reader :site
10
+
11
+ def initialize(site)
12
+ @site = site
13
+ @defaults_cache = {}
14
+ end
15
+
16
+ def reset
17
+ @glob_cache = {}
18
+ @defaults_cache = {}
19
+ end
20
+
21
+ def ensure_time!(set)
22
+ return set unless set.key?("values") && set["values"].key?("date")
23
+ return set if set["values"]["date"].is_a?(Time)
24
+
25
+ set["values"]["date"] = Utils.parse_date(
26
+ set["values"]["date"],
27
+ "An invalid date format was found in a front-matter default set: #{set}"
28
+ )
29
+ set
30
+ end
31
+
32
+ # Collects a hash with all default values for a resource
33
+ #
34
+ # @param path [String] the relative path of the resource
35
+ # @param collection_name [Symbol] :posts, :pages, etc.
36
+ #
37
+ # @return [Hash] all default values (an empty hash if there are none)
38
+ def all(path, collection_name)
39
+ if @defaults_cache.key?([path, collection_name])
40
+ return @defaults_cache[[path, collection_name]]
41
+ end
42
+
43
+ defaults = {}
44
+ merge_data_cascade_for_path(path, defaults)
45
+
46
+ old_scope = nil
47
+ matching_sets(path, collection_name).each do |set|
48
+ if has_precedence?(old_scope, set["scope"])
49
+ defaults = Utils.deep_merge_hashes(defaults, set["values"])
50
+ old_scope = set["scope"]
51
+ else
52
+ defaults = Utils.deep_merge_hashes(set["values"], defaults)
53
+ end
54
+ end
55
+
56
+ @defaults_cache[[path, collection_name]] = defaults
57
+ end
58
+
59
+ private
60
+
61
+ def merge_data_cascade_for_path(path, merged_data)
62
+ absolute_path = site.in_source_dir(path)
63
+ site.defaults_reader.path_defaults
64
+ .select { |k, _v| absolute_path.include? k }
65
+ .sort_by { |k, _v| k.length }
66
+ .each do |defaults|
67
+ merged_data.merge!(defaults[1])
68
+ end
69
+ end
70
+
71
+ # Checks if a given default setting scope matches the given path and collection
72
+ #
73
+ # scope - the hash indicating the scope, as defined in bridgetown.config.yml
74
+ # path - the path to check for
75
+ # collection - the collection (:posts or :pages) to check for
76
+ #
77
+ # Returns true if the scope applies to the given collection and path
78
+ def applies?(scope, path, collection)
79
+ applies_collection?(scope, collection) && applies_path?(scope, path)
80
+ end
81
+
82
+ def applies_path?(scope, path)
83
+ rel_scope_path = scope["path"]
84
+ return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty?
85
+
86
+ sanitized_path = strip_collections_dir(sanitize_path(path))
87
+
88
+ if rel_scope_path.include?("*")
89
+ glob_scope(sanitized_path, rel_scope_path)
90
+ else
91
+ path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
92
+ end
93
+ end
94
+
95
+ def glob_scope(sanitized_path, rel_scope_path)
96
+ site_source = Pathname.new(site.source)
97
+ abs_scope_path = site_source.join(rel_scope_path).to_s
98
+
99
+ glob_cache(abs_scope_path).each do |scope_path|
100
+ scope_path = Pathname.new(scope_path).relative_path_from(site_source).to_s
101
+ scope_path = strip_collections_dir(scope_path)
102
+ Bridgetown.logger.debug "Globbed Scope Path:", scope_path
103
+ return true if path_is_subpath?(sanitized_path, scope_path)
104
+ end
105
+ false
106
+ end
107
+
108
+ def glob_cache(path)
109
+ @glob_cache ||= {}
110
+ @glob_cache[path] ||= Dir.glob(path)
111
+ end
112
+
113
+ def path_is_subpath?(path, parent_path)
114
+ path.start_with?(parent_path)
115
+ end
116
+
117
+ def strip_collections_dir(path)
118
+ collections_dir = site.config["collections_dir"]
119
+ slashed_coll_dir = collections_dir.empty? ? "/" : "#{collections_dir}/"
120
+ return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir)
121
+
122
+ path.sub(slashed_coll_dir, "")
123
+ end
124
+
125
+ # Determines whether the scope applies to collection.
126
+ # The scope applies to the collection if:
127
+ # 1. no 'collection' is specified
128
+ # 2. the 'collection' in the scope is the same as the collection asked about
129
+ #
130
+ # @param scope [Hash] the defaults set being asked about
131
+ # @param collection [Symbol] the collection of the resource being processed
132
+ #
133
+ # @return [Boolean] whether either of the above conditions are satisfied
134
+ def applies_collection?(scope, collection)
135
+ !scope.key?("collection") || scope["collection"].eql?(collection.to_s)
136
+ end
137
+
138
+ # Checks if a given set of default values is valid
139
+ #
140
+ # @param set [Hash] the default value hash as defined in bridgetown.config.yml
141
+ #
142
+ # @return [Boolean] if the set is valid and can be used
143
+ def valid?(set)
144
+ set.is_a?(Hash) && set["values"].is_a?(Hash)
145
+ end
146
+
147
+ # Determines if a new scope has precedence over an old one
148
+ #
149
+ # old_scope - the old scope hash, or nil if there's none
150
+ # new_scope - the new scope hash
151
+ #
152
+ # Returns true if the new scope has precedence over the older
153
+ # rubocop: disable Naming/PredicateName
154
+ def has_precedence?(old_scope, new_scope)
155
+ return true if old_scope.nil?
156
+
157
+ new_path = sanitize_path(new_scope["path"])
158
+ old_path = sanitize_path(old_scope["path"])
159
+
160
+ if new_path.length != old_path.length
161
+ new_path.length >= old_path.length
162
+ elsif new_scope.key?("collection")
163
+ true
164
+ else
165
+ !old_scope.key? "collection"
166
+ end
167
+ end
168
+ # rubocop: enable Naming/PredicateName
169
+
170
+ # Collects a list of sets that match the given path and collection
171
+ #
172
+ # @return [Array<Hash>]
173
+ def matching_sets(path, collection)
174
+ @matched_set_cache ||= {}
175
+ @matched_set_cache[path] ||= {}
176
+ @matched_set_cache[path][collection] ||= valid_sets.select do |set|
177
+ !set.key?("scope") || applies?(set["scope"], path, collection)
178
+ end
179
+ end
180
+
181
+ # Returns a list of valid sets
182
+ #
183
+ # This is not cached to allow plugins to modify the configuration
184
+ # and have their changes take effect
185
+ #
186
+ # @return [Array<Hash>]
187
+ def valid_sets
188
+ sets = site.config["defaults"]
189
+ return [] unless sets.is_a?(Array)
190
+
191
+ sets.filter_map do |set|
192
+ if valid?(set)
193
+ massage_scope!(set)
194
+ # TODO: is this trip really necessary?
195
+ ensure_time!(set)
196
+ else
197
+ Bridgetown.logger.warn "Defaults:", "An invalid front-matter default set was found:"
198
+ Bridgetown.logger.warn set.to_s
199
+ nil
200
+ end
201
+ end
202
+ end
203
+
204
+ # Set path to blank if not specified and alias older type to collection
205
+ def massage_scope!(set)
206
+ set["scope"] ||= {}
207
+ set["scope"]["path"] ||= ""
208
+ return unless set["scope"]["type"] && !set["scope"]["collection"]
209
+
210
+ set["scope"]["collection"] = set["scope"]["type"]
211
+ end
212
+
213
+ SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z!
214
+
215
+ # Sanitizes the given path by removing a leading and adding a trailing slash
216
+ def sanitize_path(path)
217
+ if path.nil? || path.empty?
218
+ ""
219
+ else
220
+ path.gsub(SANITIZATION_REGEX, "")
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ module Importer
6
+ # Requires klass#content and klass#front_matter_line_count accessors
7
+ def self.included(klass)
8
+ klass.include Bridgetown::FrontMatter::RubyDSL
9
+ end
10
+
11
+ def read_front_matter(file_path)
12
+ file_contents = File.read(
13
+ file_path, **Bridgetown::Utils.merged_file_read_opts(Bridgetown::Current.site, {})
14
+ )
15
+ fm_result = nil
16
+ Loaders.for(self).each do |loader|
17
+ fm_result = loader.read(file_contents, file_path:) and break
18
+ end
19
+
20
+ if fm_result
21
+ self.content = fm_result.content
22
+ self.front_matter_line_count = fm_result.line_count
23
+ fm_result.front_matter
24
+ elsif is_a?(Layout)
25
+ self.content = file_contents
26
+ {}
27
+ else
28
+ yaml_data = YAMLParser.load_file(file_path)
29
+ (yaml_data.is_a?(Array) ? { rows: yaml_data } : yaml_data)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ module Loaders
6
+ # An abstract base class for processing front matter
7
+ class Base
8
+ # @param origin_or_layout [Bridgetown::Model::RepoOrigin, Bridgetown::Layout]
9
+ def initialize(origin_or_layout)
10
+ @origin_or_layout = origin_or_layout
11
+ end
12
+
13
+ # Reads the contents of a file, returning a possible {Result}
14
+ #
15
+ # @param file_contents [String] the contents of the file being processed
16
+ # @param file_path [String] the path to the file being processed
17
+ # @return [Result, nil]
18
+ def read(file_contents, file_path:) # rubocop:disable Lint/UnusedMethodArgument
19
+ raise "Implement #read in a subclass of Bridgetown::FrontMatter::Loaders::Base"
20
+ end
21
+
22
+ private
23
+
24
+ # @return [Bridgetown::Model::RepoOrigin, Bridgetown::Layout]
25
+ attr_reader :origin_or_layout
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ module Loaders
6
+ # Reads Ruby front matter delineated by fenced code blocks or ERB/Serbea indicators.
7
+ #
8
+ # For example, all of these resources load the hash `{published: false,
9
+ # title: "My post"}` as their front matter.
10
+ #
11
+ # ~~~
12
+ # ```ruby
13
+ # {
14
+ # published: false,
15
+ # title: My post
16
+ # }
17
+ # ```
18
+ # ~~~
19
+ #
20
+ # ~~~~
21
+ # ~~~ruby
22
+ # {
23
+ # published: false,
24
+ # title: My post
25
+ # }
26
+ # ~~~
27
+ # ~~~~
28
+ #
29
+ # ~~~
30
+ # ###ruby
31
+ # {
32
+ # published: false,
33
+ # title: My post
34
+ # }
35
+ # ###
36
+ # ~~~
37
+ #
38
+ # ~~~
39
+ # ---ruby
40
+ # {
41
+ # published: false,
42
+ # title: My post
43
+ # }
44
+ # ---
45
+ # ~~~
46
+ #
47
+ # ~~~~
48
+ # ~~~<%
49
+ # {
50
+ # published: false,
51
+ # title: My post
52
+ # }
53
+ # %>~~~
54
+ # ~~~~
55
+ #
56
+ # ~~~~
57
+ # ~~~{%
58
+ # {
59
+ # published: false,
60
+ # title: My post
61
+ # }
62
+ # %}~~~
63
+ # ~~~~
64
+ class Ruby < Base
65
+ HEADER = %r!\A[~`#-]{3,}(?:ruby|<%|{%)[ \t]*\n!
66
+ BLOCK = %r!#{HEADER.source}(.*?\n?)^((?:%>|%})?[~`#-]{3,}[ \t]*$\n?)!m
67
+
68
+ # Determines whether a given file has Ruby front matter
69
+ #
70
+ # @param file [Pathname, String] the path to the file
71
+ # @return [Boolean] true if the file has Ruby front matter, false otherwise
72
+ def self.header?(file)
73
+ File.open(file, "rb", &:gets)&.match?(HEADER) || false
74
+ end
75
+
76
+ # @see {Base#read}
77
+ def read(file_contents, file_path:)
78
+ if (ruby_content = file_contents.match(BLOCK)) && should_execute_inline_ruby?
79
+ Result.new(
80
+ content: ruby_content.post_match.lstrip,
81
+ front_matter: process_ruby_data(ruby_content[1], file_path, 2),
82
+ line_count: ruby_content[1].lines.size - 1
83
+ )
84
+ elsif self.class.header?(file_path)
85
+ Result.new(
86
+ front_matter: process_ruby_data(
87
+ File.read(file_path).lines[1..].join("\n"),
88
+ file_path,
89
+ 2
90
+ ),
91
+ line_count: 0
92
+ )
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def process_ruby_data(rubycode, file_path, starting_line)
99
+ Bridgetown::Utils::RubyExec.process_ruby_data(
100
+ @origin_or_layout,
101
+ rubycode,
102
+ file_path,
103
+ starting_line
104
+ )
105
+ end
106
+
107
+ def should_execute_inline_ruby?
108
+ Bridgetown::Current.site.config.should_execute_inline_ruby?
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ module Loaders
6
+ # Reads YAML-formatted front matter delineated by triple hyphens
7
+ #
8
+ # As an example, this resource loads to the hash `{"published": false,
9
+ # "title": "My post"}`.
10
+ #
11
+ # ~~~
12
+ # ---
13
+ # published: false
14
+ # title: My post
15
+ # ---
16
+ # ~~~
17
+ class YAML < Base
18
+ HEADER = %r!\A---[ \t]*\n!
19
+ BLOCK = %r!#{HEADER.source}(.*?\n?)^((---|\.\.\.)[ \t]*$\n?)!m
20
+
21
+ # Determines whether a given file has YAML front matter
22
+ #
23
+ # @param file [String] the path to the file
24
+ # @return [Boolean] true if the file has YAML front matter, false otherwise
25
+ def self.header?(file)
26
+ File.open(file, "rb", &:gets)&.match?(HEADER) || false
27
+ end
28
+
29
+ # @see {Base#read}
30
+ def read(file_contents, **)
31
+ yaml_content = file_contents.match(BLOCK) or return
32
+
33
+ Result.new(
34
+ content: yaml_content.post_match.lstrip,
35
+ front_matter: YAMLParser.load(yaml_content[1]),
36
+ line_count: yaml_content[1].lines.size - 1
37
+ )
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ module Loaders
6
+ autoload :Base, "bridgetown-core/front_matter/loaders/base"
7
+ autoload :Ruby, "bridgetown-core/front_matter/loaders/ruby"
8
+ autoload :YAML, "bridgetown-core/front_matter/loaders/yaml"
9
+
10
+ Result = Struct.new(:content, :front_matter, :line_count, keyword_init: true)
11
+
12
+ # Constructs a list of possible loaders for a {Model::RepoOrigin} or {Layout}
13
+ #
14
+ # @param origin_or_layout [Bridgetown::Model::RepoOrigin, Bridgetown::Layout]
15
+ # @return [Array<Loaders::Base>]
16
+ def self.for(origin_or_layout)
17
+ registry.map { |loader_class| loader_class.new(origin_or_layout) }
18
+ end
19
+
20
+ # Determines whether a given file has front matter
21
+ #
22
+ # @param path [Pathname, String] the path to the file
23
+ # @return [Boolean] true if the file has front matter, false otherwise
24
+ def self.front_matter?(file)
25
+ registry.any? { |loader_class| loader_class.header?(file) }
26
+ end
27
+
28
+ # Registers a new type of front matter loader
29
+ #
30
+ # @param loader_class [Loader::Base]
31
+ # @return [void]
32
+ def self.register(loader_class)
33
+ registry.push(loader_class)
34
+ end
35
+
36
+ private_class_method def self.registry
37
+ @registry ||= []
38
+ end
39
+
40
+ register YAML
41
+ register Ruby
42
+ end
43
+ end
44
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bridgetown
4
- module Utils
5
- module RubyFrontMatterDSL
4
+ module FrontMatter
5
+ module RubyDSL
6
6
  def front_matter(scope: nil, &block)
7
- RubyFrontMatter.new(scope: scope).tap { |fm| fm.instance_exec(&block) }
7
+ RubyFrontMatter.new(scope:).tap { |fm| fm.instance_exec(&block) }
8
8
  end
9
9
  end
10
10
 
@@ -21,8 +21,8 @@ module Bridgetown
21
21
  set(key, value[0], &block)
22
22
  end
23
23
 
24
- def each(&block)
25
- @data.each(&block)
24
+ def each(&)
25
+ @data.each(&)
26
26
  end
27
27
 
28
28
  def get(key)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module FrontMatter
5
+ autoload :Defaults, "bridgetown-core/front_matter/defaults"
6
+ autoload :Importer, "bridgetown-core/front_matter/importer"
7
+ autoload :Loaders, "bridgetown-core/front_matter/loaders"
8
+ autoload :RubyDSL, "bridgetown-core/front_matter/ruby"
9
+ autoload :RubyFrontMatter, "bridgetown-core/front_matter/ruby"
10
+ end
11
+ end