klenod-build 0.0.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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +29 -0
  3. data/lib/klenod/build/asset.rb +235 -0
  4. data/lib/klenod/build/asset_compression.rb +58 -0
  5. data/lib/klenod/build/asset_generation_queue.rb +50 -0
  6. data/lib/klenod/build/cli/application.rb +108 -0
  7. data/lib/klenod/build/cli.rb +3 -0
  8. data/lib/klenod/build/config.rb +142 -0
  9. data/lib/klenod/build/context.rb +370 -0
  10. data/lib/klenod/build/dependency.rb +21 -0
  11. data/lib/klenod/build/errors.rb +175 -0
  12. data/lib/klenod/build/filesystem_resolver.rb +147 -0
  13. data/lib/klenod/build/graph/invalidator.rb +246 -0
  14. data/lib/klenod/build/graph.rb +864 -0
  15. data/lib/klenod/build/graphviz.rb +282 -0
  16. data/lib/klenod/build/hashing.rb +21 -0
  17. data/lib/klenod/build/invalidation_result.rb +55 -0
  18. data/lib/klenod/build/load_result.rb +7 -0
  19. data/lib/klenod/build/loaded_module.rb +38 -0
  20. data/lib/klenod/build/module_id.rb +100 -0
  21. data/lib/klenod/build/module_record.rb +22 -0
  22. data/lib/klenod/build/plugin.rb +35 -0
  23. data/lib/klenod/build/plugins/class_names_runtime.rb +125 -0
  24. data/lib/klenod/build/plugins/component_defaults.rb +12 -0
  25. data/lib/klenod/build/plugins/data_plugin.rb +117 -0
  26. data/lib/klenod/build/plugins/gem_import_plugin.rb +125 -0
  27. data/lib/klenod/build/plugins/google_fonts_plugin/font_metrics.json +17687 -0
  28. data/lib/klenod/build/plugins/google_fonts_plugin/font_metrics.rb +105 -0
  29. data/lib/klenod/build/plugins/google_fonts_plugin/font_metrics.txt +31 -0
  30. data/lib/klenod/build/plugins/google_fonts_plugin.rb +387 -0
  31. data/lib/klenod/build/plugins/haml_plugin/companions.rb +40 -0
  32. data/lib/klenod/build/plugins/haml_plugin/errors.rb +114 -0
  33. data/lib/klenod/build/plugins/haml_plugin/helper_source.rb +123 -0
  34. data/lib/klenod/build/plugins/haml_plugin/parser.rb +85 -0
  35. data/lib/klenod/build/plugins/haml_plugin/transformer/ruby_builder.rb +1039 -0
  36. data/lib/klenod/build/plugins/haml_plugin/transformer.rb +766 -0
  37. data/lib/klenod/build/plugins/haml_plugin.rb +369 -0
  38. data/lib/klenod/build/plugins/image_plugin.rb +401 -0
  39. data/lib/klenod/build/plugins/intl_plugin.rb +34 -0
  40. data/lib/klenod/build/plugins/markdown_compiler.rb +180 -0
  41. data/lib/klenod/build/plugins/markdown_plugin.rb +161 -0
  42. data/lib/klenod/build/plugins/router_plugin.rb +942 -0
  43. data/lib/klenod/build/plugins/ruby_plugin.rb +34 -0
  44. data/lib/klenod/build/plugins/svg_plugin.rb +197 -0
  45. data/lib/klenod/build/plugins.rb +22 -0
  46. data/lib/klenod/build/profiler.rb +79 -0
  47. data/lib/klenod/build/resolution_error_formatter.rb +42 -0
  48. data/lib/klenod/build/resolver.rb +95 -0
  49. data/lib/klenod/build/ruby_import_rewriter.rb +436 -0
  50. data/lib/klenod/build/source_map/editor.rb +110 -0
  51. data/lib/klenod/build/source_map/map.rb +168 -0
  52. data/lib/klenod/build/source_map/vlq.rb +68 -0
  53. data/lib/klenod/build/source_map.rb +12 -0
  54. data/lib/klenod/build/transform_result.rb +12 -0
  55. data/lib/klenod/build/version.rb +7 -0
  56. data/lib/klenod/build/watched_pattern.rb +11 -0
  57. data/lib/klenod/build/watcher.rb +141 -0
  58. data/lib/klenod/build.rb +15 -0
  59. metadata +310 -0
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Klenod
4
+ module Build
5
+ class Graph
6
+ class Invalidator
7
+ def initialize(graph, resolver, source_loader:)
8
+ @graph = graph
9
+ @resolver = resolver
10
+ @source_loader = source_loader
11
+ end
12
+
13
+ def invalidate_paths(changed_paths, removed_paths: [])
14
+ @resolver.clear_cache
15
+ previous_assets = graph.assets
16
+ evaluated_module_ids = mods.keys
17
+ changed_module_ids = module_ids_for_paths(changed_paths)
18
+ removed_module_ids = module_ids_for_paths(removed_paths)
19
+ failed_retry_ids = failed_module_ids_for_created_paths(changed_paths)
20
+ pattern_owner_ids = module_ids_for_watched_paths(changed_paths + removed_paths)
21
+ plugin_owner_ids = plugin_invalidated_module_ids(changed_paths + removed_paths)
22
+ reload_module_ids = (changed_module_ids + failed_retry_ids + pattern_owner_ids + plugin_owner_ids).uniq
23
+ affected_dependents = dependent_closure(reload_module_ids + removed_module_ids)
24
+ errors = []
25
+
26
+ removed_module_ids.each do |module_id|
27
+ records.delete(module_id)
28
+ mods.delete(module_id)
29
+ end
30
+
31
+ reloaded_module_ids =
32
+ reload_module_ids.filter_map do |module_id|
33
+ if evaluated_module_ids.include?(module_id)
34
+ graph.load_module(module_id, force: true)
35
+ else
36
+ graph.collect_module(module_id, force: true)
37
+ end
38
+ module_id
39
+ rescue => e
40
+ mark_module_failed(module_id, e)
41
+ errors << [module_id, e]
42
+ nil
43
+ end
44
+ failed_reload_ids = errors.map(&:first) & reload_module_ids
45
+ blocked_dependent_ids = dependent_closure(failed_reload_ids)
46
+ blocked_dependent_ids.each { |module_id| mods.delete(module_id) }
47
+
48
+ reevaluated_module_ids =
49
+ affected_dependents.filter_map do |module_id|
50
+ next if removed_module_ids.include?(module_id)
51
+ next if reload_module_ids.include?(module_id)
52
+ next if blocked_dependent_ids.include?(module_id)
53
+
54
+ if evaluated_module_ids.include?(module_id)
55
+ graph.load_module(module_id, reevaluate: true)
56
+ module_id
57
+ else
58
+ graph.collect_module(module_id, force: true)
59
+ nil
60
+ end
61
+ rescue => e
62
+ errors << [module_id, e]
63
+ nil
64
+ end
65
+ asset_updates = diff_assets(previous_assets, graph.assets)
66
+ asset_changes = asset_changes_for(asset_updates)
67
+
68
+ InvalidationResult.new(
69
+ changed_module_ids.freeze,
70
+ removed_module_ids.freeze,
71
+ reloaded_module_ids.freeze,
72
+ reevaluated_module_ids.freeze,
73
+ asset_changes.added.freeze,
74
+ asset_changes.changed.freeze,
75
+ asset_changes.removed.freeze,
76
+ asset_updates.freeze,
77
+ errors.freeze
78
+ )
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :graph, :resolver, :source_loader
84
+
85
+ def records
86
+ graph.records
87
+ end
88
+
89
+ def mods
90
+ graph.mods
91
+ end
92
+
93
+ def module_ids_for_paths(paths)
94
+ paths
95
+ .flat_map { |path| module_ids_for_path(path) }
96
+ .select { |module_id| records.key?(module_id) }
97
+ .uniq
98
+ end
99
+
100
+ def module_ids_for_watched_paths(paths)
101
+ relative_paths =
102
+ paths.filter_map do |path|
103
+ Pathname.new(path).expand_path.relative_path_from(resolver.source_dir).to_s
104
+ rescue ArgumentError
105
+ nil
106
+ end
107
+
108
+ records.filter_map do |module_id, record|
109
+ module_id if relative_paths.any? { |path| record.watched_patterns.any? { |pattern| pattern.match?(path) } }
110
+ end
111
+ end
112
+
113
+ def failed_module_ids_for_created_paths(paths)
114
+ relative_paths = relative_paths_for(paths)
115
+ return [] if relative_paths.empty?
116
+
117
+ records.filter_map do |module_id, record|
118
+ next unless record.status == :failed
119
+
120
+ unresolved_path = record.metadata.fetch(:error).unresolved_path if record.metadata.fetch(:error).respond_to?(:unresolved_path)
121
+ module_id if unresolved_path && relative_paths.any? { |path| path_satisfies_unresolved_path?(path, unresolved_path) }
122
+ end
123
+ end
124
+
125
+ def relative_paths_for(paths)
126
+ paths.filter_map do |path|
127
+ Pathname.new(path).expand_path.relative_path_from(resolver.source_dir).to_s
128
+ rescue ArgumentError
129
+ nil
130
+ end
131
+ end
132
+
133
+ def path_satisfies_unresolved_path?(path, unresolved_path)
134
+ path == unresolved_path || path.start_with?("#{unresolved_path}.")
135
+ end
136
+
137
+ def plugin_invalidated_module_ids(paths)
138
+ graph.plugins
139
+ .flat_map { |plugin| plugin.invalidate_module_ids(paths, graph) }
140
+ .uniq
141
+ .select { |module_id| graph_relevant_module_id?(module_id) }
142
+ end
143
+
144
+ def graph_relevant_module_id?(module_id)
145
+ records.key?(module_id) || records.any? do |_candidate_id, record|
146
+ record.resolved_dependencies.any? { |dependency| dependency.module_id == module_id }
147
+ end
148
+ end
149
+
150
+ def module_ids_for_path(path)
151
+ absolute_path = Pathname.new(path).expand_path
152
+ relative = absolute_path.relative_path_from(resolver.source_dir).to_s
153
+
154
+ records.each_key.select { |module_id| module_id.path == relative }
155
+ rescue ArgumentError
156
+ []
157
+ end
158
+
159
+ def dependent_closure(module_ids)
160
+ seen = Set.new
161
+ queue = module_ids.dup
162
+
163
+ until queue.empty?
164
+ module_id = queue.shift
165
+
166
+ direct_dependents(module_id).each do |dependent_id|
167
+ next if seen.include?(dependent_id)
168
+
169
+ seen << dependent_id
170
+ queue << dependent_id
171
+ end
172
+ end
173
+
174
+ seen.to_a
175
+ end
176
+
177
+ def direct_dependents(module_id)
178
+ records.filter_map do |candidate_id, record|
179
+ candidate_id if record.resolved_dependencies.any? { |dependency| dependency.module_id == module_id }
180
+ end
181
+ end
182
+
183
+ def mark_module_failed(module_id, error)
184
+ cached = records[module_id]
185
+ loaded_source =
186
+ begin
187
+ source_loader.call(module_id)
188
+ rescue
189
+ LoadResult.new(cached&.source || "", nil, nil)
190
+ end
191
+ source = loaded_source.source
192
+ source_hash = loaded_source.source_hash || Hashing.hexdigest(source)
193
+ version = cached ? cached.version + 1 : 0
194
+
195
+ records[module_id] =
196
+ ModuleRecord.new(
197
+ module_id,
198
+ source_hash,
199
+ "",
200
+ [],
201
+ [],
202
+ source,
203
+ "",
204
+ nil,
205
+ [],
206
+ [],
207
+ {error: error},
208
+ version,
209
+ :failed
210
+ )
211
+ mods[module_id] = FailedModule.new(error)
212
+ end
213
+
214
+ def diff_assets(previous_assets, current_assets)
215
+ previous_paths = previous_assets.keys
216
+ current_paths = current_assets.keys
217
+ shared_paths = previous_paths & current_paths
218
+
219
+ [
220
+ *(current_paths - previous_paths).map do |path|
221
+ AssetUpdate.new(path, nil, current_assets.fetch(path))
222
+ end,
223
+ *shared_paths.filter_map do |path|
224
+ previous_asset = previous_assets.fetch(path)
225
+ current_asset = current_assets.fetch(path)
226
+ next if previous_asset.content_hash == current_asset.content_hash
227
+
228
+ AssetUpdate.new(path, previous_asset, current_asset)
229
+ end,
230
+ *(previous_paths - current_paths).map do |path|
231
+ AssetUpdate.new(path, previous_assets.fetch(path), nil)
232
+ end
233
+ ]
234
+ end
235
+
236
+ def asset_changes_for(asset_updates)
237
+ AssetChanges.new(
238
+ asset_updates.select(&:added?).map(&:output_path),
239
+ asset_updates.select(&:changed?).map(&:output_path),
240
+ asset_updates.select(&:removed?).map(&:output_path)
241
+ )
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end