bridgetown-core 0.19.3 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -1
  3. data/lib/bridgetown-core.rb +30 -11
  4. data/lib/bridgetown-core/cleaner.rb +7 -1
  5. data/lib/bridgetown-core/collection.rb +173 -77
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/configure.rb +4 -0
  8. data/lib/bridgetown-core/commands/console.rb +4 -0
  9. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  10. data/lib/bridgetown-core/concerns/site/configurable.rb +7 -3
  11. data/lib/bridgetown-core/concerns/site/content.rb +57 -15
  12. data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
  13. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  14. data/lib/bridgetown-core/concerns/site/writable.rb +11 -1
  15. data/lib/bridgetown-core/concerns/validatable.rb +1 -0
  16. data/lib/bridgetown-core/configuration.rb +39 -19
  17. data/lib/bridgetown-core/converter.rb +14 -0
  18. data/lib/bridgetown-core/converters/identity.rb +0 -9
  19. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  20. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +3 -0
  21. data/lib/bridgetown-core/current.rb +10 -0
  22. data/lib/bridgetown-core/document.rb +6 -14
  23. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  24. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  25. data/lib/bridgetown-core/drops/resource_drop.rb +81 -0
  26. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  27. data/lib/bridgetown-core/drops/unified_payload_drop.rb +4 -0
  28. data/lib/bridgetown-core/entry_filter.rb +10 -23
  29. data/lib/bridgetown-core/errors.rb +0 -2
  30. data/lib/bridgetown-core/filters.rb +2 -1
  31. data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
  32. data/lib/bridgetown-core/layout.rb +2 -2
  33. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  34. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  35. data/lib/bridgetown-core/model/base.rb +138 -0
  36. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  37. data/lib/bridgetown-core/model/file_origin.rb +119 -0
  38. data/lib/bridgetown-core/model/origin.rb +38 -0
  39. data/lib/bridgetown-core/page.rb +9 -1
  40. data/lib/bridgetown-core/plugin_manager.rb +0 -2
  41. data/lib/bridgetown-core/publisher.rb +7 -1
  42. data/lib/bridgetown-core/reader.rb +25 -12
  43. data/lib/bridgetown-core/readers/data_reader.rb +3 -4
  44. data/lib/bridgetown-core/readers/post_reader.rb +1 -1
  45. data/lib/bridgetown-core/regenerator.rb +8 -1
  46. data/lib/bridgetown-core/related_posts.rb +1 -1
  47. data/lib/bridgetown-core/renderer.rb +5 -12
  48. data/lib/bridgetown-core/resource/base.rb +275 -0
  49. data/lib/bridgetown-core/resource/destination.rb +49 -0
  50. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  51. data/lib/bridgetown-core/resource/taxonomy_term.rb +25 -0
  52. data/lib/bridgetown-core/resource/taxonomy_type.rb +47 -0
  53. data/lib/bridgetown-core/resource/transformer.rb +173 -0
  54. data/lib/bridgetown-core/ruby_template_view.rb +4 -0
  55. data/lib/bridgetown-core/site.rb +9 -1
  56. data/lib/bridgetown-core/static_file.rb +33 -10
  57. data/lib/bridgetown-core/url.rb +1 -0
  58. data/lib/bridgetown-core/utils.rb +40 -40
  59. data/lib/bridgetown-core/utils/platforms.rb +1 -0
  60. data/lib/bridgetown-core/version.rb +2 -2
  61. data/lib/site_template/webpack.config.js.erb +8 -6
  62. metadata +28 -21
  63. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  64. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  65. data/lib/bridgetown-core/utils/exec.rb +0 -26
  66. data/lib/bridgetown-core/utils/internet.rb +0 -37
  67. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class TaxonomyTerm
6
+ attr_reader :resource
7
+
8
+ attr_reader :label
9
+
10
+ attr_reader :type
11
+
12
+ def initialize(resource:, label:, type:)
13
+ @resource = resource
14
+ @label = label
15
+ @type = type
16
+ end
17
+
18
+ def to_liquid
19
+ {
20
+ label: label,
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class TaxonomyType
6
+ # @return [Bridgetown::Site]
7
+ attr_reader :site
8
+
9
+ # @return [String] aka `category`, `tag`, `region`, etc.
10
+ attr_reader :label
11
+
12
+ # @return [String] the key used in front matter
13
+ attr_reader :key
14
+
15
+ # @return [HashWithDotAccess::Hash] any associated metadata
16
+ attr_reader :metadata
17
+
18
+ # @param site [Bridgetown::Site]
19
+ # @param label [String]
20
+ # @param key [String]
21
+ def initialize(site:, label:, key:, metadata:)
22
+ @site = site
23
+ @label = label
24
+ @key = key
25
+ @metadata = metadata
26
+ end
27
+
28
+ def terms
29
+ site.resources.map do |resource|
30
+ resource.taxonomies[label].terms
31
+ end.flatten.group_by(&:label).with_dot_access
32
+ end
33
+
34
+ def inspect
35
+ "#<#{self.class} label=#{label}>"
36
+ end
37
+
38
+ def to_liquid
39
+ {
40
+ "label" => label,
41
+ "key" => key,
42
+ "metadata" => metadata,
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class Transformer
6
+ # @return [Array<Hash>]
7
+ attr_reader :conversions
8
+
9
+ # @return [Bridgetown::Resource::Base]
10
+ attr_reader :resource
11
+
12
+ # @return [Bridgetown::Site]
13
+ attr_reader :site
14
+
15
+ # @return [String]
16
+ attr_reader :output_ext
17
+
18
+ def initialize(resource)
19
+ @resource = resource
20
+ @site = resource.site
21
+ execute_inline_ruby
22
+ @output_ext = output_ext_from_converters
23
+ end
24
+
25
+ # @return [String]
26
+ def final_ext
27
+ permalink_ext || output_ext
28
+ end
29
+
30
+ def process!
31
+ Bridgetown.logger.debug "Transforming:", resource.relative_path
32
+ resource.around_hook :render do
33
+ run_conversions
34
+ resource.place_in_layout? ? place_into_layouts : resource.output = resource.content.dup
35
+ end
36
+ end
37
+
38
+ def inspect
39
+ "#<#{self.class} Conversion Steps: #{conversions.length}>"
40
+ end
41
+
42
+ private
43
+
44
+ ### Utilities
45
+
46
+ def permalink_ext
47
+ resource_permalink = resource.permalink
48
+ if resource_permalink &&
49
+ !resource_permalink.end_with?("/") &&
50
+ !resource_permalink.end_with?(".*")
51
+ permalink_ext = File.extname(resource_permalink)
52
+ permalink_ext unless permalink_ext.empty?
53
+ end
54
+ end
55
+
56
+ # @return [Array<Bridgetown::Converter>]
57
+ def converters
58
+ @converters ||= site.matched_converters_for_convertible(resource)
59
+ end
60
+
61
+ # @return [String]
62
+ def output_ext_from_converters
63
+ @conversions = converters.map do |converter|
64
+ {
65
+ converter: converter,
66
+ output_ext: converter.output_ext(resource.extname),
67
+ }
68
+ end
69
+
70
+ conversions
71
+ .reverse
72
+ .find do |conversion|
73
+ conversions.length == 1 ||
74
+ !conversion[:converter].is_a?(Bridgetown::Converters::Identity)
75
+ end
76
+ .fetch(:output_ext)
77
+ end
78
+
79
+ # @return [Array<Bridgetown::Layout>]
80
+ def validated_layouts
81
+ layout = site.layouts[resource.data.layout]
82
+ warn_on_missing_layout layout, resource.data.layout
83
+
84
+ layout_list = Set.new([layout])
85
+ while layout
86
+ layout_name = layout.data.layout
87
+ layout = site.layouts[layout_name]
88
+ warn_on_missing_layout layout, layout_name
89
+
90
+ layout_list << layout
91
+ end
92
+
93
+ layout_list.to_a.compact
94
+ end
95
+
96
+ def warn_on_missing_layout(layout, layout_name)
97
+ if layout.nil? && layout_name
98
+ Bridgetown.logger.warn "Build Warning:", "Layout '#{layout_name}' " \
99
+ "requested via #{resource.relative_path} does not exist."
100
+ end
101
+ end
102
+
103
+ ### Transformation Actions
104
+
105
+ def execute_inline_ruby
106
+ return unless site.config.should_execute_inline_ruby?
107
+
108
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(resource, self)
109
+ end
110
+
111
+ def run_conversions # rubocop:disable Metrics/AbcSize
112
+ input = resource.content.to_s
113
+
114
+ # @param content [String]
115
+ # @param converter [Bridgetown::Converter]
116
+ resource.content = converters.each_with_index.inject(input) do |content, (converter, index)|
117
+ output = if converter.method(:convert).arity == 1
118
+ converter.convert content
119
+ else
120
+ converter.convert content, resource
121
+ end
122
+ conversions[index] = {
123
+ type: :content,
124
+ converter: converter,
125
+ output: Bridgetown.env.production? ? nil : output,
126
+ output_ext: conversions[index][:output_ext],
127
+ }
128
+ output
129
+ rescue StandardError => e
130
+ Bridgetown.logger.error "Conversion error:",
131
+ "#{converter.class} encountered an error while "\
132
+ "converting `#{resource.relative_path}'"
133
+ raise e
134
+ end
135
+ end
136
+
137
+ def place_into_layouts
138
+ Bridgetown.logger.debug "Placing in Layouts:", resource.relative_path
139
+ output = resource.content.dup
140
+ validated_layouts.each do |layout|
141
+ output = run_layout_conversions layout, output
142
+ end
143
+ resource.output = output
144
+ end
145
+
146
+ def run_layout_conversions(layout, output)
147
+ layout_converters = site.matched_converters_for_convertible(layout)
148
+ layout_input = layout.content.dup
149
+
150
+ layout_converters.inject(layout_input) do |content, converter|
151
+ next(content) unless [2, -2].include?(converter.method(:convert).arity)
152
+
153
+ layout.current_document = resource
154
+ layout.current_document_output = output
155
+ layout_output = converter.convert content, layout
156
+
157
+ conversions << {
158
+ type: :layout,
159
+ layout: layout,
160
+ converter: converter,
161
+ output: Bridgetown.env.production? ? nil : layout_output,
162
+ }
163
+ layout_output
164
+ rescue StandardError => e
165
+ Bridgetown.logger.error "Conversion error:",
166
+ "#{converter.class} encountered an error while "\
167
+ "converting `#{resource.relative_path}'"
168
+ raise e
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
@@ -41,6 +41,10 @@ module Bridgetown
41
41
  end
42
42
  end
43
43
 
44
+ def collections
45
+ site.collections
46
+ end
47
+
44
48
  def site_drop
45
49
  site.site_payload.site
46
50
  end
@@ -26,6 +26,10 @@ module Bridgetown
26
26
  # @return [Array<Page>]
27
27
  attr_accessor :pages
28
28
 
29
+ # NOTE: Eventually pages will be deprecated once the Resource content engine
30
+ # is default
31
+ alias_method :generated_pages, :pages
32
+
29
33
  attr_accessor :exclude, :include, :lsi, :highlighter, :permalink_style,
30
34
  :time, :future, :unpublished, :limit_posts,
31
35
  :keep_files, :baseurl, :data, :file_read_opts,
@@ -46,7 +50,7 @@ module Bridgetown
46
50
 
47
51
  ensure_not_in_dest
48
52
 
49
- Bridgetown.sites << self
53
+ Bridgetown::Current.site = self
50
54
  Bridgetown::Hooks.trigger :site, :after_init, self
51
55
 
52
56
  reset # Processable
@@ -64,5 +68,9 @@ module Bridgetown
64
68
  end
65
69
  end
66
70
  end
71
+
72
+ def inspect
73
+ "#<Bridgetown::Site #{metadata.inspect.delete_prefix("{").delete_suffix("}")}>"
74
+ end
67
75
  end
68
76
  end
@@ -4,7 +4,7 @@ module Bridgetown
4
4
  class StaticFile
5
5
  extend Forwardable
6
6
 
7
- attr_reader :relative_path, :extname, :name, :data
7
+ attr_reader :relative_path, :extname, :name, :data, :site, :collection
8
8
 
9
9
  def_delegator :to_liquid, :to_json, :to_json
10
10
 
@@ -25,8 +25,7 @@ module Bridgetown
25
25
  # base - The String path to the <source>.
26
26
  # dir - The String path between <source> and the file.
27
27
  # name - The String filename of the file.
28
- # rubocop: disable Metrics/ParameterLists
29
- def initialize(site, base, dir, name, collection = nil)
28
+ def initialize(site, base, dir, name, collection = nil) # rubocop:disable Metrics/ParameterLists
30
29
  @site = site
31
30
  @base = base
32
31
  @dir = dir
@@ -34,9 +33,15 @@ module Bridgetown
34
33
  @collection = collection
35
34
  @relative_path = File.join(*[@dir, @name].compact)
36
35
  @extname = File.extname(@name)
37
- @data = @site.frontmatter_defaults.all(relative_path, type)
36
+ @data = @site.frontmatter_defaults.all(relative_path, type).with_dot_access
37
+ if site.uses_resource? && !data.permalink
38
+ data.permalink = if collection && !collection.builtin?
39
+ "/:collection/:path.*"
40
+ else
41
+ "/:path.*"
42
+ end
43
+ end
38
44
  end
39
- # rubocop: enable Metrics/ParameterLists
40
45
 
41
46
  # Returns source file path.
42
47
  def path
@@ -51,8 +56,11 @@ module Bridgetown
51
56
  #
52
57
  # Returns destination file path.
53
58
  def destination(dest)
54
- dest = @site.in_dest_dir(dest)
55
- @site.in_dest_dir(dest, Bridgetown::URL.unescape_path(url))
59
+ dest = site.in_dest_dir(dest)
60
+ dest_url = url
61
+ dest_url = dest_url.delete_prefix(site.baseurl) if site.uses_resource? &&
62
+ site.baseurl.present? && collection
63
+ site.in_dest_dir(dest, Bridgetown::URL.unescape_path(dest_url))
56
64
  end
57
65
 
58
66
  def destination_rel_dir
@@ -118,6 +126,15 @@ module Bridgetown
118
126
  @basename ||= File.basename(name, extname).gsub(%r!\.*\z!, "")
119
127
  end
120
128
 
129
+ def relative_path_basename_without_prefix
130
+ return_path = Pathname.new("")
131
+ Pathname.new(cleaned_relative_path).each_filename do |filename|
132
+ return_path += filename unless filename.starts_with?("_")
133
+ end
134
+
135
+ (return_path.dirname + return_path.basename(".*")).to_s
136
+ end
137
+
121
138
  def placeholders
122
139
  {
123
140
  collection: @collection.label,
@@ -154,15 +171,21 @@ module Bridgetown
154
171
  # be overriden in the collection's configuration in bridgetown.config.yml.
155
172
  def url
156
173
  @url ||= begin
157
- base = if @collection.nil? || @collection.label == "posts"
174
+ newly_processed = false
175
+ special_posts_case = @collection&.label == "posts" &&
176
+ site.config.content_engine != "resource"
177
+ base = if @collection.nil? || special_posts_case
158
178
  cleaned_relative_path
179
+ elsif site.uses_resource?
180
+ newly_processed = true
181
+ Bridgetown::Resource::PermalinkProcessor.new(self).transform
159
182
  else
160
183
  Bridgetown::URL.new(
161
184
  template: @collection.url_template,
162
185
  placeholders: placeholders
163
186
  )
164
187
  end.to_s.chomp("/")
165
- base << extname
188
+ newly_processed ? base : "#{base}#{extname}"
166
189
  end
167
190
  end
168
191
 
@@ -174,7 +197,7 @@ module Bridgetown
174
197
  # Returns the front matter defaults defined for the file's URL and/or type
175
198
  # as defined in bridgetown.config.yml.
176
199
  def defaults
177
- @defaults ||= @site.frontmatter_defaults.all url, type
200
+ @defaults ||= site.frontmatter_defaults.all url, type
178
201
  end
179
202
 
180
203
  # Returns a debug string on inspecting the static file.
@@ -129,6 +129,7 @@ module Bridgetown
129
129
  #
130
130
  # Returns the escaped path.
131
131
  def self.escape_path(path)
132
+ path = path.to_s
132
133
  return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path)
133
134
 
134
135
  # Because URI.escape doesn't escape "?", "[" and "]" by default,
@@ -4,12 +4,9 @@ module Bridgetown
4
4
  module Utils
5
5
  extend self
6
6
  autoload :Ansi, "bridgetown-core/utils/ansi"
7
- autoload :Exec, "bridgetown-core/utils/exec"
8
- autoload :Internet, "bridgetown-core/utils/internet"
9
7
  autoload :RubyExec, "bridgetown-core/utils/ruby_exec"
10
8
  autoload :Platforms, "bridgetown-core/utils/platforms"
11
9
  autoload :ThreadEvent, "bridgetown-core/utils/thread_event"
12
- autoload :WinTZ, "bridgetown-core/utils/win_tz"
13
10
 
14
11
  # Constants for use in #slugify
15
12
  SLUGIFY_MODES = %w(raw default pretty simple ascii latin).freeze
@@ -74,38 +71,32 @@ module Bridgetown
74
71
  end
75
72
  end
76
73
 
77
- # Read array from the supplied hash favouring the singular key
78
- # and then the plural key, and handling any nil entries.
74
+ # Read array from the supplied hash, merging the singular key with the
75
+ # plural key as needing, and handling any nil or duplicate entries.
79
76
  #
80
- # hash - the hash to read from
81
- # singular_key - the singular key
82
- # plural_key - the plural key
83
- #
84
- # Returns an array
85
- def pluralized_array_from_hash(hash, singular_key, plural_key)
86
- array = []
87
- value = value_from_singular_key(hash, singular_key)
88
- value ||= value_from_plural_key(hash, plural_key)
77
+ # @param hsh [Hash] the hash to read from
78
+ # @param singular_key [Symbol] the singular key
79
+ # @param plural_key [Symbol] the plural key
80
+ # @return [Array]
81
+ def pluralized_array_from_hash(hsh, singular_key, plural_key)
82
+ array = [
83
+ hsh[singular_key],
84
+ value_from_plural_key(hsh, plural_key),
85
+ ]
89
86
 
90
- array << value
91
87
  array.flatten!
92
88
  array.compact!
89
+ array.uniq!
93
90
  array
94
91
  end
95
92
 
96
- def value_from_singular_key(hash, key)
97
- hash[key] if hash.key?(key) || (hash.default_proc && hash[key])
98
- end
99
-
100
- def value_from_plural_key(hash, key)
101
- if hash.key?(key) || (hash.default_proc && hash[key])
102
- val = hash[key]
103
- case val
104
- when String
105
- val.split
106
- when Array
107
- val.compact
108
- end
93
+ def value_from_plural_key(hsh, key)
94
+ val = hsh[key]
95
+ case val
96
+ when String
97
+ val.split
98
+ when Array
99
+ val.compact
109
100
  end
110
101
  end
111
102
 
@@ -124,7 +115,7 @@ module Bridgetown
124
115
 
125
116
  # Determines whether a given file has
126
117
  #
127
- # Returns true if the YAML front matter is present.
118
+ # @return [Boolean] if the YAML front matter is present.
128
119
  # rubocop: disable Naming/PredicateName
129
120
  def has_yaml_header?(file)
130
121
  File.open(file, "rb", &:readline).match? %r!\A---\s*\r?\n!
@@ -134,7 +125,7 @@ module Bridgetown
134
125
 
135
126
  # Determine whether the given content string contains Liquid Tags or Vaiables
136
127
  #
137
- # Returns true is the string contains sequences of `{%` or `{{`
128
+ # @return [Boolean] if the string contains sequences of `{%` or `{{`
138
129
  def has_liquid_construct?(content)
139
130
  return false if content.nil? || content.empty?
140
131
 
@@ -207,7 +198,7 @@ module Bridgetown
207
198
  slug.gsub!(%r!^\-|\-$!i, "")
208
199
 
209
200
  slug.downcase! unless cased
210
- Bridgetown.logger.warn("Warning:", "Empty `slug` generated for '#{string}'.") if slug.empty?
201
+
211
202
  slug
212
203
  end
213
204
 
@@ -347,7 +338,7 @@ module Bridgetown
347
338
 
348
339
  # Return an asset path based on the Webpack manifest file
349
340
  # @param site [Bridgetown::Site] The current site object
350
- # @param asset_type [String] js or css
341
+ # @param asset_type [String] js or css, or filename in manifest
351
342
  #
352
343
  # @return [String] Returns "MISSING_WEBPACK_MANIFEST" if the manifest
353
344
  # file isnt found
@@ -363,21 +354,30 @@ module Bridgetown
363
354
  manifest = JSON.parse(File.read(manifest_file))
364
355
 
365
356
  known_assets = %w(js css)
357
+ asset_path = nil
366
358
  if known_assets.include?(asset_type)
367
359
  asset_path = manifest["main.#{asset_type}"]
368
-
369
360
  log_webpack_asset_error(asset_type) && return if asset_path.nil?
370
-
371
- asset_path = asset_path.split("/").last
372
- return [static_frontend_path(site), asset_type, asset_path].join("/")
361
+ else
362
+ asset_path = manifest.find do |item, _|
363
+ item.sub(%r{^../(frontend/|src/)?}, "") == asset_type
364
+ end&.last
373
365
  end
374
366
 
375
- Bridgetown.logger.error("Unknown Webpack asset type", asset_type)
376
- nil
367
+ if asset_path
368
+ static_frontend_path(site, ["js", asset_path])
369
+ else
370
+ Bridgetown.logger.error("Unknown Webpack asset type", asset_type)
371
+ nil
372
+ end
377
373
  end
378
374
 
379
- def static_frontend_path(site)
380
- path_parts = [site.config["baseurl"].to_s.chomp("/"), "_bridgetown/static"]
375
+ def static_frontend_path(site, additional_parts = [])
376
+ path_parts = [
377
+ site.config["baseurl"].to_s.gsub(%r(^/|/$), ""),
378
+ "_bridgetown/static",
379
+ *additional_parts,
380
+ ]
381
381
  path_parts[0] = "/#{path_parts[0]}" unless path_parts[0].empty?
382
382
  Addressable::URI.parse(path_parts.join("/")).normalize.to_s
383
383
  end