bridgetown-core 0.19.3 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bridgetown-core.gemspec +1 -1
- data/lib/bridgetown-core.rb +30 -11
- data/lib/bridgetown-core/cleaner.rb +7 -1
- data/lib/bridgetown-core/collection.rb +173 -77
- data/lib/bridgetown-core/commands/base.rb +9 -0
- data/lib/bridgetown-core/commands/configure.rb +4 -0
- data/lib/bridgetown-core/commands/console.rb +4 -0
- data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
- data/lib/bridgetown-core/concerns/site/configurable.rb +7 -3
- data/lib/bridgetown-core/concerns/site/content.rb +57 -15
- data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
- data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
- data/lib/bridgetown-core/concerns/site/writable.rb +11 -1
- data/lib/bridgetown-core/concerns/validatable.rb +1 -0
- data/lib/bridgetown-core/configuration.rb +39 -19
- data/lib/bridgetown-core/converter.rb +14 -0
- data/lib/bridgetown-core/converters/identity.rb +0 -9
- data/lib/bridgetown-core/converters/markdown.rb +14 -4
- data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +3 -0
- data/lib/bridgetown-core/current.rb +10 -0
- data/lib/bridgetown-core/document.rb +6 -14
- data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
- data/lib/bridgetown-core/drops/page_drop.rb +4 -0
- data/lib/bridgetown-core/drops/resource_drop.rb +81 -0
- data/lib/bridgetown-core/drops/site_drop.rb +33 -8
- data/lib/bridgetown-core/drops/unified_payload_drop.rb +4 -0
- data/lib/bridgetown-core/entry_filter.rb +10 -23
- data/lib/bridgetown-core/errors.rb +0 -2
- data/lib/bridgetown-core/filters.rb +2 -1
- data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
- data/lib/bridgetown-core/layout.rb +2 -2
- data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
- data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
- data/lib/bridgetown-core/model/base.rb +138 -0
- data/lib/bridgetown-core/model/builder_origin.rb +40 -0
- data/lib/bridgetown-core/model/file_origin.rb +119 -0
- data/lib/bridgetown-core/model/origin.rb +38 -0
- data/lib/bridgetown-core/page.rb +9 -1
- data/lib/bridgetown-core/plugin_manager.rb +0 -2
- data/lib/bridgetown-core/publisher.rb +7 -1
- data/lib/bridgetown-core/reader.rb +25 -12
- data/lib/bridgetown-core/readers/data_reader.rb +3 -4
- data/lib/bridgetown-core/readers/post_reader.rb +1 -1
- data/lib/bridgetown-core/regenerator.rb +8 -1
- data/lib/bridgetown-core/related_posts.rb +1 -1
- data/lib/bridgetown-core/renderer.rb +5 -12
- data/lib/bridgetown-core/resource/base.rb +275 -0
- data/lib/bridgetown-core/resource/destination.rb +49 -0
- data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
- data/lib/bridgetown-core/resource/taxonomy_term.rb +25 -0
- data/lib/bridgetown-core/resource/taxonomy_type.rb +47 -0
- data/lib/bridgetown-core/resource/transformer.rb +173 -0
- data/lib/bridgetown-core/ruby_template_view.rb +4 -0
- data/lib/bridgetown-core/site.rb +9 -1
- data/lib/bridgetown-core/static_file.rb +33 -10
- data/lib/bridgetown-core/url.rb +1 -0
- data/lib/bridgetown-core/utils.rb +40 -40
- data/lib/bridgetown-core/utils/platforms.rb +1 -0
- data/lib/bridgetown-core/version.rb +2 -2
- data/lib/site_template/webpack.config.js.erb +8 -6
- metadata +28 -21
- data/lib/bridgetown-core/page_without_a_file.rb +0 -17
- data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
- data/lib/bridgetown-core/utils/exec.rb +0 -26
- data/lib/bridgetown-core/utils/internet.rb +0 -37
- data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -16,8 +16,8 @@ module Bridgetown
|
|
16
16
|
#
|
17
17
|
# Returns @content, a Hash of the .yaml, .yml,
|
18
18
|
# .json, and .csv files in the base directory
|
19
|
-
def read
|
20
|
-
base = site.in_source_dir(
|
19
|
+
def read
|
20
|
+
base = site.in_source_dir(site.config.data_dir)
|
21
21
|
read_data_to(base, @content)
|
22
22
|
merge_environment_specific_metadata!
|
23
23
|
@content = @content.with_dot_access
|
@@ -31,7 +31,7 @@ module Bridgetown
|
|
31
31
|
#
|
32
32
|
# Returns nothing
|
33
33
|
def read_data_to(dir, data)
|
34
|
-
return unless File.directory?(dir)
|
34
|
+
return unless File.directory?(dir)
|
35
35
|
|
36
36
|
entries = Dir.chdir(dir) do
|
37
37
|
Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) }
|
@@ -39,7 +39,6 @@ module Bridgetown
|
|
39
39
|
|
40
40
|
entries.each do |entry|
|
41
41
|
path = @site.in_source_dir(dir, entry)
|
42
|
-
next if @entry_filter.symlink?(path)
|
43
42
|
|
44
43
|
if File.directory?(path)
|
45
44
|
read_data_to(
|
@@ -19,7 +19,7 @@ module Bridgetown
|
|
19
19
|
# Checks if a renderable object needs to be regenerated
|
20
20
|
#
|
21
21
|
# Returns a boolean.
|
22
|
-
def regenerate?(document)
|
22
|
+
def regenerate?(document) # rubocop:todo Metrics/CyclomaticComplexity
|
23
23
|
return true if disabled
|
24
24
|
|
25
25
|
case document
|
@@ -27,6 +27,8 @@ module Bridgetown
|
|
27
27
|
regenerate_page?(document)
|
28
28
|
when Document
|
29
29
|
regenerate_document?(document)
|
30
|
+
when Bridgetown::Resource::Base
|
31
|
+
regenerate_resource?(document)
|
30
32
|
else
|
31
33
|
source_path = document.respond_to?(:path) ? document.path : nil
|
32
34
|
dest_path = document.destination(@site.dest) if document.respond_to?(:destination)
|
@@ -176,6 +178,11 @@ module Bridgetown
|
|
176
178
|
)
|
177
179
|
end
|
178
180
|
|
181
|
+
# TODO: need to manage dependencies, but for now always regenerate
|
182
|
+
def regenerate_resource?(_resource)
|
183
|
+
true
|
184
|
+
end
|
185
|
+
|
179
186
|
def existing_file_modified?(path)
|
180
187
|
# If one of this file dependencies have been modified,
|
181
188
|
# set the regeneration bit for both the dependency and the file to true
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Bridgetown
|
4
|
+
# This class handles the output rendering and layout placement of pages and
|
5
|
+
# documents. For rendering of resources in particular, see Bridgetown::Resource::Transformer
|
4
6
|
class Renderer
|
5
7
|
attr_reader :document, :site
|
6
8
|
|
@@ -121,21 +123,11 @@ module Bridgetown
|
|
121
123
|
"in #{document.relative_path} does not exist."
|
122
124
|
end
|
123
125
|
|
124
|
-
def converters_for_layout(layout)
|
125
|
-
site.converters.select do |converter|
|
126
|
-
if converter.method(:matches).arity == 1
|
127
|
-
converter.matches(layout.ext)
|
128
|
-
else
|
129
|
-
converter.matches(layout.ext, layout)
|
130
|
-
end
|
131
|
-
end.sort
|
132
|
-
end
|
133
|
-
|
134
126
|
# Render layout content into document.output
|
135
127
|
#
|
136
128
|
# Returns String rendered content
|
137
129
|
def render_layout(output, layout)
|
138
|
-
layout_converters =
|
130
|
+
layout_converters = site.matched_converters_for_convertible(layout)
|
139
131
|
|
140
132
|
layout_content = layout.content.dup
|
141
133
|
layout_converters.reduce(layout_content) do |layout_output, converter|
|
@@ -163,7 +155,8 @@ module Bridgetown
|
|
163
155
|
|
164
156
|
def permalink_ext
|
165
157
|
document_permalink = document.permalink
|
166
|
-
if document_permalink &&
|
158
|
+
if document_permalink &&
|
159
|
+
!document_permalink.end_with?("/")
|
167
160
|
permalink_ext = File.extname(document_permalink)
|
168
161
|
permalink_ext unless permalink_ext.empty?
|
169
162
|
end
|
@@ -0,0 +1,275 @@
|
|
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
|
+
# @!attribute [r] collection
|
27
|
+
# @return [Bridgetown::Collection] collection of this resource
|
28
|
+
|
29
|
+
# @!attribute [r] relative_path
|
30
|
+
# @return [Pathname] the relative path of source file or
|
31
|
+
# file-like origin
|
32
|
+
|
33
|
+
DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
|
34
|
+
|
35
|
+
# @param site [Bridgetown::Site]
|
36
|
+
# @param origin [Bridgetown::Resource::Origin]
|
37
|
+
def initialize(model:)
|
38
|
+
@model = model
|
39
|
+
@site = model.site
|
40
|
+
self.data = HashWithDotAccess::Hash.new
|
41
|
+
|
42
|
+
trigger_hooks(:post_init)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Bridgetown::Collection]
|
46
|
+
def collection
|
47
|
+
model.collection
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Pathname]
|
51
|
+
def relative_path
|
52
|
+
model.origin.relative_path
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [Bridgetown::Resource::Transformer]
|
56
|
+
def transformer
|
57
|
+
@transformer ||= Bridgetown::Resource::Transformer.new(self)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param new_data [HashWithDotAccess::Hash]
|
61
|
+
def data=(new_data)
|
62
|
+
unless new_data.is_a?(HashWithDotAccess::Hash)
|
63
|
+
raise "#{self.class} data should be of type HashWithDotAccess::Hash"
|
64
|
+
end
|
65
|
+
|
66
|
+
@data = new_data
|
67
|
+
@data.default_proc = proc do |_, key|
|
68
|
+
site.frontmatter_defaults.find(
|
69
|
+
relative_path.to_s,
|
70
|
+
collection.label.to_sym,
|
71
|
+
key.to_s
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Bridgetown::Resource::Base]
|
77
|
+
def read!
|
78
|
+
self.data = model.data_attributes
|
79
|
+
self.content = model.content # could be nil
|
80
|
+
|
81
|
+
unless collection.data?
|
82
|
+
self.untransformed_content = content
|
83
|
+
determine_slug_and_date
|
84
|
+
normalize_categories_and_tags
|
85
|
+
import_taxonomies_from_data
|
86
|
+
end
|
87
|
+
|
88
|
+
@destination = Destination.new(self) if requires_destination?
|
89
|
+
|
90
|
+
trigger_hooks(:post_read)
|
91
|
+
|
92
|
+
self
|
93
|
+
end
|
94
|
+
alias_method :read, :read! # TODO: eventually use the bang version only
|
95
|
+
|
96
|
+
def transform!
|
97
|
+
transformer.process! unless collection.data?
|
98
|
+
end
|
99
|
+
|
100
|
+
def trigger_hooks(hook_name, *args)
|
101
|
+
Bridgetown::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
|
102
|
+
Bridgetown::Hooks.trigger :resources, hook_name, self, *args
|
103
|
+
end
|
104
|
+
|
105
|
+
def around_hook(hook_suffix)
|
106
|
+
trigger_hooks :"pre_#{hook_suffix}"
|
107
|
+
yield
|
108
|
+
trigger_hooks :"post_#{hook_suffix}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def relative_path_basename_without_prefix
|
112
|
+
return_path = Pathname.new("")
|
113
|
+
relative_path.each_filename do |filename|
|
114
|
+
if matches = DATE_FILENAME_MATCHER.match(filename) # rubocop:disable Lint/AssignmentInCondition
|
115
|
+
filename = matches[2] + matches[3]
|
116
|
+
end
|
117
|
+
|
118
|
+
return_path += filename unless filename.starts_with?("_")
|
119
|
+
end
|
120
|
+
|
121
|
+
(return_path.dirname + return_path.basename(".*")).to_s
|
122
|
+
end
|
123
|
+
|
124
|
+
def basename_without_ext
|
125
|
+
relative_path.basename(".*").to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String]
|
129
|
+
def extname
|
130
|
+
relative_path.extname
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [String, nil]
|
134
|
+
def permalink
|
135
|
+
data&.permalink
|
136
|
+
end
|
137
|
+
|
138
|
+
def path
|
139
|
+
(model.origin.respond_to?(:original_path) ? model.origin.original_path : relative_path).to_s
|
140
|
+
end
|
141
|
+
|
142
|
+
def absolute_url
|
143
|
+
format_url destination&.absolute_url
|
144
|
+
end
|
145
|
+
|
146
|
+
def relative_url
|
147
|
+
format_url destination&.relative_url
|
148
|
+
end
|
149
|
+
alias_method :id, :relative_url
|
150
|
+
|
151
|
+
def date
|
152
|
+
data["date"] ||= site.time # TODO: this doesn't reflect documented behavior
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [Hash<String, Hash<String => Bridgetown::Resource::TaxonomyType,
|
156
|
+
# Array<Bridgetown::Resource::TaxonomyTerm>>>]
|
157
|
+
def taxonomies
|
158
|
+
@taxonomies ||= site.taxonomy_types.values.each_with_object(
|
159
|
+
HashWithDotAccess::Hash.new
|
160
|
+
) do |taxonomy, hsh|
|
161
|
+
hsh[taxonomy.label] = {
|
162
|
+
type: taxonomy,
|
163
|
+
terms: [],
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def requires_destination?
|
169
|
+
collection.write? && data.config&.output != false
|
170
|
+
end
|
171
|
+
|
172
|
+
def write?
|
173
|
+
requires_destination? && site.publisher.publish?(self)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Write the generated Document file to the destination directory.
|
177
|
+
#
|
178
|
+
# dest - The String path to the destination dir.
|
179
|
+
#
|
180
|
+
# Returns nothing.
|
181
|
+
def write(_dest = nil)
|
182
|
+
destination.write(output)
|
183
|
+
trigger_hooks(:post_write)
|
184
|
+
end
|
185
|
+
|
186
|
+
def to_s
|
187
|
+
output || content || ""
|
188
|
+
end
|
189
|
+
|
190
|
+
# Create a Liquid-understandable version of this resource.
|
191
|
+
#
|
192
|
+
# @return [Drops::DocumentDrop] represents this resource's data.
|
193
|
+
def to_liquid
|
194
|
+
@to_liquid ||= Drops::ResourceDrop.new(self)
|
195
|
+
end
|
196
|
+
|
197
|
+
def inspect
|
198
|
+
"#<#{self.class} [#{collection.label}] #{relative_path}>"
|
199
|
+
end
|
200
|
+
|
201
|
+
# Compare this document against another document.
|
202
|
+
# Comparison is a comparison between the 2 paths of the documents.
|
203
|
+
#
|
204
|
+
# Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
|
205
|
+
# equal or greater than the other doc's path. See String#<=> for more details.
|
206
|
+
def <=>(other) # rubocop:todo Metrics/AbcSize
|
207
|
+
return nil unless other.respond_to?(:data)
|
208
|
+
|
209
|
+
if data.date.respond_to?(:to_datetime) && other.data.date.respond_to?(:to_datetime)
|
210
|
+
return data.date.to_datetime <=> other.data.date.to_datetime
|
211
|
+
end
|
212
|
+
|
213
|
+
cmp = data["date"] <=> other.data["date"]
|
214
|
+
cmp = path <=> other.path if cmp.nil? || cmp.zero?
|
215
|
+
cmp
|
216
|
+
end
|
217
|
+
|
218
|
+
def next_resource
|
219
|
+
pos = collection.resources.index { |item| item.equal?(self) }
|
220
|
+
collection.resources[pos + 1] if pos && pos < collection.resources.length - 1
|
221
|
+
end
|
222
|
+
alias_method :next_doc, :next_resource
|
223
|
+
|
224
|
+
def previous_resource
|
225
|
+
pos = collection.docs.index { |item| item.equal?(self) }
|
226
|
+
collection.resources[pos - 1] if pos&.positive?
|
227
|
+
end
|
228
|
+
alias_method :previous_doc, :previous_resource
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def determine_slug_and_date
|
233
|
+
return unless relative_path.to_s =~ DATE_FILENAME_MATCHER
|
234
|
+
|
235
|
+
new_date, slug = Regexp.last_match.captures
|
236
|
+
modify_date(new_date)
|
237
|
+
|
238
|
+
slug.gsub!(%r!\.*\z!, "")
|
239
|
+
data.slug ||= slug
|
240
|
+
end
|
241
|
+
|
242
|
+
def modify_date(new_date)
|
243
|
+
if !data.date || data.date.to_i == site.time.to_i
|
244
|
+
data.date = Utils.parse_date(
|
245
|
+
new_date,
|
246
|
+
"Document '#{relative_path}' does not have a valid date in the #{model}."
|
247
|
+
)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def normalize_categories_and_tags
|
252
|
+
data.categories = Bridgetown::Utils.pluralized_array_from_hash(
|
253
|
+
data, :category, :categories
|
254
|
+
)
|
255
|
+
data.tags = Bridgetown::Utils.pluralized_array_from_hash(
|
256
|
+
data, :tag, :tags
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
def import_taxonomies_from_data
|
261
|
+
taxonomies.each do |_label, metadata|
|
262
|
+
Array(data[metadata.type.key]).each do |term|
|
263
|
+
metadata.terms << TaxonomyTerm.new(
|
264
|
+
resource: self, label: term, type: metadata.type
|
265
|
+
)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def format_url(url)
|
271
|
+
url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "")
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
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 = permalink.sub(%r{\.[^/]*$}, "").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
|