jekyll 4.2.1 → 4.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +350 -350
- data/LICENSE +21 -21
- data/README.markdown +86 -86
- data/exe/jekyll +57 -57
- data/lib/blank_template/_config.yml +3 -3
- data/lib/blank_template/_layouts/default.html +12 -12
- data/lib/blank_template/_sass/main.scss +9 -9
- data/lib/blank_template/assets/css/main.scss +4 -4
- data/lib/blank_template/index.md +8 -8
- data/lib/jekyll/cache.rb +190 -190
- data/lib/jekyll/cleaner.rb +111 -111
- data/lib/jekyll/collection.rb +309 -309
- data/lib/jekyll/command.rb +105 -105
- data/lib/jekyll/commands/build.rb +93 -93
- data/lib/jekyll/commands/clean.rb +45 -45
- data/lib/jekyll/commands/doctor.rb +177 -177
- data/lib/jekyll/commands/help.rb +34 -34
- data/lib/jekyll/commands/new.rb +172 -169
- data/lib/jekyll/commands/new_theme.rb +40 -40
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +122 -122
- data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
- data/lib/jekyll/commands/serve/servlet.rb +202 -202
- data/lib/jekyll/commands/serve/websockets.rb +81 -81
- data/lib/jekyll/commands/serve.rb +362 -362
- data/lib/jekyll/configuration.rb +313 -313
- data/lib/jekyll/converter.rb +54 -54
- data/lib/jekyll/converters/identity.rb +41 -41
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +199 -199
- data/lib/jekyll/converters/markdown.rb +113 -113
- data/lib/jekyll/converters/smartypants.rb +70 -70
- data/lib/jekyll/convertible.rb +257 -257
- data/lib/jekyll/deprecator.rb +50 -50
- data/lib/jekyll/document.rb +544 -544
- data/lib/jekyll/drops/collection_drop.rb +20 -20
- data/lib/jekyll/drops/document_drop.rb +70 -70
- data/lib/jekyll/drops/drop.rb +293 -293
- data/lib/jekyll/drops/excerpt_drop.rb +19 -19
- data/lib/jekyll/drops/jekyll_drop.rb +32 -32
- data/lib/jekyll/drops/site_drop.rb +66 -66
- data/lib/jekyll/drops/static_file_drop.rb +14 -14
- data/lib/jekyll/drops/unified_payload_drop.rb +26 -26
- data/lib/jekyll/drops/url_drop.rb +140 -140
- data/lib/jekyll/entry_filter.rb +121 -121
- data/lib/jekyll/errors.rb +20 -20
- data/lib/jekyll/excerpt.rb +201 -201
- data/lib/jekyll/external.rb +79 -79
- data/lib/jekyll/filters/date_filters.rb +110 -110
- data/lib/jekyll/filters/grouping_filters.rb +64 -64
- data/lib/jekyll/filters/url_filters.rb +98 -98
- data/lib/jekyll/filters.rb +535 -535
- data/lib/jekyll/frontmatter_defaults.rb +240 -240
- data/lib/jekyll/generator.rb +5 -5
- data/lib/jekyll/hooks.rb +107 -107
- data/lib/jekyll/inclusion.rb +32 -32
- data/lib/jekyll/layout.rb +67 -67
- data/lib/jekyll/liquid_extensions.rb +22 -22
- data/lib/jekyll/liquid_renderer/file.rb +77 -77
- data/lib/jekyll/liquid_renderer/table.rb +55 -55
- data/lib/jekyll/liquid_renderer.rb +80 -80
- data/lib/jekyll/log_adapter.rb +151 -151
- data/lib/jekyll/mime.types +866 -866
- data/lib/jekyll/page.rb +217 -217
- data/lib/jekyll/page_excerpt.rb +25 -25
- data/lib/jekyll/page_without_a_file.rb +14 -14
- data/lib/jekyll/path_manager.rb +74 -74
- data/lib/jekyll/plugin.rb +92 -92
- data/lib/jekyll/plugin_manager.rb +115 -115
- data/lib/jekyll/profiler.rb +58 -58
- data/lib/jekyll/publisher.rb +23 -23
- data/lib/jekyll/reader.rb +192 -192
- data/lib/jekyll/readers/collection_reader.rb +23 -23
- data/lib/jekyll/readers/data_reader.rb +79 -79
- data/lib/jekyll/readers/layout_reader.rb +62 -62
- data/lib/jekyll/readers/page_reader.rb +25 -25
- data/lib/jekyll/readers/post_reader.rb +85 -85
- data/lib/jekyll/readers/static_file_reader.rb +25 -25
- data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
- data/lib/jekyll/regenerator.rb +195 -195
- data/lib/jekyll/related_posts.rb +52 -52
- data/lib/jekyll/renderer.rb +265 -265
- data/lib/jekyll/site.rb +551 -551
- data/lib/jekyll/static_file.rb +208 -208
- data/lib/jekyll/stevenson.rb +60 -60
- data/lib/jekyll/tags/highlight.rb +110 -110
- data/lib/jekyll/tags/include.rb +275 -275
- data/lib/jekyll/tags/link.rb +42 -42
- data/lib/jekyll/tags/post_url.rb +106 -106
- data/lib/jekyll/theme.rb +86 -86
- data/lib/jekyll/theme_builder.rb +121 -121
- data/lib/jekyll/url.rb +167 -167
- data/lib/jekyll/utils/ansi.rb +57 -57
- data/lib/jekyll/utils/exec.rb +26 -26
- data/lib/jekyll/utils/internet.rb +37 -37
- data/lib/jekyll/utils/platforms.rb +67 -67
- data/lib/jekyll/utils/thread_event.rb +31 -31
- data/lib/jekyll/utils/win_tz.rb +75 -75
- data/lib/jekyll/utils.rb +367 -367
- data/lib/jekyll/version.rb +5 -5
- data/lib/jekyll.rb +195 -195
- data/lib/site_template/.gitignore +5 -5
- data/lib/site_template/404.html +25 -25
- data/lib/site_template/_config.yml +55 -55
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
- data/lib/site_template/about.markdown +18 -18
- data/lib/site_template/index.markdown +6 -6
- data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
- data/lib/theme_template/Gemfile +4 -4
- data/lib/theme_template/LICENSE.txt.erb +21 -21
- data/lib/theme_template/README.md.erb +52 -52
- data/lib/theme_template/_layouts/default.html +1 -1
- data/lib/theme_template/_layouts/page.html +5 -5
- data/lib/theme_template/_layouts/post.html +5 -5
- data/lib/theme_template/example/_config.yml.erb +1 -1
- data/lib/theme_template/example/_post.md +12 -12
- data/lib/theme_template/example/index.html +14 -14
- data/lib/theme_template/example/style.scss +7 -7
- data/lib/theme_template/gitignore.erb +6 -6
- data/lib/theme_template/theme.gemspec.erb +16 -16
- data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
- data/rubocop/jekyll/no_p_allowed.rb +23 -23
- data/rubocop/jekyll/no_puts_allowed.rb +23 -23
- data/rubocop/jekyll.rb +5 -5
- metadata +3 -3
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jekyll
|
|
4
|
-
module Commands
|
|
5
|
-
class Serve < Command
|
|
6
|
-
# Similar to the pattern in Utils::ThreadEvent except we are maintaining the
|
|
7
|
-
# state of @running instead of just signaling an event. We have to maintain this
|
|
8
|
-
# state since Serve is just called via class methods instead of an instance
|
|
9
|
-
# being created each time.
|
|
10
|
-
@mutex = Mutex.new
|
|
11
|
-
@run_cond = ConditionVariable.new
|
|
12
|
-
@running = false
|
|
13
|
-
|
|
14
|
-
class << self
|
|
15
|
-
COMMAND_OPTIONS = {
|
|
16
|
-
"ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."],
|
|
17
|
-
"host" => ["host", "-H", "--host [HOST]", "Host to bind to"],
|
|
18
|
-
"open_url" => ["-o", "--open-url", "Launch your site in a browser"],
|
|
19
|
-
"detach" => ["-B", "--detach",
|
|
20
|
-
"Run the server in the background",],
|
|
21
|
-
"ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."],
|
|
22
|
-
"port" => ["-P", "--port [PORT]", "Port to listen on"],
|
|
23
|
-
"show_dir_listing" => ["--show-dir-listing",
|
|
24
|
-
"Show a directory listing instead of loading" \
|
|
25
|
-
" your index file.",],
|
|
26
|
-
"skip_initial_build" => ["skip_initial_build", "--skip-initial-build",
|
|
27
|
-
"Skips the initial site build which occurs before" \
|
|
28
|
-
" the server is started.",],
|
|
29
|
-
"livereload" => ["-l", "--livereload",
|
|
30
|
-
"Use LiveReload to automatically refresh browsers",],
|
|
31
|
-
"livereload_ignore" => ["--livereload-ignore ignore GLOB1[,GLOB2[,...]]",
|
|
32
|
-
Array,
|
|
33
|
-
"Files for LiveReload to ignore. " \
|
|
34
|
-
"Remember to quote the values so your shell " \
|
|
35
|
-
"won't expand them",],
|
|
36
|
-
"livereload_min_delay" => ["--livereload-min-delay [SECONDS]",
|
|
37
|
-
"Minimum reload delay",],
|
|
38
|
-
"livereload_max_delay" => ["--livereload-max-delay [SECONDS]",
|
|
39
|
-
"Maximum reload delay",],
|
|
40
|
-
"livereload_port" => ["--livereload-port [PORT]", Integer,
|
|
41
|
-
"Port for LiveReload to listen on",],
|
|
42
|
-
}.freeze
|
|
43
|
-
|
|
44
|
-
DIRECTORY_INDEX = %w(
|
|
45
|
-
index.htm
|
|
46
|
-
index.html
|
|
47
|
-
index.rhtml
|
|
48
|
-
index.xht
|
|
49
|
-
index.xhtml
|
|
50
|
-
index.cgi
|
|
51
|
-
index.xml
|
|
52
|
-
index.json
|
|
53
|
-
).freeze
|
|
54
|
-
|
|
55
|
-
LIVERELOAD_PORT = 35_729
|
|
56
|
-
LIVERELOAD_DIR = File.join(__dir__, "serve", "livereload_assets")
|
|
57
|
-
|
|
58
|
-
attr_reader :mutex, :run_cond, :running
|
|
59
|
-
alias_method :running?, :running
|
|
60
|
-
|
|
61
|
-
def init_with_program(prog)
|
|
62
|
-
prog.command(:serve) do |cmd|
|
|
63
|
-
cmd.description "Serve your site locally"
|
|
64
|
-
cmd.syntax "serve [options]"
|
|
65
|
-
cmd.alias :server
|
|
66
|
-
cmd.alias :s
|
|
67
|
-
|
|
68
|
-
add_build_options(cmd)
|
|
69
|
-
COMMAND_OPTIONS.each do |key, val|
|
|
70
|
-
cmd.option key, *val
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
cmd.action do |_, opts|
|
|
74
|
-
opts["livereload_port"] ||= LIVERELOAD_PORT
|
|
75
|
-
opts["serving"] = true
|
|
76
|
-
opts["watch"] = true unless opts.key?("watch")
|
|
77
|
-
|
|
78
|
-
# Set the reactor to nil so any old reactor will be GCed.
|
|
79
|
-
# We can't unregister a hook so while running tests we don't want to
|
|
80
|
-
# inadvertently keep using a reactor created by a previous test.
|
|
81
|
-
@reload_reactor = nil
|
|
82
|
-
|
|
83
|
-
config = configuration_from_options(opts)
|
|
84
|
-
config["url"] = default_url(config) if Jekyll.env == "development"
|
|
85
|
-
|
|
86
|
-
process_with_graceful_fail(cmd, config, Build, Serve)
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
def process(opts)
|
|
94
|
-
opts = configuration_from_options(opts)
|
|
95
|
-
destination = opts["destination"]
|
|
96
|
-
if opts["livereload"]
|
|
97
|
-
validate_options(opts)
|
|
98
|
-
register_reload_hooks(opts)
|
|
99
|
-
end
|
|
100
|
-
setup(destination)
|
|
101
|
-
|
|
102
|
-
start_up_webrick(opts, destination)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def shutdown
|
|
106
|
-
@server.shutdown if running?
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Perform logical validation of CLI options
|
|
110
|
-
|
|
111
|
-
private
|
|
112
|
-
|
|
113
|
-
def validate_options(opts)
|
|
114
|
-
if opts["livereload"]
|
|
115
|
-
if opts["detach"]
|
|
116
|
-
Jekyll.logger.warn "Warning:", "--detach and --livereload are mutually exclusive." \
|
|
117
|
-
" Choosing --livereload"
|
|
118
|
-
opts["detach"] = false
|
|
119
|
-
end
|
|
120
|
-
if opts["ssl_cert"] || opts["ssl_key"]
|
|
121
|
-
# This is not technically true. LiveReload works fine over SSL, but
|
|
122
|
-
# EventMachine's SSL support in Windows requires building the gem's
|
|
123
|
-
# native extensions against OpenSSL and that proved to be a process
|
|
124
|
-
# so tedious that expecting users to do it is a non-starter.
|
|
125
|
-
Jekyll.logger.abort_with "Error:", "LiveReload does not support SSL"
|
|
126
|
-
end
|
|
127
|
-
unless opts["watch"]
|
|
128
|
-
# Using livereload logically implies you want to watch the files
|
|
129
|
-
opts["watch"] = true
|
|
130
|
-
end
|
|
131
|
-
elsif %w(livereload_min_delay
|
|
132
|
-
livereload_max_delay
|
|
133
|
-
livereload_ignore
|
|
134
|
-
livereload_port).any? { |o| opts[o] }
|
|
135
|
-
Jekyll.logger.abort_with "--livereload-min-delay, "\
|
|
136
|
-
"--livereload-max-delay, --livereload-ignore, and "\
|
|
137
|
-
"--livereload-port require the --livereload option."
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# rubocop:disable Metrics/AbcSize
|
|
142
|
-
def register_reload_hooks(opts)
|
|
143
|
-
require_relative "serve/live_reload_reactor"
|
|
144
|
-
@reload_reactor = LiveReloadReactor.new
|
|
145
|
-
|
|
146
|
-
Jekyll::Hooks.register(:site, :post_render) do |site|
|
|
147
|
-
regenerator = Jekyll::Regenerator.new(site)
|
|
148
|
-
@changed_pages = site.pages.select do |p|
|
|
149
|
-
regenerator.regenerate?(p)
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# A note on ignoring files: LiveReload errs on the side of reloading when it
|
|
154
|
-
# comes to the message it gets. If, for example, a page is ignored but a CSS
|
|
155
|
-
# file linked in the page isn't, the page will still be reloaded if the CSS
|
|
156
|
-
# file is contained in the message sent to LiveReload. Additionally, the
|
|
157
|
-
# path matching is very loose so that a message to reload "/" will always
|
|
158
|
-
# lead the page to reload since every page starts with "/".
|
|
159
|
-
Jekyll::Hooks.register(:site, :post_write) do
|
|
160
|
-
if @changed_pages && @reload_reactor && @reload_reactor.running?
|
|
161
|
-
ignore, @changed_pages = @changed_pages.partition do |p|
|
|
162
|
-
Array(opts["livereload_ignore"]).any? do |filter|
|
|
163
|
-
File.fnmatch(filter, Jekyll.sanitized_path(p.relative_path))
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
Jekyll.logger.debug "LiveReload:", "Ignoring #{ignore.map(&:relative_path)}"
|
|
167
|
-
@reload_reactor.reload(@changed_pages)
|
|
168
|
-
end
|
|
169
|
-
@changed_pages = nil
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
# rubocop:enable Metrics/AbcSize
|
|
173
|
-
|
|
174
|
-
# Do a base pre-setup of WEBRick so that everything is in place
|
|
175
|
-
# when we get ready to party, checking for an setting up an error page
|
|
176
|
-
# and making sure our destination exists.
|
|
177
|
-
|
|
178
|
-
def setup(destination)
|
|
179
|
-
require_relative "serve/servlet"
|
|
180
|
-
|
|
181
|
-
FileUtils.mkdir_p(destination)
|
|
182
|
-
if File.exist?(File.join(destination, "404.html"))
|
|
183
|
-
WEBrick::HTTPResponse.class_eval do
|
|
184
|
-
def create_error_page
|
|
185
|
-
@header["Content-Type"] = "text/html; charset=UTF-8"
|
|
186
|
-
@body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def webrick_opts(opts)
|
|
193
|
-
opts = {
|
|
194
|
-
:JekyllOptions => opts,
|
|
195
|
-
:DoNotReverseLookup => true,
|
|
196
|
-
:MimeTypes => mime_types,
|
|
197
|
-
:DocumentRoot => opts["destination"],
|
|
198
|
-
:StartCallback => start_callback(opts["detach"]),
|
|
199
|
-
:StopCallback => stop_callback(opts["detach"]),
|
|
200
|
-
:BindAddress => opts["host"],
|
|
201
|
-
:Port => opts["port"],
|
|
202
|
-
:DirectoryIndex => DIRECTORY_INDEX,
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
opts[:DirectoryIndex] = [] if opts[:JekyllOptions]["show_dir_listing"]
|
|
206
|
-
|
|
207
|
-
enable_ssl(opts)
|
|
208
|
-
enable_logging(opts)
|
|
209
|
-
opts
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
def start_up_webrick(opts, destination)
|
|
213
|
-
@reload_reactor.start(opts) if opts["livereload"]
|
|
214
|
-
|
|
215
|
-
@server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
|
|
216
|
-
@server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts)
|
|
217
|
-
|
|
218
|
-
Jekyll.logger.info "Server address:", server_address(@server, opts)
|
|
219
|
-
launch_browser @server, opts if opts["open_url"]
|
|
220
|
-
boot_or_detach @server, opts
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
# Recreate NondisclosureName under utf-8 circumstance
|
|
224
|
-
def file_handler_opts
|
|
225
|
-
WEBrick::Config::FileHandler.merge(
|
|
226
|
-
:FancyIndexing => true,
|
|
227
|
-
:NondisclosureName => [
|
|
228
|
-
".ht*", "~*",
|
|
229
|
-
]
|
|
230
|
-
)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def server_address(server, options = {})
|
|
234
|
-
format_url(
|
|
235
|
-
server.config[:SSLEnable],
|
|
236
|
-
server.config[:BindAddress],
|
|
237
|
-
server.config[:Port],
|
|
238
|
-
options["baseurl"]
|
|
239
|
-
)
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def format_url(ssl_enabled, address, port, baseurl = nil)
|
|
243
|
-
format("%<prefix>s://%<address>s:%<port>i%<baseurl>s",
|
|
244
|
-
:prefix => ssl_enabled ? "https" : "http",
|
|
245
|
-
:address => address,
|
|
246
|
-
:port => port,
|
|
247
|
-
:baseurl => baseurl ? "#{baseurl}/" : "")
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def default_url(opts)
|
|
251
|
-
config = configuration_from_options(opts)
|
|
252
|
-
format_url(
|
|
253
|
-
config["ssl_cert"] && config["ssl_key"],
|
|
254
|
-
config["host"] == "127.0.0.1" ? "localhost" : config["host"],
|
|
255
|
-
config["port"]
|
|
256
|
-
)
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def launch_browser(server, opts)
|
|
260
|
-
address = server_address(server, opts)
|
|
261
|
-
return system "start", address if Utils::Platforms.windows?
|
|
262
|
-
return system "xdg-open", address if Utils::Platforms.linux?
|
|
263
|
-
return system "open", address if Utils::Platforms.osx?
|
|
264
|
-
|
|
265
|
-
Jekyll.logger.error "Refusing to launch browser; " \
|
|
266
|
-
"Platform launcher unknown."
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
# Keep in our area with a thread or detach the server as requested
|
|
270
|
-
# by the user. This method determines what we do based on what you
|
|
271
|
-
# ask us to do.
|
|
272
|
-
def boot_or_detach(server, opts)
|
|
273
|
-
if opts["detach"]
|
|
274
|
-
pid = Process.fork do
|
|
275
|
-
server.start
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
Process.detach(pid)
|
|
279
|
-
Jekyll.logger.info "Server detached with pid '#{pid}'.", \
|
|
280
|
-
"Run `pkill -f jekyll' or `kill -9 #{pid}'" \
|
|
281
|
-
" to stop the server."
|
|
282
|
-
else
|
|
283
|
-
t = Thread.new { server.start }
|
|
284
|
-
trap("INT") { server.shutdown }
|
|
285
|
-
t.join
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# Make the stack verbose if the user requests it.
|
|
290
|
-
def enable_logging(opts)
|
|
291
|
-
opts[:AccessLog] = []
|
|
292
|
-
level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
|
|
293
|
-
opts[:Logger] = WEBrick::Log.new($stdout, level)
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
# Add SSL to the stack if the user triggers --enable-ssl and they
|
|
297
|
-
# provide both types of certificates commonly needed. Raise if they
|
|
298
|
-
# forget to add one of the certificates.
|
|
299
|
-
def enable_ssl(opts)
|
|
300
|
-
cert, key, src =
|
|
301
|
-
opts[:JekyllOptions].values_at("ssl_cert", "ssl_key", "source")
|
|
302
|
-
|
|
303
|
-
return if cert.nil? && key.nil?
|
|
304
|
-
raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key
|
|
305
|
-
|
|
306
|
-
require "openssl"
|
|
307
|
-
require "webrick/https"
|
|
308
|
-
|
|
309
|
-
opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert))
|
|
310
|
-
begin
|
|
311
|
-
opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
|
|
312
|
-
rescue StandardError
|
|
313
|
-
if defined?(OpenSSL::PKey::EC)
|
|
314
|
-
opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key))
|
|
315
|
-
else
|
|
316
|
-
raise
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
opts[:SSLEnable] = true
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
def start_callback(detached)
|
|
323
|
-
unless detached
|
|
324
|
-
proc do
|
|
325
|
-
mutex.synchronize do
|
|
326
|
-
# Block until EventMachine reactor starts
|
|
327
|
-
@reload_reactor&.started_event&.wait
|
|
328
|
-
@running = true
|
|
329
|
-
Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
|
|
330
|
-
@run_cond.broadcast
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def stop_callback(detached)
|
|
337
|
-
unless detached
|
|
338
|
-
proc do
|
|
339
|
-
mutex.synchronize do
|
|
340
|
-
unless @reload_reactor.nil?
|
|
341
|
-
@reload_reactor.stop
|
|
342
|
-
@reload_reactor.stopped_event.wait
|
|
343
|
-
end
|
|
344
|
-
@running = false
|
|
345
|
-
@run_cond.broadcast
|
|
346
|
-
end
|
|
347
|
-
end
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
def mime_types
|
|
352
|
-
file = File.expand_path("../mime.types", __dir__)
|
|
353
|
-
WEBrick::HTTPUtils.load_mime_types(file)
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def read_file(source_dir, file_path)
|
|
357
|
-
File.read(Jekyll.sanitized_path(source_dir, file_path))
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
end
|
|
362
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Commands
|
|
5
|
+
class Serve < Command
|
|
6
|
+
# Similar to the pattern in Utils::ThreadEvent except we are maintaining the
|
|
7
|
+
# state of @running instead of just signaling an event. We have to maintain this
|
|
8
|
+
# state since Serve is just called via class methods instead of an instance
|
|
9
|
+
# being created each time.
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
@run_cond = ConditionVariable.new
|
|
12
|
+
@running = false
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
COMMAND_OPTIONS = {
|
|
16
|
+
"ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."],
|
|
17
|
+
"host" => ["host", "-H", "--host [HOST]", "Host to bind to"],
|
|
18
|
+
"open_url" => ["-o", "--open-url", "Launch your site in a browser"],
|
|
19
|
+
"detach" => ["-B", "--detach",
|
|
20
|
+
"Run the server in the background",],
|
|
21
|
+
"ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."],
|
|
22
|
+
"port" => ["-P", "--port [PORT]", "Port to listen on"],
|
|
23
|
+
"show_dir_listing" => ["--show-dir-listing",
|
|
24
|
+
"Show a directory listing instead of loading" \
|
|
25
|
+
" your index file.",],
|
|
26
|
+
"skip_initial_build" => ["skip_initial_build", "--skip-initial-build",
|
|
27
|
+
"Skips the initial site build which occurs before" \
|
|
28
|
+
" the server is started.",],
|
|
29
|
+
"livereload" => ["-l", "--livereload",
|
|
30
|
+
"Use LiveReload to automatically refresh browsers",],
|
|
31
|
+
"livereload_ignore" => ["--livereload-ignore ignore GLOB1[,GLOB2[,...]]",
|
|
32
|
+
Array,
|
|
33
|
+
"Files for LiveReload to ignore. " \
|
|
34
|
+
"Remember to quote the values so your shell " \
|
|
35
|
+
"won't expand them",],
|
|
36
|
+
"livereload_min_delay" => ["--livereload-min-delay [SECONDS]",
|
|
37
|
+
"Minimum reload delay",],
|
|
38
|
+
"livereload_max_delay" => ["--livereload-max-delay [SECONDS]",
|
|
39
|
+
"Maximum reload delay",],
|
|
40
|
+
"livereload_port" => ["--livereload-port [PORT]", Integer,
|
|
41
|
+
"Port for LiveReload to listen on",],
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
DIRECTORY_INDEX = %w(
|
|
45
|
+
index.htm
|
|
46
|
+
index.html
|
|
47
|
+
index.rhtml
|
|
48
|
+
index.xht
|
|
49
|
+
index.xhtml
|
|
50
|
+
index.cgi
|
|
51
|
+
index.xml
|
|
52
|
+
index.json
|
|
53
|
+
).freeze
|
|
54
|
+
|
|
55
|
+
LIVERELOAD_PORT = 35_729
|
|
56
|
+
LIVERELOAD_DIR = File.join(__dir__, "serve", "livereload_assets")
|
|
57
|
+
|
|
58
|
+
attr_reader :mutex, :run_cond, :running
|
|
59
|
+
alias_method :running?, :running
|
|
60
|
+
|
|
61
|
+
def init_with_program(prog)
|
|
62
|
+
prog.command(:serve) do |cmd|
|
|
63
|
+
cmd.description "Serve your site locally"
|
|
64
|
+
cmd.syntax "serve [options]"
|
|
65
|
+
cmd.alias :server
|
|
66
|
+
cmd.alias :s
|
|
67
|
+
|
|
68
|
+
add_build_options(cmd)
|
|
69
|
+
COMMAND_OPTIONS.each do |key, val|
|
|
70
|
+
cmd.option key, *val
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
cmd.action do |_, opts|
|
|
74
|
+
opts["livereload_port"] ||= LIVERELOAD_PORT
|
|
75
|
+
opts["serving"] = true
|
|
76
|
+
opts["watch"] = true unless opts.key?("watch")
|
|
77
|
+
|
|
78
|
+
# Set the reactor to nil so any old reactor will be GCed.
|
|
79
|
+
# We can't unregister a hook so while running tests we don't want to
|
|
80
|
+
# inadvertently keep using a reactor created by a previous test.
|
|
81
|
+
@reload_reactor = nil
|
|
82
|
+
|
|
83
|
+
config = configuration_from_options(opts)
|
|
84
|
+
config["url"] = default_url(config) if Jekyll.env == "development"
|
|
85
|
+
|
|
86
|
+
process_with_graceful_fail(cmd, config, Build, Serve)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
#
|
|
92
|
+
|
|
93
|
+
def process(opts)
|
|
94
|
+
opts = configuration_from_options(opts)
|
|
95
|
+
destination = opts["destination"]
|
|
96
|
+
if opts["livereload"]
|
|
97
|
+
validate_options(opts)
|
|
98
|
+
register_reload_hooks(opts)
|
|
99
|
+
end
|
|
100
|
+
setup(destination)
|
|
101
|
+
|
|
102
|
+
start_up_webrick(opts, destination)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def shutdown
|
|
106
|
+
@server.shutdown if running?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Perform logical validation of CLI options
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def validate_options(opts)
|
|
114
|
+
if opts["livereload"]
|
|
115
|
+
if opts["detach"]
|
|
116
|
+
Jekyll.logger.warn "Warning:", "--detach and --livereload are mutually exclusive." \
|
|
117
|
+
" Choosing --livereload"
|
|
118
|
+
opts["detach"] = false
|
|
119
|
+
end
|
|
120
|
+
if opts["ssl_cert"] || opts["ssl_key"]
|
|
121
|
+
# This is not technically true. LiveReload works fine over SSL, but
|
|
122
|
+
# EventMachine's SSL support in Windows requires building the gem's
|
|
123
|
+
# native extensions against OpenSSL and that proved to be a process
|
|
124
|
+
# so tedious that expecting users to do it is a non-starter.
|
|
125
|
+
Jekyll.logger.abort_with "Error:", "LiveReload does not support SSL"
|
|
126
|
+
end
|
|
127
|
+
unless opts["watch"]
|
|
128
|
+
# Using livereload logically implies you want to watch the files
|
|
129
|
+
opts["watch"] = true
|
|
130
|
+
end
|
|
131
|
+
elsif %w(livereload_min_delay
|
|
132
|
+
livereload_max_delay
|
|
133
|
+
livereload_ignore
|
|
134
|
+
livereload_port).any? { |o| opts[o] }
|
|
135
|
+
Jekyll.logger.abort_with "--livereload-min-delay, "\
|
|
136
|
+
"--livereload-max-delay, --livereload-ignore, and "\
|
|
137
|
+
"--livereload-port require the --livereload option."
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# rubocop:disable Metrics/AbcSize
|
|
142
|
+
def register_reload_hooks(opts)
|
|
143
|
+
require_relative "serve/live_reload_reactor"
|
|
144
|
+
@reload_reactor = LiveReloadReactor.new
|
|
145
|
+
|
|
146
|
+
Jekyll::Hooks.register(:site, :post_render) do |site|
|
|
147
|
+
regenerator = Jekyll::Regenerator.new(site)
|
|
148
|
+
@changed_pages = site.pages.select do |p|
|
|
149
|
+
regenerator.regenerate?(p)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# A note on ignoring files: LiveReload errs on the side of reloading when it
|
|
154
|
+
# comes to the message it gets. If, for example, a page is ignored but a CSS
|
|
155
|
+
# file linked in the page isn't, the page will still be reloaded if the CSS
|
|
156
|
+
# file is contained in the message sent to LiveReload. Additionally, the
|
|
157
|
+
# path matching is very loose so that a message to reload "/" will always
|
|
158
|
+
# lead the page to reload since every page starts with "/".
|
|
159
|
+
Jekyll::Hooks.register(:site, :post_write) do
|
|
160
|
+
if @changed_pages && @reload_reactor && @reload_reactor.running?
|
|
161
|
+
ignore, @changed_pages = @changed_pages.partition do |p|
|
|
162
|
+
Array(opts["livereload_ignore"]).any? do |filter|
|
|
163
|
+
File.fnmatch(filter, Jekyll.sanitized_path(p.relative_path))
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
Jekyll.logger.debug "LiveReload:", "Ignoring #{ignore.map(&:relative_path)}"
|
|
167
|
+
@reload_reactor.reload(@changed_pages)
|
|
168
|
+
end
|
|
169
|
+
@changed_pages = nil
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
# rubocop:enable Metrics/AbcSize
|
|
173
|
+
|
|
174
|
+
# Do a base pre-setup of WEBRick so that everything is in place
|
|
175
|
+
# when we get ready to party, checking for an setting up an error page
|
|
176
|
+
# and making sure our destination exists.
|
|
177
|
+
|
|
178
|
+
def setup(destination)
|
|
179
|
+
require_relative "serve/servlet"
|
|
180
|
+
|
|
181
|
+
FileUtils.mkdir_p(destination)
|
|
182
|
+
if File.exist?(File.join(destination, "404.html"))
|
|
183
|
+
WEBrick::HTTPResponse.class_eval do
|
|
184
|
+
def create_error_page
|
|
185
|
+
@header["Content-Type"] = "text/html; charset=UTF-8"
|
|
186
|
+
@body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def webrick_opts(opts)
|
|
193
|
+
opts = {
|
|
194
|
+
:JekyllOptions => opts,
|
|
195
|
+
:DoNotReverseLookup => true,
|
|
196
|
+
:MimeTypes => mime_types,
|
|
197
|
+
:DocumentRoot => opts["destination"],
|
|
198
|
+
:StartCallback => start_callback(opts["detach"]),
|
|
199
|
+
:StopCallback => stop_callback(opts["detach"]),
|
|
200
|
+
:BindAddress => opts["host"],
|
|
201
|
+
:Port => opts["port"],
|
|
202
|
+
:DirectoryIndex => DIRECTORY_INDEX,
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
opts[:DirectoryIndex] = [] if opts[:JekyllOptions]["show_dir_listing"]
|
|
206
|
+
|
|
207
|
+
enable_ssl(opts)
|
|
208
|
+
enable_logging(opts)
|
|
209
|
+
opts
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def start_up_webrick(opts, destination)
|
|
213
|
+
@reload_reactor.start(opts) if opts["livereload"]
|
|
214
|
+
|
|
215
|
+
@server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
|
|
216
|
+
@server.mount(opts["baseurl"].to_s, Servlet, destination, file_handler_opts)
|
|
217
|
+
|
|
218
|
+
Jekyll.logger.info "Server address:", server_address(@server, opts)
|
|
219
|
+
launch_browser @server, opts if opts["open_url"]
|
|
220
|
+
boot_or_detach @server, opts
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Recreate NondisclosureName under utf-8 circumstance
|
|
224
|
+
def file_handler_opts
|
|
225
|
+
WEBrick::Config::FileHandler.merge(
|
|
226
|
+
:FancyIndexing => true,
|
|
227
|
+
:NondisclosureName => [
|
|
228
|
+
".ht*", "~*",
|
|
229
|
+
]
|
|
230
|
+
)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def server_address(server, options = {})
|
|
234
|
+
format_url(
|
|
235
|
+
server.config[:SSLEnable],
|
|
236
|
+
server.config[:BindAddress],
|
|
237
|
+
server.config[:Port],
|
|
238
|
+
options["baseurl"]
|
|
239
|
+
)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def format_url(ssl_enabled, address, port, baseurl = nil)
|
|
243
|
+
format("%<prefix>s://%<address>s:%<port>i%<baseurl>s",
|
|
244
|
+
:prefix => ssl_enabled ? "https" : "http",
|
|
245
|
+
:address => address,
|
|
246
|
+
:port => port,
|
|
247
|
+
:baseurl => baseurl ? "#{baseurl}/" : "")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def default_url(opts)
|
|
251
|
+
config = configuration_from_options(opts)
|
|
252
|
+
format_url(
|
|
253
|
+
config["ssl_cert"] && config["ssl_key"],
|
|
254
|
+
config["host"] == "127.0.0.1" ? "localhost" : config["host"],
|
|
255
|
+
config["port"]
|
|
256
|
+
)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def launch_browser(server, opts)
|
|
260
|
+
address = server_address(server, opts)
|
|
261
|
+
return system "start", address if Utils::Platforms.windows?
|
|
262
|
+
return system "xdg-open", address if Utils::Platforms.linux?
|
|
263
|
+
return system "open", address if Utils::Platforms.osx?
|
|
264
|
+
|
|
265
|
+
Jekyll.logger.error "Refusing to launch browser; " \
|
|
266
|
+
"Platform launcher unknown."
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Keep in our area with a thread or detach the server as requested
|
|
270
|
+
# by the user. This method determines what we do based on what you
|
|
271
|
+
# ask us to do.
|
|
272
|
+
def boot_or_detach(server, opts)
|
|
273
|
+
if opts["detach"]
|
|
274
|
+
pid = Process.fork do
|
|
275
|
+
server.start
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
Process.detach(pid)
|
|
279
|
+
Jekyll.logger.info "Server detached with pid '#{pid}'.", \
|
|
280
|
+
"Run `pkill -f jekyll' or `kill -9 #{pid}'" \
|
|
281
|
+
" to stop the server."
|
|
282
|
+
else
|
|
283
|
+
t = Thread.new { server.start }
|
|
284
|
+
trap("INT") { server.shutdown }
|
|
285
|
+
t.join
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Make the stack verbose if the user requests it.
|
|
290
|
+
def enable_logging(opts)
|
|
291
|
+
opts[:AccessLog] = []
|
|
292
|
+
level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
|
|
293
|
+
opts[:Logger] = WEBrick::Log.new($stdout, level)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Add SSL to the stack if the user triggers --enable-ssl and they
|
|
297
|
+
# provide both types of certificates commonly needed. Raise if they
|
|
298
|
+
# forget to add one of the certificates.
|
|
299
|
+
def enable_ssl(opts)
|
|
300
|
+
cert, key, src =
|
|
301
|
+
opts[:JekyllOptions].values_at("ssl_cert", "ssl_key", "source")
|
|
302
|
+
|
|
303
|
+
return if cert.nil? && key.nil?
|
|
304
|
+
raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key
|
|
305
|
+
|
|
306
|
+
require "openssl"
|
|
307
|
+
require "webrick/https"
|
|
308
|
+
|
|
309
|
+
opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert))
|
|
310
|
+
begin
|
|
311
|
+
opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
|
|
312
|
+
rescue StandardError
|
|
313
|
+
if defined?(OpenSSL::PKey::EC)
|
|
314
|
+
opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key))
|
|
315
|
+
else
|
|
316
|
+
raise
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
opts[:SSLEnable] = true
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def start_callback(detached)
|
|
323
|
+
unless detached
|
|
324
|
+
proc do
|
|
325
|
+
mutex.synchronize do
|
|
326
|
+
# Block until EventMachine reactor starts
|
|
327
|
+
@reload_reactor&.started_event&.wait
|
|
328
|
+
@running = true
|
|
329
|
+
Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
|
|
330
|
+
@run_cond.broadcast
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def stop_callback(detached)
|
|
337
|
+
unless detached
|
|
338
|
+
proc do
|
|
339
|
+
mutex.synchronize do
|
|
340
|
+
unless @reload_reactor.nil?
|
|
341
|
+
@reload_reactor.stop
|
|
342
|
+
@reload_reactor.stopped_event.wait
|
|
343
|
+
end
|
|
344
|
+
@running = false
|
|
345
|
+
@run_cond.broadcast
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def mime_types
|
|
352
|
+
file = File.expand_path("../mime.types", __dir__)
|
|
353
|
+
WEBrick::HTTPUtils.load_mime_types(file)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def read_file(source_dir, file_path)
|
|
357
|
+
File.read(Jekyll.sanitized_path(source_dir, file_path))
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|