jekyll-favicon 0.2.9 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +22 -0
  3. data/.devcontainer/devcontainer.json +42 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +4 -4
  5. data/.github/workflows/gem-push.yml +3 -3
  6. data/.github/workflows/test.yml +38 -0
  7. data/.gitignore +5 -0
  8. data/.reek.yml +25 -0
  9. data/.vscode/launch.json +16 -0
  10. data/.vscode/settings.json +7 -0
  11. data/.vscode/tasks.json +15 -0
  12. data/CHANGELOG.md +23 -1
  13. data/Gemfile +3 -1
  14. data/README.md +81 -34
  15. data/Rakefile +9 -7
  16. data/bin/console +1 -0
  17. data/bin/debug +22 -0
  18. data/config/jekyll/favicon.yml +115 -0
  19. data/config/jekyll/favicon/static_file.yml +3 -0
  20. data/config/jekyll/favicon/static_file/convertible.yml +41 -0
  21. data/config/jekyll/favicon/static_file/mutable.yml +22 -0
  22. data/config/jekyll/favicon/static_file/referenceable.yml +15 -0
  23. data/config/jekyll/favicon/static_file/sourceable.yml +3 -0
  24. data/config/jekyll/favicon/static_file/taggable.yml +22 -0
  25. data/jekyll-favicon.gemspec +24 -24
  26. data/lib/jekyll-favicon.rb +7 -16
  27. data/lib/jekyll/favicon.rb +19 -16
  28. data/lib/jekyll/favicon/configuration.rb +73 -0
  29. data/lib/jekyll/favicon/configuration/defaults.rb +49 -0
  30. data/lib/jekyll/favicon/generator.rb +10 -84
  31. data/lib/jekyll/favicon/hooks.rb +12 -10
  32. data/lib/jekyll/favicon/static_data_file.rb +17 -0
  33. data/lib/jekyll/favicon/static_file.rb +97 -0
  34. data/lib/jekyll/favicon/static_file/convertible.rb +129 -0
  35. data/lib/jekyll/favicon/static_file/mutable.rb +81 -0
  36. data/lib/jekyll/favicon/static_file/referenceable.rb +22 -0
  37. data/lib/jekyll/favicon/static_file/sourceable.rb +73 -0
  38. data/lib/jekyll/favicon/static_file/taggable.rb +53 -0
  39. data/lib/jekyll/favicon/static_graphic_file.rb +21 -0
  40. data/lib/jekyll/favicon/tag.rb +14 -17
  41. data/lib/jekyll/favicon/utils.rb +48 -0
  42. data/lib/jekyll/favicon/utils/configuration/compact.rb +58 -0
  43. data/lib/jekyll/favicon/utils/configuration/merge.rb +70 -0
  44. data/lib/jekyll/favicon/utils/configuration/patch.rb +49 -0
  45. data/lib/jekyll/favicon/utils/convert.rb +39 -0
  46. data/lib/jekyll/favicon/utils/tag.rb +70 -0
  47. data/lib/jekyll/favicon/version.rb +3 -1
  48. metadata +67 -71
  49. data/.rubocop.yml +0 -5
  50. data/.ruby-version +0 -1
  51. data/.travis.yml +0 -21
  52. data/Gemfile.lock +0 -97
  53. data/lib/browserconfig.rb +0 -54
  54. data/lib/hash.rb +0 -12
  55. data/lib/image.rb +0 -33
  56. data/lib/jekyll/favicon/config/defaults.yml +0 -55
  57. data/lib/jekyll/favicon/icon.rb +0 -73
  58. data/lib/jekyll/favicon/metadata.rb +0 -12
  59. data/lib/jekyll/favicon/templates/chrome.html.erb +0 -6
  60. data/lib/jekyll/favicon/templates/classic.html.erb +0 -8
  61. data/lib/jekyll/favicon/templates/ie.html.erb +0 -4
  62. data/lib/jekyll/favicon/templates/safari.html.erb +0 -8
  63. data/lib/string.rb +0 -14
  64. data/lib/webmanifest.rb +0 -30
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Jekyll
6
+ module Favicon
7
+ ROOT = Pathname.new File.dirname(File.dirname(File.dirname(File.dirname(__dir__))))
8
+
9
+ module Configuration
10
+ # Create configurable for include
11
+ module Defaults
12
+ def self.included(base)
13
+ *modules, class_or_module_name = base_name_to_parts base.name
14
+ method_name = "#{class_or_module_name}_defaults"
15
+ define_defaults base, method_name do
16
+ Defaults.load_defaults(*modules, class_or_module_name)
17
+ end
18
+ end
19
+
20
+ def self.load_defaults(*parts)
21
+ load_file "config", *parts
22
+ end
23
+
24
+ def self.load_file(*parts)
25
+ path = Favicon::ROOT.join(*parts).to_s
26
+ path = "#{path}.yml"
27
+ YAML.load_file path
28
+ end
29
+
30
+ def self.define_defaults(base, method_name, &block)
31
+ base.define_singleton_method("defaults", &block)
32
+ define_method(method_name, &block)
33
+ end
34
+
35
+ def self.base_name_to_parts(name)
36
+ name.split("::").collect do |module_or_class|
37
+ camelcase_to_snakecase module_or_class
38
+ end
39
+ end
40
+
41
+ def self.camelcase_to_snakecase(camelcase)
42
+ camelcase.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
43
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
44
+ .downcase
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -1,91 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll/plugin"
4
+ require "jekyll/generator"
5
+ require "jekyll/favicon"
6
+
1
7
  module Jekyll
2
8
  module Favicon
3
- # Extended generator that creates all the stastic icons and metadata files
9
+ # New generator that creates all the stastic icons and metadata files
4
10
  class Generator < Jekyll::Generator
5
- priority :high
6
-
7
- attr_accessor :template
8
-
9
11
  def generate(site)
10
- @site = site
11
- if File.file? source_path Favicon.config['source']
12
- @template = favicon_tempfile source_path Favicon.config['source']
13
- generate_icons && generate_metadata
14
- else
15
- Jekyll.logger.warn 'Jekyll::Favicon: Missing ' \
16
- "#{Favicon.config['source']}, not generating " \
17
- 'favicons.'
18
- end
19
- end
20
-
21
- def clean
22
- return unless @template
23
- @template.close
24
- @template.unlink
25
- end
26
-
27
- private
28
-
29
- def source_path(path = nil)
30
- File.join(*[@site.source, path].compact)
31
- end
32
-
33
- def favicon_tempfile(source)
34
- tempfile = Tempfile.new(['favicon-template', '.png'])
35
- options = { background: 'none' }
36
- if source.svg?
37
- options[:density] = Favicon.config['svg']['density']
38
- options[:resize] = Favicon.config['svg']['dimensions']
39
- elsif source.png?
40
- options[:resize] = Favicon.config['png']['dimensions']
41
- end
42
- Image.convert source, tempfile.path, options
43
- tempfile
44
- end
45
-
46
- def generate_icons
47
- @site.static_files.push ico_icon
48
- @site.static_files.push(*png_icons)
49
- @site.static_files.push(*svg_icons)
50
- end
51
-
52
- def ico_icon
53
- target = Favicon.config['ico']['target']
54
- Icon.new @site, Favicon.config['source'], @template.path, target
55
- end
56
-
57
- def png_icons
58
- Favicon.config.deep_find('sizes').uniq.collect do |size|
59
- target = File.join Favicon.config['path'], "favicon-#{size}.png"
60
- Icon.new @site, Favicon.config['source'], @template.path, target
61
- end
62
- end
63
-
64
- def svg_icons
65
- return [] unless Favicon.config['source'].svg?
66
- source = Favicon.config['source']
67
- %w[safari-pinned-tab.svg].collect do |name|
68
- target = File.join Favicon.config['path'], name
69
- Icon.new @site, source, source_path(source), target
70
- end
71
- end
72
-
73
- def generate_metadata
74
- @site.pages.push metadata Browserconfig.new,
75
- Favicon.config['ie']['browserconfig']
76
- @site.pages.push metadata Webmanifest.new,
77
- Favicon.config['chrome']['manifest']
78
- end
79
-
80
- def metadata(document, config)
81
- page = Metadata.new @site, @site.source,
82
- File.dirname(config['target']),
83
- File.basename(config['target'])
84
- favicon_path = File.join (@site.baseurl || ''), Favicon.config['path']
85
- document.load source_path(config['source']), config, favicon_path
86
- page.content = document.dump
87
- page.data = { 'layout' => nil }
88
- page
12
+ Favicon.assets(site)
13
+ .select(&:generable?)
14
+ .each { |asset| site.static_files << asset }
89
15
  end
90
16
  end
91
17
  end
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll/hooks"
4
+
1
5
  Jekyll::Hooks.register :site, :after_init do |site|
2
- Jekyll::Favicon.merge site.config['favicon']
3
- favicon_config = Jekyll::Favicon.config
4
- site.config['exclude'] << favicon_config['source']
5
- site.config['exclude'] << favicon_config['chrome']['manifest']['source']
6
- site.config['exclude'] << favicon_config['ie']['browserconfig']['source']
7
- end
6
+ static_files = Jekyll::Favicon.assets(site)
7
+ .uniq(&:path)
8
+ excludes = site.config["exclude"]
9
+ static_files.each do |static_file|
10
+ source = static_file.source_relative_path
11
+ excludes << source and next if static_file.generable?
8
12
 
9
- Jekyll::Hooks.register :site, :post_write do |site|
10
- favicon_generators = site.generators.select do |generator|
11
- generator.is_a? Jekyll::Favicon::Generator
13
+ Jekyll.logger.warn Jekyll::Favicon,
14
+ "Missing #{source}, not generating favicons."
12
15
  end
13
- favicon_generators.each(&:clean)
14
16
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll/favicon/static_file"
4
+ require "jekyll/favicon/static_file/mutable"
5
+
6
+ module Jekyll
7
+ module Favicon
8
+ # StaticFile extension for data exchange formats
9
+ class StaticDataFile < StaticFile
10
+ include StaticFile::Mutable
11
+
12
+ def generable?
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "forwardable"
5
+ require "jekyll/static_file"
6
+ require "jekyll/favicon/static_file/sourceable"
7
+ require "jekyll/favicon/static_file/taggable"
8
+ require "jekyll/favicon/static_file/referenceable"
9
+ require "jekyll/favicon/utils"
10
+ require "jekyll/favicon/configuration"
11
+
12
+ module Jekyll
13
+ module Favicon
14
+ # Class for static files from with spec dictionary
15
+ # Modify source from spec source
16
+ # Enable tags from spec tags
17
+ # Enable refer
18
+ class StaticFile < Jekyll::StaticFile
19
+ include StaticFile::Sourceable
20
+ include StaticFile::Taggable
21
+ include StaticFile::Referenceable
22
+
23
+ attr_reader :spec, :site
24
+
25
+ def initialize(site, spec = {})
26
+ raise StandardError unless spec.include? "name"
27
+
28
+ @spec = spec
29
+ spec_dir, spec_name = File.split spec_relative_path
30
+ super site, site.source, spec_dir, spec_name
31
+ end
32
+
33
+ def generable?
34
+ sourceable?
35
+ end
36
+
37
+ def taggable?
38
+ generable? && super
39
+ end
40
+
41
+ def patch(configuration)
42
+ taggable_patch spec_patch configuration
43
+ end
44
+
45
+ def href
46
+ Pathname.new("/")
47
+ .join(*[site.baseurl, url].compact)
48
+ .to_s
49
+ end
50
+
51
+ private
52
+
53
+ def spec_patch(configuration)
54
+ Utils.patch configuration do |value|
55
+ case value
56
+ when :site_dir then site_dir
57
+ when :background then site_background
58
+ when :href then href
59
+ else value
60
+ end
61
+ end
62
+ end
63
+
64
+ def site_dir
65
+ site_configuration.fetch("dir", ".")
66
+ end
67
+
68
+ def site_background
69
+ site_configuration.fetch("background", "transparent")
70
+ end
71
+
72
+ def site_configuration
73
+ Configuration.merged site
74
+ end
75
+
76
+ def spec_relative_path
77
+ spec_relative_pathname.cleanpath
78
+ end
79
+
80
+ def spec_relative_pathname
81
+ return spec_pathname if spec_pathname.relative?
82
+
83
+ pathname.relative_path_from "/"
84
+ end
85
+
86
+ def spec_pathname
87
+ Pathname.new(site_dir)
88
+ .join(*spec_dir_name)
89
+ end
90
+
91
+ def spec_dir_name
92
+ spec.values_at("dir", "name")
93
+ .compact
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll/favicon/configuration/defaults"
4
+ require "jekyll/favicon/utils"
5
+
6
+ module Jekyll
7
+ module Favicon
8
+ class StaticFile < Jekyll::StaticFile
9
+ # Create static file based on a source file
10
+ module Convertible
11
+ include Configuration::Defaults
12
+
13
+ def convertible?
14
+ convert.any? || convert_allow_empty?
15
+ end
16
+
17
+ def convert
18
+ convert_defaults = convertible_defaults.dig File.extname(path), @extname
19
+ convert_normalized = convert_normalize convert_spec
20
+ convert_consolidated = Utils.merge convert_defaults, convert_normalized
21
+ patch convert_patch(convert_consolidated || {})
22
+ end
23
+
24
+ def convertible_patch(configuration)
25
+ Utils.patch configuration do |value|
26
+ case value
27
+ when :sizes then sizes.join " "
28
+ else value
29
+ end
30
+ end
31
+ end
32
+
33
+ def convert_odd_source?
34
+ img = MiniMagick::Image.open path
35
+ Utils.odd? img.dimensions
36
+ end
37
+
38
+ def convert_size(size, separator = "x")
39
+ return size unless convert_odd_source? || Utils.odd?(size)
40
+ min_dimension = size.split(separator).min
41
+ [min_dimension, min_dimension].join(separator)
42
+ end
43
+
44
+ def sizes
45
+ if (match = Utils.name_to_size(name)) then [match[1]]
46
+ elsif (define = Utils.define_to_size(convert_spec["define"])) then define
47
+ elsif (resize = convert_spec["resize"]) then [resize]
48
+ elsif (size = convert_spec["size"]) then [size]
49
+ end
50
+ end
51
+
52
+ # Jekyll::StaticFile method
53
+ # asks if dest mtime is older than source mtime after original modified?
54
+ def modified?
55
+ super || self.class.mtimes.fetch(href, -1) < mtime
56
+ end
57
+
58
+ # Jekyll::StaticFile method
59
+ # adds dest mtime to list after original write
60
+ def write(dest)
61
+ super(dest) && self.class.mtimes[href] = mtime
62
+ end
63
+
64
+ private
65
+
66
+ # Jekyll::StaticFile method
67
+ # add file creation instead of copying
68
+ def copy_file(dest_path)
69
+ case @extname
70
+ when ".svg" then super(dest_path)
71
+ when ".ico", ".png"
72
+ Utils.convert path, dest_path, convert
73
+ else Jekyll.logger.warn "Jekyll::Favicon: Can't generate " \
74
+ " #{dest_path}. Extension not supported."
75
+ end
76
+ end
77
+
78
+ def convert_allow_empty?
79
+ @extname == ".svg" && @extname == File.extname(path)
80
+ end
81
+
82
+ def convert_spec
83
+ spec.fetch "convert", {}
84
+ end
85
+
86
+ def convertible_keys
87
+ convertible_defaults["defaults"].keys
88
+ end
89
+
90
+ def convert_normalize(options)
91
+ return {} unless options
92
+
93
+ Utils.slice_and_compact options, convertible_keys
94
+ end
95
+
96
+ def convert_patch(options)
97
+ patched_options = convert_patch_options options
98
+ Utils.slice_and_compact patched_options, convertible_keys
99
+ end
100
+
101
+ def convert_patch_options(options)
102
+ %w[size extent].each_with_object(options) do |name, memo|
103
+ method = "convert_patch_option_#{name}".to_sym
104
+ memo[name] = send(method, options[name])
105
+ end
106
+ end
107
+
108
+ def convert_patch_option_size(size)
109
+ case size
110
+ when :auto
111
+ convert_size size if (size = sizes.first)
112
+ else size
113
+ end
114
+ end
115
+
116
+ def convert_patch_option_extent(extent)
117
+ case extent
118
+ when :auto
119
+ if (size = sizes.first)
120
+ width, height = size.split "x"
121
+ size if width != height
122
+ end
123
+ else extent
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rexml/document"
4
+ require "jekyll/favicon/utils"
5
+ require "jekyll/favicon/static_graphic_file"
6
+
7
+ module Jekyll
8
+ module Favicon
9
+ class StaticFile < Jekyll::StaticFile
10
+ # Create static file based on a source file
11
+ module Mutable
12
+ def mutable?
13
+ mutation.any? || super
14
+ end
15
+
16
+ def mutation
17
+ refers = case @extname
18
+ when ".xml"
19
+ mutation_refers.select { |refer| refer.key? "browserconfig" }
20
+ else
21
+ mutation_refers.collect { |refer| refer["webmanifest"] }
22
+ .compact
23
+ end
24
+ patch(Utils.merge(*refers) || {})
25
+ end
26
+
27
+ # overrides Jekyll::StaticFile method
28
+ def mtime
29
+ return super if File.file? path
30
+ end
31
+
32
+ private
33
+
34
+ # overrides Jekyll::StaticFile method
35
+ def copy_file(dest_path)
36
+ # return unless mutable?
37
+ # return super(dest_path) unless mutation.any?
38
+
39
+ File.write dest_path, mutated_content
40
+ end
41
+
42
+ def mutated_content
43
+ case @extname
44
+ when ".json", ".webmanifest", ".manifest" then mutated_content_json
45
+ when ".xml" then mutated_content_xml
46
+ end
47
+ end
48
+
49
+ def mutated_content_json
50
+ mutated = Jekyll::Utils.deep_merge_hashes (mutable || {}), mutation
51
+ JSON.pretty_generate mutated
52
+ end
53
+
54
+ def mutated_content_xml
55
+ mutated = Utils.mutate_element (mutable || REXML::Document.new), mutation
56
+ output = +""
57
+ mutated.write output
58
+ output
59
+ end
60
+
61
+ def mutable
62
+ return unless File.file? path
63
+
64
+ content = File.read path
65
+ case File.extname path
66
+ when ".xml" then REXML::Document.new content
67
+ else JSON.parse content
68
+ end
69
+ end
70
+
71
+ def mutation_refers
72
+ site.static_files
73
+ .select { |static_file| static_file.is_a? StaticFile }
74
+ .select(&:referenceable?)
75
+ .collect(&:refer)
76
+ .flatten
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end