# ========================================================================== # Project: Spade - CommonJS Runtime # Copyright: ©2010 Strobe Inc. All rights reserved. # License: Licened under MIT license (see LICENSE) # ========================================================================== require 'json' module Spade class Loader def initialize(ctx) @ctx = ctx end def discoverRoot(path) Spade.discover_root path end def root(path=nil) return @ctx.rootdir if path.nil? @ctx.rootdir = path @packages = nil end # exposed to JS. Find the JS file on disk and register the module def loadFactory(spade, id, formats, done=nil) formats = formats.to_a # We may get a V8::Array, we want a normal one # load individual files if id =~ /^file:\// js_path = id[5..-1] if File.exists? js_path load_module id, js_path, ['js'], js_path end return nil end parts = id.split '/' package_name = parts.shift package_info = packages[package_name] skip_module = false return nil if package_info.nil? if parts.size==1 && parts[0] == '~package' skip_module = true elsif parts.size==1 && parts[0] == 'main' parts = (package_info[:json]['main'] || 'lib/main').split('/') dirs = [parts[0...-1]] parts = [parts[-1]] else dirs = extract_dirs(parts, package_info) end # register the package first - also make sure dependencies are # registered since they are needed for loading plugins unless package_info[:registered] package_info[:registered] = true @ctx.eval "spade.register('#{package_name}', #{package_info[:json].to_json});" deps = package_info[:json]['dependencies'] || []; deps.each do |dep_name, ignored| dep_package_info = packages[dep_name] next unless dep_package_info && !dep_package_info[:registered] dep_package_info[:registered] = true @ctx.eval "spade.register('#{dep_name}', #{dep_package_info[:json].to_json});" # Add new formats if they are specified in our dependencies dep_formats = dep_package_info[:json]['plugin:formats'] formats.unshift(*dep_formats.keys).uniq! if dep_formats end end unless skip_module filename = parts.pop base_path = package_info[:path] formats << ['js'] if formats.empty? dirs.each do |dirname| formats.each do |fmt| cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt) if File.exist? cur_path load_module id, cur_path, fmt, cur_path return nil end end rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb') if File.exists? rb_path load_ruby id, rb_path return nil end end end return nil end # exposed to JS. Determines if the named id exists in the system def exists(spade, id, formats) # individual files return File.exists?(id[5..-1]) if id =~ /^file:\// parts = id.split '/' package_name = parts.shift package_info = packages[package_name] return false if package_info.nil? return true if parts.size==1 && parts[0] == '~package' dirs = extract_dirs(parts, package_info) filename = parts.pop base_path = package_info[:path] formats = ['js'] if formats.nil? dirs.each do |dirname| formats.each do |fmt| cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt) return true if File.exist? cur_path end rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb') return File.exists? rb_path end end def load_module(id, module_path, format, path) module_contents = File.read(module_path).to_json # encode as string @ctx.eval("spade.register('#{id}',#{module_contents}, { format: #{format.to_s.to_json}, filename: #{path.to_s.to_json} });") nil end def load_ruby(id, rb_path) klass = Spade.exports_for rb_path exports = klass.nil? ? {} : klass.new(@ctx) @ctx['$__rb_exports__'] = exports @ctx.eval(%[(function() { var exp = $__rb_exports__; spade.register('#{id}', function(r,e,m) { m.exports = exp; }); })();]) @ctx['$__rb_exports__'] = nil end def packages @packages unless @packages.nil? @packages = {} if defined?(BPM) package_paths = Dir.glob(File.join(LibGems.dir, 'gems', '*')) package_paths.each{|path| add_package(path) } end # in reverse order of precedence %w[.spade/packages vendor/cache vendor/packages packages].each do |p| package_paths = Dir.glob File.join(@ctx.rootdir, p.split('/'), '*') package_paths.each { |path| add_package(path) } end # add self add_package @ctx.rootdir @packages end private def extract_dirs(parts, package_info) dirname = (parts.size > 0 && parts.first.chars.first == '~') ? parts.shift[1..-1] : 'lib' dirs = package_info[:directories][dirname] raise "Can't require from unknown directory: #{dirname}" unless dirs dirs = [dirs] unless dirs.is_a?(Array) dirs end def add_package(path) json_package = File.join(path, 'package.json') return unless File.exists?(json_package) json = JSON.load(File.read(json_package)) rescue nil return if json.nil? directories = json["directories"] || { "lib" => "lib" } json["root"] = "file:/"+File.split(path).join('/') @packages[json["name"]] = { :registered => false, :path => path, :directories => directories, :json => json } end end end