class Opal::Builder

def self.build(*args, &block)

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

def self.extensions

All the extensions supported by registered processors
def self.extensions
  @extensions ||= []
end

def self.processors

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

def self.register_processor(processor, processor_extensions)


processing.
The processor is able to recognize paths suitable for its type of
## `.match?(path)`

- Opal's compiler options
- the asset's filename
- the unprocessed source
The processor will be instantiated passing:
## `.new(source, filename, compiler_options)`

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

The processed source
## `#to_s`

An array of string containing the logic paths of required directories
## `#required_trees`

An array of string containing the logic paths of required assets
## `#requires`

A processor will respond to:
Register a builder processor and the supported extensions.
@public
def self.register_processor(processor, processor_extensions)
  return if processors.include?(processor)
  processors << processor
  processor_extensions.each { |ext| extensions << ext }
end

def already_processed

def already_processed
  @already_processed ||= Set.new
end

def append_paths(*paths)

def append_paths(*paths)
  path_reader.append_paths(*paths)
end

def build(path, options = {})

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

def build_require(path, options = {})

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

def build_str(source, rel_path, options = {})

def build_str(source, rel_path, options = {})
  return if source.nil?
  abs_path = expand_path(rel_path)
  rel_path = expand_ext(rel_path)
  asset = processor_for(source, rel_path, abs_path, options)
  requires = preload + asset.requires + tree_requires(asset, abs_path)
  requires.map { |r| process_require(r, options) }
  processed << asset
  self
rescue MissingRequire => error
  raise error, "A file required by #{rel_path.inspect} wasn't found.\n#{error.message}", error.backtrace
end

def expand_ext(path)

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

def expand_path(path)

def expand_path(path)
  return if stub?(path)
  (path_reader.expand(path) || File.expand_path(path)).to_s
end

def extensions

def extensions
  ::Opal::Builder.extensions
end

def initialize(options = nil)

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
  @processed = []
end

def initialize_copy(other)

def initialize_copy(other)
  super
  @stubs = other.stubs.dup
  @preload = other.preload.dup
  @processors = other.processors.dup
  @path_reader = other.path_reader.dup
  @prerequired = other.prerequired.dup
  @compiler_options = other.compiler_options.dup
  @missing_require_severity = other.missing_require_severity.to_sym
  @processed = other.processed.dup
end

def process_require(rel_path, options)

def process_require(rel_path, options)
  return if prerequired.include?(rel_path)
  return if already_processed.include?(rel_path)
  already_processed << rel_path
  source = stub?(rel_path) ? '' : read(rel_path)
  # 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, options.merge(requirable: true))
  process_requires(rel_path, asset.requires + tree_requires(asset, abs_path), options)
  processed << asset
end

def process_requires(rel_path, requires, options)

def process_requires(rel_path, requires, options)
  requires.map { |r| process_require(r, options) }
rescue MissingRequire => error
  raise error, "A file required by #{rel_path.inspect} wasn't found.\n#{error.message}", error.backtrace
end

def processor_for(source, rel_path, abs_path, options)

def processor_for(source, rel_path, abs_path, options)
  processor = processors.find { |p| p.match? abs_path } ||
              raise(ProcessorNotFound, "can't find processor for rel_path: " \
                                       "#{rel_path.inspect}, "\
                                       "abs_path: #{abs_path.inspect}, "\
                                       "source: #{source.inspect}, "\
                                       "processors: #{processors.inspect}"
              )
  processor.new(source, rel_path, @compiler_options.merge(options))
end

def read(path)

def read(path)
  path_reader.read(path) || begin
    print_list = ->(list) { "- #{list.join("\n- ")}\n" }
    message = "can't find file: #{path.inspect} in:\n" +
              print_list[path_reader.paths] +
              "\nWith the following extensions:\n" +
              print_list[path_reader.extensions] +
              "\nAnd the following processors:\n" +
              print_list[processors]
    case missing_require_severity
    when :error   then raise MissingRequire, message
    when :warning then warn message
    when :ignore  then # noop
    end
    nil
  end
end

def source_for(path)

Retrieve the source for a given path the same way #build would do.
def source_for(path)
  read(path)
end

def source_map

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

def stub?(path)

def stub?(path)
  stubs.include?(path)
end

def to_s

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

def tree_requires(asset, asset_path)

def tree_requires(asset, asset_path)
  dirname = asset_path.to_s.empty? ? Pathname.pwd : Pathname(asset_path).expand_path.dirname
  abs_base_paths = path_reader.paths.map { |p| File.expand_path(p) }
  asset.required_trees.flat_map do |tree|
    abs_tree_path = dirname.join(tree).expand_path.to_s
    abs_base_path = abs_base_paths.find { |p| abs_tree_path.start_with?(p) }
    if abs_base_path
      abs_base_path = Pathname(abs_base_path)
      entries_glob  = Pathname(abs_tree_path).join('**', "*{.js,}.{#{extensions.join ','}}")
      Pathname.glob(entries_glob).map { |file| file.relative_path_from(abs_base_path).to_s }
    else
      [] # the tree is not part of any known base path
    end
  end
end