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