Class: Klenod::Build::Graph
- Inherits:
-
Object
- Object
- Klenod::Build::Graph
- Includes:
- TSort
- Defined in:
- lib/klenod/build/graph.rb,
lib/klenod/build/graph/invalidator.rb
Defined Under Namespace
Classes: AsyncResult, FailedModule, InFlightLoad, Invalidator
Instance Attribute Summary collapse
-
#asset_generation_queue ⇒ Object
readonly
Returns the value of attribute asset_generation_queue.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#mods ⇒ Object
readonly
Returns the value of attribute mods.
-
#plugins ⇒ Object
readonly
Returns the value of attribute plugins.
-
#profiler ⇒ Object
readonly
Returns the value of attribute profiler.
-
#records ⇒ Object
readonly
Returns the value of attribute records.
Instance Method Summary collapse
- #absolute_path(module_id) ⇒ Object
- #asset(output_path) ⇒ Object
- #asset_references_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) ⇒ Object
- #assets ⇒ Object
- #assets_for(logical_name) ⇒ Object
- #assets_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) ⇒ Object
- #bundle(entrypoints:) ⇒ Object
- #collect(specifier) ⇒ Object
- #collect_module(module_id, force: false) ⇒ Object
- #collect_module_now(module_id, force: false) ⇒ Object
- #each_asset(&block) ⇒ Object
- #evaluate_module(module_id) ⇒ Object
- #evaluated?(record_or_module_id) ⇒ Boolean
- #exports(record_or_module_id) ⇒ Object
-
#initialize(source_dir:, plugins:, mode: :development, asset_generation_concurrency: AssetGenerationQueue::DEFAULT_CONCURRENCY, asset_download_concurrency: AssetGenerationQueue::DEFAULT_DOWNLOAD_CONCURRENCY, profiler: nil) ⇒ Graph
constructor
A new instance of Graph.
- #invalidate_paths(changed_paths, removed_paths: []) ⇒ Object
- #load(specifier) ⇒ Object
- #load_module(module_id, force: false, reevaluate: false) ⇒ Object
- #load_module_now(module_id, force: false, reevaluate: false) ⇒ Object
- #module_id_for(record_or_module_id) ⇒ Object
- #register_virtual_module(module_id, source, owner_id: nil, metadata: {}) ⇒ Object
- #resolve_dependency(dependency) ⇒ Object
- #source_dir ⇒ Object
- #tsort_each_child(node, &block) ⇒ Object
- #tsort_each_node(&block) ⇒ Object
- #unregister_virtual_modules(owner_id) ⇒ Object
- #virtual_module_metadata(module_id) ⇒ Object
- #wait_for_assets ⇒ Object
Constructor Details
#initialize(source_dir:, plugins:, mode: :development, asset_generation_concurrency: AssetGenerationQueue::DEFAULT_CONCURRENCY, asset_download_concurrency: AssetGenerationQueue::DEFAULT_DOWNLOAD_CONCURRENCY, profiler: nil) ⇒ Graph
Returns a new instance of Graph.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/klenod/build/graph.rb', line 62 def initialize( source_dir:, plugins:, mode: :development, asset_generation_concurrency: AssetGenerationQueue::DEFAULT_CONCURRENCY, asset_download_concurrency: AssetGenerationQueue::DEFAULT_DOWNLOAD_CONCURRENCY, profiler: nil ) @profiler = profiler || Profiler.new @resolver = Resolver.new(source_dir: source_dir, profiler: @profiler) @plugins = plugins @mode = mode @asset_generation_queue = AssetGenerationQueue.new( concurrency: asset_generation_concurrency, download_concurrency: asset_download_concurrency ) @records = {} @mods = {} @virtual_sources = {} @virtual_metadata = {} @virtual_owners = {} @loading_tasks = {} end |
Instance Attribute Details
#asset_generation_queue ⇒ Object (readonly)
Returns the value of attribute asset_generation_queue.
59 60 61 |
# File 'lib/klenod/build/graph.rb', line 59 def asset_generation_queue @asset_generation_queue end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
59 60 61 |
# File 'lib/klenod/build/graph.rb', line 59 def mode @mode end |
#mods ⇒ Object (readonly)
Returns the value of attribute mods.
59 60 61 |
# File 'lib/klenod/build/graph.rb', line 59 def mods @mods end |
#plugins ⇒ Object (readonly)
Returns the value of attribute plugins.
60 61 62 |
# File 'lib/klenod/build/graph.rb', line 60 def plugins @plugins end |
#profiler ⇒ Object (readonly)
Returns the value of attribute profiler.
59 60 61 |
# File 'lib/klenod/build/graph.rb', line 59 def profiler @profiler end |
#records ⇒ Object (readonly)
Returns the value of attribute records.
59 60 61 |
# File 'lib/klenod/build/graph.rb', line 59 def records @records end |
Instance Method Details
#absolute_path(module_id) ⇒ Object
196 197 198 |
# File 'lib/klenod/build/graph.rb', line 196 def absolute_path(module_id) @resolver.absolute_path(module_id) end |
#asset(output_path) ⇒ Object
124 125 126 |
# File 'lib/klenod/build/graph.rb', line 124 def asset(output_path) assets.fetch(output_path) end |
#asset_references_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/klenod/build/graph.rb', line 147 def asset_references_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) seen_assets = {} module_ids_for_assets(record_or_module_id, recursive: recursive, type: type) .each_with_index .flat_map do |module_id, index| module_assets = @records.fetch(module_id).assets module_assets.filter_map do |asset| next unless asset_matches?(asset, type: type, content_type: content_type) next if seen_assets.key?(asset.output_path) seen_assets[asset.output_path] = true Runtime::AssetReference.new(index:, asset:) end end end |
#assets ⇒ Object
120 121 122 |
# File 'lib/klenod/build/graph.rb', line 120 def assets @records.values.select { |record| record.status == :loaded }.flat_map(&:assets).to_h { |asset| [asset.output_path, asset] } end |
#assets_for(logical_name) ⇒ Object
138 139 140 |
# File 'lib/klenod/build/graph.rb', line 138 def assets_for(logical_name) assets.values.select { |asset| asset.logical_name == logical_name.to_s } end |
#assets_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) ⇒ Object
142 143 144 145 |
# File 'lib/klenod/build/graph.rb', line 142 def assets_for_module(record_or_module_id, type: nil, content_type: nil, recursive: true) asset_references_for_module(record_or_module_id, type: type, content_type: content_type, recursive: recursive) .map(&:asset) end |
#bundle(entrypoints:) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/klenod/build/graph.rb', line 99 def bundle(entrypoints:) loaded_entrypoints = @profiler.measure(:entrypoints, count: entrypoints.length) do entrypoints.to_h do |entrypoint| dependency = Dependency.create(specifier: entrypoint, importer_id: nil, kind: :entrypoint) resolved = resolve_dependency(dependency) [entrypoint, collect_module(resolved.module_id).id.to_s] end end @profiler.measure(:runtime_dependencies) { collect_all_runtime_dependencies } @profiler.measure(:bundle_specs) do Runtime::Bundle.new( loaded_entrypoints, runtime_module_specs, runtime_asset_specs, source_root: source_dir.to_s ) end end |
#collect(specifier) ⇒ Object
93 94 95 96 97 |
# File 'lib/klenod/build/graph.rb', line 93 def collect(specifier) dependency = Dependency.create(specifier: specifier, importer_id: nil, kind: :entrypoint) resolved = resolve_dependency(dependency) collect_module(resolved.module_id) end |
#collect_module(module_id, force: false) ⇒ Object
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
# File 'lib/klenod/build/graph.rb', line 280 def collect_module(module_id, force: false) raise_import_cycle_if_present(module_id) unless force if !force if @mode == :build && (cached = @records[module_id]) @profiler.count(:collect_module_cache_hit) raise_failed_module!(cached) return cached end return @loading_tasks.fetch(module_id).wait if @loading_tasks.key?(module_id) begin in_flight_load = InFlightLoad.create @loading_tasks[module_id] = in_flight_load result = AsyncResult.capture { collect_module_now(module_id, force: force) } in_flight_load.finish(result) return AsyncResult.unwrap(result) ensure @loading_tasks.delete(module_id) if @loading_tasks[module_id].equal?(in_flight_load) end end collect_module_now(module_id, force: force) end |
#collect_module_now(module_id, force: false) ⇒ Object
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/klenod/build/graph.rb', line 306 def collect_module_now(module_id, force: false) with_loading_stack(module_id) do loaded_source = read_module_source(module_id) source = loaded_source.source source_hash = loaded_source.source_hash || Hashing.hexdigest(source) cached = @records[module_id] if cached&.source_hash == source_hash && !force raise_failed_module!(cached) @profiler.count(:collect_module_cache_hit) return cached end @profiler.count(:collect_module_cache_miss) transform = loaded_source.transform || transform_module_source(module_id, source) resolved_dependencies = resolve_transform_dependencies(module_id, transform) dependency_records = collect_eager_dependency_records(resolved_dependencies) transform = finalize_transform_result(module_id, transform, resolved_dependencies, dependency_records) assert_supported_transform!(module_id, source, transform) transformed_hash = Hashing.hexdigest(transform.code) record = build_module_record(module_id, source, source_hash, transformed_hash, transform, resolved_dependencies, cached) @records[module_id] = record @mods.delete(module_id) @profiler.progress(:collect_module, module_id: record.id.to_s, total_records: @records.length) record end end |
#each_asset(&block) ⇒ Object
163 164 165 166 167 |
# File 'lib/klenod/build/graph.rb', line 163 def each_asset(&block) return enum_for(:each_asset) unless block assets.each_value(&block) end |
#evaluate_module(module_id) ⇒ Object
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/klenod/build/graph.rb', line 335 def evaluate_module(module_id) return @mods.fetch(module_id) if @mods.key?(module_id) record = @records.fetch(module_id) { collect_module(module_id) } raise_failed_module!(record) evaluate_eager_dependencies(record.resolved_dependencies) dependency_records = dependency_records_for(eager_dependencies(record.resolved_dependencies)) mod = Runtime::Mod.new( module_id.to_s, record.transformed_source, imports: imports_for(record.resolved_dependencies, dependency_records), source_map: record.source_map, version: record.version, eval_path: eval_path_for(module_id) ) @mods[module_id] = mod end |
#evaluated?(record_or_module_id) ⇒ Boolean
133 134 135 136 |
# File 'lib/klenod/build/graph.rb', line 133 def evaluated?(record_or_module_id) mod = @mods[module_id_for(record_or_module_id)] mod && !mod.is_a?(FailedModule) end |
#exports(record_or_module_id) ⇒ Object
128 129 130 131 |
# File 'lib/klenod/build/graph.rb', line 128 def exports(record_or_module_id) module_id = module_id_for(record_or_module_id) evaluate_module(module_id).const_get(:Exports) end |
#invalidate_paths(changed_paths, removed_paths: []) ⇒ Object
225 226 227 228 |
# File 'lib/klenod/build/graph.rb', line 225 def invalidate_paths(changed_paths, removed_paths: []) Invalidator.new(self, @resolver, source_loader: method(:read_module_source)) .invalidate_paths(changed_paths, removed_paths: removed_paths) end |
#load(specifier) ⇒ Object
87 88 89 90 91 |
# File 'lib/klenod/build/graph.rb', line 87 def load(specifier) dependency = Dependency.create(specifier: specifier, importer_id: nil, kind: :entrypoint) resolved = resolve_dependency(dependency) load_module(resolved.module_id) end |
#load_module(module_id, force: false, reevaluate: false) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/klenod/build/graph.rb', line 230 def load_module(module_id, force: false, reevaluate: false) raise_import_cycle_if_present(module_id) unless force || reevaluate if !force && !reevaluate return @loading_tasks.fetch(module_id).wait if @loading_tasks.key?(module_id) begin in_flight_load = InFlightLoad.create @loading_tasks[module_id] = in_flight_load result = AsyncResult.capture { load_module_now(module_id, force: force, reevaluate: reevaluate) } in_flight_load.finish(result) return AsyncResult.unwrap(result) ensure @loading_tasks.delete(module_id) if @loading_tasks[module_id].equal?(in_flight_load) end end load_module_now(module_id, force: force, reevaluate: reevaluate) end |
#load_module_now(module_id, force: false, reevaluate: false) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/klenod/build/graph.rb', line 250 def load_module_now(module_id, force: false, reevaluate: false) with_loading_stack(module_id) do loaded_source = read_module_source(module_id) source = loaded_source.source source_hash = loaded_source.source_hash || Hashing.hexdigest(source) cached = @records[module_id] if cached&.source_hash == source_hash && !force && !reevaluate raise_failed_module!(cached) @profiler.count(:load_module_cache_hit) evaluate_module(module_id) unless @mods.key?(module_id) return cached end @profiler.count(:load_module_cache_miss) transform = loaded_source.transform || transform_module_source(module_id, source) resolved_dependencies = resolve_transform_dependencies(module_id, transform) dependency_records = load_eager_dependency_records(resolved_dependencies) transform = finalize_transform_result(module_id, transform, resolved_dependencies, dependency_records) assert_supported_transform!(module_id, source, transform) transformed_hash = Hashing.hexdigest(transform.code) mod = instantiate_module(module_id, transform, resolved_dependencies, dependency_records, cached) record = build_module_record(module_id, source, source_hash, transformed_hash, transform, resolved_dependencies, mod) @records[module_id] = record @mods[module_id] = mod record end end |
#module_id_for(record_or_module_id) ⇒ Object
366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/klenod/build/graph.rb', line 366 def module_id_for(record_or_module_id) case record_or_module_id when ModuleRecord record_or_module_id.id when ModuleId record_or_module_id else ref = record_or_module_id.to_s @records.each_key.find { |module_id| module_id.to_s == ref } || module_id_for_canonical_ref(ref) || module_id_for_absolute_ref(ref) || raise(KeyError, "No module loaded for #{record_or_module_id.inspect}") end end |
#register_virtual_module(module_id, source, owner_id: nil, metadata: {}) ⇒ Object
204 205 206 207 208 |
# File 'lib/klenod/build/graph.rb', line 204 def register_virtual_module(module_id, source, owner_id: nil, metadata: {}) @virtual_sources[module_id] = source @virtual_metadata[module_id] = .freeze @virtual_owners[module_id] = owner_id if owner_id end |
#resolve_dependency(dependency) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/klenod/build/graph.rb', line 175 def resolve_dependency(dependency) if (virtual_module_id = dependency.[:virtual_module_id]) @profiler.count(:resolve_virtual_metadata_hit) return ResolvedDependency.new(dependency, virtual_module_id, {virtual: true}) end @plugins.each do |plugin| @profiler.count(:plugin_resolve_check) resolved = @profiler.measure(:plugin_resolve, plugin: plugin.class.name, specifier: dependency.specifier) do plugin.resolve(dependency, self) end @profiler.count(:plugin_resolve_hit) if resolved return resolved if resolved end @profiler.measure(:resolver_resolve, specifier: dependency.specifier) do @resolver.resolve(dependency) end end |
#source_dir ⇒ Object
200 201 202 |
# File 'lib/klenod/build/graph.rb', line 200 def source_dir @resolver.source_dir end |
#tsort_each_child(node, &block) ⇒ Object
359 360 361 362 363 364 |
# File 'lib/klenod/build/graph.rb', line 359 def tsort_each_child(node, &block) record = @records.fetch(node) return if record.status != :loaded record.resolved_dependencies.map(&:module_id).each(&block) end |
#tsort_each_node(&block) ⇒ Object
355 356 357 |
# File 'lib/klenod/build/graph.rb', line 355 def tsort_each_node(&block) @records.each_key(&block) end |
#unregister_virtual_modules(owner_id) ⇒ Object
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/klenod/build/graph.rb', line 214 def unregister_virtual_modules(owner_id) module_ids = @virtual_owners.filter_map { |module_id, owner| module_id if owner == owner_id } module_ids.each do |module_id| @virtual_sources.delete(module_id) @virtual_metadata.delete(module_id) @virtual_owners.delete(module_id) @records.delete(module_id) @mods.delete(module_id) end end |
#virtual_module_metadata(module_id) ⇒ Object
210 211 212 |
# File 'lib/klenod/build/graph.rb', line 210 def (module_id) @virtual_metadata.fetch(module_id, {}) end |
#wait_for_assets ⇒ Object
169 170 171 172 173 |
# File 'lib/klenod/build/graph.rb', line 169 def wait_for_assets with_async_task do |task| each_asset.map { |asset| task.async { asset.wait } }.each(&:wait) end end |