Class: Opal::Builder

Inherits:
Object
  • Object
show all
Includes:
Directory, Project::Collection
Defined in:
opal/lib/opal/builder.rb,
opal/lib/opal/builder/directory.rb,
opal/lib/opal/builder/processor.rb,
opal/lib/opal/builder/scheduler.rb,
opal/lib/opal/builder/scheduler/prefork.rb,
opal/lib/opal/builder/scheduler/sequential.rb

Defined Under Namespace

Modules: Directory Classes: MissingRequire, Processor, ProcessorNotFound, Scheduler

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Directory

#compile_to_directory, #source_prefix, #version_prefix

Methods included from Project::Collection

#add_project, #all_projects, #has_project?, #project_of, #projects, #setup_project, #use_gem

Constructor Details

#initialize(options = nil) ⇒ Builder

Returns a new instance of Builder.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'opal/lib/opal/builder.rb', line 67

def initialize(options = nil)
  (options || {}).each_pair do |k, v|
    public_send("#{k}=", v)
  end

  @stubs                    ||= []
  @preload                  ||= []
  @processors               ||= ::Opal::Builder.processors
  @path_reader              ||= PathReader.new(Opal.paths, extensions.map { |e| [".#{e}", ".js.#{e}"] }.flatten)
  @prerequired              ||= []
  @compiler_options         ||= Opal::Config.compiler_options
  @missing_require_severity ||= Opal::Config.missing_require_severity
  @cache                    ||= Opal.cache
  @scheduler                ||= Opal.builder_scheduler

  if @scheduler.respond_to? :new
    @scheduler = @scheduler.new(self)
  end

  @processed = []
end

Instance Attribute Details

#cacheObject

Returns the value of attribute cache.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def cache
  @cache
end

#compiler_optionsObject

Returns the value of attribute compiler_options.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def compiler_options
  @compiler_options
end

#missing_require_severityObject

Returns the value of attribute missing_require_severity.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def missing_require_severity
  @missing_require_severity
end

#path_readerObject

Returns the value of attribute path_reader.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def path_reader
  @path_reader
end

#preloadObject

Returns the value of attribute preload.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def preload
  @preload
end

#prerequiredObject

Returns the value of attribute prerequired.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def prerequired
  @prerequired
end

#processedObject (readonly)

Returns the value of attribute processed.



182
183
184
# File 'opal/lib/opal/builder.rb', line 182

def processed
  @processed
end

#processorsObject

Returns the value of attribute processors.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def processors
  @processors
end

#schedulerObject

Returns the value of attribute scheduler.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def scheduler
  @scheduler
end

#stubsObject

Returns the value of attribute stubs.



184
185
186
# File 'opal/lib/opal/builder.rb', line 184

def stubs
  @stubs
end

Class Method Details

.build(*args, &block) ⇒ Object



89
90
91
# File 'opal/lib/opal/builder.rb', line 89

def self.build(*args, &block)
  new.build(*args, &block)
end

.extensionsObject

All the extensions supported by registered processors



20
21
22
# File 'opal/lib/opal/builder.rb', line 20

def self.extensions
  @extensions ||= []
end

.processorsObject

The registered processors



15
16
17
# File 'opal/lib/opal/builder.rb', line 15

def self.processors
  @processors ||= []
end

.register_processor(processor, processor_extensions) ⇒ Object

Register a builder processor and the supported extensions. A processor will respond to:

#requires

An array of string containing the logic paths of required assets

#required_trees

An array of string containing the logic paths of required directories

#autoloads

An array of entities that are autoloaded and their compile-time load failure can be safely ignored

#to_s

The processed source

#source_map

An instance of ::Opal::SourceMap::File representing the processd asset's source map.

.new(source, filename, compiler_options)

The processor will be instantiated passing:

  • the unprocessed source
  • the asset's filename
  • Opal's compiler options

.match?(path)

The processor is able to recognize paths suitable for its type of processing.



55
56
57
58
59
# File 'opal/lib/opal/builder.rb', line 55

def self.register_processor(processor, processor_extensions)
  return if processors.include?(processor)
  processors << processor
  processor_extensions.each { |ext| extensions << ext }
end

Instance Method Details

#already_processedObject



175
176
177
# File 'opal/lib/opal/builder.rb', line 175

def already_processed
  @already_processed ||= Set.new
end

#append_paths(*paths) ⇒ Object



141
142
143
144
# File 'opal/lib/opal/builder.rb', line 141

def append_paths(*paths)
  paths.each { |i| setup_project(i) }
  path_reader.append_paths(*paths)
end

#build(path, options = {}) ⇒ Object



93
94
95
# File 'opal/lib/opal/builder.rb', line 93

def build(path, options = {})
  build_str(source_for(path), path, options)
end

#build_require(path, options = {}) ⇒ Object



115
116
117
# File 'opal/lib/opal/builder.rb', line 115

def build_require(path, options = {})
  process_require(path, [], options)
end

#build_str(source, rel_path, options = {}) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
# File 'opal/lib/opal/builder.rb', line 102

def build_str(source, rel_path, options = {})
  return if source.nil?
  abs_path = expand_path(rel_path)
  setup_project(abs_path)
  rel_path = expand_ext(rel_path)
  asset = processor_for(source, rel_path, abs_path, false, options)
  requires = preload + asset.requires + tree_requires(asset, abs_path)
  # Don't automatically load modules required by the module
  process_requires(rel_path, requires, asset.autoloads, options.merge(load: false))
  processed << asset
  self
end

#compiled_source(with_source_map: true) ⇒ Object

Output method #compiled_source aims to replace #to_s



220
221
222
223
224
# File 'opal/lib/opal/builder.rb', line 220

def compiled_source(with_source_map: true)
  compiled_source = to_s
  compiled_source += "\n" + source_map.to_data_uri_comment if with_source_map
  compiled_source
end

#dependent_filesObject

Return a list of dependent files, for watching purposes



202
203
204
# File 'opal/lib/opal/builder.rb', line 202

def dependent_files
  processed.map(&:abs_path).compact.select { |fn| File.exist?(fn) }
end

#esm?Boolean

Returns:

  • (Boolean)


187
188
189
# File 'opal/lib/opal/builder.rb', line 187

def esm?
  @compiler_options[:esm]
end

#expand_ext(path) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
# File 'opal/lib/opal/builder.rb', line 206

def expand_ext(path)
  abs_path = path_reader.expand(path)

  if abs_path
    File.join(
      File.dirname(path),
      File.basename(abs_path)
    )
  else
    path
  end
end

#initialize_copy(other) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'opal/lib/opal/builder.rb', line 119

def initialize_copy(other)
  super
  @stubs = other.stubs.dup
  @preload = other.preload.dup
  @processors = other.processors.dup
  @path_reader = other.path_reader.dup
  @projects = other.projects.dup
  @prerequired = other.prerequired.dup
  @compiler_options = other.compiler_options.dup
  @missing_require_severity = other.missing_require_severity.to_sym
  @processed = other.processed.dup
  @scheduler = other.scheduler.dup.tap { |i| i.builder = self }
end

#output_extensionObject

Output extension, to be used by runners. At least Node.JS switches to ESM mode only if the extension is "mjs"



193
194
195
196
197
198
199
# File 'opal/lib/opal/builder.rb', line 193

def output_extension
  if esm?
    'mjs'
  else
    'js'
  end
end

#process_require(rel_path, autoloads, options) ⇒ Object



168
169
170
171
172
173
# File 'opal/lib/opal/builder.rb', line 168

def process_require(rel_path, autoloads, options)
  return if already_processed.include?(rel_path)
  already_processed << rel_path
  asset = process_require_threadsafely(rel_path, autoloads, options)
  processed << asset if asset
end

#process_require_threadsafely(rel_path, autoloads, options) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'opal/lib/opal/builder.rb', line 146

def process_require_threadsafely(rel_path, autoloads, options)
  return if prerequired.include?(rel_path)

  autoload = autoloads.include? rel_path

  source = stub?(rel_path) ? '' : read(rel_path, autoload)

  # The handling is delegated to the runtime
  return if source.nil?

  abs_path = expand_path(rel_path)
  rel_path = expand_ext(rel_path)
  asset = processor_for(source, rel_path, abs_path, autoload, options.merge(requirable: true))
  process_requires(
    rel_path,
    asset.requires + tree_requires(asset, abs_path),
    asset.autoloads,
    options
  )
  asset
end

#source_for(path) ⇒ Object

Retrieve the source for a given path the same way #build would do.



98
99
100
# File 'opal/lib/opal/builder.rb', line 98

def source_for(path)
  read(path, false)
end

#source_mapObject



137
138
139
# File 'opal/lib/opal/builder.rb', line 137

def source_map
  ::Opal::SourceMap::Index.new(processed.map(&:source_map), join: "\n")
end

#to_sObject



133
134
135
# File 'opal/lib/opal/builder.rb', line 133

def to_s
  processed.map(&:to_s).join("\n")
end