jekyllplusadmin 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +80 -0
  3. data/LICENSE +21 -0
  4. data/README.markdown +60 -0
  5. data/bin/jekyll +51 -0
  6. data/lib/jekyll.rb +180 -0
  7. data/lib/jekyll/cleaner.rb +105 -0
  8. data/lib/jekyll/collection.rb +205 -0
  9. data/lib/jekyll/command.rb +65 -0
  10. data/lib/jekyll/commands/build.rb +77 -0
  11. data/lib/jekyll/commands/clean.rb +42 -0
  12. data/lib/jekyll/commands/doctor.rb +114 -0
  13. data/lib/jekyll/commands/help.rb +31 -0
  14. data/lib/jekyll/commands/new.rb +82 -0
  15. data/lib/jekyll/commands/serve.rb +235 -0
  16. data/lib/jekyll/commands/serve/servlet.rb +61 -0
  17. data/lib/jekyll/configuration.rb +323 -0
  18. data/lib/jekyll/converter.rb +48 -0
  19. data/lib/jekyll/converters/identity.rb +21 -0
  20. data/lib/jekyll/converters/markdown.rb +92 -0
  21. data/lib/jekyll/converters/markdown/kramdown_parser.rb +117 -0
  22. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +33 -0
  23. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +102 -0
  24. data/lib/jekyll/converters/smartypants.rb +34 -0
  25. data/lib/jekyll/convertible.rb +297 -0
  26. data/lib/jekyll/deprecator.rb +46 -0
  27. data/lib/jekyll/document.rb +444 -0
  28. data/lib/jekyll/drops/collection_drop.rb +22 -0
  29. data/lib/jekyll/drops/document_drop.rb +27 -0
  30. data/lib/jekyll/drops/drop.rb +176 -0
  31. data/lib/jekyll/drops/jekyll_drop.rb +21 -0
  32. data/lib/jekyll/drops/site_drop.rb +38 -0
  33. data/lib/jekyll/drops/unified_payload_drop.rb +25 -0
  34. data/lib/jekyll/drops/url_drop.rb +83 -0
  35. data/lib/jekyll/entry_filter.rb +72 -0
  36. data/lib/jekyll/errors.rb +10 -0
  37. data/lib/jekyll/excerpt.rb +127 -0
  38. data/lib/jekyll/external.rb +59 -0
  39. data/lib/jekyll/filters.rb +367 -0
  40. data/lib/jekyll/frontmatter_defaults.rb +188 -0
  41. data/lib/jekyll/generator.rb +3 -0
  42. data/lib/jekyll/hooks.rb +101 -0
  43. data/lib/jekyll/layout.rb +49 -0
  44. data/lib/jekyll/liquid_extensions.rb +22 -0
  45. data/lib/jekyll/liquid_renderer.rb +39 -0
  46. data/lib/jekyll/liquid_renderer/file.rb +50 -0
  47. data/lib/jekyll/liquid_renderer/table.rb +94 -0
  48. data/lib/jekyll/log_adapter.rb +115 -0
  49. data/lib/jekyll/mime.types +800 -0
  50. data/lib/jekyll/page.rb +180 -0
  51. data/lib/jekyll/plugin.rb +96 -0
  52. data/lib/jekyll/plugin_manager.rb +95 -0
  53. data/lib/jekyll/publisher.rb +21 -0
  54. data/lib/jekyll/reader.rb +126 -0
  55. data/lib/jekyll/readers/collection_reader.rb +20 -0
  56. data/lib/jekyll/readers/data_reader.rb +69 -0
  57. data/lib/jekyll/readers/layout_reader.rb +53 -0
  58. data/lib/jekyll/readers/page_reader.rb +21 -0
  59. data/lib/jekyll/readers/post_reader.rb +62 -0
  60. data/lib/jekyll/readers/static_file_reader.rb +21 -0
  61. data/lib/jekyll/regenerator.rb +175 -0
  62. data/lib/jekyll/related_posts.rb +56 -0
  63. data/lib/jekyll/renderer.rb +191 -0
  64. data/lib/jekyll/site.rb +391 -0
  65. data/lib/jekyll/static_file.rb +141 -0
  66. data/lib/jekyll/stevenson.rb +58 -0
  67. data/lib/jekyll/tags/highlight.rb +122 -0
  68. data/lib/jekyll/tags/include.rb +190 -0
  69. data/lib/jekyll/tags/post_url.rb +88 -0
  70. data/lib/jekyll/url.rb +136 -0
  71. data/lib/jekyll/utils.rb +287 -0
  72. data/lib/jekyll/utils/ansi.rb +59 -0
  73. data/lib/jekyll/utils/platforms.rb +30 -0
  74. data/lib/jekyll/version.rb +3 -0
  75. data/lib/site_template/.gitignore +3 -0
  76. data/lib/site_template/_config.yml +21 -0
  77. data/lib/site_template/_includes/footer.html +38 -0
  78. data/lib/site_template/_includes/head.html +12 -0
  79. data/lib/site_template/_includes/header.html +27 -0
  80. data/lib/site_template/_includes/icon-github.html +1 -0
  81. data/lib/site_template/_includes/icon-github.svg +1 -0
  82. data/lib/site_template/_includes/icon-twitter.html +1 -0
  83. data/lib/site_template/_includes/icon-twitter.svg +1 -0
  84. data/lib/site_template/_layouts/default.html +20 -0
  85. data/lib/site_template/_layouts/page.html +14 -0
  86. data/lib/site_template/_layouts/post.html +15 -0
  87. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +25 -0
  88. data/lib/site_template/_sass/_base.scss +206 -0
  89. data/lib/site_template/_sass/_layout.scss +242 -0
  90. data/lib/site_template/_sass/_syntax-highlighting.scss +71 -0
  91. data/lib/site_template/about.md +15 -0
  92. data/lib/site_template/css/main.scss +53 -0
  93. data/lib/site_template/feed.xml +30 -0
  94. data/lib/site_template/index.html +23 -0
  95. metadata +252 -0
@@ -0,0 +1,31 @@
1
+ module Jekyll
2
+ module Commands
3
+ class Help < Command
4
+ class << self
5
+ def init_with_program(prog)
6
+ prog.command(:help) do |c|
7
+ c.syntax 'help [subcommand]'
8
+ c.description 'Show the help message, optionally for a given subcommand.'
9
+
10
+ c.action do |args, _|
11
+ cmd = (args.first || "").to_sym
12
+ if args.empty?
13
+ puts prog
14
+ elsif prog.has_command? cmd
15
+ puts prog.commands[cmd]
16
+ else
17
+ invalid_command(prog, cmd)
18
+ abort
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def invalid_command(prog, cmd)
25
+ Jekyll.logger.error "Error:", "Hmm... we don't know what the '#{cmd}' command is."
26
+ Jekyll.logger.info "Valid commands:", prog.commands.keys.join(", ")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,82 @@
1
+ require 'erb'
2
+
3
+ module Jekyll
4
+ module Commands
5
+ class New < Command
6
+ class << self
7
+ def init_with_program(prog)
8
+ prog.command(:new) do |c|
9
+ c.syntax 'new PATH'
10
+ c.description 'Creates a new Jekyll site scaffold in PATH'
11
+
12
+ c.option 'force', '--force', 'Force creation even if PATH already exists'
13
+ c.option 'blank', '--blank', 'Creates scaffolding but with empty files'
14
+
15
+ c.action do |args, options|
16
+ Jekyll::Commands::New.process(args, options)
17
+ end
18
+ end
19
+ end
20
+
21
+ def process(args, options = {})
22
+ raise ArgumentError.new('You must specify a path.') if args.empty?
23
+
24
+ new_blog_path = File.expand_path(args.join(" "), Dir.pwd)
25
+ FileUtils.mkdir_p new_blog_path
26
+ if preserve_source_location?(new_blog_path, options)
27
+ Jekyll.logger.abort_with "Conflict:", "#{new_blog_path} exists and is not empty."
28
+ end
29
+
30
+ if options["blank"]
31
+ create_blank_site new_blog_path
32
+ else
33
+ create_sample_files new_blog_path
34
+
35
+ File.open(File.expand_path(initialized_post_name, new_blog_path), "w") do |f|
36
+ f.write(scaffold_post_content)
37
+ end
38
+ end
39
+
40
+ Jekyll.logger.info "New jekyll site installed in #{new_blog_path}."
41
+ end
42
+
43
+ def create_blank_site(path)
44
+ Dir.chdir(path) do
45
+ FileUtils.mkdir(%w(_layouts _posts _drafts))
46
+ FileUtils.touch("index.html")
47
+ end
48
+ end
49
+
50
+ def scaffold_post_content
51
+ ERB.new(File.read(File.expand_path(scaffold_path, site_template))).result
52
+ end
53
+
54
+ # Internal: Gets the filename of the sample post to be created
55
+ #
56
+ # Returns the filename of the sample post, as a String
57
+ def initialized_post_name
58
+ "_posts/#{Time.now.strftime('%Y-%m-%d')}-welcome-to-jekyll.markdown"
59
+ end
60
+
61
+ private
62
+
63
+ def preserve_source_location?(path, options)
64
+ !options["force"] && !Dir["#{path}/**/*"].empty?
65
+ end
66
+
67
+ def create_sample_files(path)
68
+ FileUtils.cp_r site_template + '/.', path
69
+ FileUtils.rm File.expand_path(scaffold_path, path)
70
+ end
71
+
72
+ def site_template
73
+ File.expand_path("../../site_template", File.dirname(__FILE__))
74
+ end
75
+
76
+ def scaffold_path
77
+ "_posts/0000-00-00-welcome-to-jekyll.markdown.erb"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,235 @@
1
+ module Jekyll
2
+ module Commands
3
+ class Serve < Command
4
+ class << self
5
+ COMMAND_OPTIONS = {
6
+ "ssl_cert" => ["--ssl-cert [CERT]", "X.509 (SSL) certificate."],
7
+ "host" => ["host", "-H", "--host [HOST]", "Host to bind to"],
8
+ "open_url" => ["-o", "--open-url", "Launch your browser with your site."],
9
+ "detach" => ["-B", "--detach", "Run the server in the background (detach)"],
10
+ "ssl_key" => ["--ssl-key [KEY]", "X.509 (SSL) Private Key."],
11
+ "port" => ["-P", "--port [PORT]", "Port to listen on"],
12
+ "baseurl" => ["-b", "--baseurl [URL]", "Base URL"],
13
+ "skip_initial_build" => ["skip_initial_build", "--skip-initial-build",
14
+ "Skips the initial site build which occurs before the server is started."]
15
+ }
16
+
17
+ #
18
+
19
+ def init_with_program(prog)
20
+ prog.command(:serve) do |cmd|
21
+ cmd.description "Serve your site locally"
22
+ cmd.syntax "serve [options]"
23
+ cmd.alias :server
24
+ cmd.alias :s
25
+
26
+ add_build_options(cmd)
27
+ COMMAND_OPTIONS.each do |key, val|
28
+ cmd.option key, *val
29
+ end
30
+
31
+ cmd.action do |_, opts|
32
+ opts["serving"] = true
33
+ opts["watch" ] = true unless opts.key?("watch")
34
+ Build.process(opts)
35
+ Serve.process(opts)
36
+ end
37
+ end
38
+ end
39
+
40
+ #
41
+
42
+ def process(opts)
43
+ opts = configuration_from_options(opts)
44
+ destination = opts["destination"]
45
+ setup(destination)
46
+
47
+ server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
48
+ server.mount(opts["baseurl"], Servlet, destination, file_handler_opts)
49
+ server.mount('/admin', admin)
50
+ Jekyll.logger.info "Server address:", server_address(server, opts)
51
+ launch_browser server, opts if opts["open_url"]
52
+ boot_or_detach server, opts
53
+ end
54
+
55
+ def admin
56
+ Class.new WEBrick::HTTPServlet::AbstractServlet do
57
+ def do_GET request, response
58
+ WEBrick::HTTPAuth.basic_auth(request, response, 'My Realm') do |user, pass|
59
+ credentials = SafeYAML.load_file(Jekyll::Configuration::DEFAULTS['source'] + '/admin/config.yml')
60
+ user == credentials['user'] && pass == credentials['password']
61
+ end
62
+ status, content_type, body = 'hello'
63
+
64
+ body_text = IO.read(File.join(Jekyll::Configuration::DEFAULTS['source'], 'admin/posts.html'))
65
+ body_text.gsub!('% previous_post %', IO.read(Jekyll::Configuration::DEFAULTS['source'] + '/_posts/' + Dir.entries(Jekyll::Configuration::DEFAULTS['source'] + '/_posts')[-1]))
66
+
67
+ response.status = 200
68
+ response['Content-Type'] = 'text/html'
69
+ response.body = body_text
70
+ end
71
+
72
+ def do_POST request, response
73
+ puts request
74
+ File.open(Jekyll::Configuration::DEFAULTS['source'] + '/_posts/' + DateTime.now.strftime('%Y-%m-%d') + '-post' + DateTime.now.strftime('%s') + '.markdown', 'wb') do |f|
75
+ f.write(request.query['post'])
76
+ end
77
+ response.status = 200
78
+ response['Content-Type'] = 'text/html'
79
+ response.body = 'Your post has been added...'
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ # Do a base pre-setup of WEBRick so that everything is in place
86
+ # when we get ready to party, checking for an setting up an error page
87
+ # and making sure our destination exists.
88
+
89
+ private
90
+ def setup(destination)
91
+ require_relative "serve/servlet"
92
+
93
+ FileUtils.mkdir_p(destination)
94
+ if File.exist?(File.join(destination, "404.html"))
95
+ WEBrick::HTTPResponse.class_eval do
96
+ def create_error_page
97
+ @header["Content-Type"] = "text/html; charset=UTF-8"
98
+ @body = IO.read(File.join(@config[:DocumentRoot], "404.html"))
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ #
105
+
106
+ private
107
+ def webrick_opts(opts)
108
+ opts = {
109
+ :JekyllOptions => opts,
110
+ :DoNotReverseLookup => true,
111
+ :MimeTypes => mime_types,
112
+ :DocumentRoot => opts["destination"],
113
+ :StartCallback => start_callback(opts["detach"]),
114
+ :BindAddress => opts["host"],
115
+ :Port => opts["port"],
116
+ :DirectoryIndex => %W(
117
+ index.htm
118
+ index.html
119
+ index.rhtml
120
+ index.cgi
121
+ index.xml
122
+ )
123
+ }
124
+
125
+ enable_ssl(opts)
126
+ enable_logging(opts)
127
+ opts
128
+ end
129
+
130
+ # Recreate NondisclosureName under utf-8 circumstance
131
+
132
+ private
133
+ def file_handler_opts
134
+ WEBrick::Config::FileHandler.merge({
135
+ :FancyIndexing => true,
136
+ :NondisclosureName => [
137
+ '.ht*', '~*'
138
+ ]
139
+ })
140
+ end
141
+
142
+ #
143
+
144
+ private
145
+ def server_address(server, opts)
146
+ address = server.config[:BindAddress]
147
+ baseurl = "#{opts["baseurl"]}/" if opts["baseurl"]
148
+ port = server.config[:Port]
149
+
150
+ "http://#{address}:#{port}#{baseurl}"
151
+ end
152
+
153
+ #
154
+
155
+ private
156
+ def launch_browser(server, opts)
157
+ command =
158
+ if Utils::Platforms.windows?
159
+ "start"
160
+ elsif Utils::Platforms.osx?
161
+ "open"
162
+ else
163
+ "xdg-open"
164
+ end
165
+ system command, server_address(server, opts)
166
+ end
167
+
168
+ # Keep in our area with a thread or detach the server as requested
169
+ # by the user. This method determines what we do based on what you
170
+ # ask us to do.
171
+
172
+ private
173
+ def boot_or_detach(server, opts)
174
+ if opts["detach"]
175
+ pid = Process.fork do
176
+ server.start
177
+ end
178
+
179
+ Process.detach(pid)
180
+ Jekyll.logger.info "Server detached with pid '#{pid}'.", \
181
+ "Run `pkill -f jekyll' or `kill -9 #{pid}' to stop the server."
182
+ else
183
+ t = Thread.new { server.start }
184
+ trap("INT") { server.shutdown }
185
+ t.join
186
+ end
187
+ end
188
+
189
+ # Make the stack verbose if the user requests it.
190
+
191
+ private
192
+ def enable_logging(opts)
193
+ opts[:AccessLog] = []
194
+ level = WEBrick::Log.const_get(opts[:JekyllOptions]["verbose"] ? :DEBUG : :WARN)
195
+ opts[:Logger] = WEBrick::Log.new($stdout, level)
196
+ end
197
+
198
+ # Add SSL to the stack if the user triggers --enable-ssl and they
199
+ # provide both types of certificates commonly needed. Raise if they
200
+ # forget to add one of the certificates.
201
+
202
+ private
203
+ def enable_ssl(opts)
204
+ return if !opts[:JekyllOptions]["ssl_cert"] && !opts[:JekyllOptions]["ssl_key"]
205
+ if !opts[:JekyllOptions]["ssl_cert"] || !opts[:JekyllOptions]["ssl_key"]
206
+ raise RuntimeError, "--ssl-cert or --ssl-key missing."
207
+ end
208
+
209
+ require "openssl"
210
+ require "webrick/https"
211
+ source_key = Jekyll.sanitized_path(opts[:JekyllOptions]["source"], opts[:JekyllOptions]["ssl_key" ])
212
+ source_certificate = Jekyll.sanitized_path(opts[:JekyllOptions]["source"], opts[:JekyllOptions]["ssl_cert"])
213
+ opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(source_certificate))
214
+ opts[:SSLPrivateKey ] = OpenSSL::PKey::RSA.new(File.read(source_key))
215
+ opts[:EnableSSL] = true
216
+ end
217
+
218
+ private
219
+ def start_callback(detached)
220
+ unless detached
221
+ proc do
222
+ Jekyll.logger.info("Server running...", "press ctrl-c to stop.")
223
+ end
224
+ end
225
+ end
226
+
227
+ private
228
+ def mime_types
229
+ file = File.expand_path('../mime.types', File.dirname(__FILE__))
230
+ WEBrick::HTTPUtils.load_mime_types(file)
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,61 @@
1
+ require "webrick"
2
+
3
+ module Jekyll
4
+ module Commands
5
+ class Serve
6
+ class Servlet < WEBrick::HTTPServlet::FileHandler
7
+ DEFAULTS = {
8
+ "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
9
+ "no-store, no-cache, must-revalidate"
10
+ }
11
+
12
+ def initialize(server, root, callbacks)
13
+ # So we can access them easily.
14
+ @jekyll_opts = server.config[:JekyllOptions]
15
+ set_defaults
16
+ super
17
+ end
18
+
19
+ # Add the ability to tap file.html the same way that Nginx does on our
20
+ # Docker images (or on GitHub Pages.) The difference is that we might end
21
+ # up with a different preference on which comes first.
22
+
23
+ def search_file(req, res, basename)
24
+ # /file.* > /file/index.html > /file.html
25
+ super || super(req, res, "#{basename}.html")
26
+ end
27
+
28
+ #
29
+
30
+ def do_GET(req, res)
31
+ rtn = super
32
+ validate_and_ensure_charset(req, res)
33
+ res.header.merge!(@headers)
34
+ rtn
35
+ end
36
+
37
+ #
38
+
39
+ private
40
+ def validate_and_ensure_charset(_req, res)
41
+ key = res.header.keys.grep(/content-type/i).first
42
+ typ = res.header[key]
43
+
44
+ unless typ =~ /;\s*charset=/
45
+ res.header[key] = "#{typ}; charset=#{@jekyll_opts["encoding"]}"
46
+ end
47
+ end
48
+
49
+ #
50
+
51
+ private
52
+ def set_defaults
53
+ hash_ = @jekyll_opts.fetch("webrick", {}).fetch("headers", {})
54
+ DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash|
55
+ hash[key] = val unless hash.key?(key)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,323 @@
1
+ # encoding: UTF-8
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
+ 'plugins_dir' => '_plugins',
12
+ 'layouts_dir' => '_layouts',
13
+ 'data_dir' => '_data',
14
+ 'includes_dir' => '_includes',
15
+ 'collections' => {},
16
+
17
+ # Handling Reading
18
+ 'safe' => false,
19
+ 'include' => ['.htaccess'],
20
+ 'exclude' => [],
21
+ 'keep_files' => ['.git', '.svn'],
22
+ 'encoding' => 'utf-8',
23
+ 'markdown_ext' => 'markdown,mkdown,mkdn,mkd,md',
24
+
25
+ # Filtering Content
26
+ 'show_drafts' => nil,
27
+ 'limit_posts' => 0,
28
+ 'future' => false,
29
+ 'unpublished' => false,
30
+
31
+ # Plugins
32
+ 'whitelist' => [],
33
+ 'gems' => [],
34
+
35
+ # Conversion
36
+ 'markdown' => 'kramdown',
37
+ 'highlighter' => 'rouge',
38
+ 'lsi' => false,
39
+ 'excerpt_separator' => "\n\n",
40
+ 'incremental' => false,
41
+
42
+ # Serving
43
+ 'detach' => false, # default to not detaching the server
44
+ 'port' => '4000',
45
+ 'host' => '127.0.0.1',
46
+ 'baseurl' => '',
47
+
48
+ # Output Configuration
49
+ 'permalink' => 'date',
50
+ 'paginate_path' => '/page:num',
51
+ 'timezone' => nil, # use the local timezone
52
+
53
+ 'quiet' => false,
54
+ 'verbose' => false,
55
+ 'defaults' => [],
56
+
57
+ 'rdiscount' => {
58
+ 'extensions' => []
59
+ },
60
+
61
+ 'redcarpet' => {
62
+ 'extensions' => []
63
+ },
64
+
65
+ 'kramdown' => {
66
+ 'auto_ids' => true,
67
+ 'toc_levels' => '1..6',
68
+ 'entity_output' => 'as_char',
69
+ 'smart_quotes' => 'lsquo,rsquo,ldquo,rdquo',
70
+ 'input' => "GFM",
71
+ 'hard_wrap' => false,
72
+ 'footnote_nr' => 1
73
+ }
74
+ }]
75
+
76
+ # Public: Turn all keys into string
77
+ #
78
+ # Return a copy of the hash where all its keys are strings
79
+ def stringify_keys
80
+ reduce({}) { |hsh, (k, v)| hsh.merge(k.to_s => v) }
81
+ end
82
+
83
+ def get_config_value_with_override(config_key, override)
84
+ override[config_key] || self[config_key] || DEFAULTS[config_key]
85
+ end
86
+
87
+ # Public: Directory of the Jekyll source folder
88
+ #
89
+ # override - the command-line options hash
90
+ #
91
+ # Returns the path to the Jekyll source directory
92
+ def source(override)
93
+ get_config_value_with_override('source', override)
94
+ end
95
+
96
+ def quiet(override = {})
97
+ get_config_value_with_override('quiet', override)
98
+ end
99
+ alias_method :quiet?, :quiet
100
+
101
+ def verbose(override = {})
102
+ get_config_value_with_override('verbose', override)
103
+ end
104
+ alias_method :verbose?, :verbose
105
+
106
+ def safe_load_file(filename)
107
+ case File.extname(filename)
108
+ when /\.toml/i
109
+ Jekyll::External.require_with_graceful_fail('toml') unless defined?(TOML)
110
+ TOML.load_file(filename)
111
+ when /\.ya?ml/i
112
+ SafeYAML.load_file(filename) || {}
113
+ else
114
+ raise ArgumentError, "No parser for '#{filename}' is available. Use a .toml or .y(a)ml file instead."
115
+ end
116
+ end
117
+
118
+ # Public: Generate list of configuration files from the override
119
+ #
120
+ # override - the command-line options hash
121
+ #
122
+ # Returns an Array of config files
123
+ def config_files(override)
124
+ # Adjust verbosity quickly
125
+ Jekyll.logger.adjust_verbosity(:quiet => quiet?(override), :verbose => verbose?(override))
126
+
127
+ # Get configuration from <source>/_config.yml or <source>/<config_file>
128
+ config_files = override.delete('config')
129
+ if config_files.to_s.empty?
130
+ default = %w(yml yaml).find(-> { 'yml' }) do |ext|
131
+ File.exist?(Jekyll.sanitized_path(source(override), "_config.#{ext}"))
132
+ end
133
+ config_files = Jekyll.sanitized_path(source(override), "_config.#{default}")
134
+ @default_config_file = true
135
+ end
136
+ config_files = [config_files] unless config_files.is_a? Array
137
+ config_files
138
+ end
139
+
140
+ # Public: Read configuration and return merged Hash
141
+ #
142
+ # file - the path to the YAML file to be read in
143
+ #
144
+ # Returns this configuration, overridden by the values in the file
145
+ def read_config_file(file)
146
+ next_config = safe_load_file(file)
147
+ check_config_is_hash!(next_config, file)
148
+ Jekyll.logger.info "Configuration file:", file
149
+ next_config
150
+ rescue SystemCallError
151
+ if @default_config_file
152
+ Jekyll.logger.warn "Configuration file:", "none"
153
+ {}
154
+ else
155
+ Jekyll.logger.error "Fatal:", "The configuration file '#{file}' could not be found."
156
+ raise LoadError, "The Configuration file '#{file}' could not be found."
157
+ end
158
+ end
159
+
160
+ # Public: Read in a list of configuration files and merge with this hash
161
+ #
162
+ # files - the list of configuration file paths
163
+ #
164
+ # Returns the full configuration, with the defaults overridden by the values in the
165
+ # configuration files
166
+ def read_config_files(files)
167
+ configuration = clone
168
+
169
+ begin
170
+ files.each do |config_file|
171
+ new_config = read_config_file(config_file)
172
+ configuration = Utils.deep_merge_hashes(configuration, new_config)
173
+ end
174
+ rescue ArgumentError => err
175
+ Jekyll.logger.warn "WARNING:", "Error reading configuration. " \
176
+ "Using defaults (and options)."
177
+ $stderr.puts "#{err}"
178
+ end
179
+
180
+ configuration.fix_common_issues.backwards_compatibilize.add_default_collections
181
+ end
182
+
183
+ # Public: Split a CSV string into an array containing its values
184
+ #
185
+ # csv - the string of comma-separated values
186
+ #
187
+ # Returns an array of the values contained in the CSV
188
+ def csv_to_array(csv)
189
+ csv.split(",").map(&:strip)
190
+ end
191
+
192
+ # Public: Ensure the proper options are set in the configuration to allow for
193
+ # backwards-compatibility with Jekyll pre-1.0
194
+ #
195
+ # Returns the backwards-compatible configuration
196
+ def backwards_compatibilize
197
+ config = clone
198
+ # Provide backwards-compatibility
199
+ if config.key?('auto') || config.key?('watch')
200
+ Jekyll::Deprecator.deprecation_message "Auto-regeneration can no longer" \
201
+ " be set from your configuration file(s). Use the"\
202
+ " --[no-]watch/-w command-line option instead."
203
+ config.delete('auto')
204
+ config.delete('watch')
205
+ end
206
+
207
+ if config.key? 'server'
208
+ Jekyll::Deprecator.deprecation_message "The 'server' configuration option" \
209
+ " is no longer accepted. Use the 'jekyll serve'" \
210
+ " subcommand to serve your site with WEBrick."
211
+ config.delete('server')
212
+ end
213
+
214
+ renamed_key 'server_port', 'port', config
215
+ renamed_key 'plugins', 'plugins_dir', config
216
+ renamed_key 'layouts', 'layouts_dir', config
217
+ renamed_key 'data_source', 'data_dir', config
218
+
219
+ if config.key? 'pygments'
220
+ Jekyll::Deprecator.deprecation_message "The 'pygments' configuration option" \
221
+ " has been renamed to 'highlighter'. Please update your" \
222
+ " config file accordingly. The allowed values are 'rouge', " \
223
+ "'pygments' or null."
224
+
225
+ config['highlighter'] = 'pygments' if config['pygments']
226
+ config.delete('pygments')
227
+ end
228
+
229
+ %w(include exclude).each do |option|
230
+ config[option] ||= []
231
+ if config[option].is_a?(String)
232
+ Jekyll::Deprecator.deprecation_message "The '#{option}' configuration option" \
233
+ " must now be specified as an array, but you specified" \
234
+ " a string. For now, we've treated the string you provided" \
235
+ " as a list of comma-separated values."
236
+ config[option] = csv_to_array(config[option])
237
+ end
238
+ config[option].map!(&:to_s)
239
+ end
240
+
241
+ if (config['kramdown'] || {}).key?('use_coderay')
242
+ Jekyll::Deprecator.deprecation_message "Please change 'use_coderay'" \
243
+ " to 'enable_coderay' in your configuration file."
244
+ config['kramdown']['use_coderay'] = config['kramdown'].delete('enable_coderay')
245
+ end
246
+
247
+ if config.fetch('markdown', 'kramdown').to_s.downcase.eql?("maruku")
248
+ Jekyll.logger.abort_with "Error:", "You're using the 'maruku' " \
249
+ "Markdown processor, which has been removed as of 3.0.0. " \
250
+ "We recommend you switch to Kramdown. To do this, replace " \
251
+ "`markdown: maruku` with `markdown: kramdown` in your " \
252
+ "`_config.yml` file."
253
+ end
254
+
255
+ config
256
+ end
257
+
258
+ def fix_common_issues
259
+ config = clone
260
+
261
+ if config.key?('paginate') && (!config['paginate'].is_a?(Integer) || config['paginate'] < 1)
262
+ Jekyll.logger.warn "Config Warning:", "The `paginate` key must be a" \
263
+ " positive integer or nil. It's currently set to '#{config['paginate'].inspect}'."
264
+ config['paginate'] = nil
265
+ end
266
+
267
+ config
268
+ end
269
+
270
+ def add_default_collections
271
+ config = clone
272
+
273
+ return config if config['collections'].nil?
274
+
275
+ if config['collections'].is_a?(Array)
276
+ config['collections'] = Hash[config['collections'].map { |c| [c, {}] }]
277
+ end
278
+ config['collections']['posts'] ||= {}
279
+ config['collections']['posts']['output'] = true
280
+ config['collections']['posts']['permalink'] = style_to_permalink(config['permalink'])
281
+
282
+ config
283
+ end
284
+
285
+ def renamed_key(old, new, config, _ = nil)
286
+ if config.key?(old)
287
+ Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" \
288
+ "option has been renamed to '#{new}'. Please update your config " \
289
+ "file accordingly."
290
+ config[new] = config.delete(old)
291
+ end
292
+ end
293
+
294
+ private
295
+
296
+ def style_to_permalink(permalink_style)
297
+ case permalink_style.to_sym
298
+ when :pretty
299
+ "/:categories/:year/:month/:day/:title/"
300
+ when :none
301
+ "/:categories/:title:output_ext"
302
+ when :date
303
+ "/:categories/:year/:month/:day/:title:output_ext"
304
+ when :ordinal
305
+ "/:categories/:year/:y_day/:title:output_ext"
306
+ else
307
+ permalink_style.to_s
308
+ end
309
+ end
310
+
311
+ # Private: Checks if a given config is a hash
312
+ #
313
+ # extracted_config - the value to check
314
+ # file - the file from which the config was extracted
315
+ #
316
+ # Raises an ArgumentError if given config is not a hash
317
+ def check_config_is_hash!(extracted_config, file)
318
+ unless extracted_config.is_a?(Hash)
319
+ raise ArgumentError.new("Configuration file: (INVALID) #{file}".yellow)
320
+ end
321
+ end
322
+ end
323
+ end