jekyll 4.0.1 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +350 -163
  3. data/LICENSE +21 -21
  4. data/README.markdown +86 -90
  5. data/exe/jekyll +57 -57
  6. data/lib/blank_template/_config.yml +3 -3
  7. data/lib/blank_template/_layouts/default.html +12 -12
  8. data/lib/blank_template/_sass/main.scss +9 -9
  9. data/lib/blank_template/assets/css/main.scss +4 -4
  10. data/lib/blank_template/index.md +8 -8
  11. data/lib/jekyll/cache.rb +190 -190
  12. data/lib/jekyll/cleaner.rb +111 -111
  13. data/lib/jekyll/collection.rb +309 -309
  14. data/lib/jekyll/command.rb +105 -103
  15. data/lib/jekyll/commands/build.rb +93 -93
  16. data/lib/jekyll/commands/clean.rb +45 -45
  17. data/lib/jekyll/commands/doctor.rb +177 -173
  18. data/lib/jekyll/commands/help.rb +34 -34
  19. data/lib/jekyll/commands/new.rb +169 -169
  20. data/lib/jekyll/commands/new_theme.rb +40 -42
  21. data/lib/jekyll/commands/serve/live_reload_reactor.rb +122 -122
  22. data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
  23. data/lib/jekyll/commands/serve/servlet.rb +202 -202
  24. data/lib/jekyll/commands/serve/websockets.rb +81 -81
  25. data/lib/jekyll/commands/serve.rb +362 -354
  26. data/lib/jekyll/configuration.rb +313 -316
  27. data/lib/jekyll/converter.rb +54 -54
  28. data/lib/jekyll/converters/identity.rb +41 -41
  29. data/lib/jekyll/converters/markdown/kramdown_parser.rb +199 -130
  30. data/lib/jekyll/converters/markdown.rb +113 -113
  31. data/lib/jekyll/converters/smartypants.rb +70 -70
  32. data/lib/jekyll/convertible.rb +257 -254
  33. data/lib/jekyll/deprecator.rb +50 -50
  34. data/lib/jekyll/document.rb +544 -522
  35. data/lib/jekyll/drops/collection_drop.rb +20 -20
  36. data/lib/jekyll/drops/document_drop.rb +70 -69
  37. data/lib/jekyll/drops/drop.rb +293 -215
  38. data/lib/jekyll/drops/excerpt_drop.rb +19 -19
  39. data/lib/jekyll/drops/jekyll_drop.rb +32 -32
  40. data/lib/jekyll/drops/site_drop.rb +66 -66
  41. data/lib/jekyll/drops/static_file_drop.rb +14 -14
  42. data/lib/jekyll/drops/unified_payload_drop.rb +26 -26
  43. data/lib/jekyll/drops/url_drop.rb +140 -132
  44. data/lib/jekyll/entry_filter.rb +121 -110
  45. data/lib/jekyll/errors.rb +20 -20
  46. data/lib/jekyll/excerpt.rb +201 -201
  47. data/lib/jekyll/external.rb +79 -79
  48. data/lib/jekyll/filters/date_filters.rb +110 -110
  49. data/lib/jekyll/filters/grouping_filters.rb +64 -64
  50. data/lib/jekyll/filters/url_filters.rb +98 -68
  51. data/lib/jekyll/filters.rb +535 -454
  52. data/lib/jekyll/frontmatter_defaults.rb +240 -245
  53. data/lib/jekyll/generator.rb +5 -5
  54. data/lib/jekyll/hooks.rb +107 -106
  55. data/lib/jekyll/inclusion.rb +32 -0
  56. data/lib/jekyll/layout.rb +67 -62
  57. data/lib/jekyll/liquid_extensions.rb +22 -22
  58. data/lib/jekyll/liquid_renderer/file.rb +77 -77
  59. data/lib/jekyll/liquid_renderer/table.rb +55 -75
  60. data/lib/jekyll/liquid_renderer.rb +80 -77
  61. data/lib/jekyll/log_adapter.rb +151 -151
  62. data/lib/jekyll/mime.types +866 -866
  63. data/lib/jekyll/page.rb +217 -186
  64. data/lib/jekyll/page_excerpt.rb +25 -0
  65. data/lib/jekyll/page_without_a_file.rb +14 -14
  66. data/lib/jekyll/path_manager.rb +74 -31
  67. data/lib/jekyll/plugin.rb +92 -92
  68. data/lib/jekyll/plugin_manager.rb +115 -115
  69. data/lib/jekyll/profiler.rb +58 -0
  70. data/lib/jekyll/publisher.rb +23 -23
  71. data/lib/jekyll/reader.rb +192 -187
  72. data/lib/jekyll/readers/collection_reader.rb +23 -22
  73. data/lib/jekyll/readers/data_reader.rb +79 -75
  74. data/lib/jekyll/readers/layout_reader.rb +62 -61
  75. data/lib/jekyll/readers/page_reader.rb +25 -24
  76. data/lib/jekyll/readers/post_reader.rb +85 -84
  77. data/lib/jekyll/readers/static_file_reader.rb +25 -24
  78. data/lib/jekyll/readers/theme_assets_reader.rb +52 -51
  79. data/lib/jekyll/regenerator.rb +195 -195
  80. data/lib/jekyll/related_posts.rb +52 -52
  81. data/lib/jekyll/renderer.rb +265 -267
  82. data/lib/jekyll/site.rb +551 -527
  83. data/lib/jekyll/static_file.rb +208 -203
  84. data/lib/jekyll/stevenson.rb +60 -60
  85. data/lib/jekyll/tags/highlight.rb +110 -110
  86. data/lib/jekyll/tags/include.rb +275 -221
  87. data/lib/jekyll/tags/link.rb +42 -41
  88. data/lib/jekyll/tags/post_url.rb +106 -107
  89. data/lib/jekyll/theme.rb +86 -80
  90. data/lib/jekyll/theme_builder.rb +121 -121
  91. data/lib/jekyll/url.rb +167 -164
  92. data/lib/jekyll/utils/ansi.rb +57 -57
  93. data/lib/jekyll/utils/exec.rb +26 -26
  94. data/lib/jekyll/utils/internet.rb +37 -37
  95. data/lib/jekyll/utils/platforms.rb +67 -82
  96. data/lib/jekyll/utils/thread_event.rb +31 -31
  97. data/lib/jekyll/utils/win_tz.rb +75 -75
  98. data/lib/jekyll/utils.rb +367 -367
  99. data/lib/jekyll/version.rb +5 -5
  100. data/lib/jekyll.rb +195 -206
  101. data/lib/site_template/.gitignore +5 -5
  102. data/lib/site_template/404.html +25 -25
  103. data/lib/site_template/_config.yml +55 -55
  104. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
  105. data/lib/site_template/about.markdown +18 -18
  106. data/lib/site_template/index.markdown +6 -6
  107. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
  108. data/lib/theme_template/Gemfile +4 -4
  109. data/lib/theme_template/LICENSE.txt.erb +21 -21
  110. data/lib/theme_template/README.md.erb +52 -52
  111. data/lib/theme_template/_layouts/default.html +1 -1
  112. data/lib/theme_template/_layouts/page.html +5 -5
  113. data/lib/theme_template/_layouts/post.html +5 -5
  114. data/lib/theme_template/example/_config.yml.erb +1 -1
  115. data/lib/theme_template/example/_post.md +12 -12
  116. data/lib/theme_template/example/index.html +14 -14
  117. data/lib/theme_template/example/style.scss +7 -7
  118. data/lib/theme_template/gitignore.erb +6 -6
  119. data/lib/theme_template/theme.gemspec.erb +16 -19
  120. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
  121. data/rubocop/jekyll/no_p_allowed.rb +23 -23
  122. data/rubocop/jekyll/no_puts_allowed.rb +23 -23
  123. data/rubocop/jekyll.rb +5 -5
  124. metadata +20 -38
@@ -1,107 +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
- escaped_slug = Regexp.escape(slug)
20
- @name_regex = %r!^_posts/#{path}#{date}-#{escaped_slug}\.[^.]+|
21
- ^#{path}_posts/?#{date}-#{escaped_slug}\.[^.]+!x
22
- end
23
-
24
- def post_date
25
- @post_date ||= Utils.parse_date(
26
- date,
27
- "'#{date}' does not contain valid date and/or title."
28
- )
29
- end
30
-
31
- def ==(other)
32
- other.relative_path.match(@name_regex)
33
- end
34
-
35
- def deprecated_equality(other)
36
- slug == post_slug(other) &&
37
- post_date.year == other.date.year &&
38
- post_date.month == other.date.month &&
39
- post_date.day == other.date.day
40
- end
41
-
42
- private
43
-
44
- # Construct the directory-aware post slug for a Jekyll::Post
45
- #
46
- # other - the Jekyll::Post
47
- #
48
- # Returns the post slug with the subdirectory (relative to _posts)
49
- def post_slug(other)
50
- path = other.basename.split("/")[0...-1].join("/")
51
- if path.nil? || path == ""
52
- other.data["slug"]
53
- else
54
- path + "/" + other.data["slug"]
55
- end
56
- end
57
- end
58
-
59
- class PostUrl < Liquid::Tag
60
- include Jekyll::Filters::URLFilters
61
-
62
- def initialize(tag_name, post, tokens)
63
- super
64
- @orig_post = post.strip
65
- begin
66
- @post = PostComparer.new(@orig_post)
67
- rescue StandardError => e
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
73
- end
74
- end
75
-
76
- def render(context)
77
- @context = context
78
- site = context.registers[:site]
79
-
80
- site.posts.docs.each do |document|
81
- return relative_url(document) if @post == document
82
- end
83
-
84
- # New matching method did not match, fall back to old method
85
- # with deprecation warning if this matches
86
-
87
- site.posts.docs.each do |document|
88
- next unless @post.deprecated_equality document
89
-
90
- Jekyll::Deprecator.deprecation_message "A call to "\
91
- "'{% post_url #{@post.name} %}' did not match " \
92
- "a post using the new matching method of checking name " \
93
- "(path-date-slug) equality. Please make sure that you " \
94
- "change this tag to match the post's name exactly."
95
- return relative_url(document)
96
- end
97
-
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
102
- end
103
- end
104
- end
105
- end
106
-
107
- 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,80 +1,86 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- class Theme
5
- extend Forwardable
6
- attr_reader :name
7
- def_delegator :gemspec, :version, :version
8
-
9
- def initialize(name)
10
- @name = name.downcase.strip
11
- Jekyll.logger.debug "Theme:", name
12
- Jekyll.logger.debug "Theme source:", root
13
- end
14
-
15
- def root
16
- # Must use File.realpath to resolve symlinks created by rbenv
17
- # Otherwise, Jekyll.sanitized path with prepend the unresolved root
18
- @root ||= File.realpath(gemspec.full_gem_path)
19
- rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
20
- raise "Path #{gemspec.full_gem_path} does not exist, is not accessible "\
21
- "or includes a symbolic link loop"
22
- end
23
-
24
- def includes_path
25
- @includes_path ||= path_for "_includes"
26
- end
27
-
28
- def layouts_path
29
- @layouts_path ||= path_for "_layouts"
30
- end
31
-
32
- def sass_path
33
- @sass_path ||= path_for "_sass"
34
- end
35
-
36
- def assets_path
37
- @assets_path ||= path_for "assets"
38
- end
39
-
40
- def runtime_dependencies
41
- gemspec.runtime_dependencies
42
- end
43
-
44
- private
45
-
46
- def path_for(folder)
47
- path = realpath_for(folder)
48
- path if path && File.directory?(path)
49
- end
50
-
51
- def realpath_for(folder)
52
- # This resolves all symlinks for the theme subfolder and then ensures that the directory
53
- # remains inside the theme root. This prevents the use of symlinks for theme subfolders to
54
- # escape the theme root.
55
- # However, symlinks are allowed to point to other directories within the theme.
56
- Jekyll.sanitized_path(root, File.realpath(Jekyll.sanitized_path(root, folder.to_s)))
57
- rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP => e
58
- log_realpath_exception(e, folder)
59
- nil
60
- end
61
-
62
- def log_realpath_exception(err, folder)
63
- return if err.is_a?(Errno::ENOENT)
64
-
65
- case err
66
- when Errno::EACCES
67
- Jekyll.logger.error "Theme error:", "Directory '#{folder}' is not accessible."
68
- when Errno::ELOOP
69
- Jekyll.logger.error "Theme error:", "Directory '#{folder}' includes a symbolic link loop."
70
- end
71
- end
72
-
73
- def gemspec
74
- @gemspec ||= Gem::Specification.find_by_name(name)
75
- rescue Gem::LoadError
76
- raise Jekyll::Errors::MissingDependencyException,
77
- "The #{name} theme could not be found."
78
- end
79
- end
80
- 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
@@ -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