jekyll-imgwh 1.2.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97ed49b87d0bbe492af4ed8522310efabdc9746d7704dda82cbacd7b86aa23de
4
- data.tar.gz: 3cf0726edda511915644e2c8fff32ef114caa347debf38a39ef20b0a59e2caf4
3
+ metadata.gz: 7efeac12ff14b51441a976e3ea7b7d9c286d9429d59aeebfae72ed8666f7313c
4
+ data.tar.gz: 69f883d0d544d950cff948d71aeb5ff5333f11ad2810458b30a171192b9c50f4
5
5
  SHA512:
6
- metadata.gz: 46cd039ca46ee40493efde196c88e47970c85f422c7e061af8403119cb946571770e1a84d0239d20c5aa2aee3f003102e6cc2640e7c1c9b22ebacfbd5001ce56
7
- data.tar.gz: 5c259abe5bf29223445f2b2a59f151076015847b97ef740f4730ffc64ea6374e4e62e431e046bea936f0d0926e3c46d0290d0f7ed5937ec97bd4c6c35e003596
6
+ metadata.gz: 1a0e4b3815bd80f84029a9f3ee4ca5237a9d70f4aa14f930914ddd134298eb1668da03b49e8a643aae8311c4cb4d5d970eea9af4d6ffbceaabf93360c07d2a66
7
+ data.tar.gz: 152ed5a03712c50cd8e412cd5c9892cbeac5317b7605489f0124df1a59d800908d4046b7704f8c035384f668a30988d89ebfe9ed6ceaa18f211d7393a79e9122
data/README.md ADDED
@@ -0,0 +1,208 @@
1
+ [![Gem Version](https://badge.fury.io/rb/jekyll-imgwh.svg)](https://badge.fury.io/rb/jekyll-imgwh)
2
+
3
+ A [Jekyll](https://jekyllrb.com/) plugin to simplify maintenance of the images on the site.
4
+
5
+ It provides a [Liquid tag `imgwh`](#liquid-tag) for `<img>` elements, which ensures image still exists and automatically fills `width` and `height` attributes allowing image to take up space before it loads, to mitigate content layout shifts.
6
+
7
+ It also provides a [Liquid filter `imgwh`](#liquid-filter), which returns image size as an array.
8
+
9
+ # Installation
10
+
11
+ Add preferred variant from the following ones to your site's `Gemfile` and run `bundle install`:
12
+
13
+ ```ruby
14
+ gem "jekyll-imgwh", group: :jekyll_plugins
15
+ gem "jekyll-imgwh", group: :jekyll_plugins, git: "https://github.com/ojuuji/jekyll-imgwh"
16
+ gem "jekyll-imgwh", group: :jekyll_plugins, path: "/local/path/to/jekyll-imgwh"
17
+ ```
18
+
19
+ # Usage
20
+
21
+ ## Liquid Tag
22
+
23
+ This plugin exposes Liquid tag `imgwh` with the following syntax:
24
+
25
+ ```liquid
26
+ {% imgwh <src> [<rest>] %}
27
+ ```
28
+
29
+ i.e. `<src>` is required and `<rest>` is optional. They both can include Liquid markup.
30
+
31
+ After rendering, `<rest>` is added to generated HTML `<img>` element as-is, and `<src>` is used as a value for `src` attribute.
32
+
33
+ Plugin extracts size of the referenced image and automatically sets `width` and `height` attributes in the generated HTML `<img>` element.
34
+
35
+ Extra whitespace around `<src>` and `<rest>` is stripped.
36
+
37
+ Example:
38
+
39
+ ```liquid
40
+ {% imgwh "/assets/{{ site.title | slugify }}.png" alt="{{ site.title }}" %}
41
+ ```
42
+
43
+ with `site.title="My Site"` and image size 200x67 it would generate the following HTML `<img>` element:
44
+
45
+ ```html
46
+ <img src="/assets/my-site.png" width="200" height="67" alt="My Site">
47
+ ```
48
+
49
+ ### Quotes and Whitespace
50
+
51
+ `<src>` can be specified with single quotes, double quotes, or without quotes. This also defines quotation for the generated `src`, `width`, and `height` attributes: they always use the same quotes as `<src>`:
52
+
53
+ ```
54
+ {% imgwh "/foo.png" %} -> <img src="/foo.png" width="123" height="456">
55
+ {% imgwh '/foo.png' %} -> <img src='/foo.png' width='123' height='456'>
56
+ {% imgwh /foo.png %} -> <img src=/foo.png width=123 height=456>
57
+ ```
58
+
59
+ Whitespace can be freely used in single- and double-quoted `<src>`. To use the same quote character in the `<src>` value specify it twice:
60
+
61
+ ```
62
+ {% imgwh "/f{{ 'oo' | append: "".png"" }}" %} -> OK (src="/foo.png")
63
+ {% imgwh "/f{{ 'oo' | append: ".png" }}" %} -> ERROR
64
+ {% imgwh '/f{{ 'oo' | append: ".png" }}' %} -> ERROR
65
+ {% imgwh '/f{{ ''oo'' | append: ".png" }}' %} -> OK (src='/foo.png')
66
+ ```
67
+
68
+ For unquoted `<src>` whitespace is allowed only within Liquid filters (i.e. between `{{` and `}}`):
69
+
70
+ ```
71
+ {% imgwh /f{{ 'oo' | append: ".png" }} %} -> OK (src=/foo.png)
72
+ {% imgwh /My Site.png %} -> ERROR (tries to open "/My" image)
73
+ {% imgwh /{{ site.title }}.png %} -> OK (src=/My Site.png)
74
+ ```
75
+
76
+ Note, in the last example, although plugin did not fire an error, generated `src` attribute is not valid (`<img>` element would use `src=/My`). After rendering Liquid markup in the `<src>` value, plugin does not perform any further normalization for the resulting URI. It is up to the caller to provide correct URI. Plugin only extracts and URL-decodes the path from it.
77
+
78
+ ## Liquid Filter
79
+
80
+ This plugin exposes a Liquid filter `imgwh`, which returns image size as an array.
81
+
82
+ It accepts no extra arguments and follows the same [path resolution](#path-resolution) rules as the tag.
83
+
84
+ For example, if `/assets/images/logo.png` size is 520x348, this template
85
+
86
+ ```liquid
87
+ <pre>
88
+ {{ "/assets/images/logo.png" | imgwh | inspect }}
89
+ {{ "/assets/images/logo.png" | imgwh | first }}
90
+ {{ "/assets/images/logo.png" | imgwh | last }}
91
+ </pre>
92
+ ```
93
+
94
+ would render to
95
+
96
+ ```html
97
+ <pre>
98
+ [520, 348]
99
+ 520
100
+ 348
101
+ </pre>
102
+ ```
103
+
104
+ ## Path Resolution
105
+
106
+ When the given URI contains scheme, plugin raises an error unless this scheme is listed in [`allowed_schemes`](#allowed_schemes) option (which is empty by default). In case of allowed scheme plugin tries to retrieve image size using the given URI as-is.
107
+
108
+ For URIs without scheme plugin uses URL-decoded path from URI to find image on the local filesystem.
109
+
110
+ When the image path is absolute, image is searched relative to the site source directory.
111
+
112
+ When the image path is relative, image is searched relative to the directory of the current page (`page.dir`).
113
+
114
+ When the image is not found, and a theme is used, and the path is absolute, image is also searched relative to the theme root directory.
115
+
116
+ For the local files, like Jekyll itself, plugin does not allow using files outside the source or theme root directories (in fact, plugin uses the path sanitize helpers from Jekyll). It will not be an immediate error in this case (for example, `"/../foo.png"` i.e. one level above the site root), but the image will be searched then in the source directory and the theme root directory no matter how many levels above the site root it is.
117
+
118
+ ## Error Handling
119
+
120
+ In case plugin cannot determine the image size (due to a syntax error, Liquid template error, image being nonexistent, not an image, etc.) it unconditionally raises an error which stops the site generation.
121
+
122
+ # Configuration
123
+
124
+ This plugin uses the following configuration options by default. The configuration file is the same as Jekyll's (which is `_config.yml` unless overridden):
125
+
126
+ ```yml
127
+ jekyll-imgwh:
128
+ allowed_schemes: []
129
+ extra_rest:
130
+ ```
131
+
132
+ These are default options i.e. you do not need to specify any of them unless you want to use different value.
133
+
134
+ ### `allowed_schemes`
135
+
136
+ By default plugin allows only local image files to be used. This means if the given image URI contains non-empty scheme, plugin raises an error.
137
+
138
+ Option `allowed_schemes` adds exception for the schemes specified in it. For URIs with allowed schemes plugin will try to access them and retrieve the image size.
139
+
140
+ For example, to allow HTTPS image URLs and [data URLs](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data) use the following:
141
+
142
+ ```yml
143
+ jekyll-imgwh:
144
+ allowed_schemes: ["data", "https"]
145
+ ```
146
+
147
+ ### `extra_rest`
148
+
149
+ Remember `imgwh` tag syntax? This option injects additional text into all generated HTML `<img>` elements. So we may say the tag syntax is actually this:
150
+
151
+ ```liquid
152
+ {% imgwh <src> <extra_rest> [<rest>] %}
153
+ ```
154
+
155
+ For example, since all generated HTML `<img>` elements get the size attributes, it might be a good idea to set lazy loading for the images:
156
+
157
+ ```yml
158
+ jekyll-imgwh:
159
+ extra_rest: loading="lazy"
160
+ ```
161
+
162
+ Like `<src>` and `<rest>`, `<extra_rest>` can also include Liquid markup, which is rendered in context of images where `<extra_rest>` is injected.
163
+
164
+ # Troubleshooting
165
+
166
+ When error is related to the image image file, plugin mentions the file path in the error message:
167
+
168
+ ```
169
+ $ bundle exec jekyll serve
170
+ <...>
171
+ Liquid Exception: jekyll-imgwh: 'Y:/ssg/assets/images/foo.png' could not be found in index.html
172
+ <...>
173
+ ```
174
+
175
+ Plugin also logs a lot of info which can help to resolve errors raised by it. Use `jekyll serve --verbose` flag to output this debug info.
176
+
177
+ For example, for template
178
+
179
+ ```
180
+ {% imgwh "/assets/images/{{ product.key }}.png" alt="{{ project.title }} Logo" class="www-logo" %}
181
+ ```
182
+
183
+ it would print something like this in case of successful generation:
184
+
185
+ ```
186
+ $ bundle exec jekyll serve --verbose
187
+ <...>
188
+ jekyll-imgwh: ---
189
+ jekyll-imgwh: content: '"/assets/images/{{ product.key }}.png" alt="{{ project.title }} Logo" class="www-logo"'
190
+ jekyll-imgwh: src: '/assets/images/{{ product.key }}.png'
191
+ jekyll-imgwh: rest: 'alt="{{ project.title }} Logo" class="www-logo"'
192
+ jekyll-imgwh: src rendered: '/assets/images/foo.png'
193
+ jekyll-imgwh: image path: 'Y:/ssg/assets/images/foo.png'
194
+ jekyll-imgwh: image size: [128, 64]
195
+ jekyll-imgwh: rest rendered: 'alt="My Product Logo" class="www-logo"'
196
+ <...>
197
+ ```
198
+
199
+ # Development
200
+
201
+ To get started with the development:
202
+
203
+ ```sh
204
+ git clone https://github.com/ojuuji/jekyll-imgwh.git
205
+ cd jekyll-imgwh
206
+ bundle install
207
+ bundle exec rspec
208
+ ```
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "fastimage"
5
+ require "jekyll"
6
+ require "jekyll/imgwh/version"
7
+ require "uri"
8
+
9
+ module Jekyll
10
+ module Imgwh
11
+ module Helpers
12
+ def debug(message)
13
+ Jekyll.logger.debug "#{NAME}:", message
14
+ end
15
+
16
+ def image_size(src, context)
17
+ uri = URI(src)
18
+
19
+ if uri.scheme.nil?
20
+ src = resolve_path(CGI.unescape(uri.path), context)
21
+ else
22
+ allowed_schemes = context.registers[:site].config.dig(NAME, "allowed_schemes") || []
23
+ unless allowed_schemes.include?(uri.scheme)
24
+ raise ArgumentError, "#{NAME}: URIs with '#{uri.scheme}' scheme are not allowed"
25
+ end
26
+ end
27
+
28
+ FastImage.size(src) or raise LoadError, "#{NAME}: could not get size of image '#{src}'"
29
+ end
30
+
31
+ def resolve_path(path, context)
32
+ local_path = path.start_with?("/") ? path : File.join(context.registers[:page]["dir"], path)
33
+ local_path = context.registers[:site].in_source_dir(local_path)
34
+ debug "image path: '#{local_path}'"
35
+ return local_path if File.file?(local_path)
36
+
37
+ themed_path = context.registers[:site].in_theme_dir(path) if path.start_with?("/")
38
+ raise LoadError, "#{NAME}: '#{local_path}' could not be found" unless themed_path
39
+
40
+ debug "themed image path: '#{themed_path}'"
41
+ return themed_path if File.file?(themed_path)
42
+
43
+ raise LoadError, "#{NAME}: none of '#{local_path}', '#{themed_path}' could be found"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jekyll/imgwh/helpers"
4
+
5
+ module Jekyll
6
+ module Imgwh
7
+ class Tag < Liquid::Tag
8
+ include Helpers
9
+
10
+ def initialize(tag_name, content, tokens)
11
+ super
12
+
13
+ @content = content.strip
14
+
15
+ if (m = @content.match(%r/^(["'])((?:\1\1|(?!\1).)+)\1(?:\s+(.+))?$/))
16
+ @quote, @src, @rest = m.captures
17
+ @src = @src.gsub("#{@quote}#{@quote}", @quote)
18
+
19
+ elsif (m = @content.match(%r/^(?!["'])((?:(?!\{\{)\S|\{\{.+?\}\})+)(?:\s+(.+))?$/))
20
+ @quote = ""
21
+ @src, @rest = m.captures
22
+
23
+ else
24
+ raise SyntaxError, "#{NAME}: invalid #{tag_name} tag: '#{@content}'"
25
+ end
26
+ end
27
+
28
+ def render(context)
29
+ ["---", "content: '#{@content}'", "src: '#{@src}'", "rest: '#{@rest}'"].map { |x| debug x }
30
+
31
+ src = Liquid::Template.parse(@src).render(context)
32
+ debug "src rendered: '#{src}'"
33
+ img = "<img src=#{quoted src}"
34
+
35
+ size = image_size(src, context)
36
+ debug "image size: #{size}"
37
+ img << " width=#{quoted size[0]} height=#{quoted size[1]}" << render_rest(context) << ">"
38
+ end
39
+
40
+ private
41
+
42
+ def quoted(value)
43
+ "#{@quote}#{value}#{@quote}"
44
+ end
45
+
46
+ def render_rest(context)
47
+ rest = +""
48
+
49
+ extra_rest = context.registers[:site].config.dig(NAME, "extra_rest")
50
+ unless extra_rest.nil?
51
+ extra_rest = Liquid::Template.parse(extra_rest).render(context)
52
+ debug "extra_rest rendered: '#{extra_rest}'"
53
+ rest << " #{extra_rest}" unless extra_rest.empty?
54
+ end
55
+
56
+ tag_rest = Liquid::Template.parse(@rest).render(context)
57
+ debug "rest rendered: '#{tag_rest}'"
58
+ rest << " #{tag_rest}" unless tag_rest.empty?
59
+
60
+ rest
61
+ end
62
+ end
63
+ end
64
+ end
@@ -3,6 +3,6 @@
3
3
  module Jekyll
4
4
  module Imgwh
5
5
  NAME = "jekyll-imgwh"
6
- VERSION = "1.2.0"
6
+ VERSION = "1.3.1"
7
7
  end
8
8
  end
data/lib/jekyll-imgwh.rb CHANGED
@@ -1,114 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi"
4
- require "fastimage"
5
- require "jekyll"
6
- require "jekyll-imgwh/version"
7
- require "uri"
3
+ require "jekyll/imgwh/tag"
8
4
 
9
5
  module Jekyll
10
6
  module Imgwh
11
- class Tag < Liquid::Tag
12
- def initialize(tag_name, content, tokens)
13
- super
7
+ Liquid::Template.register_tag "imgwh", Tag
14
8
 
15
- @content = content.strip
16
-
17
- if (m = @content.match(%r/^(["'])((?:\1\1|(?!\1).)+)\1(?:\s+(.+))?$/))
18
- @quote, @src, @rest = m.captures
19
- @src = @src.gsub("#{@quote}#{@quote}", @quote)
20
-
21
- elsif (m = @content.match(%r/^(?!["'])((?:(?!\{\{)\S|\{\{.+?\}\})+)(?:\s+(.+))?$/))
22
- @quote = ""
23
- @src, @rest = m.captures
24
-
25
- else
26
- raise SyntaxError, "#{NAME}: invalid #{tag_name} tag: '#{@content}'"
27
- end
28
- end
29
-
30
- def debug(message)
31
- Jekyll.logger.debug "#{NAME}:", message
32
- end
33
-
34
- def render(context)
35
- ["---", "content: '#{@content}'", "src: '#{@src}'", "rest: '#{@rest}'"].map { |x| debug x }
36
-
37
- src = Liquid::Template.parse(@src).render(context)
38
- debug "src rendered: '#{src}'"
39
- img = "<img src=#{quoted src}"
40
-
41
- size = image_size(src, context)
42
- debug "image size: #{size}"
43
- img << " width=#{quoted size[0]} height=#{quoted size[1]}" << render_rest(context) << ">"
44
- end
45
-
46
- private
47
-
48
- def quoted(value)
49
- "#{@quote}#{value}#{@quote}"
50
- end
51
-
52
- def image_size(src, context)
53
- uri = URI(src)
54
-
55
- if uri.scheme.nil?
56
- src = resolve_path(CGI.unescape(uri.path), context)
57
- else
58
- allowed_schemes = context.registers[:site].config.dig(NAME, "allowed_schemes") || []
59
- unless allowed_schemes.include?(uri.scheme)
60
- raise ArgumentError, "#{NAME}: URIs with '#{uri.scheme}' scheme are not allowed"
61
- end
62
- end
63
-
64
- FastImage.size(src) or raise LoadError, "#{NAME}: could not get size of image '#{src}'"
65
- end
66
-
67
- def render_rest(context)
68
- rest = +""
69
-
70
- extra_rest = context.registers[:site].config.dig(NAME, "extra_rest")
71
- unless extra_rest.nil?
72
- extra_rest = Liquid::Template.parse(extra_rest).render(context)
73
- debug "extra_rest rendered: '#{extra_rest}'"
74
- rest << " #{extra_rest}" unless extra_rest.empty?
75
- end
76
-
77
- tag_rest = Liquid::Template.parse(@rest).render(context)
78
- debug "rest rendered: '#{tag_rest}'"
79
- rest << " #{tag_rest}" unless tag_rest.empty?
80
-
81
- rest
82
- end
83
-
84
- def resolve_path(path, context)
85
- local_path = resolve_local_path(path, context)
86
- return local_path if File.file?(local_path)
87
-
88
- themed_path = resolve_themed_path(path, context) if path.start_with?("/")
89
- raise LoadError, "#{NAME}: '#{local_path}' could not be found" unless themed_path
90
-
91
- return themed_path if File.file?(themed_path)
92
-
93
- raise LoadError, "#{NAME}: none of '#{local_path}', '#{themed_path}' could be found"
94
- end
95
-
96
- def resolve_local_path(path, context)
97
- path = File.join(context.registers[:page]["dir"], path) unless path.start_with?("/")
98
- local_path = File.join(context.registers[:site].source, path)
99
- debug "image path: '#{local_path}'"
100
-
101
- local_path
102
- end
103
-
104
- def resolve_themed_path(path, context)
105
- themed_path = context.registers[:site].in_theme_dir(path)
106
- debug "themed image path: '#{themed_path}'"
9
+ def imgwh(src)
10
+ image_size(src, @context)
11
+ end
107
12
 
108
- themed_path
109
- end
13
+ Liquid::Template.register_filter(self)
110
14
 
111
- Liquid::Template.register_tag "imgwh", self
112
- end
15
+ # Include after register_filter so they do not leak to Liquid yet are accessible within imgwh()
16
+ include Helpers
113
17
  end
114
18
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-imgwh
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikalai Ananenka
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-23 00:00:00.000000000 Z
10
+ date: 2025-04-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: fastimage
@@ -107,22 +107,24 @@ dependencies:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
109
  version: '0.9'
110
- description: 'Jekyll tag for HTML <img> element which verifies that image exists and
111
- automatically fills width and height attributes.
112
-
113
- '
114
110
  email:
115
111
  - ojuuji@gmail.com
116
112
  executables: []
117
113
  extensions: []
118
- extra_rdoc_files: []
114
+ extra_rdoc_files:
115
+ - README.md
119
116
  files:
117
+ - README.md
120
118
  - lib/jekyll-imgwh.rb
121
- - lib/jekyll-imgwh/version.rb
119
+ - lib/jekyll/imgwh/helpers.rb
120
+ - lib/jekyll/imgwh/tag.rb
121
+ - lib/jekyll/imgwh/version.rb
122
122
  homepage: https://github.com/ojuuji/jekyll-imgwh
123
123
  licenses:
124
124
  - MIT
125
- metadata: {}
125
+ metadata:
126
+ homepage_uri: https://github.com/ojuuji/jekyll-imgwh
127
+ bug_tracker_uri: https://github.com/ojuuji/jekyll-imgwh/issues
126
128
  rdoc_options: []
127
129
  require_paths:
128
130
  - lib
@@ -139,5 +141,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
141
  requirements: []
140
142
  rubygems_version: 3.6.2
141
143
  specification_version: 4
142
- summary: Jekyll tag for HTML <img> element with auto filled size attributes
144
+ summary: A tag for <img> element with some automation, and a filter returning image
145
+ size.
143
146
  test_files: []