jekyll 4.2.1 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +350 -350
- data/LICENSE +21 -21
- data/README.markdown +86 -86
- data/exe/jekyll +57 -57
- data/lib/blank_template/_config.yml +3 -3
- data/lib/blank_template/_layouts/default.html +12 -12
- data/lib/blank_template/_sass/main.scss +9 -9
- data/lib/blank_template/assets/css/main.scss +4 -4
- data/lib/blank_template/index.md +8 -8
- data/lib/jekyll/cache.rb +190 -190
- data/lib/jekyll/cleaner.rb +111 -111
- data/lib/jekyll/collection.rb +309 -309
- data/lib/jekyll/command.rb +105 -105
- data/lib/jekyll/commands/build.rb +93 -93
- data/lib/jekyll/commands/clean.rb +45 -45
- data/lib/jekyll/commands/doctor.rb +177 -177
- data/lib/jekyll/commands/help.rb +34 -34
- data/lib/jekyll/commands/new.rb +172 -169
- data/lib/jekyll/commands/new_theme.rb +40 -40
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +122 -122
- data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
- data/lib/jekyll/commands/serve/servlet.rb +202 -202
- data/lib/jekyll/commands/serve/websockets.rb +81 -81
- data/lib/jekyll/commands/serve.rb +362 -362
- data/lib/jekyll/configuration.rb +313 -313
- data/lib/jekyll/converter.rb +54 -54
- data/lib/jekyll/converters/identity.rb +41 -41
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +199 -199
- data/lib/jekyll/converters/markdown.rb +113 -113
- data/lib/jekyll/converters/smartypants.rb +70 -70
- data/lib/jekyll/convertible.rb +257 -257
- data/lib/jekyll/deprecator.rb +50 -50
- data/lib/jekyll/document.rb +544 -544
- data/lib/jekyll/drops/collection_drop.rb +20 -20
- data/lib/jekyll/drops/document_drop.rb +70 -70
- data/lib/jekyll/drops/drop.rb +293 -293
- data/lib/jekyll/drops/excerpt_drop.rb +19 -19
- data/lib/jekyll/drops/jekyll_drop.rb +32 -32
- data/lib/jekyll/drops/site_drop.rb +66 -66
- data/lib/jekyll/drops/static_file_drop.rb +14 -14
- data/lib/jekyll/drops/unified_payload_drop.rb +26 -26
- data/lib/jekyll/drops/url_drop.rb +140 -140
- data/lib/jekyll/entry_filter.rb +121 -121
- data/lib/jekyll/errors.rb +20 -20
- data/lib/jekyll/excerpt.rb +201 -201
- data/lib/jekyll/external.rb +79 -79
- data/lib/jekyll/filters/date_filters.rb +110 -110
- data/lib/jekyll/filters/grouping_filters.rb +64 -64
- data/lib/jekyll/filters/url_filters.rb +98 -98
- data/lib/jekyll/filters.rb +535 -535
- data/lib/jekyll/frontmatter_defaults.rb +240 -240
- data/lib/jekyll/generator.rb +5 -5
- data/lib/jekyll/hooks.rb +107 -107
- data/lib/jekyll/inclusion.rb +32 -32
- data/lib/jekyll/layout.rb +67 -67
- data/lib/jekyll/liquid_extensions.rb +22 -22
- data/lib/jekyll/liquid_renderer/file.rb +77 -77
- data/lib/jekyll/liquid_renderer/table.rb +55 -55
- data/lib/jekyll/liquid_renderer.rb +80 -80
- data/lib/jekyll/log_adapter.rb +151 -151
- data/lib/jekyll/mime.types +866 -866
- data/lib/jekyll/page.rb +217 -217
- data/lib/jekyll/page_excerpt.rb +25 -25
- data/lib/jekyll/page_without_a_file.rb +14 -14
- data/lib/jekyll/path_manager.rb +74 -74
- data/lib/jekyll/plugin.rb +92 -92
- data/lib/jekyll/plugin_manager.rb +115 -115
- data/lib/jekyll/profiler.rb +58 -58
- data/lib/jekyll/publisher.rb +23 -23
- data/lib/jekyll/reader.rb +192 -192
- data/lib/jekyll/readers/collection_reader.rb +23 -23
- data/lib/jekyll/readers/data_reader.rb +79 -79
- data/lib/jekyll/readers/layout_reader.rb +62 -62
- data/lib/jekyll/readers/page_reader.rb +25 -25
- data/lib/jekyll/readers/post_reader.rb +85 -85
- data/lib/jekyll/readers/static_file_reader.rb +25 -25
- data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
- data/lib/jekyll/regenerator.rb +195 -195
- data/lib/jekyll/related_posts.rb +52 -52
- data/lib/jekyll/renderer.rb +265 -265
- data/lib/jekyll/site.rb +551 -551
- data/lib/jekyll/static_file.rb +208 -208
- data/lib/jekyll/stevenson.rb +60 -60
- data/lib/jekyll/tags/highlight.rb +110 -110
- data/lib/jekyll/tags/include.rb +275 -275
- data/lib/jekyll/tags/link.rb +42 -42
- data/lib/jekyll/tags/post_url.rb +106 -106
- data/lib/jekyll/theme.rb +86 -86
- data/lib/jekyll/theme_builder.rb +121 -121
- data/lib/jekyll/url.rb +167 -167
- data/lib/jekyll/utils/ansi.rb +57 -57
- data/lib/jekyll/utils/exec.rb +26 -26
- data/lib/jekyll/utils/internet.rb +37 -37
- data/lib/jekyll/utils/platforms.rb +67 -67
- data/lib/jekyll/utils/thread_event.rb +31 -31
- data/lib/jekyll/utils/win_tz.rb +75 -75
- data/lib/jekyll/utils.rb +367 -367
- data/lib/jekyll/version.rb +5 -5
- data/lib/jekyll.rb +195 -195
- data/lib/site_template/.gitignore +5 -5
- data/lib/site_template/404.html +25 -25
- data/lib/site_template/_config.yml +55 -55
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
- data/lib/site_template/about.markdown +18 -18
- data/lib/site_template/index.markdown +6 -6
- data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
- data/lib/theme_template/Gemfile +4 -4
- data/lib/theme_template/LICENSE.txt.erb +21 -21
- data/lib/theme_template/README.md.erb +52 -52
- data/lib/theme_template/_layouts/default.html +1 -1
- data/lib/theme_template/_layouts/page.html +5 -5
- data/lib/theme_template/_layouts/post.html +5 -5
- data/lib/theme_template/example/_config.yml.erb +1 -1
- data/lib/theme_template/example/_post.md +12 -12
- data/lib/theme_template/example/index.html +14 -14
- data/lib/theme_template/example/style.scss +7 -7
- data/lib/theme_template/gitignore.erb +6 -6
- data/lib/theme_template/theme.gemspec.erb +16 -16
- data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
- data/rubocop/jekyll/no_p_allowed.rb +23 -23
- data/rubocop/jekyll/no_puts_allowed.rb +23 -23
- data/rubocop/jekyll.rb +5 -5
- metadata +3 -3
data/lib/jekyll/tags/post_url.rb
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jekyll
|
|
4
|
-
module Tags
|
|
5
|
-
class PostComparer
|
|
6
|
-
MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
|
|
7
|
-
|
|
8
|
-
attr_reader :path, :date, :slug, :name
|
|
9
|
-
|
|
10
|
-
def initialize(name)
|
|
11
|
-
@name = name
|
|
12
|
-
|
|
13
|
-
all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
|
|
14
|
-
unless all
|
|
15
|
-
raise Jekyll::Errors::InvalidPostNameError,
|
|
16
|
-
"'#{name}' does not contain valid date and/or title."
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
|
|
20
|
-
@name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}!
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def post_date
|
|
24
|
-
@post_date ||= Utils.parse_date(
|
|
25
|
-
date,
|
|
26
|
-
"'#{date}' does not contain valid date and/or title."
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def ==(other)
|
|
31
|
-
other.relative_path.match(@name_regex)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def deprecated_equality(other)
|
|
35
|
-
slug == post_slug(other) &&
|
|
36
|
-
post_date.year == other.date.year &&
|
|
37
|
-
post_date.month == other.date.month &&
|
|
38
|
-
post_date.day == other.date.day
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
# Construct the directory-aware post slug for a Jekyll::Post
|
|
44
|
-
#
|
|
45
|
-
# other - the Jekyll::Post
|
|
46
|
-
#
|
|
47
|
-
# Returns the post slug with the subdirectory (relative to _posts)
|
|
48
|
-
def post_slug(other)
|
|
49
|
-
path = other.basename.split("/")[0...-1].join("/")
|
|
50
|
-
if path.nil? || path == ""
|
|
51
|
-
other.data["slug"]
|
|
52
|
-
else
|
|
53
|
-
"#{path}/#{other.data["slug"]}"
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
class PostUrl < Liquid::Tag
|
|
59
|
-
include Jekyll::Filters::URLFilters
|
|
60
|
-
|
|
61
|
-
def initialize(tag_name, post, tokens)
|
|
62
|
-
super
|
|
63
|
-
@orig_post = post.strip
|
|
64
|
-
begin
|
|
65
|
-
@post = PostComparer.new(@orig_post)
|
|
66
|
-
rescue StandardError => e
|
|
67
|
-
raise Jekyll::Errors::PostURLError, <<~MSG
|
|
68
|
-
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
|
|
69
|
-
Make sure the post exists and the name is correct.
|
|
70
|
-
#{e.class}: #{e.message}
|
|
71
|
-
MSG
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def render(context)
|
|
76
|
-
@context = context
|
|
77
|
-
site = context.registers[:site]
|
|
78
|
-
|
|
79
|
-
site.posts.docs.each do |document|
|
|
80
|
-
return relative_url(document) if @post == document
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# New matching method did not match, fall back to old method
|
|
84
|
-
# with deprecation warning if this matches
|
|
85
|
-
|
|
86
|
-
site.posts.docs.each do |document|
|
|
87
|
-
next unless @post.deprecated_equality document
|
|
88
|
-
|
|
89
|
-
Jekyll::Deprecator.deprecation_message "A call to "\
|
|
90
|
-
"'{% post_url #{@post.name} %}' did not match " \
|
|
91
|
-
"a post using the new matching method of checking name " \
|
|
92
|
-
"(path-date-slug) equality. Please make sure that you " \
|
|
93
|
-
"change this tag to match the post's name exactly."
|
|
94
|
-
return relative_url(document)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
raise Jekyll::Errors::PostURLError, <<~MSG
|
|
98
|
-
Could not find post "#{@orig_post}" in tag 'post_url'.
|
|
99
|
-
Make sure the post exists and the name is correct.
|
|
100
|
-
MSG
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
Liquid::Template.register_tag("post_url", Jekyll::Tags::PostUrl)
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Tags
|
|
5
|
+
class PostComparer
|
|
6
|
+
MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :path, :date, :slug, :name
|
|
9
|
+
|
|
10
|
+
def initialize(name)
|
|
11
|
+
@name = name
|
|
12
|
+
|
|
13
|
+
all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
|
|
14
|
+
unless all
|
|
15
|
+
raise Jekyll::Errors::InvalidPostNameError,
|
|
16
|
+
"'#{name}' does not contain valid date and/or title."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
|
|
20
|
+
@name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}!
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def post_date
|
|
24
|
+
@post_date ||= Utils.parse_date(
|
|
25
|
+
date,
|
|
26
|
+
"'#{date}' does not contain valid date and/or title."
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ==(other)
|
|
31
|
+
other.relative_path.match(@name_regex)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def deprecated_equality(other)
|
|
35
|
+
slug == post_slug(other) &&
|
|
36
|
+
post_date.year == other.date.year &&
|
|
37
|
+
post_date.month == other.date.month &&
|
|
38
|
+
post_date.day == other.date.day
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Construct the directory-aware post slug for a Jekyll::Post
|
|
44
|
+
#
|
|
45
|
+
# other - the Jekyll::Post
|
|
46
|
+
#
|
|
47
|
+
# Returns the post slug with the subdirectory (relative to _posts)
|
|
48
|
+
def post_slug(other)
|
|
49
|
+
path = other.basename.split("/")[0...-1].join("/")
|
|
50
|
+
if path.nil? || path == ""
|
|
51
|
+
other.data["slug"]
|
|
52
|
+
else
|
|
53
|
+
"#{path}/#{other.data["slug"]}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class PostUrl < Liquid::Tag
|
|
59
|
+
include Jekyll::Filters::URLFilters
|
|
60
|
+
|
|
61
|
+
def initialize(tag_name, post, tokens)
|
|
62
|
+
super
|
|
63
|
+
@orig_post = post.strip
|
|
64
|
+
begin
|
|
65
|
+
@post = PostComparer.new(@orig_post)
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
raise Jekyll::Errors::PostURLError, <<~MSG
|
|
68
|
+
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
|
|
69
|
+
Make sure the post exists and the name is correct.
|
|
70
|
+
#{e.class}: #{e.message}
|
|
71
|
+
MSG
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def render(context)
|
|
76
|
+
@context = context
|
|
77
|
+
site = context.registers[:site]
|
|
78
|
+
|
|
79
|
+
site.posts.docs.each do |document|
|
|
80
|
+
return relative_url(document) if @post == document
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# New matching method did not match, fall back to old method
|
|
84
|
+
# with deprecation warning if this matches
|
|
85
|
+
|
|
86
|
+
site.posts.docs.each do |document|
|
|
87
|
+
next unless @post.deprecated_equality document
|
|
88
|
+
|
|
89
|
+
Jekyll::Deprecator.deprecation_message "A call to "\
|
|
90
|
+
"'{% post_url #{@post.name} %}' did not match " \
|
|
91
|
+
"a post using the new matching method of checking name " \
|
|
92
|
+
"(path-date-slug) equality. Please make sure that you " \
|
|
93
|
+
"change this tag to match the post's name exactly."
|
|
94
|
+
return relative_url(document)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
raise Jekyll::Errors::PostURLError, <<~MSG
|
|
98
|
+
Could not find post "#{@orig_post}" in tag 'post_url'.
|
|
99
|
+
Make sure the post exists and the name is correct.
|
|
100
|
+
MSG
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
Liquid::Template.register_tag("post_url", Jekyll::Tags::PostUrl)
|
data/lib/jekyll/theme.rb
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Jekyll
|
|
4
|
-
class Theme
|
|
5
|
-
extend Forwardable
|
|
6
|
-
attr_reader :name
|
|
7
|
-
|
|
8
|
-
def_delegator :gemspec, :version, :version
|
|
9
|
-
|
|
10
|
-
def initialize(name)
|
|
11
|
-
@name = name.downcase.strip
|
|
12
|
-
Jekyll.logger.debug "Theme:", name
|
|
13
|
-
Jekyll.logger.debug "Theme source:", root
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def root
|
|
17
|
-
# Must use File.realpath to resolve symlinks created by rbenv
|
|
18
|
-
# Otherwise, Jekyll.sanitized path with prepend the unresolved root
|
|
19
|
-
@root ||= File.realpath(gemspec.full_gem_path)
|
|
20
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
|
|
21
|
-
raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
|
|
22
|
-
"or includes a symbolic link loop"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# The name of theme directory
|
|
26
|
-
def basename
|
|
27
|
-
@basename ||= File.basename(root)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def includes_path
|
|
31
|
-
@includes_path ||= path_for "_includes"
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def layouts_path
|
|
35
|
-
@layouts_path ||= path_for "_layouts"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def sass_path
|
|
39
|
-
@sass_path ||= path_for "_sass"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def assets_path
|
|
43
|
-
@assets_path ||= path_for "assets"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def runtime_dependencies
|
|
47
|
-
gemspec.runtime_dependencies
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
private
|
|
51
|
-
|
|
52
|
-
def path_for(folder)
|
|
53
|
-
path = realpath_for(folder)
|
|
54
|
-
path if path && File.directory?(path)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def realpath_for(folder)
|
|
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.
|
|
61
|
-
# However, symlinks are allowed to point to other directories within the theme.
|
|
62
|
-
Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
|
|
63
|
-
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
|
|
64
|
-
log_realpath_exception(e, folder)
|
|
65
|
-
nil
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def log_realpath_exception(err, folder)
|
|
69
|
-
return if err.is_a?(Errno::ENOENT)
|
|
70
|
-
|
|
71
|
-
case err
|
|
72
|
-
when Errno::EACCES
|
|
73
|
-
Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
|
|
74
|
-
when Errno::ELOOP
|
|
75
|
-
Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def gemspec
|
|
80
|
-
@gemspec ||= Gem::Specification.find_by_name(name)
|
|
81
|
-
rescue Gem::LoadError
|
|
82
|
-
raise Jekyll::Errors::MissingDependencyException,
|
|
83
|
-
"The #{name} theme could not be found."
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
class Theme
|
|
5
|
+
extend Forwardable
|
|
6
|
+
attr_reader :name
|
|
7
|
+
|
|
8
|
+
def_delegator :gemspec, :version, :version
|
|
9
|
+
|
|
10
|
+
def initialize(name)
|
|
11
|
+
@name = name.downcase.strip
|
|
12
|
+
Jekyll.logger.debug "Theme:", name
|
|
13
|
+
Jekyll.logger.debug "Theme source:", root
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def root
|
|
17
|
+
# Must use File.realpath to resolve symlinks created by rbenv
|
|
18
|
+
# Otherwise, Jekyll.sanitized path with prepend the unresolved root
|
|
19
|
+
@root ||= File.realpath(gemspec.full_gem_path)
|
|
20
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
|
|
21
|
+
raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
|
|
22
|
+
"or includes a symbolic link loop"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The name of theme directory
|
|
26
|
+
def basename
|
|
27
|
+
@basename ||= File.basename(root)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def includes_path
|
|
31
|
+
@includes_path ||= path_for "_includes"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def layouts_path
|
|
35
|
+
@layouts_path ||= path_for "_layouts"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def sass_path
|
|
39
|
+
@sass_path ||= path_for "_sass"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def assets_path
|
|
43
|
+
@assets_path ||= path_for "assets"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def runtime_dependencies
|
|
47
|
+
gemspec.runtime_dependencies
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def path_for(folder)
|
|
53
|
+
path = realpath_for(folder)
|
|
54
|
+
path if path && File.directory?(path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def realpath_for(folder)
|
|
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.
|
|
61
|
+
# However, symlinks are allowed to point to other directories within the theme.
|
|
62
|
+
Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
|
|
63
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
|
|
64
|
+
log_realpath_exception(e, folder)
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def log_realpath_exception(err, folder)
|
|
69
|
+
return if err.is_a?(Errno::ENOENT)
|
|
70
|
+
|
|
71
|
+
case err
|
|
72
|
+
when Errno::EACCES
|
|
73
|
+
Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
|
|
74
|
+
when Errno::ELOOP
|
|
75
|
+
Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def gemspec
|
|
80
|
+
@gemspec ||= Gem::Specification.find_by_name(name)
|
|
81
|
+
rescue Gem::LoadError
|
|
82
|
+
raise Jekyll::Errors::MissingDependencyException,
|
|
83
|
+
"The #{name} theme could not be found."
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/jekyll/theme_builder.rb
CHANGED
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
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
|
|
16
|
-
|
|
17
|
-
def create!
|
|
18
|
-
create_directories
|
|
19
|
-
create_starter_files
|
|
20
|
-
create_gemspec
|
|
21
|
-
create_accessories
|
|
22
|
-
initialize_git_repo
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def user_name
|
|
26
|
-
@user_name ||= `git config user.name`.chomp
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def user_email
|
|
30
|
-
@user_email ||= `git config user.email`.chomp
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
|
|
35
|
-
def root
|
|
36
|
-
@root ||= Pathname.new(File.expand_path("../", __dir__))
|
|
37
|
-
end
|
|
38
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
def template(filename)
|
|
47
|
-
erb.render(template_file(filename).read)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def erb
|
|
51
|
-
@erb ||= ERBRenderer.new(self)
|
|
52
|
-
end
|
|
53
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
def write_file(filename, contents)
|
|
63
|
-
full_path = path.join(filename)
|
|
64
|
-
Jekyll.logger.info "create", full_path.to_s
|
|
65
|
-
File.write(full_path, contents)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def create_directories
|
|
69
|
-
mkdir_p(SCAFFOLD_DIRECTORIES)
|
|
70
|
-
end
|
|
71
|
-
|
|
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
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def create_gemspec
|
|
79
|
-
write_file("Gemfile", template("Gemfile"))
|
|
80
|
-
write_file("#{name}.gemspec", template("theme.gemspec"))
|
|
81
|
-
end
|
|
82
|
-
|
|
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
|
|
89
|
-
end
|
|
90
|
-
|
|
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
|
|
96
|
-
|
|
97
|
-
class ERBRenderer
|
|
98
|
-
extend Forwardable
|
|
99
|
-
|
|
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
|
|
103
|
-
|
|
104
|
-
def initialize(theme_builder)
|
|
105
|
-
@theme_builder = theme_builder
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def jekyll_version_with_minor
|
|
109
|
-
Jekyll::VERSION.split(".").take(2).join(".")
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def theme_directories
|
|
113
|
-
SCAFFOLD_DIRECTORIES
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def render(contents)
|
|
117
|
-
ERB.new(contents).result binding
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
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
|
|
16
|
+
|
|
17
|
+
def create!
|
|
18
|
+
create_directories
|
|
19
|
+
create_starter_files
|
|
20
|
+
create_gemspec
|
|
21
|
+
create_accessories
|
|
22
|
+
initialize_git_repo
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def user_name
|
|
26
|
+
@user_name ||= `git config user.name`.chomp
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def user_email
|
|
30
|
+
@user_email ||= `git config user.email`.chomp
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def root
|
|
36
|
+
@root ||= Pathname.new(File.expand_path("../", __dir__))
|
|
37
|
+
end
|
|
38
|
+
|
|
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
|
|
45
|
+
|
|
46
|
+
def template(filename)
|
|
47
|
+
erb.render(template_file(filename).read)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def erb
|
|
51
|
+
@erb ||= ERBRenderer.new(self)
|
|
52
|
+
end
|
|
53
|
+
|
|
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
|
|
61
|
+
|
|
62
|
+
def write_file(filename, contents)
|
|
63
|
+
full_path = path.join(filename)
|
|
64
|
+
Jekyll.logger.info "create", full_path.to_s
|
|
65
|
+
File.write(full_path, contents)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def create_directories
|
|
69
|
+
mkdir_p(SCAFFOLD_DIRECTORIES)
|
|
70
|
+
end
|
|
71
|
+
|
|
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
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def create_gemspec
|
|
79
|
+
write_file("Gemfile", template("Gemfile"))
|
|
80
|
+
write_file("#{name}.gemspec", template("theme.gemspec"))
|
|
81
|
+
end
|
|
82
|
+
|
|
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
|
|
89
|
+
end
|
|
90
|
+
|
|
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
|
|
96
|
+
|
|
97
|
+
class ERBRenderer
|
|
98
|
+
extend Forwardable
|
|
99
|
+
|
|
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
|
|
103
|
+
|
|
104
|
+
def initialize(theme_builder)
|
|
105
|
+
@theme_builder = theme_builder
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def jekyll_version_with_minor
|
|
109
|
+
Jekyll::VERSION.split(".").take(2).join(".")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def theme_directories
|
|
113
|
+
SCAFFOLD_DIRECTORIES
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def render(contents)
|
|
117
|
+
ERB.new(contents).result binding
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|