cloudcannon-jekyll 0.0.7 → 0.3.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.
@@ -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,149 @@
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
+ payload = @site.site_payload.merge({
19
+ "gem_version" => CloudCannonJekyll::VERSION,
20
+ })
21
+
22
+ drafts = add_blogging_config(collections_config)
23
+ add_collection_paths(collections_config)
24
+ add_data_config(collections_config)
25
+
26
+ generate_file("config", payload.merge({
27
+ "pwd" => Dir.pwd,
28
+ "config" => @site.config,
29
+ "collections" => collections_config,
30
+ }))
31
+
32
+ generate_file("details", payload.merge({
33
+ "drafts" => drafts,
34
+ }))
35
+ end
36
+
37
+ def collections_dir
38
+ return "" if Jekyll::VERSION.start_with? "2."
39
+
40
+ @site.config["collections_dir"] || ""
41
+ end
42
+
43
+ def data_dir
44
+ @site.config["data_dir"] || "_data"
45
+ end
46
+
47
+ def add_category_folder_config(collections_config, posts_config = {})
48
+ posts = @site.posts || @site.collections["posts"]
49
+ seen = {}
50
+
51
+ posts.map do |post|
52
+ parts = post.relative_path.split("/_posts/")
53
+ path = parts.first
54
+
55
+ # Ignore unless it's an unseen category folder post
56
+ next if parts.length < 2 || path.empty? || seen[path]
57
+
58
+ # Could check this to ensure raw files exist since posts can be generated without files
59
+ # next if @reader.read_posts(parts[0]).empty?
60
+
61
+ seen[path] = true
62
+ folder = path.sub(%r!^\/+!, "")
63
+ collections_path = "#{collections_dir}/#{folder}".gsub(%r!\/+!, "/").sub(%r!^\/+!, "")
64
+
65
+ collections_config["#{folder}/posts"] = posts_config.merge({
66
+ "_path" => "#{collections_path}/_posts",
67
+ })
68
+
69
+ # Adding the category draft config like this isn't ideal, since you could have drafts
70
+ # without posts, but it's a decent trade off vs looking for _drafts folders
71
+ collections_config["#{folder}/drafts"] = posts_config.merge({
72
+ "_path" => "#{collections_path}/_drafts",
73
+ })
74
+
75
+ path
76
+ end
77
+ end
78
+
79
+ # Add data to collections config if raw data files exist
80
+ def add_data_config(collections_config)
81
+ data_files = @reader.read_data(data_dir)
82
+ collections_config["data"] = { "_path" => data_dir } if data_files&.keys&.any?
83
+ end
84
+
85
+ # Add posts/drafts to collections config
86
+ def add_blogging_config(collections_config)
87
+ collections_config["posts"] = { "output" => true } if Jekyll::VERSION.start_with? "2."
88
+ drafts = @reader.read_drafts(collections_dir)
89
+
90
+ if collections_config.key?("posts")
91
+ collections_config["drafts"] = collections_config["posts"].dup
92
+ elsif drafts.any?
93
+ collections_config["drafts"] = {}
94
+ end
95
+
96
+ folders = add_category_folder_config(collections_config, collections_config["posts"])
97
+ folders.compact.each do |folder|
98
+ drafts += @reader.read_drafts(folder)
99
+ end
100
+
101
+ drafts
102
+ end
103
+
104
+ # Add _path to each collection config
105
+ def add_collection_paths(collections_config)
106
+ collections_config.each do |key, collection|
107
+ next if collection.key?("_path")
108
+
109
+ collection["_path"] = File.join(collections_dir, "_#{key}").sub(%r!^\/+!, "")
110
+ end
111
+ end
112
+
113
+ def generate_file(filename, data)
114
+ dest = destination_path(filename)
115
+ FileUtils.mkdir_p(File.dirname(dest))
116
+ File.open(dest, "w") { |file| file.write(file_content(filename, data)) }
14
117
  @site.keep_files ||= []
15
- @site.keep_files << "_cloudcannon/details.json"
118
+ @site.keep_files << path(filename)
16
119
  end
17
120
 
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?
121
+ def version_path_suffix
122
+ return "-2.x" if Jekyll::VERSION.start_with? "2."
123
+ return "-3.0-4.x" if %r!3\.[0-4]\.! =~ Jekyll::VERSION
22
124
 
23
- File.expand_path(path, File.dirname(__FILE__))
125
+ ""
24
126
  end
25
127
 
26
- def destination_path
27
- Jekyll.sanitized_path(@site.dest, "_cloudcannon/details.json")
128
+ def path(filename, suffix = "")
129
+ "_cloudcannon/#{filename}#{suffix}.json"
28
130
  end
29
131
 
30
- def file_content
31
- json = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", "_cloudcannon/details.json")
32
- json.content = File.read(source_path)
132
+ def source_path(filename)
133
+ File.expand_path(path(filename, version_path_suffix), File.dirname(__FILE__))
134
+ end
33
135
 
34
- json.data["layout"] = nil
35
- json.data["sitemap"] = false
36
- json.data["permalink"] = "/_cloudcannon/details.json"
136
+ def destination_path(filename)
137
+ Jekyll.sanitized_path(@site.dest, path(filename))
138
+ end
37
139
 
38
- json.render({}, @site.site_payload.merge("gem_version" => CloudCannonJekyll::VERSION))
39
- json.output
140
+ def file_content(filename, data)
141
+ page = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", path(filename))
142
+ page.content = File.read(source_path(filename))
143
+ page.data["layout"] = nil
144
+ page.data["sitemap"] = false
145
+ page.data["permalink"] = "/#{path(filename)}"
146
+ page.render({}, data)
147
+ page.output
40
148
  end
41
149
  end
42
150
  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