Class: Utopia::ImportMap::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/utopia/import_map.rb

Overview

Builder class for constructing import maps with scoped base URIs.

The builder supports nested ‘with(base:)` blocks where each base is resolved relative to its parent base, following RFC 3986 URL resolution rules.

Examples:

Nested base resolution.

ImportMap.build do |map|
  # No base - imports as-is
  map.import("app", "/app.js")

  # Base: "https://cdn.example.com/"
  map.with(base: "https://cdn.example.com/") do |cdn|
    cdn.import("lib", "lib.js")  # => "https://cdn.example.com/lib.js"

    # Nested base: "https://cdn.example.com/" + "v2/" = "https://cdn.example.com/v2/"
    cdn.with(base: "v2/") do |v2|
      v2.import("new-lib", "lib.js")  # => "https://cdn.example.com/v2/lib.js"
    end
  end
end

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(import_map, base: nil) ⇒ Builder

Returns a new instance of Builder.



91
92
93
94
# File 'lib/utopia/import_map.rb', line 91

def initialize(import_map, base: nil)
  @import_map = import_map
  @base = Protocol::URL[base]
end

Class Method Details

.build(import_map, **options, &block) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/utopia/import_map.rb', line 79

def self.build(import_map, **options, &block)
  builder = self.new(import_map, **options)
  
  if block.arity == 1
    yield(builder)
  else
    builder.instance_eval(&block)
  end
  
  return builder
end

Instance Method Details

#import(specifier, value, integrity: nil) ⇒ Object

Add an import mapping with the current base URI.

If a base is set, the value is resolved relative to that base following RFC 3986. Absolute URLs (scheme://…) are preserved as-is when used as values.

Examples:

With base URL.

builder = Builder.new(map, base: "https://cdn.com/")
builder.import("lib", "lib.js")  # Resolves to: "https://cdn.com/lib.js"
builder.import("ext", "https://other.com/ext.js")  # Keeps: "https://other.com/ext.js"


110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/utopia/import_map.rb', line 110

def import(specifier, value, integrity: nil)
  resolved_value = if @base
    value_url = Protocol::URL[value]
    
    # Combine base with value
    (@base + value_url).to_s
  else
    value
  end
  
  @import_map.import(specifier, resolved_value, integrity: integrity)
  
  self
end

#scope(scope_prefix, imports) ⇒ Object

Add a scope mapping.

Scopes allow different import resolutions for different parts of your application.

Examples:

Scope-specific imports.

builder.scope("/admin/", {"utils" => "/admin/utils.js"})


162
163
164
165
# File 'lib/utopia/import_map.rb', line 162

def scope(scope_prefix, imports)
  @import_map.scope(scope_prefix, imports)
  self
end

#with(base:, &block) ⇒ Object

Create a nested scope with a different base URI.

The new base is resolved relative to the current base. This allows for hierarchical organization of imports from different sources.

Examples:

Nested CDN paths.

builder.with(base: "https://cdn.com/") do |cdn|
  cdn.with(base: "libs/v2/") do |v2|
    # Base is now: "https://cdn.com/libs/v2/"
    v2.import("util", "util.js")  # => "https://cdn.com/libs/v2/util.js"
  end
end


141
142
143
144
145
146
147
148
149
150
# File 'lib/utopia/import_map.rb', line 141

def with(base:, &block)
  # Resolve the new base relative to the current base
  resolved_base = if @base
    @base + Protocol::URL[base]
  else
    base
  end
  
  self.class.build(@import_map, base: resolved_base, &block)
end