klenod-build 0.0.2 → 0.0.4
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/lib/klenod/build/errors.rb +1 -0
- data/lib/klenod/build/plugins/asset_javascript_metadata.rb +96 -0
- data/lib/klenod/build/plugins/haml_plugin.rb +18 -9
- data/lib/klenod/build/plugins/image_plugin.rb +33 -24
- data/lib/klenod/build/plugins/svg_plugin.rb +16 -10
- data/lib/klenod/build/plugins/test_plugin.rb +49 -0
- data/lib/klenod/build/plugins.rb +1 -0
- data/lib/klenod/build/test_suite.rb +94 -0
- data/lib/klenod/build/version.rb +1 -1
- data/lib/klenod/build/watcher.rb +3 -2
- data/lib/klenod/build.rb +2 -0
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 560774e128fafb845afbff51f556e3eb30d3a1b7d280b9fbdc7ac36b48c4b741
|
|
4
|
+
data.tar.gz: 559ce8134726f2fa31084811a9dcda01987a957c073370f5ac0595c8d7a3f703
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 77cb936bd77e35262827d9903124f1b37cec73bb11db28c1a5e497d02a20d9283e989a46524c2f674b7c0f49734632e3da02b324a4988dbccbe3fe244e591921
|
|
7
|
+
data.tar.gz: eccc054b80b674a4f304fb6ec2abbdaf3bdf244576dc0f9bfd7b0d23b7b35ac77f5a47a061ad013cd9743295606b7ca3526a50e8f2dff48cc86ba13d4dac6fc2
|
data/lib/klenod/build/errors.rb
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
require_relative "../asset"
|
|
6
|
+
require_relative "../hashing"
|
|
7
|
+
|
|
8
|
+
module Klenod
|
|
9
|
+
module Build
|
|
10
|
+
module Plugins
|
|
11
|
+
module AssetJavaScriptMetadata
|
|
12
|
+
IDENTIFIER_PATTERN = /\A[$A-Z_a-z][$0-9A-Z_a-z]*\z/
|
|
13
|
+
LOGICAL_NAME = "virtual:klenod/asset-metadata.js"
|
|
14
|
+
SOURCE = <<~JAVASCRIPT
|
|
15
|
+
export class ImageBase {
|
|
16
|
+
constructor({ src, width, height, contentType, aspectRatio }) {
|
|
17
|
+
this.src = src;
|
|
18
|
+
this.width = width;
|
|
19
|
+
this.height = height;
|
|
20
|
+
this.contentType = contentType;
|
|
21
|
+
this.aspectRatio = aspectRatio;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class ImageVariant extends ImageBase {
|
|
26
|
+
constructor({ src, width, height, contentType, aspectRatio, format, descriptor, quality }) {
|
|
27
|
+
super({ src, width, height, contentType, aspectRatio });
|
|
28
|
+
this.format = format;
|
|
29
|
+
this.descriptor = descriptor;
|
|
30
|
+
this.quality = quality;
|
|
31
|
+
Object.freeze(this);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default class ImageMetadata extends ImageBase {
|
|
36
|
+
constructor({ src, width, height, contentType, aspectRatio, variants = [] }) {
|
|
37
|
+
super({ src, width, height, contentType, aspectRatio });
|
|
38
|
+
this.variants = Object.freeze([...variants]);
|
|
39
|
+
Object.freeze(this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get srcset() {
|
|
43
|
+
if (this.variants.length === 0) return null;
|
|
44
|
+
return this.variants.map((variant) => `${variant.src} ${variant.descriptor}`).join(", ");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get sizes() {
|
|
48
|
+
const variantWidths = this.variants.map((variant) => variant.width).filter(Boolean);
|
|
49
|
+
const displayWidth = variantWidths.length > 0 ? Math.max(...variantWidths) : this.width;
|
|
50
|
+
return displayWidth ? `(max-width: ${displayWidth}px) 100vw, ${displayWidth}px` : null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export class SvgMetadata extends ImageBase {
|
|
55
|
+
constructor({ src, width, height, contentType, aspectRatio }) {
|
|
56
|
+
super({ src, width, height, contentType, aspectRatio });
|
|
57
|
+
Object.freeze(this);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
JAVASCRIPT
|
|
61
|
+
CONTENT_HASH = Hashing.short(SOURCE)
|
|
62
|
+
OUTPUT_PATH = "/assets/klenod_asset_metadata.#{CONTENT_HASH}.js"
|
|
63
|
+
|
|
64
|
+
module_function
|
|
65
|
+
|
|
66
|
+
def object_literal(properties)
|
|
67
|
+
"{#{properties.map { |key, value| property(key, value) }.join(",")}}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def aspect_ratio(width, height)
|
|
71
|
+
return nil unless width && height && !height.zero?
|
|
72
|
+
|
|
73
|
+
width.to_f / height
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def property(key, value)
|
|
77
|
+
name = key.to_s
|
|
78
|
+
name = JSON.generate(name) unless name.match?(IDENTIFIER_PATTERN)
|
|
79
|
+
"#{name}:#{JSON.generate(value)}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def asset
|
|
83
|
+
Asset.new(
|
|
84
|
+
LOGICAL_NAME,
|
|
85
|
+
CONTENT_HASH,
|
|
86
|
+
OUTPUT_PATH,
|
|
87
|
+
nil,
|
|
88
|
+
SOURCE,
|
|
89
|
+
"application/javascript",
|
|
90
|
+
{type: :javascript, javascript_runtime: :asset_metadata}
|
|
91
|
+
)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -30,6 +30,7 @@ module Klenod
|
|
|
30
30
|
DEFAULT_FACTORY = ComponentDefaults::DEFAULT_FACTORY
|
|
31
31
|
DEFAULT_COMPONENT_CHILDREN = :eager
|
|
32
32
|
COMPONENT_CHILDREN_MODES = %i[eager lazy].freeze
|
|
33
|
+
I18N_OPTIONS = %i[class constant].freeze
|
|
33
34
|
HAML_HELPER_SPECIFIER = "virtual:klenod/haml_helper"
|
|
34
35
|
HAML_HELPER_MODULE_ID = ModuleId.new("#{HAML_HELPER_SPECIFIER}.rb", nil)
|
|
35
36
|
STATIC_CLASS_SOURCE_PATTERN = /^[ \t]*(?:%[-:\w]+)?(?:#[\w-]+)?\.[\w-]|[({][^)}\n]*\bclass\s*=/m
|
|
@@ -47,6 +48,7 @@ module Klenod
|
|
|
47
48
|
DEFAULT_FACTORY = HamlPlugin::DEFAULT_FACTORY
|
|
48
49
|
DEFAULT_COMPONENT_CHILDREN = HamlPlugin::DEFAULT_COMPONENT_CHILDREN
|
|
49
50
|
COMPONENT_CHILDREN_MODES = HamlPlugin::COMPONENT_CHILDREN_MODES
|
|
51
|
+
I18N_OPTIONS = HamlPlugin::I18N_OPTIONS
|
|
50
52
|
HAML_HELPER_SPECIFIER = HamlPlugin::HAML_HELPER_SPECIFIER
|
|
51
53
|
HAML_HELPER_MODULE_ID = HamlPlugin::HAML_HELPER_MODULE_ID
|
|
52
54
|
STATIC_CLASS_SOURCE_PATTERN = HamlPlugin::STATIC_CLASS_SOURCE_PATTERN
|
|
@@ -64,15 +66,14 @@ module Klenod
|
|
|
64
66
|
factory: DEFAULT_FACTORY,
|
|
65
67
|
component_children: DEFAULT_COMPONENT_CHILDREN,
|
|
66
68
|
variables: nil,
|
|
67
|
-
|
|
68
|
-
i18n_constant: nil,
|
|
69
|
+
i18n: nil,
|
|
69
70
|
cache_static_subtrees: false
|
|
70
71
|
)
|
|
71
72
|
@component_base_class = component_base_class
|
|
72
73
|
@factory = factory
|
|
73
74
|
@component_children = validate_component_children(component_children)
|
|
74
75
|
@variables = validate_variables(variables)
|
|
75
|
-
@i18n_class, @i18n_constant = validate_i18n_options(
|
|
76
|
+
@i18n_class, @i18n_constant = validate_i18n_options(i18n)
|
|
76
77
|
@cache_static_subtrees = cache_static_subtrees
|
|
77
78
|
@transformer = Transformer.new
|
|
78
79
|
end
|
|
@@ -296,15 +297,23 @@ module Klenod
|
|
|
296
297
|
raise ArgumentError, "variables[#{kind.inspect}] must be a Ruby expression"
|
|
297
298
|
end
|
|
298
299
|
|
|
299
|
-
def validate_i18n_options(
|
|
300
|
-
return [nil, nil] if
|
|
301
|
-
raise ArgumentError, "
|
|
300
|
+
def validate_i18n_options(i18n)
|
|
301
|
+
return [nil, nil] if i18n.nil?
|
|
302
|
+
raise ArgumentError, "i18n must be a Hash" unless i18n.is_a?(Hash)
|
|
302
303
|
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
unknown_options = i18n.keys - I18N_OPTIONS
|
|
305
|
+
unless unknown_options.empty?
|
|
306
|
+
raise ArgumentError, "i18n contains unknown options: #{unknown_options.map(&:inspect).join(", ")}"
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
i18n_class = i18n[:class]
|
|
310
|
+
raise ArgumentError, "i18n[:class] must be configured" if i18n_class.nil?
|
|
311
|
+
|
|
312
|
+
i18n_constant = (i18n[:constant] || "I18n").to_s
|
|
313
|
+
raise ArgumentError, "i18n[:constant] must be a Ruby constant name" unless i18n_constant.match?(/\A[A-Z]\w*\z/)
|
|
305
314
|
|
|
306
315
|
[
|
|
307
|
-
Transformer::ConstPath.parse(i18n_class, name: "
|
|
316
|
+
Transformer::ConstPath.parse(i18n_class, name: "i18n[:class]").to_s,
|
|
308
317
|
i18n_constant
|
|
309
318
|
]
|
|
310
319
|
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "image_size"
|
|
4
|
-
require "json"
|
|
5
4
|
require "rmagick"
|
|
6
5
|
require "uri"
|
|
7
6
|
|
|
@@ -11,6 +10,7 @@ require_relative "../hashing"
|
|
|
11
10
|
require_relative "../load_result"
|
|
12
11
|
require_relative "../plugin"
|
|
13
12
|
require_relative "../transform_result"
|
|
13
|
+
require_relative "asset_javascript_metadata"
|
|
14
14
|
|
|
15
15
|
module Klenod
|
|
16
16
|
module Build
|
|
@@ -52,7 +52,7 @@ module Klenod
|
|
|
52
52
|
asset = default_image_asset(module_id, source_path, source_hash, dimensions, image_options, context.asset_generation_queue)
|
|
53
53
|
variant_assets = generate_variant_assets(module_id, source_path, source_hash, dimensions, image_options, context.asset_generation_queue)
|
|
54
54
|
javascript_asset = javascript_image_asset(module_id, asset, variant_assets)
|
|
55
|
-
assets = [asset, *variant_assets
|
|
55
|
+
assets = [asset, *variant_assets]
|
|
56
56
|
image_runtime_dependency =
|
|
57
57
|
Dependency
|
|
58
58
|
.create(
|
|
@@ -69,7 +69,7 @@ module Klenod
|
|
|
69
69
|
nil,
|
|
70
70
|
assets,
|
|
71
71
|
[],
|
|
72
|
-
{
|
|
72
|
+
{image_javascript_asset: javascript_asset}
|
|
73
73
|
)
|
|
74
74
|
LoadResult.new(
|
|
75
75
|
image_source(module_id),
|
|
@@ -185,8 +185,14 @@ module Klenod
|
|
|
185
185
|
|
|
186
186
|
def image_runtime_source
|
|
187
187
|
<<~RUBY
|
|
188
|
-
|
|
189
|
-
Data.define(:src, :width, :height, :content_type, :variants) do
|
|
188
|
+
ImageMetadata =
|
|
189
|
+
Data.define(:src, :width, :height, :content_type, :aspect_ratio, :variants) do
|
|
190
|
+
def initialize(src:, width:, height:, content_type:, aspect_ratio:, variants:)
|
|
191
|
+
super(src:, width:, height:, content_type:, aspect_ratio:, variants: variants.dup.freeze)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
alias to_s src
|
|
195
|
+
|
|
190
196
|
def srcset
|
|
191
197
|
return nil if variants.empty?
|
|
192
198
|
|
|
@@ -201,7 +207,9 @@ module Klenod
|
|
|
201
207
|
end
|
|
202
208
|
end
|
|
203
209
|
|
|
204
|
-
ImageVariant = Data.define(:src, :width, :height, :content_type, :format, :descriptor, :
|
|
210
|
+
ImageVariant = Data.define(:src, :width, :height, :content_type, :aspect_ratio, :format, :descriptor, :quality) do
|
|
211
|
+
alias to_s src
|
|
212
|
+
end
|
|
205
213
|
RUBY
|
|
206
214
|
end
|
|
207
215
|
|
|
@@ -216,11 +224,12 @@ module Klenod
|
|
|
216
224
|
<<~RUBY
|
|
217
225
|
ImageRuntime = __klenod_import__(#{image_runtime_dependency.id.inspect})
|
|
218
226
|
Default =
|
|
219
|
-
ImageRuntime::
|
|
227
|
+
ImageRuntime::ImageMetadata.new(
|
|
220
228
|
src: #{asset.output_path.inspect},
|
|
221
229
|
width: #{asset.metadata[:width].inspect},
|
|
222
230
|
height: #{asset.metadata[:height].inspect},
|
|
223
231
|
content_type: #{asset.content_type.inspect},
|
|
232
|
+
aspect_ratio: #{AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height]).inspect},
|
|
224
233
|
variants: [
|
|
225
234
|
#{variants.join(",\n ")}
|
|
226
235
|
]
|
|
@@ -236,9 +245,10 @@ module Klenod
|
|
|
236
245
|
width: #{asset.metadata[:width].inspect},
|
|
237
246
|
height: #{asset.metadata[:height].inspect},
|
|
238
247
|
content_type: #{asset.content_type.inspect},
|
|
248
|
+
aspect_ratio: #{AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height]).inspect},
|
|
239
249
|
format: #{asset.metadata[:format].inspect},
|
|
240
250
|
descriptor: #{asset.metadata[:descriptor].inspect},
|
|
241
|
-
|
|
251
|
+
quality: #{asset.metadata[:quality].inspect}
|
|
242
252
|
)
|
|
243
253
|
RUBY
|
|
244
254
|
end
|
|
@@ -249,7 +259,7 @@ module Klenod
|
|
|
249
259
|
Asset.new(
|
|
250
260
|
module_id.path,
|
|
251
261
|
hash,
|
|
252
|
-
"#{
|
|
262
|
+
"/assets/#{asset_name(module_id)}.#{hash}#{module_id.extname}.js",
|
|
253
263
|
nil,
|
|
254
264
|
code,
|
|
255
265
|
"application/javascript",
|
|
@@ -258,21 +268,20 @@ module Klenod
|
|
|
258
268
|
end
|
|
259
269
|
|
|
260
270
|
def javascript_image_module_source(asset, variant_assets)
|
|
261
|
-
variants = variant_assets.map {
|
|
262
|
-
|
|
263
|
-
display_width = variant_assets.filter_map { it.metadata[:width] }.max || asset.metadata[:width]
|
|
264
|
-
sizes = display_width ? "(max-width: #{display_width}px) 100vw, #{display_width}px" : nil
|
|
265
|
-
metadata = {
|
|
271
|
+
variants = variant_assets.map { "new ImageVariant(#{AssetJavaScriptMetadata.object_literal(javascript_image_variant(it))})" }
|
|
272
|
+
properties = {
|
|
266
273
|
src: asset.output_path,
|
|
267
274
|
width: asset.metadata[:width],
|
|
268
275
|
height: asset.metadata[:height],
|
|
269
276
|
contentType: asset.content_type,
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
aspectRatio: AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height])
|
|
278
|
+
}.map { |key, value| AssetJavaScriptMetadata.property(key, value) }
|
|
279
|
+
properties << %(variants:[#{variants.join(",")}])
|
|
280
|
+
|
|
281
|
+
<<~JAVASCRIPT
|
|
282
|
+
import ImageMetadata, { ImageVariant } from #{AssetJavaScriptMetadata::OUTPUT_PATH.inspect};
|
|
283
|
+
export default new ImageMetadata({#{properties.join(",")}});
|
|
284
|
+
JAVASCRIPT
|
|
276
285
|
end
|
|
277
286
|
|
|
278
287
|
def javascript_image_variant(asset)
|
|
@@ -281,10 +290,11 @@ module Klenod
|
|
|
281
290
|
width: asset.metadata[:width],
|
|
282
291
|
height: asset.metadata[:height],
|
|
283
292
|
contentType: asset.content_type,
|
|
293
|
+
aspectRatio: AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height]),
|
|
284
294
|
format: asset.metadata[:format],
|
|
285
295
|
descriptor: asset.metadata[:descriptor],
|
|
286
|
-
|
|
287
|
-
}
|
|
296
|
+
quality: asset.metadata[:quality]
|
|
297
|
+
}.compact
|
|
288
298
|
end
|
|
289
299
|
|
|
290
300
|
def image_asset(assets)
|
|
@@ -334,8 +344,7 @@ module Klenod
|
|
|
334
344
|
width: width,
|
|
335
345
|
height: scaled_height(dimensions, width),
|
|
336
346
|
format: format.to_sym,
|
|
337
|
-
descriptor: descriptor
|
|
338
|
-
source_width: width
|
|
347
|
+
descriptor: descriptor
|
|
339
348
|
}
|
|
340
349
|
metadata[:quality] = quality if quality
|
|
341
350
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "json"
|
|
4
|
-
|
|
5
3
|
require_relative "../asset"
|
|
6
4
|
require_relative "../dependency"
|
|
7
5
|
require_relative "../errors"
|
|
@@ -9,6 +7,7 @@ require_relative "../hashing"
|
|
|
9
7
|
require_relative "../module_id"
|
|
10
8
|
require_relative "../plugin"
|
|
11
9
|
require_relative "../transform_result"
|
|
10
|
+
require_relative "asset_javascript_metadata"
|
|
12
11
|
|
|
13
12
|
module Klenod
|
|
14
13
|
module Build
|
|
@@ -71,9 +70,9 @@ module Klenod
|
|
|
71
70
|
svg_module_source(asset, svg_runtime_dependency),
|
|
72
71
|
[svg_runtime_dependency],
|
|
73
72
|
nil,
|
|
74
|
-
[asset
|
|
73
|
+
[asset],
|
|
75
74
|
[],
|
|
76
|
-
{asset_bytes: code,
|
|
75
|
+
{asset_bytes: code, svg_javascript_asset: javascript_asset}
|
|
77
76
|
)
|
|
78
77
|
end
|
|
79
78
|
|
|
@@ -95,7 +94,9 @@ module Klenod
|
|
|
95
94
|
|
|
96
95
|
def svg_runtime_source
|
|
97
96
|
<<~RUBY
|
|
98
|
-
|
|
97
|
+
SvgMetadata = Data.define(:src, :width, :height, :content_type, :aspect_ratio) do
|
|
98
|
+
alias to_s src
|
|
99
|
+
end
|
|
99
100
|
RUBY
|
|
100
101
|
end
|
|
101
102
|
|
|
@@ -103,11 +104,12 @@ module Klenod
|
|
|
103
104
|
<<~RUBY
|
|
104
105
|
SvgRuntime = __klenod_import__(#{svg_runtime_dependency.id.inspect})
|
|
105
106
|
Default =
|
|
106
|
-
SvgRuntime::
|
|
107
|
+
SvgRuntime::SvgMetadata.new(
|
|
107
108
|
src: #{asset.output_path.inspect},
|
|
108
109
|
width: #{asset.metadata[:width].inspect},
|
|
109
110
|
height: #{asset.metadata[:height].inspect},
|
|
110
|
-
content_type: #{asset.content_type.inspect}
|
|
111
|
+
content_type: #{asset.content_type.inspect},
|
|
112
|
+
aspect_ratio: #{AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height]).inspect}
|
|
111
113
|
)
|
|
112
114
|
|
|
113
115
|
RUBY
|
|
@@ -119,7 +121,7 @@ module Klenod
|
|
|
119
121
|
Asset.new(
|
|
120
122
|
module_id.path,
|
|
121
123
|
hash,
|
|
122
|
-
"#{
|
|
124
|
+
"/assets/#{asset_name(module_id)}.#{hash}#{module_id.extname}.js",
|
|
123
125
|
nil,
|
|
124
126
|
code,
|
|
125
127
|
"application/javascript",
|
|
@@ -132,10 +134,14 @@ module Klenod
|
|
|
132
134
|
src: asset.output_path,
|
|
133
135
|
width: asset.metadata[:width],
|
|
134
136
|
height: asset.metadata[:height],
|
|
135
|
-
contentType: asset.content_type
|
|
137
|
+
contentType: asset.content_type,
|
|
138
|
+
aspectRatio: AssetJavaScriptMetadata.aspect_ratio(asset.metadata[:width], asset.metadata[:height])
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
<<~JAVASCRIPT
|
|
142
|
+
import { SvgMetadata } from #{AssetJavaScriptMetadata::OUTPUT_PATH.inspect};
|
|
143
|
+
export default new SvgMetadata(#{AssetJavaScriptMetadata.object_literal(metadata)});
|
|
144
|
+
JAVASCRIPT
|
|
139
145
|
end
|
|
140
146
|
|
|
141
147
|
def svg_dimensions(code)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../errors"
|
|
4
|
+
require_relative "../module_id"
|
|
5
|
+
require_relative "../plugin"
|
|
6
|
+
|
|
7
|
+
module Klenod
|
|
8
|
+
module Build
|
|
9
|
+
module Plugins
|
|
10
|
+
module TestPlugin
|
|
11
|
+
def self.new(...)
|
|
12
|
+
Plugin.new(...)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Plugin < Klenod::Build::Plugin
|
|
16
|
+
DEFAULT_PATTERN = "**/*.test.rb"
|
|
17
|
+
|
|
18
|
+
def initialize(pattern: DEFAULT_PATTERN)
|
|
19
|
+
@pattern = pattern.to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :pattern
|
|
23
|
+
|
|
24
|
+
def discover(source_dir:)
|
|
25
|
+
root = Pathname.new(source_dir).expand_path
|
|
26
|
+
|
|
27
|
+
root
|
|
28
|
+
.glob(pattern)
|
|
29
|
+
.select(&:file?)
|
|
30
|
+
.map { |path| ModuleId.new(path.relative_path_from(root).to_s.tr("\\", "/"), nil) }
|
|
31
|
+
.sort_by(&:to_s)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_module_id?(module_id)
|
|
35
|
+
module_id.scheme == :app && File.fnmatch?(pattern, module_id.relative_path, File::FNM_PATHNAME | File::FNM_EXTGLOB)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def finalize(module_id, result, resolved_dependencies, _dependency_records, _context)
|
|
39
|
+
imported_test = resolved_dependencies.find { |dependency| test_module_id?(dependency.module_id) }
|
|
40
|
+
return result unless imported_test
|
|
41
|
+
|
|
42
|
+
error = TestImportError.new("Test file #{imported_test.module_id.path.inspect} cannot be imported")
|
|
43
|
+
raise error.with_resolution_context(dependency: imported_test.dependency, importer_id: module_id)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/klenod/build/plugins.rb
CHANGED
|
@@ -17,6 +17,7 @@ module Klenod
|
|
|
17
17
|
autoload :TomlPlugin, File.expand_path("plugins/data_plugin", __dir__)
|
|
18
18
|
autoload :TextPlugin, File.expand_path("plugins/data_plugin", __dir__)
|
|
19
19
|
autoload :RouterPlugin, File.expand_path("plugins/router_plugin", __dir__)
|
|
20
|
+
autoload :TestPlugin, File.expand_path("plugins/test_plugin", __dir__)
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Klenod
|
|
4
|
+
module Build
|
|
5
|
+
TestSelection = Data.define(:test_paths, :removed_test_paths) do
|
|
6
|
+
def empty?
|
|
7
|
+
test_paths.empty? && removed_test_paths.empty?
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class TestSuite
|
|
12
|
+
def initialize(context:, plugin:)
|
|
13
|
+
@context = context
|
|
14
|
+
@plugin = plugin
|
|
15
|
+
@dependency_ids = {}
|
|
16
|
+
@failed_test_ids = Set.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def collect
|
|
20
|
+
test_ids = discover
|
|
21
|
+
test_ids.each { |test_id| index(test_id) }
|
|
22
|
+
selection(test_ids)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def update(event)
|
|
26
|
+
previous_test_ids = @dependency_ids.keys.to_set
|
|
27
|
+
current_test_ids = discover.to_set
|
|
28
|
+
removed_test_ids = previous_test_ids - current_test_ids
|
|
29
|
+
added_test_ids = current_test_ids - previous_test_ids
|
|
30
|
+
affected_module_ids = update_module_ids(event)
|
|
31
|
+
affected_test_ids =
|
|
32
|
+
@dependency_ids.filter_map do |test_id, dependency_ids|
|
|
33
|
+
test_id if current_test_ids.include?(test_id) && dependency_ids.intersect?(affected_module_ids)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
test_ids = (added_test_ids + affected_test_ids + @failed_test_ids).select { |test_id| current_test_ids.include?(test_id) }
|
|
37
|
+
removed_test_ids.each { |test_id| remove(test_id) }
|
|
38
|
+
test_ids.each { |test_id| index(test_id) }
|
|
39
|
+
|
|
40
|
+
selection(test_ids, removed_test_ids)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
attr_reader :context, :plugin
|
|
46
|
+
|
|
47
|
+
def discover
|
|
48
|
+
plugin.discover(source_dir: context.graph.source_dir)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def index(test_id)
|
|
52
|
+
dependency_ids = Set[test_id]
|
|
53
|
+
@dependency_ids[test_id] = dependency_ids
|
|
54
|
+
collect_dependencies(test_id, dependency_ids)
|
|
55
|
+
@failed_test_ids.delete(test_id)
|
|
56
|
+
rescue
|
|
57
|
+
@failed_test_ids << test_id
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def collect_dependencies(module_id, dependency_ids)
|
|
61
|
+
record = context.graph.records[module_id] || context.graph.collect_module(module_id)
|
|
62
|
+
|
|
63
|
+
record.resolved_dependencies.each do |dependency|
|
|
64
|
+
dependency_id = dependency.module_id
|
|
65
|
+
next unless dependency_ids.add?(dependency_id)
|
|
66
|
+
|
|
67
|
+
collect_dependencies(dependency_id, dependency_ids)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def remove(test_id)
|
|
72
|
+
@dependency_ids.delete(test_id)
|
|
73
|
+
@failed_test_ids.delete(test_id)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def update_module_ids(event)
|
|
77
|
+
result = event.result
|
|
78
|
+
Set.new(
|
|
79
|
+
result.changed_module_ids +
|
|
80
|
+
result.removed_module_ids +
|
|
81
|
+
result.reloaded_module_ids +
|
|
82
|
+
result.reevaluated_module_ids
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def selection(test_ids, removed_test_ids = [])
|
|
87
|
+
TestSelection.new(
|
|
88
|
+
test_ids.map(&:relative_path).sort.freeze,
|
|
89
|
+
removed_test_ids.map(&:relative_path).sort.freeze
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/klenod/build/version.rb
CHANGED
data/lib/klenod/build/watcher.rb
CHANGED
|
@@ -53,10 +53,11 @@ module Klenod
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def initialize(source_dir:, context:, debounce_interval: 0.1)
|
|
56
|
+
def initialize(source_dir:, context:, debounce_interval: 0.1, ignore: nil)
|
|
57
57
|
@source_dir = source_dir
|
|
58
58
|
@context = context
|
|
59
59
|
@debounce_interval = debounce_interval
|
|
60
|
+
@ignore = ignore
|
|
60
61
|
@graph_version = 0
|
|
61
62
|
@pending_changes = PendingChanges.new
|
|
62
63
|
@pending_mutex = Mutex.new
|
|
@@ -68,7 +69,7 @@ module Klenod
|
|
|
68
69
|
def start
|
|
69
70
|
@worker_thread = Thread.new { process_pending_updates }
|
|
70
71
|
@listener =
|
|
71
|
-
Listen.to(@source_dir) do |modified, added, removed|
|
|
72
|
+
Listen.to(@source_dir, ignore: @ignore) do |modified, added, removed|
|
|
72
73
|
enqueue_update(modified + added, removed)
|
|
73
74
|
end
|
|
74
75
|
@listener.start
|
data/lib/klenod/build.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Klenod
|
|
|
9
9
|
|
|
10
10
|
module Build
|
|
11
11
|
autoload :Graphviz, "klenod/build/graphviz"
|
|
12
|
+
autoload :TestSelection, "klenod/build/test_suite"
|
|
13
|
+
autoload :TestSuite, "klenod/build/test_suite"
|
|
12
14
|
autoload :UpdateEvent, "klenod/build/watcher"
|
|
13
15
|
autoload :Watcher, "klenod/build/watcher"
|
|
14
16
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: klenod-build
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrés Alin
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.0.
|
|
18
|
+
version: 0.0.4
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.0.
|
|
25
|
+
version: 0.0.4
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: async
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -249,6 +249,7 @@ files:
|
|
|
249
249
|
- lib/klenod/build/module_record.rb
|
|
250
250
|
- lib/klenod/build/plugin.rb
|
|
251
251
|
- lib/klenod/build/plugins.rb
|
|
252
|
+
- lib/klenod/build/plugins/asset_javascript_metadata.rb
|
|
252
253
|
- lib/klenod/build/plugins/class_names_runtime.rb
|
|
253
254
|
- lib/klenod/build/plugins/component_defaults.rb
|
|
254
255
|
- lib/klenod/build/plugins/data_plugin.rb
|
|
@@ -271,6 +272,7 @@ files:
|
|
|
271
272
|
- lib/klenod/build/plugins/router_plugin.rb
|
|
272
273
|
- lib/klenod/build/plugins/ruby_plugin.rb
|
|
273
274
|
- lib/klenod/build/plugins/svg_plugin.rb
|
|
275
|
+
- lib/klenod/build/plugins/test_plugin.rb
|
|
274
276
|
- lib/klenod/build/profiler.rb
|
|
275
277
|
- lib/klenod/build/resolution_error_formatter.rb
|
|
276
278
|
- lib/klenod/build/resolver.rb
|
|
@@ -279,6 +281,7 @@ files:
|
|
|
279
281
|
- lib/klenod/build/source_map/editor.rb
|
|
280
282
|
- lib/klenod/build/source_map/map.rb
|
|
281
283
|
- lib/klenod/build/source_map/vlq.rb
|
|
284
|
+
- lib/klenod/build/test_suite.rb
|
|
282
285
|
- lib/klenod/build/transform_result.rb
|
|
283
286
|
- lib/klenod/build/version.rb
|
|
284
287
|
- lib/klenod/build/watched_pattern.rb
|