opal 0.6.3 → 0.7.0.beta1

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.
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