ngage 0.0.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.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/exe/ngage +55 -0
- data/lib/ngage.rb +3 -0
- data/lib/ngage/jekyll.rb +204 -0
- data/lib/ngage/jekyll/cleaner.rb +111 -0
- data/lib/ngage/jekyll/collection.rb +235 -0
- data/lib/ngage/jekyll/command.rb +103 -0
- data/lib/ngage/jekyll/commands/build.rb +93 -0
- data/lib/ngage/jekyll/commands/clean.rb +45 -0
- data/lib/ngage/jekyll/commands/doctor.rb +173 -0
- data/lib/ngage/jekyll/commands/help.rb +34 -0
- data/lib/ngage/jekyll/commands/new.rb +157 -0
- data/lib/ngage/jekyll/commands/new_theme.rb +42 -0
- data/lib/ngage/jekyll/commands/serve.rb +354 -0
- data/lib/ngage/jekyll/commands/serve/live_reload_reactor.rb +122 -0
- data/lib/ngage/jekyll/commands/serve/livereload_assets/livereload.js +1183 -0
- data/lib/ngage/jekyll/commands/serve/servlet.rb +203 -0
- data/lib/ngage/jekyll/commands/serve/websockets.rb +81 -0
- data/lib/ngage/jekyll/configuration.rb +391 -0
- data/lib/ngage/jekyll/converter.rb +54 -0
- data/lib/ngage/jekyll/converters/identity.rb +41 -0
- data/lib/ngage/jekyll/converters/markdown.rb +116 -0
- data/lib/ngage/jekyll/converters/markdown/kramdown_parser.rb +122 -0
- data/lib/ngage/jekyll/converters/smartypants.rb +70 -0
- data/lib/ngage/jekyll/convertible.rb +253 -0
- data/lib/ngage/jekyll/deprecator.rb +50 -0
- data/lib/ngage/jekyll/document.rb +503 -0
- data/lib/ngage/jekyll/drops/collection_drop.rb +20 -0
- data/lib/ngage/jekyll/drops/document_drop.rb +69 -0
- data/lib/ngage/jekyll/drops/drop.rb +209 -0
- data/lib/ngage/jekyll/drops/excerpt_drop.rb +15 -0
- data/lib/ngage/jekyll/drops/jekyll_drop.rb +32 -0
- data/lib/ngage/jekyll/drops/site_drop.rb +56 -0
- data/lib/ngage/jekyll/drops/static_file_drop.rb +14 -0
- data/lib/ngage/jekyll/drops/unified_payload_drop.rb +26 -0
- data/lib/ngage/jekyll/drops/url_drop.rb +89 -0
- data/lib/ngage/jekyll/entry_filter.rb +127 -0
- data/lib/ngage/jekyll/errors.rb +20 -0
- data/lib/ngage/jekyll/excerpt.rb +180 -0
- data/lib/ngage/jekyll/external.rb +76 -0
- data/lib/ngage/jekyll/filters.rb +390 -0
- data/lib/ngage/jekyll/filters/date_filters.rb +110 -0
- data/lib/ngage/jekyll/filters/grouping_filters.rb +64 -0
- data/lib/ngage/jekyll/filters/url_filters.rb +68 -0
- data/lib/ngage/jekyll/frontmatter_defaults.rb +233 -0
- data/lib/ngage/jekyll/generator.rb +5 -0
- data/lib/ngage/jekyll/hooks.rb +106 -0
- data/lib/ngage/jekyll/layout.rb +62 -0
- data/lib/ngage/jekyll/liquid_extensions.rb +22 -0
- data/lib/ngage/jekyll/liquid_renderer.rb +63 -0
- data/lib/ngage/jekyll/liquid_renderer/file.rb +56 -0
- data/lib/ngage/jekyll/liquid_renderer/table.rb +98 -0
- data/lib/ngage/jekyll/log_adapter.rb +151 -0
- data/lib/ngage/jekyll/mime.types +825 -0
- data/lib/ngage/jekyll/page.rb +185 -0
- data/lib/ngage/jekyll/page_without_a_file.rb +14 -0
- data/lib/ngage/jekyll/plugin.rb +92 -0
- data/lib/ngage/jekyll/plugin_manager.rb +115 -0
- data/lib/ngage/jekyll/publisher.rb +23 -0
- data/lib/ngage/jekyll/reader.rb +154 -0
- data/lib/ngage/jekyll/readers/collection_reader.rb +22 -0
- data/lib/ngage/jekyll/readers/data_reader.rb +75 -0
- data/lib/ngage/jekyll/readers/layout_reader.rb +70 -0
- data/lib/ngage/jekyll/readers/page_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/post_reader.rb +72 -0
- data/lib/ngage/jekyll/readers/static_file_reader.rb +25 -0
- data/lib/ngage/jekyll/readers/theme_assets_reader.rb +51 -0
- data/lib/ngage/jekyll/regenerator.rb +195 -0
- data/lib/ngage/jekyll/related_posts.rb +52 -0
- data/lib/ngage/jekyll/renderer.rb +266 -0
- data/lib/ngage/jekyll/site.rb +476 -0
- data/lib/ngage/jekyll/static_file.rb +169 -0
- data/lib/ngage/jekyll/stevenson.rb +60 -0
- data/lib/ngage/jekyll/tags/highlight.rb +108 -0
- data/lib/ngage/jekyll/tags/include.rb +226 -0
- data/lib/ngage/jekyll/tags/link.rb +40 -0
- data/lib/ngage/jekyll/tags/post_url.rb +104 -0
- data/lib/ngage/jekyll/theme.rb +73 -0
- data/lib/ngage/jekyll/theme_builder.rb +121 -0
- data/lib/ngage/jekyll/url.rb +160 -0
- data/lib/ngage/jekyll/utils.rb +370 -0
- data/lib/ngage/jekyll/utils/ansi.rb +57 -0
- data/lib/ngage/jekyll/utils/exec.rb +26 -0
- data/lib/ngage/jekyll/utils/internet.rb +37 -0
- data/lib/ngage/jekyll/utils/platforms.rb +82 -0
- data/lib/ngage/jekyll/utils/thread_event.rb +31 -0
- data/lib/ngage/jekyll/utils/win_tz.rb +75 -0
- data/lib/ngage/site_template/.gitignore +5 -0
- data/lib/ngage/site_template/404.html +25 -0
- data/lib/ngage/site_template/_config.yml +47 -0
- data/lib/ngage/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -0
- data/lib/ngage/site_template/about.markdown +18 -0
- data/lib/ngage/site_template/index.markdown +6 -0
- data/lib/ngage/theme_template/CODE_OF_CONDUCT.md.erb +74 -0
- data/lib/ngage/theme_template/Gemfile +4 -0
- data/lib/ngage/theme_template/LICENSE.txt.erb +21 -0
- data/lib/ngage/theme_template/README.md.erb +52 -0
- data/lib/ngage/theme_template/_layouts/default.html +1 -0
- data/lib/ngage/theme_template/_layouts/page.html +5 -0
- data/lib/ngage/theme_template/_layouts/post.html +5 -0
- data/lib/ngage/theme_template/example/_config.yml.erb +1 -0
- data/lib/ngage/theme_template/example/_post.md +12 -0
- data/lib/ngage/theme_template/example/index.html +14 -0
- data/lib/ngage/theme_template/example/style.scss +7 -0
- data/lib/ngage/theme_template/gitignore.erb +6 -0
- data/lib/ngage/theme_template/theme.gemspec.erb +19 -0
- data/lib/ngage/version.rb +5 -0
- metadata +328 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webrick"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module Commands
|
|
7
|
+
class Serve
|
|
8
|
+
# This class is used to determine if the Servlet should modify a served file
|
|
9
|
+
# to insert the LiveReload script tags
|
|
10
|
+
class SkipAnalyzer
|
|
11
|
+
BAD_USER_AGENTS = [%r!MSIE!].freeze
|
|
12
|
+
|
|
13
|
+
def self.skip_processing?(request, response, options)
|
|
14
|
+
new(request, response, options).skip_processing?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(request, response, options)
|
|
18
|
+
@options = options
|
|
19
|
+
@request = request
|
|
20
|
+
@response = response
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def skip_processing?
|
|
24
|
+
!html? || chunked? || inline? || bad_browser?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def chunked?
|
|
28
|
+
@response["Transfer-Encoding"] == "chunked"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def inline?
|
|
32
|
+
@response["Content-Disposition"] =~ %r!^inline!
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def bad_browser?
|
|
36
|
+
BAD_USER_AGENTS.any? { |pattern| @request["User-Agent"] =~ pattern }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def html?
|
|
40
|
+
@response["Content-Type"] =~ %r!text/html!
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# This class inserts the LiveReload script tags into HTML as it is served
|
|
45
|
+
class BodyProcessor
|
|
46
|
+
HEAD_TAG_REGEX = %r!<head>|<head[^(er)][^<]*>!
|
|
47
|
+
|
|
48
|
+
attr_reader :content_length, :new_body, :livereload_added
|
|
49
|
+
|
|
50
|
+
def initialize(body, options)
|
|
51
|
+
@body = body
|
|
52
|
+
@options = options
|
|
53
|
+
@processed = false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def processed?
|
|
57
|
+
@processed
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# rubocop:disable Metrics/MethodLength
|
|
61
|
+
def process!
|
|
62
|
+
@new_body = []
|
|
63
|
+
# @body will usually be a File object but Strings occur in rare cases
|
|
64
|
+
if @body.respond_to?(:each)
|
|
65
|
+
begin
|
|
66
|
+
@body.each { |line| @new_body << line.to_s }
|
|
67
|
+
ensure
|
|
68
|
+
@body.close
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
@new_body = @body.lines
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
@content_length = 0
|
|
75
|
+
@livereload_added = false
|
|
76
|
+
|
|
77
|
+
@new_body.each do |line|
|
|
78
|
+
if !@livereload_added && line["<head"]
|
|
79
|
+
line.gsub!(HEAD_TAG_REGEX) do |match|
|
|
80
|
+
%(#{match}#{template.result(binding)})
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
@livereload_added = true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
@content_length += line.bytesize
|
|
87
|
+
@processed = true
|
|
88
|
+
end
|
|
89
|
+
@new_body = @new_body.join
|
|
90
|
+
end
|
|
91
|
+
# rubocop:enable Metrics/MethodLength
|
|
92
|
+
|
|
93
|
+
def template
|
|
94
|
+
# Unclear what "snipver" does. Doc at
|
|
95
|
+
# https://github.com/livereload/livereload-js states that the recommended
|
|
96
|
+
# setting is 1.
|
|
97
|
+
|
|
98
|
+
# Complicated JavaScript to ensure that livereload.js is loaded from the
|
|
99
|
+
# same origin as the page. Mostly useful for dealing with the browser's
|
|
100
|
+
# distinction between 'localhost' and 127.0.0.1
|
|
101
|
+
template = <<-TEMPLATE
|
|
102
|
+
<script>
|
|
103
|
+
document.write(
|
|
104
|
+
'<script src="http://' +
|
|
105
|
+
(location.host || 'localhost').split(':')[0] +
|
|
106
|
+
':<%=@options["livereload_port"] %>/livereload.js?snipver=1<%= livereload_args %>"' +
|
|
107
|
+
'></' +
|
|
108
|
+
'script>');
|
|
109
|
+
</script>
|
|
110
|
+
TEMPLATE
|
|
111
|
+
ERB.new(Jekyll::Utils.strip_heredoc(template))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def livereload_args
|
|
115
|
+
# XHTML standard requires ampersands to be encoded as entities when in
|
|
116
|
+
# attributes. See http://stackoverflow.com/a/2190292
|
|
117
|
+
src = ""
|
|
118
|
+
if @options["livereload_min_delay"]
|
|
119
|
+
src += "&mindelay=#{@options["livereload_min_delay"]}"
|
|
120
|
+
end
|
|
121
|
+
if @options["livereload_max_delay"]
|
|
122
|
+
src += "&maxdelay=#{@options["livereload_max_delay"]}"
|
|
123
|
+
end
|
|
124
|
+
src += "&port=#{@options["livereload_port"]}" if @options["livereload_port"]
|
|
125
|
+
src
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class Servlet < WEBrick::HTTPServlet::FileHandler
|
|
130
|
+
DEFAULTS = {
|
|
131
|
+
"Cache-Control" => "private, max-age=0, proxy-revalidate, " \
|
|
132
|
+
"no-store, no-cache, must-revalidate",
|
|
133
|
+
}.freeze
|
|
134
|
+
|
|
135
|
+
def initialize(server, root, callbacks)
|
|
136
|
+
# So we can access them easily.
|
|
137
|
+
@jekyll_opts = server.config[:JekyllOptions]
|
|
138
|
+
set_defaults
|
|
139
|
+
super
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def search_index_file(req, res)
|
|
143
|
+
super ||
|
|
144
|
+
search_file(req, res, ".html") ||
|
|
145
|
+
search_file(req, res, ".xhtml")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Add the ability to tap file.html the same way that Nginx does on our
|
|
149
|
+
# Docker images (or on GitHub Pages.) The difference is that we might end
|
|
150
|
+
# up with a different preference on which comes first.
|
|
151
|
+
|
|
152
|
+
def search_file(req, res, basename)
|
|
153
|
+
# /file.* > /file/index.html > /file.html
|
|
154
|
+
super ||
|
|
155
|
+
super(req, res, "#{basename}.html") ||
|
|
156
|
+
super(req, res, "#{basename}.xhtml")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# rubocop:disable Naming/MethodName
|
|
160
|
+
def do_GET(req, res)
|
|
161
|
+
rtn = super
|
|
162
|
+
|
|
163
|
+
if @jekyll_opts["livereload"]
|
|
164
|
+
return rtn if SkipAnalyzer.skip_processing?(req, res, @jekyll_opts)
|
|
165
|
+
|
|
166
|
+
processor = BodyProcessor.new(res.body, @jekyll_opts)
|
|
167
|
+
processor.process!
|
|
168
|
+
res.body = processor.new_body
|
|
169
|
+
res.content_length = processor.content_length.to_s
|
|
170
|
+
|
|
171
|
+
if processor.livereload_added
|
|
172
|
+
# Add a header to indicate that the page content has been modified
|
|
173
|
+
res["X-Rack-LiveReload"] = "1"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
validate_and_ensure_charset(req, res)
|
|
178
|
+
res.header.merge!(@headers)
|
|
179
|
+
rtn
|
|
180
|
+
end
|
|
181
|
+
# rubocop:enable Naming/MethodName
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
def validate_and_ensure_charset(_req, res)
|
|
186
|
+
key = res.header.keys.grep(%r!content-type!i).first
|
|
187
|
+
typ = res.header[key]
|
|
188
|
+
|
|
189
|
+
unless typ =~ %r!;\s*charset=!
|
|
190
|
+
res.header[key] = "#{typ}; charset=#{@jekyll_opts["encoding"]}"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def set_defaults
|
|
195
|
+
hash_ = @jekyll_opts.fetch("webrick", {}).fetch("headers", {})
|
|
196
|
+
DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash|
|
|
197
|
+
hash[key] = val unless hash.key?(key)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "http/parser"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module Commands
|
|
7
|
+
class Serve
|
|
8
|
+
# The LiveReload protocol requires the server to serve livereload.js over HTTP
|
|
9
|
+
# despite the fact that the protocol itself uses WebSockets. This custom connection
|
|
10
|
+
# class addresses the dual protocols that the server needs to understand.
|
|
11
|
+
class HttpAwareConnection < EventMachine::WebSocket::Connection
|
|
12
|
+
attr_reader :reload_body, :reload_size
|
|
13
|
+
|
|
14
|
+
def initialize(_opts)
|
|
15
|
+
# If EventMachine SSL support on Windows ever gets better, the code below will
|
|
16
|
+
# set up the reactor to handle SSL
|
|
17
|
+
#
|
|
18
|
+
# @ssl_enabled = opts["ssl_cert"] && opts["ssl_key"]
|
|
19
|
+
# if @ssl_enabled
|
|
20
|
+
# em_opts[:tls_options] = {
|
|
21
|
+
# :private_key_file => Jekyll.sanitized_path(opts["source"], opts["ssl_key"]),
|
|
22
|
+
# :cert_chain_file => Jekyll.sanitized_path(opts["source"], opts["ssl_cert"])
|
|
23
|
+
# }
|
|
24
|
+
# em_opts[:secure] = true
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
# This is too noisy even for --verbose, but uncomment if you need it for
|
|
28
|
+
# a specific WebSockets issue. Adding ?LR-verbose=true onto the URL will
|
|
29
|
+
# enable logging on the client side.
|
|
30
|
+
# em_opts[:debug] = true
|
|
31
|
+
|
|
32
|
+
em_opts = {}
|
|
33
|
+
super(em_opts)
|
|
34
|
+
|
|
35
|
+
reload_file = File.join(Serve.singleton_class::LIVERELOAD_DIR, "livereload.js")
|
|
36
|
+
|
|
37
|
+
@reload_body = File.read(reload_file)
|
|
38
|
+
@reload_size = @reload_body.bytesize
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
|
42
|
+
def dispatch(data)
|
|
43
|
+
parser = Http::Parser.new
|
|
44
|
+
parser << data
|
|
45
|
+
|
|
46
|
+
# WebSockets requests will have a Connection: Upgrade header
|
|
47
|
+
if parser.http_method != "GET" || parser.upgrade?
|
|
48
|
+
super
|
|
49
|
+
elsif parser.request_url =~ %r!^\/livereload.js!
|
|
50
|
+
headers = [
|
|
51
|
+
"HTTP/1.1 200 OK",
|
|
52
|
+
"Content-Type: application/javascript",
|
|
53
|
+
"Content-Length: #{reload_size}",
|
|
54
|
+
"",
|
|
55
|
+
"",
|
|
56
|
+
].join("\r\n")
|
|
57
|
+
send_data(headers)
|
|
58
|
+
|
|
59
|
+
# stream_file_data would free us from keeping livereload.js in memory
|
|
60
|
+
# but JRuby blocks on that call and never returns
|
|
61
|
+
send_data(reload_body)
|
|
62
|
+
close_connection_after_writing
|
|
63
|
+
else
|
|
64
|
+
body = "This port only serves livereload.js over HTTP.\n"
|
|
65
|
+
headers = [
|
|
66
|
+
"HTTP/1.1 400 Bad Request",
|
|
67
|
+
"Content-Type: text/plain",
|
|
68
|
+
"Content-Length: #{body.bytesize}",
|
|
69
|
+
"",
|
|
70
|
+
"",
|
|
71
|
+
].join("\r\n")
|
|
72
|
+
send_data(headers)
|
|
73
|
+
send_data(body)
|
|
74
|
+
close_connection_after_writing
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
# rubocop:enable Metrics/MethodLength
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
class Configuration < Hash
|
|
5
|
+
# Default options. Overridden by values in _config.yml.
|
|
6
|
+
# Strings rather than symbols are used for compatibility with YAML.
|
|
7
|
+
DEFAULTS = Configuration[{
|
|
8
|
+
# Where things are
|
|
9
|
+
"source" => Dir.pwd,
|
|
10
|
+
"destination" => File.join(Dir.pwd, "_site"),
|
|
11
|
+
"collections_dir" => "",
|
|
12
|
+
"cache_dir" => ".jekyll-cache",
|
|
13
|
+
"plugins_dir" => "_plugins",
|
|
14
|
+
"layouts_dir" => "_layouts",
|
|
15
|
+
"data_dir" => "_data",
|
|
16
|
+
"includes_dir" => "_includes",
|
|
17
|
+
"collections" => {},
|
|
18
|
+
|
|
19
|
+
# Handling Reading
|
|
20
|
+
"safe" => false,
|
|
21
|
+
"include" => [".htaccess"],
|
|
22
|
+
"exclude" => %w(
|
|
23
|
+
Gemfile Gemfile.lock node_modules vendor/bundle/ vendor/cache/ vendor/gems/
|
|
24
|
+
vendor/ruby/
|
|
25
|
+
),
|
|
26
|
+
"keep_files" => [".git", ".svn"],
|
|
27
|
+
"encoding" => "utf-8",
|
|
28
|
+
"markdown_ext" => "markdown,mkdown,mkdn,mkd,md",
|
|
29
|
+
"strict_front_matter" => false,
|
|
30
|
+
|
|
31
|
+
# Filtering Content
|
|
32
|
+
"show_drafts" => nil,
|
|
33
|
+
"limit_posts" => 0,
|
|
34
|
+
"future" => false,
|
|
35
|
+
"unpublished" => false,
|
|
36
|
+
|
|
37
|
+
# Plugins
|
|
38
|
+
"whitelist" => [],
|
|
39
|
+
"plugins" => [],
|
|
40
|
+
|
|
41
|
+
# Conversion
|
|
42
|
+
"markdown" => "kramdown",
|
|
43
|
+
"highlighter" => "rouge",
|
|
44
|
+
"lsi" => false,
|
|
45
|
+
"excerpt_separator" => "\n\n",
|
|
46
|
+
"incremental" => false,
|
|
47
|
+
|
|
48
|
+
# Serving
|
|
49
|
+
"detach" => false, # default to not detaching the server
|
|
50
|
+
"port" => "4000",
|
|
51
|
+
"host" => "127.0.0.1",
|
|
52
|
+
"baseurl" => nil, # this mounts at /, i.e. no subdirectory
|
|
53
|
+
"show_dir_listing" => false,
|
|
54
|
+
|
|
55
|
+
# Output Configuration
|
|
56
|
+
"permalink" => "date",
|
|
57
|
+
"paginate_path" => "/page:num",
|
|
58
|
+
"timezone" => nil, # use the local timezone
|
|
59
|
+
|
|
60
|
+
"quiet" => false,
|
|
61
|
+
"verbose" => false,
|
|
62
|
+
"defaults" => [],
|
|
63
|
+
|
|
64
|
+
"liquid" => {
|
|
65
|
+
"error_mode" => "warn",
|
|
66
|
+
"strict_filters" => false,
|
|
67
|
+
"strict_variables" => false,
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"kramdown" => {
|
|
71
|
+
"auto_ids" => true,
|
|
72
|
+
"toc_levels" => "1..6",
|
|
73
|
+
"entity_output" => "as_char",
|
|
74
|
+
"smart_quotes" => "lsquo,rsquo,ldquo,rdquo",
|
|
75
|
+
"input" => "GFM",
|
|
76
|
+
"hard_wrap" => false,
|
|
77
|
+
"footnote_nr" => 1,
|
|
78
|
+
"show_warnings" => false,
|
|
79
|
+
},
|
|
80
|
+
}.map { |k, v| [k, v.freeze] }].freeze
|
|
81
|
+
|
|
82
|
+
class << self
|
|
83
|
+
# Static: Produce a Configuration ready for use in a Site.
|
|
84
|
+
# It takes the input, fills in the defaults where values do not
|
|
85
|
+
# exist, and patches common issues including migrating options for
|
|
86
|
+
# backwards compatiblity. Except where a key or value is being fixed,
|
|
87
|
+
# the user configuration will override the defaults.
|
|
88
|
+
#
|
|
89
|
+
# user_config - a Hash or Configuration of overrides.
|
|
90
|
+
#
|
|
91
|
+
# Returns a Configuration filled with defaults and fixed for common
|
|
92
|
+
# problems and backwards-compatibility.
|
|
93
|
+
def from(user_config)
|
|
94
|
+
Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
|
|
95
|
+
.add_default_collections
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Public: Turn all keys into string
|
|
100
|
+
#
|
|
101
|
+
# Return a copy of the hash where all its keys are strings
|
|
102
|
+
def stringify_keys
|
|
103
|
+
reduce({}) { |hsh, (k, v)| hsh.merge(k.to_s => v) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def get_config_value_with_override(config_key, override)
|
|
107
|
+
override[config_key] || self[config_key] || DEFAULTS[config_key]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Public: Directory of the Jekyll source folder
|
|
111
|
+
#
|
|
112
|
+
# override - the command-line options hash
|
|
113
|
+
#
|
|
114
|
+
# Returns the path to the Jekyll source directory
|
|
115
|
+
def source(override)
|
|
116
|
+
get_config_value_with_override("source", override)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def quiet(override = {})
|
|
120
|
+
get_config_value_with_override("quiet", override)
|
|
121
|
+
end
|
|
122
|
+
alias_method :quiet?, :quiet
|
|
123
|
+
|
|
124
|
+
def verbose(override = {})
|
|
125
|
+
get_config_value_with_override("verbose", override)
|
|
126
|
+
end
|
|
127
|
+
alias_method :verbose?, :verbose
|
|
128
|
+
|
|
129
|
+
def safe_load_file(filename)
|
|
130
|
+
case File.extname(filename)
|
|
131
|
+
when %r!\.toml!i
|
|
132
|
+
Jekyll::External.require_with_graceful_fail("tomlrb") unless defined?(Tomlrb)
|
|
133
|
+
Tomlrb.load_file(filename)
|
|
134
|
+
when %r!\.ya?ml!i
|
|
135
|
+
SafeYAML.load_file(filename) || {}
|
|
136
|
+
else
|
|
137
|
+
raise ArgumentError, "No parser for '#{filename}' is available.
|
|
138
|
+
Use a .y(a)ml or .toml file instead."
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Public: Generate list of configuration files from the override
|
|
143
|
+
#
|
|
144
|
+
# override - the command-line options hash
|
|
145
|
+
#
|
|
146
|
+
# Returns an Array of config files
|
|
147
|
+
def config_files(override)
|
|
148
|
+
# Adjust verbosity quickly
|
|
149
|
+
Jekyll.logger.adjust_verbosity(
|
|
150
|
+
:quiet => quiet?(override),
|
|
151
|
+
:verbose => verbose?(override)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Get configuration from <source>/_config.yml or <source>/<config_file>
|
|
155
|
+
config_files = override["config"]
|
|
156
|
+
if config_files.to_s.empty?
|
|
157
|
+
default = %w(yml yaml).find(-> { "yml" }) do |ext|
|
|
158
|
+
File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}"))
|
|
159
|
+
end
|
|
160
|
+
config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
|
|
161
|
+
@default_config_file = true
|
|
162
|
+
end
|
|
163
|
+
Array(config_files)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Public: Read configuration and return merged Hash
|
|
167
|
+
#
|
|
168
|
+
# file - the path to the YAML file to be read in
|
|
169
|
+
#
|
|
170
|
+
# Returns this configuration, overridden by the values in the file
|
|
171
|
+
def read_config_file(file)
|
|
172
|
+
next_config = safe_load_file(file)
|
|
173
|
+
check_config_is_hash!(next_config, file)
|
|
174
|
+
Jekyll.logger.info "Configuration file:", file
|
|
175
|
+
next_config
|
|
176
|
+
rescue SystemCallError
|
|
177
|
+
if @default_config_file ||= nil
|
|
178
|
+
Jekyll.logger.warn "Configuration file:", "none"
|
|
179
|
+
{}
|
|
180
|
+
else
|
|
181
|
+
Jekyll.logger.error "Fatal:", "The configuration file '#{file}'
|
|
182
|
+
could not be found."
|
|
183
|
+
raise LoadError, "The Configuration file '#{file}' could not be found."
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Public: Read in a list of configuration files and merge with this hash
|
|
188
|
+
#
|
|
189
|
+
# files - the list of configuration file paths
|
|
190
|
+
#
|
|
191
|
+
# Returns the full configuration, with the defaults overridden by the values in the
|
|
192
|
+
# configuration files
|
|
193
|
+
def read_config_files(files)
|
|
194
|
+
configuration = clone
|
|
195
|
+
|
|
196
|
+
begin
|
|
197
|
+
files.each do |config_file|
|
|
198
|
+
next if config_file.nil? || config_file.empty?
|
|
199
|
+
|
|
200
|
+
new_config = read_config_file(config_file)
|
|
201
|
+
configuration = Utils.deep_merge_hashes(configuration, new_config)
|
|
202
|
+
end
|
|
203
|
+
rescue ArgumentError => err
|
|
204
|
+
Jekyll.logger.warn "WARNING:", "Error reading configuration. " \
|
|
205
|
+
"Using defaults (and options)."
|
|
206
|
+
warn err
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
configuration.backwards_compatibilize.add_default_collections
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Public: Split a CSV string into an array containing its values
|
|
213
|
+
#
|
|
214
|
+
# csv - the string of comma-separated values
|
|
215
|
+
#
|
|
216
|
+
# Returns an array of the values contained in the CSV
|
|
217
|
+
def csv_to_array(csv)
|
|
218
|
+
csv.split(",").map(&:strip)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Public: Ensure the proper options are set in the configuration to allow for
|
|
222
|
+
# backwards-compatibility with Jekyll pre-1.0
|
|
223
|
+
#
|
|
224
|
+
# Returns the backwards-compatible configuration
|
|
225
|
+
def backwards_compatibilize
|
|
226
|
+
config = clone
|
|
227
|
+
# Provide backwards-compatibility
|
|
228
|
+
check_auto(config)
|
|
229
|
+
check_server(config)
|
|
230
|
+
check_plugins(config)
|
|
231
|
+
|
|
232
|
+
renamed_key "server_port", "port", config
|
|
233
|
+
renamed_key "gems", "plugins", config
|
|
234
|
+
renamed_key "layouts", "layouts_dir", config
|
|
235
|
+
renamed_key "data_source", "data_dir", config
|
|
236
|
+
|
|
237
|
+
check_pygments(config)
|
|
238
|
+
check_include_exclude(config)
|
|
239
|
+
check_coderay(config)
|
|
240
|
+
check_maruku(config)
|
|
241
|
+
|
|
242
|
+
config
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# DEPRECATED.
|
|
246
|
+
def fix_common_issues
|
|
247
|
+
self
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def add_default_collections
|
|
251
|
+
config = clone
|
|
252
|
+
|
|
253
|
+
# It defaults to `{}`, so this is only if someone sets it to null manually.
|
|
254
|
+
return config if config["collections"].nil?
|
|
255
|
+
|
|
256
|
+
# Ensure we have a hash.
|
|
257
|
+
if config["collections"].is_a?(Array)
|
|
258
|
+
config["collections"] = Hash[config["collections"].map { |c| [c, {}] }]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
config["collections"] = Utils.deep_merge_hashes(
|
|
262
|
+
{ "posts" => {} }, config["collections"]
|
|
263
|
+
).tap do |collections|
|
|
264
|
+
collections["posts"]["output"] = true
|
|
265
|
+
if config["permalink"]
|
|
266
|
+
collections["posts"]["permalink"] ||= style_to_permalink(config["permalink"])
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
config
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def renamed_key(old, new, config, _ = nil)
|
|
274
|
+
if config.key?(old)
|
|
275
|
+
Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" \
|
|
276
|
+
" option has been renamed to '#{new}'. Please update your config" \
|
|
277
|
+
" file accordingly."
|
|
278
|
+
config[new] = config.delete(old)
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
private
|
|
283
|
+
|
|
284
|
+
def style_to_permalink(permalink_style)
|
|
285
|
+
case permalink_style.to_sym
|
|
286
|
+
when :pretty
|
|
287
|
+
"/:categories/:year/:month/:day/:title/"
|
|
288
|
+
when :none
|
|
289
|
+
"/:categories/:title:output_ext"
|
|
290
|
+
when :date
|
|
291
|
+
"/:categories/:year/:month/:day/:title:output_ext"
|
|
292
|
+
when :ordinal
|
|
293
|
+
"/:categories/:year/:y_day/:title:output_ext"
|
|
294
|
+
else
|
|
295
|
+
permalink_style.to_s
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Private: Checks if a given config is a hash
|
|
300
|
+
#
|
|
301
|
+
# extracted_config - the value to check
|
|
302
|
+
# file - the file from which the config was extracted
|
|
303
|
+
#
|
|
304
|
+
# Raises an ArgumentError if given config is not a hash
|
|
305
|
+
def check_config_is_hash!(extracted_config, file)
|
|
306
|
+
unless extracted_config.is_a?(Hash)
|
|
307
|
+
raise ArgumentError, "Configuration file: (INVALID) #{file}".yellow
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def check_auto(config)
|
|
312
|
+
if config.key?("auto") || config.key?("watch")
|
|
313
|
+
Jekyll::Deprecator.deprecation_message "Auto-regeneration can no longer" \
|
|
314
|
+
" be set from your configuration file(s). Use the" \
|
|
315
|
+
" --[no-]watch/-w command-line option instead."
|
|
316
|
+
config.delete("auto")
|
|
317
|
+
config.delete("watch")
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def check_server(config)
|
|
322
|
+
if config.key?("server")
|
|
323
|
+
Jekyll::Deprecator.deprecation_message "The 'server' configuration option" \
|
|
324
|
+
" is no longer accepted. Use the 'jekyll serve'" \
|
|
325
|
+
" subcommand to serve your site with WEBrick."
|
|
326
|
+
config.delete("server")
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def check_pygments(config)
|
|
331
|
+
if config.key?("pygments")
|
|
332
|
+
Jekyll::Deprecator.deprecation_message "The 'pygments' configuration option" \
|
|
333
|
+
" has been renamed to 'highlighter'. Please update your" \
|
|
334
|
+
" config file accordingly. The allowed values are 'rouge', " \
|
|
335
|
+
"'pygments' or null."
|
|
336
|
+
|
|
337
|
+
config["highlighter"] = "pygments" if config["pygments"]
|
|
338
|
+
config.delete("pygments")
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def check_include_exclude(config)
|
|
343
|
+
%w(include exclude).each do |option|
|
|
344
|
+
if config[option].is_a?(String)
|
|
345
|
+
Jekyll::Deprecator.deprecation_message "The '#{option}' configuration option" \
|
|
346
|
+
" must now be specified as an array, but you specified" \
|
|
347
|
+
" a string. For now, we've treated the string you provided" \
|
|
348
|
+
" as a list of comma-separated values."
|
|
349
|
+
config[option] = csv_to_array(config[option])
|
|
350
|
+
end
|
|
351
|
+
config[option]&.map!(&:to_s)
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def check_coderay(config)
|
|
356
|
+
if (config["kramdown"] || {}).key?("use_coderay")
|
|
357
|
+
Jekyll::Deprecator.deprecation_message "Please change 'use_coderay'" \
|
|
358
|
+
" to 'enable_coderay' in your configuration file."
|
|
359
|
+
config["kramdown"]["use_coderay"] = config["kramdown"].delete("enable_coderay")
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def check_maruku(config)
|
|
364
|
+
if config.fetch("markdown", "kramdown").to_s.casecmp("maruku").zero?
|
|
365
|
+
Jekyll.logger.abort_with "Error:", "You're using the 'maruku' " \
|
|
366
|
+
"Markdown processor, which has been removed as of 3.0.0. " \
|
|
367
|
+
"We recommend you switch to Kramdown. To do this, replace " \
|
|
368
|
+
"`markdown: maruku` with `markdown: kramdown` in your " \
|
|
369
|
+
"`_config.yml` file."
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Private: Checks if the `plugins` config is a String
|
|
374
|
+
#
|
|
375
|
+
# config - the config hash
|
|
376
|
+
#
|
|
377
|
+
# Raises a Jekyll::Errors::InvalidConfigurationError if the config `plugins`
|
|
378
|
+
# is a string
|
|
379
|
+
def check_plugins(config)
|
|
380
|
+
if config.key?("plugins") && config["plugins"].is_a?(String)
|
|
381
|
+
Jekyll.logger.error "Configuration Error:", "You specified the" \
|
|
382
|
+
" `plugins` config in your configuration file as a string, please" \
|
|
383
|
+
" use an array instead. If you wanted to set the directory of your" \
|
|
384
|
+
" plugins, use the config key `plugins_dir` instead."
|
|
385
|
+
raise Jekyll::Errors::InvalidConfigurationError,
|
|
386
|
+
"'plugins' should not be a string, but was: " \
|
|
387
|
+
"#{config["plugins"].inspect}. Use 'plugins_dir' instead."
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|