cloudcannon-jekyll 0.0.8 → 0.3.1

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.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloudCannonJekyll
4
+ # Processes Jekyll configuration to enable the plugin is run and fix common issues
4
5
  class Configuration
5
6
  def self.processed?(site)
6
7
  site.instance_variable_get(:@_cloudcannon_jekyll_processed) == true
@@ -16,7 +17,7 @@ module CloudCannonJekyll
16
17
  config = config.fix_common_issues if config.respond_to? :fix_common_issues
17
18
  config = config.add_default_excludes if config.respond_to? :add_default_excludes
18
19
 
19
- key = Jekyll::VERSION.start_with?("2") ? "gems" : "plugins"
20
+ key = Jekyll::VERSION.start_with?("2.") ? "gems" : "plugins"
20
21
 
21
22
  config[key] = Array(config[key])
22
23
  config[key].push("cloudcannon-jekyll") unless config[key].include? "cloudcannon-jekyll"
@@ -2,41 +2,154 @@
2
2
 
3
3
  require "jekyll"
4
4
  require "fileutils"
5
+ require_relative "reader"
5
6
 
6
7
  module CloudCannonJekyll
8
+ # Generates JSON files containing build config and build output details
7
9
  class Generator < Jekyll::Generator
8
10
  priority :lowest
9
11
 
10
12
  def generate(site)
11
13
  @site = site
12
- FileUtils.mkdir_p(File.dirname(destination_path))
13
- File.open(destination_path, "w") { |f| f.write(file_content) }
14
+ @reader = Reader.new(@site)
15
+
16
+ collections_config = @site.config["collections"].dup || {}
17
+
18
+ # Workaround for empty collection configurations
19
+ collections_config.each_key do |key|
20
+ collections_config[key] ||= { "output" => false }
21
+ end
22
+
23
+ payload = @site.site_payload.merge({
24
+ "gem_version" => CloudCannonJekyll::VERSION,
25
+ })
26
+
27
+ drafts = add_blogging_config(collections_config)
28
+ add_collection_paths(collections_config)
29
+ add_data_config(collections_config)
30
+
31
+ generate_file("config", payload.merge({
32
+ "pwd" => Dir.pwd,
33
+ "config" => @site.config,
34
+ "collections" => collections_config,
35
+ }))
36
+
37
+ generate_file("details", payload.merge({
38
+ "drafts" => drafts,
39
+ }))
40
+ end
41
+
42
+ def collections_dir
43
+ return "" if Jekyll::VERSION.start_with? "2."
44
+
45
+ @site.config["collections_dir"] || ""
46
+ end
47
+
48
+ def data_dir
49
+ @site.config["data_dir"] || "_data"
50
+ end
51
+
52
+ def add_category_folder_config(collections_config, posts_config = {})
53
+ posts = @site.posts || @site.collections["posts"]
54
+ seen = {}
55
+
56
+ posts.map do |post|
57
+ parts = post.relative_path.split("/_posts/")
58
+ path = parts.first
59
+
60
+ # Ignore unless it's an unseen category folder post
61
+ next if parts.length < 2 || path.empty? || seen[path]
62
+
63
+ # Could check this to ensure raw files exist since posts can be generated without files
64
+ # next if @reader.read_posts(parts[0]).empty?
65
+
66
+ seen[path] = true
67
+ folder = path.sub(%r!^\/+!, "")
68
+ collections_path = "#{collections_dir}/#{folder}".gsub(%r!\/+!, "/").sub(%r!^\/+!, "")
69
+
70
+ collections_config["#{folder}/posts"] = posts_config.merge({
71
+ "_path" => "#{collections_path}/_posts",
72
+ })
73
+
74
+ # Adding the category draft config like this isn't ideal, since you could have drafts
75
+ # without posts, but it's a decent trade off vs looking for _drafts folders
76
+ collections_config["#{folder}/drafts"] = posts_config.merge({
77
+ "_path" => "#{collections_path}/_drafts",
78
+ })
79
+
80
+ path
81
+ end
82
+ end
83
+
84
+ # Add data to collections config if raw data files exist
85
+ def add_data_config(collections_config)
86
+ data_files = @reader.read_data(data_dir)
87
+ collections_config["data"] = { "_path" => data_dir } if data_files&.keys&.any?
88
+ end
89
+
90
+ # Add posts/drafts to collections config
91
+ def add_blogging_config(collections_config)
92
+ collections_config["posts"] = { "output" => true } if Jekyll::VERSION.start_with? "2."
93
+ drafts = @reader.read_drafts(collections_dir)
94
+
95
+ if collections_config.key?("posts")
96
+ collections_config["drafts"] = collections_config["posts"].dup
97
+ elsif drafts.any?
98
+ collections_config["drafts"] = {}
99
+ end
100
+
101
+ folders = add_category_folder_config(collections_config, collections_config["posts"])
102
+ folders.compact.each do |folder|
103
+ drafts += @reader.read_drafts(folder)
104
+ end
105
+
106
+ drafts
107
+ end
108
+
109
+ # Add _path to each collection config
110
+ def add_collection_paths(collections_config)
111
+ collections_config.each do |key, collection|
112
+ next if collection.key?("_path")
113
+
114
+ collection["_path"] = File.join(collections_dir, "_#{key}").sub(%r!^\/+!, "")
115
+ end
116
+ end
117
+
118
+ def generate_file(filename, data)
119
+ dest = destination_path(filename)
120
+ FileUtils.mkdir_p(File.dirname(dest))
121
+ File.open(dest, "w") { |file| file.write(file_content(filename, data)) }
14
122
  @site.keep_files ||= []
15
- @site.keep_files << "_cloudcannon/details.json"
123
+ @site.keep_files << path(filename)
16
124
  end
17
125
 
18
- def source_path
19
- path = "_cloudcannon/details.json"
20
- path = "_cloudcannon/details-2.x.json" if Jekyll::VERSION.start_with? "2."
21
- path = "_cloudcannon/details-3.0.x.json" unless (%r!3\.[0-4]\.! =~ Jekyll::VERSION).nil?
126
+ def version_path_suffix
127
+ return "-2.x" if Jekyll::VERSION.start_with? "2."
128
+ return "-3.0-4.x" if %r!3\.[0-4]\.! =~ Jekyll::VERSION
22
129
 
23
- File.expand_path(path, File.dirname(__FILE__))
130
+ ""
24
131
  end
25
132
 
26
- def destination_path
27
- Jekyll.sanitized_path(@site.dest, "_cloudcannon/details.json")
133
+ def path(filename, suffix = "")
134
+ "_cloudcannon/#{filename}#{suffix}.json"
28
135
  end
29
136
 
30
- def file_content
31
- json = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", "_cloudcannon/details.json")
32
- json.content = File.read(source_path)
137
+ def source_path(filename)
138
+ File.expand_path(path(filename, version_path_suffix), File.dirname(__FILE__))
139
+ end
33
140
 
34
- json.data["layout"] = nil
35
- json.data["sitemap"] = false
36
- json.data["permalink"] = "/_cloudcannon/details.json"
141
+ def destination_path(filename)
142
+ Jekyll.sanitized_path(@site.dest, path(filename))
143
+ end
37
144
 
38
- json.render({}, @site.site_payload.merge("gem_version" => CloudCannonJekyll::VERSION))
39
- json.output
145
+ def file_content(filename, data)
146
+ page = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", path(filename))
147
+ page.content = File.read(source_path(filename))
148
+ page.data["layout"] = nil
149
+ page.data["sitemap"] = false
150
+ page.data["permalink"] = "/#{path(filename)}"
151
+ page.render({}, data)
152
+ page.output
40
153
  end
41
154
  end
42
155
  end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+
5
+ module CloudCannonJekyll
6
+ # Filter for converting Jekyll objects into JSON
7
+ module JsonifyFilter
8
+ STATIC_EXTENSIONS = [".html", ".htm"].freeze
9
+
10
+ CC_JSONIFY_KEY_SWAPS = {
11
+ "collections" => {
12
+ "_sort_key" => "_sort-key",
13
+ "_subtext_key" => "_subtext-key",
14
+ "_image_key" => "_image-key",
15
+ "_image_size" => "_image-size",
16
+ "_singular_name" => "_singular-name",
17
+ "_singular_key" => "_singular-key",
18
+ "_disable_add" => "_disable-add",
19
+ "_add_options" => "_add-options",
20
+ },
21
+ }.freeze
22
+
23
+ @simple_types = [
24
+ String,
25
+ Numeric,
26
+ Integer,
27
+ Float,
28
+ Date,
29
+ Time,
30
+ NilClass,
31
+ Object.const_defined?("Fixnum") ? Fixnum : nil,
32
+ ].compact.freeze
33
+
34
+ @document_types = [
35
+ Jekyll::Document,
36
+ Jekyll::Page,
37
+ Jekyll::VERSION.start_with?("2.") ? Jekyll::Post : nil,
38
+ ].compact.freeze
39
+
40
+ def self.document_type?(input)
41
+ @document_types.include?(input.class)
42
+ end
43
+
44
+ def self.simple_type?(input)
45
+ @simple_types.include?(input.class) || [true, false].include?(input)
46
+ end
47
+
48
+ def self.static_file_to_json(input, depth, max_depth)
49
+ path = input.relative_path.sub(%r!^\/+!, "")
50
+ url = Jekyll::VERSION.start_with?("2.") ? "/#{path}" : input.url
51
+
52
+ out = [
53
+ "\"path\": #{JsonifyFilter.to_json(path, depth, max_depth)}",
54
+ "\"url\": #{JsonifyFilter.to_json(url, depth, max_depth)}",
55
+ ]
56
+
57
+ "{#{out.join(",")}}"
58
+ end
59
+
60
+ def self.document_data_to_a(data, prevent, depth, max_depth)
61
+ prevent += %w(content output next previous excerpt)
62
+
63
+ out = data.map do |key, value|
64
+ next if prevent.include? key
65
+
66
+ prevent.push key
67
+ "#{key.to_json}: #{JsonifyFilter.to_json(value, depth, max_depth)}"
68
+ end
69
+
70
+ out.compact
71
+ end
72
+
73
+ def self.legacy_post_to_json(input, depth, max_depth)
74
+ prevent = %w(dir name path url date id categories tags)
75
+
76
+ out = [
77
+ "\"name\": #{JsonifyFilter.to_json(input.name, depth, max_depth)}",
78
+ "\"path\": #{JsonifyFilter.to_json(input.path, depth, max_depth)}",
79
+ "\"url\": #{JsonifyFilter.to_json(input.url, depth, max_depth)}",
80
+ "\"date\": #{JsonifyFilter.to_json(input.date, depth, max_depth)}",
81
+ "\"id\": #{JsonifyFilter.to_json(input.id, depth, max_depth)}",
82
+ "\"categories\": #{JsonifyFilter.to_json(input.categories, depth, max_depth)}",
83
+ "\"tags\": #{JsonifyFilter.to_json(input.tags, depth, max_depth)}",
84
+ ]
85
+
86
+ out += JsonifyFilter.document_data_to_a(input.data, prevent, depth, max_depth)
87
+ "{#{out.join(",")}}"
88
+ end
89
+
90
+ def self.page_to_json(input, depth, max_depth)
91
+ prevent = %w(dir name path url)
92
+
93
+ out = [
94
+ "\"name\": #{JsonifyFilter.to_json(input.name, depth, max_depth)}",
95
+ "\"path\": #{JsonifyFilter.to_json(input.path, depth, max_depth)}",
96
+ "\"url\": #{JsonifyFilter.to_json(input.url, depth, max_depth)}",
97
+ ]
98
+
99
+ # Merge Jekyll Defaults into data for pages (missing at v3.8.5)
100
+ defaults = input.site.frontmatter_defaults.all(input.relative_path, :pages).tap do |default|
101
+ default.delete("date")
102
+ end
103
+
104
+ data = Jekyll::Utils.deep_merge_hashes(defaults, input.data)
105
+
106
+ out += JsonifyFilter.document_data_to_a(data, prevent, depth, max_depth)
107
+ "{#{out.join(",")}}"
108
+ end
109
+
110
+ def self.document_to_json(input, depth, max_depth)
111
+ prevent = %w(dir id relative_path url collection)
112
+
113
+ out = [
114
+ "\"path\": #{JsonifyFilter.to_json(input.relative_path, depth, max_depth)}",
115
+ "\"url\": #{JsonifyFilter.to_json(input.url, depth, max_depth)}",
116
+ ]
117
+
118
+ collection = input.collection
119
+ unless collection.nil?
120
+ collection_json = JsonifyFilter.to_json(collection.label, depth, max_depth)
121
+ out.push("\"collection\": #{collection_json}")
122
+ end
123
+
124
+ out += JsonifyFilter.document_data_to_a(input.data, prevent, depth, max_depth)
125
+ "{#{out.join(",")}}"
126
+ end
127
+
128
+ def self.array_to_json(input, depth, max_depth, key_swaps = {})
129
+ array = input.map do |value|
130
+ JsonifyFilter.to_json(value, depth, max_depth, key_swaps)
131
+ end
132
+
133
+ "[#{array.join(",")}]"
134
+ end
135
+
136
+ def self.hash_to_json(input, depth, max_depth, key_swaps = {})
137
+ out = input.map do |key, value|
138
+ string_key = (key_swaps[key] || key).to_s.to_json
139
+ "#{string_key}: #{JsonifyFilter.to_json(value, depth, max_depth, key_swaps)}"
140
+ end
141
+
142
+ "{#{out.join(",")}}"
143
+ end
144
+
145
+ def self.config_to_select_data_json(input, depth)
146
+ prevent = %w(source destination collections_dir cache_dir plugins_dir layouts_dir data_dir
147
+ includes_dir collections safe include exclude keep_files encoding markdown_ext
148
+ strict_front_matter show_drafts limit_posts future unpublished whitelist
149
+ plugins markdown highlighter lsi excerpt_separator incremental detach port host
150
+ baseurl show_dir_listing permalink paginate_path timezone quiet verbose defaults
151
+ liquid kramdown title url description uploads_dir _comments _options _editor
152
+ _explore _source_editor _array_structures maruku redcloth rdiscount redcarpet
153
+ gems plugins)
154
+
155
+ out = input.map do |key, value|
156
+ next unless value.is_a?(Array) || value.is_a?(Hash)
157
+ next if prevent.include? key
158
+
159
+ prevent.push key
160
+ "#{key.to_s.to_json}: #{JsonifyFilter.to_json(value, depth)}"
161
+ end
162
+
163
+ out.compact!
164
+
165
+ "{#{out.join(",")}}" if out.any?
166
+ end
167
+
168
+ def self.to_json(input, depth, max_depth = 9, key_swaps = {})
169
+ depth += 1
170
+
171
+ if depth > max_depth || (depth > 2 && JsonifyFilter.document_type?(input))
172
+ '"MAXIMUM_DEPTH"'
173
+ elsif JsonifyFilter.simple_type?(input)
174
+ input.to_json
175
+ elsif input.is_a?(Jekyll::StaticFile)
176
+ JsonifyFilter.static_file_to_json(input, depth, max_depth)
177
+ elsif input.is_a?(Jekyll::Page)
178
+ JsonifyFilter.page_to_json(input, depth, max_depth)
179
+ elsif Jekyll::VERSION.start_with?("2.") && input.is_a?(Jekyll::Post)
180
+ JsonifyFilter.legacy_post_to_json(input, depth, max_depth)
181
+ elsif input.is_a?(Jekyll::Document)
182
+ JsonifyFilter.document_to_json(input, depth, max_depth)
183
+ elsif input.is_a?(Array)
184
+ JsonifyFilter.array_to_json(input, depth, max_depth, key_swaps)
185
+ elsif input.is_a?(Hash)
186
+ JsonifyFilter.hash_to_json(input, depth, max_depth, key_swaps)
187
+ else
188
+ input.class.to_s.prepend("UNSUPPORTED:").to_json
189
+ end
190
+ end
191
+
192
+ def cc_static_files_jsonify(input)
193
+ out = input.map do |page|
194
+ JsonifyFilter.to_json(page, 1) if STATIC_EXTENSIONS.include?(page.extname)
195
+ end
196
+
197
+ out.compact!
198
+
199
+ "[#{out.join(",")}]"
200
+ end
201
+
202
+ def cc_select_data_jsonify(input)
203
+ if input.key? "_select_data"
204
+ JsonifyFilter.to_json(input["_select_data"], 0)
205
+ else
206
+ JsonifyFilter.config_to_select_data_json(input, 0)
207
+ end
208
+ end
209
+
210
+ def cc_jsonify(input, key_swaps_key = nil, max_depth = 8)
211
+ if CC_JSONIFY_KEY_SWAPS.key? key_swaps_key
212
+ JsonifyFilter.to_json(input, 0, max_depth, CC_JSONIFY_KEY_SWAPS[key_swaps_key])
213
+ else
214
+ JsonifyFilter.to_json(input, 0, max_depth)
215
+ end
216
+ end
217
+ end
218
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloudCannonJekyll
4
+ # Utility class to help generate files with no source file
4
5
  class PageWithoutAFile < Jekyll::Page
5
6
  def read_yaml(*)
6
7
  @data ||= {}
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll"
4
+
5
+ if !Jekyll::VERSION.start_with? "2."
6
+ require_relative "readers/data-reader"
7
+ else
8
+ require_relative "readers/old-data-reader"
9
+ end
10
+
11
+ module CloudCannonJekyll
12
+ # Wraps read functions into one class
13
+ class Reader
14
+ attr_reader :site
15
+
16
+ def initialize(site)
17
+ @site = site
18
+ end
19
+
20
+ def read_data(dir = "_data")
21
+ if Jekyll::VERSION.start_with? "2."
22
+ CloudCannonJekyll::OldDataReader.new(@site).read(dir)
23
+ else
24
+ CloudCannonJekyll::DataReader.new(@site).read(dir)
25
+ end
26
+ end
27
+
28
+ def read_drafts(dir = "")
29
+ if Jekyll::VERSION.start_with? "2."
30
+ @site.read_content(dir, "_drafts", Jekyll::Draft)
31
+ else
32
+ Jekyll::PostReader.new(@site).read_drafts(dir)
33
+ end
34
+ end
35
+
36
+ def read_posts(dir = "")
37
+ if Jekyll::VERSION.start_with? "2."
38
+ @site.read_content(dir, "_posts", Jekyll::Post)
39
+ else
40
+ Jekyll::PostReader.new(@site).read_posts(dir)
41
+ end
42
+ end
43
+ end
44
+ end