class Opal::Builder

def self.build(*args, &block)

def self.build(*args, &block)
  new.build(*args, &block)
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 = {})
  source = read(path)
  build_str(source, path, options)
end

def build_require(path, options = {})

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

def build_str source, filename, options = {}

def build_str source, filename, options = {}
  path = path_reader.expand(filename).to_s unless stub?(filename)
  asset = processor_for(source, filename, path, options)
  requires = preload + asset.requires + tree_requires(asset, path)
  requires.map { |r| process_require(r, options) }
  processed << asset
  self
rescue MissingRequire => error
  raise error, "A file required by #{filename.inspect} wasn't found.\n#{error.message}", error.backtrace
end

def extensions

def extensions
  @extensions ||= DEFAULT_PROCESSORS.flat_map(&:extensions).compact
end

def initialize(options = nil)

def initialize(options = nil)
  (options || {}).each_pair do |k,v|
    public_send("#{k}=", v)
  end
  @stubs             ||= []
  @preload           ||= []
  @processors        ||= DEFAULT_PROCESSORS
  @path_reader       ||= PathReader.new
  @prerequired       ||= []
  @compiler_options  ||= {}
  @default_processor ||= RubyProcessor
  @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
  @processed = other.processed.dup
end

def process_require(filename, options)

def process_require(filename, options)
  filename = filename.gsub(/\.(rb|js|opal)#{REGEXP_END}/, '')
  return if prerequired.include?(filename)
  return if already_processed.include?(filename)
  already_processed << filename
  source = stub?(filename) ? '' : read(filename)
  if source.nil?
    message = "can't find file: #{filename.inspect}"
    case @compiler_options[:dynamic_require_severity]
    when :error then raise LoadError, message
    when :warning then warn "can't find file: #{filename.inspect}"
    end
  end
  path = path_reader.expand(filename).to_s unless stub?(filename)
  asset = processor_for(source, filename, path, options.merge(requirable: true))
  process_requires(filename, asset.requires+tree_requires(asset, path), options)
  processed << asset
end

def process_requires(filename, requires, options)

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

def processor_for(source, filename, path, options)

def processor_for(source, filename, path, options)
  processor   = processors.find { |p| p.match? path }
  processor ||= default_processor
  return processor.new(source, filename, compiler_options.merge(options))
end

def read(path)

def read(path)
  path_reader.read(path) or
    raise MissingRequire, "can't find file: #{path.inspect} in #{path_reader.paths.inspect}"
end

def source_map

def source_map
  processed.map(&:source_map).reduce(:+).as_json.to_json
end

def stub? filename

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

def to_s

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

def tree_requires(asset, path)

def tree_requires(asset, path)
  if path.nil? or path.empty?
    dirname = Dir.pwd
  else
    dirname = File.dirname(File.expand_path(path))
  end
  paths = path_reader.paths.map{|p| File.expand_path(p)}
  asset.required_trees.flat_map do |tree|
    expanded = File.expand_path(tree, dirname)
    base = paths.find { |p| expanded.start_with?(p) }
    next [] if base.nil?
    globs = extensions.map { |ext| File.join base, tree, '**', "*.#{ext}" }
    Dir[*globs].map do |file|
      Pathname(file).relative_path_from(Pathname(base)).to_s.gsub(/(\.js)?(\.(?:#{extensions.join '|'}))#{REGEXP_END}/, '')
    end
  end
end