jekyll 3.9.3 → 4.4.1

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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +511 -89
  3. data/LICENSE +1 -1
  4. data/README.markdown +48 -27
  5. data/lib/blank_template/_config.yml +3 -0
  6. data/lib/blank_template/_layouts/default.html +12 -0
  7. data/lib/blank_template/_sass/base.scss +9 -0
  8. data/lib/blank_template/assets/css/main.scss +4 -0
  9. data/lib/blank_template/index.md +8 -0
  10. data/lib/jekyll/cache.rb +186 -0
  11. data/lib/jekyll/cleaner.rb +8 -7
  12. data/lib/jekyll/collection.rb +84 -11
  13. data/lib/jekyll/command.rb +33 -6
  14. data/lib/jekyll/commands/build.rb +8 -28
  15. data/lib/jekyll/commands/clean.rb +3 -2
  16. data/lib/jekyll/commands/doctor.rb +46 -35
  17. data/lib/jekyll/commands/help.rb +1 -1
  18. data/lib/jekyll/commands/new.rb +44 -50
  19. data/lib/jekyll/commands/new_theme.rb +27 -28
  20. data/lib/jekyll/commands/serve/live_reload_reactor.rb +9 -16
  21. data/lib/jekyll/commands/serve/servlet.rb +21 -22
  22. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  23. data/lib/jekyll/commands/serve.rb +75 -97
  24. data/lib/jekyll/configuration.rb +66 -158
  25. data/lib/jekyll/converters/identity.rb +18 -0
  26. data/lib/jekyll/converters/markdown/kramdown_parser.rb +83 -33
  27. data/lib/jekyll/converters/markdown.rb +49 -40
  28. data/lib/jekyll/converters/smartypants.rb +34 -14
  29. data/lib/jekyll/convertible.rb +36 -34
  30. data/lib/jekyll/deprecator.rb +2 -4
  31. data/lib/jekyll/document.rb +107 -72
  32. data/lib/jekyll/drops/collection_drop.rb +3 -4
  33. data/lib/jekyll/drops/document_drop.rb +9 -3
  34. data/lib/jekyll/drops/drop.rb +115 -33
  35. data/lib/jekyll/drops/excerpt_drop.rb +8 -0
  36. data/lib/jekyll/drops/site_drop.rb +9 -8
  37. data/lib/jekyll/drops/static_file_drop.rb +4 -4
  38. data/lib/jekyll/drops/theme_drop.rb +39 -0
  39. data/lib/jekyll/drops/unified_payload_drop.rb +7 -2
  40. data/lib/jekyll/drops/url_drop.rb +55 -3
  41. data/lib/jekyll/entry_filter.rb +42 -51
  42. data/lib/jekyll/excerpt.rb +48 -38
  43. data/lib/jekyll/external.rb +20 -19
  44. data/lib/jekyll/filters/date_filters.rb +6 -3
  45. data/lib/jekyll/filters/grouping_filters.rb +1 -2
  46. data/lib/jekyll/filters/url_filters.rb +50 -15
  47. data/lib/jekyll/filters.rb +211 -50
  48. data/lib/jekyll/frontmatter_defaults.rb +45 -36
  49. data/lib/jekyll/hooks.rb +26 -26
  50. data/lib/jekyll/inclusion.rb +32 -0
  51. data/lib/jekyll/layout.rb +12 -19
  52. data/lib/jekyll/liquid_extensions.rb +0 -2
  53. data/lib/jekyll/liquid_renderer/file.rb +24 -3
  54. data/lib/jekyll/liquid_renderer/table.rb +26 -77
  55. data/lib/jekyll/liquid_renderer.rb +31 -16
  56. data/lib/jekyll/log_adapter.rb +5 -1
  57. data/lib/jekyll/page.rb +51 -23
  58. data/lib/jekyll/page_excerpt.rb +25 -0
  59. data/lib/jekyll/page_without_a_file.rb +0 -4
  60. data/lib/jekyll/path_manager.rb +74 -0
  61. data/lib/jekyll/plugin.rb +5 -11
  62. data/lib/jekyll/plugin_manager.rb +15 -5
  63. data/lib/jekyll/profiler.rb +51 -0
  64. data/lib/jekyll/reader.rb +65 -10
  65. data/lib/jekyll/readers/collection_reader.rb +1 -0
  66. data/lib/jekyll/readers/data_reader.rb +48 -10
  67. data/lib/jekyll/readers/layout_reader.rb +3 -12
  68. data/lib/jekyll/readers/page_reader.rb +5 -5
  69. data/lib/jekyll/readers/post_reader.rb +32 -19
  70. data/lib/jekyll/readers/static_file_reader.rb +4 -4
  71. data/lib/jekyll/readers/theme_assets_reader.rb +8 -5
  72. data/lib/jekyll/regenerator.rb +4 -12
  73. data/lib/jekyll/related_posts.rb +1 -1
  74. data/lib/jekyll/renderer.rb +34 -49
  75. data/lib/jekyll/site.rb +151 -58
  76. data/lib/jekyll/static_file.rb +64 -28
  77. data/lib/jekyll/stevenson.rb +4 -8
  78. data/lib/jekyll/tags/highlight.rb +44 -57
  79. data/lib/jekyll/tags/include.rb +114 -80
  80. data/lib/jekyll/tags/link.rb +12 -7
  81. data/lib/jekyll/tags/post_url.rb +33 -30
  82. data/lib/jekyll/theme.rb +20 -18
  83. data/lib/jekyll/theme_builder.rb +91 -89
  84. data/lib/jekyll/url.rb +18 -10
  85. data/lib/jekyll/utils/ansi.rb +2 -2
  86. data/lib/jekyll/utils/exec.rb +0 -1
  87. data/lib/jekyll/utils/internet.rb +2 -4
  88. data/lib/jekyll/utils/platforms.rb +37 -52
  89. data/lib/jekyll/utils/thread_event.rb +1 -5
  90. data/lib/jekyll/utils.rb +29 -28
  91. data/lib/jekyll/version.rb +1 -1
  92. data/lib/jekyll.rb +9 -14
  93. data/lib/site_template/.gitignore +2 -0
  94. data/lib/site_template/404.html +2 -1
  95. data/lib/site_template/_config.yml +17 -5
  96. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
  97. data/lib/theme_template/README.md.erb +1 -3
  98. data/lib/theme_template/gitignore.erb +1 -0
  99. data/lib/theme_template/theme.gemspec.erb +1 -4
  100. data/rubocop/jekyll/assert_equal_literal_actual.rb +150 -0
  101. data/rubocop/jekyll/no_p_allowed.rb +5 -6
  102. data/rubocop/jekyll/no_puts_allowed.rb +5 -6
  103. metadata +149 -37
  104. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
  105. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
  106. data/lib/jekyll/utils/rouge.rb +0 -22
  107. /data/lib/site_template/{about.md → about.markdown} +0 -0
  108. /data/lib/site_template/{index.md → index.markdown} +0 -0
data/README.markdown CHANGED
@@ -1,19 +1,12 @@
1
1
  # [Jekyll](https://jekyllrb.com/)
2
2
 
3
3
  [![Gem Version](https://img.shields.io/gem/v/jekyll.svg)][ruby-gems]
4
- [![Linux Build Status](https://img.shields.io/travis/jekyll/jekyll/master.svg?label=Linux%20build)][travis]
5
- [![Windows Build status](https://img.shields.io/appveyor/ci/jekyll/jekyll/master.svg?label=Windows%20build)][appveyor]
6
- [![Maintainability](https://api.codeclimate.com/v1/badges/8ba0cb5b17bb9848e128/maintainability)](codeclimate)
7
- [![Test Coverage](https://api.codeclimate.com/v1/badges/8ba0cb5b17bb9848e128/test_coverage)](coverage)
8
- [![Dependency Status](https://img.shields.io/gemnasium/jekyll/jekyll.svg)][gemnasium]
9
- [![Security](https://hakiri.io/github/jekyll/jekyll/master.svg)][hakiri]
4
+ [![Build Status](https://github.com/jekyll/jekyll/workflows/Continuous%20Integration/badge.svg)][ci-workflow]
5
+ [![Backers on Open Collective](https://opencollective.com/jekyll/backers/badge.svg)](#backers)
6
+ [![Sponsors on Open Collective](https://opencollective.com/jekyll/sponsors/badge.svg)](#sponsors)
10
7
 
11
8
  [ruby-gems]: https://rubygems.org/gems/jekyll
12
- [gemnasium]: https://gemnasium.com/jekyll/jekyll
13
- [codeclimate]: https://codeclimate.com/github/jekyll/jekyll
14
- [coverage]: https://codeclimate.com/github/jekyll/jekyll/coverage
15
- [hakiri]: https://hakiri.io/github/jekyll/jekyll/master
16
- [travis]: https://travis-ci.org/jekyll/jekyll
9
+ [ci-workflow]: https://github.com/jekyll/jekyll/actions?query=workflow%3A%22Continuous+Integration%22+branch%3Amaster
17
10
  [appveyor]: https://ci.appveyor.com/project/jekyll/jekyll/branch/master
18
11
 
19
12
  Jekyll is a simple, blog-aware, static site generator perfect for personal, project, or organization sites. Think of it like a file-based CMS, without all the complexity. Jekyll takes your content, renders Markdown and Liquid templates, and spits out a complete, static website ready to be served by Apache, Nginx or another web server. Jekyll is the engine behind [GitHub Pages](https://pages.github.com), which you can use to host sites right from your GitHub repositories.
@@ -22,39 +15,67 @@ Jekyll is a simple, blog-aware, static site generator perfect for personal, proj
22
15
 
23
16
  Jekyll does what you tell it to do — no more, no less. It doesn't try to outsmart users by making bold assumptions, nor does it burden them with needless complexity and configuration. Put simply, Jekyll gets out of your way and allows you to concentrate on what truly matters: your content.
24
17
 
25
- See: https://jekyllrb.com/philosophy
26
-
27
- ## Having trouble?
28
-
29
- See: https://jekyllrb.com/docs/troubleshooting/
18
+ See: [https://jekyllrb.com/philosophy](https://jekyllrb.com/philosophy)
30
19
 
31
20
  ## Getting Started
32
21
 
33
22
  * [Install](https://jekyllrb.com/docs/installation/) the gem
34
23
  * Read up about its [Usage](https://jekyllrb.com/docs/usage/) and [Configuration](https://jekyllrb.com/docs/configuration/)
35
- * Take a gander at some existing [Sites](https://wiki.github.com/jekyll/jekyll/sites)
24
+ * Take a gander at some existing [Sites](https://github.com/jekyll/jekyll/wiki/sites)
36
25
  * [Fork](https://github.com/jekyll/jekyll/fork) and [Contribute](https://jekyllrb.com/docs/contributing/) your own modifications
37
- * Have questions? Check out our official forum community [Jekyll Talk](https://talk.jekyllrb.com/) or [`#jekyll` on irc.freenode.net](https://botbot.me/freenode/jekyll/)
26
+ * Have questions? Check out our official forum community [Jekyll Talk](https://talk.jekyllrb.com/) and [`#jekyll` Channel on Libera IRC](https://libera.chat)
27
+
28
+ ## Diving In
29
+
30
+ * [Migrate](https://import.jekyllrb.com/docs/home/) from your previous system
31
+ * Learn how [Front Matter](https://jekyllrb.com/docs/front-matter/) works
32
+ * Put information on your site with [Variables](https://jekyllrb.com/docs/variables/)
33
+ * Customize the [Permalinks](https://jekyllrb.com/docs/permalinks/) your posts are generated with
34
+ * Use the built-in [Liquid Extensions](https://jekyllrb.com/docs/templates/) to make your life easier
35
+ * Use custom [Plugins](https://jekyllrb.com/docs/plugins/) to generate content specific to your site
36
+ * Watch [video tutorials from Giraffe Academy](https://jekyllrb.com/tutorials/video-walkthroughs/)
37
+
38
+ ## Need help?
39
+
40
+ If you don't find the answer to your problem in our [docs](https://jekyllrb.com/docs/), or in the [troubleshooting section](https://jekyllrb.com/docs/troubleshooting/), ask the [community](https://jekyllrb.com/docs/community/) for help.
38
41
 
39
42
  ## Code of Conduct
40
43
 
41
44
  In order to have a more open and welcoming community, Jekyll adheres to a
42
- [code of conduct](CODE_OF_CONDUCT.markdown) adapted from the Ruby on Rails code of
45
+ [code of conduct](https://jekyllrb.com/docs/conduct/) adapted from the Ruby on Rails code of
43
46
  conduct.
44
47
 
45
48
  Please adhere to this code of conduct in any interactions you have in the
46
49
  Jekyll community. It is strictly enforced on all official Jekyll
47
50
  repositories, websites, and resources. If you encounter someone violating
48
- these terms, please let one of our core team members [Olivia](mailto:olivia@jekyllrb.com?subject=Jekyll%20CoC%20Violation), [Pat](mailto:pat@jekyllrb.com?subject=Jekyll%20CoC%20Violation), [Matt](mailto:matt@jekyllrb.com?subject=Jekyll%20CoC%20Violation) or [Parker](mailto:parker@jekyllrb.com?subject=Jekyll%20CoC%20Violation) know and we will address it as soon as possible.
51
+ these terms, please let one of our [core team members](https://jekyllrb.com/team/#core-team) know and we will address it as soon as possible.
49
52
 
50
- ## Diving In
53
+ ## Credits
51
54
 
52
- * [Migrate](http://import.jekyllrb.com/docs/home/) from your previous system
53
- * Learn how the [YAML Front Matter](https://jekyllrb.com/docs/frontmatter/) works
54
- * Put information on your site with [Variables](https://jekyllrb.com/docs/variables/)
55
- * Customize the [Permalinks](https://jekyllrb.com/docs/permalinks/) your posts are generated with
56
- * Use the built-in [Liquid Extensions](https://jekyllrb.com/docs/templates/) to make your life easier
57
- * Use custom [Plugins](https://jekyllrb.com/docs/plugins/) to generate content specific to your site
55
+ ### Sponsors
56
+
57
+ Support this project by becoming a sponsor. Your logo will show up in this README with a link to your website. [Become a sponsor!](https://opencollective.com/jekyll#sponsor)
58
+ [![Jekyll Sponsor 0](https://opencollective.com/jekyll/sponsor/0/avatar.svg)](https://opencollective.com/jekyll/sponsor/0/website)
59
+ [![Jekyll Sponsor 1](https://opencollective.com/jekyll/sponsor/1/avatar.svg)](https://opencollective.com/jekyll/sponsor/1/website)
60
+ [![Jekyll Sponsor 2](https://opencollective.com/jekyll/sponsor/2/avatar.svg)](https://opencollective.com/jekyll/sponsor/2/website)
61
+ [![Jekyll Sponsor 3](https://opencollective.com/jekyll/sponsor/3/avatar.svg)](https://opencollective.com/jekyll/sponsor/3/website)
62
+ [![Jekyll Sponsor 4](https://opencollective.com/jekyll/sponsor/4/avatar.svg)](https://opencollective.com/jekyll/sponsor/4/website)
63
+ [![Jekyll Sponsor 5](https://opencollective.com/jekyll/sponsor/5/avatar.svg)](https://opencollective.com/jekyll/sponsor/5/website)
64
+ [![Jekyll Sponsor 6](https://opencollective.com/jekyll/sponsor/6/avatar.svg)](https://opencollective.com/jekyll/sponsor/6/website)
65
+ [![Jekyll Sponsor 7](https://opencollective.com/jekyll/sponsor/7/avatar.svg)](https://opencollective.com/jekyll/sponsor/7/website)
66
+ [![Jekyll Sponsor 8](https://opencollective.com/jekyll/sponsor/8/avatar.svg)](https://opencollective.com/jekyll/sponsor/8/website)
67
+ [![Jekyll Sponsor 9](https://opencollective.com/jekyll/sponsor/9/avatar.svg)](https://opencollective.com/jekyll/sponsor/9/website)
68
+
69
+ ### Contributors
70
+
71
+ This project exists thanks to all the people who contribute.
72
+ [![Jekyll Contributors](https://opencollective.com/jekyll/contributors.svg?width=890&button=false)](../../graphs/contributors)
73
+
74
+ ### Backers
75
+
76
+ Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/jekyll#backer)
77
+
78
+ [![Jekyll Backers](https://opencollective.com/jekyll/backers.svg?width=890)](https://opencollective.com/jekyll#backers)
58
79
 
59
80
  ## License
60
81
 
@@ -0,0 +1,3 @@
1
+ url: "" # the base hostname & protocol for your site, e.g. http://example.com
2
+ baseurl: "" # the subpath of your site, e.g. /blog
3
+ title: "" # the name of your site, e.g. ACME Corp.
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="{{ site.lang | default: "en-US" }}">
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta charset="utf-8">
6
+ <title>{{ page.title }} - {{ site.title }}</title>
7
+ <link rel="stylesheet" href="{{ "/assets/css/main.css" | relative_url }}">
8
+ </head>
9
+ <body>
10
+ {{ content }}
11
+ </body>
12
+ </html>
@@ -0,0 +1,9 @@
1
+ $backgroundColor: #ffffff;
2
+ $bodyColor: #000000;
3
+ $bodyFont: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
4
+
5
+ body {
6
+ background: $backgroundColor;
7
+ color: $bodyColor;
8
+ font-family: $bodyFont;
9
+ }
@@ -0,0 +1,4 @@
1
+ ---
2
+ ---
3
+
4
+ @import "base";
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: "Happy Jekylling!"
4
+ ---
5
+
6
+ ## You're ready to go!
7
+
8
+ Start developing your Jekyll website.
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Jekyll
6
+ class Cache
7
+ # class-wide base cache
8
+ @base_cache = {}
9
+
10
+ # class-wide directive to write cache to disk is enabled by default
11
+ @disk_cache_enabled = true
12
+
13
+ class << self
14
+ attr_accessor :cache_dir # class-wide cache location
15
+
16
+ attr_reader :base_cache, # class-wide base cache reader
17
+ :disk_cache_enabled # class-wide directive to write cache to disk
18
+
19
+ # Disable Marshaling cached items to disk
20
+ def disable_disk_cache!
21
+ @disk_cache_enabled = false
22
+ end
23
+
24
+ # Clear all caches
25
+ def clear
26
+ delete_cache_files
27
+ base_cache.each_value(&:clear)
28
+ end
29
+
30
+ # Compare the current config to the cached config
31
+ # If they are different, clear all caches
32
+ #
33
+ # Returns nothing.
34
+ def clear_if_config_changed(config)
35
+ config = config.inspect
36
+ cache = Jekyll::Cache.new "Jekyll::Cache"
37
+ return if cache.key?("config") && cache["config"] == config
38
+
39
+ clear
40
+ cache = Jekyll::Cache.new "Jekyll::Cache"
41
+ cache["config"] = config
42
+ nil
43
+ end
44
+
45
+ private
46
+
47
+ # Delete all cached items from all caches
48
+ #
49
+ # Returns nothing.
50
+ def delete_cache_files
51
+ FileUtils.rm_rf(@cache_dir) if disk_cache_enabled
52
+ end
53
+ end
54
+
55
+ #
56
+
57
+ # Get an existing named cache, or create a new one if none exists
58
+ #
59
+ # name - name of the cache
60
+ #
61
+ # Returns nothing.
62
+ def initialize(name)
63
+ @cache = Jekyll::Cache.base_cache[name] ||= {}
64
+ @name = name.gsub(%r![^\w\s-]!, "-")
65
+ end
66
+
67
+ # Clear this particular cache
68
+ def clear
69
+ delete_cache_files
70
+ @cache.clear
71
+ end
72
+
73
+ # Retrieve a cached item
74
+ # Raises if key does not exist in cache
75
+ #
76
+ # Returns cached value
77
+ def [](key)
78
+ return @cache[key] if @cache.key?(key)
79
+
80
+ path = path_to(hash(key))
81
+ if disk_cache_enabled? && File.file?(path) && File.readable?(path)
82
+ @cache[key] = load(path)
83
+ else
84
+ raise
85
+ end
86
+ end
87
+
88
+ # Add an item to cache
89
+ #
90
+ # Returns nothing.
91
+ def []=(key, value)
92
+ @cache[key] = value
93
+ return unless disk_cache_enabled?
94
+
95
+ path = path_to(hash(key))
96
+ value = new Hash(value) if value.is_a?(Hash) && !value.default.nil?
97
+ dump(path, value)
98
+ rescue TypeError
99
+ Jekyll.logger.debug "Cache:", "Cannot dump object #{key}"
100
+ end
101
+
102
+ # If an item already exists in the cache, retrieve it.
103
+ # Else execute code block, and add the result to the cache, and return that result.
104
+ def getset(key)
105
+ self[key]
106
+ rescue StandardError
107
+ value = yield
108
+ self[key] = value
109
+ value
110
+ end
111
+
112
+ # Remove one particular item from the cache
113
+ #
114
+ # Returns nothing.
115
+ def delete(key)
116
+ @cache.delete(key)
117
+ File.delete(path_to(hash(key))) if disk_cache_enabled?
118
+ end
119
+
120
+ # Check if `key` already exists in this cache
121
+ #
122
+ # Returns true if key exists in the cache, false otherwise
123
+ def key?(key)
124
+ # First, check if item is already cached in memory
125
+ return true if @cache.key?(key)
126
+ # Otherwise, it might be cached on disk
127
+ # but we should not consider the disk cache if it is disabled
128
+ return false unless disk_cache_enabled?
129
+
130
+ path = path_to(hash(key))
131
+ File.file?(path) && File.readable?(path)
132
+ end
133
+
134
+ def disk_cache_enabled?
135
+ !!Jekyll::Cache.disk_cache_enabled
136
+ end
137
+
138
+ private
139
+
140
+ # Given a hashed key, return the path to where this item would be saved on disk.
141
+ def path_to(hash = nil)
142
+ @base_dir ||= File.join(Jekyll::Cache.cache_dir, @name)
143
+ return @base_dir if hash.nil?
144
+
145
+ File.join(@base_dir, hash[0..1], hash[2..-1]).freeze
146
+ end
147
+
148
+ # Given a key, return a SHA2 hash that can be used for caching this item to disk.
149
+ def hash(key)
150
+ Digest::SHA2.hexdigest(key).freeze
151
+ end
152
+
153
+ # Remove all this caches items from disk
154
+ #
155
+ # Returns nothing.
156
+ def delete_cache_files
157
+ FileUtils.rm_rf(path_to) if disk_cache_enabled?
158
+ end
159
+
160
+ # Load `path` from disk and return the result.
161
+ # This MUST NEVER be called in Safe Mode
162
+ # rubocop:disable Security/MarshalLoad
163
+ def load(path)
164
+ raise unless disk_cache_enabled?
165
+
166
+ cached_file = File.open(path, "rb")
167
+ value = Marshal.load(cached_file)
168
+ cached_file.close
169
+ value
170
+ end
171
+ # rubocop:enable Security/MarshalLoad
172
+
173
+ # Given a path and a value, save value to disk at path.
174
+ # This should NEVER be called in Safe Mode
175
+ #
176
+ # Returns nothing.
177
+ def dump(path, value)
178
+ return unless disk_cache_enabled?
179
+
180
+ FileUtils.mkdir_p(File.dirname(path))
181
+ File.open(path, "wb") do |cached_file|
182
+ Marshal.dump(value, cached_file)
183
+ end
184
+ end
185
+ end
186
+ end
@@ -3,7 +3,7 @@
3
3
  module Jekyll
4
4
  # Handles the cleanup of a site's destination before it is built.
5
5
  class Cleaner
6
- HIDDEN_FILE_REGEX = %r!\/\.{1,2}$!
6
+ HIDDEN_FILE_REGEX = %r!/\.{1,2}$!.freeze
7
7
  attr_reader :site
8
8
 
9
9
  def initialize(site)
@@ -29,7 +29,7 @@ module Jekyll
29
29
 
30
30
  # Private: The metadata file storing dependency tree and build history
31
31
  #
32
- # Returns an Array with the metdata file as the only item
32
+ # Returns an Array with the metadata file as the only item
33
33
  def metadata_file
34
34
  [site.regenerator.metadata_file]
35
35
  end
@@ -44,7 +44,8 @@ module Jekyll
44
44
  dirs = keep_dirs
45
45
 
46
46
  Utils.safe_glob(site.in_dest_dir, ["**", "*"], File::FNM_DOTMATCH).each do |file|
47
- next if file =~ HIDDEN_FILE_REGEX || file =~ regex || dirs.include?(file)
47
+ next if HIDDEN_FILE_REGEX.match?(file) || regex.match?(file) || dirs.include?(file)
48
+
48
49
  files << file
49
50
  end
50
51
 
@@ -65,7 +66,7 @@ module Jekyll
65
66
  #
66
67
  # Returns a Set with the directory paths
67
68
  def new_dirs
68
- @new_dirs ||= new_files.map { |file| parent_dirs(file) }.flatten.to_set
69
+ @new_dirs ||= new_files.flat_map { |file| parent_dirs(file) }.to_set
69
70
  end
70
71
 
71
72
  # Private: The list of parent directories of a given file
@@ -76,7 +77,7 @@ module Jekyll
76
77
  if parent_dir == site.dest
77
78
  []
78
79
  else
79
- [parent_dir] + parent_dirs(parent_dir)
80
+ parent_dirs(parent_dir).unshift(parent_dir)
80
81
  end
81
82
  end
82
83
 
@@ -93,7 +94,7 @@ module Jekyll
93
94
  #
94
95
  # Returns a Set with the directory paths
95
96
  def keep_dirs
96
- site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
97
+ site.keep_files.flat_map { |file| parent_dirs(site.in_dest_dir(file)) }.to_set
97
98
  end
98
99
 
99
100
  # Private: Creates a regular expression from the config's keep_files array
@@ -104,7 +105,7 @@ module Jekyll
104
105
  #
105
106
  # Returns the regular expression
106
107
  def keep_file_regex
107
- %r!\A#{Regexp.quote(site.dest)}\/(#{Regexp.union(site.keep_files).source})!
108
+ %r!\A#{Regexp.quote(site.dest)}/(#{Regexp.union(site.keep_files).source})!
108
109
  end
109
110
  end
110
111
  end
@@ -35,7 +35,7 @@ module Jekyll
35
35
  def method_missing(method, *args, &blck)
36
36
  if docs.respond_to?(method.to_sym)
37
37
  Jekyll.logger.warn "Deprecation:",
38
- "#{label}.#{method} should be changed to #{label}.docs.#{method}."
38
+ "#{label}.#{method} should be changed to #{label}.docs.#{method}."
39
39
  Jekyll.logger.warn "", "Called by #{caller(0..0)}."
40
40
  docs.public_send(method.to_sym, *args, &blck)
41
41
  else
@@ -58,13 +58,15 @@ module Jekyll
58
58
  filtered_entries.each do |file_path|
59
59
  full_path = collection_dir(file_path)
60
60
  next if File.directory?(full_path)
61
+
61
62
  if Utils.has_yaml_header? full_path
62
63
  read_document(full_path)
63
64
  else
64
65
  read_static_file(file_path, full_path)
65
66
  end
66
67
  end
67
- docs.sort!
68
+ site.static_files.concat(files) unless files.empty?
69
+ sort_docs!
68
70
  end
69
71
 
70
72
  # All the entries in this collection.
@@ -73,11 +75,14 @@ module Jekyll
73
75
  # relative to the collection's directory
74
76
  def entries
75
77
  return [] unless exists?
76
- @entries ||=
78
+
79
+ @entries ||= begin
80
+ collection_dir_slash = "#{collection_dir}/"
77
81
  Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry|
78
- entry["#{collection_dir}/"] = ""
82
+ entry[collection_dir_slash] = ""
79
83
  entry
80
84
  end
85
+ end
81
86
  end
82
87
 
83
88
  # Filtered version of the entries in this collection.
@@ -86,6 +91,7 @@ module Jekyll
86
91
  # Returns a list of filtered entry paths.
87
92
  def filtered_entries
88
93
  return [] unless exists?
94
+
89
95
  @filtered_entries ||=
90
96
  Dir.chdir(directory) do
91
97
  entry_filter.filter(entries).reject do |f|
@@ -124,6 +130,7 @@ module Jekyll
124
130
  # is stored on the filesystem.
125
131
  def collection_dir(*files)
126
132
  return directory if files.empty?
133
+
127
134
  site.in_source_dir(container, relative_directory, *files)
128
135
  end
129
136
 
@@ -149,7 +156,7 @@ module Jekyll
149
156
  #
150
157
  # Returns the inspect string
151
158
  def inspect
152
- "#<Jekyll::Collection @label=#{label} docs=#{docs}>"
159
+ "#<#{self.class} @label=#{label} docs=#{docs}>"
153
160
  end
154
161
 
155
162
  # Produce a sanitized label name
@@ -160,7 +167,7 @@ module Jekyll
160
167
  #
161
168
  # Returns a sanitized version of the label.
162
169
  def sanitize_label(label)
163
- label.gsub(%r![^a-z0-9_\-\.]!i, "")
170
+ label.gsub(%r![^a-z0-9_\-.]!i, "")
164
171
  end
165
172
 
166
173
  # Produce a representation of this Collection for use in Liquid.
@@ -207,17 +214,83 @@ module Jekyll
207
214
  @container ||= site.config["collections_dir"]
208
215
  end
209
216
 
210
- private
211
-
212
217
  def read_document(full_path)
213
218
  doc = Document.new(full_path, :site => site, :collection => self)
214
219
  doc.read
215
- if site.unpublished || doc.published?
216
- docs << doc
220
+ docs << doc if site.unpublished || doc.published?
221
+ end
222
+
223
+ def sort_docs!
224
+ if metadata["order"].is_a?(Array)
225
+ rearrange_docs!
226
+ elsif metadata["sort_by"].is_a?(String)
227
+ sort_docs_by_key!
228
+ else
229
+ docs.sort!
217
230
  end
218
231
  end
219
232
 
220
- private
233
+ # A custom sort function based on Schwartzian transform
234
+ # Refer https://byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details
235
+ def sort_docs_by_key!
236
+ meta_key = metadata["sort_by"]
237
+ # Modify `docs` array to cache document's property along with the Document instance
238
+ docs.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives|
239
+ order = determine_sort_order(meta_key, apples, olives)
240
+
241
+ # Fall back to `Document#<=>` if the properties were equal or were non-sortable
242
+ # Otherwise continue with current sort-order
243
+ if order.nil? || order.zero?
244
+ apples[-1] <=> olives[-1]
245
+ else
246
+ order
247
+ end
248
+
249
+ # Finally restore the `docs` array with just the Document objects themselves
250
+ end.map!(&:last)
251
+ end
252
+
253
+ def determine_sort_order(sort_key, apples, olives)
254
+ apple_property, apple_document = apples
255
+ olive_property, olive_document = olives
256
+
257
+ if apple_property.nil? && !olive_property.nil?
258
+ order_with_warning(sort_key, apple_document, 1)
259
+ elsif !apple_property.nil? && olive_property.nil?
260
+ order_with_warning(sort_key, olive_document, -1)
261
+ else
262
+ apple_property <=> olive_property
263
+ end
264
+ end
265
+
266
+ def order_with_warning(sort_key, document, order)
267
+ Jekyll.logger.warn "Sort warning:", "'#{sort_key}' not defined in #{document.relative_path}"
268
+ order
269
+ end
270
+
271
+ # Rearrange documents within the `docs` array as listed in the `metadata["order"]` array.
272
+ #
273
+ # Involves converting the two arrays into hashes based on relative_paths as keys first, then
274
+ # merging them to remove duplicates and finally retrieving the Document instances from the
275
+ # merged array.
276
+ def rearrange_docs!
277
+ docs_table = {}
278
+ custom_order = {}
279
+
280
+ # pre-sort to normalize default array across platforms and then proceed to create a Hash
281
+ # from that sorted array.
282
+ docs.sort.each do |doc|
283
+ docs_table[doc.relative_path] = doc
284
+ end
285
+
286
+ metadata["order"].each do |entry|
287
+ custom_order[File.join(relative_directory, entry)] = nil
288
+ end
289
+
290
+ result = Jekyll::Utils.deep_merge_hashes(custom_order, docs_table).values
291
+ result.compact!
292
+ self.docs = result
293
+ end
221
294
 
222
295
  def read_static_file(file_path, full_path)
223
296
  relative_dir = Jekyll.sanitized_path(
@@ -40,6 +40,7 @@ module Jekyll
40
40
  # Returns a full Jekyll configuration
41
41
  def configuration_from_options(options)
42
42
  return options if options.is_a?(Jekyll::Configuration)
43
+
43
44
  Jekyll.configuration(options)
44
45
  end
45
46
 
@@ -51,28 +52,54 @@ module Jekyll
51
52
  # rubocop:disable Metrics/MethodLength
52
53
  def add_build_options(cmd)
53
54
  cmd.option "config", "--config CONFIG_FILE[,CONFIG_FILE2,...]",
54
- Array, "Custom configuration file"
55
+ Array, "Custom configuration file"
55
56
  cmd.option "destination", "-d", "--destination DESTINATION",
56
- "The current folder will be generated into DESTINATION"
57
+ "The current folder will be generated into DESTINATION"
57
58
  cmd.option "source", "-s", "--source SOURCE", "Custom source directory"
58
59
  cmd.option "future", "--future", "Publishes posts with a future date"
59
60
  cmd.option "limit_posts", "--limit_posts MAX_POSTS", Integer,
60
- "Limits the number of posts to parse and publish"
61
+ "Limits the number of posts to parse and publish"
61
62
  cmd.option "watch", "-w", "--[no-]watch", "Watch for changes and rebuild"
62
63
  cmd.option "baseurl", "-b", "--baseurl URL",
63
- "Serve the website from the given base URL"
64
+ "Serve the website from the given base URL"
64
65
  cmd.option "force_polling", "--force_polling", "Force watch to use polling"
65
66
  cmd.option "lsi", "--lsi", "Use LSI for improved related posts"
66
67
  cmd.option "show_drafts", "-D", "--drafts", "Render posts in the _drafts folder"
67
68
  cmd.option "unpublished", "--unpublished",
68
- "Render posts that were marked as unpublished"
69
+ "Render posts that were marked as unpublished"
70
+ cmd.option "disable_disk_cache", "--disable-disk-cache",
71
+ "Disable caching to disk in non-safe mode"
69
72
  cmd.option "quiet", "-q", "--quiet", "Silence output."
70
73
  cmd.option "verbose", "-V", "--verbose", "Print verbose output."
71
74
  cmd.option "incremental", "-I", "--incremental", "Enable incremental rebuild."
72
75
  cmd.option "strict_front_matter", "--strict_front_matter",
73
- "Fail if errors are present in front matter"
76
+ "Fail if errors are present in front matter"
74
77
  end
75
78
  # rubocop:enable Metrics/MethodLength
79
+
80
+ # Run ::process method in a given set of Jekyll::Command subclasses and suggest
81
+ # re-running the associated command with --trace switch to obtain any additional
82
+ # information or backtrace regarding the encountered Exception.
83
+ #
84
+ # cmd - the Jekyll::Command to be handled
85
+ # options - configuration overrides
86
+ # klass - an array of Jekyll::Command subclasses associated with the command
87
+ #
88
+ # Note that all exceptions are rescued..
89
+ # rubocop: disable Lint/RescueException
90
+ def process_with_graceful_fail(cmd, options, *klass)
91
+ klass.each { |k| k.process(options) if k.respond_to?(:process) }
92
+ rescue Exception => e
93
+ raise e if cmd.trace
94
+
95
+ msg = " Please append `--trace` to the `#{cmd.name}` command "
96
+ dashes = "-" * msg.length
97
+ Jekyll.logger.error "", dashes
98
+ Jekyll.logger.error "Jekyll #{Jekyll::VERSION} ", msg
99
+ Jekyll.logger.error "", " for any additional information or backtrace. "
100
+ Jekyll.logger.abort_with "", dashes
101
+ end
102
+ # rubocop: enable Lint/RescueException
76
103
  end
77
104
  end
78
105
  end