bridgetown-core 0.19.3 → 0.21.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -2
  3. data/lib/bridgetown-core.rb +37 -29
  4. data/lib/bridgetown-core/cleaner.rb +9 -3
  5. data/lib/bridgetown-core/collection.rb +177 -78
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/build.rb +0 -11
  8. data/lib/bridgetown-core/commands/concerns/git_helpers.rb +20 -0
  9. data/lib/bridgetown-core/commands/configure.rb +8 -3
  10. data/lib/bridgetown-core/commands/console.rb +4 -0
  11. data/lib/bridgetown-core/commands/doctor.rb +1 -19
  12. data/lib/bridgetown-core/commands/new.rb +6 -6
  13. data/lib/bridgetown-core/commands/plugins.rb +14 -13
  14. data/lib/bridgetown-core/commands/serve.rb +0 -14
  15. data/lib/bridgetown-core/commands/webpack.rb +75 -0
  16. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +12 -0
  17. data/lib/bridgetown-core/commands/webpack/setup.rb +4 -0
  18. data/lib/bridgetown-core/commands/webpack/update.rb +3 -0
  19. data/lib/bridgetown-core/commands/webpack/webpack.config.js +18 -0
  20. data/lib/{site_template/webpack.config.js.erb → bridgetown-core/commands/webpack/webpack.defaults.js.erb} +32 -16
  21. data/lib/bridgetown-core/component.rb +183 -0
  22. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  23. data/lib/bridgetown-core/concerns/front_matter_importer.rb +52 -0
  24. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  25. data/lib/bridgetown-core/concerns/site/configurable.rb +10 -10
  26. data/lib/bridgetown-core/concerns/site/content.rb +56 -15
  27. data/lib/bridgetown-core/concerns/site/localizable.rb +3 -5
  28. data/lib/bridgetown-core/concerns/site/processable.rb +6 -4
  29. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  30. data/lib/bridgetown-core/concerns/site/writable.rb +12 -2
  31. data/lib/bridgetown-core/concerns/validatable.rb +2 -5
  32. data/lib/bridgetown-core/configuration.rb +61 -33
  33. data/lib/bridgetown-core/configurations/bt-postcss.rb +6 -6
  34. data/lib/bridgetown-core/configurations/netlify.rb +1 -0
  35. data/lib/bridgetown-core/configurations/tailwindcss.rb +6 -6
  36. data/lib/bridgetown-core/converter.rb +23 -0
  37. data/lib/bridgetown-core/converters/erb_templates.rb +51 -35
  38. data/lib/bridgetown-core/converters/identity.rb +0 -9
  39. data/lib/bridgetown-core/converters/liquid_templates.rb +1 -1
  40. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  41. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +5 -38
  42. data/lib/bridgetown-core/converters/ruby_templates.rb +17 -0
  43. data/lib/bridgetown-core/converters/smartypants.rb +3 -1
  44. data/lib/bridgetown-core/core_ext/psych.rb +19 -0
  45. data/lib/bridgetown-core/current.rb +10 -0
  46. data/lib/bridgetown-core/document.rb +9 -16
  47. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  48. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  49. data/lib/bridgetown-core/drops/relations_drop.rb +23 -0
  50. data/lib/bridgetown-core/drops/resource_drop.rb +83 -0
  51. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  52. data/lib/bridgetown-core/drops/unified_payload_drop.rb +5 -0
  53. data/lib/bridgetown-core/entry_filter.rb +17 -28
  54. data/lib/bridgetown-core/errors.rb +0 -2
  55. data/lib/bridgetown-core/filters.rb +3 -26
  56. data/lib/bridgetown-core/filters/from_liquid.rb +23 -0
  57. data/lib/bridgetown-core/filters/url_filters.rb +12 -0
  58. data/lib/bridgetown-core/frontmatter_defaults.rb +1 -1
  59. data/lib/bridgetown-core/generators/prototype_generator.rb +59 -20
  60. data/lib/bridgetown-core/helpers.rb +48 -9
  61. data/lib/bridgetown-core/layout.rb +53 -21
  62. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  63. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  64. data/lib/bridgetown-core/model/base.rb +138 -0
  65. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  66. data/lib/bridgetown-core/model/origin.rb +38 -0
  67. data/lib/bridgetown-core/model/repo_origin.rb +126 -0
  68. data/lib/bridgetown-core/page.rb +11 -2
  69. data/lib/bridgetown-core/plugin_manager.rb +1 -3
  70. data/lib/bridgetown-core/publisher.rb +8 -2
  71. data/lib/bridgetown-core/reader.rb +37 -22
  72. data/lib/bridgetown-core/readers/data_reader.rb +5 -5
  73. data/lib/bridgetown-core/readers/defaults_reader.rb +1 -1
  74. data/lib/bridgetown-core/readers/layout_reader.rb +1 -1
  75. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  76. data/lib/bridgetown-core/readers/post_reader.rb +5 -4
  77. data/lib/bridgetown-core/regenerator.rb +9 -2
  78. data/lib/bridgetown-core/related_posts.rb +9 -6
  79. data/lib/bridgetown-core/renderer.rb +6 -13
  80. data/lib/bridgetown-core/resource/base.rb +329 -0
  81. data/lib/bridgetown-core/resource/destination.rb +49 -0
  82. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  83. data/lib/bridgetown-core/resource/relations.rb +132 -0
  84. data/lib/bridgetown-core/resource/taxonomy_term.rb +34 -0
  85. data/lib/bridgetown-core/resource/taxonomy_type.rb +56 -0
  86. data/lib/bridgetown-core/resource/transformer.rb +177 -0
  87. data/lib/bridgetown-core/ruby_template_view.rb +11 -11
  88. data/lib/bridgetown-core/site.rb +13 -6
  89. data/lib/bridgetown-core/static_file.rb +33 -10
  90. data/lib/bridgetown-core/tags/highlight.rb +2 -15
  91. data/lib/bridgetown-core/tags/include.rb +1 -1
  92. data/lib/bridgetown-core/tags/post_url.rb +2 -2
  93. data/lib/bridgetown-core/url.rb +1 -0
  94. data/lib/bridgetown-core/utils.rb +49 -43
  95. data/lib/bridgetown-core/utils/require_gems.rb +60 -0
  96. data/lib/bridgetown-core/utils/ruby_exec.rb +6 -9
  97. data/lib/bridgetown-core/utils/ruby_front_matter.rb +39 -0
  98. data/lib/bridgetown-core/version.rb +2 -2
  99. data/lib/bridgetown-core/watcher.rb +2 -1
  100. data/lib/bridgetown-core/yaml_parser.rb +22 -0
  101. data/lib/site_template/config/.keep +0 -0
  102. data/lib/site_template/package.json.erb +4 -4
  103. data/lib/site_template/plugins/site_builder.rb +1 -1
  104. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +1 -1
  105. metadata +46 -41
  106. data/lib/bridgetown-core/external.rb +0 -58
  107. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  108. data/lib/bridgetown-core/path_manager.rb +0 -31
  109. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  110. data/lib/bridgetown-core/readers/static_file_reader.rb +0 -25
  111. data/lib/bridgetown-core/utils/exec.rb +0 -26
  112. data/lib/bridgetown-core/utils/internet.rb +0 -37
  113. data/lib/bridgetown-core/utils/platforms.rb +0 -80
  114. data/lib/bridgetown-core/utils/thread_event.rb +0 -31
  115. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -0,0 +1,329 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class Base
6
+ include Comparable
7
+ include Bridgetown::Publishable
8
+ include Bridgetown::LayoutPlaceable
9
+ include Bridgetown::LiquidRenderable
10
+
11
+ # @return [HashWithDotAccess::Hash]
12
+ attr_reader :data
13
+
14
+ # @return [Destination]
15
+ attr_reader :destination
16
+
17
+ # @return [Bridgetown::Model::Base]
18
+ attr_reader :model
19
+
20
+ # @return [Bridgetown::Site]
21
+ attr_reader :site
22
+
23
+ # @return [String]
24
+ attr_accessor :content, :untransformed_content, :output
25
+
26
+ DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
27
+
28
+ # @param site [Bridgetown::Site]
29
+ # @param origin [Bridgetown::Resource::Origin]
30
+ def initialize(model:)
31
+ @model = model
32
+ @site = model.site
33
+ self.data = HashWithDotAccess::Hash.new
34
+
35
+ trigger_hooks(:post_init)
36
+ end
37
+
38
+ # Collection associated with this resource
39
+ #
40
+ # @return [Bridgetown::Collection]
41
+ def collection
42
+ model.collection
43
+ end
44
+
45
+ # Layout associated with this resource
46
+ # This will output a warning if the layout can't be found.
47
+ #
48
+ # @return [Bridgetown::Layout]
49
+ def layout
50
+ return @layout if @layout
51
+ return if no_layout?
52
+
53
+ @layout = site.layouts[data.layout].tap do |layout|
54
+ unless layout
55
+ Bridgetown.logger.warn "Resource:", "Layout '#{data.layout}' " \
56
+ "requested via #{relative_path} does not exist."
57
+ end
58
+ end
59
+ end
60
+
61
+ # The relative path of source file or file-like origin
62
+ #
63
+ # @return [Pathname]
64
+ def relative_path
65
+ model.origin.relative_path
66
+ end
67
+
68
+ # @return [Bridgetown::Resource::Transformer]
69
+ def transformer
70
+ @transformer ||= Bridgetown::Resource::Transformer.new(self)
71
+ end
72
+
73
+ # @return [Bridgetown::Resource::Relations]
74
+ def relations
75
+ @relations ||= Bridgetown::Resource::Relations.new(self)
76
+ end
77
+
78
+ # @param new_data [HashWithDotAccess::Hash]
79
+ def data=(new_data)
80
+ unless new_data.is_a?(HashWithDotAccess::Hash)
81
+ raise "#{self.class} data should be of type HashWithDotAccess::Hash"
82
+ end
83
+
84
+ @data = new_data
85
+ @data.default_proc = proc do |_, key|
86
+ site.frontmatter_defaults.find(
87
+ relative_path.to_s,
88
+ collection.label.to_sym,
89
+ key.to_s
90
+ )
91
+ end
92
+ end
93
+
94
+ # @return [Bridgetown::Resource::Base]
95
+ def read!
96
+ self.data = model.data_attributes
97
+ self.content = model.content # could be nil
98
+
99
+ unless collection.data?
100
+ self.untransformed_content = content
101
+ normalize_categories_and_tags
102
+ import_taxonomies_from_data
103
+ ensure_default_data
104
+ transformer.execute_inline_ruby!
105
+ set_date_from_string(data.date)
106
+ end
107
+
108
+ @destination = Destination.new(self) if requires_destination?
109
+
110
+ trigger_hooks(:post_read)
111
+
112
+ self
113
+ end
114
+ alias_method :read, :read! # TODO: eventually use the bang version only
115
+
116
+ def transform!
117
+ transformer.process! unless collection.data?
118
+ end
119
+
120
+ def trigger_hooks(hook_name, *args)
121
+ Bridgetown::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
122
+ Bridgetown::Hooks.trigger :resources, hook_name, self, *args
123
+ end
124
+
125
+ def around_hook(hook_suffix)
126
+ trigger_hooks :"pre_#{hook_suffix}"
127
+ yield
128
+ trigger_hooks :"post_#{hook_suffix}"
129
+ end
130
+
131
+ # @return [String]
132
+ def relative_path_basename_without_prefix
133
+ return_path = Pathname.new("")
134
+ relative_path.each_filename do |filename|
135
+ if matches = DATE_FILENAME_MATCHER.match(filename) # rubocop:disable Lint/AssignmentInCondition
136
+ filename = matches[2] + matches[3]
137
+ end
138
+
139
+ return_path += filename unless filename.starts_with?("_")
140
+ end
141
+
142
+ (return_path.dirname + return_path.basename(".*")).to_s
143
+ end
144
+
145
+ # @return [String]
146
+ def basename_without_ext
147
+ relative_path.basename(".*").to_s
148
+ end
149
+
150
+ # @return [String]
151
+ def extname
152
+ relative_path.extname
153
+ end
154
+
155
+ # @return [String, nil]
156
+ def permalink
157
+ data&.permalink
158
+ end
159
+
160
+ # @return [String]
161
+ def path
162
+ (model.origin.respond_to?(:original_path) ? model.origin.original_path : relative_path).to_s
163
+ end
164
+
165
+ # @return [String]
166
+ def absolute_url
167
+ format_url destination&.absolute_url
168
+ end
169
+
170
+ # @return [String]
171
+ def relative_url
172
+ format_url destination&.relative_url
173
+ end
174
+
175
+ # @return [String]
176
+ def id
177
+ model.origin.id
178
+ end
179
+
180
+ def date
181
+ data["date"] ||= site.time
182
+ end
183
+
184
+ # @return [Hash<String, Hash<String => Bridgetown::Resource::TaxonomyType,
185
+ # Array<Bridgetown::Resource::TaxonomyTerm>>>]
186
+ def taxonomies
187
+ @taxonomies ||= site.taxonomy_types.values.each_with_object(
188
+ HashWithDotAccess::Hash.new
189
+ ) do |taxonomy, hsh|
190
+ hsh[taxonomy.label] = {
191
+ type: taxonomy,
192
+ terms: [],
193
+ }
194
+ end
195
+ end
196
+
197
+ def requires_destination?
198
+ collection.write? && data.config&.output != false
199
+ end
200
+
201
+ def write?
202
+ requires_destination? && site.publisher.publish?(self)
203
+ end
204
+
205
+ # Write the generated Document file to the destination directory.
206
+ #
207
+ # dest - The String path to the destination dir.
208
+ #
209
+ # Returns nothing.
210
+ def write(_dest = nil)
211
+ destination.write(output)
212
+ trigger_hooks(:post_write)
213
+ end
214
+
215
+ def to_s
216
+ output || content || ""
217
+ end
218
+
219
+ # Create a Liquid-understandable version of this resource.
220
+ #
221
+ # @return [Drops::ResourceDrop] represents this resource's data.
222
+ def to_liquid
223
+ @to_liquid ||= Drops::ResourceDrop.new(self)
224
+ end
225
+
226
+ def to_h
227
+ {
228
+ id: id,
229
+ absolute_url: absolute_url,
230
+ relative_path: relative_path,
231
+ relative_url: relative_url,
232
+ date: date,
233
+ data: data,
234
+ taxonomies: taxonomies,
235
+ untransformed_content: untransformed_content,
236
+ content: content,
237
+ output: output,
238
+ }
239
+ end
240
+
241
+ def as_json(*)
242
+ to_h
243
+ end
244
+
245
+ ruby2_keywords def to_json(*options)
246
+ as_json(*options).to_json(*options)
247
+ end
248
+
249
+ def inspect
250
+ "#<#{self.class} #{id}>"
251
+ end
252
+
253
+ # Compare this document against another document.
254
+ # Comparison is a comparison between the 2 paths of the documents.
255
+ #
256
+ # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
257
+ # equal or greater than the other doc's path. See String#<=> for more details.
258
+ def <=>(other) # rubocop:todo Metrics/AbcSize
259
+ return nil unless other.respond_to?(:data)
260
+
261
+ if data.date.respond_to?(:to_datetime) && other.data.date.respond_to?(:to_datetime)
262
+ return data.date.to_datetime <=> other.data.date.to_datetime
263
+ end
264
+
265
+ cmp = data["date"] <=> other.data["date"]
266
+ cmp = path <=> other.path if cmp.nil? || cmp.zero?
267
+ cmp
268
+ end
269
+
270
+ def next_resource
271
+ pos = collection.resources.index { |item| item.equal?(self) }
272
+ collection.resources[pos + 1] if pos && pos < collection.resources.length - 1
273
+ end
274
+ alias_method :next_doc, :next_resource
275
+
276
+ def previous_resource
277
+ pos = collection.resources.index { |item| item.equal?(self) }
278
+ collection.resources[pos - 1] if pos&.positive?
279
+ end
280
+ alias_method :previous_doc, :previous_resource
281
+
282
+ private
283
+
284
+ def ensure_default_data
285
+ slug = if matches = relative_path.to_s.match(DATE_FILENAME_MATCHER) # rubocop:disable Lint/AssignmentInCondition
286
+ set_date_from_string(matches[1]) unless data.date
287
+ matches[2]
288
+ else
289
+ basename_without_ext
290
+ end
291
+
292
+ data.slug ||= slug
293
+ data.title ||= Bridgetown::Utils.titleize_slug(slug)
294
+ end
295
+
296
+ def set_date_from_string(new_date) # rubocop:disable Naming/AccessorMethodName
297
+ return unless new_date.is_a?(String)
298
+
299
+ data.date = Bridgetown::Utils.parse_date(
300
+ new_date,
301
+ "Document '#{relative_path}' does not have a valid date in the #{model}."
302
+ )
303
+ end
304
+
305
+ def normalize_categories_and_tags
306
+ data.categories = Bridgetown::Utils.pluralized_array_from_hash(
307
+ data, :category, :categories
308
+ )
309
+ data.tags = Bridgetown::Utils.pluralized_array_from_hash(
310
+ data, :tag, :tags
311
+ )
312
+ end
313
+
314
+ def import_taxonomies_from_data
315
+ taxonomies.each do |_label, metadata|
316
+ Array(data[metadata.type.key]).each do |term|
317
+ metadata.terms << TaxonomyTerm.new(
318
+ resource: self, label: term, type: metadata.type
319
+ )
320
+ end
321
+ end
322
+ end
323
+
324
+ def format_url(url)
325
+ url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "")
326
+ end
327
+ end
328
+ end
329
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class Destination
6
+ # @return [Bridgetown::Resource::Base]
7
+ attr_accessor :resource
8
+
9
+ # @return [String]
10
+ attr_accessor :output_ext
11
+
12
+ # @param resource [Bridgetown::Resource::Base]
13
+ def initialize(resource)
14
+ @resource = resource
15
+ @output_ext = resource.transformer.final_ext
16
+ end
17
+
18
+ def absolute_url
19
+ Addressable::URI.parse(
20
+ resource.site.config.url.to_s + relative_url
21
+ ).normalize.to_s
22
+ end
23
+
24
+ def relative_url
25
+ @processor ||= PermalinkProcessor.new(resource)
26
+ @processor.transform
27
+ end
28
+
29
+ def final_ext
30
+ output_ext || resource.extname
31
+ end
32
+
33
+ def output_path
34
+ path = URL.unescape_path(relative_url)
35
+ path = path.delete_prefix(resource.site.baseurl) if resource.site.baseurl.present?
36
+ path = resource.site.in_dest_dir(path)
37
+ path = File.join(path, "index.html") if relative_url.end_with? "/"
38
+ path
39
+ end
40
+
41
+ def write(output)
42
+ path = output_path
43
+ FileUtils.mkdir_p(File.dirname(path))
44
+ Bridgetown.logger.debug "Writing:", path
45
+ File.write(path, output, mode: "wb")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class PermalinkProcessor
6
+ # @return [Bridgetown::Resource::Base]
7
+ attr_accessor :resource
8
+
9
+ attr_accessor :slugify_mode
10
+
11
+ def self.placeholder_processors
12
+ @placeholder_processors || {}
13
+ end
14
+
15
+ def self.register_placeholder(key, block)
16
+ @placeholder_processors ||= {}
17
+ @placeholder_processors[key] = block
18
+ end
19
+
20
+ def initialize(resource)
21
+ @resource = resource
22
+ @slugify_mode = @resource.site.config.slugify_mode
23
+ end
24
+
25
+ def final_ext
26
+ resource.method(:destination).arity == 1 ? resource.extname : resource.destination.final_ext
27
+ end
28
+
29
+ def transform
30
+ permalink = resource.data.permalink ||
31
+ permalink_for_permalink_style(resource.collection.default_permalink)
32
+
33
+ # Strip out file extension and process each segment of a URL to swap out
34
+ # placeholders such as :categories or :title
35
+ url_segments = Bridgetown::Filters::URLFilters.strip_extname(permalink).split("/")
36
+ new_url = url_segments.map do |segment|
37
+ segment.starts_with?(":") ? process_segment(segment.sub(%r{^:}, "")) : segment
38
+ end.select(&:present?).join("/")
39
+
40
+ # No relative URLs should ever end in /index.html
41
+ new_url.sub!(%r{/index$}, "") if final_ext == ".html"
42
+
43
+ add_baseurl finalize_permalink(new_url, permalink)
44
+ end
45
+
46
+ def process_segment(segment)
47
+ segment = segment.to_sym
48
+ if self.class.placeholder_processors[segment]
49
+ segment_value = self.class.placeholder_processors[segment].(resource)
50
+ if segment_value.is_a?(Hash)
51
+ segment_value[:raw_value]
52
+ elsif segment_value.is_a?(Array)
53
+ segment_value.map do |subsegment|
54
+ Utils.slugify(subsegment, mode: slugify_mode)
55
+ end.join("/")
56
+ else
57
+ Utils.slugify(segment_value, mode: slugify_mode)
58
+ end
59
+ else
60
+ segment
61
+ end
62
+ end
63
+
64
+ def permalink_for_permalink_style(permalink_style)
65
+ collection_prefix = ("/:collection" unless resource.collection.builtin?)
66
+
67
+ case permalink_style.to_sym
68
+ when :pretty
69
+ "#{collection_prefix}/:categories/:year/:month/:day/:slug/"
70
+ when :pretty_ext, :date
71
+ "#{collection_prefix}/:categories/:year/:month/:day/:slug.*"
72
+ when :simple
73
+ "#{collection_prefix}/:categories/:slug/"
74
+ when :simple_ext
75
+ "#{collection_prefix}/:categories/:slug.*"
76
+ else
77
+ permalink_style.to_s
78
+ end
79
+ end
80
+
81
+ # @param new_url [String]
82
+ # @param permalink [String]
83
+ def finalize_permalink(new_url, permalink)
84
+ # Handle .* style permalinks or files not ending in .html
85
+ if permalink.ends_with?(".*") || !%r{\.html?$}.match?(final_ext)
86
+ "/#{new_url}#{final_ext}"
87
+ # If permalink includes the file extension, add it back in to the URL
88
+ elsif permalink =~ %r{\.[^/]*$}
89
+ "/#{new_url}#{Regexp.last_match[0]}"
90
+ # Ensure index-style URLs get output correctly
91
+ elsif permalink.ends_with?("/")
92
+ "/#{new_url}/".sub(%r{^/index/$}, "/")
93
+ # We good :)
94
+ else
95
+ "/#{new_url}"
96
+ end
97
+ end
98
+
99
+ def add_baseurl(permalink)
100
+ resource.site.baseurl.present? ? "#{resource.site.baseurl}#{permalink}" : permalink
101
+ end
102
+
103
+ ### Default Placeholders Processors
104
+
105
+ # @param resource [Bridgetown::Resource::Base]
106
+ register_placeholder :path, ->(resource) do
107
+ { raw_value: resource.relative_path_basename_without_prefix }
108
+ end
109
+
110
+ # @param resource [Bridgetown::Resource::Base]
111
+ register_placeholder :name, ->(resource) do
112
+ resource.basename_without_ext
113
+ end
114
+
115
+ # @param resource [Bridgetown::Resource::Base]
116
+ register_placeholder :slug, ->(resource) do
117
+ resource.data.slug || placeholder_processors[:name].(resource)
118
+ end
119
+
120
+ # @param resource [Bridgetown::Resource::Base]
121
+ register_placeholder :title, ->(resource) do
122
+ resource.data.title || placeholder_processors[:slug].(resource)
123
+ end
124
+
125
+ # @param resource [Bridgetown::Resource::Base]
126
+ register_placeholder :locale, ->(resource) do
127
+ locale_data = resource.data.locale
128
+ resource.site.config.available_locales.include?(locale_data) ? locale_data : nil
129
+ end
130
+ register_placeholder :lang, placeholder_processors[:locale]
131
+
132
+ # @param resource [Bridgetown::Resource::Base]
133
+ register_placeholder :collection, ->(resource) do
134
+ resource.collection.label
135
+ end
136
+
137
+ # @param resource [Bridgetown::Resource::Base]
138
+ register_placeholder :categories, ->(resource) do
139
+ resource.taxonomies.category&.terms&.map(&:label)&.uniq
140
+ end
141
+
142
+ # YYYY
143
+ # @param resource [Bridgetown::Resource::Base]
144
+ register_placeholder :year, ->(resource) do
145
+ resource.date.strftime("%Y")
146
+ end
147
+
148
+ # MM: 01..12
149
+ # @param resource [Bridgetown::Resource::Base]
150
+ register_placeholder :month, ->(resource) do
151
+ resource.date.strftime("%m")
152
+ end
153
+
154
+ # DD: 01..31
155
+ # @param resource [Bridgetown::Resource::Base]
156
+ register_placeholder :day, ->(resource) do
157
+ resource.date.strftime("%d")
158
+ end
159
+
160
+ # D: 1..31
161
+ # @param resource [Bridgetown::Resource::Base]
162
+ register_placeholder :i_day, ->(resource) do
163
+ resource.date.strftime("%-d")
164
+ end
165
+
166
+ # M: 1..12
167
+ # @param resource [Bridgetown::Resource::Base]
168
+ register_placeholder :i_month, ->(resource) do
169
+ resource.date.strftime("%-m")
170
+ end
171
+
172
+ # YY: 00..99
173
+ # @param resource [Bridgetown::Resource::Base]
174
+ register_placeholder :short_year, ->(resource) do
175
+ resource.date.strftime("%y")
176
+ end
177
+ end
178
+ end
179
+ end