cloudcannon-jekyll 0.1.0 → 0.3.2

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,26 +1,27 @@
1
1
  {
2
- "time": {{ site.time | jsonify }},
2
+ "time": {{ site.time | date_to_xmlschema | cc_jsonify }},
3
3
  "cloudcannon": {
4
4
  "name": "cloudcannon-jekyll",
5
- "version": {{ gem_version | jsonify }}
5
+ "version": {{ gem_version | cc_jsonify }}
6
6
  },
7
7
  "generator": {
8
8
  "name": "jekyll",
9
- "version": {{ jekyll.version | jsonify }},
10
- "environment": {{ jekyll.env | jsonify }},
9
+ "version": {{ jekyll.version | cc_jsonify }},
10
+ "environment": {{ jekyll.env | cc_jsonify }},
11
11
  "metadata": {
12
- "markdown": {{ site.markdown | cc_safe_jsonify }},
13
- "kramdown": {{ site.kramdown | cc_safe_jsonify }},
14
- "commonmark": {{ site.commonmark | cc_safe_jsonify }}
12
+ "markdown": {{ site.markdown | cc_jsonify }},
13
+ "kramdown": {{ site.kramdown | cc_jsonify }},
14
+ "commonmark": {{ site.commonmark | cc_jsonify }}
15
15
  }
16
16
  },{% if site.cloudcannon.data.first %}{% assign data_seen = false %}
17
17
  "data": {
18
- {% for data in site.data %}{% assign key = data[0] %}{% if site.cloudcannon.data[key] %}{% if data_seen %},{% endif %}{{ data[0] | cc_safe_jsonify }}: {{ data[1] | cc_safe_jsonify }}{% assign data_seen = true %}{% endif %}{% endfor %}},
19
- {% elsif site.cloudcannon.data %}"data": {{ site.data | cc_safe_jsonify }},{% endif %}
18
+ {% for data in site.data %}{% assign key = data[0] %}{% if site.cloudcannon.data[key] %}{% if data_seen %},{% endif %}{{ data[0] | cc_jsonify }}: {{ data[1] | cc_jsonify }}{% assign data_seen = true %}{% endif %}{% endfor %}},
19
+ {% elsif site.cloudcannon.data %}"data": {{ site.data | cc_jsonify }},{% endif %}
20
20
  "collections": {
21
- {% for collection in site.collections %}"{{ collection.label | xml_escape }}": {{ collection.docs | cc_safe_jsonify }}{% unless forloop.last %},{% endunless %}
21
+ "drafts": {{ drafts | cc_jsonify }}{% if site.collections.size > 0 %},{% endif %}
22
+ {% for collection in site.collections %}"{{ collection.label | xml_escape }}": {{ collection.docs | cc_jsonify }}{% unless forloop.last %},{% endunless %}
22
23
  {% endfor %}
23
24
  },
24
- "pages": {{ site.pages | cc_safe_jsonify }},
25
- "static": {{ site.static_files | cc_static_files_jsonify }}
25
+ "pages": {{ site.pages | cc_jsonify }},
26
+ "static-pages": {{ site.static_files | cc_static_files_jsonify }}
26
27
  }
@@ -1,17 +1,17 @@
1
1
  {
2
- "time": {{ site.time | jsonify }},
2
+ "time": {{ site.time | date_to_xmlschema | cc_jsonify }},
3
3
  "cloudcannon": {
4
4
  "name": "cloudcannon-jekyll",
5
- "version": {{ gem_version | jsonify }}
5
+ "version": {{ gem_version | cc_jsonify }}
6
6
  },
7
7
  "generator": {
8
8
  "name": "jekyll",
9
- "version": {{ jekyll.version | jsonify }},
10
- "environment": {{ jekyll.env | jsonify }},
9
+ "version": {{ jekyll.version | cc_jsonify }},
10
+ "environment": {{ jekyll.env | cc_jsonify }},
11
11
  "metadata": {
12
- "markdown": {{ site.markdown | cc_safe_jsonify }},
13
- "kramdown": {{ site.kramdown | cc_safe_jsonify }},
14
- "commonmark": {{ site.commonmark | cc_safe_jsonify }}
12
+ "markdown": {{ site.markdown | cc_jsonify }},
13
+ "kramdown": {{ site.kramdown | cc_jsonify }},
14
+ "commonmark": {{ site.commonmark | cc_jsonify }}
15
15
  }
16
16
  },
17
17
  {% if site.cloudcannon.data.first -%}
@@ -21,20 +21,21 @@
21
21
  {%- assign key = data[0] %}
22
22
  {%- if site.cloudcannon.data[key] %}
23
23
  {%- if data_seen %},{% endif -%}
24
- {{ data[0] | cc_safe_jsonify }}: {{ data[1] | cc_safe_jsonify }}
24
+ {{ data[0] | cc_jsonify }}: {{ data[1] | cc_jsonify }}
25
25
  {%- assign data_seen = true %}
26
26
  {%- endif %}
27
27
  {%- endfor -%}
28
28
  },
29
29
  {% elsif site.cloudcannon.data -%}
30
- "data": {{ site.data | cc_safe_jsonify }},
30
+ "data": {{ site.data | cc_jsonify }},
31
31
  {%- endif %}
32
32
  "collections": {
33
+ "drafts": {{ drafts | cc_jsonify }}{% if site.collections.size > 0 %},{% endif %}
33
34
  {%- for collection in site.collections -%}
34
- "{{ collection.label | xml_escape }}": {{ collection.docs | cc_safe_jsonify }}
35
+ "{{ collection.label | xml_escape }}": {{ collection.docs | cc_jsonify }}
35
36
  {%- unless forloop.last %},{% endunless %}
36
37
  {%- endfor -%}
37
38
  },
38
- "pages": {{ site.pages | cc_safe_jsonify }},
39
- "static": {{ site.static_files | cc_static_files_jsonify }}
39
+ "pages": {{ site.pages | cc_jsonify }},
40
+ "static-pages": {{ site.static_files | cc_static_files_jsonify }}
40
41
  }
@@ -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,33 +2,133 @@
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
14
+ @reader = Reader.new(@site)
12
15
 
13
- details_data = { "gem_version" => CloudCannonJekyll::VERSION }
16
+ collections_config = @site.config["collections"].dup || {}
14
17
 
15
- generate_file("details", @site.site_payload.merge(details_data))
16
- generate_file("config", @site.site_payload)
18
+ # Workaround for empty collection configurations
19
+ collections_config.each_key do |key|
20
+ collections_config[key] ||= { "output" => false }
21
+ end
17
22
 
18
- @site.keep_files ||= []
19
- @site.keep_files << path("details")
20
- @site.keep_files << path("config")
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
+ # rubocop:disable Metrics/AbcSize
53
+ def add_category_folder_config(collections_config, posts_config = {})
54
+ posts = @site.posts || @site.collections["posts"]
55
+ docs = posts.class.method_defined?(:docs) ? posts.docs : posts
56
+ seen = {}
57
+
58
+ docs.map do |post|
59
+ parts = post.relative_path.split("/_posts/")
60
+ path = parts.first
61
+
62
+ # Ignore unless it's an unseen category folder post
63
+ next if parts.length < 2 || path.empty? || seen[path]
64
+
65
+ # Could check this to ensure raw files exist since posts can be generated without files
66
+ # next if @reader.read_posts(parts[0]).empty?
67
+
68
+ seen[path] = true
69
+ folder = path.sub(%r!^\/+!, "")
70
+ collections_path = "#{collections_dir}/#{folder}".gsub(%r!\/+!, "/").sub(%r!^\/+!, "")
71
+
72
+ collections_config["#{folder}/posts"] = posts_config.merge({
73
+ "_path" => "#{collections_path}/_posts",
74
+ })
75
+
76
+ # Adding the category draft config like this isn't ideal, since you could have drafts
77
+ # without posts, but it's a decent trade off vs looking for _drafts folders
78
+ collections_config["#{folder}/drafts"] = posts_config.merge({
79
+ "_path" => "#{collections_path}/_drafts",
80
+ })
81
+
82
+ path
83
+ end
84
+ end
85
+ # rubocop:enable Metrics/AbcSize
86
+
87
+ # Add data to collections config if raw data files exist
88
+ def add_data_config(collections_config)
89
+ data_files = @reader.read_data(data_dir)
90
+ collections_config["data"] = { "_path" => data_dir } if data_files&.keys&.any?
91
+ end
92
+
93
+ # Add posts/drafts to collections config
94
+ def add_blogging_config(collections_config)
95
+ collections_config["posts"] = { "output" => true } if Jekyll::VERSION.start_with? "2."
96
+ drafts = @reader.read_drafts(collections_dir)
97
+
98
+ if collections_config.key?("posts")
99
+ collections_config["drafts"] = collections_config["posts"].dup
100
+ elsif drafts.any?
101
+ collections_config["drafts"] = {}
102
+ end
103
+
104
+ folders = add_category_folder_config(collections_config, collections_config["posts"])
105
+ folders.compact.each do |folder|
106
+ drafts += @reader.read_drafts(folder)
107
+ end
108
+
109
+ drafts
110
+ end
111
+
112
+ # Add _path to each collection config
113
+ def add_collection_paths(collections_config)
114
+ collections_config.each do |key, collection|
115
+ next if collection.key?("_path")
116
+
117
+ collection["_path"] = File.join(collections_dir, "_#{key}").sub(%r!^\/+!, "")
118
+ end
21
119
  end
22
120
 
23
121
  def generate_file(filename, data)
24
122
  dest = destination_path(filename)
25
123
  FileUtils.mkdir_p(File.dirname(dest))
26
- File.open(dest, "w") { |f| f.write(file_content(filename, data)) }
124
+ File.open(dest, "w") { |file| file.write(file_content(filename, data)) }
125
+ @site.keep_files ||= []
126
+ @site.keep_files << path(filename)
27
127
  end
28
128
 
29
129
  def version_path_suffix
30
130
  return "-2.x" if Jekyll::VERSION.start_with? "2."
31
- return "-3.0-4.x" if Jekyll::VERSION.match? %r!3\.[0-4]\.!
131
+ return "-3.0-4.x" if %r!3\.[0-4]\.! =~ Jekyll::VERSION
32
132
 
33
133
  ""
34
134
  end
@@ -46,13 +146,13 @@ module CloudCannonJekyll
46
146
  end
47
147
 
48
148
  def file_content(filename, data)
49
- json = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", path(filename))
50
- json.content = File.read(source_path(filename))
51
- json.data["layout"] = nil
52
- json.data["sitemap"] = false
53
- json.data["permalink"] = "/#{path(filename)}"
54
- json.render({}, data)
55
- json.output
149
+ page = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", path(filename))
150
+ page.content = File.read(source_path(filename))
151
+ page.data["layout"] = nil
152
+ page.data["sitemap"] = false
153
+ page.data["permalink"] = "/#{path(filename)}"
154
+ page.render({}, data)
155
+ page.output
56
156
  end
57
157
  end
58
158
  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 > 3 && 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