jekyll_picture_tag 1.13.0 → 2.0.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/.envrc +4 -0
- data/.github/workflows/code-checks.yml +33 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +29 -76
- data/.ruby-version +1 -1
- data/docs/.envrc +2 -0
- data/docs/devs/contributing/code.md +14 -4
- data/docs/devs/contributing/docs.md +24 -6
- data/docs/devs/contributing/setup.md +21 -1
- data/docs/devs/contributing/testing.md +19 -37
- data/docs/devs/releases.md +45 -4
- data/docs/index.md +43 -18
- data/docs/logo.png +0 -0
- data/docs/logo.svg +880 -0
- data/docs/users/configuration/disable.md +1 -1
- data/docs/users/configuration/ignore_missing.md +1 -1
- data/docs/users/configuration/kramdown_fix.md +1 -1
- data/docs/users/configuration/suppress_warnings.md +1 -1
- data/docs/users/configuration/urls.md +69 -0
- data/docs/users/deployment.md +49 -0
- data/docs/users/getting_started.md +55 -0
- data/docs/users/installation.md +18 -38
- data/docs/users/liquid_tag/argument_reference/crop.md +21 -36
- data/docs/users/liquid_tag/examples.md +13 -25
- data/docs/users/liquid_tag/index.md +1 -1
- data/docs/users/notes/{migration.md → migration_1.md} +1 -1
- data/docs/users/notes/migration_2.md +99 -0
- data/docs/users/presets/cropping.md +21 -22
- data/docs/users/presets/default.md +11 -2
- data/docs/users/presets/examples.md +77 -45
- data/docs/users/presets/fallback_image.md +1 -1
- data/docs/users/presets/html_attributes.md +1 -1
- data/docs/users/presets/image_formats.md +3 -3
- data/docs/users/presets/image_quality.md +71 -56
- data/docs/users/presets/index.md +19 -45
- data/docs/users/presets/link_source.md +1 -1
- data/docs/users/presets/media_queries.md +1 -1
- data/docs/users/presets/nomarkdown_override.md +1 -1
- data/docs/users/presets/pixel_ratio_srcsets.md +1 -1
- data/docs/users/presets/width_height_attributes.md +1 -1
- data/docs/users/presets/width_srcsets.md +61 -23
- data/docs/users/presets/writing_presets.md +65 -0
- data/docs/users/tutorial.md +97 -0
- data/jekyll_picture_tag.gemspec +38 -23
- data/lib/jekyll_picture_tag.rb +8 -5
- data/lib/jekyll_picture_tag/cache.rb +64 -3
- data/lib/jekyll_picture_tag/defaults/global.rb +18 -0
- data/lib/jekyll_picture_tag/defaults/presets.rb +57 -0
- data/lib/jekyll_picture_tag/images.rb +1 -0
- data/lib/jekyll_picture_tag/images/generated_image.rb +25 -60
- data/lib/jekyll_picture_tag/images/image_file.rb +105 -0
- data/lib/jekyll_picture_tag/images/img_uri.rb +3 -10
- data/lib/jekyll_picture_tag/images/source_image.rb +44 -9
- data/lib/jekyll_picture_tag/instructions.rb +70 -6
- data/lib/jekyll_picture_tag/instructions/children/config.rb +128 -0
- data/lib/jekyll_picture_tag/instructions/children/context.rb +24 -0
- data/lib/jekyll_picture_tag/instructions/children/params.rb +90 -0
- data/lib/jekyll_picture_tag/instructions/children/parsers.rb +48 -0
- data/lib/jekyll_picture_tag/instructions/children/preset.rb +182 -0
- data/lib/jekyll_picture_tag/instructions/parents/conditional_instruction.rb +69 -0
- data/lib/jekyll_picture_tag/instructions/parents/env_instruction.rb +29 -0
- data/lib/jekyll_picture_tag/output_formats/basic.rb +5 -17
- data/lib/jekyll_picture_tag/parsers.rb +6 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/arg_splitter.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/configuration.rb +28 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/html_attributes.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/image_backend.rb +33 -0
- data/lib/jekyll_picture_tag/parsers/preset.rb +43 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/tag_parser.rb +15 -12
- data/lib/jekyll_picture_tag/router.rb +35 -93
- data/lib/jekyll_picture_tag/srcsets/basic.rb +4 -10
- data/lib/jekyll_picture_tag/utils.rb +10 -20
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +48 -9
- metadata +161 -80
- data/.travis.yml +0 -8
- data/Dockerfile +0 -9
- data/docs/users/configuration/cdn.md +0 -35
- data/docs/users/configuration/relative_urls.md +0 -15
- data/docs/users/notes/input_checking.md +0 -6
- data/jekyll-picture-tag.gemspec +0 -52
- data/lib/jekyll-picture-tag.rb +0 -25
- data/lib/jekyll_picture_tag/cache/base.rb +0 -59
- data/lib/jekyll_picture_tag/cache/generated.rb +0 -20
- data/lib/jekyll_picture_tag/cache/source.rb +0 -19
- data/lib/jekyll_picture_tag/defaults/global.yml +0 -11
- data/lib/jekyll_picture_tag/defaults/presets.yml +0 -11
- data/lib/jekyll_picture_tag/instructions/configuration.rb +0 -121
- data/lib/jekyll_picture_tag/instructions/preset.rb +0 -122
- data/lib/jekyll_picture_tag/instructions/set.rb +0 -75
data/lib/jekyll_picture_tag.rb
CHANGED
@@ -5,9 +5,12 @@ require_relative 'jekyll_picture_tag/cache'
|
|
5
5
|
require_relative 'jekyll_picture_tag/images'
|
6
6
|
require_relative 'jekyll_picture_tag/instructions'
|
7
7
|
require_relative 'jekyll_picture_tag/output_formats'
|
8
|
+
require_relative 'jekyll_picture_tag/parsers'
|
8
9
|
require_relative 'jekyll_picture_tag/router'
|
9
10
|
require_relative 'jekyll_picture_tag/srcsets'
|
10
11
|
require_relative 'jekyll_picture_tag/utils'
|
12
|
+
require_relative 'jekyll_picture_tag/defaults/presets'
|
13
|
+
require_relative 'jekyll_picture_tag/defaults/global'
|
11
14
|
|
12
15
|
# Title: Jekyll Picture Tag
|
13
16
|
# Authors: Rob Wierzbowski : @robwierzbowski
|
@@ -60,7 +63,9 @@ module PictureTag
|
|
60
63
|
def render(context)
|
61
64
|
setup(context)
|
62
65
|
|
63
|
-
if PictureTag.disabled?
|
66
|
+
if PictureTag.disabled? || PictureTag.raw_params.empty?
|
67
|
+
Utils.warning 'You have called JPT without any arguments.'
|
68
|
+
|
64
69
|
''
|
65
70
|
else
|
66
71
|
PictureTag.output_class.new.to_s
|
@@ -70,11 +75,9 @@ module PictureTag
|
|
70
75
|
private
|
71
76
|
|
72
77
|
def setup(context)
|
78
|
+
PictureTag.clear_instructions
|
73
79
|
PictureTag.context = context
|
74
|
-
|
75
|
-
# Now that we have both the tag parameters and the context object, we can
|
76
|
-
# build our instruction set.
|
77
|
-
PictureTag.instructions = Instructions::Set.new(@raw_params)
|
80
|
+
PictureTag.raw_params = @raw_params
|
78
81
|
|
79
82
|
# We need to explicitly prevent jekyll from overwriting our generated
|
80
83
|
# image files:
|
@@ -1,3 +1,64 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PictureTag
|
4
|
+
# Store expensive bits of information between text files. Originally cached
|
5
|
+
# width & heights of images in addition to digests, now just image digests.
|
6
|
+
class Cache
|
7
|
+
def initialize(base_name)
|
8
|
+
@base_name = base_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
data[key]
|
13
|
+
end
|
14
|
+
|
15
|
+
def []=(key, value)
|
16
|
+
raise ArgumentError unless template.keys.include? key
|
17
|
+
|
18
|
+
data[key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
# Call after updating data.
|
22
|
+
def write
|
23
|
+
return if PictureTag.config['disable_disk_cache']
|
24
|
+
|
25
|
+
FileUtils.mkdir_p(File.join(base_directory, sub_directory))
|
26
|
+
|
27
|
+
File.open(filename, 'w+') do |f|
|
28
|
+
f.write JSON.generate(data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def data
|
35
|
+
@data ||= if File.exist?(filename)
|
36
|
+
JSON.parse(File.read(filename)).transform_keys(&:to_sym)
|
37
|
+
else
|
38
|
+
template
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/(cache_dir)/assets/myimage.jpg.json
|
43
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
44
|
+
def base_directory
|
45
|
+
File.join(PictureTag.site.cache_dir, 'jpt')
|
46
|
+
end
|
47
|
+
|
48
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/(cache_dir)/assets/myimage.jpg.json
|
49
|
+
# ^^^^^^^^
|
50
|
+
def sub_directory
|
51
|
+
File.dirname(@base_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
# /home/dave/my_blog/.jekyll-cache/jpt/somefolder/myimage.jpg.json
|
55
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
56
|
+
def filename
|
57
|
+
File.join(base_directory, @base_name + '.json')
|
58
|
+
end
|
59
|
+
|
60
|
+
def template
|
61
|
+
{ digest: nil }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module PictureTag
|
2
|
+
# Default settings for _config.yml
|
3
|
+
DEFAULT_CONFIG = {
|
4
|
+
'picture' => {
|
5
|
+
'source' => '',
|
6
|
+
'output' => 'generated',
|
7
|
+
'suppress_warnings' => false,
|
8
|
+
'relative_url' => true,
|
9
|
+
'cdn_environments' => ['production'],
|
10
|
+
'nomarkdown' => true,
|
11
|
+
'ignore_missing_images' => false,
|
12
|
+
'disabled' => false,
|
13
|
+
'fast_build' => false,
|
14
|
+
'ignore_baseurl' => false,
|
15
|
+
'baseurl_key' => 'baseurl'
|
16
|
+
}
|
17
|
+
}.freeze
|
18
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module PictureTag
|
2
|
+
DEFAULT_PRESET = { 'markup' => 'auto',
|
3
|
+
'formats' => ['original'],
|
4
|
+
'widths' => [400, 600, 800, 1000],
|
5
|
+
'fallback_width' => 800,
|
6
|
+
'fallback_format' => 'original',
|
7
|
+
'noscript' => false,
|
8
|
+
'link_source' => false,
|
9
|
+
'quality' => 75,
|
10
|
+
'format_quality' => { 'webp' => 50,
|
11
|
+
'avif' => 30,
|
12
|
+
'jp2' => 30 },
|
13
|
+
'data_sizes' => true,
|
14
|
+
'keep' => 'attention',
|
15
|
+
'dimension_attributes' => false,
|
16
|
+
'strip_metadata' => true,
|
17
|
+
'image_options' => {
|
18
|
+
'avif' => { 'compression' => 'av1', 'speed' => 8 }
|
19
|
+
} }.freeze
|
20
|
+
|
21
|
+
STOCK_PRESETS = {
|
22
|
+
'jpt-webp' => { 'formats' => %w[webp original] },
|
23
|
+
|
24
|
+
'jpt-avif' => { 'formats' => %w[avif webp original] },
|
25
|
+
|
26
|
+
'jpt-lazy' => { 'markup' => 'data_auto',
|
27
|
+
'noscript' => true,
|
28
|
+
'formats' => %w[webp original],
|
29
|
+
'attributes' => { 'parent' => 'class="lazy"' } },
|
30
|
+
|
31
|
+
'jpt-loaded' => { 'formats' => %w[avif jp2 webp original],
|
32
|
+
'dimension_attributes' => true },
|
33
|
+
|
34
|
+
'jpt-direct' => { 'markup' => 'direct_url',
|
35
|
+
'fallback_format' => 'webp',
|
36
|
+
'fallback_width' => 600 },
|
37
|
+
|
38
|
+
'jpt-thumbnail' => { 'base_width' => 250,
|
39
|
+
'pixel_ratios' => [1, 1.5, 2],
|
40
|
+
'formats' => %w[webp original],
|
41
|
+
'fallback_width' => 250,
|
42
|
+
'attributes' => { 'picture' => 'class="icon"' } },
|
43
|
+
|
44
|
+
'jpt-avatar' => { 'base_width' => 100,
|
45
|
+
'pixel_ratios' => [1, 1.5, 2],
|
46
|
+
'fallback_width' => 100,
|
47
|
+
'crop' => '1:1' }
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
STOCK_MEDIA_QUERIES = {
|
51
|
+
'jpt-mobile' => 'max-width: 480px',
|
52
|
+
'jpt-tablet' => 'max-width: 768',
|
53
|
+
'jpt-laptop' => 'max-width: 1024px',
|
54
|
+
'jpt-desktop' => 'max-width: 1200',
|
55
|
+
'jpt-wide' => 'min-width: 1201'
|
56
|
+
}.freeze
|
57
|
+
end
|
@@ -1,18 +1,20 @@
|
|
1
|
-
require '
|
1
|
+
require 'ruby-vips'
|
2
2
|
|
3
3
|
module PictureTag
|
4
|
-
# Represents a generated image file.
|
4
|
+
# Represents a generated image, but not the file itself. Its purpose is to
|
5
|
+
# make its properties available for query, and hand them off to the ImageFile
|
6
|
+
# class for generation.
|
5
7
|
class GeneratedImage
|
6
|
-
attr_reader :width
|
8
|
+
attr_reader :width
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(source_file:, width:, format:, crop: nil, gravity: '')
|
10
|
+
def initialize(source_file:, width:, format:)
|
11
11
|
@source = source_file
|
12
12
|
@width = width
|
13
|
-
@
|
14
|
-
|
15
|
-
|
13
|
+
@raw_format = format
|
14
|
+
end
|
15
|
+
|
16
|
+
def format
|
17
|
+
@format ||= process_format(@raw_format)
|
16
18
|
end
|
17
19
|
|
18
20
|
def exists?
|
@@ -43,77 +45,40 @@ module PictureTag
|
|
43
45
|
|
44
46
|
# Post crop
|
45
47
|
def source_width
|
46
|
-
|
47
|
-
|
48
|
-
cache[:width]
|
48
|
+
image.width
|
49
49
|
end
|
50
50
|
|
51
51
|
# Post crop
|
52
52
|
def source_height
|
53
|
-
|
54
|
-
|
55
|
-
cache[:height]
|
53
|
+
image.height
|
56
54
|
end
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
# We exclude width and format from the cache name, since it isn't specific to them.
|
61
|
-
def cache
|
62
|
-
@cache ||= Cache::Generated.new("#{@source.base_name}-#{id}")
|
56
|
+
def quality
|
57
|
+
PictureTag.quality(format, width)
|
63
58
|
end
|
64
59
|
|
65
|
-
|
66
|
-
return if @source.missing
|
67
|
-
|
68
|
-
# Ensure it's generated:
|
69
|
-
image
|
70
|
-
|
71
|
-
cache[:width] = @source_dimensions[:width]
|
72
|
-
cache[:height] = @source_dimensions[:height]
|
73
|
-
|
74
|
-
cache.write
|
75
|
-
end
|
60
|
+
private
|
76
61
|
|
77
|
-
# Hash all inputs and truncate, so we know when they change without getting
|
62
|
+
# Hash all inputs and truncate, so we know when they change without getting
|
63
|
+
# too long.
|
78
64
|
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-1234abcde.jpg
|
79
65
|
# ^^^^^^^^^
|
80
66
|
def id
|
81
|
-
@id ||= Digest::MD5.hexdigest(
|
67
|
+
@id ||= Digest::MD5.hexdigest(settings.join)[0..8]
|
82
68
|
end
|
83
69
|
|
84
|
-
|
85
|
-
|
86
|
-
@image ||= open_image
|
70
|
+
def settings
|
71
|
+
[@source.digest, @source.crop, @source.keep, quality]
|
87
72
|
end
|
88
73
|
|
89
|
-
def
|
90
|
-
|
91
|
-
image_base.combine_options do |i|
|
92
|
-
i.auto_orient
|
93
|
-
if @crop
|
94
|
-
i.gravity @gravity
|
95
|
-
i.crop @crop
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
@source_dimensions = { width: image_base.width, height: image_base.height }
|
100
|
-
|
101
|
-
image_base
|
74
|
+
def image
|
75
|
+
@image ||= Vips::Image.new_from_file @source.name
|
102
76
|
end
|
103
77
|
|
104
78
|
def generate_image
|
105
|
-
|
106
|
-
|
107
|
-
image.format(@format, 0, { resize: "#{@width}x", quality: quality })
|
108
|
-
FileUtils.mkdir_p(File.dirname(absolute_filename))
|
109
|
-
|
110
|
-
image.write absolute_filename
|
111
|
-
|
112
|
-
FileUtils.chmod(0o644, absolute_filename)
|
113
|
-
end
|
79
|
+
return if @source.missing
|
114
80
|
|
115
|
-
|
116
|
-
PictureTag.quality(@format, @width)
|
81
|
+
ImageFile.new(@source, self)
|
117
82
|
end
|
118
83
|
|
119
84
|
def process_format(format)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module PictureTag
|
2
|
+
# Basically a wrapper class for vips. Handles image operations.
|
3
|
+
# Vips returns new images for Crop, resize, and autorotate operations.
|
4
|
+
# Quality, metadata stripping, and format are applied on write.
|
5
|
+
#
|
6
|
+
# This deserves to be two classes and a factory, one for normal vips save and
|
7
|
+
# one for magicksave. This is illustrated by the fact that stubbing backend
|
8
|
+
# determination logic for its unit tests would basically require
|
9
|
+
# re-implementing it completely.
|
10
|
+
#
|
11
|
+
# I'm planning to implement standalone imagemagick as an alternative to vips,
|
12
|
+
# so when I add that I'll also do that refactoring. For now it works fine and
|
13
|
+
# it's not too bloated.
|
14
|
+
class ImageFile
|
15
|
+
def initialize(source, base)
|
16
|
+
@source = source
|
17
|
+
@base = base
|
18
|
+
|
19
|
+
build
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :source, :base
|
25
|
+
|
26
|
+
def build
|
27
|
+
notify
|
28
|
+
|
29
|
+
mkdir
|
30
|
+
|
31
|
+
image = load_image
|
32
|
+
|
33
|
+
image = process(image)
|
34
|
+
|
35
|
+
write(image)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Processing pipeline
|
39
|
+
def process(image)
|
40
|
+
image = crop(image) if source.crop?
|
41
|
+
|
42
|
+
image = resize(image)
|
43
|
+
|
44
|
+
image.autorot
|
45
|
+
end
|
46
|
+
|
47
|
+
def handler
|
48
|
+
PictureTag.backend.handler_for(@base.format)
|
49
|
+
end
|
50
|
+
|
51
|
+
def quality_key
|
52
|
+
handler == :vips ? :Q : :quality
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_opts
|
56
|
+
opts = PictureTag.preset['image_options'][@base.format] || {}
|
57
|
+
|
58
|
+
opts[:strip] = PictureTag.preset['strip_metadata']
|
59
|
+
|
60
|
+
# gifs don't accept a quality setting, and PNGs don't on older versions of
|
61
|
+
# vips. Since it's not remarkably useful anyway, we'll ignore them.
|
62
|
+
opts[quality_key] = base.quality unless %w[gif png].include? base.format
|
63
|
+
|
64
|
+
opts.transform_keys(&:to_sym)
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_image
|
68
|
+
Vips::Image.new_from_file source.name
|
69
|
+
end
|
70
|
+
|
71
|
+
def write(image)
|
72
|
+
case handler
|
73
|
+
when :vips
|
74
|
+
image.write_to_file(base.absolute_filename, **write_opts)
|
75
|
+
# If vips can't handle it, fall back to imagemagick.
|
76
|
+
when :magick
|
77
|
+
image.magicksave(base.absolute_filename, **write_opts)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Fix permissions. TODO - still necessary?
|
81
|
+
FileUtils.chmod(0o644, base.absolute_filename)
|
82
|
+
end
|
83
|
+
|
84
|
+
def notify
|
85
|
+
puts 'Generating new image file: ' + base.name
|
86
|
+
end
|
87
|
+
|
88
|
+
def resize(image)
|
89
|
+
image.resize(scale_value)
|
90
|
+
end
|
91
|
+
|
92
|
+
def crop(image)
|
93
|
+
image.smartcrop(*source.dimensions,
|
94
|
+
interesting: PictureTag.keep(@source.media_preset))
|
95
|
+
end
|
96
|
+
|
97
|
+
def scale_value
|
98
|
+
base.width.to_f / source.width
|
99
|
+
end
|
100
|
+
|
101
|
+
def mkdir
|
102
|
+
FileUtils.mkdir_p(File.dirname(base.absolute_filename))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -18,7 +18,7 @@ module PictureTag
|
|
18
18
|
# | domain | baseurl | directory | filename
|
19
19
|
def to_s
|
20
20
|
Addressable::URI.escape(
|
21
|
-
File.join(domain, baseurl, directory, @filename)
|
21
|
+
File.join(domain, PictureTag.baseurl, directory, @filename)
|
22
22
|
)
|
23
23
|
end
|
24
24
|
|
@@ -29,21 +29,14 @@ module PictureTag
|
|
29
29
|
# | domain | baseurl | j-p-t output dir | filename
|
30
30
|
def domain
|
31
31
|
if PictureTag.cdn?
|
32
|
-
PictureTag.
|
33
|
-
elsif PictureTag.
|
32
|
+
PictureTag.cdn_url
|
33
|
+
elsif PictureTag.relative_url
|
34
34
|
''
|
35
35
|
else
|
36
36
|
PictureTag.config['url'] || ''
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
# https://example.com/my-base-path/assets/generated-images/image.jpg
|
41
|
-
# ^^^^^^^^^^^^^
|
42
|
-
# | domain | baseurl | directory | filename
|
43
|
-
def baseurl
|
44
|
-
PictureTag.config['baseurl'] || ''
|
45
|
-
end
|
46
|
-
|
47
40
|
# https://example.com/my-base-path/assets/generated-images/image.jpg
|
48
41
|
# ^^^^^^^^^^^^^^^^^^^^^^^^
|
49
42
|
# | domain | baseurl | directory | filename
|