spade-runtime 0.1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/bin/spaderun +9 -0
- data/lib/spade-runtime.rb +1 -0
- data/lib/spade/runtime.rb +20 -0
- data/lib/spade/runtime/bundle.rb +173 -0
- data/lib/spade/runtime/cli.rb +7 -0
- data/lib/spade/runtime/cli/base.rb +181 -0
- data/lib/spade/runtime/compiler.rb +34 -0
- data/lib/spade/runtime/console.rb +39 -0
- data/lib/spade/runtime/context.rb +114 -0
- data/lib/spade/runtime/exports.rb +86 -0
- data/lib/spade/runtime/loader.rb +209 -0
- data/lib/spade/runtime/reactor.rb +159 -0
- data/lib/spade/runtime/server.rb +66 -0
- data/lib/spade/runtime/shell.rb +36 -0
- data/lib/spade/runtime/version.rb +5 -0
- data/spade-runtime.gemspec +36 -0
- data/spec/cli/update_spec.rb +64 -0
- data/spec/javascript/async-test.js +123 -0
- data/spec/javascript/compiler/javascript.js +13 -0
- data/spec/javascript/compiler/ruby.js +14 -0
- data/spec/javascript/loader-test.js +64 -0
- data/spec/javascript/normalize-test.js +73 -0
- data/spec/javascript/packages-test.js +44 -0
- data/spec/javascript/relative-require-test.js +72 -0
- data/spec/javascript/require-test.js +117 -0
- data/spec/javascript/sandbox/compile.js +37 -0
- data/spec/javascript/sandbox/creation.js +44 -0
- data/spec/javascript/sandbox/format.js +79 -0
- data/spec/javascript/sandbox/misc.js +57 -0
- data/spec/javascript/sandbox/preprocessor.js +81 -0
- data/spec/javascript/sandbox/require.js +48 -0
- data/spec/javascript/sandbox/run-command.js +21 -0
- data/spec/javascript/spade/externs.js +14 -0
- data/spec/javascript/spade/load-factory.js +15 -0
- data/spec/javascript/spade/misc.js +23 -0
- data/spec/javascript/spade/ready.js +12 -0
- data/spec/javascript/spade/register.js +13 -0
- data/spec/javascript_spec.rb +7 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/cli.rb +109 -0
- data/spec/support/core_test.rb +61 -0
- data/spec/support/matchers.rb +12 -0
- data/spec/support/path.rb +62 -0
- metadata +218 -0
data/.gitignore
ADDED
data/bin/spaderun
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'spade/runtime'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spade/core'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'spade/packager'
|
5
|
+
rescue LoadError
|
6
|
+
# Packager isn't necessary, but we should use it if available
|
7
|
+
end
|
8
|
+
|
9
|
+
module Spade
|
10
|
+
module Runtime
|
11
|
+
autoload :Bundle, 'spade/runtime/bundle'
|
12
|
+
autoload :Context, 'spade/runtime/context'
|
13
|
+
autoload :MainContext,'spade/runtime/context'
|
14
|
+
autoload :Server, 'spade/runtime/server'
|
15
|
+
autoload :Shell, 'spade/runtime/shell'
|
16
|
+
autoload :CLI, 'spade/runtime/cli'
|
17
|
+
autoload :Namespace, 'spade/runtime/exports'
|
18
|
+
autoload :Exports, 'spade/runtime/exports'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
module Spade::Runtime
|
10
|
+
BOOT_PATH = File.dirname(Spade::JSPATH)
|
11
|
+
|
12
|
+
module Bundle
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def update(rootdir, opts ={})
|
16
|
+
|
17
|
+
verbose = opts[:verbose]
|
18
|
+
spade_path = File.join(rootdir, Spade::SPADE_DIR)
|
19
|
+
spade_package_path = File.join(spade_path, 'packages')
|
20
|
+
FileUtils.rm_r(spade_path) if File.exists? spade_path
|
21
|
+
|
22
|
+
FileUtils.mkdir_p File.join(spade_package_path)
|
23
|
+
|
24
|
+
FileUtils.ln_s BOOT_PATH, File.join(spade_path, 'boot')
|
25
|
+
|
26
|
+
installed = []
|
27
|
+
package_dirs = []
|
28
|
+
|
29
|
+
# In order of precedence
|
30
|
+
package_dirs += %w[packages vendor/packages vendor/cache].map{|p| File.join(rootdir, p.split('/')) }
|
31
|
+
package_dirs << File.join(LibGems.dir, 'gems') if defined?(Spade::Packager)
|
32
|
+
|
33
|
+
for package_dir in package_dirs
|
34
|
+
Dir.glob(File.join(package_dir, '*')).each do |path|
|
35
|
+
json_file = File.join(path, 'package.json')
|
36
|
+
next unless File.exists?(json_file)
|
37
|
+
|
38
|
+
# How would this happen?
|
39
|
+
next if installed.include? path
|
40
|
+
installed << path
|
41
|
+
|
42
|
+
json = JSON.load File.read(json_file)
|
43
|
+
package_name = json['name']
|
44
|
+
package_version = json['version']
|
45
|
+
|
46
|
+
local = path.index(rootdir) == 0
|
47
|
+
|
48
|
+
# Use relative paths if embedded
|
49
|
+
old_path = if local
|
50
|
+
# Figure out how many levels deep the spade_path is in the project
|
51
|
+
levels = spade_package_path.sub(rootdir, '').split(File::SEPARATOR).reject{|p| p.empty? }.count
|
52
|
+
# Build relative path
|
53
|
+
File.join(['..'] * levels, path.sub(rootdir, ''))
|
54
|
+
else
|
55
|
+
path
|
56
|
+
end
|
57
|
+
|
58
|
+
new_path = File.join(spade_path, 'packages', package_name)
|
59
|
+
|
60
|
+
unless File.exist?(new_path)
|
61
|
+
FileUtils.ln_s old_path, new_path, :force => true
|
62
|
+
|
63
|
+
puts "Installing #{local ? "local" : "remote"} package #{package_name}" if verbose
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
File.open(File.join(rootdir, 'spade-boot.js'), 'w+') do |fp|
|
69
|
+
fp.write gen_spade_boot(rootdir, opts)
|
70
|
+
end
|
71
|
+
puts "Wrote spade-boot.js" if verbose
|
72
|
+
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def gen_spade_boot(rootdir, opts={})
|
77
|
+
verbose = opts[:verbose]
|
78
|
+
spade_path = File.join(rootdir, Spade::SPADE_DIR, 'packages', '*')
|
79
|
+
known_packages = Dir.glob(spade_path).map do |path|
|
80
|
+
package_name = File.basename path
|
81
|
+
info = JSON.load File.read(File.join(path, 'package.json'))
|
82
|
+
info["sync"] = true
|
83
|
+
info["root"] = "#{Spade::SPADE_DIR}/packages/#{package_name}"
|
84
|
+
info["files"] = package_file_list(path)
|
85
|
+
info
|
86
|
+
end
|
87
|
+
|
88
|
+
if File.exists? File.join(rootdir, 'package.json')
|
89
|
+
info = JSON.load File.read(File.join(rootdir, 'package.json'))
|
90
|
+
else
|
91
|
+
info = {
|
92
|
+
"name" => File.basename(rootdir),
|
93
|
+
"directories" => { "lib" => "lib" }
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
info["root"] = '.'
|
98
|
+
info["sync"] = true
|
99
|
+
info["files"] = package_file_list('.')
|
100
|
+
|
101
|
+
packages = resolve_dependencies(info, known_packages)
|
102
|
+
|
103
|
+
%[// GENERATED: #{Time.now.to_s}
|
104
|
+
// This file is automatically generated by spade. To update run
|
105
|
+
// 'spade update'. To use this file, reference it in your HTML file. Base
|
106
|
+
// sure that your base URL is to the top level directory containing all of
|
107
|
+
// your files.
|
108
|
+
|
109
|
+
/*globals spade */
|
110
|
+
//@ begin boot
|
111
|
+
(function() {
|
112
|
+
// load spade itself
|
113
|
+
var script = document.createElement('script');
|
114
|
+
script.src = "#{Spade::SPADE_DIR}/boot/spade.js";
|
115
|
+
script.onload = function() {
|
116
|
+
|
117
|
+
// Register remaining packages with spade
|
118
|
+
#{packages.map{|p| %[spade.register("#{p["name"]}", #{JSON.pretty_generate(p)});\n] } * "\n"}
|
119
|
+
|
120
|
+
// find the main module to run
|
121
|
+
var main = null;
|
122
|
+
var scripts = document.scripts || document.getElementsByTagName("script"),
|
123
|
+
len = scripts.length;
|
124
|
+
for(var idx=0;!main && idx<len;idx++) {
|
125
|
+
main = scripts[idx].getAttribute('data-require');
|
126
|
+
}
|
127
|
+
scripts = null; // avoid memory leaks in IE
|
128
|
+
|
129
|
+
if (main) spade.ready(function() { spade.require(main); });
|
130
|
+
};
|
131
|
+
|
132
|
+
var head = document.head || document.body || document;
|
133
|
+
head.appendChild(script);
|
134
|
+
|
135
|
+
script = head = null; // avoid memory leaks in IE
|
136
|
+
})();
|
137
|
+
//@ end boot
|
138
|
+
]
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def package_file_list(path)
|
145
|
+
get_all_files(path).map{|p| p.sub(path+File::SEPARATOR, '') } - %w(package.json spade-boot.js)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Supports recursive, be careful
|
149
|
+
def get_all_files(path)
|
150
|
+
Dir.glob(File.join(path, '**', '*')).
|
151
|
+
map{|p| File.symlink?(p) ? get_all_files(p) : p }.
|
152
|
+
flatten.
|
153
|
+
reject{|p| File.extname(p).empty? }
|
154
|
+
end
|
155
|
+
|
156
|
+
def resolve_dependencies(package, available)
|
157
|
+
# TODO: Should available be a hash to speed lookup?
|
158
|
+
if package["dependencies"]
|
159
|
+
# TODO: Check version numbers
|
160
|
+
dependencies = package["dependencies"].keys
|
161
|
+
packages = available.select{|p| dependencies.include?(p["name"]) }.
|
162
|
+
map{|p| resolve_dependencies(p, available) }.
|
163
|
+
flatten
|
164
|
+
(packages << package).uniq
|
165
|
+
else
|
166
|
+
[package]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Spade
|
4
|
+
module Runtime
|
5
|
+
module CLI
|
6
|
+
class Base < Thor
|
7
|
+
|
8
|
+
class_option :working, :required => false,
|
9
|
+
:default => Spade.discover_root(Dir.pwd),
|
10
|
+
:aliases => ['-w'],
|
11
|
+
:desc => 'Root working directory.'
|
12
|
+
|
13
|
+
class_option :verbose, :type => :boolean, :default => false,
|
14
|
+
:aliases => ['-V'],
|
15
|
+
:desc => 'Show additional debug information while running'
|
16
|
+
|
17
|
+
class_option :require, :type => :array, :required => false,
|
18
|
+
:aliases => ['-r'],
|
19
|
+
:desc => "optional JS files to require before invoking main command"
|
20
|
+
|
21
|
+
map "-i" => "console", "--interactive" => "console"
|
22
|
+
desc "console", "Opens an interactive JavaScript console"
|
23
|
+
def console
|
24
|
+
require 'readline'
|
25
|
+
|
26
|
+
shell = Spade::Runtime::Shell.new
|
27
|
+
context(:with => shell) do |ctx|
|
28
|
+
shell.ctx = ctx
|
29
|
+
puts "help() for help. quit() to quit."
|
30
|
+
puts "Spade #{Spade::Runtime::VERSION} (V8 #{V8::VERSION})"
|
31
|
+
puts "WORKING=#{options[:working]}" if options[:verbose]
|
32
|
+
|
33
|
+
trap("SIGINT") { puts "^C" }
|
34
|
+
repl ctx
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
map "-e" => "exec"
|
39
|
+
desc "exec [FILENAME]", "Executes filename or stdin"
|
40
|
+
def exec(*)
|
41
|
+
exec_args = ARGV.dup
|
42
|
+
arg = exec_args.shift while arg != "exec" && !exec_args.empty?
|
43
|
+
|
44
|
+
filename = exec_args.shift
|
45
|
+
puts "Filename: #{filename}" if options[:verbose]
|
46
|
+
|
47
|
+
if filename
|
48
|
+
puts "Working: #{options[:working]}" if options[:verbose]
|
49
|
+
filename = File.expand_path filename, Dir.pwd
|
50
|
+
throw "#{filename} not found" unless File.exists?(filename)
|
51
|
+
fp = File.open filename
|
52
|
+
source = File.basename filename
|
53
|
+
rootdir = Spade.discover_root filename
|
54
|
+
|
55
|
+
caller_id = nil
|
56
|
+
if rootdir
|
57
|
+
json_path = File.join(rootdir, 'package.json')
|
58
|
+
if File.exist?(json_path)
|
59
|
+
package_json = JSON.parse(File.read(json_path))
|
60
|
+
caller_id = "#{package_json['name']}/main"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# peek at first line. If it is poundhash, skip. else rewind file
|
65
|
+
unless fp.readline =~ /^\#\!/
|
66
|
+
fp.rewind
|
67
|
+
end
|
68
|
+
|
69
|
+
# Can't set pos on STDIN so we can only do this for files
|
70
|
+
if options[:verbose]
|
71
|
+
pos = fp.pos
|
72
|
+
puts fp.read
|
73
|
+
fp.pos = pos
|
74
|
+
end
|
75
|
+
else
|
76
|
+
fp = $stdin
|
77
|
+
source = '<stdin>'
|
78
|
+
rootdir = options[:working]
|
79
|
+
caller_id = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
if options[:verbose]
|
83
|
+
puts "source: #{source}"
|
84
|
+
puts "rootdir: #{rootdir}"
|
85
|
+
puts "caller_id: #{caller_id}"
|
86
|
+
end
|
87
|
+
|
88
|
+
begin
|
89
|
+
# allow for poundhash
|
90
|
+
context(:argv => exec_args, :rootdir => rootdir, :caller_id => caller_id) do |ctx|
|
91
|
+
ctx.eval(fp, source) # eval the rest
|
92
|
+
end
|
93
|
+
rescue Interrupt => e
|
94
|
+
puts; exit
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
map "server" => "preview"
|
99
|
+
desc "preview", "Starts a preview server for testing"
|
100
|
+
long_desc %[
|
101
|
+
The preview command starts a simple file server that can be used to
|
102
|
+
load JavaScript-based apps in the browser. This is a convenient way to
|
103
|
+
run apps in the browser instead of having to setup Apache on your
|
104
|
+
local machine. If you are already loading apps through your own web
|
105
|
+
server (for ex using Rails) the preview server is not required.
|
106
|
+
]
|
107
|
+
method_option :port, :type => :string, :default => '4020',
|
108
|
+
:aliases => ['-p'],
|
109
|
+
:desc => 'Port number'
|
110
|
+
def preview
|
111
|
+
require 'spade/runtime/server'
|
112
|
+
trap("SIGINT") { Spade::Runtime::Server.shutdown }
|
113
|
+
Spade::Runtime::Server.run(options[:working], options[:port]);
|
114
|
+
end
|
115
|
+
|
116
|
+
desc "update", "Update package info in the current project"
|
117
|
+
def update
|
118
|
+
Spade::Runtime::Bundle.update(options[:working], :verbose => options[:verbose])
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def repl(ctx)
|
124
|
+
ctx.reactor.next_tick do
|
125
|
+
line = Readline.readline("spade> ", true)
|
126
|
+
begin
|
127
|
+
result = ctx.eval(line, '<console>')
|
128
|
+
puts result unless result.nil?
|
129
|
+
rescue V8::JSError => e
|
130
|
+
puts e.message
|
131
|
+
puts e.backtrace(:javascript)
|
132
|
+
rescue StandardError => e
|
133
|
+
puts e
|
134
|
+
puts e.backtrace.join("\n")
|
135
|
+
end
|
136
|
+
repl(ctx)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Loads a JS file into the context. This is not a require; just load
|
141
|
+
def load(cxt, libfile)
|
142
|
+
begin
|
143
|
+
content = File.readlines(libfile)
|
144
|
+
content.shift if content.first && (content.first =~ /^\#\!/)
|
145
|
+
cxt.eval(content*'')
|
146
|
+
#cxt.load(libfile)
|
147
|
+
rescue V8::JSError => e
|
148
|
+
puts e.message
|
149
|
+
puts e.backtrace(:javascript)
|
150
|
+
rescue StandardError => e
|
151
|
+
puts e
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Initialize a context to work against. This will load also handle
|
156
|
+
# autorequires
|
157
|
+
def context(opts={})
|
158
|
+
opts[:rootdir] ||= options[:working]
|
159
|
+
opts[:verbose] = options[:verbose]
|
160
|
+
Spade::Runtime::MainContext.new(opts) do |ctx|
|
161
|
+
|
162
|
+
requires = opts[:require]
|
163
|
+
requires.each { |r| load(ctx, r) } if requires
|
164
|
+
|
165
|
+
yield(ctx) if block_given?
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def method_missing(meth, *)
|
170
|
+
if File.exist?(meth.to_s)
|
171
|
+
ARGV.unshift("exec")
|
172
|
+
invoke :exec
|
173
|
+
else
|
174
|
+
super
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
module Spade::Runtime
|
8
|
+
|
9
|
+
# Compiler plugin for the default context. Know how to create a new
|
10
|
+
# isolated context for the object
|
11
|
+
class Compiler
|
12
|
+
|
13
|
+
def initialize(ctx)
|
14
|
+
@ctx = ctx
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup(sandbox)
|
18
|
+
if sandbox['isIsolated']
|
19
|
+
sandbox['ctx'] = Context.new :reactor => @ctx.reactor
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile(data, sandbox, filename)
|
24
|
+
ctx = sandbox['ctx'] || @ctx
|
25
|
+
ctx.eval("(#{data})", filename)
|
26
|
+
end
|
27
|
+
|
28
|
+
def teardown(sandbox)
|
29
|
+
sandbox['ctx'] = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
|
8
|
+
module Spade::Runtime
|
9
|
+
|
10
|
+
class Console
|
11
|
+
|
12
|
+
def debug(*args)
|
13
|
+
puts "\033[35mDEBUG: #{args * ','}\033[m"
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(*args)
|
18
|
+
puts args * ','
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def error(*args)
|
23
|
+
puts "\033[31mERROR: #{args * ','}\033[m"
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def warn(*args)
|
28
|
+
puts "\033[33mWARN: #{args * ','}\033[m"
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def log(*args)
|
33
|
+
puts args * ','
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|