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,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module Commands
|
|
7
|
+
class New < Command
|
|
8
|
+
class << self
|
|
9
|
+
def init_with_program(prog)
|
|
10
|
+
prog.command(:new) do |c|
|
|
11
|
+
c.syntax "new PATH"
|
|
12
|
+
c.description "Creates a new Jekyll site scaffold in PATH"
|
|
13
|
+
|
|
14
|
+
c.option "force", "--force", "Force creation even if PATH already exists"
|
|
15
|
+
c.option "blank", "--blank", "Creates scaffolding but with empty files"
|
|
16
|
+
c.option "skip-bundle", "--skip-bundle", "Skip 'bundle install'"
|
|
17
|
+
|
|
18
|
+
c.action do |args, options|
|
|
19
|
+
Jekyll::Commands::New.process(args, options)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def process(args, options = {})
|
|
25
|
+
raise ArgumentError, "You must specify a path." if args.empty?
|
|
26
|
+
|
|
27
|
+
new_blog_path = File.expand_path(args.join(" "), Dir.pwd)
|
|
28
|
+
FileUtils.mkdir_p new_blog_path
|
|
29
|
+
if preserve_source_location?(new_blog_path, options)
|
|
30
|
+
Jekyll.logger.error "Conflict:", "#{new_blog_path} exists and is not empty."
|
|
31
|
+
Jekyll.logger.abort_with "", "Ensure #{new_blog_path} is empty or else " \
|
|
32
|
+
"try again with `--force` to proceed and overwrite any files."
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if options["blank"]
|
|
36
|
+
create_blank_site new_blog_path
|
|
37
|
+
else
|
|
38
|
+
create_site new_blog_path
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
after_install(new_blog_path, options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def create_blank_site(path)
|
|
45
|
+
Dir.chdir(path) do
|
|
46
|
+
FileUtils.mkdir(%w(_layouts _posts _drafts))
|
|
47
|
+
FileUtils.touch("index.html")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def scaffold_post_content
|
|
52
|
+
ERB.new(File.read(File.expand_path(scaffold_path, site_template))).result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Internal: Gets the filename of the sample post to be created
|
|
56
|
+
#
|
|
57
|
+
# Returns the filename of the sample post, as a String
|
|
58
|
+
def initialized_post_name
|
|
59
|
+
"_posts/#{Time.now.strftime("%Y-%m-%d")}-welcome-to-jekyll.markdown"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def gemfile_contents
|
|
65
|
+
<<~RUBY
|
|
66
|
+
source "https://rubygems.org"
|
|
67
|
+
# Hello! This is where you manage which Jekyll version is used to run.
|
|
68
|
+
# When you want to use a different version, change it below, save the
|
|
69
|
+
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
|
70
|
+
#
|
|
71
|
+
# bundle exec jekyll serve
|
|
72
|
+
#
|
|
73
|
+
# This will help ensure the proper Jekyll version is running.
|
|
74
|
+
# Happy Jekylling!
|
|
75
|
+
gem "jekyll", "~> #{Jekyll::VERSION}"
|
|
76
|
+
# This is the default theme for new Jekyll sites. You may change this to anything you like.
|
|
77
|
+
gem "minima", "~> 2.0"
|
|
78
|
+
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
|
|
79
|
+
# uncomment the line below. To upgrade, run `bundle update github-pages`.
|
|
80
|
+
# gem "github-pages", group: :jekyll_plugins
|
|
81
|
+
# If you have any plugins, put them here!
|
|
82
|
+
group :jekyll_plugins do
|
|
83
|
+
gem "jekyll-feed", "~> 0.6"
|
|
84
|
+
end
|
|
85
|
+
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
|
86
|
+
gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
|
87
|
+
# Performance-booster for watching directories on Windows
|
|
88
|
+
gem "wdm", "~> 0.1.0" if Gem.win_platform?
|
|
89
|
+
|
|
90
|
+
RUBY
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create_site(new_blog_path)
|
|
94
|
+
create_sample_files new_blog_path
|
|
95
|
+
|
|
96
|
+
File.open(File.expand_path(initialized_post_name, new_blog_path), "w") do |f|
|
|
97
|
+
f.write(scaffold_post_content)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
File.open(File.expand_path("Gemfile", new_blog_path), "w") do |f|
|
|
101
|
+
f.write(gemfile_contents)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def preserve_source_location?(path, options)
|
|
106
|
+
!options["force"] && !Dir["#{path}/**/*"].empty?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def create_sample_files(path)
|
|
110
|
+
FileUtils.cp_r site_template + "/.", path
|
|
111
|
+
FileUtils.chmod_R "u+w", path
|
|
112
|
+
FileUtils.rm File.expand_path(scaffold_path, path)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def site_template
|
|
116
|
+
File.expand_path("../../site_template", __dir__)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def scaffold_path
|
|
120
|
+
"_posts/0000-00-00-welcome-to-jekyll.markdown.erb"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# After a new blog has been created, print a success notification and
|
|
124
|
+
# then automatically execute bundle install from within the new blog dir
|
|
125
|
+
# unless the user opts to generate a blank blog or skip 'bundle install'.
|
|
126
|
+
|
|
127
|
+
def after_install(path, options = {})
|
|
128
|
+
unless options["blank"] || options["skip-bundle"]
|
|
129
|
+
begin
|
|
130
|
+
require "bundler"
|
|
131
|
+
bundle_install path
|
|
132
|
+
rescue LoadError
|
|
133
|
+
Jekyll.logger.info "Could not load Bundler. Bundle install skipped."
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Jekyll.logger.info "New jekyll site installed in #{path.cyan}."
|
|
138
|
+
Jekyll.logger.info "Bundle install skipped." if options["skip-bundle"]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def bundle_install(path)
|
|
142
|
+
Jekyll.logger.info "Running bundle install in #{path.cyan}..."
|
|
143
|
+
Dir.chdir(path) do
|
|
144
|
+
exe = Gem.bin_path("bundler", "bundle")
|
|
145
|
+
process, output = Jekyll::Utils::Exec.run("ruby", exe, "install")
|
|
146
|
+
|
|
147
|
+
output.to_s.each_line do |line|
|
|
148
|
+
Jekyll.logger.info("Bundler:".green, line.strip) unless line.to_s.empty?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
raise SystemExit unless process.success?
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module Commands
|
|
7
|
+
class NewTheme < Jekyll::Command
|
|
8
|
+
class << self
|
|
9
|
+
def init_with_program(prog)
|
|
10
|
+
prog.command(:"new-theme") do |c|
|
|
11
|
+
c.syntax "new-theme NAME"
|
|
12
|
+
c.description "Creates a new Jekyll theme scaffold"
|
|
13
|
+
c.option "code_of_conduct", \
|
|
14
|
+
"-c", "--code-of-conduct", \
|
|
15
|
+
"Include a Code of Conduct. (defaults to false)"
|
|
16
|
+
|
|
17
|
+
c.action do |args, opts|
|
|
18
|
+
Jekyll::Commands::NewTheme.process(args, opts)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# rubocop:disable Metrics/AbcSize
|
|
24
|
+
def process(args, opts)
|
|
25
|
+
if !args || args.empty?
|
|
26
|
+
raise Jekyll::Errors::InvalidThemeName, "You must specify a theme name."
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
new_theme_name = args.join("_")
|
|
30
|
+
theme = Jekyll::ThemeBuilder.new(new_theme_name, opts)
|
|
31
|
+
Jekyll.logger.abort_with "Conflict:", "#{theme.path} already exists." if theme.path.exist?
|
|
32
|
+
|
|
33
|
+
theme.create!
|
|
34
|
+
Jekyll.logger.info "Your new Jekyll theme, #{theme.name.cyan}," \
|
|
35
|
+
" is ready for you in #{theme.path.to_s.cyan}!"
|
|
36
|
+
Jekyll.logger.info "For help getting started, read #{theme.path}/README.md."
|
|
37
|
+
end
|
|
38
|
+
# rubocop:enable Metrics/AbcSize
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,354 @@
|
|
|
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
|
+
opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
|
|
311
|
+
opts[:SSLEnable] = true
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def start_callback(detached)
|
|
315
|
+
unless detached
|
|
316
|
+
proc do
|
|
317
|
+
mutex.synchronize do
|
|
318
|
+
# Block until EventMachine reactor starts
|
|
319
|
+
@reload_reactor&.started_event&.wait
|
|
320
|
+
@running = true
|
|
321
|
+
Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
|
|
322
|
+
@run_cond.broadcast
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def stop_callback(detached)
|
|
329
|
+
unless detached
|
|
330
|
+
proc do
|
|
331
|
+
mutex.synchronize do
|
|
332
|
+
unless @reload_reactor.nil?
|
|
333
|
+
@reload_reactor.stop
|
|
334
|
+
@reload_reactor.stopped_event.wait
|
|
335
|
+
end
|
|
336
|
+
@running = false
|
|
337
|
+
@run_cond.broadcast
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def mime_types
|
|
344
|
+
file = File.expand_path("../mime.types", __dir__)
|
|
345
|
+
WEBrick::HTTPUtils.load_mime_types(file)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def read_file(source_dir, file_path)
|
|
349
|
+
File.read(Jekyll.sanitized_path(source_dir, file_path))
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|