opal 0.6.3 → 0.7.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.spectator +2 -0
  4. data/.spectator-mspec +3 -0
  5. data/.travis.yml +8 -11
  6. data/CHANGELOG.md +33 -0
  7. data/CONTRIBUTING.md +8 -43
  8. data/Gemfile +15 -4
  9. data/Guardfile +77 -0
  10. data/README.md +15 -9
  11. data/Rakefile +36 -12
  12. data/benchmarks/operators.rb +11 -0
  13. data/bin/opal +10 -13
  14. data/bin/opal-build +4 -4
  15. data/bin/opal-mspec +10 -0
  16. data/bin/opal-repl +4 -3
  17. data/examples/sinatra/Gemfile +1 -1
  18. data/examples/sinatra/config.ru +3 -3
  19. data/lib/mspec/opal/main.rb.erb +2 -2
  20. data/lib/mspec/opal/rake_task.rb +31 -24
  21. data/lib/mspec/opal/runner.rb +18 -1
  22. data/lib/mspec/opal/sprockets.js +17 -0
  23. data/lib/opal.rb +1 -34
  24. data/lib/opal/builder.rb +92 -58
  25. data/lib/opal/builder_processors.rb +165 -0
  26. data/lib/opal/cli.rb +85 -144
  27. data/lib/opal/cli_options.rb +136 -90
  28. data/lib/opal/cli_runners.rb +10 -0
  29. data/lib/opal/cli_runners/nodejs.rb +56 -0
  30. data/lib/opal/cli_runners/phantom.js +35 -0
  31. data/lib/opal/cli_runners/phantomjs.rb +28 -0
  32. data/lib/opal/cli_runners/server.rb +54 -0
  33. data/lib/opal/compiler.rb +35 -16
  34. data/lib/opal/erb.rb +29 -15
  35. data/lib/opal/hike_path_finder.rb +18 -0
  36. data/lib/opal/nodes.rb +1 -0
  37. data/lib/opal/nodes/call.rb +107 -26
  38. data/lib/opal/nodes/call_special.rb +31 -6
  39. data/lib/opal/nodes/class.rb +2 -2
  40. data/lib/opal/nodes/constants.rb +5 -20
  41. data/lib/opal/nodes/def.rb +4 -4
  42. data/lib/opal/nodes/defined.rb +3 -3
  43. data/lib/opal/nodes/definitions.rb +1 -1
  44. data/lib/opal/nodes/for.rb +35 -0
  45. data/lib/opal/nodes/helpers.rb +2 -2
  46. data/lib/opal/nodes/iter.rb +3 -3
  47. data/lib/opal/nodes/literal.rb +10 -2
  48. data/lib/opal/nodes/masgn.rb +2 -2
  49. data/lib/opal/nodes/module.rb +2 -2
  50. data/lib/opal/nodes/scope.rb +1 -0
  51. data/lib/opal/nodes/singleton_class.rb +2 -2
  52. data/lib/opal/nodes/super.rb +2 -2
  53. data/lib/opal/nodes/top.rb +30 -3
  54. data/lib/opal/parser.rb +15 -1
  55. data/lib/opal/parser/grammar.rb +2571 -2452
  56. data/lib/opal/parser/grammar.y +37 -5
  57. data/lib/opal/parser/keywords.rb +2 -0
  58. data/lib/opal/parser/lexer.rb +21 -11
  59. data/lib/opal/path_reader.rb +28 -0
  60. data/lib/opal/paths.rb +38 -0
  61. data/lib/opal/source_map.rb +32 -15
  62. data/lib/opal/sprockets/environment.rb +9 -2
  63. data/lib/opal/sprockets/erb.rb +1 -2
  64. data/lib/opal/sprockets/path_reader.rb +34 -0
  65. data/lib/opal/sprockets/processor.rb +40 -39
  66. data/lib/opal/sprockets/server.rb +47 -33
  67. data/lib/opal/version.rb +1 -1
  68. data/opal.gemspec +10 -5
  69. data/opal/README.md +6 -0
  70. data/opal/corelib/array.rb +36 -4
  71. data/opal/corelib/array/inheritance.rb +6 -6
  72. data/opal/corelib/basic_object.rb +9 -9
  73. data/opal/corelib/boolean.rb +1 -1
  74. data/opal/corelib/class.rb +12 -12
  75. data/opal/corelib/dir.rb +20 -0
  76. data/opal/corelib/enumerable.rb +42 -42
  77. data/opal/corelib/enumerator.rb +1 -1
  78. data/opal/corelib/error.rb +2 -2
  79. data/opal/corelib/file.rb +56 -0
  80. data/opal/corelib/hash.rb +5 -5
  81. data/opal/corelib/helpers.rb +3 -3
  82. data/opal/corelib/io.rb +13 -10
  83. data/opal/corelib/kernel.rb +44 -68
  84. data/opal/corelib/method.rb +1 -1
  85. data/opal/corelib/module.rb +89 -114
  86. data/opal/corelib/nil_class.rb +1 -1
  87. data/opal/corelib/numeric.rb +27 -23
  88. data/opal/corelib/proc.rb +5 -5
  89. data/opal/corelib/range.rb +8 -4
  90. data/opal/corelib/regexp.rb +5 -5
  91. data/opal/corelib/runtime.js +589 -272
  92. data/opal/corelib/string.rb +52 -37
  93. data/opal/corelib/string/inheritance.rb +5 -5
  94. data/opal/corelib/time.rb +102 -52
  95. data/opal/corelib/variables.rb +3 -3
  96. data/opal/opal.rb +2 -0
  97. data/package.json +9 -0
  98. data/spec/filters/bugs/array.rb +0 -6
  99. data/spec/filters/bugs/language.rb +4 -0
  100. data/spec/filters/bugs/numeric.rb +7 -6
  101. data/spec/filters/bugs/opal.rb +2 -0
  102. data/spec/filters/bugs/regexp.rb +4 -0
  103. data/spec/filters/bugs/string.rb +0 -7
  104. data/spec/filters/bugs/stringscanner.rb +4 -1
  105. data/spec/filters/unsupported/private_methods.rb +2 -0
  106. data/spec/lib/builder_processors_spec.rb +27 -0
  107. data/spec/lib/builder_spec.rb +66 -0
  108. data/spec/{cli → lib}/cli_spec.rb +60 -5
  109. data/spec/{cli → lib}/compiler_spec.rb +66 -5
  110. data/spec/{cli → lib}/dependency_resolver_spec.rb +1 -1
  111. data/spec/lib/fixtures/no_requires.rb +1 -0
  112. data/spec/{cli → lib}/fixtures/opal_file.rb +0 -0
  113. data/spec/lib/fixtures/require_tree_test.rb +3 -0
  114. data/spec/lib/fixtures/required_tree_test/required_file1.rb +1 -0
  115. data/spec/lib/fixtures/required_tree_test/required_file2.rb +1 -0
  116. data/spec/lib/fixtures/requires.rb +7 -0
  117. data/spec/{cli → lib}/fixtures/sprockets_file.js.rb +0 -0
  118. data/spec/lib/fixtures/sprockets_require_tree_test.rb +3 -0
  119. data/spec/lib/hike_path_finder_spec.rb +23 -0
  120. data/spec/{cli → lib}/lexer_spec.rb +1 -1
  121. data/spec/{cli → lib}/parser/alias_spec.rb +1 -1
  122. data/spec/{cli → lib}/parser/and_spec.rb +1 -1
  123. data/spec/{cli → lib}/parser/attrasgn_spec.rb +1 -1
  124. data/spec/{cli → lib}/parser/begin_spec.rb +1 -1
  125. data/spec/{cli → lib}/parser/block_spec.rb +1 -1
  126. data/spec/{cli → lib}/parser/break_spec.rb +1 -1
  127. data/spec/{cli → lib}/parser/call_spec.rb +1 -1
  128. data/spec/{cli → lib}/parser/class_spec.rb +1 -1
  129. data/spec/{cli → lib}/parser/comments_spec.rb +1 -1
  130. data/spec/{cli → lib}/parser/def_spec.rb +1 -1
  131. data/spec/{cli → lib}/parser/if_spec.rb +1 -1
  132. data/spec/{cli → lib}/parser/iter_spec.rb +1 -1
  133. data/spec/{cli → lib}/parser/lambda_spec.rb +1 -1
  134. data/spec/{cli → lib}/parser/literal_spec.rb +1 -1
  135. data/spec/{cli → lib}/parser/masgn_spec.rb +1 -1
  136. data/spec/{cli → lib}/parser/module_spec.rb +1 -1
  137. data/spec/{cli → lib}/parser/not_spec.rb +1 -1
  138. data/spec/{cli → lib}/parser/op_asgn1_spec.rb +1 -1
  139. data/spec/{cli → lib}/parser/op_asgn2_spec.rb +1 -1
  140. data/spec/{cli → lib}/parser/or_spec.rb +1 -1
  141. data/spec/{cli → lib}/parser/return_spec.rb +1 -1
  142. data/spec/{cli → lib}/parser/sclass_spec.rb +1 -1
  143. data/spec/{cli → lib}/parser/string_spec.rb +8 -1
  144. data/spec/{cli → lib}/parser/super_spec.rb +1 -1
  145. data/spec/lib/parser/unary_spec.rb +48 -0
  146. data/spec/{cli → lib}/parser/undef_spec.rb +1 -1
  147. data/spec/{cli → lib}/parser/unless_spec.rb +1 -1
  148. data/spec/{cli → lib}/parser/variables_spec.rb +1 -1
  149. data/spec/{cli → lib}/parser/while_spec.rb +1 -1
  150. data/spec/{cli → lib}/parser/yield_spec.rb +1 -1
  151. data/spec/lib/path_reader_spec.rb +24 -0
  152. data/spec/lib/shared/path_finder_shared.rb +19 -0
  153. data/spec/lib/shared/path_reader_shared.rb +31 -0
  154. data/spec/lib/spec_helper.rb +9 -0
  155. data/spec/lib/sprockets/environment_spec.rb +30 -0
  156. data/spec/{cli → lib}/sprockets/erb_spec.rb +1 -1
  157. data/spec/lib/sprockets/path_reader_spec.rb +25 -0
  158. data/spec/{cli → lib}/sprockets/processor_spec.rb +9 -2
  159. data/spec/lib/sprockets/server_spec.rb +20 -0
  160. data/spec/opal/compiler/irb_spec.rb +11 -11
  161. data/spec/opal/core/fixtures/require_tree_files/file 1.rb +1 -0
  162. data/spec/opal/core/fixtures/require_tree_files/file 2.rb +1 -0
  163. data/spec/opal/core/fixtures/require_tree_files/file 3.rb +1 -0
  164. data/spec/opal/core/fixtures/require_tree_files/file 4.rb +1 -0
  165. data/spec/opal/core/fixtures/require_tree_files/file 5.rb +1 -0
  166. data/spec/opal/core/kernel/require_tree_spec.rb +7 -0
  167. data/spec/opal/core/kernel/respond_to_spec.rb +2 -2
  168. data/spec/opal/core/runtime/method_missing_spec.rb +19 -0
  169. data/spec/opal/core/source_map_spec.rb +2 -2
  170. data/spec/opal/core/string_spec.rb +11 -0
  171. data/spec/opal/stdlib/erb/erb_spec.rb +0 -1
  172. data/spec/opal/stdlib/thread/mutex_spec.rb +40 -0
  173. data/spec/opal/stdlib/thread/thread_queue_spec.rb +32 -0
  174. data/spec/opal/stdlib/thread/thread_spec.rb +60 -0
  175. data/spec/rubyspecs +54 -11
  176. data/spec/spec_helper.rb +18 -3
  177. data/spec/support/mspec_rspec_adapter.rb +33 -0
  178. data/spec/{cli/spec_helper.rb → support/parser_helpers.rb} +10 -10
  179. data/stdlib/README.md +3 -0
  180. data/stdlib/benchmark.rb +10 -0
  181. data/stdlib/date.rb +2 -2
  182. data/stdlib/dir.rb +1 -5
  183. data/stdlib/file.rb +1 -7
  184. data/stdlib/json.rb +10 -1
  185. data/stdlib/native.rb +5 -5
  186. data/stdlib/nodejs.rb +5 -0
  187. data/stdlib/nodejs/dir.rb +13 -0
  188. data/stdlib/nodejs/file.rb +98 -0
  189. data/stdlib/nodejs/fileutils.rb +26 -0
  190. data/stdlib/nodejs/io.rb +2 -0
  191. data/stdlib/nodejs/irb.rb +45 -0
  192. data/stdlib/nodejs/process.rb +16 -0
  193. data/stdlib/nodejs/require.rb +32 -0
  194. data/stdlib/nodejs/rubygems.rb +68 -0
  195. data/stdlib/nodejs/runtime.rb +25 -0
  196. data/stdlib/nodejs/yaml.rb +11 -0
  197. data/stdlib/opal-parser.rb +1 -2
  198. data/stdlib/opal-source-maps.rb +2 -0
  199. data/stdlib/phantomjs.rb +8 -0
  200. data/stdlib/process.rb +10 -0
  201. data/stdlib/promise.rb +12 -4
  202. data/stdlib/set.rb +27 -0
  203. data/stdlib/source_map.rb +5 -63
  204. data/stdlib/source_map/map.rb +220 -0
  205. data/stdlib/source_map/mapping.rb +26 -0
  206. data/stdlib/source_map/offset.rb +88 -0
  207. data/stdlib/source_map/version.rb +3 -0
  208. data/stdlib/source_map/vlq.rb +77 -101
  209. data/stdlib/sourcemap.rb +1 -0
  210. data/stdlib/strscan.rb +7 -1
  211. data/stdlib/template.rb +1 -1
  212. data/stdlib/thread.rb +147 -7
  213. metadata +238 -104
  214. data/lib/mspec/opal/mspec_fixes.rb +0 -87
  215. data/spec/cli/sprockets/environment_spec.rb +0 -14
  216. data/spec/filters/bugs/symbol.rb +0 -5
  217. data/spec/opal/core/kernel/warn_spec.rb +0 -83
  218. data/spec/opal/core/language/numbers_spec.rb +0 -60
  219. data/stdlib/opal-source-maps.js.erb +0 -2
  220. data/stdlib/source_map/generator.rb +0 -251
  221. data/stdlib/source_map/parser.rb +0 -102
@@ -1,87 +0,0 @@
1
- require 'mspec/mocks/mock'
2
- require 'mspec/guards/guard'
3
-
4
- # 1. Opal does not support mutable strings
5
- class ExceptionState
6
- def initialize(state, location, exception)
7
- @exception = exception
8
-
9
- @description = location ? ["An exception occurred during: #{location}"] : []
10
- if state
11
- @description << "\n" unless @description.empty?
12
- @description << state.description
13
- @describe = state.describe
14
- @it = state.it
15
- @description = @description.join ""
16
- else
17
- @describe = @it = ""
18
- end
19
- end
20
- end
21
-
22
- # 2. class_eval() doesnt except string parameter
23
- def Mock.install_method(obj, sym, type=nil)
24
- meta = obj.singleton_class
25
-
26
- key = replaced_key obj, sym
27
- sym = sym.to_sym
28
-
29
- if (sym == :respond_to? or mock_respond_to?(obj, sym, true)) and !replaced?(key.first)
30
- meta.__send__ :alias_method, key.first, sym
31
- end
32
-
33
- # meta.class_eval <<-END
34
- # def #{sym}(*args, &block)
35
- # Mock.verify_call self, :#{sym}, *args, &block
36
- # end
37
- # END
38
- meta.class_eval {
39
- define_method(sym) do |*args, &block|
40
- Mock.verify_call self, sym, *args, &block
41
- end
42
- }
43
-
44
- proxy = MockProxy.new type
45
-
46
- if proxy.mock?
47
- MSpec.expectation
48
- MSpec.actions :expectation, MSpec.current.state
49
- end
50
-
51
- if proxy.stub?
52
- stubs[key].unshift proxy
53
- else
54
- mocks[key] << proxy
55
- end
56
- objects[key] = obj
57
-
58
- proxy
59
- end
60
-
61
- # 3. Waiting for: https://github.com/rubyspec/mspec/pull/40
62
- class SpecGuard
63
- def implementation?(*args)
64
- args.any? do |name|
65
- !!case name
66
- when :rubinius
67
- RUBY_NAME =~ /^rbx/
68
- when :ruby
69
- RUBY_NAME =~ /^ruby/
70
- when :jruby
71
- RUBY_NAME =~ /^jruby/
72
- when :ironruby
73
- RUBY_NAME =~ /^ironruby/
74
- when :macruby
75
- RUBY_NAME =~ /^macruby/
76
- when :maglev
77
- RUBY_NAME =~ /^maglev/
78
- when :topaz
79
- RUBY_NAME =~ /^topaz/
80
- when :opal
81
- RUBY_NAME =~ /^opal/
82
- else
83
- false
84
- end
85
- end
86
- end
87
- end
@@ -1,14 +0,0 @@
1
- require 'cli/spec_helper'
2
- require 'opal/sprockets/environment'
3
-
4
- describe Opal::Environment do
5
- let(:env) { described_class.new }
6
- let(:logical_path) { 'sprockets_file' }
7
-
8
- before { env.append_path File.expand_path('../../fixtures/', __FILE__) }
9
-
10
- it 'compiles Ruby to JS' do
11
- expect(env[logical_path].source).to include('$puts(')
12
- expect(env[logical_path+'.js'].source).to include('$puts(')
13
- end
14
- end
@@ -1,5 +0,0 @@
1
- opal_filter "Symbol" do
2
- fails "Symbol#to_proc sends self to arguments passed when calling #call on the Proc"
3
- fails "Symbol#to_proc raises an ArgumentError when calling #call on the Proc without receiver"
4
- fails "Symbol#to_proc passes along the block passed to Proc#call"
5
- end
@@ -1,83 +0,0 @@
1
- require 'spec_helper'
2
- require 'stringio'
3
-
4
- describe 'Kernel#warn' do
5
- before do
6
- @fake_stderr = StringIO.new
7
- end
8
-
9
- it 'writes single message to $stderr if $VERBOSE is true' do
10
- old_verbose = $VERBOSE
11
- $VERBOSE = true
12
-
13
- captured_stderr {
14
- warn 'this is a warning message'
15
- }.should == 'this is a warning message'
16
-
17
- $VERBOSE = old_verbose
18
- end
19
-
20
- it 'writes multiple messages to $stderr if $VERBOSE is true' do
21
- old_verbose = $VERBOSE
22
- $VERBOSE = true
23
-
24
- captured_stderr {
25
- warn 'this is a warning message', 'this is another'
26
- }.should == "this is a warning message\nthis is another"
27
-
28
- $VERBOSE = old_verbose
29
- end
30
-
31
- it 'does not write empty message to $stderr if $VERBOSE is true' do
32
- old_verbose = $VERBOSE
33
- $VERBOSE = true
34
-
35
- captured_stderr {
36
- warn
37
- }.should be_nil
38
-
39
- $VERBOSE = old_verbose
40
- end
41
-
42
- it 'does write message to $stderr if $VERBOSE is false' do
43
- old_verbose = $VERBOSE
44
- $VERBOSE = false
45
-
46
- captured_stderr {
47
- warn 'this is a warning message'
48
- }.should == 'this is a warning message'
49
-
50
- $VERBOSE = old_verbose
51
- end
52
-
53
- it 'does not write message to $stderr if $VERBOSE is nil' do
54
- old_verbose = $VERBOSE
55
- $VERBOSE = nil
56
-
57
- captured_stderr {
58
- warn 'this is a warning message'
59
- }.should be_nil
60
-
61
- $VERBOSE = old_verbose
62
- end
63
-
64
- it 'returns a nil value' do
65
- old_verbose = $VERBOSE
66
- $VERBOSE = true
67
-
68
- captured_stderr {
69
- (warn 'this is a warning message').should be_nil
70
- }
71
-
72
- $VERBOSE = old_verbose
73
- end
74
-
75
- def captured_stderr
76
- original_stderr = $stderr
77
- $stderr = @fake_stderr
78
- yield
79
- @fake_stderr.tap(&:rewind).read
80
- ensure
81
- $stderr = original_stderr
82
- end
83
- end
@@ -1,60 +0,0 @@
1
- describe "Ruby numbers in various ways" do
2
-
3
- it "the standard way" do
4
- 435.should == 435
5
- end
6
-
7
- it "with underscore separations" do
8
- 4_35.should == 435
9
- end
10
-
11
- it "with some decimals" do
12
- 4.35.should == 4.35
13
- end
14
-
15
- it "with decimals but no integer part should be a SyntaxError" do
16
- lambda { eval(".75") }.should raise_error(SyntaxError)
17
- lambda { eval("-.75") }.should raise_error(SyntaxError)
18
- end
19
-
20
- # TODO : find a better description
21
- it "using the e expression" do
22
- 1.2e-3.should == 0.0012
23
- end
24
-
25
- it "the hexdecimal notation" do
26
- 0xffff.should == 65535
27
- end
28
-
29
- it "the binary notation" do
30
- 0b01011.should == 11
31
- 0101.should == 5
32
- 001010.should == 10
33
- 0b1010.should == 10
34
- 0b10_10.should == 10
35
- end
36
-
37
- it "octal representation" do
38
- 0377.should == 255
39
- 0o377.should == 255
40
- 0o3_77.should == 255
41
- end
42
-
43
- ruby_version_is '' ... '1.9' do
44
- it "character to numeric shortcut" do
45
- ?z.should == 122
46
- end
47
-
48
- it "character with control character to numeric shortcut" do
49
- # Control-Z
50
- #?\C-z.should == 26
51
-
52
- # Meta-Z
53
- #?\M-z.should == 250
54
-
55
- # Meta-Control-Z
56
- #?\M-\C-z.should == 154
57
- end
58
- end
59
-
60
- end
@@ -1,2 +0,0 @@
1
- <% require_asset 'source_map' %>
2
- <%= Opal.compile File.read(File.join Opal.core_dir, '..', 'lib', "opal/source_map.rb") %>
@@ -1,251 +0,0 @@
1
- class SourceMap
2
- module Generator
3
-
4
- # An object (responding to <<) that will be written to whenever
5
- # {add_generated} is called.
6
- #
7
- # @example
8
- #
9
- # File.open("/var/www/a.js.min"){ |f|
10
- # map = SourceMap.new(:generated_output => f)
11
- # map.add_generated('function(a,b,c){minified=1}\n', :source => 'a.js')
12
- # map.save('/var/www/a.js.map')
13
- # }
14
- # File.read('/var/www/a.js.min') == 'function(a,b,c){minified=1}\n'
15
- #
16
- attr_accessor :generated_output
17
-
18
- # Add the mapping for generated code to this source map.
19
- #
20
- # The first parameter is the generated text that you're going to add to the output, if
21
- # it contains multiple lines of code then it will be added to the source map as
22
- # several mappings.
23
- #
24
- # If present, the second parameter represents the original source of the generated
25
- # fragment, and may contain:
26
- #
27
- # :source => String, # The filename of the source fille that contains this fragment.
28
- # :source_line => Integer, # The line in that file that contains this fragment
29
- # :source_col => Integer, # The column in that line at which this fragment starts
30
- # :name => String # The original name for this variable.
31
- # :exact_position => Bool # Whether all lines in the generated fragment came from
32
- # the same position in the source.
33
- #
34
- # The :source key is required to set :source_line, :source_col or :name.
35
- #
36
- # If unset :source_line and :source_col default to 1,0 for the first line of the
37
- # generated fragment.
38
- #
39
- # Normally :source_line is incremented and :source_col reset at every line break in
40
- # the generated code (because we assume that you're copying a verbatim fragment from
41
- # the source into the generated code). If that is not the case, you can set
42
- # :exact_position => true, and then all lines in the generated output will be given
43
- # the same :source_line and :source_col.
44
- #
45
- # The :name property is used if the fragment you are adding contains only a name that
46
- # you have renamed in the source transformation.
47
- #
48
- # If you'd like to ensure that the source map stays in sync with the generated
49
- # source, consider calling {source_map.generated_output = StringIO.new} and then
50
- # accessing your generated javascript with {source_map.generated_output.string},
51
- # otherwise be careful to always write to both.
52
- #
53
- # NOTE: By long-standing convention, the first line of a file is numbered 1, not 0.
54
- #
55
- # NOTE: when generating a source map, you should either use this method always, or use
56
- # the {#add_mapping} method always.
57
- #
58
- def add_generated(text, opts={})
59
- if !opts[:source] && (opts[:name] || opts[:source_line] || opts[:source_col])
60
- raise "mapping must have :source to have :source_line, :source_col or :name"
61
- elsif opts[:source_line] && opts[:source_line] < 1
62
- raise "files start on line 1 (got :source_line => #{opts[:source_line]})"
63
- elsif !(remain = opts.keys - [:source, :source_line, :source_col, :name, :exact_position]).empty?
64
- raise "mapping had unexpected keys: #{remain.inspect}"
65
- end
66
-
67
- source_line = opts[:source_line] || 1
68
- source_col = opts[:source_col] || 0
69
- self.generated_line ||= 1
70
- self.generated_col ||= 0
71
-
72
- text.split(/(\n)/).each do |line|
73
- if line == "\n"
74
- self.generated_line += 1
75
- self.generated_col = 0
76
- unless opts[:exact_position]
77
- source_line += 1
78
- source_col = 0
79
- end
80
- elsif line != ""
81
- mapping = {
82
- :generated_line => generated_line,
83
- :generated_col => generated_col,
84
- }
85
- if opts[:source]
86
- mapping[:source] = opts[:source]
87
- mapping[:source_line] = source_line
88
- mapping[:source_col] = source_col
89
- mapping[:name] = opts[:name] if opts[:name]
90
- end
91
-
92
- mappings << mapping
93
-
94
- self.generated_col += line.size
95
- source_col += line.size unless opts[:exact_position]
96
- end
97
- end
98
-
99
- generated_output += text if generated_output
100
- end
101
-
102
- # Add a mapping to the list for this object.
103
- #
104
- # A mapping identifies a fragment of code that has been moved around during
105
- # transformation from the source file to the generated file. The fragment should
106
- # be contiguous and not contain any line breaks.
107
- #
108
- # Mappings are Hashes with a valid subset of the following 6 keys:
109
- #
110
- # :generated_line => Integer, # The line in the generated file that contains this fragment.
111
- # :generated_col => Integer, # The column in the generated_line that this mapping starts on
112
- # :source => String, # The filename of the source fille that contains this fragment.
113
- # :source_line => Integer, # The line in that file that contains this fragment.
114
- # :source_col => Integer, # The column in that line at which this fragment starts.
115
- # :name => String # The original name for this variable (if applicable).
116
- #
117
- #
118
- # The only 3 valid subsets of keys are:
119
- # [:generated_line, :generated_col] To indicate that this is a fragment in the
120
- # output file that you don't have the source for.
121
- #
122
- # [:generated_line, :generated_col, :source, :source_line, :source_col] To indicate
123
- # that this is a fragment in the output file that you do have the source for.
124
- #
125
- # [:generated_line, :generated_col, :source, :source_line, :source_col, :name] To
126
- # indicate that this is a particular identifier at a particular location in the original.
127
- #
128
- # Any other combination of keys would produce an invalid source map.
129
- #
130
- # NOTE: By long-standing convention, the first line of a file is numbered 1, not 0.
131
- #
132
- # NOTE: when generating a source map, you should either use this method always,
133
- # or use the {#add_generated} method always.
134
- #
135
- def add_mapping(map)
136
- if !map[:generated_line] || !map[:generated_col]
137
- raise "mapping must have :generated_line and :generated_col"
138
- elsif map[:source] && !(map[:source_line] && map[:source_col])
139
- raise "mapping must have :source_line and :source_col if it has :source"
140
- elsif !map[:source] && (map[:source_line] || map[:source_col])
141
- raise "mapping may not have a :source_line or :source_col without a :source"
142
- elsif map[:name] && !map[:source]
143
- raise "mapping may not have a :name without a :source"
144
- elsif map[:source_line] && map[:source_line] < 1
145
- raise "files start on line 1 (got :source_line => #{map[:source_line]})"
146
- elsif map[:generated_line] < 1
147
- raise "files start on line 1 (got :generated_line => #{map[:generated_line]})"
148
- elsif !(remain = map.keys - [:generated_line, :generated_col, :source, :source_line, :source_col, :name]).empty?
149
- raise "mapping had unexpected keys: #{remain.inspect}"
150
- end
151
-
152
- mappings << map
153
- end
154
-
155
- # Convert the map into an object suitable for direct serialisation.
156
- def as_json
157
- serialized_mappings = serialize_mappings!
158
-
159
- {
160
- 'version' => version,
161
- 'file' => file,
162
- 'sourceRoot' => source_root,
163
- 'sources' => sources,
164
- 'names' => names,
165
- 'mappings' => serialized_mappings
166
- }
167
- end
168
-
169
- # Convert the map to a string.
170
- def to_s
171
- as_json.to_json
172
- end
173
-
174
- # Write this map to a file.
175
- def save(file)
176
- File.open(file, "w"){ |f| f << to_s }
177
- end
178
-
179
- protected
180
-
181
- attr_reader :source_ids, :name_ids
182
- attr_accessor :generated_line, :generated_col
183
-
184
- # Get the id for the given file. If we've not
185
- # seen this file before, add it to the list.
186
- def source_id(file)
187
- if (cached = source_ids[file])
188
- cached
189
- else
190
- sources << file
191
- source_ids[file] = sources.size - 1
192
- end
193
- end
194
-
195
- # Get the id for the given name. If we've not
196
- # seen this name before, add it to the list.
197
- def name_id(name)
198
- if (cached = name_ids[file])
199
- cached
200
- else
201
- names << name
202
- name_ids[file] = names.size - 1
203
- end
204
- end
205
-
206
- # Encode a vlq. As each field in the output should be relative to the
207
- # previous occurance of that field, we keep track of each one.
208
- def vlq(num, type)
209
- ret = num - @previous_vlq[type]
210
- @previous_vlq[type] = num
211
- VLQ.encode(ret)
212
- end
213
-
214
- # Serialize the list of mappings into the string of base64 variable length
215
- # quanities. As a side-effect, regenerate the sources and names arrays.
216
- def serialize_mappings!
217
- # clear all internals as we're about to re-generate them.
218
- @sources = []
219
- @source_ids = {}
220
- @names = []
221
- @name_ids = {}
222
- @previous_vlq = Hash.new{ 0 }
223
-
224
- return "" if mappings.empty?
225
-
226
- by_lines = mappings.group_by{ |x| x[:generated_line] }
227
-
228
- (1..by_lines.keys.max).map do |line|
229
- # reset the generated_col on each line as indicated by the VLQ spec.
230
- # (the other values continue to be relative)
231
- @previous_vlq[:generated_col] = 0
232
-
233
- fragments = (by_lines[line] || []).sort_by{ |x| x[:generated_col] }
234
- fragments.map do |map|
235
- serialize_mapping(map)
236
- end.join(",")
237
- end.join(";")
238
- end
239
-
240
- def serialize_mapping(map)
241
- item = vlq(map[:generated_col], :generated_col)
242
- if map[:source]
243
- item += vlq(source_id(map[:source]), :source)
244
- item += vlq(map[:source_line] - 1, :source_line)
245
- item += vlq(map[:source_col], :source_col)
246
- item += vlq(name_id(map[:name]), :name) if map[:name]
247
- end
248
- item
249
- end
250
- end
251
- end