opal 0.7.2 → 0.8.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGELOG.md +29 -0
- data/CONTRIBUTING.md +51 -4
- data/Gemfile +3 -0
- data/README.md +5 -5
- data/config.ru +1 -1
- data/examples/sinatra/Gemfile +1 -0
- data/examples/sinatra/config.ru +13 -3
- data/lib/mspec/opal/rake_task.rb +21 -30
- data/lib/mspec/opal/runner.rb +37 -0
- data/lib/mspec/opal/special_calls.rb +6 -0
- data/lib/opal/builder.rb +1 -0
- data/lib/opal/builder_processors.rb +5 -2
- data/lib/opal/cli_runners/phantom.js +10 -1
- data/lib/opal/compiler.rb +6 -3
- data/lib/opal/config.rb +48 -0
- data/lib/opal/nodes/call.rb +3 -2
- data/lib/opal/nodes/literal.rb +19 -2
- data/lib/opal/parser/grammar.rb +2224 -2196
- data/lib/opal/parser/grammar.y +25 -7
- data/lib/opal/parser/lexer.rb +12 -9
- data/lib/opal/path_reader.rb +1 -1
- data/lib/opal/sprockets/erb.rb +6 -20
- data/lib/opal/sprockets/path_reader.rb +4 -2
- data/lib/opal/sprockets/processor.rb +135 -80
- data/lib/opal/sprockets/server.rb +49 -78
- data/lib/opal/sprockets/source_map_header_patch.rb +41 -0
- data/lib/opal/sprockets/source_map_server.rb +115 -0
- data/lib/opal/version.rb +1 -1
- data/lib/tilt/opal.rb +48 -0
- data/opal.gemspec +1 -1
- data/opal/corelib/array.rb +179 -51
- data/opal/corelib/array/inheritance.rb +14 -0
- data/opal/corelib/boolean.rb +5 -0
- data/opal/corelib/hash.rb +1 -1
- data/opal/corelib/kernel.rb +660 -164
- data/opal/corelib/match_data.rb +44 -21
- data/opal/corelib/module.rb +83 -53
- data/opal/corelib/numeric.rb +15 -1
- data/opal/corelib/regexp.rb +31 -75
- data/opal/corelib/runtime.js +20 -8
- data/opal/corelib/string.rb +754 -243
- data/opal/corelib/string/inheritance.rb +20 -3
- data/opal/corelib/struct.rb +30 -6
- data/opal/corelib/variables.rb +2 -2
- data/spec/filters/bugs/array.rb +0 -39
- data/spec/filters/bugs/kernel.rb +10 -7
- data/spec/filters/bugs/module.rb +21 -0
- data/spec/filters/bugs/opal.rb +0 -5
- data/spec/filters/bugs/singleton.rb +0 -2
- data/spec/filters/bugs/string.rb +69 -315
- data/spec/filters/bugs/struct.rb +0 -16
- data/spec/filters/unsupported/encoding.rb +7 -0
- data/spec/filters/unsupported/float.rb +3 -0
- data/spec/filters/unsupported/integer_size.rb +52 -0
- data/spec/filters/unsupported/marshal.rb +4 -0
- data/spec/filters/unsupported/mutable_strings.rb +37 -0
- data/spec/filters/unsupported/private_methods.rb +11 -0
- data/spec/filters/unsupported/rational_numbers.rb +4 -0
- data/spec/filters/unsupported/regular_expressions.rb +47 -0
- data/spec/filters/unsupported/symbols.rb +7 -0
- data/spec/filters/unsupported/tainted.rb +23 -1
- data/spec/filters/unsupported/trusted.rb +5 -0
- data/spec/lib/fixtures/complex_sprockets.js.rb.erb +4 -0
- data/spec/lib/fixtures/jst_file.js.jst +1 -0
- data/spec/lib/parser/call_spec.rb +19 -0
- data/spec/lib/parser/def_spec.rb +6 -0
- data/spec/lib/sprockets/erb_spec.rb +17 -4
- data/spec/lib/sprockets/processor_spec.rb +50 -18
- data/spec/lib/sprockets/server_spec.rb +39 -11
- data/spec/lib/tilt/opal_spec.rb +19 -0
- data/spec/opal/core/kernel/format_spec.rb +10 -10
- data/spec/opal/core/language/predefined_spec.rb +10 -0
- data/spec/opal/core/object_id_spec.rb +56 -0
- data/spec/opal/core/runtime/bridged_classes_spec.rb +4 -4
- data/spec/opal/core/runtime_spec.rb +7 -0
- data/spec/opal/stdlib/native/native_class_spec.rb +1 -1
- data/spec/opal/stdlib/promise/always_spec.rb +30 -0
- data/spec/opal/stdlib/promise/then_spec.rb +8 -0
- data/spec/opal/stdlib/promise/trace_spec.rb +8 -0
- data/spec/rubyspecs +15 -104
- data/spec/spec_helper.rb +4 -0
- data/stdlib/native.rb +7 -18
- data/stdlib/nodejs/file.rb +1 -1
- data/stdlib/opal-parser.rb +1 -0
- data/stdlib/pp.rb +7 -5
- data/stdlib/promise.rb +53 -41
- data/tasks/testing.rake +8 -6
- metadata +28 -14
- data/spec/filters/bugs/match_data.rb +0 -13
- data/spec/filters/bugs/numeric.rb +0 -22
- data/spec/filters/bugs/regexp.rb +0 -9
- data/spec/filters/bugs/unknown.rb +0 -11
data/lib/opal/parser/grammar.y
CHANGED
@@ -607,16 +607,12 @@ rule
|
|
607
607
|
result = [s(:hash, *val[0])]
|
608
608
|
}
|
609
609
|
|
610
|
-
paren_args: tLPAREN2
|
611
|
-
{
|
612
|
-
result = []
|
613
|
-
}
|
614
|
-
| tLPAREN2 call_args opt_nl tRPAREN
|
610
|
+
paren_args: tLPAREN2 opt_call_args rparen
|
615
611
|
{
|
616
612
|
result = val[1]
|
617
613
|
}
|
618
|
-
|
619
|
-
|
614
|
+
|
615
|
+
rparen: opt_nl tRPAREN
|
620
616
|
|
621
617
|
opt_paren_args: none
|
622
618
|
{
|
@@ -624,6 +620,25 @@ rule
|
|
624
620
|
}
|
625
621
|
| paren_args
|
626
622
|
|
623
|
+
opt_call_args: none
|
624
|
+
{
|
625
|
+
result = []
|
626
|
+
}
|
627
|
+
| call_args
|
628
|
+
| args tCOMMA
|
629
|
+
{
|
630
|
+
result = val[0]
|
631
|
+
}
|
632
|
+
| args tCOMMA assocs tCOMMA
|
633
|
+
{
|
634
|
+
result = val[0]
|
635
|
+
result << new_hash(nil, val[2], nil)
|
636
|
+
}
|
637
|
+
| assocs tCOMMA
|
638
|
+
{
|
639
|
+
result = [new_hash(nil, val[0], nil)]
|
640
|
+
}
|
641
|
+
|
627
642
|
call_args: command
|
628
643
|
{
|
629
644
|
result = [val[0]]
|
@@ -899,6 +914,7 @@ rule
|
|
899
914
|
| kDEF fname
|
900
915
|
{
|
901
916
|
push_scope
|
917
|
+
lexer.lex_state = :expr_endfn
|
902
918
|
}
|
903
919
|
f_arglist bodystmt kEND
|
904
920
|
{
|
@@ -912,6 +928,7 @@ rule
|
|
912
928
|
fname
|
913
929
|
{
|
914
930
|
push_scope
|
931
|
+
lexer.lex_state = :expr_endfn
|
915
932
|
}
|
916
933
|
f_arglist bodystmt kEND
|
917
934
|
{
|
@@ -1456,6 +1473,7 @@ xstring_contents: none
|
|
1456
1473
|
| f_args term
|
1457
1474
|
{
|
1458
1475
|
result = val[0]
|
1476
|
+
lexer.lex_state = :expr_beg
|
1459
1477
|
}
|
1460
1478
|
|
1461
1479
|
kwrest_mark: tPOW
|
data/lib/opal/parser/lexer.rb
CHANGED
@@ -249,6 +249,8 @@ module Opal
|
|
249
249
|
"\v"
|
250
250
|
elsif scan(/a/)
|
251
251
|
"\a"
|
252
|
+
elsif scan(/b/)
|
253
|
+
"\b"
|
252
254
|
elsif scan(/e/)
|
253
255
|
"\e"
|
254
256
|
elsif scan(/s/)
|
@@ -258,12 +260,7 @@ module Opal
|
|
258
260
|
elsif scan(/x([0-9a-fA-F]{1,2})/)
|
259
261
|
scanner[1].to_i(16).chr
|
260
262
|
elsif scan(/u([0-9a-zA-Z]{1,4})/)
|
261
|
-
|
262
|
-
scanner[1].to_i(16).chr(Encoding::UTF_8)
|
263
|
-
else
|
264
|
-
# FIXME: no encoding on 1.8
|
265
|
-
""
|
266
|
-
end
|
263
|
+
scanner[1].to_i(16).chr(Encoding::UTF_8)
|
267
264
|
else
|
268
265
|
# escaped char doesnt need escaping, so just return it
|
269
266
|
scan(/./)
|
@@ -620,6 +617,8 @@ module Opal
|
|
620
617
|
|
621
618
|
if [:expr_beg, :expr_dot, :expr_mid, :expr_arg, :expr_cmdarg].include? @lex_state
|
622
619
|
@lex_state = cmd_start ? :expr_cmdarg : :expr_arg
|
620
|
+
elsif @lex_state == :expr_fname
|
621
|
+
@lex_state = :expr_endfn
|
623
622
|
else
|
624
623
|
@lex_state = :expr_end
|
625
624
|
end
|
@@ -877,13 +876,17 @@ module Opal
|
|
877
876
|
elsif scan(/\=/)
|
878
877
|
@lex_state = :expr_beg
|
879
878
|
return new_op_asgn('/')
|
880
|
-
|
881
|
-
|
882
|
-
|
879
|
+
end
|
880
|
+
|
881
|
+
if arg?
|
883
882
|
if !check(/\s/) && @space_seen
|
884
883
|
self.strterm = new_strterm(STR_REGEXP, '/', '/')
|
885
884
|
return :tREGEXP_BEG
|
886
885
|
end
|
886
|
+
end
|
887
|
+
|
888
|
+
if after_operator?
|
889
|
+
@lex_state = :expr_arg
|
887
890
|
else
|
888
891
|
@lex_state = :expr_beg
|
889
892
|
end
|
data/lib/opal/path_reader.rb
CHANGED
data/lib/opal/sprockets/erb.rb
CHANGED
@@ -1,33 +1,19 @@
|
|
1
1
|
require 'tilt'
|
2
2
|
require 'sprockets'
|
3
|
+
require 'opal/sprockets/processor'
|
3
4
|
|
4
5
|
module Opal
|
5
6
|
module ERB
|
6
|
-
class Processor <
|
7
|
-
# vvv BOILERPLATE vvv
|
8
|
-
self.default_mime_type = 'application/javascript'
|
9
|
-
|
10
|
-
def self.engine_initialized?
|
11
|
-
true
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.version
|
15
|
-
::Opal::VERSION
|
16
|
-
end
|
17
|
-
|
7
|
+
class Processor < ::Opal::Processor
|
18
8
|
def initialize_engine
|
19
|
-
|
9
|
+
super
|
20
10
|
require_template_library 'opal/erb'
|
21
11
|
end
|
22
12
|
|
23
|
-
def prepare
|
24
|
-
end
|
25
|
-
# ^^^ BOILERPLATE ^^^
|
26
|
-
|
27
|
-
|
28
13
|
def evaluate(context, locals, &block)
|
29
|
-
context.
|
30
|
-
|
14
|
+
compiler = Opal::ERB::Compiler.new(@data, context.logical_path.sub(/^templates\//, ''))
|
15
|
+
@data = compiler.prepared_source
|
16
|
+
super
|
31
17
|
end
|
32
18
|
end
|
33
19
|
end
|
@@ -13,14 +13,16 @@ module Opal
|
|
13
13
|
env[path, bundle: true].to_s
|
14
14
|
else
|
15
15
|
context.depend_on(path)
|
16
|
-
File.
|
16
|
+
File.open(expand(path), 'rb:UTF-8'){|f| f.read}
|
17
17
|
end
|
18
18
|
rescue ::Sprockets::FileNotFound
|
19
19
|
nil
|
20
20
|
end
|
21
21
|
|
22
22
|
def expand path
|
23
|
-
env.resolve(path)
|
23
|
+
env.resolve(path) or
|
24
|
+
# Sprockets 3 just returns nil for unknown paths
|
25
|
+
raise ::Sprockets::FileNotFound, path.inspect
|
24
26
|
end
|
25
27
|
|
26
28
|
def paths
|
@@ -1,93 +1,169 @@
|
|
1
1
|
require 'set'
|
2
|
-
require 'tilt'
|
2
|
+
require 'tilt/opal'
|
3
3
|
require 'sprockets'
|
4
|
-
require 'opal/version'
|
5
4
|
require 'opal/builder'
|
6
5
|
require 'opal/sprockets/path_reader'
|
6
|
+
require 'opal/sprockets/source_map_server'
|
7
7
|
|
8
8
|
$OPAL_SOURCE_MAPS = {}
|
9
9
|
|
10
10
|
module Opal
|
11
11
|
# The Processor class is used to make ruby files (with rb or opal extensions)
|
12
12
|
# available to any sprockets based server. Processor will then get passed any
|
13
|
-
# ruby source file to build.
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# * source_map_enabled [true by default]
|
21
|
-
# * irb_enabled [false by default]
|
22
|
-
# * inline_operators_enabled [false by default]
|
23
|
-
#
|
24
|
-
class Processor < Tilt::Template
|
25
|
-
# vvv BOILERPLATE vvv
|
26
|
-
self.default_mime_type = 'application/javascript'
|
27
|
-
|
28
|
-
def self.engine_initialized?
|
29
|
-
true
|
13
|
+
# ruby source file to build.
|
14
|
+
class Processor < TiltTemplate
|
15
|
+
# DEPRECATED:
|
16
|
+
# Support legacy accessors to default options, now moved to Opal::Config
|
17
|
+
Opal::Config.default_config.keys.each do |config_option|
|
18
|
+
define_singleton_method(config_option) { Opal::Config.config[config_option] }
|
19
|
+
define_singleton_method("#{config_option}=") { |value| Opal::Config.config[config_option] = value }
|
30
20
|
end
|
31
21
|
|
32
|
-
def
|
33
|
-
::
|
34
|
-
end
|
22
|
+
def evaluate(context, locals, &block)
|
23
|
+
return super unless context.is_a? ::Sprockets::Context
|
35
24
|
|
36
|
-
|
37
|
-
|
38
|
-
|
25
|
+
@sprockets = sprockets = context.environment
|
26
|
+
|
27
|
+
# In Sprockets 3 logical_path has an odd behavior when the filename is "index"
|
28
|
+
# thus we need to bake our own logical_path
|
29
|
+
filename = context.respond_to?(:filename) ? context.filename : context.pathname.to_s
|
30
|
+
logical_path = filename.sub(%r{^#{context.root_path}/?(.*?)#{sprockets_extnames_regexp}}, '\1')
|
31
|
+
|
32
|
+
compiler_options = self.compiler_options.merge(file: logical_path)
|
33
|
+
|
34
|
+
# Opal will be loaded immediately to as the runtime redefines some crucial
|
35
|
+
# methods such that need to be implemented as soon as possible:
|
36
|
+
#
|
37
|
+
# E.g. It forwards .toString() to .$to_s() for Opal objects including Array.
|
38
|
+
# If .$to_s() is not implemented and some other lib is loaded before
|
39
|
+
# corelib/* .toString results in an `undefined is not a function` error.
|
40
|
+
compiler_options.merge!(requirable: false) if logical_path == 'opal'
|
39
41
|
|
40
|
-
|
42
|
+
compiler = Compiler.new(data, compiler_options)
|
43
|
+
result = compiler.compile
|
44
|
+
|
45
|
+
process_requires(compiler.requires, context)
|
46
|
+
process_required_trees(compiler.required_trees, context)
|
47
|
+
|
48
|
+
if Opal::Config.source_map_enabled
|
49
|
+
map_contents = compiler.source_map.as_json.to_json
|
50
|
+
::Opal::SourceMapServer.set_map_cache(sprockets, logical_path, map_contents)
|
51
|
+
end
|
52
|
+
|
53
|
+
result.to_s
|
41
54
|
end
|
42
|
-
# ^^^ BOILERPLATE ^^^
|
43
55
|
|
56
|
+
def self.sprockets_extnames_regexp(sprockets)
|
57
|
+
joined_extnames = sprockets.engines.keys.map { |ext| Regexp.escape(ext) }.join('|')
|
58
|
+
Regexp.new("(#{joined_extnames})*$")
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
attr_accessor :const_missing_enabled
|
49
|
-
attr_accessor :dynamic_require_severity
|
50
|
-
attr_accessor :source_map_enabled
|
51
|
-
attr_accessor :irb_enabled
|
52
|
-
attr_accessor :inline_operators_enabled
|
61
|
+
def sprockets_extnames_regexp
|
62
|
+
@sprockets_extnames_regexp ||= self.class.sprockets_extnames_regexp(@sprockets)
|
63
|
+
end
|
53
64
|
|
54
|
-
|
65
|
+
def process_requires(requires, context)
|
66
|
+
requires.each do |required|
|
67
|
+
required = required.sub(sprockets_extnames_regexp, '')
|
68
|
+
context.require_asset required unless stubbed_files.include? required
|
69
|
+
end
|
55
70
|
end
|
56
71
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
72
|
+
# Mimics (v2) Sprockets::DirectiveProcessor#process_require_tree_directive
|
73
|
+
def process_required_trees(required_trees, context)
|
74
|
+
return if required_trees.empty?
|
75
|
+
|
76
|
+
# This is the root dir of the logical path, we need this because
|
77
|
+
# the compiler gives us the path relative to the file's logical path.
|
78
|
+
dirname = File.dirname(file).gsub(/#{Regexp.escape File.dirname(context.logical_path)}$/, '')
|
79
|
+
dirname = Pathname(dirname)
|
80
|
+
|
81
|
+
required_trees.each do |original_required_tree|
|
82
|
+
required_tree = Pathname(original_required_tree)
|
83
|
+
|
84
|
+
unless required_tree.relative?
|
85
|
+
raise ArgumentError, "require_tree argument must be a relative path: #{required_tree.inspect}"
|
86
|
+
end
|
87
|
+
|
88
|
+
required_tree = dirname.join(file, '..', required_tree)
|
89
|
+
|
90
|
+
unless required_tree.directory?
|
91
|
+
raise ArgumentError, "require_tree argument must be a directory: #{[original_required_tree, required_tree].inspect}"
|
92
|
+
end
|
93
|
+
|
94
|
+
context.depend_on required_tree.to_s
|
95
|
+
|
96
|
+
environment = context.environment
|
97
|
+
|
98
|
+
if environment.respond_to?(:each_entry)
|
99
|
+
# Sprockets 2
|
100
|
+
environment.each_entry(required_tree) do |pathname|
|
101
|
+
if pathname.to_s == file
|
102
|
+
next
|
103
|
+
elsif pathname.directory?
|
104
|
+
context.depend_on(pathname)
|
105
|
+
elsif context.asset_requirable?(pathname)
|
106
|
+
context.require_asset(pathname)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
else
|
110
|
+
# Sprockets 3
|
111
|
+
processor = ::Sprockets::DirectiveProcessor.new
|
112
|
+
processor.instance_variable_set('@dirname', File.dirname(file))
|
113
|
+
processor.instance_variable_set('@environment', environment)
|
114
|
+
path = processor.__send__(:expand_relative_dirname, :require_tree, original_required_tree)
|
115
|
+
absolute_paths = environment.__send__(:stat_sorted_tree_with_dependencies, path).first.map(&:first)
|
116
|
+
|
117
|
+
absolute_paths.each do |path|
|
118
|
+
path = Pathname(path)
|
119
|
+
pathname = path.relative_path_from(dirname)
|
120
|
+
|
121
|
+
if name.to_s == file
|
122
|
+
next
|
123
|
+
elsif path.directory?
|
124
|
+
context.depend_on(path.to_s)
|
125
|
+
else
|
126
|
+
context.require_asset(pathname)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
64
132
|
|
65
|
-
self.
|
133
|
+
def self.load_asset_code(sprockets, name)
|
134
|
+
asset = sprockets[name.sub(/(\.(js|rb|opal))*$/, '.js')]
|
135
|
+
return '' if asset.nil?
|
66
136
|
|
137
|
+
opal_extnames = sprockets.engines.map do |ext, engine|
|
138
|
+
ext if engine <= ::Opal::Processor
|
139
|
+
end.compact
|
67
140
|
|
68
|
-
|
69
|
-
|
141
|
+
module_name = -> asset { asset.logical_path.sub(/\.js$/, '') }
|
142
|
+
path_extnames = -> path { File.basename(path).scan(/\.[^.]+/) }
|
143
|
+
mark_as_loaded = -> path { "Opal.mark_as_loaded(#{path.inspect});" }
|
144
|
+
processed_by_opal = -> asset { (path_extnames[asset.pathname] & opal_extnames).any? }
|
70
145
|
|
71
|
-
|
72
|
-
|
146
|
+
non_opal_assets = ([asset]+asset.dependencies)
|
147
|
+
.select { |asset| not(processed_by_opal[asset]) }
|
148
|
+
.map { |asset| module_name[asset] }
|
73
149
|
|
74
|
-
|
75
|
-
|
150
|
+
mark_as_loaded = (['opal'] + non_opal_assets + stubbed_files.to_a)
|
151
|
+
.map { |path| mark_as_loaded[path] }
|
76
152
|
|
77
|
-
if
|
78
|
-
|
79
|
-
"#{result.to_s}\n//# sourceMappingURL=#{File.basename(context.logical_path)}.map\n"
|
80
|
-
else
|
81
|
-
result.to_s
|
153
|
+
if processed_by_opal[asset]
|
154
|
+
load_asset_code = "Opal.load(#{module_name[asset].inspect});"
|
82
155
|
end
|
83
|
-
end
|
84
156
|
|
85
|
-
|
86
|
-
|
157
|
+
<<-JS
|
158
|
+
if (typeof(Opal) !== 'undefined') {
|
159
|
+
#{mark_as_loaded.join("\n")}
|
160
|
+
#{load_asset_code}
|
161
|
+
}
|
162
|
+
JS
|
87
163
|
end
|
88
164
|
|
89
165
|
def self.stubbed_files
|
90
|
-
@stubbed_files ||=
|
166
|
+
@stubbed_files ||= Set.new
|
91
167
|
end
|
92
168
|
|
93
169
|
def self.stub_file(name)
|
@@ -97,29 +173,8 @@ module Opal
|
|
97
173
|
def stubbed_files
|
98
174
|
self.class.stubbed_files
|
99
175
|
end
|
100
|
-
|
101
|
-
def self.new_builder(context)
|
102
|
-
compiler_options = {
|
103
|
-
:method_missing => method_missing_enabled,
|
104
|
-
:arity_check => arity_check_enabled,
|
105
|
-
:const_missing => const_missing_enabled,
|
106
|
-
:dynamic_require_severity => dynamic_require_severity,
|
107
|
-
:irb => irb_enabled,
|
108
|
-
:inline_operators => inline_operators_enabled,
|
109
|
-
}
|
110
|
-
|
111
|
-
path_reader = ::Opal::Sprockets::PathReader.new(context.environment, context)
|
112
|
-
return Builder.new(
|
113
|
-
compiler_options: compiler_options,
|
114
|
-
stubs: stubbed_files,
|
115
|
-
path_reader: path_reader
|
116
|
-
)
|
117
|
-
end
|
118
176
|
end
|
119
177
|
end
|
120
178
|
|
121
|
-
Tilt.register 'rb', Opal::Processor
|
122
179
|
Sprockets.register_engine '.rb', Opal::Processor
|
123
|
-
|
124
|
-
Tilt.register 'opal', Opal::Processor
|
125
180
|
Sprockets.register_engine '.opal', Opal::Processor
|