jekyll 4.0.0.pre.alpha1 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +58 -17
- data/lib/jekyll.rb +5 -1
- data/lib/jekyll/cache.rb +71 -64
- data/lib/jekyll/cleaner.rb +5 -5
- data/lib/jekyll/collection.rb +6 -4
- data/lib/jekyll/command.rb +4 -2
- data/lib/jekyll/commands/new.rb +4 -4
- data/lib/jekyll/commands/serve.rb +9 -1
- data/lib/jekyll/commands/serve/servlet.rb +13 -14
- data/lib/jekyll/commands/serve/websockets.rb +1 -1
- data/lib/jekyll/configuration.rb +40 -129
- data/lib/jekyll/converters/identity.rb +2 -2
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -9
- data/lib/jekyll/convertible.rb +21 -20
- data/lib/jekyll/document.rb +56 -31
- data/lib/jekyll/drops/document_drop.rb +12 -0
- data/lib/jekyll/drops/drop.rb +14 -8
- data/lib/jekyll/drops/site_drop.rb +11 -1
- data/lib/jekyll/drops/url_drop.rb +52 -1
- data/lib/jekyll/entry_filter.rb +38 -44
- data/lib/jekyll/excerpt.rb +3 -3
- data/lib/jekyll/filters.rb +140 -22
- data/lib/jekyll/filters/url_filters.rb +41 -14
- data/lib/jekyll/frontmatter_defaults.rb +15 -20
- data/lib/jekyll/hooks.rb +2 -5
- data/lib/jekyll/inclusion.rb +32 -0
- data/lib/jekyll/liquid_renderer.rb +18 -15
- data/lib/jekyll/liquid_renderer/file.rb +10 -0
- data/lib/jekyll/liquid_renderer/table.rb +18 -61
- data/lib/jekyll/mime.types +53 -11
- data/lib/jekyll/page.rb +26 -2
- data/lib/jekyll/page_excerpt.rb +25 -0
- data/lib/jekyll/path_manager.rb +31 -0
- data/lib/jekyll/profiler.rb +58 -0
- data/lib/jekyll/reader.rb +4 -1
- data/lib/jekyll/readers/collection_reader.rb +1 -0
- data/lib/jekyll/readers/data_reader.rb +1 -0
- data/lib/jekyll/readers/layout_reader.rb +1 -0
- data/lib/jekyll/readers/page_reader.rb +5 -5
- data/lib/jekyll/readers/post_reader.rb +2 -1
- data/lib/jekyll/readers/static_file_reader.rb +3 -3
- data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
- data/lib/jekyll/renderer.rb +9 -15
- data/lib/jekyll/site.rb +21 -12
- data/lib/jekyll/static_file.rb +15 -10
- data/lib/jekyll/tags/highlight.rb +2 -4
- data/lib/jekyll/tags/include.rb +67 -11
- data/lib/jekyll/tags/post_url.rb +8 -5
- data/lib/jekyll/theme.rb +19 -10
- data/lib/jekyll/url.rb +7 -3
- data/lib/jekyll/utils.rb +14 -18
- data/lib/jekyll/utils/platforms.rb +1 -1
- data/lib/jekyll/utils/win_tz.rb +1 -1
- data/lib/jekyll/version.rb +1 -1
- data/lib/theme_template/theme.gemspec.erb +1 -4
- metadata +33 -36
data/lib/jekyll/page.rb
CHANGED
@@ -15,6 +15,7 @@ module Jekyll
|
|
15
15
|
ATTRIBUTES_FOR_LIQUID = %w(
|
16
16
|
content
|
17
17
|
dir
|
18
|
+
excerpt
|
18
19
|
name
|
19
20
|
path
|
20
21
|
url
|
@@ -47,7 +48,8 @@ module Jekyll
|
|
47
48
|
end
|
48
49
|
|
49
50
|
process(name)
|
50
|
-
read_yaml(
|
51
|
+
read_yaml(PathManager.join(base, dir), name)
|
52
|
+
generate_excerpt if site.config["page_excerpts"]
|
51
53
|
|
52
54
|
data.default_proc = proc do |_, key|
|
53
55
|
site.frontmatter_defaults.find(relative_path, type, key)
|
@@ -145,7 +147,7 @@ module Jekyll
|
|
145
147
|
|
146
148
|
# The path to the page source file, relative to the site source
|
147
149
|
def relative_path
|
148
|
-
@relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A
|
150
|
+
@relative_path ||= File.join(*[@dir, @name].map(&:to_s).reject(&:empty?)).sub(%r!\A/!, "")
|
149
151
|
end
|
150
152
|
|
151
153
|
# Obtain destination path.
|
@@ -182,5 +184,27 @@ module Jekyll
|
|
182
184
|
def write?
|
183
185
|
true
|
184
186
|
end
|
187
|
+
|
188
|
+
def excerpt_separator
|
189
|
+
@excerpt_separator ||= (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
|
190
|
+
end
|
191
|
+
|
192
|
+
def excerpt
|
193
|
+
return @excerpt if defined?(@excerpt)
|
194
|
+
|
195
|
+
@excerpt = data["excerpt"]&.to_s
|
196
|
+
end
|
197
|
+
|
198
|
+
def generate_excerpt?
|
199
|
+
!excerpt_separator.empty? && self.class == Jekyll::Page && html?
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
|
204
|
+
def generate_excerpt
|
205
|
+
return unless generate_excerpt?
|
206
|
+
|
207
|
+
data["excerpt"] ||= Jekyll::PageExcerpt.new(self)
|
208
|
+
end
|
185
209
|
end
|
186
210
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
class PageExcerpt < Excerpt
|
5
|
+
attr_reader :doc
|
6
|
+
alias_method :id, :relative_path
|
7
|
+
|
8
|
+
EXCERPT_ATTRIBUTES = (Page::ATTRIBUTES_FOR_LIQUID - %w(excerpt)).freeze
|
9
|
+
private_constant :EXCERPT_ATTRIBUTES
|
10
|
+
|
11
|
+
def to_liquid
|
12
|
+
@to_liquid ||= doc.to_liquid(EXCERPT_ATTRIBUTES)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_with_liquid?
|
16
|
+
return false if data["render_with_liquid"] == false
|
17
|
+
|
18
|
+
Jekyll::Utils.has_liquid_construct?(content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<#{self.class} id=#{id.inspect}>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
# A singleton class that caches frozen instances of path strings returned from its methods.
|
5
|
+
#
|
6
|
+
# NOTE:
|
7
|
+
# This class exists because `File.join` allocates an Array and returns a new String on every
|
8
|
+
# call using **the same arguments**. Caching the result means reduced memory usage.
|
9
|
+
# However, the caches are never flushed so that they can be used even when a site is
|
10
|
+
# regenerating. The results are frozen to deter mutation of the cached string.
|
11
|
+
#
|
12
|
+
# Therefore, employ this class only for situations where caching the result is necessary
|
13
|
+
# for performance reasons.
|
14
|
+
#
|
15
|
+
class PathManager
|
16
|
+
# This class cannot be initialized from outside
|
17
|
+
private_class_method :new
|
18
|
+
|
19
|
+
# Wraps `File.join` to cache the frozen result.
|
20
|
+
# Reassigns `nil`, empty strings and empty arrays to a frozen empty string beforehand.
|
21
|
+
#
|
22
|
+
# Returns a frozen string.
|
23
|
+
def self.join(base, item)
|
24
|
+
base = "" if base.nil? || base.empty?
|
25
|
+
item = "" if item.nil? || item.empty?
|
26
|
+
@join ||= {}
|
27
|
+
@join[base] ||= {}
|
28
|
+
@join[base][item] ||= File.join(base, item).freeze
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
class Profiler
|
5
|
+
TERMINAL_TABLE_STYLES = {
|
6
|
+
:alignment => :right,
|
7
|
+
:border_top => false,
|
8
|
+
:border_bottom => false,
|
9
|
+
}.freeze
|
10
|
+
private_constant :TERMINAL_TABLE_STYLES
|
11
|
+
|
12
|
+
def self.tabulate(table_rows)
|
13
|
+
require "terminal-table"
|
14
|
+
|
15
|
+
rows = table_rows.dup
|
16
|
+
header = rows.shift
|
17
|
+
footer = rows.pop
|
18
|
+
output = +"\n"
|
19
|
+
|
20
|
+
table = Terminal::Table.new do |t|
|
21
|
+
t << header
|
22
|
+
t << :separator
|
23
|
+
rows.each { |row| t << row }
|
24
|
+
t << :separator
|
25
|
+
t << footer
|
26
|
+
t.style = TERMINAL_TABLE_STYLES
|
27
|
+
t.align_column(0, :left)
|
28
|
+
end
|
29
|
+
|
30
|
+
output << table.to_s << "\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(site)
|
34
|
+
@site = site
|
35
|
+
end
|
36
|
+
|
37
|
+
def profile_process
|
38
|
+
profile_data = { "PHASE" => "TIME" }
|
39
|
+
total_time = 0
|
40
|
+
|
41
|
+
[:reset, :read, :generate, :render, :cleanup, :write].each do |method|
|
42
|
+
start_time = Time.now
|
43
|
+
@site.send(method)
|
44
|
+
end_time = (Time.now - start_time).round(4)
|
45
|
+
profile_data[method.to_s.upcase] = format("%.4f", end_time)
|
46
|
+
total_time += end_time
|
47
|
+
end
|
48
|
+
|
49
|
+
profile_data["TOTAL TIME"] = format("%.4f", total_time)
|
50
|
+
|
51
|
+
Jekyll.logger.info "\nBuild Process Summary:"
|
52
|
+
Jekyll.logger.info Profiler.tabulate(Array(profile_data))
|
53
|
+
|
54
|
+
Jekyll.logger.info "\nSite Render Stats:"
|
55
|
+
@site.print_stats
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/jekyll/reader.rb
CHANGED
@@ -85,7 +85,7 @@ module Jekyll
|
|
85
85
|
def retrieve_dirs(_base, dir, dot_dirs)
|
86
86
|
dot_dirs.each do |file|
|
87
87
|
dir_path = site.in_source_dir(dir, file)
|
88
|
-
rel_path =
|
88
|
+
rel_path = PathManager.join(dir, file)
|
89
89
|
@site.reader.read_directories(rel_path) unless @site.dest.chomp("/") == dir_path
|
90
90
|
end
|
91
91
|
end
|
@@ -161,11 +161,14 @@ module Jekyll
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def read_included_excludes
|
164
|
+
entry_filter = EntryFilter.new(site)
|
165
|
+
|
164
166
|
site.include.each do |entry|
|
165
167
|
next if entry == ".htaccess"
|
166
168
|
|
167
169
|
entry_path = site.in_source_dir(entry)
|
168
170
|
next if File.directory?(entry_path)
|
171
|
+
next if entry_filter.symlink?(entry_path)
|
169
172
|
|
170
173
|
read_included_file(entry_path) if File.file?(entry_path)
|
171
174
|
end
|
@@ -3,20 +3,20 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class PageReader
|
5
5
|
attr_reader :site, :dir, :unfiltered_content
|
6
|
+
|
6
7
|
def initialize(site, dir)
|
7
8
|
@site = site
|
8
9
|
@dir = dir
|
9
10
|
@unfiltered_content = []
|
10
11
|
end
|
11
12
|
|
12
|
-
#
|
13
|
-
# object for each file.
|
13
|
+
# Create a new `Jekyll::Page` object for each entry in a given array.
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# files - An array of file names inside `@dir`
|
16
16
|
#
|
17
|
-
# Returns an array of
|
17
|
+
# Returns an array of publishable `Jekyll::Page` objects.
|
18
18
|
def read(files)
|
19
|
-
files.
|
19
|
+
files.each do |page|
|
20
20
|
@unfiltered_content << Page.new(@site, @site.source, @dir, page)
|
21
21
|
end
|
22
22
|
@unfiltered_content.select { |page| site.publisher.publish?(page) }
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class PostReader
|
5
5
|
attr_reader :site, :unfiltered_content
|
6
|
+
|
6
7
|
def initialize(site)
|
7
8
|
@site = site
|
8
9
|
end
|
@@ -50,7 +51,7 @@ module Jekyll
|
|
50
51
|
# Returns klass type of content files
|
51
52
|
def read_content(dir, magic_dir, matcher)
|
52
53
|
@site.reader.get_entries(dir, magic_dir).map do |entry|
|
53
|
-
next unless entry
|
54
|
+
next unless matcher.match?(entry)
|
54
55
|
|
55
56
|
path = @site.in_source_dir(File.join(dir, magic_dir, entry))
|
56
57
|
Document.new(path,
|
@@ -3,16 +3,16 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class StaticFileReader
|
5
5
|
attr_reader :site, :dir, :unfiltered_content
|
6
|
+
|
6
7
|
def initialize(site, dir)
|
7
8
|
@site = site
|
8
9
|
@dir = dir
|
9
10
|
@unfiltered_content = []
|
10
11
|
end
|
11
12
|
|
12
|
-
#
|
13
|
-
# object for each file.
|
13
|
+
# Create a new StaticFile object for every entry in a given list of basenames.
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# files - an array of file basenames.
|
16
16
|
#
|
17
17
|
# Returns an array of static files.
|
18
18
|
def read(files)
|
data/lib/jekyll/renderer.rb
CHANGED
@@ -66,7 +66,7 @@ module Jekyll
|
|
66
66
|
# Render the document.
|
67
67
|
#
|
68
68
|
# Returns String rendered document output
|
69
|
-
# rubocop: disable AbcSize
|
69
|
+
# rubocop: disable Metrics/AbcSize
|
70
70
|
def render_document
|
71
71
|
info = {
|
72
72
|
:registers => { :site => site, :page => payload["page"] },
|
@@ -91,7 +91,7 @@ module Jekyll
|
|
91
91
|
|
92
92
|
output
|
93
93
|
end
|
94
|
-
# rubocop: enable AbcSize
|
94
|
+
# rubocop: enable Metrics/AbcSize
|
95
95
|
|
96
96
|
# Convert the document using the converters which match this renderer's document.
|
97
97
|
#
|
@@ -125,13 +125,13 @@ module Jekyll
|
|
125
125
|
LiquidRenderer.format_error(e, path || document.relative_path)
|
126
126
|
end
|
127
127
|
template.render!(payload, info)
|
128
|
-
# rubocop: disable RescueException
|
128
|
+
# rubocop: disable Lint/RescueException
|
129
129
|
rescue Exception => e
|
130
130
|
Jekyll.logger.error "Liquid Exception:",
|
131
131
|
LiquidRenderer.format_error(e, path || document.relative_path)
|
132
132
|
raise e
|
133
133
|
end
|
134
|
-
# rubocop: enable RescueException
|
134
|
+
# rubocop: enable Lint/RescueException
|
135
135
|
|
136
136
|
# Checks if the layout specified in the document actually exists
|
137
137
|
#
|
@@ -174,16 +174,10 @@ module Jekyll
|
|
174
174
|
# layout - the layout to check
|
175
175
|
# Returns nothing
|
176
176
|
def validate_layout(layout)
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
"in #{document.relative_path} does not exist."
|
182
|
-
)
|
183
|
-
elsif !layout.nil?
|
184
|
-
layout_source = layout.path.start_with?(site.source) ? :site : :theme
|
185
|
-
Jekyll.logger.debug "Layout source:", layout_source
|
186
|
-
end
|
177
|
+
return unless invalid_layout?(layout)
|
178
|
+
|
179
|
+
Jekyll.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
|
180
|
+
"in #{document.relative_path} does not exist."
|
187
181
|
end
|
188
182
|
|
189
183
|
# Render layout content into document.output
|
@@ -197,7 +191,7 @@ module Jekyll
|
|
197
191
|
layout.content,
|
198
192
|
payload,
|
199
193
|
info,
|
200
|
-
layout.
|
194
|
+
layout.path
|
201
195
|
)
|
202
196
|
end
|
203
197
|
|
data/lib/jekyll/site.rb
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class Site
|
5
5
|
attr_reader :source, :dest, :cache_dir, :config
|
6
|
-
attr_accessor :layouts, :pages, :static_files, :drafts,
|
6
|
+
attr_accessor :layouts, :pages, :static_files, :drafts, :inclusions,
|
7
7
|
:exclude, :include, :lsi, :highlighter, :permalink_style,
|
8
8
|
:time, :future, :unpublished, :safe, :plugins, :limit_posts,
|
9
9
|
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
|
10
10
|
:gems, :plugin_manager, :theme
|
11
11
|
|
12
12
|
attr_accessor :converters, :generators, :reader
|
13
|
-
attr_reader :regenerator, :liquid_renderer, :includes_load_paths
|
13
|
+
attr_reader :regenerator, :liquid_renderer, :includes_load_paths, :filter_cache, :profiler
|
14
14
|
|
15
15
|
# Public: Initialize a new Site.
|
16
16
|
#
|
@@ -23,8 +23,10 @@ module Jekyll
|
|
23
23
|
self.config = config
|
24
24
|
|
25
25
|
@cache_dir = in_source_dir(config["cache_dir"])
|
26
|
+
@filter_cache = {}
|
26
27
|
|
27
28
|
@reader = Reader.new(self)
|
29
|
+
@profiler = Profiler.new(self)
|
28
30
|
@regenerator = Regenerator.new(self)
|
29
31
|
@liquid_renderer = LiquidRenderer.new(self)
|
30
32
|
|
@@ -70,19 +72,21 @@ module Jekyll
|
|
70
72
|
#
|
71
73
|
# Returns nothing.
|
72
74
|
def process
|
75
|
+
return profiler.profile_process if config["profile"]
|
76
|
+
|
73
77
|
reset
|
74
78
|
read
|
75
79
|
generate
|
76
80
|
render
|
77
81
|
cleanup
|
78
82
|
write
|
79
|
-
print_stats if config["profile"]
|
80
83
|
end
|
81
84
|
|
82
85
|
def print_stats
|
83
86
|
Jekyll.logger.info @liquid_renderer.stats_table
|
84
87
|
end
|
85
88
|
|
89
|
+
# rubocop:disable Metrics/AbcSize
|
86
90
|
# rubocop:disable Metrics/MethodLength
|
87
91
|
#
|
88
92
|
# Reset Site details.
|
@@ -95,6 +99,7 @@ module Jekyll
|
|
95
99
|
Time.now
|
96
100
|
end
|
97
101
|
self.layouts = {}
|
102
|
+
self.inclusions = {}
|
98
103
|
self.pages = []
|
99
104
|
self.static_files = []
|
100
105
|
self.data = {}
|
@@ -114,6 +119,7 @@ module Jekyll
|
|
114
119
|
Jekyll::Hooks.trigger :site, :after_reset, self
|
115
120
|
end
|
116
121
|
# rubocop:enable Metrics/MethodLength
|
122
|
+
# rubocop:enable Metrics/AbcSize
|
117
123
|
|
118
124
|
# Load necessary libraries, plugins, converters, and generators.
|
119
125
|
#
|
@@ -145,9 +151,9 @@ module Jekyll
|
|
145
151
|
#
|
146
152
|
# Returns a Hash containing collection name-to-instance pairs.
|
147
153
|
def collections
|
148
|
-
@collections ||=
|
149
|
-
[
|
150
|
-
end
|
154
|
+
@collections ||= collection_names.each_with_object({}) do |name, hsh|
|
155
|
+
hsh[name] = Jekyll::Collection.new(self, name)
|
156
|
+
end
|
151
157
|
end
|
152
158
|
|
153
159
|
# The list of collection names.
|
@@ -326,15 +332,15 @@ module Jekyll
|
|
326
332
|
#
|
327
333
|
# Returns an Array of Documents which should be written
|
328
334
|
def docs_to_write
|
329
|
-
|
335
|
+
documents.select(&:write?)
|
330
336
|
end
|
331
337
|
|
332
338
|
# Get all the documents
|
333
339
|
#
|
334
340
|
# Returns an Array of all Documents
|
335
341
|
def documents
|
336
|
-
|
337
|
-
|
342
|
+
collections.each_with_object(Set.new) do |(_, collection), set|
|
343
|
+
set.merge(collection.docs).merge(collection.files)
|
338
344
|
end.to_a
|
339
345
|
end
|
340
346
|
|
@@ -431,6 +437,8 @@ module Jekyll
|
|
431
437
|
private
|
432
438
|
|
433
439
|
def load_theme_configuration(config)
|
440
|
+
return config if config["ignore_theme_config"] == true
|
441
|
+
|
434
442
|
theme_config_file = in_theme_dir("_config.yml")
|
435
443
|
return config unless File.exist?(theme_config_file)
|
436
444
|
|
@@ -469,8 +477,8 @@ module Jekyll
|
|
469
477
|
|
470
478
|
# Disable Marshaling cache to disk in Safe Mode
|
471
479
|
def configure_cache
|
472
|
-
Jekyll::Cache.
|
473
|
-
Jekyll::Cache.disable_disk_cache! if safe
|
480
|
+
Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
|
481
|
+
Jekyll::Cache.disable_disk_cache! if safe || config["disable_disk_cache"]
|
474
482
|
end
|
475
483
|
|
476
484
|
def configure_plugins
|
@@ -520,7 +528,8 @@ module Jekyll
|
|
520
528
|
def render_regenerated(document, payload)
|
521
529
|
return unless regenerator.regenerate?(document)
|
522
530
|
|
523
|
-
document.
|
531
|
+
document.renderer.payload = payload
|
532
|
+
document.output = document.renderer.run
|
524
533
|
document.trigger_hooks(:post_render)
|
525
534
|
end
|
526
535
|
end
|