jekyll-skyhook 1.0.0
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 +7 -0
- data/lib/jekyll-skyhook/tags.rb +167 -0
- data/lib/jekyll-skyhook.rb +354 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b7b5eb02cafd3faa4b686413d6f77ded1e247786c09fb23ac6c77cb23252f818
|
4
|
+
data.tar.gz: c8b4fdf65dd016a38b102eb447b4408dacbabb1de74ea1b1326007e19d293d2b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5ebe3144da494e72596d464143bab8d8f11b3d19ce91293093e077ad8df9bdc737085af6b8a23310ec246f7f6b0c3331e69ca32fb3198e217f546cb4aa92f9e
|
7
|
+
data.tar.gz: ec8abf0e67ef4b9dbdfc8b2e302911da71a99bb3730254ef031ebeba95bef0d1690e54e59d43a43bcb57ecee8f10467d23f2f439cf055cb238771571e012da43
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Jekyll
|
2
|
+
module Tags
|
3
|
+
class AssetTag < Liquid::Tag
|
4
|
+
def initialize(tag_name, asset_name, tokens)
|
5
|
+
super
|
6
|
+
@asset_name = asset_name
|
7
|
+
end
|
8
|
+
|
9
|
+
def render(context)
|
10
|
+
site = context.registers[:site]
|
11
|
+
manifest_path = File.join(site.cache_dir, 'skyhook', 'assets-manifest.json')
|
12
|
+
|
13
|
+
# Render any variables in the asset name
|
14
|
+
asset_name = Liquid::Template.parse(@asset_name).render(context).strip
|
15
|
+
|
16
|
+
manifest = JSON.parse(File.read(manifest_path))
|
17
|
+
digested = manifest.fetch(asset_name)
|
18
|
+
"/#{digested}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class SrcsetTag < Liquid::Tag
|
23
|
+
def initialize(tag_name, markup, tokens)
|
24
|
+
super
|
25
|
+
@markup = markup.strip
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(context)
|
29
|
+
site = context.registers[:site]
|
30
|
+
skyhook = Jekyll::Skyhook.instance(site)
|
31
|
+
|
32
|
+
asset_path, format, widths = parse_srcset_markup(@markup)
|
33
|
+
|
34
|
+
# Render any variables in the path
|
35
|
+
asset_path = Liquid::Template.parse(asset_path).render(context).strip
|
36
|
+
|
37
|
+
# Find the original file
|
38
|
+
original_file_path = File.join(site.source, asset_path)
|
39
|
+
unless File.exist?(original_file_path)
|
40
|
+
raise "Image file not found: #{asset_path}"
|
41
|
+
end
|
42
|
+
|
43
|
+
srcset = widths.map do |width|
|
44
|
+
transformations = { width: width.to_s }
|
45
|
+
transformations[:format] = format if format
|
46
|
+
digested_path = skyhook.process_image_transformation(original_file_path, transformations)
|
47
|
+
"/#{digested_path} #{width}w"
|
48
|
+
end.join(', ')
|
49
|
+
|
50
|
+
"srcset=\"#{srcset}\""
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def parse_srcset_markup(markup)
|
56
|
+
# Parse: path[format="webp"] 400 800 1200
|
57
|
+
# Or: path 400 800 1200
|
58
|
+
|
59
|
+
if markup.include?('[format="')
|
60
|
+
match = markup.match(/^([^\[]+)\[format="([^"]+)"\](.*)$/)
|
61
|
+
if match
|
62
|
+
asset_path = match[1].strip
|
63
|
+
format = match[2]
|
64
|
+
widths_part = match[3].strip
|
65
|
+
if widths_part.empty?
|
66
|
+
# No widths provided - fall back to simple parsing
|
67
|
+
return [asset_path, nil, []]
|
68
|
+
else
|
69
|
+
widths = widths_part.split.map(&:to_i)
|
70
|
+
return [asset_path, format, widths]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Fallback to original parsing - be careful with liquid variables
|
76
|
+
# Look for numeric parts from the end
|
77
|
+
parts = markup.split
|
78
|
+
numeric_parts = []
|
79
|
+
path_parts = []
|
80
|
+
|
81
|
+
parts.reverse.each do |part|
|
82
|
+
if part.match?(/^\d+$/)
|
83
|
+
numeric_parts.unshift(part.to_i)
|
84
|
+
else
|
85
|
+
path_parts = parts[0...(parts.length - numeric_parts.length)]
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
asset_path = path_parts.join(' ')
|
91
|
+
widths = numeric_parts
|
92
|
+
[asset_path, nil, widths]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ImageTransformTag < Liquid::Tag
|
97
|
+
def initialize(tag_name, markup, tokens)
|
98
|
+
super
|
99
|
+
@markup = markup.strip
|
100
|
+
end
|
101
|
+
|
102
|
+
def render(context)
|
103
|
+
site = context.registers[:site]
|
104
|
+
skyhook = Jekyll::Skyhook.instance(site)
|
105
|
+
|
106
|
+
asset_path, transformations = parse_markup(@markup)
|
107
|
+
|
108
|
+
# Render any variables in the asset path
|
109
|
+
asset_path = Liquid::Template.parse(asset_path).render(context).strip
|
110
|
+
|
111
|
+
# Convert transformation values using context
|
112
|
+
parsed_transformations = {}
|
113
|
+
transformations.each do |key, value|
|
114
|
+
parsed_value = Liquid::Template.parse(value).render(context).strip
|
115
|
+
parsed_transformations[key.to_sym] = parsed_value
|
116
|
+
end
|
117
|
+
|
118
|
+
# Find the original file
|
119
|
+
original_file_path = File.join(site.source, asset_path)
|
120
|
+
unless File.exist?(original_file_path)
|
121
|
+
raise "Image file not found: #{asset_path}"
|
122
|
+
end
|
123
|
+
|
124
|
+
# Process the transformation or get original digested path
|
125
|
+
if parsed_transformations.any?
|
126
|
+
digested_path = skyhook.process_image_transformation(original_file_path, parsed_transformations)
|
127
|
+
return "" unless digested_path
|
128
|
+
"/#{digested_path}"
|
129
|
+
else
|
130
|
+
# No transformations - return original digested asset path
|
131
|
+
manifest_path = File.join(site.cache_dir, 'skyhook', 'assets-manifest.json')
|
132
|
+
if File.exist?(manifest_path)
|
133
|
+
manifest = JSON.parse(File.read(manifest_path))
|
134
|
+
digested_path = manifest[asset_path]
|
135
|
+
digested_path ? "/#{digested_path}" : ""
|
136
|
+
else
|
137
|
+
""
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def parse_markup(markup)
|
145
|
+
# Parse syntax like: foo/bar/baz.jpg[format="png"][width="400"]
|
146
|
+
match = markup.match(/^([^\[]+)(.*)$/)
|
147
|
+
return [markup, {}] unless match
|
148
|
+
|
149
|
+
asset_path = match[1].strip
|
150
|
+
brackets_part = match[2]
|
151
|
+
|
152
|
+
transformations = {}
|
153
|
+
|
154
|
+
# Extract all [key="value"] pairs
|
155
|
+
brackets_part.scan(/\[([^=]+)="([^"]+)"\]/) do |key, value|
|
156
|
+
transformations[key.strip] = value
|
157
|
+
end
|
158
|
+
|
159
|
+
[asset_path, transformations]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
Liquid::Template.register_tag('asset', Jekyll::Tags::AssetTag)
|
166
|
+
Liquid::Template.register_tag('srcset', Jekyll::Tags::SrcsetTag)
|
167
|
+
Liquid::Template.register_tag('image_transform', Jekyll::Tags::ImageTransformTag)
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'json'
|
4
|
+
require 'listen'
|
5
|
+
require 'css_parser'
|
6
|
+
require 'jekyll-skyhook/tags'
|
7
|
+
|
8
|
+
module Jekyll
|
9
|
+
class Skyhook
|
10
|
+
class << self
|
11
|
+
def instance(site)
|
12
|
+
@instances ||= {}
|
13
|
+
@instances[site] ||= new(site)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
private_class_method :new
|
17
|
+
|
18
|
+
def initialize(site)
|
19
|
+
@site = site
|
20
|
+
skyhook_config = site.config['skyhook'] || {}
|
21
|
+
@asset_dirs = Array(skyhook_config['asset_dirs'] || ['assets'])
|
22
|
+
@digest_dir = skyhook_config['digest_dir'] || '_digested'
|
23
|
+
@digested_path = File.join(site.source, @digest_dir)
|
24
|
+
FileUtils.mkdir_p(@digested_path)
|
25
|
+
|
26
|
+
# Configure image processing library
|
27
|
+
@image_processor_library = skyhook_config['image_processor'] || 'mini_magick'
|
28
|
+
load_image_processor
|
29
|
+
|
30
|
+
# Load existing manifest if it exists
|
31
|
+
manifest_dir = File.join(site.cache_dir, 'skyhook')
|
32
|
+
manifest_path = File.join(manifest_dir, 'assets-manifest.json')
|
33
|
+
@manifest = if File.exist?(manifest_path)
|
34
|
+
JSON.parse(File.read(manifest_path))
|
35
|
+
else
|
36
|
+
{}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def should_digest?
|
41
|
+
skyhook_config = @site.config['skyhook'] || {}
|
42
|
+
digest_assets = skyhook_config.fetch('digest_assets', true)
|
43
|
+
env = @site.config['environment'] || 'development'
|
44
|
+
|
45
|
+
case digest_assets
|
46
|
+
when true
|
47
|
+
true
|
48
|
+
when false
|
49
|
+
false
|
50
|
+
when Array
|
51
|
+
digest_assets.map(&:to_s).map(&:downcase).include?(env.downcase)
|
52
|
+
else
|
53
|
+
digest_assets.to_s.downcase == env.downcase
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_digest(file_path)
|
58
|
+
content = File.read(file_path)
|
59
|
+
Digest::MD5.hexdigest(content)
|
60
|
+
end
|
61
|
+
|
62
|
+
def process_assets
|
63
|
+
return unless should_digest?
|
64
|
+
|
65
|
+
css_files = []
|
66
|
+
|
67
|
+
@asset_dirs.each do |asset_dir|
|
68
|
+
assets_dir = File.join(@site.source, asset_dir)
|
69
|
+
next unless Dir.exist?(assets_dir)
|
70
|
+
|
71
|
+
files = Dir.glob(File.join(assets_dir, '**', '*'))
|
72
|
+
|
73
|
+
files.each do |file|
|
74
|
+
next if File.directory?(file)
|
75
|
+
|
76
|
+
if File.extname(file) == '.css'
|
77
|
+
css_files << file
|
78
|
+
else
|
79
|
+
process_file(file)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Process CSS files last so their URL rewriting can reference already-processed assets
|
85
|
+
css_files.each do |file|
|
86
|
+
process_file(file)
|
87
|
+
end
|
88
|
+
|
89
|
+
write_manifest
|
90
|
+
end
|
91
|
+
|
92
|
+
def process_file(file)
|
93
|
+
digest = generate_digest(file)
|
94
|
+
ext = File.extname(file)
|
95
|
+
basename = File.basename(file, ext)
|
96
|
+
original_relative_dir = File.dirname(file).sub(@site.source, '').sub(%r{^/}, '')
|
97
|
+
|
98
|
+
digested_dir = File.join(@digested_path, original_relative_dir)
|
99
|
+
FileUtils.mkdir_p(digested_dir)
|
100
|
+
|
101
|
+
new_filename = "#{basename}-#{digest}#{ext}"
|
102
|
+
new_filepath = File.join(digested_dir, new_filename)
|
103
|
+
|
104
|
+
FileUtils.cp(file, new_filepath)
|
105
|
+
|
106
|
+
static_file_dir = File.dirname(new_filepath).sub(@site.source, '').sub(%r{^/}, '')
|
107
|
+
@site.static_files << Jekyll::StaticFile.new(
|
108
|
+
@site,
|
109
|
+
@site.source,
|
110
|
+
static_file_dir,
|
111
|
+
new_filename
|
112
|
+
)
|
113
|
+
|
114
|
+
original_relative_path = file.sub("#{@site.source}/", '')
|
115
|
+
@manifest[original_relative_path] = File.join(@digest_dir, original_relative_dir, new_filename)
|
116
|
+
|
117
|
+
rewrite_css_urls(new_filepath) if ext == '.css'
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_image_transformation(file_path, transformations = {})
|
121
|
+
return nil unless File.exist?(file_path)
|
122
|
+
return nil unless transformations.any?
|
123
|
+
|
124
|
+
original_relative_path = file_path.sub("#{@site.source}/", '')
|
125
|
+
transform_key = build_transform_key(original_relative_path, transformations)
|
126
|
+
|
127
|
+
return @manifest[transform_key] if @manifest.key?(transform_key)
|
128
|
+
|
129
|
+
processed_image = apply_transformations(file_path, transformations)
|
130
|
+
return nil unless processed_image
|
131
|
+
|
132
|
+
result = store_transformed_image(original_relative_path, transformations, processed_image)
|
133
|
+
|
134
|
+
# Write manifest after new transformation
|
135
|
+
write_manifest
|
136
|
+
|
137
|
+
result
|
138
|
+
end
|
139
|
+
|
140
|
+
def store_version(original_path, transformations, processed_image_path)
|
141
|
+
original_relative_path = original_path.sub("#{@site.source}/", '')
|
142
|
+
transform_key = build_transform_key(original_relative_path, transformations)
|
143
|
+
|
144
|
+
content = File.read(processed_image_path)
|
145
|
+
digest = generate_digest_from_content(content)
|
146
|
+
|
147
|
+
ext = File.extname(processed_image_path)
|
148
|
+
basename = File.basename(original_relative_path, File.extname(original_relative_path))
|
149
|
+
transform_suffix = transformations.map { |k, v| "#{k}#{v}" }.sort.join('-')
|
150
|
+
|
151
|
+
original_relative_dir = File.dirname(original_relative_path)
|
152
|
+
digested_dir = File.join(@digested_path, original_relative_dir)
|
153
|
+
FileUtils.mkdir_p(digested_dir)
|
154
|
+
|
155
|
+
new_filename = "#{basename}-#{transform_suffix}-#{digest}#{ext}"
|
156
|
+
new_filepath = File.join(digested_dir, new_filename)
|
157
|
+
|
158
|
+
FileUtils.cp(processed_image_path, new_filepath)
|
159
|
+
|
160
|
+
static_file_dir = File.dirname(new_filepath).sub(@site.source, '').sub(%r{^/}, '')
|
161
|
+
@site.static_files << Jekyll::StaticFile.new(
|
162
|
+
@site,
|
163
|
+
@site.source,
|
164
|
+
static_file_dir,
|
165
|
+
new_filename
|
166
|
+
)
|
167
|
+
|
168
|
+
digested_relative_path = File.join(@digest_dir, original_relative_dir, new_filename)
|
169
|
+
@manifest[transform_key] = digested_relative_path
|
170
|
+
|
171
|
+
digested_relative_path
|
172
|
+
end
|
173
|
+
|
174
|
+
def write_manifest
|
175
|
+
manifest_dir = File.join(@site.cache_dir, 'skyhook')
|
176
|
+
FileUtils.mkdir_p(manifest_dir)
|
177
|
+
manifest_path = File.join(manifest_dir, 'assets-manifest.json')
|
178
|
+
File.write(manifest_path, JSON.pretty_generate(@manifest))
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def build_transform_key(original_path, transformations)
|
184
|
+
params = transformations.map { |k, v| "#{k}=#{v}" }.sort.join('&')
|
185
|
+
"#{original_path}##{params}"
|
186
|
+
end
|
187
|
+
|
188
|
+
def load_image_processor
|
189
|
+
case @image_processor_library.to_s.downcase
|
190
|
+
when 'vips'
|
191
|
+
require 'image_processing/vips'
|
192
|
+
@image_processor = ImageProcessing::Vips
|
193
|
+
when 'mini_magick'
|
194
|
+
require 'image_processing/mini_magick'
|
195
|
+
@image_processor = ImageProcessing::MiniMagick
|
196
|
+
else
|
197
|
+
Jekyll.logger.warn "Skyhook:", "Unknown image_processor '#{@image_processor_library}'. Using mini_magick as fallback."
|
198
|
+
require 'image_processing/mini_magick'
|
199
|
+
@image_processor = ImageProcessing::MiniMagick
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def apply_transformations(file_path, transformations)
|
204
|
+
processor = @image_processor.source(file_path)
|
205
|
+
|
206
|
+
processor = processor.resize_to_limit(transformations[:width].to_i, nil) if transformations[:width]
|
207
|
+
processor = processor.resize_to_limit(nil, transformations[:height].to_i) if transformations[:height]
|
208
|
+
processor = processor.convert(transformations[:format]) if transformations[:format]
|
209
|
+
|
210
|
+
processor
|
211
|
+
end
|
212
|
+
|
213
|
+
def store_transformed_image(original_relative_path, transformations, processed_image)
|
214
|
+
ext = transformations[:format] ? ".#{transformations[:format]}" : File.extname(original_relative_path)
|
215
|
+
basename = File.basename(original_relative_path, File.extname(original_relative_path))
|
216
|
+
transform_suffix = transformations.map { |k, v| "#{k}#{v}" }.sort.join('-')
|
217
|
+
|
218
|
+
original_relative_dir = File.dirname(original_relative_path)
|
219
|
+
digested_dir = File.join(@digested_path, original_relative_dir)
|
220
|
+
FileUtils.mkdir_p(digested_dir)
|
221
|
+
|
222
|
+
# Process image and save to temporary location
|
223
|
+
temp_result = processed_image.call
|
224
|
+
|
225
|
+
# Calculate digest from processed content
|
226
|
+
digest = generate_digest(temp_result.path)
|
227
|
+
|
228
|
+
# Create final filename with digest
|
229
|
+
new_filename = "#{basename}-#{transform_suffix}-#{digest}#{ext}"
|
230
|
+
new_filepath = File.join(digested_dir, new_filename)
|
231
|
+
|
232
|
+
# Move processed image to final location
|
233
|
+
FileUtils.mv(temp_result.path, new_filepath)
|
234
|
+
|
235
|
+
static_file_dir = File.dirname(new_filepath).sub(@site.source, '').sub(%r{^/}, '')
|
236
|
+
@site.static_files << Jekyll::StaticFile.new(
|
237
|
+
@site,
|
238
|
+
@site.source,
|
239
|
+
static_file_dir,
|
240
|
+
new_filename
|
241
|
+
)
|
242
|
+
|
243
|
+
transform_key = build_transform_key(original_relative_path, transformations)
|
244
|
+
digested_relative_path = File.join(@digest_dir, original_relative_dir, new_filename)
|
245
|
+
@manifest[transform_key] = digested_relative_path
|
246
|
+
|
247
|
+
digested_relative_path
|
248
|
+
end
|
249
|
+
|
250
|
+
def generate_digest_from_content(content)
|
251
|
+
Digest::MD5.hexdigest(content)
|
252
|
+
end
|
253
|
+
|
254
|
+
def rewrite_css_urls(css_file)
|
255
|
+
# Find the original CSS file path from the manifest
|
256
|
+
original_css_path = nil
|
257
|
+
@manifest.each do |key, value|
|
258
|
+
if value == css_file.sub("#{@site.source}/", '')
|
259
|
+
original_css_path = key
|
260
|
+
break
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
return unless original_css_path
|
265
|
+
|
266
|
+
parser = CssParser::Parser.new
|
267
|
+
parser.load_file!(css_file)
|
268
|
+
|
269
|
+
parser.each_rule_set do |rule_set|
|
270
|
+
rule_set.each_declaration do |property, value, _|
|
271
|
+
next unless value.include?('url(')
|
272
|
+
|
273
|
+
updated_value = value.gsub(/url\(['"]?(.*?)['"]?\)/) do |match|
|
274
|
+
asset_path = $1
|
275
|
+
next match if asset_path.start_with?('/') || asset_path.include?('://') || asset_path.start_with?('//')
|
276
|
+
|
277
|
+
# Resolve relative path from original CSS location
|
278
|
+
original_css_dir = File.dirname(original_css_path)
|
279
|
+
resolved_asset_path = File.join(original_css_dir, asset_path)
|
280
|
+
|
281
|
+
# Normalize path (remove ../ and ./)
|
282
|
+
resolved_asset_path = File.expand_path(resolved_asset_path, '/').sub('/', '')
|
283
|
+
|
284
|
+
|
285
|
+
if @manifest.key?(resolved_asset_path)
|
286
|
+
"url(/#{@manifest[resolved_asset_path]})"
|
287
|
+
else
|
288
|
+
match
|
289
|
+
end
|
290
|
+
end
|
291
|
+
rule_set[property] = updated_value
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
File.write(css_file, parser.to_s)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
class SkyhookWatcher
|
300
|
+
def initialize(site)
|
301
|
+
@site = site
|
302
|
+
@skyhook = Skyhook.instance(site)
|
303
|
+
skyhook_config = site.config['skyhook'] || {}
|
304
|
+
@asset_dirs = Array(skyhook_config['asset_dirs'] || ['assets'])
|
305
|
+
end
|
306
|
+
|
307
|
+
def start
|
308
|
+
return if @listener&.listening?
|
309
|
+
return unless @skyhook.should_digest?
|
310
|
+
|
311
|
+
@asset_dirs.each do |asset_dir|
|
312
|
+
assets_dir = File.join(@site.source, asset_dir)
|
313
|
+
next unless Dir.exist?(assets_dir)
|
314
|
+
|
315
|
+
digested_path = @skyhook.instance_variable_get(:@digested_path)
|
316
|
+
|
317
|
+
@listener = Listen.to(assets_dir,
|
318
|
+
ignore: %r{#{Regexp.escape(digested_path)}}) do |modified, added, removed|
|
319
|
+
process_changes(added, modified, removed)
|
320
|
+
end
|
321
|
+
@listener.start
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
private
|
326
|
+
|
327
|
+
def process_changes(added, modified, removed)
|
328
|
+
return if added.empty? && modified.empty? && removed.empty?
|
329
|
+
|
330
|
+
added.each { |f| @skyhook.process_file(f) }
|
331
|
+
modified.each { |f| @skyhook.process_file(f) }
|
332
|
+
removed.each { |f| remove_from_manifest(f) }
|
333
|
+
|
334
|
+
@skyhook.write_manifest
|
335
|
+
puts "Processed #{added.size} new, #{modified.size} modified, #{removed.size} removed assets"
|
336
|
+
end
|
337
|
+
|
338
|
+
def remove_from_manifest(file)
|
339
|
+
original_relative_path = file.sub("#{@site.source}/", '')
|
340
|
+
@skyhook.instance_variable_get(:@manifest).delete(original_relative_path)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
Jekyll::Hooks.register :site, :after_init do |site|
|
346
|
+
skyhook = Jekyll::Skyhook.instance(site)
|
347
|
+
skyhook.process_assets if skyhook.should_digest?
|
348
|
+
end
|
349
|
+
|
350
|
+
Jekyll::Hooks.register :site, :after_reset do |site|
|
351
|
+
if site.config['serving']
|
352
|
+
@skyhook_watcher ||= Jekyll::SkyhookWatcher.new(site).start
|
353
|
+
end
|
354
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-skyhook
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Arclight Automata
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-07-24 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: jekyll
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.7'
|
19
|
+
- - "<"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '5.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '3.7'
|
29
|
+
- - "<"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '5.0'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: listen
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - "~>"
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '3.0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - "~>"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: css_parser
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '1.11'
|
53
|
+
type: :runtime
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '1.11'
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: image_processing
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '1.0'
|
67
|
+
type: :runtime
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '1.0'
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: bundler
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.0'
|
81
|
+
type: :development
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '2.0'
|
88
|
+
- !ruby/object:Gem::Dependency
|
89
|
+
name: rspec
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '3.0'
|
95
|
+
type: :development
|
96
|
+
prerelease: false
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '3.0'
|
102
|
+
email:
|
103
|
+
- root@arclight.run
|
104
|
+
executables: []
|
105
|
+
extensions: []
|
106
|
+
extra_rdoc_files: []
|
107
|
+
files:
|
108
|
+
- lib/jekyll-skyhook.rb
|
109
|
+
- lib/jekyll-skyhook/tags.rb
|
110
|
+
homepage: https://github.com/arclight0/jekyll-skyhook
|
111
|
+
licenses:
|
112
|
+
- MIT
|
113
|
+
metadata: {}
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubygems_version: 3.6.2
|
129
|
+
specification_version: 4
|
130
|
+
summary: Modern asset processing for Jekyll with image transformations
|
131
|
+
test_files: []
|