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.
- checksums.yaml +4 -4
- data/.github/workflows/stale.yml +19 -0
- data/.gitignore +1 -0
- data/.reek.yml +6 -0
- data/.rubocop.yml +24 -2
- data/.travis.yml +2 -1
- data/Gemfile +2 -0
- data/HISTORY.md +45 -0
- data/cloudcannon-jekyll.gemspec +5 -4
- data/lib/cloudcannon-jekyll.rb +4 -4
- data/lib/cloudcannon-jekyll/_cloudcannon/config-2.x.json +36 -0
- data/lib/cloudcannon-jekyll/_cloudcannon/config-3.0-4.x.json +36 -0
- data/lib/cloudcannon-jekyll/_cloudcannon/config.json +57 -0
- data/lib/cloudcannon-jekyll/_cloudcannon/details-2.x.json +16 -13
- data/lib/cloudcannon-jekyll/_cloudcannon/details-3.0-4.x.json +27 -0
- data/lib/cloudcannon-jekyll/_cloudcannon/details.json +28 -15
- data/lib/cloudcannon-jekyll/configuration.rb +2 -1
- data/lib/cloudcannon-jekyll/generator.rb +131 -18
- data/lib/cloudcannon-jekyll/jsonify-filter.rb +218 -0
- data/lib/cloudcannon-jekyll/page-without-a-file.rb +1 -0
- data/lib/cloudcannon-jekyll/reader.rb +44 -0
- data/lib/cloudcannon-jekyll/readers/data-reader.rb +18 -0
- data/lib/cloudcannon-jekyll/readers/old-data-reader.rb +55 -0
- data/lib/cloudcannon-jekyll/version.rb +1 -1
- data/script/ci-smoke-test +10 -0
- data/script/cibuild +1 -2
- data/script/release +4 -2
- data/script/test +1 -0
- metadata +32 -10
- data/lib/cloudcannon-jekyll/_cloudcannon/details-3.0.x.json +0 -24
- data/lib/cloudcannon-jekyll/safe-jsonify-filter.rb +0 -170
@@ -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
|
-
|
13
|
-
|
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 <<
|
123
|
+
@site.keep_files << path(filename)
|
16
124
|
end
|
17
125
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
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
|
-
|
130
|
+
""
|
24
131
|
end
|
25
132
|
|
26
|
-
def
|
27
|
-
|
133
|
+
def path(filename, suffix = "")
|
134
|
+
"_cloudcannon/#{filename}#{suffix}.json"
|
28
135
|
end
|
29
136
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
137
|
+
def source_path(filename)
|
138
|
+
File.expand_path(path(filename, version_path_suffix), File.dirname(__FILE__))
|
139
|
+
end
|
33
140
|
|
34
|
-
|
35
|
-
|
36
|
-
|
141
|
+
def destination_path(filename)
|
142
|
+
Jekyll.sanitized_path(@site.dest, path(filename))
|
143
|
+
end
|
37
144
|
|
38
|
-
|
39
|
-
|
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
|
@@ -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
|