jekyll 3.8.7 → 4.1.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.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +71 -62
  3. data/LICENSE +1 -1
  4. data/README.markdown +46 -17
  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/main.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.rb +10 -1
  11. data/lib/jekyll/cache.rb +190 -0
  12. data/lib/jekyll/cleaner.rb +5 -4
  13. data/lib/jekyll/collection.rb +82 -10
  14. data/lib/jekyll/command.rb +33 -6
  15. data/lib/jekyll/commands/build.rb +11 -20
  16. data/lib/jekyll/commands/clean.rb +2 -0
  17. data/lib/jekyll/commands/doctor.rb +15 -8
  18. data/lib/jekyll/commands/help.rb +1 -1
  19. data/lib/jekyll/commands/new.rb +37 -35
  20. data/lib/jekyll/commands/new_theme.rb +30 -28
  21. data/lib/jekyll/commands/serve.rb +55 -81
  22. data/lib/jekyll/commands/serve/live_reload_reactor.rb +6 -10
  23. data/lib/jekyll/commands/serve/servlet.rb +22 -25
  24. data/lib/jekyll/commands/serve/websockets.rb +1 -1
  25. data/lib/jekyll/configuration.rb +61 -149
  26. data/lib/jekyll/converters/identity.rb +18 -0
  27. data/lib/jekyll/converters/markdown.rb +49 -40
  28. data/lib/jekyll/converters/markdown/kramdown_parser.rb +84 -11
  29. data/lib/jekyll/converters/smartypants.rb +34 -14
  30. data/lib/jekyll/convertible.rb +30 -31
  31. data/lib/jekyll/deprecator.rb +1 -3
  32. data/lib/jekyll/document.rb +89 -61
  33. data/lib/jekyll/drops/collection_drop.rb +2 -3
  34. data/lib/jekyll/drops/document_drop.rb +14 -1
  35. data/lib/jekyll/drops/drop.rb +17 -14
  36. data/lib/jekyll/drops/excerpt_drop.rb +4 -0
  37. data/lib/jekyll/drops/page_drop.rb +18 -0
  38. data/lib/jekyll/drops/site_drop.rb +6 -5
  39. data/lib/jekyll/drops/unified_payload_drop.rb +1 -0
  40. data/lib/jekyll/drops/url_drop.rb +53 -1
  41. data/lib/jekyll/entry_filter.rb +42 -45
  42. data/lib/jekyll/excerpt.rb +45 -34
  43. data/lib/jekyll/external.rb +10 -5
  44. data/lib/jekyll/filters.rb +200 -40
  45. data/lib/jekyll/filters/date_filters.rb +6 -3
  46. data/lib/jekyll/filters/grouping_filters.rb +1 -2
  47. data/lib/jekyll/filters/url_filters.rb +46 -14
  48. data/lib/jekyll/frontmatter_defaults.rb +46 -35
  49. data/lib/jekyll/hooks.rb +4 -8
  50. data/lib/jekyll/inclusion.rb +32 -0
  51. data/lib/jekyll/liquid_extensions.rb +0 -2
  52. data/lib/jekyll/liquid_renderer.rb +31 -16
  53. data/lib/jekyll/liquid_renderer/file.rb +24 -3
  54. data/lib/jekyll/liquid_renderer/table.rb +36 -77
  55. data/lib/jekyll/log_adapter.rb +5 -1
  56. data/lib/jekyll/mime.types +53 -11
  57. data/lib/jekyll/page.rb +54 -12
  58. data/lib/jekyll/page_excerpt.rb +26 -0
  59. data/lib/jekyll/page_without_a_file.rb +0 -4
  60. data/lib/jekyll/path_manager.rb +31 -0
  61. data/lib/jekyll/plugin.rb +5 -11
  62. data/lib/jekyll/plugin_manager.rb +2 -0
  63. data/lib/jekyll/profiler.rb +58 -0
  64. data/lib/jekyll/reader.rb +42 -9
  65. data/lib/jekyll/readers/collection_reader.rb +1 -0
  66. data/lib/jekyll/readers/data_reader.rb +8 -9
  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 +31 -18
  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/renderer.rb +23 -40
  74. data/lib/jekyll/site.rb +91 -38
  75. data/lib/jekyll/static_file.rb +62 -21
  76. data/lib/jekyll/stevenson.rb +2 -3
  77. data/lib/jekyll/tags/highlight.rb +19 -51
  78. data/lib/jekyll/tags/include.rb +82 -42
  79. data/lib/jekyll/tags/link.rb +11 -7
  80. data/lib/jekyll/tags/post_url.rb +25 -21
  81. data/lib/jekyll/theme.rb +16 -18
  82. data/lib/jekyll/theme_builder.rb +91 -89
  83. data/lib/jekyll/url.rb +10 -5
  84. data/lib/jekyll/utils.rb +18 -21
  85. data/lib/jekyll/utils/ansi.rb +1 -1
  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 +8 -8
  89. data/lib/jekyll/utils/thread_event.rb +1 -5
  90. data/lib/jekyll/utils/win_tz.rb +2 -2
  91. data/lib/jekyll/version.rb +1 -1
  92. data/lib/site_template/.gitignore +2 -0
  93. data/lib/site_template/404.html +1 -0
  94. data/lib/site_template/_config.yml +17 -5
  95. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
  96. data/lib/site_template/{about.md → about.markdown} +0 -0
  97. data/lib/site_template/{index.md → index.markdown} +0 -0
  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 +149 -0
  101. metadata +69 -31
  102. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
  103. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
  104. data/lib/jekyll/utils/rouge.rb +0 -22
@@ -3,9 +3,11 @@
3
3
  module Jekyll
4
4
  module Tags
5
5
  class Link < Liquid::Tag
6
+ include Jekyll::Filters::URLFilters
7
+
6
8
  class << self
7
9
  def tag_name
8
- self.name.split("::").last.downcase
10
+ name.split("::").last.downcase
9
11
  end
10
12
  end
11
13
 
@@ -16,19 +18,21 @@ module Jekyll
16
18
  end
17
19
 
18
20
  def render(context)
21
+ @context = context
19
22
  site = context.registers[:site]
23
+ relative_path = Liquid::Template.parse(@relative_path).render(context)
20
24
 
21
25
  site.each_site_file do |item|
22
- return item.url if item.relative_path == @relative_path
26
+ return relative_url(item) if item.relative_path == relative_path
23
27
  # This takes care of the case for static files that have a leading /
24
- return item.url if item.relative_path == "/#{@relative_path}"
28
+ return relative_url(item) if item.relative_path == "/#{relative_path}"
25
29
  end
26
30
 
27
- raise ArgumentError, <<-MSG
28
- Could not find document '#{@relative_path}' in tag '#{self.class.tag_name}'.
31
+ raise ArgumentError, <<~MSG
32
+ Could not find document '#{relative_path}' in tag '#{self.class.tag_name}'.
29
33
 
30
- Make sure the document exists and the path is correct.
31
- MSG
34
+ Make sure the document exists and the path is correct.
35
+ MSG
32
36
  end
33
37
  end
34
38
  end
@@ -3,7 +3,7 @@
3
3
  module Jekyll
4
4
  module Tags
5
5
  class PostComparer
6
- MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!
6
+ MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
7
7
 
8
8
  attr_reader :path, :date, :slug, :name
9
9
 
@@ -13,7 +13,7 @@ module Jekyll
13
13
  all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
14
14
  unless all
15
15
  raise Jekyll::Errors::InvalidPostNameError,
16
- "'#{name}' does not contain valid date and/or title."
16
+ "'#{name}' does not contain valid date and/or title."
17
17
  end
18
18
 
19
19
  escaped_slug = Regexp.escape(slug)
@@ -22,8 +22,10 @@ module Jekyll
22
22
  end
23
23
 
24
24
  def post_date
25
- @post_date ||= Utils.parse_date(date,
26
- "\"#{date}\" does not contain valid date and/or title.")
25
+ @post_date ||= Utils.parse_date(
26
+ date,
27
+ "'#{date}' does not contain valid date and/or title."
28
+ )
27
29
  end
28
30
 
29
31
  def ==(other)
@@ -38,6 +40,7 @@ module Jekyll
38
40
  end
39
41
 
40
42
  private
43
+
41
44
  # Construct the directory-aware post slug for a Jekyll::Post
42
45
  #
43
46
  # other - the Jekyll::Post
@@ -54,47 +57,48 @@ module Jekyll
54
57
  end
55
58
 
56
59
  class PostUrl < Liquid::Tag
60
+ include Jekyll::Filters::URLFilters
61
+
57
62
  def initialize(tag_name, post, tokens)
58
63
  super
59
64
  @orig_post = post.strip
60
65
  begin
61
66
  @post = PostComparer.new(@orig_post)
62
67
  rescue StandardError => e
63
- raise Jekyll::Errors::PostURLError, <<-MSG
64
- Could not parse name of post "#{@orig_post}" in tag 'post_url'.
65
-
66
- Make sure the post exists and the name is correct.
67
-
68
- #{e.class}: #{e.message}
69
- MSG
68
+ raise Jekyll::Errors::PostURLError, <<~MSG
69
+ Could not parse name of post "#{@orig_post}" in tag 'post_url'.
70
+ Make sure the post exists and the name is correct.
71
+ #{e.class}: #{e.message}
72
+ MSG
70
73
  end
71
74
  end
72
75
 
73
76
  def render(context)
77
+ @context = context
74
78
  site = context.registers[:site]
75
79
 
76
- site.posts.docs.each do |p|
77
- return p.url if @post == p
80
+ site.posts.docs.each do |document|
81
+ return relative_url(document) if @post == document
78
82
  end
79
83
 
80
84
  # New matching method did not match, fall back to old method
81
85
  # with deprecation warning if this matches
82
86
 
83
- site.posts.docs.each do |p|
84
- next unless @post.deprecated_equality p
87
+ site.posts.docs.each do |document|
88
+ next unless @post.deprecated_equality document
89
+
85
90
  Jekyll::Deprecator.deprecation_message "A call to "\
86
91
  "'{% post_url #{@post.name} %}' did not match " \
87
92
  "a post using the new matching method of checking name " \
88
93
  "(path-date-slug) equality. Please make sure that you " \
89
94
  "change this tag to match the post's name exactly."
90
- return p.url
95
+ return relative_url(document)
91
96
  end
92
97
 
93
- raise Jekyll::Errors::PostURLError, <<-MSG
94
- Could not find post "#{@orig_post}" in tag 'post_url'.
95
-
96
- Make sure the post exists and the name is correct.
97
- MSG
98
+ raise Jekyll::Errors::PostURLError, <<~MSG
99
+ Could not find post "#{@orig_post}" in tag 'post_url'.
100
+ Make sure the post exists and the name is correct.
101
+ MSG
98
102
  end
99
103
  end
100
104
  end
@@ -3,14 +3,14 @@
3
3
  module Jekyll
4
4
  class Theme
5
5
  extend Forwardable
6
- attr_reader :name
6
+ attr_reader :name
7
+
7
8
  def_delegator :gemspec, :version, :version
8
9
 
9
10
  def initialize(name)
10
11
  @name = name.downcase.strip
11
12
  Jekyll.logger.debug "Theme:", name
12
13
  Jekyll.logger.debug "Theme source:", root
13
- configure_sass
14
14
  end
15
15
 
16
16
  def root
@@ -22,26 +22,25 @@ module Jekyll
22
22
  "or includes a symbolic link loop"
23
23
  end
24
24
 
25
+ # The name of theme directory
26
+ def basename
27
+ @basename ||= File.basename(root)
28
+ end
29
+
25
30
  def includes_path
26
- @includes_path ||= path_for "_includes".freeze
31
+ @includes_path ||= path_for "_includes"
27
32
  end
28
33
 
29
34
  def layouts_path
30
- @layouts_path ||= path_for "_layouts".freeze
35
+ @layouts_path ||= path_for "_layouts"
31
36
  end
32
37
 
33
38
  def sass_path
34
- @sass_path ||= path_for "_sass".freeze
39
+ @sass_path ||= path_for "_sass"
35
40
  end
36
41
 
37
42
  def assets_path
38
- @assets_path ||= path_for "assets".freeze
39
- end
40
-
41
- def configure_sass
42
- return unless sass_path
43
- External.require_with_graceful_fail("sass") unless defined?(Sass)
44
- Sass.load_paths << sass_path
43
+ @assets_path ||= path_for "assets"
45
44
  end
46
45
 
47
46
  def runtime_dependencies
@@ -56,9 +55,9 @@ module Jekyll
56
55
  end
57
56
 
58
57
  def realpath_for(folder)
59
- # This resolves all symlinks for the theme subfolder and then ensures
60
- # that the directory remains inside the theme root. This prevents the
61
- # use of symlinks for theme subfolders to escape the theme root.
58
+ # This resolves all symlinks for the theme subfolder and then ensures that the directory
59
+ # remains inside the theme root. This prevents the use of symlinks for theme subfolders to
60
+ # escape the theme root.
62
61
  # However, symlinks are allowed to point to other directories within the theme.
63
62
  Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
64
63
  rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
@@ -73,8 +72,7 @@ module Jekyll
73
72
  when Errno::EACCES
74
73
  Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
75
74
  when Errno::ELOOP
76
- Jekyll.logger.error "Theme error:",
77
- "Directory '#{folder}' includes a symbolic link loop."
75
+ Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
78
76
  end
79
77
  end
80
78
 
@@ -82,7 +80,7 @@ module Jekyll
82
80
  @gemspec ||= Gem::Specification.find_by_name(name)
83
81
  rescue Gem::LoadError
84
82
  raise Jekyll::Errors::MissingDependencyException,
85
- "The #{name} theme could not be found."
83
+ "The #{name} theme could not be found."
86
84
  end
87
85
  end
88
86
  end
@@ -1,119 +1,121 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class Jekyll::ThemeBuilder
4
- SCAFFOLD_DIRECTORIES = %w(
5
- assets _layouts _includes _sass
6
- ).freeze
7
-
8
- attr_reader :name, :path, :code_of_conduct
3
+ module Jekyll
4
+ class ThemeBuilder
5
+ SCAFFOLD_DIRECTORIES = %w(
6
+ assets _layouts _includes _sass
7
+ ).freeze
8
+
9
+ attr_reader :name, :path, :code_of_conduct
10
+
11
+ def initialize(theme_name, opts)
12
+ @name = theme_name.to_s.tr(" ", "_").squeeze("_")
13
+ @path = Pathname.new(File.expand_path(name, Dir.pwd))
14
+ @code_of_conduct = !!opts["code_of_conduct"]
15
+ end
9
16
 
10
- def initialize(theme_name, opts)
11
- @name = theme_name.to_s.tr(" ", "_").squeeze("_")
12
- @path = Pathname.new(File.expand_path(name, Dir.pwd))
13
- @code_of_conduct = !!opts["code_of_conduct"]
14
- end
17
+ def create!
18
+ create_directories
19
+ create_starter_files
20
+ create_gemspec
21
+ create_accessories
22
+ initialize_git_repo
23
+ end
15
24
 
16
- def create!
17
- create_directories
18
- create_starter_files
19
- create_gemspec
20
- create_accessories
21
- initialize_git_repo
22
- end
25
+ def user_name
26
+ @user_name ||= `git config user.name`.chomp
27
+ end
23
28
 
24
- def user_name
25
- @user_name ||= `git config user.name`.chomp
26
- end
29
+ def user_email
30
+ @user_email ||= `git config user.email`.chomp
31
+ end
27
32
 
28
- def user_email
29
- @user_email ||= `git config user.email`.chomp
30
- end
33
+ private
31
34
 
32
- private
35
+ def root
36
+ @root ||= Pathname.new(File.expand_path("../", __dir__))
37
+ end
33
38
 
34
- def root
35
- @root ||= Pathname.new(File.expand_path("../", __dir__))
36
- end
39
+ def template_file(filename)
40
+ [
41
+ root.join("theme_template", "#{filename}.erb"),
42
+ root.join("theme_template", filename.to_s),
43
+ ].find(&:exist?)
44
+ end
37
45
 
38
- def template_file(filename)
39
- [
40
- root.join("theme_template", "#{filename}.erb"),
41
- root.join("theme_template", filename.to_s),
42
- ].find(&:exist?)
43
- end
46
+ def template(filename)
47
+ erb.render(template_file(filename).read)
48
+ end
44
49
 
45
- def template(filename)
46
- erb.render(template_file(filename).read)
47
- end
50
+ def erb
51
+ @erb ||= ERBRenderer.new(self)
52
+ end
48
53
 
49
- def erb
50
- @erb ||= ERBRenderer.new(self)
51
- end
54
+ def mkdir_p(directories)
55
+ Array(directories).each do |directory|
56
+ full_path = path.join(directory)
57
+ Jekyll.logger.info "create", full_path.to_s
58
+ FileUtils.mkdir_p(full_path)
59
+ end
60
+ end
52
61
 
53
- def mkdir_p(directories)
54
- Array(directories).each do |directory|
55
- full_path = path.join(directory)
62
+ def write_file(filename, contents)
63
+ full_path = path.join(filename)
56
64
  Jekyll.logger.info "create", full_path.to_s
57
- FileUtils.mkdir_p(full_path)
65
+ File.write(full_path, contents)
58
66
  end
59
- end
60
-
61
- def write_file(filename, contents)
62
- full_path = path.join(filename)
63
- Jekyll.logger.info "create", full_path.to_s
64
- File.write(full_path, contents)
65
- end
66
67
 
67
- def create_directories
68
- mkdir_p(SCAFFOLD_DIRECTORIES)
69
- end
68
+ def create_directories
69
+ mkdir_p(SCAFFOLD_DIRECTORIES)
70
+ end
70
71
 
71
- def create_starter_files
72
- %w(page post default).each do |layout|
73
- write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html"))
72
+ def create_starter_files
73
+ %w(page post default).each do |layout|
74
+ write_file("_layouts/#{layout}.html", template("_layouts/#{layout}.html"))
75
+ end
74
76
  end
75
- end
76
77
 
77
- def create_gemspec
78
- write_file("Gemfile", template("Gemfile"))
79
- write_file("#{name}.gemspec", template("theme.gemspec"))
80
- end
78
+ def create_gemspec
79
+ write_file("Gemfile", template("Gemfile"))
80
+ write_file("#{name}.gemspec", template("theme.gemspec"))
81
+ end
81
82
 
82
- def create_accessories
83
- accessories = %w(README.md LICENSE.txt)
84
- accessories << "CODE_OF_CONDUCT.md" if code_of_conduct
85
- accessories.each do |filename|
86
- write_file(filename, template(filename))
83
+ def create_accessories
84
+ accessories = %w(README.md LICENSE.txt)
85
+ accessories << "CODE_OF_CONDUCT.md" if code_of_conduct
86
+ accessories.each do |filename|
87
+ write_file(filename, template(filename))
88
+ end
87
89
  end
88
- end
89
90
 
90
- def initialize_git_repo
91
- Jekyll.logger.info "initialize", path.join(".git").to_s
92
- Dir.chdir(path.to_s) { `git init` }
93
- write_file(".gitignore", template("gitignore"))
94
- end
91
+ def initialize_git_repo
92
+ Jekyll.logger.info "initialize", path.join(".git").to_s
93
+ Dir.chdir(path.to_s) { `git init` }
94
+ write_file(".gitignore", template("gitignore"))
95
+ end
95
96
 
96
- class ERBRenderer
97
- extend Forwardable
97
+ class ERBRenderer
98
+ extend Forwardable
98
99
 
99
- def_delegator :@theme_builder, :name, :theme_name
100
- def_delegator :@theme_builder, :user_name, :user_name
101
- def_delegator :@theme_builder, :user_email, :user_email
100
+ def_delegator :@theme_builder, :name, :theme_name
101
+ def_delegator :@theme_builder, :user_name, :user_name
102
+ def_delegator :@theme_builder, :user_email, :user_email
102
103
 
103
- def initialize(theme_builder)
104
- @theme_builder = theme_builder
105
- end
104
+ def initialize(theme_builder)
105
+ @theme_builder = theme_builder
106
+ end
106
107
 
107
- def jekyll_version_with_minor
108
- Jekyll::VERSION.split(".").take(2).join(".")
109
- end
108
+ def jekyll_version_with_minor
109
+ Jekyll::VERSION.split(".").take(2).join(".")
110
+ end
110
111
 
111
- def theme_directories
112
- SCAFFOLD_DIRECTORIES
113
- end
112
+ def theme_directories
113
+ SCAFFOLD_DIRECTORIES
114
+ end
114
115
 
115
- def render(contents)
116
- ERB.new(contents).result binding
116
+ def render(contents)
117
+ ERB.new(contents).result binding
118
+ end
117
119
  end
118
120
  end
119
121
  end
@@ -68,6 +68,7 @@ module Jekyll
68
68
  def generate_url_from_hash(template)
69
69
  @placeholders.inject(template) do |result, token|
70
70
  break result if result.index(":").nil?
71
+
71
72
  if token.last.nil?
72
73
  # Remove leading "/" to avoid generating urls with `//`
73
74
  result.gsub("/:#{token.first}", "")
@@ -98,8 +99,8 @@ module Jekyll
98
99
  winner = pool.find { |key| @placeholders.key?(key) }
99
100
  if winner.nil?
100
101
  raise NoMethodError,
101
- "The URL template doesn't have #{pool.join(" or ")} keys. "\
102
- "Check your permalink template!"
102
+ "The URL template doesn't have #{pool.join(" or ")} keys. "\
103
+ "Check your permalink template!"
103
104
  end
104
105
 
105
106
  value = @placeholders[winner]
@@ -128,6 +129,8 @@ module Jekyll
128
129
  #
129
130
  # Returns the escaped path.
130
131
  def self.escape_path(path)
132
+ return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path)
133
+
131
134
  # Because URI.escape doesn't escape "?", "[" and "]" by default,
132
135
  # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
133
136
  #
@@ -138,8 +141,7 @@ module Jekyll
138
141
  # pct-encoded = "%" HEXDIG HEXDIG
139
142
  # sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
140
143
  # / "*" / "+" / "," / ";" / "="
141
- path = Addressable::URI.encode(path)
142
- path.encode("utf-8").sub("#", "%23")
144
+ Addressable::URI.encode(path).encode("utf-8").sub("#", "%23")
143
145
  end
144
146
 
145
147
  # Unescapes a URL path segment
@@ -153,7 +155,10 @@ module Jekyll
153
155
  #
154
156
  # Returns the unescaped path.
155
157
  def self.unescape_path(path)
156
- Addressable::URI.unencode(path.encode("utf-8"))
158
+ path = path.encode("utf-8")
159
+ return path unless path.include?("%")
160
+
161
+ Addressable::URI.unencode(path)
157
162
  end
158
163
  end
159
164
  end